| 特性 | 子類別化 | 超類別化 |
| 作用對象 | **已建立**的視窗實例 | **視窗類別** (註冊前) |
| 適用場景 | 少數現有控件修改行為 | 大量建立具相同行為的新控件 |
| 步驟 | SetWindowLong(GWL_WNDPROC) | GetClassInfoEx → 修改 WNDCLASSEX → RegisterClassEx → CreateWindowEx |
| 原始 WndProc 保存 | SetWindowLong 返回值 / GetWindowLong | GetClassInfoEx 取得的 WNDCLASSEX.lpfnWndProc |
實作流程:建立 SUPEREDITCLASS (唯讀十六進位 + Tab 導航)
1. 取得 EDIT 類別資訊
.IF uMsg == WM_CREATE
LOCAL wc:WNDCLASSEX
MOV wc.cbSize, SIZEOF WNDCLASSEX
INVOKE GetClassInfoEx, NULL, ADDR EditClass, ADDR wc
; wc 現在包含 EDIT 完整定義
2. 修改關鍵成員
PUSH wc.lpfnWndProc
POP OldWndProc ; 保存原始 EDIT WndProc
MOV wc.lpfnWndProc, OFFSET EditWndProc ; 掛上自訂程序
PUSH hInstance
POP wc.hInstance ; 必須設為本模組實例
MOV wc.lpszClassName, OFFSET OurClass ; 新類別名 "SUPEREDITCLASS"
3. 註冊新類別並批量建立控件
INVOKE RegisterClassEx, ADDR wc
XOR EBX, EBX
MOV EDI, 20
.WHILE EBX < 6
INVOKE CreateWindowEx, WS_EX_CLIENTEDGE, ADDR OurClass, NULL, \
WS_CHILD | WS_VISIBLE | WS_BORDER, \
20, EDI, 300, 25, hWnd, EBX, hInstance, NULL
MOV DWORD PTR [hwndEdit+4*EBX], EAX ; 存入句柄陣列
ADD EDI, 25
INC EBX
.ENDW
INVOKE SetFocus, hwndEdit ; 聚焦第一個
自訂視窗程序 EditWndProc 核心邏輯
字元過濾:只接受 0-9, A-F, a-f, Backspace
.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
; 非法字元:直接丟棄,不轉發 → 不顯示
.ELSEIF uMsg == WM_KEYDOWN
...
.ELSE
INVOKE CallWindowProc, OldWndProc, hEdit, uMsg, wParam, lParam
RET
.ENDIF
Tab / Shift-Tab 導航實作 (集中式,無需逐個子類別化)
.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
.ELSEIF AL == VK_TAB
INVOKE GetKeyState, VK_SHIFT
TEST EAX, 80000000h
.IF ZERO? ; 未按 Shift → 下一個
INVOKE GetWindow, hEdit, GW_HWNDNEXT
.IF EAX == NULL
INVOKE GetWindow, hEdit, GW_HWNDFIRST
.ENDIF
.ELSE ; 按 Shift → 上一個
INVOKE GetWindow, hEdit, GW_HWNDPREV
.IF EAX == NULL
INVOKE GetWindow, hEdit, GW_HWNDLAST
.ENDIF
.ENDIF
INVOKE SetFocus, EAX
XOR EAX, EAX
RET
.ELSE
INVOKE CallWindowProc, OldWndProc, hEdit, uMsg, wParam, lParam
RET
.ENDIF
關鍵 API 說明
GetKeyState(VK_SHIFT):高位元組 Bit 7 = 1 表正按下
GetWindow(hWnd, GW_HWNDNEXT/PREV/FIRST/LAST):依 Z-order 遍歷同層視窗
SetFocus(hWnd):移動輸入焦點,觸發 WM_KILLFOCUS/SETFOCUS
超類別化優勢總結
- **一次修改,處處生效**:6 個編輯框共用同一
EditWndProc,新增控件只需多一行 CreateWindowEx
- **效能優於子類別化**:無額外
SetWindowLong 呼叫,視窗建立即具備目標行為
- **原始行為完整保留**:透過
CallWindowProc 轉發未處理訊息,EDIT 原生功能 (複製/貼上/游標移動/選取) 全部正常
- **Tab 導航集中實作**:對話框管理器自動處理 Tab,但一般視窗需自行實作;超類別化讓所有同類控件共享導航邏輯
適用場景擴展
- 數值輸入框 (整數/浮點/範圍驗證)
- IP 位址輸入框 (四段式自動跳欄)
- 日期/時間輸入框 (格式強制、合法性檢查)
- 密碼框 (字元遮罩、防拖曳/複製)
- 上一篇文章: Win32 組合語言基礎:FLAT 模式、STDCALL 慣例、段偽指令與程式框架
- 下一篇文章: 組合語言子程式設計方法論:現場保護、參數傳遞、嵌套遞歸與文檔規範
Copyright ©
程式碼之家 All Rights Reserved