При использовании локальной петли сообщений существует опасность бесконечной рекурсии. Рассмотрим это на простом примере: предположим, что сложный код, содержащий локальную петлю сообщений, выполняется при нажатии некоторой кнопки на форме приложения. Пока обработчик выполняется, нетерпеливый пользователь может снова нажать кнопку, запустив вторую активацию обработчика нажатия кнопки, и так несколько раз. Конечно, организовать таким образом очень глубокую рекурсию пользователь вряд ли сможет (терпения не хватит), но часто даже то, что несколько активаций обработчика вызваны рекурсивно, может привести к неприятным последствиям. А если программа организует локальную петлю сообщений в обработчике сообщений таймера, то здесь рекурсия действительно может углубляться до переполнения стека. Поэтому при организации петли сообщений следует принимать меры против рекурсии. Например, в случае с кнопкой в обработчике ее нажатие можно запретить ( Enabled := False
), и вновь разрешить только после окончания обработки, тогда пользователь не сможет нажать кнопку во время работы локальной петли сообщений. В очередь можно поставить сообщение, не привязанное ни к какому окну. Это делается с помощью функции PostThreadMessage
. Такие сообщения необходимо самостоятельно обрабатывать в петле сообщений, потому что функция DispatchMessage
их просто игнорирует.
Рис. 1.6. Блок-схема программы с локальной петлей сообщений
Существуют также широковещательные сообщения, которые посылаются сразу нескольким окнам. Проще всего послать такое сообщение с помощью функции PostMessage
, указав в качестве адресата не дескриптор конкретного окна, а константу HWND_BROADCAST
. Такое сообщение получат все окна, расположенные непосредственно на рабочем столе и не имеющие при этом владельцев (в терминах системы). Существует также специальная функция BroadcastSystemMessage
(начиная с Windows ХР — ее расширенный вариант BroadcastSystemMessageEx
), которая позволяет уточнить, каким конкретно окнам будет отправлено широковещательное сообщение.
Кроме параметров wParam
и lParam
, каждому сообщению
приписывается время отправки и координаты курсора в момент возникновения. Соответствующие поля есть в структуре TMsg, которую используют функции GetMessage
и DispatchMessage, но у оконной процедуры не предусмотрены параметры для их передачи. Получить время отправки сообщения и координаты курсора при обработке сообщения можно с помощью функций GetMessageTime
и GetMessagePos
соответственно.
Существует также ряд функций, которые могут обрабатывать сообщения без участия DispatchMessage
и оконной процедуры. Если эти функции распознают сообщение, извлеченное из очереди, как "свое", они сами выполняют все необходимые действия по его обработке, и тогда TranslateMessage
и DispatchMessage
вызывать не нужно. К этим функциям, в частности, относятся следующие:
□ TranslateAccelerator
— на основе загруженной из ресурсов таблицы распознает нажатие "горячих" клавиш меню и вызывает оконную процедуру, передавая ей сообщение WM_COMMAND
или WM_SYSCOMMAND
, аналогичное тому, которое посылается при выборе соответствующего пункта меню пользователем;
□ TranslateMDISysAccel
— аналог предыдущей функции за исключением того, что распознает "горячие" клавиши системного меню MDI-окон;
□ IsDialogMessage
— распознает сообщения, имеющие особый смысл для диалоговых окон (например, нажатие клавиши для перехода между элементами управления). Используется для немодальных диалоговых окон и окон, не являющихся диалоговыми (т.е. созданными без помощи функций CreateDialogXXXX
), но требующими аналогичной функциональности.
Перечисленные функции при необходимости вставляются в петлю сообщений. Листинг 1.6 показывает, как будет выглядеть петля сообщений, содержащая вызов TranslateAccelerator
для родительской MDI-формы и TranslateMDISysAccel
для дочерней.
Листинг 1.6. Петля сообщении с обработкой "горячих" клавиш главного меню и системного меню MDI-окон
while GetMessage(Msg, 0, 0, 0) do
if not TranslateMDISysAccel(ActiveMDIChildHandle, Msg)
and not TranslateAccelerator(MDIFormHandle, AccHandle, Msg) then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
При отправке сообщения, в отличие от посылки, оно не ставится в очередь, а передается оконной процедуре напрямую. Отправить сообщение можно, например, с помощью функции SendMessage
. Если эта функция вызывается из той же нити, которой принадлежит окно-адресат, то фактически это эквивалентно прямому вызову оконной процедуры. Если окно принадлежит другой нити, данное сообщение становится в отдельную очередь, имеющую более высокий приоритет, чем очередь для посланных сообщений. Функции GetMessage
и PeekMessage
сначала выбирают все сообщения из этой очереди и отправляют их на обработку, и лишь затем приступают к анализу очереди посланных сообщений.
Читать дальше
Конец ознакомительного отрывка
Купить книгу