Читать «Применение Windows API» онлайн - страница 20
А. И. Легалов
class ControllerFactory : public CtrlFactory {
public:
ControllerFactory(void *argList) : CtrlFactory(argList) {}
DlgController * MakeController(HWND hwndDlg) {
return new ActualCtrl(hwndDlg, (ActualArgList*)GetArgList());
}
};
Ниже приводится определение абстрактного класса DlgController, который используется как основа для всех классов контроллеров, определенных клиентом. Мы уже видели, как работает эти наследования на примере клиентского класса EditorCtrl.
class DlgController {
public:
virtual ~DlgController() {} // In case derived class overrides
virtual void OnInitDialog(HWND hwnd) = 0;
virtual bool OnCommand(HWND hwnd, int ctrlID, int notifyCode) = 0;
virtual bool OnNotify(HWND hwnd, int idCtrl, NMHDR *hdr) = 0;
void *GetArgList() { return _argList; }
protected:
DlgController(void *argList) : _argList (argList) {}
private:
void *_argList;
};
Центральным фрагментом для многократного использования программного обеспечения является класс ModalDialog. Он делает всю работу в своем конструкторе, вызывая функцию API DialogBoxParam. Параметр, который мы передаем диалоговому окну (фактически, его процедуре диалога) — указатель на фабрику контроллера. Процедура диалога определена как статический метод (не нужен указатель: процедура диалога вызывается из Windows, поэтому отсутствует доступ по указателю).
class ModalDialog {
public:
ModalDialog(HINSTANCE hInst, HWND hwnd, int dlgResource, CtrlFactory *ctrlFactory) {
_result = DialogBoxParam(hInst, MAKEINTRESOURCE(dlgResource), hwnd, (DLGPROC)ModalDialogProc, (LPARAM)ctrlFactory);
}
static BOOL CALLBACK ModalDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
bool IsOk() const { return (_result == –1) ? false : _result != 0; }
private:
int _result;
};
В заключение отметим, что процедура диалога, общая для всех типов диалогов, реализована так, чтобы ответить на три вида сообщений: WM_INITDIALOG, WM_COMMAND и WM_NOTIFY. Фактически, все они направляют эти сообщения к объекту — контроллеру. Он получает указатель на полиморфный объект контроллера вызывая в начале метод фабрики MakeController.
Обратите внимание, что, из этой точки мы позволяем Windows, отследить указатель на контроллер. Мы сохраняем его как GWL_USERDATA — специальный длинный, который ассоциируется с каждым окном, в особенности с нашим диалогом, и доступен через его дескриптор окна.
template <class T> inline T
GetWinLong(HWND hwnd, int which = GWL_USERDATA) {
return reinterpret_cast<T>(::GetWindowLong(hwnd, which));
}
template <class T> inline void
SetWinLong(HWND hwnd, T value, int which = GWL_USERDATA) {
::SetWindowLong(hwnd, which, reinterpret_cast<long>(value));
}
Мы должны быть, хотя бы внимательными. Прежде всего мы должны освободить контроллер после того, как использовали его. Мы делаем это при обработке WM_DESTROY.
Во-вторых, Windows имеет неудачную идею (привычку) посылать сообщения WM_COMMAND и WM_NOTIFY перед WM_INITDIALOG и после WM_DESTROY. Что можно здесь сказать? Я бы побил менеджера, который ответствен за эти дела. Но раз это есть, мы должны защитить себя, проверяя, является ли ctrl ненулевым перед вызовом OnCommand и OnNotify.