Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1760237
  • 博文数量: 290
  • 博客积分: 10653
  • 博客等级: 上将
  • 技术积分: 3178
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-24 23:08
文章存档

2013年(6)

2012年(15)

2011年(25)

2010年(86)

2009年(52)

2008年(66)

2007年(40)

分类:

2007-12-10 22:50:07

Tutorial 6: Keyboard Input

第六课:键盘输入


We will learn how a Windows program receives keyboard input.

我们将学习windows程序是如何接收键盘输入的。

Download the example here.

Theory:

Since normally there's only one keyboard in each PC, all running Windows programs must share it between them. Windows is responsible for sending the key strokes to the window which has the input focus.

 

因为通常在每台PC仅有一个键盘,所以运行在windows中的程序都必须在它们之间共享的使用它(键盘)。Windows负责发送击键消息给当前获得输入焦点的窗口.


Although there may be several windows on the screen, only one of them has the input focus. The window which has input focus is the only one which can receive key strokes. You can differentiate the window which has input focus from other windows by looking at the title bar. The title bar of the window which has input focus is highlighted.

 

尽管一个屏幕上可能有几个窗口, 但在这些窗口中,仅有一个窗口有输入焦点.拥有输入焦点的那个窗口是唯一能接收击键消息的 .你能通过标题栏来区分

当前在屏幕上的那些窗口是那一个获得了输入焦点.因为获得输入焦点的窗口的标题栏是高亮显示的.


Actually, there are two main types of keyboard messages, depending on your view of the keyboard. You can view a keyboard as a collection of keys. In this case, if you press a key, Windows sends a WM_KEYDOWN message to the window which has input focus, notifying that a key is pressed. When you release the key, Windows sends a WM_KEYUP message. You treat a key as a button.

 

实际上,键盘消息有两种主要的类型,一种是依赖于你所看到的键盘.你能看到的键盘只过是一序列按键的集合.既然这样,如果你按一个键,windows将发送一个WM_KEYDOWN消息给当前有输入焦点的那个窗口,用来通知那个键被按下。当你释放这个键的时候,windows发送一个WM_KEYUP消息.所以你也可以把键盘当作一个个按钮.

 

Another way to look at the keyboard is that it's a character input device. When you press "a" key, Windows sends a WM_CHAR message to the window which has input focus, telling it that the user sends "a" character to it. In fact, Windows sends WM_KEYDOWN and WM_KEYUP messages to the window which has input focus and those messages will be translated to WM_CHAR messages by TranslateMessage call. The window procedure may decide to process all three messages or only the messages it's interested in. Most of the time, you can ignore WM_KEYDOWN and WM_KEYUP since TranslateMessage function call in the message loop translate WM_KEYDOWN and WM_KEYUP messages to WM_CHAR messages. We will focus on WM_CHAR in this tutorial.

 

另外一种情况是:把键盘看成一个字符输入设备.当你按下一个”a”键时,windows发送一个WM_CHAR的消息给当前具有输入焦点的那个窗口.告诉窗口用户发送了一个” a” 字符给它.实际上,widnows发送给这个窗口的消息是 WM_KEYDOWNWM_KEYUP,只不过这两个消息被windows调用TranslateMessage函数解释成WM_CHAR消息.而窗口程序能决定是处理所有的消息呢还是仅处理它感兴趣的那个消息.大多数时候,你可以忽略WM_KEYDOWNWM_KEYUP消息因为在消息循环中被调用的TranslateMessage函数将把WM_KEYDOWN消息和WM_KEYUP消息翻译成WM_CHAR消息.在这一课中,我们将焦点放在WM_CHAR消息上.
 

Example:

例子:

.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
 

Analysis:

分析:


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

                       这个程序从键盘接收的字符.

 

This is the variable that will store the character received from the keyboard. Since the character is sent in WPARAM of the window procedure, we define the variable as type WPARAM for simplicity. The initial value is 20h or the space since when our window refreshes its client area the first time, there is no character input. So we want to display space instead.

这个变量将存储那个从键盘接收来的字符.这个字符是通过WPARAM传给窗口过程的.我们把这个变量定义为WPARAM类型只是为了简单性.我们用20H或者是空格符的值来初始化这个变量. 因为我们的窗口第一次更新窗口客户区的时候是没有键盘输入的.所以我们应该用显示一个空白符来代替其它.

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

This is added in the window procedure to handle the WM_CHAR message. It just puts the character into the variable named "char" and then calls InvalidateRect. InvalidateRect makes the specified rectangle in the client area invalid which forces Windows to send WM_PAINT message to the window procedure. Its syntax is as follows:

这是添加在窗口程序中用来处理WM_CHAR消息的.它仅是把一个字符放进变量名为 char的变量中然后调用InvalidateRect函数.InvalidateRect函数使在客户区中的一块矩形区域无效(指定一无效区域) 以便强制windows系统发送一个WM_PAINT消息给窗口处理程序.它的句法如下:

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

lpRect is a pointer to the rectagle in the client area that we want to declare invalid. If this parameter is null, the entire client area will be marked as invalid.

LpRect是一个指向那个在客户区中我们希望它无效的矩形(即无效区域)的指针.如果这个参数为空(NULL) 那么它将使整个窗口的客户区都成为无效区域.


bErase is a flag telling Windows if it needs to erase the background. If this flag is TRUE, then Windows will erase the backgroud of the invalid rectangle when BeginPaint is called.

bErase是一个标志,它告诉windows是否需要擦除背景.如果这个标志是TRUE() ,那么当BeginPaint函数被调用的时候,windows系统将擦除无效区域的背景.

So the strategy we used here is that: we store all necessary information relating to painting the client area and generate WM_PAINT message to paint the client area. Of course, the codes in WM_PAINT section must know beforehand what's expected of them. This seems a roundabout way of doing things but it's the way of Windows.

因此,我们在这里用的策略是: 我们将保存所有关于重绘客户区的数据,然后发送WM_PAINT消息,随后处理WM_PAINT消息的程序段将根据相关的数据来重绘客户区。这样看起来像是绕了很大的弯子来做这样一件事 ,但是它就是windows的方式

Actually we can paint the client area during processing WM_CHAR message by calling GetDC and ReleaseDC pair. There is no problem there. But the fun begins when our window needs to repaint its client area. Since the codes that paint the character are in WM_CHAR section, the window procedure will not be able to repaint our character in the client area. So the bottom line is: put all necessary data and codes that do painting in WM_PAINT. You can send WM_PAINT message from anywhere in your code anytime you want to repaint the client area.

实际上我们可以通过在处理WM_CHAR消息的时候调用GetDCReleadeDC这对API函数来绘制客户区。这是没有任何问题的。但是这个函数在我们的窗口需要重绘它的客户区时才开始执行。因为绘制字符的代码是在WM_PAINT代码段中, 这样窗口过程将不能在客户区中重绘我们的字符。所以下面这一行是:把所有绘画的必需数据放在WM_PAINT中,这样,无论你想在什么时间重绘客户区,你都可以从源代码的任何地方发送WM_PAINT消息来重绘它。 

 

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

When InvalidateRect is called, it sends a WM_PAINT message back to the window procedure. So the codes in WM_PAINT section is called. It calls BeginPaint as usual to get the handle to device context and then call TextOut which draws our character in the client area at x=0, y=0. When you run the program and press any key, you will see that character echo in the upper left corner of the client window. And when the window is minimized and maximized again, the character is still there since all the codes and data essential to repaint are all gathered in WM_PAINT section.

InvalidateRect 函数被调用时,它发送一个WM_PAINT消息给窗口过程函数,所以在WM_PAINT代码段中的代码将被调用.它照常的调用BeginPaint函数来得到设备环境句柄然后调用TextOut函数在客户区中横纵坐标为0的地方为 开始画我们的字符.当你运行这个程序的时候或者是按了任何健盘上的按键,你将在窗口客户区的左上角看到这些字符.或者当你的窗口最小化后又再一次最大化时,这些字符仍然在那.因为所有重绘窗口客户区的代码和数据都在处理WM_PAINT消息的代码段中.


This article come from Iczelion's asm page

风向改变 翻译于 2007 12月10  

 

阅读(918) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~