子類別化 透過 `SetWindowLong(hWnd, GWL_WNDPROC, NewProc)` 將**既有視窗實例**的訊息處理函數替換為自訂函數,形成訊息鏈:
Windows → 自訂 WndProc → (CallWindowProc) → 原始 WndProc
適用於**少量現有控件**行為微調;大量同類控件請改用超類別化 (參考 ID:13342)。
.IF uMsg == WM_CREATE
INVOKE CreateWindowEx, WS_EX_CLIENTEDGE, ADDR EditClass, NULL, \
WS_CHILD | WS_VISIBLE | WS_BORDER, \
20, 20, 300, 25, hWnd, NULL, hInstance, NULL
MOV hwndEdit, EAX
INVOKE SetFocus, EAX
; ---- 子類別化關鍵兩行 ----
INVOKE SetWindowLong, hwndEdit, GWL_WNDPROC, ADDR EditWndProc
MOV OldWndProc, EAX ; 保存原始 WndProc 位址
.ELSEIF uMsg == WM_DESTROY
INVOKE PostQuitMessage, 0
...
.ENDIF
EditWndProc PROC hEdit:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
.IF uMsg == WM_CHAR
MOV EAX, wParam
.IF (AL >= '0' && AL <= '9') || (AL >= 'A' && AL <= 'F') || \
(AL >= 'a' && AL <= 'f') || AL == VK_BACK
.IF AL >= 'a' && AL <= 'f'
SUB AL, 20h ; 小寫轉大寫
.ENDIF
INVOKE CallWindowProc, OldWndProc, hEdit, uMsg, EAX, lParam
RET
.ENDIF
; 非法字元:直接返回 0,不轉發 → 字元被吞沒
XOR EAX, EAX
RET
.ELSEIF uMsg == WM_KEYDOWN
MOV EAX, wParam
.IF AL == VK_RETURN
INVOKE MessageBox, hEdit, ADDR Message, ADDR AppName, MB_OK|MB_ICONINFORMATION
INVOKE SetFocus, hEdit
XOR EAX, EAX
RET
.ELSE
INVOKE CallWindowProc, OldWndProc, hEdit, uMsg, wParam, lParam
RET
.ENDIF
.ELSE
INVOKE CallWindowProc, OldWndProc, hEdit, uMsg, wParam, lParam
RET
.ENDIF
XOR EAX, EAX
RET
EditWndProc ENDP
| API | 用途 | 關鍵參數 |
|---|---|---|
SetWindowLong(hWnd, GWL_WNDPROC, NewProc) | 替換視窗程序 | 返回值 = 原始 WndProc 位址 (必須保存) |
CallWindowProc(PrevProc, hWnd, Msg, wParam, lParam) | 轉發訊息給上一層處理器 | 參數原樣傳遞,保持調用慣例 |
GetWindowLong(hWnd, GWL_USERDATA) | 替代方案:儲存 OldWndProc 於視窗私有資料 | 避免全域變數,支援多實例 |
| 場景 | 推薦技術 | 理由 |
|---|---|---|
| 現有對話框中 1-2 個編輯框需過濾 | 子類別化 | 無需修改資源模板,運行時動態掛載 |
| 動態建立 10+ 同款自訂控件 | 超類別化 | 一次註冊、多次建立,無額外 SetWindowLong 開銷 |
| 需修改系統類別全域行為 (如所有 BUTTON) | 超類別化 | 系統類別不可直接 SetWindowLong (共用) |
| 第三方控件無原始碼、需擴展 | 子類別化 | 唯一可行方案 |
; 第二層子類別化
INVOKE SetWindowLong, hEdit, GWL_WNDPROC, ADDR EditWndProc2
MOV OldWndProc2, EAX ; 此時為 EditWndProc 位址
; EditWndProc2 內
INVOKE CallWindowProc, OldWndProc2, ... ; 轉發給 EditWndProc
; EditWndProc 內
INVOKE CallWindowProc, OldWndProc, ... ; 轉發給原始 EDIT
支援多層行為疊加 (如:第一層過濾字元、第二層自動大寫、第三層長度限制),各層職責單一、可獨立開發測試。