05. Механизм сообщений в операционных системах
Лекция №5. Механизм сообщений в операционных системах
Курс: Системное программирование (2026–2027)
Учебная программа: 2025, регистрационный № УП-46/2025Пп/уч
Специальность переподготовки: 9-09-0612-02 «Программное обеспечение информационных систем»
Квалификация: Инженер-программист
Тема по программе: Тема 5. Механизм сообщений в операционных системах (4 академических часа)
Формируемые компетенции: СП-23, СП-24
Введение и цели занятия
📌 Слайд 1: Тема 5. Механизм сообщений в операционных системах
В традиционном консольном программировании поток выполнения строго линеен и монологичен: программа сама последовательно определяет, когда запрашивать данные от пользователя (например, через блокирующий вызов scanf или cin), когда производить вычисления и когда выводить текстовый результат. Однако современный графический интерфейс пользователя (GUI) построен на фундаментально иной архитектурной парадигме — событийно-ориентированном программировании (Event-Driven Programming) и инверсии управления (Inversion of Control).
В операционной системе Windows пользовательский интерфейс управляется непрерывным потоком дискретных событий — сообщений (Messages). Любое физическое действие пользователя (перемещение манипулятора «мышь», нажатие кнопки мыши, нажатие клавиши на клавиатуре, изменение размеров окна, вставка флеш-накопителя) или системное событие (срабатывание таймера, необходимость перерисовки окна, завершение сетевой операции) преобразуется операционной системой в стандартизированную структуру сообщения и направляется в очередь соответствующего потока.
Цель настоящей лекции — всесторонне изучить архитектуру оконной подсистемы операционной системы, регистрацию и жизненный цикл оконных классов (WNDCLASSEXW), устройство очередей сообщений потока, механизм взаимодействия Raw Input Thread с пользовательскими окнами, внутреннее устройство цикла выборки (GetMessage / PeekMessage), различие синхронного (SendMessage) и асинхронного (PostMessage) обмена данными, а также системные интерфейсы низкоуровневой обработки событий мыши и клавиатуры.
📌 Слайд 2: План лекции
Учебные вопросы лекции:
- Парадигмы консольного и графического пользовательского интерфейса. Инверсия управления.
- Архитектура класса окна в операционной системе Windows (
WNDCLASSEXW) и создание дескриптораHWND. - Предопределенные оконные классы операционной системы и динамическое управление данными окна (
GetWindowLongPtrW/SetWindowLongPtrW). - Понятие оконного сообщения. Структура
MSG, параметрыWPARAMиLPARAM, классификация и диапазоны идентификаторов. - Архитектура очередей сообщений: системный поток необработанного ввода (Raw Input Thread, RIT), потоковая очередь и синтетические флаги.
- Синхронное (
SendMessage) и асинхронное (PostMessage) взаимодействие. Главный цикл выборки сообщений (GetMessage,TranslateMessage,DispatchMessage). - Оконная процедура (
Window Procedure) и функция обработки по умолчанию (DefWindowProcW). - Системные интерфейсы обработки мышиного ввода: клиентские и неклиентские сообщения, хит-тестинг (
WM_NCHITTEST), захват мыши (SetCapture). - Системные интерфейсы обработки клавиатурного ввода: скан-коды, виртуальные коды клавиш (
Virtual-Key), фокус ввода и генерация текстовых символовWM_CHAR.
1. Парадигмы консольного и оконного интерфейса
📌 Слайд 3: Консольная парадигма против событийного оконного интерфейса
Исторически первой моделью взаимодействия человека с ЭВМ являлась консольная (командно-строчная) парадигма:
- Линейный детерминированный поток управления: программа имеет единую точку входа
main(), последовательно инициализирует ресурсы, выполняет алгоритм и завершает работу. - Блокирующий синхронный ввод: когда программе требуются входные данные, она вызывает блокирующую функцию (
scanf,getchar,ReadFile(hStdIn)). Поток переводится ядром ОС в состояние ожидания (Blocked) до тех пор, пока пользователь не нажмет клавишу Enter. Во время этого ожидания приложение не может реагировать ни на какие другие действия.
В оконных графических операционных системах такая модель неприменима. Пользователь может в произвольный момент времени щелкнуть мышью по кнопке, свернуть окно, перетащить его на другой монитор или нажать сочетание клавиш. Графическая подсистема обязана мгновенно реагировать на любые действия пользователя.
Для решения этой задачи графические ОС используют событийно-ориентированную парадигму:
- Инверсия управления (Inversion of Control, IoC): программист больше не управляет главным потоком инструкций. Главным потоком управляет операционная система, которая опрашивает устройства ввода и вызывает зарегистрированные программистом функции обратного вызова (Callbacks). В программировании этот принцип известен как «Голливудский принцип» (Hollywood Principle): «Не звоните нам, мы сами вам перезвоним».
- Цикл обработки сообщений (Message Loop): поток создает одно или несколько окон и входит в бесконечный цикл выборки сообщений из своей системной очереди.
- Неблокирующая диспетчеризация: обработка каждого отдельного сообщения должна занимать минимальное время (миллисекунды), чтобы окно не теряло интерактивности и не переходило в состояние «Не отвечает» (Not Responding).
📌 Слайд 4: Парадигмы программирования: Консоль vs Событийно-ориентированный GUI

2. Архитектура класса окна и создание экземпляра HWND
📌 Слайд 5: Архитектура оконного класса: WNDCLASSEXW
В операционной системе Windows окно не может быть создано «из ничего». Любое окно является конкретным экземпляром предварительно зарегистрированного класса окна (Window Class). Класс окна задает фундаментальные свойства и атрибуты, общие для всех окон данного типа, важнейшим из которых является указатель на функцию обработки сообщений — оконную процедуру.
Регистрация класса окна в подсистеме USER32 выполняется заполнением структуры WNDCLASSEXW и вызовом функции RegisterClassExW:
1#include <windows.h>
2
3// Прототип оконной процедуры
4LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
5
6// Регистрация класса окна
7ATOM RegisterMainWindowClass(HINSTANCE hInstance) {
8 WNDCLASSEXW wc;
9 ZeroMemory(&wc, sizeof(wc));
10
11 wc.cbSize = sizeof(WNDCLASSEXW);
12 // CS_HREDRAW | CS_VREDRAW: полная перерисовка окна при изменении ширины или высоты
13 wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
14 wc.lpfnWndProc = MainWindowProc; // Главный обработчик сообщений
15 wc.cbClsExtra = 0; // Дополнительная память класса
16 wc.cbWndExtra = sizeof(void*); // Дополнительная память окна (под указатель)
17 wc.hInstance = hInstance; // Дескриптор модуля
18 wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); // Стандартная пиктограмма
19 wc.hCursor = LoadCursorW(NULL, IDC_ARROW); // Стандартный курсор-стрелка
20 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // Кисть фона клиентской области
21 wc.lpszMenuName = NULL; // Имя ресурса меню
22 wc.lpszClassName = L"SystemProgramming_MainWindow"; // Уникальное строковое имя
23 wc.hIconSm = LoadIconW(NULL, IDI_APPLICATION); // Малая пиктограмма
24
25 return RegisterClassExW(&wc);
26}
📌 Слайд 6: Архитектура оконного класса и создание окна (HWND)

После успешной регистрации класса приложение может создать любое количество независимых экземпляров окон вызовом функции CreateWindowExW:
1HWND hwnd = CreateWindowExW(
2 WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, // Расширенные стили окна
3 L"SystemProgramming_MainWindow", // Имя зарегистрированного класса
4 L"Системное программирование 2026", // Заголовок окна
5 WS_OVERLAPPEDWINDOW, // Базовые стили (заголовок, рамка, кнопки свернуть/закрыть)
6 CW_USEDEFAULT, CW_USEDEFAULT, // Координаты X, Y на экране
7 800, 600, // Ширина и высота окна
8 NULL, // Дескриптор родительского окна (Parent)
9 NULL, // Дескриптор меню
10 hInstance, // Дескриптор экземпляра приложения
11 NULL // Пользовательский параметр (lpParam)
12);
13
14if (hwnd == NULL) {
15 DWORD error = GetLastError();
16 // Обработка ошибки создания окна...
17 return 0;
18}
19
20// Отображение и первичная перерисовка окна на экране
21ShowWindow(hwnd, SW_SHOWDEFAULT);
22UpdateWindow(hwnd);
Важнейшее системное правило: дескриптор
HWNDжестко привязан к потоку операционной системы, который вызвал функциюCreateWindowExW. Очередь сообщений этого окна создается в контексте данного потока, и цикл выборки сообщений обязан исполняться именно в этом потоке. Вызов большинства графических функций из другого потока напрямую нарушает потоковую модель Windows.
3. Предопределенные классы и динамические данные окна
📌 Слайд 7: Предопределенные оконные классы и элементы управления
В операционной системе Windows все стандартные графические элементы управления (кнопки, поля ввода, списки, надписи) сами являются полноценными окнами (HWND), созданными на базе встроенных предопределенных классов окон:
| Имя класса | Тип элемента управления | Основные стили и особенности |
|---|---|---|
L"BUTTON" | Кнопки, переключатели, флажки | BS_PUSHBUTTON, BS_CHECKBOX, BS_RADIOBUTTON |
L"EDIT" | Однострочные и многострочные текстовые поля | ES_LEFT, ES_MULTILINE, ES_AUTOVSCROLL, ES_PASSWORD |
L"STATIC" | Текстовые метки, рамки, статические изображения | SS_LEFT, SS_CENTER, SS_BITMAP, SS_NOTIFY |
L"LISTBOX" | Список элементов с возможностью выбора | LBS_STANDARD, LBS_MULTIPLESEL, LBS_SORT |
L"COMBOBOX" | Выпадающий комбинированный список | CBS_DROPDOWN, CBS_DROPDOWNLIST, CBS_AUTOHSCROLL |
L"SCROLLBAR" | Полосы прокрутки | SBS_HORZ, SBS_VERT |
Создание дочернего элемента управления сводится к вызову CreateWindowExW с указанием предопределенного класса и стиля WS_CHILD | WS_VISIBLE:
1HWND hButton = CreateWindowExW(
2 0, L"BUTTON", L"Вычислить",
3 WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
4 50, 50, 120, 35,
5 hwndParent, (HMENU)IDC_MY_BUTTON, hInstance, NULL);
📌 Слайд 8: Дополнительные данные окна и класса: GetWindowLongPtrW
Оконная процедура WndProc в языке Си обязана быть статической функцией или свободной функцией с соглашением вызова CALLBACK (__stdcall). Однако в объектно-ориентированном программировании (C++) логика окна оформляется в виде класса (например, class Window).
Для связывания системного дескриптора HWND с указателем на экземпляр класса C++ (this) Windows предоставляет механизм хранения пользовательских данных в заголовке окна ядра:
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)pMyClass)— сохраняет 64-битный указатель на объект приложения в защищенной структуре окна.GetWindowLongPtrW(hwnd, GWLP_USERDATA)— извлекает сохраненный указатель внутри статической оконной процедуры.
1// Передача указателя this при создании окна:
2CreateWindowExW(..., (LPVOID)this);
3
4// Внутри оконной процедуры при сообщении WM_CREATE:
5LRESULT CALLBACK GlobalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
6 if (uMsg == WM_CREATE) {
7 CREATESTRUCTW *pCreate = (CREATESTRUCTW*)lParam;
8 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)pCreate->lpCreateParams);
9 }
10
11 MyWindow *pWnd = (MyWindow*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
12 if (pWnd) {
13 return pWnd->HandleMessage(uMsg, wParam, lParam);
14 }
15 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
16}
4. Понятие оконного сообщения и структура MSG
📌 Слайд 9: Анатомия оконного сообщения: структура MSG
Любое событие в оконной подсистеме Windows упаковывается в стандартизированную системную структуру MSG:
1typedef struct tagMSG {
2 HWND hwnd; // Дескриптор окна, которому адресовано сообщение
3 UINT message; // 32-битный числовой идентификатор типа сообщения (ID)
4 WPARAM wParam; // Дополнительный параметр (32 бита на x86, 64 бита на x64)
5 LPARAM lParam; // Дополнительный параметр (32 бита на x86, 64 бита на x64)
6 DWORD time; // Системное время генерации сообщения (в миллисекундах)
7 POINT pt; // Экранные координаты курсора мыши в момент генерации
8} MSG;
Смысл параметров wParam и lParam зависит исключительно от кода сообщения message:
- При сообщении
WM_SIZE:wParamсодержит флаг изменения размера (SIZE_RESTORED,SIZE_MAXIMIZED), аlParamупаковывает ширину и высоту клиентской области: ширина в младшем словеLOWORD(lParam), высота в старшемHIWORD(lParam). - При сообщении
WM_MOUSEMOVE/WM_LBUTTONDOWN:wParamсодержит битовые флаги нажатых кнопок мыши и клавиш-модификаторов (MK_LBUTTON,MK_CONTROL,MK_SHIFT), аlParam— координаты курсора относительно верхнего левого угла клиентской области: $X = \text{GET_X_LPARAM}(lParam)$, $Y = \text{GET_Y_LPARAM}(lParam)$. - При сообщении
WM_COMMAND:LOWORD(wParam)содержит идентификатор элемента управления (Control ID),HIWORD(wParam)— код уведомления (Notification Code), аlParam— дескрипторHWNDэлемента управления, пославшего сигнал.
📌 Слайд 10: Классификация и анатомия оконных сообщений (MSG)

Операционная система Windows строго разграничивает диапазоны числовых идентификаторов сообщений:
0x0000 – 0x03FF (0 .. WM_USER - 1): зарезервированы операционной системой для стандартных оконных сообщений (WM_CREATE,WM_PAINT,WM_DESTROY,WM_KEYDOWN).0x0400 – 0x7FFF (WM_USER .. 0x7FFF): сообщения, определяемые стандартными классами элементов управления (например, кнопками и списками). В собственных окнах их использование не рекомендуется, так как они могут конфликтовать с внутренними сообщениями стандартных компонентов.0x8000 – 0xBFFF (WM_APP .. 0xBFFF): диапазон, гарантированно свободный от любых системных интерпретаций. Предназначен для внутренних сообщений прикладного программиста в рамках одного приложения.0xC000 – 0xFFFF: строковые зарегистрированные сообщения, создаваемые системным вызовомRegisterWindowMessageW(L"MyInterprocessSyncMessage"). Уникальны во всей операционной системе и служат безопасным средством межпроцессного взаимодействия (IPC).
5. Архитектура очередей сообщений и диспетчеризация
📌 Слайд 11: Очереди сообщений потока и Raw Input Thread (RIT)
Каким образом механическое движение мыши или нажатие клавиши превращается в сообщение окна? В операционной системе Windows за это отвечает специализированная подсистема ядра win32k.sys и системный поток Raw Input Thread (RIT):
- Аппаратное прерывание (IRQ): контроллер клавиатуры или мыши генерирует прерывание. Драйвер устройства считывает аппаратный скан-код и передает его в исполнительную подсистему ядра.
- Поток необработанного ввода (RIT): системный поток RIT (работающий с повышенным приоритетом реального времени) извлекает событие ввода. RIT определяет, какому экрану и координатам соответствует курсор, выполняет проверку пересечения (Hit-testing) и находит целевой дескриптор
HWND. - Маршрутизация в очередь потока: RIT находит поток, владеющий данным
HWND, и помещает сообщение в аппаратную под-очередь ввода (Hardware Input Queue) целевого потока.
📌 Слайд 12: Архитектура очередей и главный цикл сообщений (Message Loop)

Устройство очереди сообщений потока:
Очередь сообщений потока (Thread Message Queue) не является простой одиночной структурой FIFO. Она состоит из нескольких компонентов разного приоритета:
- Очередь отложенных сообщений (Posted Message Queue): сообщения, отправленные вызовом
PostMessage(). Обрабатываются строго в порядке поступления (FIFO). - Очередь аппаратного ввода (Hardware Input Queue): события мыши и клавиатуры от RIT.
- Синтетические флаги (События низкого приоритета):
- Флаг
WM_PAINT: сообщение отрисовки не хранится в виде пакета данных. Вместо этого ядро выставляет окну флаг «область недействительна» (Invalid Region).WM_PAINTгенерируется планировщиком сообщений только тогда, когда очереди 1 и 2 абсолютно пусты! - Флаг
WM_TIMER: таймерные сообщения также синтезируются динамически при отсутствии других сообщений, если системное время превысило заданный интервал. - Флаг
WM_QUIT: формируется при вызовеPostQuitMessage(), сигнализируя о выходе из приложения.
- Флаг
📌 Слайд 13: Синхронная и асинхронная отправка: SendMessage vs PostMessage
В системном API Windows существуют два фундаментально различных метода отправки сообщений окну:
| Характеристика | PostMessageW() (Асинхронный) | SendMessageW() (Синхронный) |
|---|---|---|
| Тип взаимодействия | Асинхронный (Fire-and-Forget) | Синхронный (Request-Reply / RPC) |
| Очередь сообщений | Помещает сообщение в хвост очереди потока | Минует очередь сообщений! |
| Блокировка вызывающего | Завершается немедленно (0 мс) | Блокирует вызывающий поток до завершения обработки |
| Возвращаемое значение | Только статус успешности помещения в очередь (BOOL) | LRESULT, возвращенный функцией WndProc |
| Использование из потоков | Безопасно для фоновых рабочих потоков | Опасно: может вызвать межпоточный дедлок! |
1// Асинхронная отправка (рабочий поток уведомляет GUI о прогрессе):
2PostMessageW(hwndProgress, WM_USER_UPDATE_PROGRESS, (WPARAM)currentPercent, 0);
3
4// Синхронная отправка (немедленное получение длины текста в поле ввода):
5int textLength = (int)SendMessageW(hEdit, WM_GETTEXTLENGTH, 0, 0);
📌 Слайд 14: Главный цикл выборки сообщений: GetMessage, PeekMessage
Главный цикл выборки сообщений (Message Pump / Message Loop) располагается в функции wWinMain:
1MSG msg;
2// GetMessage возвращает:
3// > 0 — извлечено обычное сообщение
4// 0 — извлечено сообщение WM_QUIT (сигнал штатного выхода)
5// -1 — возникла фатальная ошибка (некорректный дескриптор HWND)
6while (GetMessageW(&msg, NULL, 0, 0) > 0) {
7 TranslateMessage(&msg); // Шаг 1: трансляция скан-кодов клавиатуры в WM_CHAR
8 DispatchMessageW(&msg); // Шаг 2: вызов оконной процедуры целевого окна msg.hwnd
9}
10return (int)msg.wParam; // Код завершения процесса
Альтернатива: неблокирующий опрос через PeekMessageW
Функция GetMessageW блокирует поток и переводит его в сон ядра, если сообщений нет. В игровых движках, системах реального времени и рендеринга требуется выполнять непрерывные вычисления при отсутствии сообщений. Для этого применяется функция PeekMessageW:
1while (true) {
2 if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
3 if (msg.message == WM_QUIT) break;
4 TranslateMessage(&msg);
5 DispatchMessageW(&msg);
6 } else {
7 // Очередь пуста: выполняем фоновые вычисления или кадр анимации
8 RenderNextFrame();
9 }
10}
6. Оконная процедура и обработка по умолчанию
📌 Слайд 15: Оконная процедура (Window Procedure) и функция DefWindowProc
Оконная процедура (Window Procedure) — это главная функция обратного вызова, в которой программист определяет реакцию окна на все интересующие события.
Функция имеет строго стандартизированную сигнатуру:
1LRESULT CALLBACK MainWindowProc(
2 HWND hwnd, // Дескриптор окна, получившего сообщение
3 UINT uMsg, // Код сообщения
4 WPARAM wParam, // Первый параметр сообщения
5 LPARAM lParam // Второй параметр сообщения
6) {
7 switch (uMsg) {
8 case WM_PAINT: {
9 PAINTSTRUCT ps;
10 HDC hdc = BeginPaint(hwnd, &ps); // Получение контекста отображения
11 // Отрисовка графики (GDI / Direct2D)...
12 EndPaint(hwnd, &ps); // Валидация области, снятие флага WM_PAINT
13 return 0; // Сообщение обработано
14 }
15 case WM_DESTROY: {
16 // Окно уничтожено пользователем: посылаем в свою очередь WM_QUIT
17 PostQuitMessage(0);
18 return 0;
19 }
20 default:
21 // Все необработанные сообщения ОБЯЗАНЫ передаваться в DefWindowProcW!
22 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
23 }
24}
Критическое правило системного программирования: операционная система посылает окну сотни системных служебных сообщений (изменение фокуса, отрисовка рамки, активация, анимация курсора, всплывающие подсказки). Если программист пропустит передачу необработанного сообщения в
DefWindowProcW(), окно зависнет, перестанет перерисовываться, двигаться и закрываться!
7. Обработка мышиного ввода
📌 Слайд 16: Обработка сообщений мыши в системном API
События манипулятора «мышь» подразделяются на сообщения клиентской области и неклиентские сообщения:
- Клиентские сообщения (Client Area Messages): генерируются, когда курсор находится внутри рабочей области окна:
WM_MOUSEMOVE— любое смещение мыши;WM_LBUTTONDOWN/WM_LBUTTONUP— нажатие/отпускание левой кнопки;WM_RBUTTONDOWN/WM_RBUTTONUP— нажатие/отпускание правой кнопки (контекстные действия);WM_LBUTTONDBLCLK— двойной щелчок (требует стиля классаCS_DBLCLKS);WM_MOUSEWHEEL— вращение колеса мыши; дельта поворота передается вGET_WHEEL_DELTA_WPARAM(wParam).
Координаты курсора относительно клиентской области извлекаются макросами:
1case WM_LBUTTONDOWN: {
2 int xPos = GET_X_LPARAM(lParam);
3 int yPos = GET_Y_LPARAM(lParam);
4 UINT flags = (UINT)wParam; // MK_CONTROL, MK_SHIFT
5 // Реакция на нажатие кнопки...
6 return 0;
7}
📌 Слайд 17: Захват мыши и неклиентские события: SetCapture и WM_NCHITTEST
1. Механизм захвата мыши (Mouse Capture)
В обычном режиме окно получает сообщения мыши только тогда, когда курсор физически находится внутри его границ. Если пользователь нажал кнопку внутри окна и, удерживая её, увел курсор за пределы окна, событие WM_LBUTTONUP по умолчанию уйдет чужому окну.
Для предотвращения потери событий перетаскивания (Drag-and-Drop) применяется захват мыши:
SetCapture(hwnd)— приказывает ОС направлять все сообщения мыши данному окну, даже если курсор ушел за пределы монитора.ReleaseCapture()— освобождает захват мыши при отпускании кнопки.
2. Неклиентские сообщения и хит-тестинг (WM_NCHITTEST)
Когда курсор мыши перемещается над заголовком окна, рамкой изменения размера или кнопкой закрытия [X], ядро генерирует неклиентские сообщения с префиксом WM_NC* (WM_NCMOUSEMOVE, WM_NCLBUTTONDOWN).
Прежде чем послать сообщение, ядро опрашивает окно сообщением WM_NCHITTEST («В какой части окна находится точка?»). Стандартная процедура DefWindowProcW возвращает коды зон:
HTCLIENT— точка внутри клиентской области;HTCAPTION— точка на заголовке окна (запуск перетаскивания окна);HTLEFT,HTRIGHT,HTTOP,HTBOTTOM— рамки изменения размеров;HTCLOSE— кнопка закрытия окна.
Переопределив WM_NCHITTEST, программист может сделать так, чтобы окно перетаскивалось за любую часть клиентской области, вернув значение HTCAPTION!
8. Обработка клавиатурного ввода
📌 Слайд 18: Обработка клавиатурного ввода: скан-коды и виртуальные клавиши
Клавиатурный ввод в операционной системе Windows обрабатывается на трех последовательных уровнях:
- Аппаратные скан-коды (Scan Codes): физические номера контактов на микроконтроллере клавиатуры. Зависят от конкретной модели и раскладки клавиатуры.
- Виртуальные коды клавиш (Virtual-Key Codes,
VK_*): аппаратно-независимые системные константы Windows (например,VK_LEFT,VK_F1,VK_RETURN,VK_ESCAPE,VK_SPACE). Генерируются сообщения:WM_KEYDOWN— физическое нажатие клавиши;WM_KEYUP— отпускание клавиши;WM_SYSKEYDOWN/WM_SYSKEYUP— нажатие комбинаций с клавишей Alt (системные команды меню). Код виртуальной клавиши передается в параметреwParam.
📌 Слайд 19: Фокус ввода и текстовый ввод: WM_CHAR и TranslateMessage
1. Символьные сообщения (WM_CHAR)
Сообщение WM_KEYDOWN сообщает программисту о факте нажатия физической кнопки (например, клавиши A), но оно не знает, какой символ должен быть напечатан (строчная 'a', заглавная 'A', русская 'ф' или 'Ф').
Трансляцией кодов клавиш в символы занимается функция TranslateMessage(&msg) в главном цикле сообщений:
- Она сопоставляет состояние
WM_KEYDOWNс текущей языковой раскладкой активного потока, а также состоянием клавиш Shift, CapsLock и AltGr. - Если нажатие образует печатный символ Юникода,
TranslateMessageсинтезирует новое сообщениеWM_CHARи помещает его обратно в очередь потока. - В сообщении
WM_CHARпараметрwParamсодержит уже готовый 16-битный код символа UTF-16 (wchar_t).
1case WM_CHAR: {
2 wchar_t ch = (wchar_t)wParam;
3 if (ch >= 32) { // Печатный символ
4 AppendCharacterToDocument(ch);
5 }
6 return 0;
7}
2. Фокус клавиатурного ввода (Keyboard Focus)
Клавиатура на компьютере одна, а окон на экране может быть множество. Сообщения клавиатуры направляются только тому окну, которое в данный момент обладает фокусом ввода:
- При получении фокуса окно получает сообщение
WM_SETFOCUS; - При потере фокуса (пользователь переключился на другое окно) —
WM_KILLFOCUS. - Программно передать фокус ввода окну можно функцией
SetFocus(hwnd).
Резюме и выводы
📌 Слайд 20: Резюме лекции
Основные итоги занятия:
- Событийно-ориентированная парадигма построена на инверсии управления (Inversion of Control): операционная система диспетчеризирует события, вызывая функции обратного вызова прикладной программы.
- Класс окна (
WNDCLASSEXW) определяет общие атрибуты окон и связывает их с главной оконной процедурой (lpfnWndProc). Каждое окно создается функциейCreateWindowExWи идентифицируется дескрипторомHWND. - Оконные сообщения упаковываются в структуру
MSG, содержащую код события и два универсальных параметраWPARAMиLPARAM. - Очереди сообщений потока обслуживаются системным потоком RIT; приоритет отдается отложенным сообщениям (
PostMessage) и аппаратному вводу, а события перерисовки (WM_PAINT) и таймеров (WM_TIMER) генерируются синтетически при простое очереди. SendMessageосуществляет прямой синхронный вызов оконной процедуры в обход очереди, в то время какPostMessageасинхронно ставит сообщение в очередь и немедленно возвращает управление.- Оконная процедура обязана передавать все необработанные сообщения в системную функцию по умолчанию
DefWindowProcW. - Ввод мыши подразделяется на клиентский и неклиентский (
WM_NCHITTEST), а длительное отслеживание движений требует явного захвата черезSetCapture. - Клавиатурный ввод разделен на аппаратно-независимые коды клавиш (
WM_KEYDOWN) и готовые символы Юникода (WM_CHAR), формируемые функциейTranslateMessage.
Контрольные вопросы для самопроверки
📌 Слайд 21: Вопросы для самопроверки
- В чем заключается фундаментальная разница между консольной и событийно-ориентированной архитектурой программ?
- Какую роль играет структура
WNDCLASSEXWи почему окно нельзя создать без предварительной регистрации класса? - Каким образом реализуется связь между системным дескриптором
HWNDи объектом языка C++ с помощью функцииSetWindowLongPtrW? - Какие поля входят в структуру сообщения
MSGи какую информацию несут параметрыWPARAMиLPARAM? - В чем заключается разница между диапазонами сообщений
WM_USER,WM_APPи строковыми сообщениямиRegisterWindowMessage? - За какие задачи отвечает системный поток Raw Input Thread (RIT) в архитектуре Windows?
- Почему сообщение
WM_PAINTназывается синтетическим и когда оно извлекается функциейGetMessage? - В чем заключается принципиальная разница в поведении функций
SendMessageиPostMessage? - Что произойдет с окном, если в функции
WndProcне передавать необработанные сообщения вDefWindowProc? - Каким образом функция
TranslateMessageпреобразует сообщенияWM_KEYDOWNв символьные сообщенияWM_CHAR?
Рекомендуемая литература и источники
📌 Слайд 22: Литература и рекомендуемые ресурсы
Основная литература:
- Устройство и функционирование OC Windows — Коньков К. А. М.: Бином, 2008. 208 с.
- Современные операционные системы (4-е изд.) — Таненбаум Э., Бос Х. СПб.: Питер, 2021. 1119 с.
- Операционные системы (2-е изд.) — Гордеев А. В. СПб.: Питер, 2009. 415 с.
- Системное программирование: методические указания к лабораторным работам — Бизюк А. Н., Соколова А. С. Витебск: УО «ВГТУ», 2024.
Дополнительная литература и справочники:
- Петцольд, Ч. Программирование для Windows 95 (в 2-х томах). — СПб.: BHV, 1997. (Классическое фундаментальное руководство по архитектуре WinAPI / GUI).
- Рихтер, Дж. Windows для профессионалов: создание эффективных Win32-приложений. — СПб.: Питер, 2008.
- Microsoft Learn: Windowing and Message Architecture — https://learn.microsoft.com/en-us/windows/win32/winmsg/windowing
- Microsoft Learn: About Messages and Message Queues — https://learn.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues
- Microsoft Learn: Keyboard and Mouse Input in Win32 — https://learn.microsoft.com/en-us/windows/win32/inputdev/user-input