Учебное пособие: Сообщения и их обработка
Поле message структуры MSG задает номер сообщения, посланного системой. Интерпретация параметров сообщения wParam и lParam зависит от самого сообщения. Для этого надо смотреть описание конкретного сообщения и обрабатывать параметры соответствующим образом. Так как в системе определено огромное количество разных сообщений, то для простоты использования применяются символические имена сообщений, задаваемыми с помощью #define в заголовочном файле. В качестве примера можно привести сообщения WM_CREATE, WM_PAINT, WM_QUIT.
hWnd указывает хендл окна, сообщения для которого будут выбираться из очереди. Если hWnd равен NULL, то будут выбираться сообщения для всех окон данного приложения, а если hWnd указывает реальное окно, то из очереди будут выбираться все сообщения, направленные этому окну или его потомкам (дочерним или используемым окнами, или их потомкам, в том числе отдаленным).
uMsgFilterMin и uMsgFilterMax обычно установлены в NULL. Вообще они задают фильтр для сообщений. GetMessage выбирает из очереди сообщения, номера (имена) которых лежат в интервале от uMsgFilterMin до uMsgFilterMax. Нулевые значения исключают фильтрацию.
Функция GetMessage возвращает во всех случаях, кроме одного, ненулевое значение, указывающее, что цикл надо продолжать. Только в одном случае эта функция возвратит 0 — если она извлечет из очереди сообщение WM_QUIT. Это сообщение посылается только при окончании работы приложения.
После завершения цикла надо сделать совсем немногое — освободить память от тех объектов, которые создавались во время работы приложения (если они еще существуют). Некоторые объекты, которые уничтожаются автоматически, можно не освобождать — это сделает Windows. Таков, например, зарегистрированный нами класс окон.
И остается еще одно дело: так как WinMain возвращает результат, то мы должны вернуть какое–либо значение. В Windows принято, что возвращаемое значение является параметром wParam сообщения WM_QUIT, завершившего цикл обработки сообщений. Таким образом мы пишем:
return msg.wParam;
Посылка и передача сообщений
Ранее, в разделе “Ошибка! Источник ссылки не найден. ”, мы рассматривали метод передачи сообщений, называемый посылкой сообщений (post message), и их обработки — извлечения из очереди в цикле обработки сообщений, трансляции и последующей передачи оконной процедуре. Источниками таких сообщений могут быть как компоненты системы, например, клавиатурный драйвер, так и само приложение. Для посылки сообщения в API предусмотрена функция
BOOL PostMessage(hWnd, uMsg, wParam, lParam);
Эта функция ставит сообщение в очередь. Возвращаемое значение TRUE указывает, что сообщение поставлено в очередь, FALSE — возникла ошибка (например, ошибочно указанный адресат или очередь сообщений переполнилась). Позже сообщение будет извлечено из очереди вызовом функции GetMessage или PeekMessage в цикле обработки сообщений.
Однако механизм посылки сообщений не всегда удобен, так как не позволяет получить результат обработки сообщения, или дождаться его завершения. Точнее, позволяет, но очень громоздким способом — надо вводить специальные ответные сообщения и дожидаться их получения.
Вообще говоря, процесс посылки и обработки посланных сообщений часто называют асинхронным способом обработки сообщений, так как сама посылка сообщения и его обработка никак между собой не связаны по времени.
Для решения этих задач вводится альтернативный механизм, называемый передачей сообщений (send message). При этом сообщение в очередь не попадает, а направляется непосредственно оконной функции[1] . Приблизительно его можно рассматривать как непосредственный вызов процедуры обработки сообщений. Для передачи сообщения используется функция
LONG SendMessage(hWnd, uMsg, wParam, lParam);
Она вызывает оконную процедуру указанного окна, получает результат обработки сообщения и возвращает управление в вызвавшую процедуру после обработки указанного сообщения. Возвращаемое этой функцией значение совпадает с результатом, выполнения оконной функции. Вы можете воспользоваться ею для передачи тех или иных сообщений даже до организации цикла обработки сообщений, так как очередь сообщений при этом не используется (кроме особых случаев в Win32 API — см. ниже).
Многие функции API используют SendMessage для передачи сообщений окну. Например, функция CreateWindow, создающая окно, в процессе создания передает ему сообщение WM_CREATE (и не только одно это сообщение), а результат, возвращенный обработчиком сообщения, используется функцией CreateWindow для продолжения создания окна.
Процесс передачи сообщений с помощью функции SendMessage называется синхронной обработкой сообщений, так как процесс передачи и обработки сообщений жестко упорядочен во времени.
То, что сообщения, переданные с помощью функции SendMessage, минуют цикл обработки сообщений, накладывает ограничения на применение этой функции. Дело в том, что цикл обработки сообщений выполняет над некоторыми сообщениями определенные операции. Так, например, нельзя передавать сообщение WM_QUIT — оно обязательно должно пройти через очередь сообщений, так как используется для завершения цикла обработки сообщений. Другие сообщения (например, клавиатуры) транслируются в цикле и их тоже надо посылать, а не передавать.
Иногда сообщения передаются не какому–либо конкретному окну и не какому–либо приложению (потоку), а всем приложениям, запущенным в системе. Для этого используются широковещательные сообщения (broadcast message), которые передаются всем главным окнам всех приложений. Для передачи такого сообщения необходимо указать в качестве хендла окна–получателя специальный символ HWND_BROADCAST (равный -1), например:
SendMessage(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndDDEClient, 0L);
В этом случае сообщение WM_DDE_INITIATE будет передано главным окнам всех приложений, так что все работающие DDE–сервера смогут на него ответить.
Существенная особенность широковещательных сообщений — то, что они будут обрабатываться сразу большим количеством окон. То есть получить результат от обработчика этого сообщения обычным путем нельзя, так как остается неопределенность, результат какого обработчика возвращать. В частном случае (Win32 API) можно получить ответы от всех обработчиков с помощью функции SendMessageCallback. В общем случае при необходимости получения ответа от обработчика тот должен передать ответное сообщение (именно так сделано при установлении DDE–разговора).
Особенности посылки сообщений в Windows API
Помимо рассмотренных функций в Windows API существует еще две функции, посылающие сообщения. С одной из них — PostQuitMessage мы уже встречались в примере 1A. Эта функция посылает сообщение WM_QUIT, которое служит для завершения цикла обработки сообщений.
BOOL PostQuitMessage(wParam);
Сообщение WM_QUIT интересно еще и тем, что оно не посылается никакому окну — хендл окна–получателя равен NULL. В принципе у вас есть возможность самому посылать такие сообщения своему приложению, однако для этого обычная функция PostMessage не подходит. Вместо нее предусмотрена другая функция
BOOL PostAppMessage(hTask, uMsg, wParam, lParam);
Эта функция направляет сообщение приложению, указанному хендлом задачи hTask[2] . Для получения этого хендла можно воспользоваться функциями GetCurrentTask или GetWindowTask.