Обработка 32-разрядных сообщений
В приложениях Win16 параметр lParam часто использовался для передачи сразу двух значений, соответственно, через младшее и старшее слово. Для удобства выделения этих значений предназначались специальные макрокоманды LOWORD и HIWORD .
Например, для сообщения WM_COMMAND в младшем слове параметра lParam передавался 16-разрядный идентификатор дочернего окна hWnd (органа управления, пославшего это сообщение), в старшем - 16-разрядный код извещения wCmd. Через параметр wParam передавался 16-разрядный идентификатор органа управления wId (рис. 1.14).
Рис. 1.14. Формат параметров wParam и lParam для сообщения WM_COMMAND в приложениях Win16
В приложениях Win32 идентификатор окна hWnd стал 32-разрядным, поэтому его никак нельзя разместить в старшем слове параметра lParam. Теперь приходится отводить для идентификатора окна все разряды параметра lParam.
А что делать с кодом извещения wCmd, который оказался "выселен" из хорошо обжитого им параметра lParam?
Его пришлось перенести в старшее слово параметра wParam, удвоившего свою разрядность (рис. 1.15).
Рис. 1.15. Формат параметров wParam и lParam для сообщения WM_COMMAND в приложениях Win32
Таким образом, идентификатор окна остался на месте, а код извещения "переехал" в старшее слово параметра wParam. Аналогичным образом изменился формат и других сообщений.
Приведем два фрагмента обработчика сообщения WM_COMMAND, первый из которых используется в приложениях Win16, а второй - в приложениях Win32.
Итак, первый фрагмент.
case WM_COMMAND: { wId = wParam; hWnd = LOWORD(lParam); nCmd = HIWORD(lParam); ... }
Приложения Win32 должны разбирать сообщение WM_COMMAND на составные части по-другому:
case WM_COMMAND: { wId = LOWORD(wParam); hWnd = (HWND)(UINT)lParam; nCmd = HIWORD(wParam); ... }
В файле windowsx.h определены макрокоманды разборки сообщений (message crackers), которые позволяют "извлечь" из сообщений отдельные параметры.
Приведем для примера определения макрокоманд разборки сообщения WM_COMMAND из файла windowsx.h:
#define GET_WM_COMMAND_ID(wp, lp) LOWORD(wp) #define GET_WM_COMMAND_HWND(wp, lp) (HWND)(lp) #define GET_WM_COMMAND_CMD(wp, lp) HIWORD(wp) #define GET_WM_COMMAND_MPS(id, hwnd, cmd) \ (WPARAM)MAKELONG(id, cmd), (LONG)(hwnd)
Есть и более впечатляющие макрокоманды, позволяющие значительно упростить внешний вид обработчика сообщений в функции окна.
Предположим, нам надо организовать обработку сообщений WM_CREATE и WM_LBUTTONDOWN . Мы готовим две функции с именами WndProc_OnCreate и WndProc_OnLButtonDown, которые будут заниматься обработкой этих сообщений:
BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct); void WndProc_OnLButtonDown(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
Имена функций не играют никакой роли, однако Microsoft рекомендует составлять их из префикса имени функции окна (например, WndProc) с добавлением строки _On и названия сообщения, в котором удален префикс WM_.
Затем в функции окна мы используем макрокоманду HANDLE_MSG , указывая ей в качестве первого параметра идентификатор окна, в качестве второго - номер сообщения и, наконец, в качестве третьего - имя функции, которая будет обрабатывать сообщение:
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hWnd, WM_CREATE, WndProc_OnCreate); HANDLE_MSG(hWnd, WM_LBUTTONDOWN, WndProc_OnLButtonDown); default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } }
Теперь оператор switch будет выглядеть компактнее, так как для обработки каждого сообщения в него добавляется только одна строка.
Следует, однако, заметить, что для работы с макрокомандой HANDLE_MSG вы должны использовать в качестве имен для последних двух параметров функции WndProc имена wParam и lParam (только эти имена).
Как выглядит функция обработчика сообщения, вызываемая макрокомандой HANDLE_MSG?
Так же, как и обычная функция, за одним исключением - она должна оканчиваться вызовом макрокоманды FORWARD_<имя сообщения> в операторе return (даже если функция имеет тип void):
void WndProc_OnLButtonDown(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { ... return FORWARD_WM_LBUTTONDOWN(hWnd, fDoubleClick, x, y, keyFlags, DefWindowProc); }
Но при обработке сообщения WM_CREATE вы должны в случае успешной инициализации данных окна вернуть значение TRUE, иначе окно так и не будет создано:
BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { ... return TRUE; }
Вы можете создать перечисленные выше макрокоманды и для обработки собственных сообщений, взяв за основу определения из файла windowsx.h.
В этой книге мы приведем несколько примеров, демонстрирующих применение макрокоманд разборки сообщений. Для отключения предупреждающего сообщения о том, что функция типа void возвращает управление при помощи оператора return, перед определением функции обработчика сообщения добавляем следующую строку:
#pragma warning(disable: 4098)