DWORD GetQueueStatus(UINT fuFlags);
Флаг |
Сообщение в очереди |
QS_KEY |
WM_KEYUP,WM_KEYDOWN, WM_SYSKEYUP или WM_SYSKEYDOWN |
QS_MOUSEMOVE |
WM_MOUSEMOVE |
QS_MOUSEBUTTON |
WM_?BUTTON* (где знак вопроса заменяет букву L, М или R, а звездочка — DOWN, UP или DBLCLK) |
QS_MOUSE |
То же, что QS_MOUSEMOVE | QS_MOUSEBUTTON |
QS_INPUT |
То же, что QS_MOUSE | QS_KEY |
QS_PAINT |
WM_PAINT |
QS_TIMER |
WM_TIMER |
QS_HOTKEY |
WM_HOTKEY |
QS_POSTMESSAGE |
Асинхронное сообщение (отличное от события аппаратного ввода), этот флаг идентичен QS_ALLPOSTMESSAGE с тем исключением, что сбрасывается при отсутствии асинхронных сообщений в диапазоне действия фильтра сообщений |
QS_ALLPOSTMESSAGE |
Асинхронное сообщение (отличное от события аппаратного ввода); этот флаг идентичен QS_POSTMESSAGE с тем исключением, что сбрасывается лишь при полном отсутствии каких-либо асинхронных сообщений (вне зависимости от фильтра сообщений) |
QS ALLEVENTS |
Тоже, что QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY |
QS_QUIT |
Сообщает о вызове PostQuitMessage, этот флаг не задокументирован, его нет в WinUser.h, и он используется самой системой |
QS_SENUMESSAGE |
Синхронное сообщение, посланное другим потоком |
QS_ALLINPUT |
То же, что QS_ALLEVENTS | QS_SENDMESSAGE |
Не все флаги обрабатываются одинаково. Например:
Флаги QS_MOUSE и QS_KEY устанавливаются, если в очереди есть хотя бы одно соответствующее сообщение. Когда GetMessage или PeekMessage извлекает последнее сообщение из очереди, соответствующие флаги сбрасываются.
Флаг QS_MOUSEMOVE устанавливается, если в очереди есть необработанное сообщение WM_MOUSE.
Флаг QS_PAINT обрабатывается иначе. Если существует хотя бы один недействительный регион (требующий перерисовки), флаг не сбрасывается. Флаг сбрасывается в случае окончательной перерисовки.
Есть еще один (недокументированный) флаг состояния очереди – QS_QUIT. Он устанавливается при вызове потоком PostQuitMessage. Сообщение WM_QUIT при этом не добавляется к очереди сообщений. GetQueueStatus не возвращает состояние этого флага.
Алгоритм выборки сообщений из очереди потока:
Когда поток вызывает GetMessage или PeekMessage, система проверяет флаги состояния очередей потока и определяет, какое сообщение надо обработать:
До появления Win32 API в ОС использовался упорядоченный ввод (serialized input), т.е. система обрабатывала события от клавиатуры и мыши в том порядке, в каком они инициируются пользователем.
Очередь ввода была одна, поэтому при зависании обработки нажатия клавиши могла зависнуть вся ОС.
В Win32 API используется разупорядоченный ввод (deserialized input). В этом случае аппаратные события не обязательно обрабатываются в порядке их поступления.
Поток принимает события в том порядке, в каком пользователь их формирует, но обрабатывает на уровне потока, а не на уровне ОС. При запуске ОС создает особый поток необработанного ввода (raw input thread, RIT).
Чтобы система могла переключаться между задачами, вне зависимости от того занят ли поток в данный момент обработкой ввода, RIT особым образом реагирует на специальные комбинации клавиш (например, Alt+Ctrl+Del или Alt+Tab).
Когда на клавиатуре нажимают или отпускают клавишу или передвигают мышь соответствующий драйвер устройства добавляет аппаратное событие в очередь RIT.
В результате поток RIT пробуждается и транслирует его в событие WM_*.
После этого RIT отправляет событие в виртуальную очередь ввода потока.
Прежде чем отправить событие, RIT должен определить поток в чью виртуальную очередь ввода необходимо отправить событие.
В случае сигнала от мыши RIT определяет, поверх какого окна находится курсор мыши, после чего отправляет событие потоку, создавшему это окно.
Если событие поступило от клавиатуры, RIT определяет, какое окно активно(находится в фокусе ввода) и все повторяется.
В Win32 API существует ряд функций, которые позволяют объединять и разъединять виртуальные очереди ввода.
Можно заставить два и более потока совместно использовать одну и ту же очередь виртуального ввода и переменные локального состояния ввода с помощью функции:
BOOL AttachThreadInput(DWORD idAttach,
DWORD idAttachTo, BOOL fAttach);
Параметр idAttach задаст идентификатор потока, чьи переменные локального со стояния ввода и очередь виртуального ввода больше не нужны, а параметр idAttachTo – идентификатор потока, чьи переменные локального состояния ввода и виртуальная очередь ввода должны совместно использоваться потоками
Параметр fAttach должен быть или TRUE, чтобы инициировать совместное использование одной очереди, или FALSE – тогда каждый поток будет вновь использовать свои переменные состояния ввода и очередь.
При вводе с клавиатуры RIT отправляет событие в виртуальную очередь ввода потока, безотносительно конкретному окну. Когда поток получает событие функцией GetMessage, клавиатурное событие извлекается и связывается с окном, которое находится в фокусе.
Функция
HWND WINAPI SetFocus(HWND hWnd);
назначает фокус ввода окну hWnd. Функция возвращает описатель окна, которое находилось в фокусе. Если фокус не был передан, функция возвращает NULL.
Изменение фокуса ввода приводит к тому, что окну теряющему фокус отправляется сообщение WM_KILLFOCUS, а окну, которое принимает фокус ввода – WM_SETFOCUS.
Данная функция не переключает виртуальные очереди ввода, поэтому если поток 1, будучи подключенным к RIT, вызовет SetFocus для окна E, то не произойдет никаких изменений. Кроме того, если поток 2 вызовет SetFocus для окна E, то при следующем подключении к RIT именно это окно будет в фокусе ввода.
В Win32 API появились две новые функции, которые управляют фокусом ввода.
BOOL WINAPI SetForegroundWindow(HWND hWnd);
Функция активизирует окно hWnd и переключает виртуальную очередь ввода на поток, который создал это окно.
HWND WINAPI GetForegroundWindow();
Функция возвращает активное окно, вне зависимости от того, какой поток его создал.