воскресенье, 23 ноября 2008 г.

Перехват сообщений в Win32 (Intercepting Messages in Win32)

Windows является операционной системой, основанной на событиях. Ничто не произойдет пока событие не будет отправлено приложению, сообщая, что в системе что-то произошло. Примерами событий, генерирующих сообщения, являются:
  • Перемещение мыши
  • Клавиатура
  • Выбор элемента меню/нажатие кнопки
  • Перемещение окна

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

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

Перехват сообщений приложения

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

Для начала, рассмотрим стандартные процедуры обработки сообщений, использующие функцию GetMessage. Цикл сообщений функции WinMain обычно выглядит следующим образом
while (GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

GetMessage ждет до тех пор пока не появятся сообщения. Все время сообщения прибывают, транслируются и отправляются оконной процедуре. Это стандартная процедура - поток возвращает результат Windows (в вызове GetMessage), и если мы вставим какой-либо код в данный цикл, то это может привести к задержке в обработке.

Однако, GetMessage удаляет сообщения из очереди. Для предотвращения удаления из очереди сообщений, следует использовать PeekMessage
BOOL PeekMessage(
LPMSG msg, // Сообщение
HWND hWnd, // Хэндл окна
UINT wFilterMin, // Начальное сообщение
UINT wFilterMax, // Конечное сообщение
UINT wRemove // PM_REMOVE или PM_NOREMOVE
);

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

Рассмотрим следующий вызов:
BOOL bMessage = PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);

Вызов даёт возможность просматривать сообщения данного приложения, но, тем не менее, возможность просматривать очередь сообщений другого приложения отсутствует. Но мы можем сделать следующее
BOOL bMessage = PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE);

Происходит отфильтровка сообщений мыши.
BOOL bMessage = PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE);

Происходит отфильтровка сообщений клавиатуры.

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