在處理 Windows 訊息 (如 WM_NOTIFY) 時,lParam 常指向結構體 (如 NMHDR)。傳統寫法使用 ASSUME 指令切換暫存器型別視圖,但 直接型別轉換語法 (STRUCT PTR [REG]).field 更簡潔、無執行期開銷、編譯期即完成型別檢查。本文對比兩種寫法並推薦最佳實務。
mov ecx, lParam
assume ecx:ptr NMHDR
push ecx
.if [ecx].code == NM_CLICK
; 處理點擊
.endif
pop ecx
assume ecx:nothing
缺點:需成對 PUSH/POP 保存暫存器、ASSUME 範圍管理繁瑣、嵌套結構時極易出錯。
mov ecx, lParam
.if (NMHDR ptr [ecx]).code == NM_CLICK
; 處理點擊
.endif
優點:單行完成、無暫存器汙染、編譯期型別檢查、支援巢狀結構 (OUTER PTR [ecx]).inner.field。
(結構型別 PTR [基址暫存器]).欄位名
; 等效 C 語言: ((結構型別*)基址)->欄位名
PTR 關鍵字不可省略。EAX/ECX/EDX/EBX/ESI/EDI/EBP 或記憶體運算數 [ESI+8]。(OUTER PTR [eax]).innerStruct.field。
.elseif uMsg == WM_NOTIFY
mov ecx, lParam
.if (NMHDR ptr [ecx]).code == NM_CLICK
; 進一步轉型為 NMITEMACTIVATE 取得項目索引
.if (NMITEMACTIVATE ptr [ecx]).iItem != -1
mov eax, (NMITEMACTIVATE ptr [ecx]).iItem
; 處理項目點擊
.endif
.elseif (NMHDR ptr [ecx]).code == NM_DBLCLK
; 處理雙擊
.endif
「總是使用 assume, 這不是運行時的,是編譯時編譯器做的操作。」(原文註解)
ASSUME:純組譯期指令,告訴組譯器「此暫存器指向某結構」,產生正確位移編碼,不產生任何機器碼。(TYPE PTR [REG]):同樣純組譯期運算,直接計算欄位位移編入指令,同樣零執行期開銷。
; ESI 指向結構體指標陣列 (DWORD 陣列,每項為結構體位址)
mov eax, [esi + ecx*4] ; 取得第 ECX 個結構體位址
mov edx, (MYSTRUCT PTR [eax]).field ; 直接存取欄位
直接型別轉換 (STRUCT PTR [REG]).field 是 MASM/JWasm/UAsm 提供的現代語法糖,完全取代 ASSUME 的繁瑣管理,同時保持零開銷。在訊息處理、結構體陣列遍歷、巢狀結構存取等高頻場景中,能顯著提升程式碼可讀性與維護性,是 Win32 組合語言開發的必備技巧。