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

Windows 視窗程式設計:在標題列繪製自訂圖形 (WM_NCPAINT 與 GetWindowDC)

     編輯:組合語言

前言

在標準視窗標題列上繪製自訂圖形(如公司 Logo、狀態指示燈、動畫),是提升應用程式辨識度的常用技巧。本文說明兩種核心方法:處理 WM_NCPAINT 非工作區重繪訊息、使用 GetWindowDC 取得整個視窗裝置內容。

方法一:處理 WM_NCPAINT

訊息含義

WM_NCPAINT (Non-Client Paint) — 當非工作區 (標題列、邊框、選單列、捲軸) 需要重繪時發送。wParam = 區域控制代碼 (HRGN),lParam = 0。

實作步驟

  1. 在視窗程序中捕捉 WM_NCPAINT。
  2. 呼叫 GetWindowDC(hWnd) 取得整個視窗 DC (含非工作區)。
  3. 使用 GDI 函數 (TextOut, Rectangle, BitBlt, DrawIcon 等) 在標題列區域繪圖。
  4. 呼叫 ReleaseDC(hWnd, hDC) 釋放 DC。
  5. 必須呼叫 DefWindowProc 讓系統繪製標準標題列元素 (標題文字、系統選單、最小化/最大化/關閉按鈕),否則標題列會空白。

程式碼範例 (MASM/Win32)

.if uMsg == WM_NCPAINT
    invoke GetWindowDC, hWnd
    mov hDC, eax
    ; 計算標題列矩形
    invoke GetWindowRect, hWnd, addr rcWindow
    invoke GetClientRect, hWnd, addr rcClient
    ; rcWindow - rcClient = 非工作區尺寸
    ; 在標題列繪製自訂圖示
    invoke DrawIconEx, hDC, x, y, hIcon, 16, 16, 0, 0, DI_NORMAL
    invoke ReleaseDC, hWnd, hDC
    invoke DefWindowProc, hWnd, uMsg, wParam, lParam
    mov eax, 0
    ret
.endif

方法二:直接使用 GetWindowDC (任意時機)

差異比較

特性GetWindowDCGetDC
取得範圍整個視窗 (含非工作區)僅工作區
適用時機任何時候 (WM_PAINT 外亦可)通常在 WM_PAINT 內
釋放方式ReleaseDC(hWnd, hDC)ReleaseDC(hWnd, hDC)
裁剪區域預設含整個視窗預設僅工作區

直接繪製範例

; 例如在按鈕點擊時,在標題列顯示狀態
invoke GetWindowDC, hWnd
mov hDC, eax
invoke TextOut, hDC, 5, 2, addr szStatus, lstrlen(szStatus)
invoke ReleaseDC, hWnd, hDC

注意:此法繪製的內容會在下一次 WM_NCPAINT 被系統覆蓋,適合暫時性提示;永久性圖形必須在 WM_NCPAINT 中重繪。

進階技巧與注意事項

  • 視窗類別樣式 CS_OWNDC:建立視窗類別時指定,可避免重複 Get/ReleaseDC 開銷,但會增加系統資源佔用。
  • 主題/視覺樣式:啟用視覺樣式 (Manifest) 時,標題列由主題引擎繪製,WM_NCPAINT 可能不被呼叫或裁剪區域受限。需呼叫 SetWindowTheme(hWnd, L"", L"") 停用該視窗主題,或處理 WM_NCPAINT 時排除主題繪製區域。
  • DPI 感知:高 DPI 環境下座標需縮放,使用 GetDpiForWindow 取得縮放因子。
  • 效能:非工作區重繪頻率較低,但複雜繪圖仍建議離屏位圖 (CreateCompatibleBitmap) 雙重緩衝。

完整最小範例架構

.386
.model flat, stdcall
include windows.inc
include user32.inc
include gdi32.inc
includelib user32.lib
includelib gdi32.lib

WndProc proto :HWND, :UINT, :WPARAM, :LPARAM

.data
szClassName db "MyTitleBarClass", 0
szAppName   db "Title Bar Drawing Demo", 0

.code
start:
    invoke GetModuleHandle, NULL
    mov hInstance, eax
    invoke RegisterClassEx, addr wcx
    invoke CreateWindowEx, 0, addr szClassName, addr szAppName, \
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, \
        NULL, NULL, hInstance, NULL
    invoke ShowWindow, eax, SW_SHOW
    invoke UpdateWindow, eax
    .while TRUE
        invoke GetMessage, addr msg, NULL, 0, 0
        .break .if eax == 0
        invoke TranslateMessage, addr msg
        invoke DispatchMessage, addr msg
    .endw
    invoke ExitProcess, 0

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg == WM_NCPAINT
        ; 方法一實作
        invoke DefWindowProc, hWnd, uMsg, wParam, lParam
        xor eax, eax
        ret
    .elseif uMsg == WM_DESTROY
        invoke PostQuitMessage, 0
    .else
        invoke DefWindowProc, hWnd, uMsg, wParam, lParam
        ret
    .endif
    xor eax, eax
    ret
WndProc endp
end start

總結

在標題列繪圖的核心是 WM_NCPAINT + GetWindowDC + DefWindowProc 三部曲。掌握非工作區繪製機制,可實現品牌化標題列、狀態指示、動態標題等進階 UI 效果,是 Windows 桌面應用開發的實用技能。

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