在處理 WM_MOUSEWHEEL 等將多項資訊打包於 wParam/lParam 的 Win32 訊息時,常需用移位指令擷取高/低字。若不當使用 SHR(邏輯右移)處理帶符號數值,會導致符號位錯誤延伸,造成判斷邏輯失效。
; WM_MOUSEWHEEL: wParam 高字為滾輪增量 (WHEEL_DELTA = 120)
; 向上滾動為 -120 (FF88h),向下為 +120 (0078h)
mov eax, wParam ; EAX = 0000FF88h (向上滾動)
shr eax, 16 ; EAX = 000000FFh (錯誤!邏輯右移補 0)
.if eax == -120 ; 永遠不成立,因 EAX 為正數 255
; 處理向上滾動
.endif
SHR 是無符號邏輯右移,高位補 0。當 16 位有符號數(如 FF88h = -120)放在 32 位暫存器高字時,其第 15 位(原 16 位數的符號位)為 1。SHR EAX, 16 將這 1 移至 AX 的第 15 位,但 EAX 高 16 位被清零,導致結果被視為無符號正數(0x0000FF88 → 0x000000FF),而非預期的有符號 -120 (0xFFFFFF88)。
mov ax, word ptr wParam+2 ; 直接取高字到 AX (AX = FF88h)
.if ax == -120 ; 有符號比較,正確判斷為真
; 向上滾動處理
.endif
mov eax, wParam
sar eax, 16 ; 算術右移,高位補符號位 (EAX = FFFFFF88h)
.if eax == -120 ; 正確
; ...
.endif
movsx eax, word ptr wParam+2 ; 從記憶體載入 16 位並符號延伸至 32 位
.if eax == -120
; ...
.endif
lParam 低字為 X 座標,高字為 Y 座標,均為有符號 16 位整數(可為負,如多螢幕負座標)。
; 錯誤做法
mov eax, lParam
shr eax, 16 ; Y 座標若為負,此處變正數
mov ebx, lParam
and ebx, 0FFFFh ; X 座標若為負,EBX 變大正數
; 正確做法
movsx eax, word ptr lParam+2 ; Y (有符號)
movsx ebx, word ptr lParam ; X (有符號)
MOVSX 或 SAR。MOVZX、AND 0FFFFh 或直接操作 AX/DX。SHR/SHL 適用。AX、DX)可避開擴展問題,代碼更短。Win32 API 大量使用打包參數,組語處理時務必區分有符號/無符號語意,選擇正確指令,避免隱蔽 Bug。