在標準視窗標題列上繪製自訂圖形(如公司 Logo、狀態指示燈、動畫),是提升應用程式辨識度的常用技巧。本文說明兩種核心方法:處理 WM_NCPAINT 非工作區重繪訊息、使用 GetWindowDC 取得整個視窗裝置內容。
WM_NCPAINT (Non-Client Paint) — 當非工作區 (標題列、邊框、選單列、捲軸) 需要重繪時發送。wParam = 區域控制代碼 (HRGN),lParam = 0。
WM_NCPAINT。GetWindowDC(hWnd) 取得整個視窗 DC (含非工作區)。TextOut, Rectangle, BitBlt, DrawIcon 等) 在標題列區域繪圖。ReleaseDC(hWnd, hDC) 釋放 DC。DefWindowProc 讓系統繪製標準標題列元素 (標題文字、系統選單、最小化/最大化/關閉按鈕),否則標題列會空白。.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 | GetDC |
|---|---|---|
| 取得範圍 | 整個視窗 (含非工作區) | 僅工作區 |
| 適用時機 | 任何時候 (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 開銷,但會增加系統資源佔用。WM_NCPAINT 可能不被呼叫或裁剪區域受限。需呼叫 SetWindowTheme(hWnd, L"", L"") 停用該視窗主題,或處理 WM_NCPAINT 時排除主題繪製區域。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 桌面應用開發的實用技能。