Windows для профессионалов


Алгоритм выборки сообщений из очереди потока


Когда поток вызывает GetMessage или PeekMessage, система проверяет флаги состоя ния очередей потока и определяет, какое сообщение надо обработать (рис 26-2)

1. Если флаг QS_SENDMESSAGE установлен, система отправляет сообщение соот ветствующей оконной процедуре GetMessage и PeekMessage контролируют процесс обработки и пе передают управление потоку сразу после того, как оконная процедура обработает сообщение, вместо этого обе функции ждут следующего сообщения.

2. Если очередь асинхронных сообщений потока не пуста, GetMessage и Peek Message заполняют переданную им структуру MSG и возвращают управление Цикл выборки сообщений (расположенный в потоке) в этот момент обычно обращается к DispatchMessage, чтобы соответствующая оконная процедура об работала сообщение.

3. Если флаг QS_QUIT установлен, GetMessage и PeekMessage возвращают сообще ние WM__QUIT (параметр wParam которого содержит указанный код заверше ния) и сбрасывают этот флаг.

4 Если в очереди виртуального ввода потока есть какие-то сообщения, GetMessage и PeekMessage возвращают сообщение, связанное с аппаратным вводом.

5. Если флаг QS_PAINT установлен, GetMessage и PeekMessage возвращают сооб щение WM_PAINT для соответствующего окна

6 Если флаг QS_TIMER установлен, GetMessage и PeekMessage возвращают сооб щение WM_TIMER.

Рис. 26-2 Алгоритм выборки сообщений из очереди потока

Хоть и трудно в это поверить, но для такого безумия есть своя причина. Главное, из чего исходила Microsoft, разрабатывая описанный алгоритм, — приложения долж ны слушаться пользователя, и именно его действия (с клавиатурой и мышью) управ ляют программой, порождая события аппаратного ввода Работая с программой, поль зователь может нажать кнопку мыши, что приводит к генерации последовательности определенных собьний. А программа порождает отдельные события, асинхронно отправляя сообщения в очсрсдь потока

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


И именно поэтому очередь асинхронных сообщений проверяется раньше очереди виртуального ввода

Прекрасный пример такой последовательности событий — вызов функции Trans lateMessage, проверяющей, не было ли выбрано из очереди ввода сообщение WM_KEY DOWN или WM_SYSKEYDOWN. Если одно из этих сообщений выбрано, система про веряет, можно ли преобразовать информацию о виртуальной клавише в символьный эквивалент. Если это возможно, TranslateMessage вызывает PostMessage, чтобы помес тить в очередь асинхронных сообщений WM_CHAR или WM_SYSCHAR При следую щем вызове GetMessage система проверяет содержимое очереди асинхронных сооб щений и, если в ней есть сообщение, извлекает его и возвращает потоку. Возвращает ся либо WM_CHAR, либо WM_SYSCHAR. При следующем вызове GetMessage система обнаруживает, что очсрсдь асинхронных сообщений пуста Тогда она проверяет оче редь ввода, где и находит сообщение WM_(SYS)KEYUP; именно оно и возвращается функцией GetMessage.

Поскольку система устроена так, а нс иначе, последовательность аппаратных со бытий:

WM_KEYDOWN
WM_KEYUP

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

WM_KEYDOWN
WM_CHAR
WM_KEYUP

Вернемся к тому, как система решает, что за сообщение должна вернуть функций GctMessage или PeekMessage. Просмотрев очередь асинхронных сообщений, система, прежде чем перейти к проверке очереди виртуального ввода, проверяет флаг QS_QUIT Вспомните этот флаг устанавливается, когда поток вызывает PostQuitMessage. Вызов PostQuitMcssage дает примерно ют же эффект, что и вызов PostMessage, которая поме щает сообщение в конец очереди и тем самым заставляет обрабатывать его до про верки очереди ввода Так почему же PostQuitMessage устанавливает флаг вместо того, чтобы поместить WM_QUIT в очередь сообщений? На то есть две причины.

Во-первых, в условиях нехватки памяти может получиться так, что асинхронное сообщение нс удастся поместить в очередь Но, если приложение хочет завершиться, оно должно завершиться — тем более при нехватке памяти.


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

те кода сообщение WM_USER будет извлечено до WM_QUIT, даже если WM_USER асин хронно помещено в очередь после вызова PostQuitMessage.

case WM_CLOSE:

PostQuitMessage(0);
PostMessage(hwnd, WM_USER, 0, 0);

А теперь о последних двух сообщениях: WM_PAINT и WM_TIMER. Сообщение WM_PAINT имеет низкий приоритет, так как прорисовка экрана — операция не са мая быстрая. Если бы это сообщение посылалось всякий раз, когда окно становится недействительным, быстродействие системы снизилось бы весьма ощутимо. Но по мещая WM_PAINT после ввода с клавиатуры, система работает гораздо быстрее. На пример, из меню можно вызвать какую-нибудь команду, открывающую диалоговое окно, выбрать в нем что-то, нажать клавишу Enter — и проделать все это даже до того, как окно появится на экране. Достаточно быстро нажимая клавиши, Вы наверняка за метите, что сообщения об их нажатии извлекаются прежде, чем дело доходит до со общений WM_PAINT. А когда Вы нажимаете клавишу Enter, подтверждая тем самым значения параметров, указанных в диалоговом окне, система разрушает окно и сбра сывает флаг QS_PAINT

Приоритет WM_TIMER еще ниже, чем WM_PAINT. Почему? Допустим, какая-то программа обновляет свое окно всякий раз, когда получает сообщение WM_TIMER. Если бы оно поступало слишком часто, программа просто не смогла бы обновлять свое окно Но поскольку сообщения WM_PAINT обрабатываются до WM_TIMER, такая проблема не возникает.

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


Содержание раздела