Fasm---Win32彙編學習8

    Fasm---Win32彙編學習8

在本節課中,我們將學習windows是如何處理鍵盤消息的。

理論:

    因爲大多數pc機只有一個鍵盤,所以所有運行的windows程序都必須共用它。Windows負責把擊鍵消息發送到具有輸入焦點的應用程序中。儘管屏幕上可能存在多個程序窗口,但是一個時刻僅有一個窗口有輸入焦點。有輸入焦點的那個應用程序總是高亮顯示的。實際上你可以從兩個角度來看鍵盤消息:一是您可以把它看成是一大堆按鍵消息的集合,在這種情況下,當你按下一個鍵的時候,windows就會發送一個WM_KEYDOWN給有輸入焦點的那個應用程序,提醒它有一個鍵被按下。當你釋放一個鍵的時候,windows又會發送一個WM_KEYUP消息,告訴一個鍵被釋放。你可以把每個鍵當成一個按鈕;另一種情況是:你可以把鍵盤看成是字符輸入的設備。當你按下一個字符'a'鍵的時候,windows會發送一個WM_CHAR消息給有輸入焦點的應用程序,告訴它'a'鍵被按下。實際上windows內部發送WM_KEYDOWN和WM_KEYUP消息,而這些消息通過TranslateMessage翻譯成WM_CHAR消息。windows窗口過程函數決定是否處理所收到的消息,一般來說你不大會去處理WM_KEYDOWN和WM_KEYUP消息,在消息循環中TranslateMessage會把上述消息轉換成WM_CHAR消息。在我們的課程中只處理WM_CHAR消息。

 

 

format PE GUI 4.0
include 'win32ax.inc'
       
macro memmov [dst, src]
{
            common
            push [src]
            pop [dst]
}

;************************數據********************************
szClassName db 'first Windows',0
szWndName db '我的第一個程序',0
szCommand dd ?
char dd 20h
hIcon rd 1
hInstanse rd 1
hCursor rd 1
hWnd rd 1


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
mov [@wc.lpszMenuName], NULL
mov [@wc.lpszClassName], szClassName
;註冊窗口類
invoke RegisterClassEx,addr @wc
;建立窗口

invoke CreateWindowEx, NULL, szClassName, szWndName,/
WS_OVERLAPPEDWINDOW,/
100, 100, 600, 400,/
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
local @rect: RECT
cmp [wMsg], WM_DESTROY
jz Quit
cmp [wMsg], WM_CHAR
jz key
cmp [wMsg], WM_PAINT
jz PAIT
invoke DefWindowProc,[hWnd],[wMsg],[wParam],[lParam]
ret


key:
push [wParam]
pop    [char]
invoke InvalidateRect,[hWnd],NULL,TRUE
jmp endWnd

PAIT:
invoke BeginPaint,[hWnd], addr @ps
mov [@hdc], eax ;保存設備環境
invoke TextOut,[@hdc],0,0,char,1
invoke EndPaint,[hWnd], addr @ps
jmp endWnd

Quit:
invoke PostQuitMessage,NULL
jmp endWnd


endWnd:
xor eax,eax
ret
endp


;///////////////////////////輸入表////////////////////////////////////////////////


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'

我們現在先編譯下這段程序代碼,我們可以看到,我們按下鍵盤的某個鍵時,我們的窗口上就會顯示某個鍵。這段代碼其實和我們上節課程的代碼也沒什麼太大的區別。我們來看。剛剛我們在上面已經說了,我們按下某個鍵的時候,Windows會發送WM_KEYDOWN和WM_KEYUP消息,然後我們通過消息循環獲得消息後,然後此時通過TranslateMessage將我們上面的WM_KEYDOWN和WM_KEYUP消息翻譯成WM_CHAR消息,我們一把你在窗口過程函數中處理的也就是我們的WM_CHAR消息。。

WM_CHAR消息的附件參數。wParam是我們的字符碼,所以我們只要處理WM_CHAR消息的時候將這個字符碼保存下來,然後通過InvalidateRect設置我們的窗口爲無效區域,此時windows就會給我們的消息隊列中發送WM_PAINT消息。之前上節課也已經說了。windows只要檢測到相關窗口存在無效區域,則就會發送WM_PAINT消息。此時我們通過在WM_PAINT消息中設置無效區域爲有效,就實現了重繪操作。

我們來看InvaildateRect函數

BOOL InvalidateRect(

HWND hWnd,    // handle of window with changed update region
CONST RECT *lpRect,    // address of rectangle coordinates
BOOL bErase    // erase-background flag
);   

 

首先第一個參數是窗口句柄,也就是要改變的更新的窗口。

第二閣參數是 一個Rect結構的指針,這個指我們客戶區我們想要設置無效的一個正方形指針。如果該值設置爲NULL的話,則整個客戶區都無效。bErase參數指定我們是否要擦除背景。也就是windows在調用WM_PAINT消息的時候會通過BeginPaint函數時把背景擦除。我們此處的做法是通過:保存所有有關重繪客戶區的數據,然後發送WM_PAINT消息,處理該消息的程序然後根據相關數據重新繪製客戶區。儘管那麼做事有點弓背,但是windows要處理那麼龐大的消息羣,沒有一定的規矩可不行。實際上我們完全可以通過GetDc來獲得設備環境的句柄,然後通在設備環境繪製字符,然後再用RealeateDc釋放設備環境的句柄,但是這樣有一個問題是,我們此時繪製字符完成後。但是如果之後windows接受到WM_PAINT消息的時候,顯然我們繪製的字符也就消失了。爲了讓字符一直顯示,我們必須把它放在WM_PAINT消息中處理。

invoke TextOut,[@hdc],0,0,char,1

在調用InvalidateRect時,WM_PAINT消息被髮送到了WINDOWS窗口處理過程,程序流程轉移到處理WM_PAINT消息的程序段,然後調用BeginPaint得到設備上下文的句柄,再調用TextOut在客戶區的(0,0)處輸出保存的按鍵字符。這樣無論您按什麼鍵都能在客戶區的左上角顯示,不僅如此,無論您怎麼縮放窗口(迫使WINDOWS重新繪製它的客戶區),字符都會在正確的地方顯示,所以必須把所有重要的繪製動作都放到處理WM_PAINT消息的程序段中去。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章