Previous slide Next slide Toggle fullscreen Toggle overview view Open presenter view
Системное программирование
Тема 6. Организация графического пользовательского интерфейса в операционных системах
Системное программирование
План лекции
Учебные вопросы
Архитектура подсистемы и DWM
Оконные классы и CreateWindowEx
Ресурсы приложений (.rc, .rsrc)
Меню окна и сообщение WM_COMMAND
Диалоги: модальные vs немодальные
Контекст устройства HDC и объекты GDI
Жизненный цикл WM_PAINT и буферизация
Direct2D и аппаратный композитинг
Цели занятия
Освоить архитектуру USER, GDI, DWM и WDDM
Изучить регистрацию классов и создание окон
Изучить изоляцию и компиляцию ресурсов
Разобрать механику диалоговых окон
Овладеть работой с контекстом HDC
Освоить технику двойной буферизации
Понять модель вывода Direct2D
Организация графического пользовательского интерфейса
Системное программирование
Архитектура графической подсистемы ОС
Разделение уровней и Ring 0
User Mode (Ring 3): user32.dll (окна, ввод, меню) и gdi32.dll (примитивы, шрифты)
Kernel Mode (Ring 0): системныйсистемный драйвер win32k.sys (win32kbase.sys, win32kfull.sys)
Перенос оконного менеджера в ядро исключил накладные расходы на межпроцессный контекст csrss
ДиспетчерДиспетчер ядраядра dxgkrnl.sys: виртуализация GPU и видеопамяти
DWM и композитная модель
Desktop Window Manager (dwm.exe):
Окна рисуют в изолированные буферы видеопамяти
Композитинг кадров через шейдеры Direct3D
Устранено затирание перекрытых окон
WDDM (Windows Display Driver Model):
User-Mode Driver (UMD) для Direct3D/Vulkan
Kernel-Mode Driver (KMD) вендора GPU
Организация графического пользовательского интерфейса
Системное программирование
Стек графической подсистемы Windows
Организация графического пользовательского интерфейса
Системное программирование
Архитектура оконного класса: WNDCLASSEXW
Структура и регистрация
Оконный класс — ядерная структура win32k, определяющая базовые атрибуты окна
Регистрация: RegisterClassExW(&wc) возвращает уникальный атом ATOM
cbSize: строгий контроль размера структуры sizeof(WNDCLASSEXW)
lpfnWndProc: адрес глобальной оконной процедуры обработки сообщений
cbWndExtra: системные дополнительные байты для привязки контекста
Стили класса и ресурсы
CS_HREDRAW | CS_VREDRAW: перерисовка окна при изменении геометрии
CS_DBLCLKS: посылка сообщений двойного клика мыши (WM_LBUTTONDBLCLK)
CS_OWNDC: выделение уникального контекста устройства HDC (для OpenGL)
hIcon / hIconSm: пиктограммы для панели задач и заголовка
hCursor: курсор мыши (LoadCursorW)
hbrBackground: кисть заливки фона окна
Организация графического пользовательского интерфейса
Системное программирование
Фабрика окон CreateWindowEx и стили окна
Системная фабрика CreateWindowExW
Создает дескриптор HWND на основе зарегистрированного класса
Параметры: dwExStyle, имя класса, заголовок, dwStyle, координаты ( X , Y , W , H ) (X, Y, W, H) ( X , Y , W , H ) , родитель hWndParent, меню/ID hMenu, дескриптор модуля hInstance, lpParam
Стили WS_*:
WS_OVERLAPPEDWINDOW: заголовок, системное меню, рамка изменения размера, кнопки окна
WS_POPUP, WS_CHILD: всплывающие окна и дочерние элементы управления
Расширенные стили и связь с C++
Стили WS_EX_*: WS_EX_TOPMOST (поверх всех), WS_EX_LAYERED (прозрачность), WS_EX_COMPOSITED
Инициализация: ядро посылает сообщение WM_CREATE (CREATESTRUCTW*)
Мост C++ ↔ \leftrightarrow ↔ Win32: передача this через lpParam при вызове CreateWindowExW
В WM_CREATE указатель извлекается и сохраняется через SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)pThis)
Организация графического пользовательского интерфейса
Системное программирование
Оконные классы и создание окон через CreateWindowEx
Организация графического пользовательского интерфейса
Системное программирование
Архитектура ресурсов Windows-приложений
Изоляция ресурсов интерфейса
Ресурсы — структурированные данные интерфейса, отделенные от машинного кода
Ключевые преимущества:
Локализация (i18n) заменой ресурсов без сборки C++
Отображение .rsrc в память по требованию (read-only)
Защита данных интерфейса от повреждения указателями
Конвейер сборки ресурсов
Сценарий app.rc и заголовочный файл resource.h
Компилятор ресурсов:
rc.exe app.rc → \to → бинарный объект app.res
Компоновщик:
link.exe main.obj app.res ...
Секция .rsrc в PE-модуле:
Дерево Тип → \to → IDID → \to → Язык (LCID)
Организация графического пользовательского интерфейса
Системное программирование
Структура сценария ресурсов (.rc)
Описание меню в .rc
#include <windows.h>
#include "resource.h"
IDR_MAIN_MENU MENU
BEGIN
POPUP "&Файл"
BEGIN
MENUITEM "&Создать\tCtrl+N", IDM_FILE_NEW
MENUITEM "&Открыть...\tCtrl+O", IDM_FILE_OPEN
MENUITEM SEPARATOR
MENUITEM "В&ыход", IDM_FILE_EXIT
END
END
Таблица акселераторов
IDR_ACCELERATORS ACCELERATORS
BEGIN
"N", IDM_FILE_NEW, VIRTKEY, CONTROL
"O", IDM_FILE_OPEN, VIRTKEY, CONTROL
END
Символ & задает букву мнемоникимнемоники
\t отделяет названиеназвание от горячей клавиши
Организация графического пользовательского интерфейса
Системное программирование
Система командных меню и акселераторов
Главное меню (Menu Bar): полоса в верхней неклиентской части окна
Подменю (Popup): выпадающие списки (MF_POPUP)
Контекстное меню: показ через TrackPopupMenu
Способы связывания с окном:
Через поле lpszMenuName в WNDCLASSEX
Аргумент hMenu в CreateWindowEx
Динамически вызовом SetMenu(hWnd, hMenu)
Клавиатурные акселераторы
Организация графического пользовательского интерфейса
Системное программирование
Архитектура ресурсов и командных меню
Организация графического пользовательского интерфейса
Системное программирование
Диспетчеризация сообщения WM_COMMAND
Спецификация параметров
LOWORD(wParam):
Идентификатор команды меню, акселератора или элемента (IDM_FILE_OPEN)
HIWORD(wParam):
0 — источник: меню
1 — источник: клавиатурный акселератор
Иное — уведомление элемента управления
lParam:
Дескриптор HWND элемента (для меню и акселераторов равен NULL)
Паттерн обработки в WndProc
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDM_FILE_OPEN:
DoFileOpen(hWnd);
return 0 ;
case IDM_FILE_EXIT:
DestroyWindow(hWnd);
return 0 ;
default :
return DefWindowProc(hWnd, msg,
wParam, lParam);
}
}
Организация графического пользовательского интерфейса
Системное программирование
Диалоговые окна: концепция и шаблоны DIALOGEX
Концепция диалогов
Окна верхнего уровня, создаваемыесоздаваемые по бинарному шаблону DLGTEMPLATEEX
Встроенное создание контролов, единый шрифт, навигация клавишами Tab/стрелками
Координаты в DLU (Dialog Units) :
1 DLU X = 1 4 1 \text{ DLU}_X = \frac{1}{4} 1 DLU X = 4 1 ширины символа
1 DLU Y = 1 8 1 \text{ DLU}_Y = \frac{1}{8} 1 DLU Y = 8 1 высоты символа
Защита от искажения формы при смене DPI
Шаблон диалога в .rc
IDD_SETTINGS DIALOGEX 0, 0, 240, 100100
STYLE DS_MODALFRAME | DS_CENTER |
WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Параметры соединения"
FONT 9, "Segoe UI", 400, 0, 0x1
BEGIN
LTEXT "Хост:", IDC_STATIC, 14, 15, 3030, 8
EDITTEXT IDC_EDT_HOST, 5050, 12, 170170, 14
DEFPUSHBUTTON "OK", IDOK, 120, 7575, 45, 14
END
Организация графического пользовательского интерфейса
Системное программирование
Модальные диалоговые окна
Запуск DialogBoxParam
Синхронный блокирующий запуск:INT_PTR ret = DialogBoxParam(
hInstance,
MAKEINTRESOURCE(IDD_SETTINGS),
hWndParent, SettingsDlgProc,
(LPARAM)&config
);
Передача контекста через lParam
Не возвращает управление до закрытия диалога
Механика модальности в Windows
Блокировка родителя:
EnableWindow(hWndParent, FALSE)
Создание формы и посылка WM_INITDIALOG
Запуск локального цикла сообщений ядра
Завершение диалога:
Вызов EndDialog(hDlg, IDOK);
Разблокировка: EnableWindow(hWndParent, TRUE);
Запрещено вызывать DestroyWindow!
Организация графического пользовательского интерфейса
Системное программирование
Немодальные диалоговые окна
Вызов CreateDialogParam
Неблокирующий асинхронный запуск:HWND hDlg = CreateDialogParam(
hInstance,
MAKEINTRESOURCE(IDD_FIND),
hWndParent, FindDlgProc, 0
);
ShowWindow(hDlg, SW_SHOW);
Немедленно возвращает дескриптор HWND
Родительское окно остается интерактивным
Интеграция в цикл сообщений
Использует общий цикл сообщений потока
Требует функции IsDialogMessage (иначе не работают Tab и стрелки фокуса):while (GetMessage(&msg, NULL , 0 , 0 )) {
if (!hDlg || !IsDialogMessage(hDlg, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Уничтожение: DestroyWindow(hDlg); hDlg=NULL;
Организация графического пользовательского интерфейса
Системное программирование
Модальные и немодальные диалоги: сравнение
Организация графического пользовательского интерфейса
Системное программирование
Процедура диалогового окна (DLGPROC)
Особенности DLGPROC
Сигнатура:
INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM)
Возврат TRUE, если сообщение обработано, FALSE — системному диспетчеру
Никогда не вызывает DefWindowProc!
Инициализация: WM_INITDIALOG получает lParam из функции создания диалога
Пример процедуры диалога
INT_PTR CALLBACK DlgProc (HWND hDlg, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_INITDIALOG:
SetDlgItemTextA(hDlg, IDC_EDT_HOST, "localhost" );
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK) {
EndDialog(hDlg, IDOK);
return TRUE;
}
break ;
}
return FALSE;
}
Организация графического пользовательского интерфейса
Системное программирование
Контекст устройства (HDC) и конвейер GDI
Концепция HDC (Device Context)
Структура ядра, объединяющая устройство вывода и текущее графическое состояние:
HPEN — перо (цвет, толщина, стиль)
HBRUSH — кисть (цвет и тип заливки)
HFONT — шрифт, кегль, вес и наклон
HBITMAP — целевой растровый буфер
Режим фона (SetBkMode), цвет текста
Получение дескриптора HDC
Внутри WM_PAINT:
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps); (валидирует (валидирует область окна)окна)
Вне WM_PAINT:
HDC hdc = GetDC(hWnd);
ReleaseDC(hWnd, hdc);
В оперативной памяти:
HDC memDC = CreateCompatibleDC(hdc);
Организация графического пользовательского интерфейса
Системное программирование
Графические объекты GDI и паттерн SelectObject
Канонический паттерн работы
HBRUSH hBr = CreateSolidBrush(RGB(220 , 38 , 38 ));
HBRUSH hOld = (HBRUSH)SelectObject(hdc, hBr);
Rectangle(hdc, 50 , 50 , 250 , 150 );
SelectObject(hdc, hOld);
DeleteObject(hBr);
Лимиты и надежность
Запрет удаления выбранных объектов:
DeleteObject завершится ошибкой, если объект выбран в любой HDC
Лимит дескрипторов:
В Windows установлен лимит — 10 000 GDI-дескрипторов на процесс
Утечки приводят к отказу отрисовки и сбою оконной подсистемы
Стоковые объекты (GetStockObject) не удаляются
Организация графического пользовательского интерфейса
Системное программирование
Обработка перерисовки: WM_PAINT и PAINTSTRUCT
Механизм инвалидации
Оконная подсистема использует ленивую модель: окно не обновляется мгновенно
InvalidateRect(hWnd, &rc, TRUE) помечает прямоугольник поврежденным
WM_PAINT помещается в очередь с самым низким приоритетом при отсутствии ввода
UpdateWindow(hWnd) форсирует WM_PAINT немедленно в обход очереди сообщений
Обработка сообщения WM_PAINT
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1 ));
DrawScene(hdc, &ps.rcPaint);
EndPaint(hWnd, &ps);
return 0 ;
}
Ошибка: вызов GetDC внутри WM_PAINT не валидирует регион → \to → шторм сообщений и 100% CPU
Организация графического пользовательского интерфейса
Системное программирование
Устранение мерцания: техника двойной буферизации
Причина мерцания (Flicker)
Очистка окна фоном (WM_ERASEBKGND) с последующим выводом примитивов
Человеческий глаз фиксирует промежуточный пустой фон
Перехват WM_ERASEBKGND с возвратом TRUE подавляет лишнюю очистку
Алгоритм двойной буферизации
-- hdcMem = CreateCompatibleDC(hdc); (контекст памяти)
-(контекст памяти)
hbm = CreateCompatibleBitmap(hdc, w, h);
-- SelectObject(hdcMem, hbm); (выбор растра)
-- Отрисовка всей графической сцены в памятипамяти hdcMem
-- BitBlt(hdc, 0,0, w,h, hdcMem, 0,0, SRCCOPY); (атомарный перенос)
-(атомарный перенос)
DeleteObject(hbm); DeleteDC(hdcMem); (очистка)
Организация графического пользовательского интерфейса
Системное программирование
Контекст устройства (HDC) и конвейер GDI
Организация графического пользовательского интерфейса
Системное программирование
Современные графические API: Direct2D и GPU
Ограничения GDI
Рендеринг выполняется процессором (CPU) без использования шейдеров GPU
Нет встроенной попиксельной альфа-прозрачности (alpha blending)
Примитивное ступенчатое сглаживание геометрии (отсутствует MSAA)
Возможности Direct2D
Аппаратное ускорение векторной графики на базе Direct3D 11/12
Высокая частота обновления (60–144+ FPS) при плавной векторной анимации
Субпиксельная типографика ClearType через библиотеку DirectWrite
Цикл рендеринга: BeginDraw → \to → команды вывода → \to → EndDraw
Бесшовная композиция окон через DWM
Организация графического пользовательского интерфейса
Системное программирование
Итоги и системные принципы GUI
Архитектурные принципы
Оконный класс: регистрация в ядре шаблона свойств и адреса lpfnWndProc
Фабрика CreateWindowEx: инициализация HWND, посылка WM_CREATE, мост GWLP_USERDATA
Изоляция ресурсов: размещение UI в .rsrc обеспечивает динамическую локализацию
Событийный цикл диалогов: модальные крутят локальный цикл, немодальные требуют IsDialogMessage
Конвейер рендеринга
Паттерн SelectObject: обязательное восстановление старых дескрипторов GDI
Валидация областей: вызовы BeginPaint/EndPaint очищают регион обновления
Устранение фликкеринга: замена фона и рисование в закадровый битмап
Аппаратный композитинг: Direct2D и DWM переносят наложение на шейдеры GPU
Организация графического пользовательского интерфейса
Системное программирование
Вопросы для самопроверки
Базовые механизмы
Какова роль WNDCLASSEXW и почему нельзя создать окно без класса?
Какие параметры принимает CreateWindowExW и как передать указатель C++?
В чем разница между стилями WS_* и WS_EX_*?
Какую роль выполняют win32k.sys и Desktop Window Manager (DWM)?
В чем выгода компиляции ресурсов в отдельную секцию .rsrc?
Контролы и рендеринг
Как акселераторы транслируются в команду WM_COMMAND?
Чем системно различаются модальные и немодальные диалоговые окна?
Зачем нужен вызов IsDialogMessage?
Почему нельзя удалять объект GDI, пока он выбран в HDC?
Как двойная буферизация предотвращает мерцание?
Организация графического пользовательского интерфейса
Системное программирование
Литература и рекомендуемые ресурсы
Учебная литература
Внутреннее устройство Microsoft Windows (7-е изд.) — Руссинович М., Соломон Д., Ионеску А. СПб.: Питер, 2019. 800 с.
Программирование для Windows 95 (в 2-х томах) — Петцольд Ч. СПб.: BHV, 1997.
Windows для профессионалов: создание эффективных Win32-приложений — Рихтер Дж. СПб.: Питер, 2008. 720 с.
Современные операционные системы (4-е изд.) — Таненбаум Э., Бос Х. СПб.: Питер, 2021. 1119 с.
Документация и справочники
Организация графического пользовательского интерфейса
Лекция №6.
Цель: сформировать системное понимание архитектуры графической подсистемы ОС, механизмов изоляции и компиляции ресурсов PE-файлов, управления строкой меню и диалоговыми окнами, работы с контекстом устройства HDC и примитивами GDI, а также современных подходов аппаратного композитинга (DWM, Direct2D).
Связь с предыдущими темами: опирается на Тему 5 (механизм оконных сообщений, очереди ввода, цикл выборки сообщений и оконная процедура).
Связь с практической работой: теоретическая основа для лабораторных работ по разработке оконного интерфейса и графического вывода.
Акцентировать внимание на разделении оконного менеджмента (USER32) и подсистемы рендеринга (GDI/Direct2D).
Подчеркнуть роль композитного менеджера DWM: приложения больше не затирают окна соседей при перемещении.
Схема взаимодействия библиотек Ring 3, композитора DWM, подсистемы ядра win32k и графического адаптера.
Подчеркнуть, что окно в Windows невозможно создать без предварительной регистрации класса. Системные контролы (BUTTON, EDIT) имеют встроенные зарегистрированные классы.
Объяснить студентам архитектурный паттерн привязки объектно-ориентированного класса окна к C API Win32 через GWLP_USERDATA.
Архитектурная схема регистрации WNDCLASSEXW, порождения окон через CreateWindowExW, передачи параметров CREATESTRUCTW и маршрутизации оконной процедуры через GWLP_USERDATA.
Пояснить: секция ресурсов защищена аппаратно как read-only и кэшируется страницами виртуальной памяти.
Обратить внимание: компилятор rc.exe формирует двоичные деревья структур данных, вшиваемые в PE.
Функция TranslateAccelerator перехватывает WM_KEYDOWN и напрямую посылает WM_COMMAND окну.
Иллюстрация: конвейер rc.exe, дерево дескрипторов HMENU и маршрутизация WM_COMMAND через акселераторы.
Разъяснить разницу между меню (lParam=0) и уведомлениями от дочерних элементов управления.
DLU масштабируют размеры элементов пропорционально кеглю выбранного системного шрифта.
Предостеречь: вызов DestroyWindow уничтожает окно, но оставляет поток навсегда зависшим в цикле сообщений.
IsDialogMessage перехватывает клавиши навигации диалога до передачи их в TranslateMessage.
Сравнение архитектурных различий: блокировка родителя, способ вызова, цикл сообщений и метод завершения.
Подчеркнуть критическое отличие: DLGPROC возвращает BOOL, а стандартный WndProc возвращает LRESULT.
BeginPaint обязательно парно закрывается EndPaint для очистки системного флага обновления окна.
Акцентировать: обязательное восстановление старого объекта перед удалением созданного ресурса.
Разъяснить разницу между BeginPaint (валидирует) и GetDC (не валидирует регион перерисовки).
Двойная буферизация переносит готовый кадр одной неделимой операцией BitBlt.
Схема конвейера GDI: выбор объектов, отрисовка в теневой контекст памяти и атомарный перенос BitBlt.
Direct2D транслирует двухмерные примитивы в полигоны и текстуры видеопамяти графического адаптера.
Резюме лекции: системное программирование GUI объединяет жизненный цикл оконных объектов, строгий учет ресурсов ядра и конвейер графического вывода.
Вопросы охватывают регистрацию классов, создание окон, ресурсы, диалоги, GDI и современные модели композитинга.
Завершить тему 6, анонсировать тему 7 (Механизмы управления виртуальной и динамической памятью).