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

Win32 組合語言進階:子類別化 實作十六進位過濾編輯框與 Enter 觸發

     編輯:組合語言

子類別化核心機制

子類別化 透過 `SetWindowLong(hWnd, GWL_WNDPROC, NewProc)` 將**既有視窗實例**的訊息處理函數替換為自訂函數,形成訊息鏈:

Windows → 自訂 WndProc → (CallWindowProc) → 原始 WndProc

適用於**少量現有控件**行為微調;大量同類控件請改用超類別化 (參考 ID:13342)。

實作流程:子類別化單一 EDIT 控件

1. WM_CREATE 建立控件並子類別化

.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

2. 自訂視窗程序 EditWndProc

字元過濾:只接受 0-9, A-F, a-f, Backspace

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 詳細說明

API用途關鍵參數
SetWindowLong(hWnd, GWL_WNDPROC, NewProc)替換視窗程序返回值 = 原始 WndProc 位址 (必須保存)
CallWindowProc(PrevProc, hWnd, Msg, wParam, lParam)轉發訊息給上一層處理器參數原樣傳遞,保持調用慣例
GetWindowLong(hWnd, GWL_USERDATA)替代方案:儲存 OldWndProc 於視窗私有資料避免全域變數,支援多實例

GWL_* 索引常數

  • `GWL_WNDPROC` (-4):視窗程序位址
  • `GWL_HINSTANCE` (-6):模組實例控制代碼
  • `GWL_ID` (-12):視窗識別碼 (子視窗 ID / 選單 ID)
  • `GWL_STYLE` (-16) / `GWL_EXSTYLE` (-20):風格位元組
  • `GWL_USERDATA` (-21):使用者自定 32 位資料

子類別化 vs 超類別化 選型指南

場景推薦技術理由
現有對話框中 1-2 個編輯框需過濾子類別化無需修改資源模板,運行時動態掛載
動態建立 10+ 同款自訂控件超類別化一次註冊、多次建立,無額外 SetWindowLong 開銷
需修改系統類別全域行為 (如所有 BUTTON)超類別化系統類別不可直接 SetWindowLong (共用)
第三方控件無原始碼、需擴展子類別化唯一可行方案

常見陷阱與除錯

  • ❌ 忘記保存 `OldWndProc` → `CallWindowProc` 參數錯誤 → 崩潰
  • ❌ `WM_CHAR` 過濾後未 `RET` 且未轉發 → 訊息洩漏導致雙重處理
  • ❌ 子類別化後未處理 `WM_NCDESTROY` 還原原始程序 → 視窗銷毀時殘留鉤子
  • ✅ 正確模式:在 `WM_NCDESTROY` 呼叫 `SetWindowLong(hWnd, GWL_WNDPROC, OldWndProc)` 還原
  • ✅ 多實例安全:用 `GWL_USERDATA` 儲存各實例專屬 `OldWndProc`,而非全域變數

進階:繼承子類別化 (鏈式攔截)

; 第二層子類別化
INVOKE SetWindowLong, hEdit, GWL_WNDPROC, ADDR EditWndProc2
MOV OldWndProc2, EAX  ; 此時為 EditWndProc 位址

; EditWndProc2 內
INVOKE CallWindowProc, OldWndProc2, ...  ; 轉發給 EditWndProc
; EditWndProc 內
INVOKE CallWindowProc, OldWndProc, ...   ; 轉發給原始 EDIT

支援多層行為疊加 (如:第一層過濾字元、第二層自動大寫、第三層長度限制),各層職責單一、可獨立開發測試。

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