WM_PAINT
WM_PAINT — это системное сообщение операционной системы Microsoft Windows, которое отправляется окну, когда часть его клиентской области (client area) требует перерисовки. Сообщение является одним из ключевых механизмов графического интерфейса Windows, обеспечивающим корректное отображение содержимого окон при изменении их размеров, перемещении, появлении поверх других окон или при программном запросе на обновление. Обработка WM_PAINT является обязательной для любого окна, которое выводит собственную графику или текст, и выполняется в рамках цикла обработки сообщений (message loop) приложения.
Механизм возникновения
Сообщение WM_PAINT генерируется системой, когда диспетчер окон (Desktop Window Manager, DWM) или подсистема графического интерфейса (GDI) определяет, что часть клиентской области окна стала недействительной (invalid). Недействительная область — это участок, содержимое которого устарело и должно быть обновлено. Причины недействительности включают:
- Изменение размеров окна (например, при растягивании границ мышью).
- Перемещение окна, в результате которого ранее скрытая часть становится видимой.
- Закрытие или перемещение другого окна, которое перекрывало данное.
- Вызов функции
InvalidateRectилиInvalidateRgnиз приложения или системы. - Программный запрос на обновление через
UpdateWindowилиRedrawWindow.
Система не отправляет WM_PAINT немедленно при возникновении недействительной области. Вместо этого она помечает окно как требующее перерисовки и помещает сообщение WM_PAINT в очередь сообщений потока, которому принадлежит окно, только когда очередь становится пустой (то есть когда нет других сообщений с более высоким приоритетом, таких как WM_TIMER или WM_MOUSEMOVE). Это позволяет избежать избыточной перерисовки при быстрых последовательных изменениях — система объединяет несколько недействительных областей в одну минимальную прямоугольную область, называемую «областью обновления» (update region).
Обработка сообщения
Обработка WM_PAINT осуществляется в оконной процедуре (window procedure) приложения. Типичная реализация включает следующие шаги:
- Вызов BeginPaint: Функция
BeginPaintполучает дескриптор контекста устройства (HDC) для окна и заполняет структуруPAINTSTRUCT, которая содержит информацию об области обновления (прямоугольникrcPaint), а также флаги, указывающие, нужно ли перерисовывать фон.BeginPaintтакже делает недействительную область действительной (valid), предотвращая повторную отправку WM_PAINT до следующего обновления.
- Рисование: Внутри блока между
BeginPaintиEndPaintприложение выполняет все операции рисования с использованием полученного HDC. Рисование может включать вывод текста, линий, фигур, изображений или вызов других функций GDI. Ограничение рисования только областьюrcPaint(или её расширением) является хорошей практикой для повышения производительности, хотя допустимо рисовать всю клиентскую область.
- Вызов EndPaint: Функция
EndPaintзавершает процесс рисования, освобождает HDC и выполняет необходимые завершающие действия, такие как снятие блокировки окна для других сообщений.
Пример фрагмента кода на C++:
``cpp case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // Рисование с использованием hdc FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW+1)); TextOut(hdc, 10, 10, L"Hello, World!", 13); EndPaint(hWnd, &ps); return 0; } ``
Если приложение не обрабатывает WM_PAINT, система передаёт его в обработчик по умолчанию DefWindowProc, который обычно ничего не делает, что приводит к пустой клиентской области.
Особенности и оптимизация
Область обновления
Область обновления, передаваемая через PAINTSTRUCT.rcPaint, представляет собой прямоугольник, охватывающий все недействительные участки. Система старается минимизировать эту область, объединяя пересекающиеся прямоугольники. Приложение может использовать эту информацию для рисования только повреждённой части, что особенно важно для сложных окон с большим объёмом графики.
Фон окна
Если в классе окна (WNDCLASS) указана кисть фона (hbrBackground), система автоматически закрашивает фон перед отправкой WM_PAINT. Это происходит при вызове BeginPaint, если в PAINTSTRUCT.fErase установлен флаг. Если фон не задан, приложение должно самостоятельно очистить область.
Сообщение WM_ERASEBKGND
Перед WM_PAINT может быть отправлено сообщение WM_ERASEBKGND, если фон требуется стереть. Обработка этого сообщения позволяет приложению контролировать процесс заливки фона, но обычно её избегают, полагаясь на кисть фона класса окна.
Производительность
- Избегайте длительных операций внутри обработчика WM_PAINT, так как это блокирует очередь сообщений окна и может вызывать задержки в отклике интерфейса.
- Для сложной графики используйте двойную буферизацию (double buffering) — рисуйте в памяти, а затем копируйте результат на экран.
- При необходимости анимации или частого обновления (например, в играх) используйте таймеры или цикл с
InvalidateRectиUpdateWindowдля принудительной перерисовки.
Отличие от других сообщений
- WM_PAINT vs WM_NCPAINT: WM_NCPAINT отвечает за перерисовку неклиентской области (рамка, заголовок, кнопки управления). WM_PAINT — только за клиентскую область.
- WM_PAINT vs WM_PRINT: WM_PRINT используется для программной печати содержимого окна в контекст устройства, не связанный с экраном (например, принтер или метафайл).
- WM_PAINT vs WM_TIMER: WM_TIMER генерируется по таймеру и не связан с недействительностью области. Для анимации часто комбинируют оба сообщения: таймер инициирует
InvalidateRect, что приводит к WM_PAINT.
История и контекст
Сообщение WM_PAINT появилось в ранних версиях Windows (начиная с Windows 1.0, 1985 год) как часть модели графического интерфейса, основанной на очередях сообщений и GDI. В отличие от современных фреймворков (например, WPF или UWP, где используется DirectX и композиция), Win32 API с WM_PAINT остаётся низкоуровневым механизмом, требующим явного управления рисованием. Несмотря на появление более высокоуровневых библиотек (MFC, Qt, Windows Forms), понимание WM_PAINT необходимо для разработки нативных приложений под Windows, особенно при работе с кастомными элементами управления или играми.
Применение в современных приложениях
Хотя многие современные приложения используют фреймворки, которые скрывают работу с WM_PAINT (например, WPF использует собственный цикл рендеринга), знание этого сообщения остаётся актуальным для:
- Разработки на чистом Win32 API (например, для системных утилит, драйверов, игр).
- Отладки проблем с отрисовкой в legacy-приложениях.
- Создания кастомных контролов в MFC или ATL.
- Понимания внутренней архитектуры Windows.
Источники
- Microsoft Docs: «WM_PAINT message» (Windows and Messages / Win32 API).
- Charles Petzold, «Programming Windows», 5th edition (1998), глава 5 «The Paint Program».
- MSDN Library: «About Painting and Drawing» (Windows GDI).
- «Windows Internals», Part 1, 7th edition (Pavel Yosifovich, Mark Russinovich, David Solomon, Alex Ionescu).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →