Очередь сообщений окна
Очередь сообщений окна — это структура данных операционной системы, используемая в средах с графическим интерфейсом пользователя (GUI), в частности в Microsoft Windows, для хранения и упорядочивания сообщений, адресованных конкретному окну. Очередь сообщений является ключевым элементом модели асинхронной обработки событий, обеспечивающей взаимодействие между приложениями, системой и пользователем. Каждое окно, созданное в рамках одного потока, имеет собственную очередь сообщений, которая управляется диспетчером сообщений (message loop) этого потока.
История
Концепция очередей сообщений возникла в ранних версиях операционной системы Windows (начиная с Windows 1.0, выпущенной в 1985 году) как часть модели кооперативной многозадачности. В этой модели все приложения выполнялись в одном потоке, и очередь сообщений была глобальной для всей системы. Приложения поочерёдно извлекали сообщения из этой очереди, что обеспечивало видимость многозадачности, но приводило к блокировкам при зависании одного из приложений.
С переходом к вытесняющей многозадачности в Windows 95 и Windows NT (середина 1990-х годов) архитектура была изменена. Каждое приложение (точнее, каждый поток, создавший окно) получило собственную очередь сообщений. Это позволило изолировать обработку сообщений разных приложений, повысив стабильность и производительность системы. В современных версиях Windows (Windows 10, Windows 11) очередь сообщений окна остаётся фундаментальным механизмом работы графического интерфейса, хотя её реализация претерпела значительные оптимизации.
Устройство и принцип работы
Основные компоненты
Очередь сообщений окна представляет собой FIFO-буфер (First In, First Out — «первым пришёл, первым обслужен»), в который помещаются структуры данных типа MSG. Каждая структура MSG содержит:
- HWND — дескриптор окна-получателя;
- UINT — идентификатор сообщения (например,
WM_PAINT,WM_LBUTTONDOWN); - WPARAM и LPARAM — дополнительные параметры, зависящие от типа сообщения;
- DWORD — время отправки сообщения;
- POINT — координаты курсора мыши в момент отправки.
Очередь создаётся автоматически при вызове функции CreateWindow или CreateWindowEx для первого окна в потоке, если она ещё не существует. Для каждого потока, создавшего хотя бы одно окно, система выделяет отдельную очередь сообщений.
Цикл обработки сообщений (Message Loop)
Основной механизм работы с очередью — цикл обработки сообщений, который реализуется в функции WinMain или в главном потоке приложения. Типичный цикл на языке C/C++ выглядит так:
``c MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } ``
- GetMessage — извлекает сообщение из очереди потока. Если очередь пуста, функция блокирует выполнение потока до появления нового сообщения. Возвращает
FALSEпри получении сообщенияWM_QUIT, что завершает цикл. - TranslateMessage — преобразует виртуальные коды клавиш в символы (генерирует сообщения
WM_CHAR). - DispatchMessage — отправляет сообщение оконной процедуре (window procedure) окна, указанного в
HWND.
Типы сообщений
Сообщения, помещаемые в очередь, делятся на две основные категории:
- Очередные (queued) сообщения — помещаются в очередь и обрабатываются в порядке поступления. К ним относятся:
- Сообщения от мыши (
WM_MOUSEMOVE,WM_LBUTTONDOWN,WM_MOUSEWHEEL); - Сообщения от клавиатуры (
WM_KEYDOWN,WM_KEYUP,WM_CHAR); - Сообщения таймера (
WM_TIMER); - Сообщения перерисовки (
WM_PAINT); - Сообщения от системы (
WM_QUIT,WM_CLOSE).
- Непосредственные (non-queued) сообщения — отправляются непосредственно оконной процедуре без помещения в очередь. К ним относятся:
- Сообщения, отправленные функциями
SendMessageилиSendMessageTimeout; - Сообщения, связанные с созданием и уничтожением окон (
WM_CREATE,WM_DESTROY); - Сообщения, генерируемые системой при изменении состояния окна (
WM_SIZE,WM_MOVE).
Непосредственные сообщения обрабатываются немедленно, в то время как очередные ожидают своей очереди. Это различие важно для синхронизации: SendMessage блокирует вызывающий поток до завершения обработки, а PostMessage помещает сообщение в очередь и возвращает управление немедленно.
Приоритеты и синхронизация
Система использует механизм приоритетов для обработки сообщений. Сообщения с высоким приоритетом (например, WM_PAINT, WM_TIMER) обрабатываются только тогда, когда в очереди нет других сообщений. Это предотвращает «залипание» интерфейса при интенсивном обновлении. Кроме того, сообщения WM_QUIT имеют наивысший приоритет и обрабатываются вне очереди.
Для синхронизации между потоками используются функции PostThreadMessage (отправляет сообщение в очередь потока, а не окна) и AttachThreadInput (позволяет одному потоку читать очередь другого). Это используется, например, для реализации диалоговых окон и модальных циклов.
Классификация и виды
Очереди сообщений можно классифицировать по нескольким признакам:
По принадлежности
- Очередь потока — одна на каждый поток, создавший окно. Содержит сообщения для всех окон этого потока.
- Очередь окна — логическая очередь, связанная с конкретным окном. Фактически сообщения хранятся в общей очереди потока, но фильтруются по
HWNDпри вызовеGetMessageс указанием дескриптора.
По типу сообщений
- Системная очередь — глобальная очередь, в которую система помещает сообщения от устройств ввода (мышь, клавиатура). Затем эти сообщения распределяются по очередям потоков.
- Прикладная очередь — очередь конкретного потока, содержащая как системные, так и пользовательские сообщения.
По способу отправки
- Очередные сообщения — через
PostMessageилиPostThreadMessage. - Непосредственные сообщения — через
SendMessage(не помещаются в очередь).
Применение
Очередь сообщений окна является основой для всех GUI-приложений в Windows. Она используется для:
- Обработки пользовательского ввода — клики мыши, нажатия клавиш, движения курсора преобразуются в сообщения и помещаются в очередь.
- Управления окнами — создание, перемещение, изменение размера, закрытие окон сопровождаются сообщениями (
WM_CREATE,WM_SIZE,WM_CLOSE). - Таймеров и анимации — сообщения
WM_TIMERгенерируются системой через заданные интервалы. - Межпроцессного взаимодействия — через сообщения можно передавать данные между окнами разных приложений (например,
WM_COPYDATA). - Обновления интерфейса — сообщение
WM_PAINTинициирует перерисовку окна.
Критика и ограничения
Несмотря на свою эффективность, модель очередей сообщений имеет ряд недостатков:
- Блокировка потока — если поток занят длительной обработкой, очередь может переполниться, и интерфейс «зависнет». Это требует использования многопоточности или асинхронных операций.
- Сложность отладки — асинхронная природа сообщений затрудняет воспроизведение и диагностику ошибок, связанных с порядком обработки.
- Производительность — при большом количестве сообщений (например, при интенсивном движении мыши) очередь может создавать нагрузку на процессор.
- Ограниченная масштабируемость — модель плохо подходит для современных высоконагруженных приложений с множеством окон, где требуется параллельная обработка.
В современных фреймворках (например, Windows Presentation Foundation, WPF) очередь сообщений остаётся в основе, но дополняется более высокоуровневыми механизмами, такими как диспетчеры (Dispatcher) и привязки данных (data binding).
Примеры
Пример 1: Простейшее приложение с циклом сообщений
```c
include <windows.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // Регистрация и создание окна (опущено для краткости) // ... MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } ```
Пример 2: Отправка пользовательского сообщения
```c // В одном потоке PostMessage(hwnd, WM_USER + 1, 0, 0); // Помещает сообщение в очередь
// В оконной процедуре case WM_USER + 1: // Обработка пользовательского сообщения break; ```
Интересные факты
- В ранних версиях Windows (до 3.0) очередь сообщений была глобальной, что приводило к тому, что одно зависшее приложение могло заблокировать всю систему.
- Сообщение
WM_QUITне помещается в очередь, а обрабатывается специальным образом —GetMessageвозвращаетFALSE, завершая цикл. - В Windows 95 была введена «очередь ввода» (input queue), которая разделяет сообщения от устройств ввода и сообщения приложений, что повысило отзывчивость системы.
- Существует понятие «модального цикла» (modal loop), который используется, например, в диалоговых окнах: он обрабатывает сообщения только для дочерних окон, блокируя остальные.
Источники
- Charles Petzold. Programming Windows, Fifth Edition. Microsoft Press, 1998.
- Jeffrey Richter. Windows via C/C++. Microsoft Press, 2007.
- Microsoft Docs. About Messages and Message Queues (Windows Desktop App Development).
- Raymond Chen. The Old New Thing: Practical Development Throughout the Evolution of Windows. Addison-Wesley, 2006.
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →