WinPro logo.

Система сообщений

НА ЭТОЙ СТРАНИЦЕ:

О сообщениях и очередях сообщений
Оконные сообщения

Типы сообщений:
- Системные сообщения
- Сообщения, определяемые приложением

Маршрутизация сообщений:
- Асинхронные сообщения
- Синхронные сообщения

Манипулирование сообщениями:
- Цикл сообщений
- Оконная процедура
- Фильтрация сообщений

Помещение сообщений в очередь и передача их напрямую оконной процедуре:
- Добавление сообщений в очередь
- Передача сообщения напрямую оконной процедуре
- Широковещательные сообщения
- Тупиковые ситуации
- Сообщение-запрос

Функции для работы с сообщениями
Структуры для работы с сообщениями
Сообщения используемые при работе с сообщениями

Примеры

О сообщениях и очередях сообщений

(о приложениях управляемых событиями)

В отличие от приложений для MS-DOS, приложения для Windows управляются событиями. В таких приложениях нет возможности вызывать функцию для получения ввода. Вместо этого эти приложения дожидаются, чтобы система самостоятельно передавала им входящую информацию.

Вводимые данные направляются системой для каждого окна отдельно. В этих целях каждое окно связано с функцией называемой ОКОННОЙ ПРОЦЕДУРОЙ. Если для какого-либо из окон есть какая-то входящая информация, то система запускает (вызывает) соответствующую оконную процедуру, передавая ей введенную информацию. Оконная процедура должна обработать полученную информацию и вернуть управление обратно системе.

Оконные сообщения

Система отправляет входящие данные оконной процедуре в виде СООБЩЕНИЙ. Эти сообщения могут генерироваться, как и системой, так и самими приложениями. Система генерирует сообщение при каждом событии ввода, например при наборе текста пользователем на клавиатуре, перемещении мыши или щелчке клавишей мыши по элементу управления (например, полосе прокрутки). Так же система генерирует сообщения как реакцию на происходящие в ней изменения, касающиеся приложения, таких как смена системного шрифта или изменения размеров одного из окон приложения. Приложение может генерировать сообщения для своих окон или для передачи данных окнам других приложений.

Система передает сообщение оконной процедуре в виде набора из четырех параметров: описатель окна, идентификатор сообщения и двух значений называемых ПАРАМЕТРАМИ СООБЩЕНИЯ. Описатель окна используется для определения, какому из окон приложения предназначается сообщение, и система по нему определяет нужную оконную процедуру.

Идентификатор сообщения это символическая константа, которая определяет назначение сообщения. Оконная процедура использует его для определения того, как обработать поступившее сообщение. Например, идентификатор сообщения WM_PAINT, говорит оконной процедуре, что клиентская область ее окна была изменена и требует перерисовки.

Параметры сообщения содержат данные или указатели на данные, которые могут понадобиться оконной процедуре для обработки поступившего сообщения. Эти значения и их смысл зависят от конкретного сообщения. Если в сообщении не используются параметры, то они обычно устанавливаются в NULL. Оконная процедура должна проверять идентификатор сообщения, для того чтобы определить, что ей делать с параметрами сообщения.

ТИПЫ СООБЩЕНИЙ

Системные сообщения

Система высылает системные сообщения для установки связи с приложением. Эти сообщения используются для контроля над приложением, предоставления ему входящих данных и другой информации. Приложение, в свою очередь, тоже может генерировать системные сообщения. В основном приложения используют эти сообщения для управления окнами, созданных на основе предопределенных классов.

Каждое системное сообщение имеет уникальный идентификатор и соответствующую ему символическую константу, которые определяют назначение сообщения. Например, символическая константа WM_PAINT является просьбой на перерисовку содержимого окна.

Префикс символической константы указывает на категорию, к которой принадлежит сообщение. Например:
BM_ элемент управления кнопка
CB_ элемент управления комбинированный список
EM_ элемент управления поле редактирования текста
LB_ элемент управления список
SBM_ элемент управления полоса прокрутки
WM_ оконные сообщения
и т. д.

Основные оконные сообщения покрывают собой широкий диапазон необходимой для работы приложения информации и различных запросов, включая ввод с мыши и клавиатуры, ввод из меню и диалогового окна, управление окном и динамическим обменом данных (DDE).

Сообщения, определяемые приложением

Приложение может создавать свои сообщения для работы со своими окнами или для установки связи с окнами других процессов.

Используются следующие идентификаторы сообщений:
1) система резервирует для себя идентификаторы в диапазоне от 0х0000 до 0х03FF (это WM_USER - 1) для системных сообщений. Приложения не могут использовать эти значения для создания своих личных сообщений.
2) Значения в диапазоне от 0х0400 (это WM_USER) до 0х7FFF могут использоваться для идентификаторов личных сообщений приложения.
3) Если ваше приложение отмечено версией 4.0 вы можете использовать значения идентификаторов от 0х8000 (WM_APP) до 0хBFFF.
4) Система возвращает идентификатор сообщения в диапазоне от 0хС000 до 0хFFFF при вызове приложением функции RegisterWindowMessage для регистрации нового сообщения. Идентификатор, возвращаемый этой функцией, гарантирует его уникальность во всей системе. Использование этой функции предотвращает конфликты, которые могут возникнуть, если какие-либо приложения используют одни и те же идентификаторы в разных целях.

Маршрутизация сообщений

Система использует два пути, для того чтобы доставить сообщение оконной процедуре:
1) сгенерированное сообщение помещается в очередь, называемой ОЧЕРЕДЬЮ СООБЩЕНИЙ. Сообщения перемещаются в этой очереди по принципу - первый вошел - первый вышел. Сообщения, помещаемые в очередь, называют АСИНХРОННЫЕ СООБЩЕНИЯ. Такие сообщения, в основном, являются результатом ввода данных, введенных пользователем через клавиатуру или мышь.
2) Система временно сохраняет сообщение в памяти как системный объект, а затем передает его напрямую оконной процедуре. Сообщения, посылаемые напрямую оконной процедуре, называются СИНХРОННЫМИ СООБЩЕНИЯМИ.

Асинхронные сообщения

Система может в один и тот же момент времени отображать любое количество окон. При перемещении мыши над каким-либо окном или вводе данных в это окно с клавиатуры система использует очередь сообщений.

Система управляет одной системной очередью и одной из очередей сообщений GUI потоков. Для того чтобы не тратить время на создание очередей сообщений для не GUI потоков, все потоки изначально создаются без очередей сообщений (т. е. при создании нового потока, система предполагает, что он не будет иметь отношения к поддержке пользовательского интерфейса). Очередь сообщений потока создается только при первом вызове потоком какой-либо пользовательской или GDI функции. Если процесс создает три потока и все эти потоки вызывают, например функцию CreateWindow, то и очередей сообщений потоков тоже будет три.

Каждый раз, когда пользователь перемещает мышь, нажимает кнопки мыши или набирает текст на клавиатуре, драйвер мыши или клавиатуры преобразует этот ввод в сообщения и размещает их в системной очереди сообщений. Система, удаляя сообщение из системной очереди, проверяет какому окну предназначено сообщение и, затем, перемещает его в очередь сообщений потока, который создал указанное в сообщении окно. Поток, удаляя сообщение из своей очереди, просит систему передать его соответствующей оконной процедуре для обработки.

Асинхронное сообщение помещается в очередь сообщений путем заполнения структуры MSG. В нее записывается описатель окна, которому предназначается сообщение, идентификатор этого сообщения, два его параметра, время появления сообщения и позицию курсора мыши. Поток может поместить сообщение в свою очередь сообщений, или очередь другого потока, используя функцию PostMessage или PostThreadMessage.

При проверки сообщений приложение может удалить сообщение из своей очереди сообщений используя функцию GetMessage. Проверка сообщения без его удаления из очереди сообщений осуществляется функцией PeekMessage.

После удаления сообщения из своей очереди приложение может использовать функцию DispatchMessage, которая заставляет систему передать сообщение нужной оконной процедуре для его обработки. Этой функции необходимо передать указатель на структуру MSG, которая была заполнена до этого функцией GetMessage или PeekMessage. DispatchMessage передает оконной процедуре описатель окна, идентификатор сообщения и оба его параметра, но не передает время его появления и позицию курсора на экране в этот момент. Приложение может получить эту информацию вызовом функций GetMessageTime и GetMessagePos во время обработки сообщения.

Поток может использовать функцию WaitMessage, для того, чтобы передать управление другим потокам, пока в его очереди сообщений нет никаких сообщений. Эта функция останавливает работу потока и не возобновляет ее до появления нового сообщения в очереди сообщений потока.

Синхронные сообщения

Синхронные сообщения передаются напрямую оконной процедуре в обход системной очереди и очереди сообщений потока. Обычно эти сообщения высылаются окну для его уведомления о событиях происходящих с ним. Например, когда пользователь активирует новое окно приложения, ему высылается серия из трех сообщений: WM_ACTIVATE, WM_SETFOCUS и WM_SETCURSOR. Эти сообщения уведомляют окно, что оно было активировано, ввод с клавиатуры направляется в него, а курсор находится в его пределах. Так же эти сообщения могут быть результатом вызова некоторых системных функций. Например, сообщение WM_WINDOWPOSCHANGED высылается после использования приложением функции SetWindowPos для перемещения окна по экрану.

Манипулирование сообщениями

Приложение должно обрабатывать и удалять все сообщения в очередях своих потоков. Однопоточное приложение обычно использует цикл обработки сообщений, который располагают в функции WinMain. В многопоточных приложениях такие циклы располагают в каждом потоке, который создает окна.

Цикл сообщений

Простейший цикл сообщений состоит из трех функций: GetMessage, TranslateMessage и DispatchMessage. Заметим, что в случае появления какой-либо ошибки функция GetMessage возвращает -1. В этом случае необходима специальная обработка этой ситуации. Пример цикла сообщений:

MSG msg;
BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
   if (bRet == -1)
   {
      // получение описателя ошибки и, возможно, выход
   }
   else
   {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
   }
}

Функция GetMessage извлекает сообщение из очереди и копирует его в структуру типа MSG. Эта функция возвращает ненулевое значение, но при извлечении сообщения WM_QUIT она возвращает ЛОЖЬ и цикл завершает свою работу. В однопоточных приложениях завершение работы цикла сообщений часто является первым шагом к закрытию этого приложения. Приложение может самостоятельно вызвать завершение этого цикла функцией PostQuitMessage. Она обычно является реакцией на сообщение WM_DESTROY в оконной процедуре главного окна приложения.

Если вы укажете описатель окна во втором параметре функции GetMessage, то функция будет извлекать из очереди сообщения, предназначенные только указанному окну.

Если поток получает ввод с клавиатуры, то цикл сообщений должен иметь функцию TranslateMessage. Система генерирует сообщения виртуальных клавиш (WM_KEYDOWN и WM_KEYUP) при каждом нажатие клавиши пользователем. Сообщение виртуальной клавиши содержит код виртуальной клавиши, который показывает какая, была нажата клавиша (а не ее символьное значение). Функция TranslateMessage переводит сообщение виртуальной клавиши в символьное сообщение (WM_CHAR) и помещает его обратно в очередь сообщений приложения. Это символьное сообщение будет извлечено на следующей итерации цикла сообщения и передано оконной процедуре.

Функция DispatchMessage отсылает сообщение оконной процедуре ассоциированной с описателем окна указанном в структуре MSG. Если описателем окна является HWND_TOPMOST, то функция отправляет сообщение оконным процедурам всех окон верхнего уровня в системе. Если описатель окна установлен в NULL, то функция никуда не отсылает такое сообщение.

Цикл сообщений главного потока приложения располагается после инициализационной части и создания хотя бы одного окна. Начав выполняться, цикл сообщений продолжает извлекать сообщения из очереди сообщений потока и отправлять их в соответствующие окна. Цикл сообщений завершает свою работу при извлечении функцией GetMessage сообщения WM_QUIT.

Вы можете изменить цикл сообщений несколькими способами. Например, вы можете извлекать сообщения из очереди без передачи их оконной процедуре. Это используется, если необходимо передавать сообщения другому окну, а не тому, чей описатель указан в сообщении. Также функция GetMessage может извлекать из очереди только определенные сообщения. Это позволяет обойти очередность FIFO расположения сообщений в очереди.

Приложение, использующее акселераторные клавиши должно переводить клавиатурные сообщения в командные. Для этого цикл сообщения должен содержать вызов функции TranslateAccelerator.

Если поток использует немодальное диалоговое окно, цикл сообщений должен содержать функцию IsDialogMessage, которая позволяет диалоговому окну получать ввод с клавиатуры.

Оконная процедура

Оконная процедура это функция, которая получает и обрабатывает все сообщения адресованные ее окнам. Каждый оконный класс имеет оконную процедуру и каждое окно, созданное на основе такого класса, использует эту процедуру для реагирования на предназначенные для них сообщения.

Система передает сообщение оконной процедуре путем инициализации ее параметров. Оконная процедура, для того чтобы выполнить соответствующее сообщению действие, проверяет идентификатор сообщение и в процессе выполнения этих действий использует информацию из параметров сообщения.

Если оконная процедура не должна обрабатывать некоторые сообщения, то такие сообщения должны возвращаться оконной процедурой обратно в систему для их обработки по умолчанию. Для этого оконная процедура должна использовать функцию DefWindowProc, которая обрабатывает сообщение по умолчанию и возвращает результат этой обработки. Оконная процедура должна вернуть это значение при своем завершении как результат своих действий над полученным ею сообщением. Большинство оконных процедур обрабатывает только несколько сообщений а остальные отдает системе вызовом функции DefWindowProc.

Так как все окна одного оконного класса используют одну и туже оконную процедуру, то ей приходиться отвечать на сообщения различных окон. Для того чтобы оконная процедура отличала сообщения для разных окон, она должна проверять описатели окон поступающих сообщений.

Фильтрация сообщений

Приложение может выбирать определенные сообщения для извлечения из очереди (игнорируя при этом другие сообщения) указав фильтр сообщений в функции GetMessage или PeekMessage. Фильтр - это диапазон допустимых идентификаторов сообщений (указывается первый и последний идентификаторы), оконных описателей или и того и другого.

Константы WM_KEYFIRST и WM_KEYLAST могут быть использованы для извлечения всех сообщений клавиатуры. Константы WM_MOUSEFIRST и WM_MOUSELAST могут быть использованы для извлечения всех сообщений мыши.

При использовании фильтра убедитесь, что нужные для извлечения сообщения будут появляться в очереди в течение работы приложения. Например, если приложение извлекает из очереди только сообщения WM_CHAR, а ввода с клавиатуры не происходит, то функция GetMessage не вернет управления, что приведет к "зависанию" приложения.

Помещение сообщений в очередь
и передача их напрямую оконной процедуре

Добавление сообщения в очередь осуществляется функцией PostMessage. Для отправки сообщения напрямую оконной процедуре используются функции SendMessage, BroadcastSystemMessage, SendMessageCallback, SendMessageTimeout, SendNotifyMessage и SendDlgItemMessage.

Добавление сообщений в очередь

Обычно сообщения добавляются приложением в очередь для того, чтобы известить какое-либо окно о необходимости выполнить им какой-либо задачи. Функция PostMessage создает структуру MSG для сообщения и копирует ее содержимое в очередь сообщений. Со временем цикл сообщений извлечет это сообщение из очереди и отправит его соответствующей оконной процедуре на обработку.

Сообщения можно добавлять в очередь и без указания определенного окна. Если в качестве описателя окна в функции PostMessage указать NULL, то сообщение будет добавлено в очередь текущего потока. Так как не указан описатель окна, то такое сообщение должно обрабатываться в цикле сообщений. Таким образом, создаются сообщения адресуемые целому приложению, а не отдельному его окну.

Иногда может требоваться выслать сообщение всем окнам верхнего уровня в системе. Для этого в функции PostMessage указывается HWND_TOPMOST в качестве описателя окна.

Ошибочно считать, что функция PostMessage всегда может добавить новое сообщение в очередь. Сообщение нельзя добавить в полностью заполненную очередь. Для этого приложение должно проверять результат, возвращаемый функцией PostMessage для того, чтобы убедиться, что сообщение было успешно добавлено в очередь. В противном случае его необходимо попытаться снова добавить в очередь сообщений.

Передача сообщения напрямую оконной процедуре

Обычно, для того чтобы оконная процедура выполнила какую-либо задачу немедленно, сообщение предается ей напрямую. Функция SendMessage передает сообщение оконной процедуре нужного окна. Она дожидается, когда процедура закончит обработку сообщения и возвращает результат этой обработки. Этим способом часто соединяются друг с другом родительские и дочерние окна. Например, родительское окно, имеющее в качестве дочернего окна элемент редактирования текста, может переслать ему текст в виде сообщения. Элемент управления может вернуть обратно родителю измененный пользователем текст.

Функция SendMessageCallback так же предает сообщение оконной процедуре, но немедленно возвращает управление. После того как оконная процедура обработает указанное сообщение, система вызывает указанную функцию обратного вызова.

Иногда может требоваться выслать сообщение всем окнам верхнего уровня в системе. Например, если приложение изменяет системное время, оно должно уведомить об этом все окна верхнего уровня сообщением WM_TIMECHANGE. Это можно сделать функцией SendMessage, указав HWND_TOPMOST в качестве описателя окна. Так же можно отослать сообщение всем приложениям функцией BroadCastSystemMessage, указав при этом BSM_APPLICATIONS в качестве параметра lpdwRecipients.

Функции InSendMessage и InSendMessageEx позволяют оконной процедуре определить, какое из сообщений было отправлено ей другим потоком. Эта возможность используется, если обработка сообщения зависит от его происхождения.

Широковещательные сообщения

Каждое сообщение содержит идентификатор сообщения и два параметра wParam и lParam. Идентификатор сообщения - это уникальное значение, показывающее назначение сообщения. Параметры сообщения предоставляют дополнительную информацию, смысл которой зависит от конкретного сообщения. В параметре wParam, обычно содержится больше информации о сообщении.

Широковещательное сообщение - это сообщение, предназначенное сразу нескольким адресатам системы. Для этого используется функция BroadCastSystemMessage. В ней можно указать одного и более адресатов. Обычно эта возможность используется приложениями установщиками драйверов, сетевыми драйверами и драйверами оборудования системного уровня.

Система обычно использует эти сообщения как реакцию на изменения на уровне драйверов оборудования системного уровня и связанных с этим компонент. Драйвер или связанные с ним компоненты высылают широковещательные сообщения для того, чтобы проинформировать приложения о произошедших изменениях. Например, драйвер гибкого диска может уведомлять приложения при вставлении пользователем диска в привод.

Вы можете отсылать широковещательные сообщения, указав HWND_BROADCAST в функции SendMessage, SendMessageCallback, SendMessageTimeout или SendNotifyMessage.

Приложение получает эти сообщения через оконную процедуру его окон верхнего уровня. Сообщения не могут высылаться дочерним окнам.

Тупиковые ситуации

При работе приложения могут возникать различные тупиковые ситуации. Например при взаимодействии двух потоков в одном из них может быть допущена ошибка, что приводит к нарушению работы второго потока. Здесь же можно привести указанный выше пример о извлечении определенных сообщений из очереди сообщений, но таких сообщений в ней не появляется. В этом случае тупиковую ситуацию вызывает функция GetMessage. Так же при передачи сообщений напрямую оконной процедуре функцией SendMessage в оконной процедуре может быть ошибка приводящая к ее зависанию. В этом случае SendMessage не вернет управлению вызвавшему ее потоку.

Перечень функций, которые могут не вернуть управление потоку:
DialogBox
DialogBoxIndirect
DialogBoxIndirectParam
DialogBoxParam
GetMessage
MessageBox
PeekMessage
SendMessage

Для того чтобы избежать тупиковых ситуаций вы должны подумать об использовании в вашем приложении функций SendNotifyMessage и SendMessageTimeout. Также оконная процедура может определить, что сообщение пришло от другого потока использовав функцию InSendMessage или InSendMessageEx.

Сообщение-запрос

Для разрешения нескольким адресатам выполнить какое-либо действие используются сообщение-запрос. Сообщение-запрос можно сгенерировать, указав значение BSF_QUERY в параметре dwFlags функции BroadCastSystemMessage. Каждый получатель сообщения-запроса должен возвращать TRUE, для того чтобы функция передала сообщение следующему адресату. Если кто-либо из получателей вернет BROADCAST_QUERY_DENY, то широковещание немедленно заканчивается, а функция возвращает ноль.

Функции для работы с сообщениями

BroadCastSystemMessage - отправка сообщений нескольким адресатам.

BroadCastSystemMessageEx - отправка сообщений нескольким адресатам с получением дополнительной информации.

DispatchMessage - передает сообщения оконной процедуре.

GetInputState - определяет есть ли в очереди сообщений потока сообщения о нажатии клавиш мыши или клавиатуры.

GetMessage - извлекает сообщения из очереди сообщений потока.

GetMessageExtraInfo - извлекает дополнительную информацию о сообщении.

GetMessagePos - получает информацию о позиции курсора для последнего сообщения извлеченного функцией GetMessage.

GetMessageTime - получает информацию о времени для последнего сообщения извлеченного функцией GetMessage.

GetQueueStatus - определяет тип сообщений находящихся в очереди сообщений вызывающего потока.

InSendMessage - определяет было ли обрабатываемое оконной процедурой сообщение выслано другим потоком.

InSendMessageEx - определяет было ли обрабатываемое оконной процедурой сообщение выслано другим потоком.

PeekMessage - извлекает сообщения из очереди без их удаления из нее.

PostMessage - помещает сообщение в очередь сообщений потока указанного окна и возвращает управление не дожидаясь его обработки.

PostQuitMessage - показывает системе, что поток сделал запрос на свое завершение.

PostThreadMessage - помещает сообщение в очередь сообщений указанного потока.

RegisterWindowMessage - создает новый идентификатор оконного сообщения, который гарантированно будет уникальным в системе.

ReplyMessage - отвечает на сообщение высланное функцией SendMessage без возвращения управления вызвавшей SendMessage функции.

SendAsyncProc - функция используемая совместно с функцией SendMessageCallback

SendMessage - отправляет сообщение напрямую в оконную процедуру.

SendMessageCallback - отправляет сообщение одному или нескольким окнам. Управление возвращается немедленно.

SendMessageTimeout - передает сообщение одному или нескольким окнам и не возвращает управления, пока сообщение не будет обработано или не истечет указанное время.

SendNotifyMessage - отправляет указанное ей сообщение одному или нескольким окнам. Если окно было создано вызвавшим функцию потоком, то она вызывает оконную процедуру этого окна и не возвращает управление пока оконная процедура не обработает сообщение.

SetMessageExtraInfo - устанавливает дополнительную информацию сообщения для текущего потока.

TranslateMessage - переводит сообщения виртуальных клавиш в символьные сообщения.

WaitMessage - передает управление другим потокам если очередь сообщений пуста.

Структуры для работы с сообщениями

BSMINFO

MSG - структура содержащая информацию о сообщении.

Сообщения используемые при работе с сообщениями

WM_APP - константа, используемая приложениями, помогающая им определить свои личные сообщения. Обычно используется в виде WM_APP + X, где X целое число.

WM_USER - константа, используемая приложениями, помогающая им определить свои личные сообщения, созданные для работы с окнами на основе личных оконных классов. Обычно используется в виде WM_APP + X, где X целое число.

ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ СООБЩЕНИЙ И ОЧЕРЕДЕЙ СООБЩЕНИЙ

Пример 1 - Многие приложения содержат только один поток, создающий окна. Обычно такие приложения регистрируют оконный класс для своего главного окна, создает его и отображает на экране, а затем начинает работу цикл сообщений - и все это в функции WinMain. Пример показывает использование цикла сообщений в функции WinMain.
Пример 2 - Пример использования цикла сообщений для потока, использующего акселераторы и создающего немодальное диалоговое окно.
Пример 3 - Иногда приложению необходимо проверить содержимое очереди сообщений потока при выполнении длительных операций. Для этого можно использовать функцию PeekMessage.
Пример 4 - Пример помещения сообщения в очередь сообщений используя функцию PostMessage. Эта функция размещает сообщение в конце очереди сообщений потока и немедленно возвращает управление без ожидания обработки этого сообщения потоком.
Пример 5 - Передача сообщений напрямую оконной процедуре и элементам управления диалогового окна.

Алексей ЕГОРОВ

содержание: oГлавная
oСООБЩЕНИЯ
oРесурсы
гостевая книга


Hosted by uCoz