首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Windows 繪圖核心:WM_PAINT、BeginPaint/EndPaint 與無效區域機制全解析

     編輯:關於C++

前言

WM_PAINT 是 Windows 視窗程式設計中最基礎也最容易被誤解的消息。本文系統梳理其觸發時機、無效區域管理、BeginPaint/EndPaint 的關鍵作用,以及常見重繪函數的區別與陷阱。

WM_PAINT 觸發機制

系統自動發送場景

  • 視窗首次創建(CreateWindow 後)
  • 視窗尺寸變化(含最大化/最小化/邊框拖動)
  • 視窗被遮擋區域重新顯示(移開遮擋視窗)
  • 調用 ScrollWindow/ScrollDC 滾動客戶區
  • 調用 InvalidateRect/InvalidateRgn 手動標記無效區域
  • 彈出式選單、工具提示、對話框遮擋後恢復

核心原則:低優先級、異步合併

WM_PAINT 只有在線程消息隊列為空時才會被投遞,且多次 Invalidate 產生的無效區域會被合併為單一 WM_PAINT。

這設計有雙重優勢:

  1. 避免高頻重繪佔用 CPU
  2. 自動合併相鄰/重疊無效區域,減少繪圖調用

無效區域與 PAINTSTRUCT

Windows 為每個視窗維護一個「繪圖信息結構」,記錄無效區域坐標。WM_PAINT 本身不攜帶坐標,必須通過 BeginPaint 獲取:

typedef struct tagPAINTSTRUCT {
    HDC hdc;            // 繪圖設備上下文
    BOOL fErase;        // 是否已擦除背景
    RECT rcPaint;       // 無效區域矩形(核心信息)
    BOOL fRestore, fIncUpdate;
    BYTE rgbReserved[32];
} PAINTSTRUCT;

BeginPaint / EndPaint:不可或缺的配對

為什麼必須調用 BeginPaint?

MSDN 明確指出:

BeginPaint 會將視窗的更新區域設置為 NULL。如果處理 WM_PAINT 時不調用 BeginPaint(或以其他方式清空更新區域),系統會持續發送 WM_PAINT,形成死循環,導致 CPU 100% 佔用與視窗閃爍。

標準處理模板

case WM_PAINT: {
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    // 1. 可選:僅繪製 ps.rcPaint 區域以提升性能
    // 2. 繪圖代碼...
    EndPaint(hwnd, &ps);
    return 0;
}

BeginPaint 的額外職責

  • 若無效區域標記需擦除背景(InvalidateRect(..., TRUE)),自動發送 WM_ERASEBKGND
  • 返回的 HDC 已設置剪裁區域為 rcPaint,繪圖自動限制在無效區域內

三大重繪函數對比

函數機制特點典型用途
InvalidateRect/Rgn標記無效區域,投遞 WM_PAINT 到消息隊列異步、合併、低優先級最常用,數據變更後請求重繪
UpdateWindow若更新區域非空,直接發送 WM_PAINT 到視窗程序(繞過隊列)同步、優先響應、不創建新消息ShowWindow 後強制首次繪製
RedrawWindowInvalidateRect + UpdateWindow 的超集可控制非客戶區、背景擦除、強制發送等需精細控制重繂行為時

常見誤區與補充

  1. 不能直接 SendMessage(hwnd, WM_PAINT, ...):系統會檢查無效區域,為空則丟棄
  2. ValidateRect 可「欺騙」系統:不繪圖直接清空無效區域,阻止後續 WM_PAINT
  3. CS_HREDRAW/CS_VREDRAW 類樣式:尺寸變化時自動整個客戶區無效
  4. BeginPaint 僅能在 WM_PAINT 處理函數中使用,其他場合調用行為未定義

總結

  • WM_PAINT 是被動通知,由系統根據無效區域異步發送
  • BeginPaint 獲取繪圖信息並清空無效區域,是打破重繪循環的關鍵
  • EndPaint 釋放 HDC,標記繪製完成
  • 按需選擇 Invalidate*(異步)、UpdateWindow(同步)、RedrawWindow(全控制)

掌握這套機制,能從根源上解決視窗閃爍、重繪死循環、繪圖效率低等經典難題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved