Системное программирование

Тема 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

center

Организация графического пользовательского интерфейса
Системное программирование

Архитектура оконного класса: 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), родитель 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

center

Организация графического пользовательского интерфейса
Системное программирование

Архитектура ресурсов Windows-приложений

Изоляция ресурсов интерфейса

  • Ресурсы — структурированные данные интерфейса, отделенные от машинного кода
  • Ключевые преимущества:
    • Локализация (i18n) заменой ресурсов без сборки C++
    • Отображение .rsrc в память по требованию (read-only)
    • Защита данных интерфейса от повреждения указателями

Конвейер сборки ресурсов

  1. Сценарий app.rc и заголовочный файл resource.h
  2. Компилятор ресурсов:
    rc.exe app.rc →\to бинарный объект app.res
  3. Компоновщик:
    link.exe main.obj app.res ...
  4. Секция .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 отделяет названиеназвание от горячей клавиши
Организация графического пользовательского интерфейса
Системное программирование

Система командных меню и акселераторов

Дескриптор HMENU и типы меню

  • Главное меню (Menu Bar): полоса в верхней неклиентской части окна
  • Подменю (Popup): выпадающие списки (MF_POPUP)
  • Контекстное меню: показ через TrackPopupMenu
  • Способы связывания с окном:
    • Через поле lpszMenuName в WNDCLASSEX
    • Аргумент hMenu в CreateWindowEx
    • Динамически вызовом SetMenu(hWnd, hMenu)

Клавиатурные акселераторы

  • Горячие клавиши транслируются вызовом:
    if (!TranslateAccelerator(hWnd, hAccel, &msg)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
  • Преобразует нажатиенажатие клавиш сразу в WM_COMMAND
Организация графического пользовательского интерфейса
Системное программирование

Архитектура ресурсов и командных меню

center

Организация графического пользовательского интерфейса
Системное программирование

Диспетчеризация сообщения 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 DLUX=141 \text{ DLU}_X = \frac{1}{4} ширины символа
    • 1 DLUY=181 \text{ DLU}_Y = \frac{1}{8} высоты символа
  • Защита от искажения формы при смене 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;
Организация графического пользовательского интерфейса
Системное программирование

Модальные и немодальные диалоги: сравнение

center

Организация графического пользовательского интерфейса
Системное программирование

Процедура диалогового окна (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

Канонический паттерн работы

// 1. Создание ресурса GDI
HBRUSH hBr = CreateSolidBrush(RGB(220, 38, 38));

// 2. Выбор в HDC и сохранение старого
HBRUSH hOld = (HBRUSH)SelectObject(hdc, hBr);

// 3. Вывод геометрических примитивов
Rectangle(hdc, 50, 50, 250, 150);

// 4. Восстановление исходного объекта
SelectObject(hdc, hOld);

// 5. Удаление созданного объекта
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);

    // ps.rcPaint — только поврежденная область
    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

center

Организация графического пользовательского интерфейса
Системное программирование

Современные графические 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
Организация графического пользовательского интерфейса
Системное программирование

Вопросы для самопроверки

Базовые механизмы

  1. Какова роль WNDCLASSEXW и почему нельзя создать окно без класса?
  2. Какие параметры принимает CreateWindowExW и как передать указатель C++?
  3. В чем разница между стилями WS_* и WS_EX_*?
  4. Какую роль выполняют win32k.sys и Desktop Window Manager (DWM)?
  5. В чем выгода компиляции ресурсов в отдельную секцию .rsrc?

Контролы и рендеринг

  1. Как акселераторы транслируются в команду WM_COMMAND?
  2. Чем системно различаются модальные и немодальные диалоговые окна?
  3. Зачем нужен вызов IsDialogMessage?
  4. Почему нельзя удалять объект GDI, пока он выбран в HDC?
  5. Как двойная буферизация предотвращает мерцание?
Организация графического пользовательского интерфейса
Системное программирование

Литература и рекомендуемые ресурсы

Учебная литература

  • Внутреннее устройство 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 (Механизмы управления виртуальной и динамической памятью).