WIN32彙編: 6.處理鍵盤消息

第六課 處理鍵盤輸入消息


在本課中,我們將要學習WINDOWS程序是如何處理鍵盤消息的。

理論:

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

例子:

.386
.model flat,stdcall
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc
include /masm32/include/gdi32.inc
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib
includelib /masm32/lib/gdi32.lib

.data
ClassName db "SimpleWinClass",0
AppName  db "Our First Window",0
char WPARAM 20h                         ; the character the program receives from keyboard

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke GetCommandLine
    mov CommandLine,eax

    invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
    invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND
    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInst
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,/
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,/
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,/
           hInst,NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,SW_SHOWNORMAL
    invoke UpdateWindow, hwnd
    .WHILE TRUE
                invoke GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
                invoke TranslateMessage, ADDR msg
                invoke DispatchMessage, ADDR msg
        .ENDW
    mov     eax,msg.wParam
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    LOCAL hdc:HDC
    LOCAL ps:PAINTSTRUCT

    .IF uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSEIF uMsg==WM_CHAR
        push wParam
        pop  char
        invoke InvalidateRect, hWnd,NULL,TRUE
    .ELSEIF uMsg==WM_PAINT
        invoke BeginPaint,hWnd, ADDR ps
        mov    hdc,eax
        invoke TextOut,hdc,0,0,ADDR char,1
        invoke EndPaint,hWnd, ADDR ps
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor    eax,eax
    ret
WndProc endp
end start
 

分析:


char WPARAM 20h               ; the character the program receives from keyboard

這個變量將保存從鍵盤接收到的字符。因爲它是在窗口過程中通過WPARAM型變量傳送的,所以我們簡單地把它定義爲WPARAM型。由於我們的窗口在初次刷新時(也即剛被創建的那一次)是沒有鍵盤輸入的所以我們把他設成空格符(20h),這樣顯示時您就什麼都看不見。

  .ELSEIF uMsg==WM_CHAR
        push wParam
        pop  char
        invoke InvalidateRect, hWnd,NULL,TRUE

這一段是用來處理WM_CHAR消息的。它把接收到的字符放入變量char中,接着調用InvalidateRect,而InvalidateRect使得窗口的客戶區無效,這樣它會發出WM_PAINT消息,而WM_PAINT消息迫使WINDOWS重新繪製它的客戶區。該函數的語法如下:

InvalidateRect proto hWnd:HWND,/
                                 lpRect:DWORD,/
                                 bErase:DWORD

lpRect是指向客戶區我們想要其無效的一個正方形結構體的指針。如果該值等於NULL,則整個客戶區都無效;布爾值bErase告訴WINDOWS是否擦除背景,如果是TRUE,則WINDOWS在調用BeginPaint函數時把背景擦掉。 所以我們此處的做法是:我們將保存所有有關重繪客戶區的數據,然後發送WM_PAINT消息,處理該消息的程序段然後根據相關數據重新繪製客戶區。儘管這麼做事有點像走了弓背,但WINDOWS要處理那麼龐大的消息羣,沒有一定的規矩可不行。實際上我們完全可以通過調用GetDC 獲得設備上下文句柄,然後繪製字符,然後再調用ReleaseDC釋放設備上下文句柄,毫無疑問這樣也能在客戶區繪製出正確的字符。但是如果這之後接收到WM_PAINT消息要處理時,客戶區會重新刷新,而我們這稍前所繪製的字符就會消失掉。所以爲了讓字符一直正確地顯示,就必須把它們放到WM_PAINT的處理過程中處理。而在本消息處理中發送WM_PAINT消息即可。

invoke TextOut,hdc,0,0,ADDR char,1

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

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