Fasm---Win32彙編學習9
本課中我們將學習如何在我們的窗口過程函數中處理鼠標按鍵消息。示例程序演示瞭如何等待左鍵按下消息,我們將在按下的位置顯示一個字符串。
和處理鍵盤輸入一樣,WINDOWS將捕捉鼠標動作並把它們發送到相關窗口。這些活動包括左、右鍵按下、移動、雙擊等(譯者注:新式鼠標還包括滾輪消息WM_WHEEL)。WINDOWS並不像處理鍵盤輸入那樣把所有的鼠標消息都導向有輸入焦點的窗口,任何鼠標經過的窗口都將接收到鼠標消息,無論有否輸入焦點。另外,窗口還會接收到鼠標在非客戶區移動的消息(WM_NCMOVE),但大多數的情況下我們都會將其忽略掉。 對鼠標的每一個按鈕都有兩個消息:WM_LBUTTONDOWN,WM_RBUTTONDOWN 。對於三鍵鼠標還會有WM_MBUTTONDOWN和WM_MBUTTONUP消息,當鼠標在某窗口客戶區移動時,該窗口將接收到WM_MOUSEMOVE消息。一個窗口若想處理WM_LBUTTONDBCLK或 WM_RBUTTONDBCLK,那麼它的窗口類必須有CS_DBLCLKS風格,否則它就會接受到一堆的按鍵起落(WM_XBUTTONDOWN或WM_XBUTTONUP)的消息。 對於所有的消息,窗口過程函數傳入的參數lParam包含了鼠標的位置,其中底位爲x座標,高位爲y座標,這些座標值都是相對於窗口客戶區的左上角的值,wParam中則包含了鼠標按鈕的狀態。
format PE GUI 4.0
include 'win32ax.inc'
;************************代碼********************************
struct MPOINT
x dd ?
y dd ?
ends
macro memmov [dst, src]
{
common
push [src]
pop [dst]
}
entry $
invoke GetModuleHandle,NULL
mov [hInstanse], eax
invoke GetCommandLine,NULL
mov [szCommand], eax
stdcall _WinMain,hInstanse, NULL, [szCommand], SW_SHOWDEFAULT
invoke ExitProcess,NULL
proc _WinMain hInstance:DWORD, hPrevInstance:DWORD, lpCmdLine:DWORD, nCmdShow:DWORD
local @wc : WNDCLASSEX
local @msg : MSG
invoke RtlZeroMemory,addr @wc,sizeof.WNDCLASSEX
invoke LoadIcon,NULL, IDI_WINLOGO
mov [hIcon], eax
invoke LoadCursor,NULL, IDC_ARROW
mov [hCursor], eax
mov [@wc.cbSize], sizeof.WNDCLASSEX
mov [@wc.style], CS_HREDRAW or CS_VREDRAW
mov [@wc.lpfnWndProc], _WndProc
mov [@wc.cbClsExtra], NULL
mov [@wc.cbWndExtra], NULL
memmov @wc.hInstance, hInstance
memmov @wc.hIcon, hIcon
memmov @wc.hCursor, hCursor
mov [@wc.hbrBackground], COLOR_WINDOW+1
mov [@wc.lpszMenuName], NULL
mov [@wc.lpszClassName], szClassName
;註冊窗口類
invoke RegisterClassEx,addr @wc
;建立窗口
invoke CreateWindowEx, NULL, szClassName, szWndName,/
WS_OVERLAPPEDWINDOW,/
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,/
NULL, NULL, [hInstanse], NULL
mov [hWnd], eax
invoke ShowWindow,[hWnd],SW_SHOWNORMAL
invoke UpdateWindow,[hWnd]
GetMsg:
invoke GetMessage,addr @msg, NULL, 0, 0
or eax, eax
jz EndMsg
invoke TranslateMessage,addr @msg
invoke DispatchMessage,addr @msg
jmp GetMsg
EndMsg:
mov eax, [@msg.wParam]
ret
endp
proc _WndProc uses ebx esi edi,hWnd:DWORD, wMsg:DWORD, wParam:DWORD, lParam:DWORD
local @hdc:DWORD
local @ps : PAINTSTRUCT
cmp [wMsg], WM_DESTROY
je Quit
cmp [wMsg], WM_LBUTTONDOWN
je LBUTTON
cmp [wMsg], WM_PAINT
je PAIT
invoke DefWindowProc,[hWnd],[wMsg],[wParam],[lParam]
ret
LBUTTON:
mov eax, [lParam]
and eax, 0000FFFFh ;使其高2個字節爲0。因爲高兩個字節爲y座標。
mov [MousePoint.x], eax
mov eax, [lParam]
shr eax, 16 ;向右移動2個字節,此時記錄爲我們的Y座標
mov [MousePoint.y], eax
mov [MouseClick], TRUE
invoke InvalidateRect,[hWnd], NULL, TRUE
jmp endWnd
PAIT:
invoke BeginPaint,[hWnd], addr @ps
mov [@hdc], eax ;保存設備環境
cmp [MouseClick], 0
je _exit
invoke TextOut,[@hdc],DWORD [MousePoint.x],DWORD [MousePoint.y], szClassName,13
_exit:
invoke EndPaint,[hWnd], addr @ps
jmp endWnd
Quit:
invoke PostQuitMessage,NULL
endWnd:
xor eax,eax
ret
endp
;///////////////////////////數據////////////////////////////////////////////////
szClassName db 'first Windows',0
szWndName db '我的第一個程序',0
szCommand dd ?
hIcon rd 1
hInstanse rd 1
hCursor rd 1
hWnd rd 1
MousePoint MPOINT <>
MouseClick db 0
section '.import' data import readable writeable
library kernel32, 'kernel32.dll',/
user32, 'user32.dll',/
gdi32, 'gdi32.dll'
include 'api/kernel32.inc'
include 'api/user32.inc'
include 'api/gdi32.inc'
分析:
窗口過程處理我們的WM_LBUTTONDOWN,因爲在上面說了,任何鼠標經過的窗口都將接收到鼠標消息。窗口還會接收到鼠標在非客戶區移動的消息(WM_NCMOVE),但大多數情況下都將其忽略。對鼠標的每一個按鈕有兩個消息。WM_LBUTTONDOWN和WM_RBUTTONDOWN消息。對於三鍵鼠標還有WM_MBUTTONDOWN和WM_MBUTTONUP消息。當鼠標在某窗口客戶區移動時,鼠標接受到WM_MOUSEMOVE消息。一個窗口若想處理WM_LBUTTONCLICK和WM_RBUTTONCLICK消息的話,則窗口類必須有CS_DBLCLKS風格。否則它會接受一堆按鍵起落的消息。(WM_XBUTTONDOWN或WM_XBUTTONUP)的消息。 對於所有的消息,窗口過程函數傳入的參數lParam包含了鼠標的位置,其中底位爲x座標,高位爲y座標,這些座標值都是相對於窗口客戶區的左上角的值,wParam中則包含了鼠標按鈕的狀態。
那麼我們很明白了。對於鼠標的消息,那麼窗口過程函數傳入的參數lParam參數包含了鼠標的位置,其中低位爲x座標,高位爲y座標。這些座標值都是相對於窗口客戶區左上角。wParam中則包含了鼠標按鈕的狀態。那麼我們的程序要實現的就是在鼠標移動到任意位置後,左鍵按下後,在我們的窗口客戶區顯示一個字符串。
那麼很顯然,我們在處理WM_LBUTTONDOWN 的時候只要將鼠標的X和Y座標保存下來,然後這時候調用InvalidateRect函數設置我們的窗口客戶區爲無效區域,此時windows就會給我們的程序發送WM_PAINT消息,此時我們在WM_PAINT消息中做一個判斷就是我們的程序到底是否觸發了WM_LBUTTONDOWN消息,那麼如何判斷,當然需要我們在處理WM_LBUTTONDOWN消息的時候做一個標記。。
很簡單,大家看代碼,很多我已經標示註釋了。
mov eax, [lParam]
and eax, 0000FFFFh ;使其高2個字節爲0。因爲高兩個字節爲y座標。
mov [MousePoint.x], eax
mov eax, [lParam]
shr eax, 16 ;向右移動2個字節,此時記錄爲我們的Y座標
mov [MousePoint.y], eax
mov [MouseClick], TRUE ;設置標記爲TRUE,等下在WM_PAINT消息中要判斷。
invoke InvalidateRect,[hWnd], NULL, TRUE
jmp endWnd
PAIT:
invoke BeginPaint,[hWnd], addr @ps
mov [@hdc], eax ;保存設備環境
cmp [MouseClick], 0
je _exit
invoke TextOut,[@hdc],DWORD [MousePoint.x],DWORD [MousePoint.y], szClassName,13
_exit:
invoke EndPaint,[hWnd], addr @ps
jmp endWnd
首先BeginPaint取得設備環境的句柄。因爲我們程序需要通過設備環境來實現重繪。說白了設備環境就是管理設備的工具。我們就是在設備環境上來進行繪圖。 然後取得設備環境的句柄後,通過GDI函數來進行訪問以及設置。說白了GDI函數就是通過操作設備環境的。
好,我們此時保存後,進行判斷我們之前的標記。因爲我們在處理WM_LBUTTONDOWN消息的時候將MouseClick設置爲true。所以這時我們只要判斷它是否爲0,就可以了。如果爲0,則說明沒有處理WM_LBUTTONDOWN消息,然後直接跳到EndPaint來關閉設備環境的句柄。 如果不爲0,說明處理WM_LBUTTONDOWN消息了,那麼通過Text輸出我們的字符串,爲了保險,我們在輸出完在將其MouseClick設置爲0。 從而進行下一次的循環。。