Windows学习笔记12——键盘相关<一>

来源:互联网 发布:php把soap 对象转json 编辑:程序博客网 时间:2024/05/04 13:25

       键盘输入是以消息的形式传递给应用程序的窗口过程。

1、当键盘按键按下时,谁获得焦点?

       Windows下可以有多个应用程序,而且每个应用程序可能有多个窗口,而只共用一个键盘,当一个按键按下时,是哪个应用程序的哪个窗口获得了焦点,这必须知道的。

       *当按键按下,发送的按键消息包含接收窗口的信息,它指定接收这个消息的那个窗口过程。我们称接收特定键盘事件的窗口具有输入焦点。

       *当前窗口(活动窗口)的特征:它通常是顶层窗口(父窗口为NULL)。

       *有输入焦点的窗口可以是活动窗口或活动窗口的子窗口。

       *当所有应用程序都最小化时,键盘的输入焦点不在任何窗口中,但是如果有键盘事件,Windows仍然会向活动窗口发送键盘消息,不过这时发送的消息的形式与非最小化的活动窗口的键盘消息形式不同。

       窗口过程可以拦截WM_SETFOCUS和WM_KILLFOCUS消息来判断它的窗口何时获得输出焦点,何时失去焦点。

       WM_SETFOCUS:是在窗口获得键盘输入焦点后,向其发送WM_SETFOCUS,也就是说,当窗口过程接收到WM_SETFOCUS时,该窗口刚刚获得键盘输入焦点。

       WM_KILLFOCUS:是在系统切换键盘输入焦点时,向将要失去焦点的窗口发送该消息。也就是说,当窗口接收到该消息时,该窗口即将失去键盘输入焦点。

2、系统队列与同步

      当使用者操作键盘时,Windows和键盘驱动程序将硬件扫描码转换为格式消息后,存放到“系统消息队列”中,然后当应用程序处理完前一个键盘消息后,Windows才会从系统消息队列中取出一个消息,并将其放入应用程序的消息队列中。

      注:“系统消息队列”,是一个独立于任何应用程序的消息队列,由Windows来维护。

      为什么要这样做?为什么要同步?

      接收键盘输入的窗口就是有输入焦点的窗口,使用者的输入速度可能比应用程序处理按键的速度快,而且特定的按键可能会使焦点从一个窗口切换到另一个窗口,后来的按键就输入到了另一个窗口,但是后来的按键已经记下目标窗口的位址,并放入应用程序的消息队列,那么后来的按键就不能输入到另一个窗口。

3、字元和按键

       应用程序从Windows接收的关于键盘事件消息可以分为:按键和字元两类。

       对于可产生显示字元的按键组合,Windows不仅给程序发送按键消息,而且还发送字元消息;对于不产生显示字元的按键(如Shift、Ctrl、Alt等),windows只产生按键消息。

4、按键消息

       当按下键盘上的一个键时,Windows把WM_KEYDOWN或者WM_SYSKEYDOWN消息放入有输入焦点的窗口的消息队列中,当释放一个按键时,Windows把WM_KEYUP或者WM_SYSKEYUP消息放入消息队列:

       WM_SYSKEYDOWN和WM_SYSKEYUP消息经常由与Alt键相组合的按键产生,这些称为“系统按键”,标识这些按键对Windows本身比对Windows应用程序更加重要。应用程序通常忽略WM_SYSKEYDOWN和WM_SYSKEYUP消息,并将它们传递给DefWindowProc处理。

        WM_KEYDOWN和WM_KEYUP消息,应用程序可以处理也可不处理,Windows本身并不处理这些消息。

        <1>、虚拟键码(用“按键消息”的wParam参数标识)

        虚拟键码保存在WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN和WM_SYSKEYUP消息的wParam参数中,此代码标志按下或是释放的键。

       真实的键码由实际键盘硬件产生。在Windows文件中将这些键码称为“扫描码(scan codes)”,但是扫描码与键盘的实际布局相关,Windows开发者为了以与设备无关的方式处理键盘,定义了所谓的“虚拟键码”.

       *大多数虚拟键码定义在WINUSER.H头文件中,以VK_开头。

       *Backspace、Tab、Enter、Escape和Spacebar键通常用于Windows程序,但是Windows一般用“字元消息”(而不是按键消息)来处理这些键。

       *Windows程序通常不需要监视Shift、Ctrl或Alt键的状态。

       *字母和数字的虚拟键码是ASCII码,Windows几乎不使用这些虚拟键码,而是使用ASCII码字元的字元消息。

      <2>、按键消息的lParam参数

      lParam参数的32位,分为6个栏位:

                 0——15位:按键的重复次数

                 16——23位:OEM扫描码(键盘硬件实际产生的代码)OEM指PC的原始设备制造商。

                 24位:扩展键标记

                 25——28位:保留

                 29位:内容代码(Context Code)

                 30位:键的先前状态(Previous key State)

                 31位:转换状态(Transition State)

      注:重复计数:按下一个键,如果窗口过程程序处理不够快,以致不能处理自动重复速率下的按键消息,Windows就把几个WM_KEYDOWN或WM_SYSKEYDOWN消息组合到单个消息中,并相应地增加重复计数。WM_KEYUP或WM_SYSKEYUP消息的重复计数总是为1。

        扩展键标记:如果该标记为1,则表示按键结果来自IBM增强键盘的附加按键,Windows通常忽略扩展键标记。

       内容代码:在按键时,如果同时按下ALT键,那么内容代码为1;对于WM_SYSKEYDOWN和WM_KEYUP而言,此位元总是为1; 对于WM_KEYDOWN和WM_KEYUP而言,此位元为0。

            两个特殊情况:*当活动窗口最小化时,则它没有输入焦点,这时所有的按键消息都会产生WM_SYSKEYDOWN和WM_SYSKEYUP消息,如果Alt键未被按下,则内容代码栏位被设定为0。

          *对于“非英文”键盘,有些字元是通过Shift、Ctrl或Alt键与其他键相组合产生的。这时内容代码栏位为1,但是此消息并不是系统按键消息。

       键的先前状态:

       如果在此之前键时释放的,则键的先前状态为0,否则为1;

       转换状态:

       如果键正被按下,则转换状态为0;如果键正在被释放,则转换状态为1。例如WM_KEYUP或WM_SYSKEYUP消息,此栏位为1,WM_KEYDOWN或WM_SYSKEYDOWN消息,此栏位为0。

      <3>、位移状态

      在处理按键消息时,程序写作者可能需要知道是否按下了位移键(Shift、Ctrl和Alt)或开关键(CapsLock、NumLock和ScrollLock)。调用GetKeyState函数,可以获得此信息。

       例如: iState = GetKeyState(VK_SHIFT);//如果按下了Shift键,则iState值为负。

       同时,在使用GetKeyState时,可以用识别字:VK_LSHIFT、VK_RSHIFT、VK_LCONTROL、 VK_RCONTROL、VK_LMENU、VK_RMENU来确定按下的shift、control、Alt键是左边的还是右边的。(注:这些识别字只能用于GetKeyState、GetAsyncKeyState)。

       *监视鼠标键与按键的组合的应用程序,通常的做法是在接收鼠标消息时检查按键。

       *注意GetKeyState并非即时检查键盘状态,而只是检查直到目前为止正在处理的消息的键盘状态。因此,正确的使用是:例如,程序判断使用者是否按下了Shift+Tab,应该在程序处理Tab键的WM_KEYDOWN消息时,调用GetKeyState函数,并传递VK_SHIFT参数。如果GetKeyState返回负值,则表示用户在按下Tab之前,按下了Shift键。即时在程序处理Tab键之前释放了Shift键也没有关系,只要在按下Tab键时Shift键时按下的。

       *由上述可知,GetKeyState不会使使用者获得独立于普通键盘消息的键盘消息,如果确实需要知道目前某键的状态,可以使用GetAsyncKeyState函数。

5、使用Home、PgUp、PgDn、End等卷动滚动列:
      窗口过程中添加如下代码,可将关于卷动列的按键、鼠标操作进行相同的消息处理。

  

            case WM_KEYDOWN:switch(wParam){case VK_HOME:SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);break;case VK_END:SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM,0);break;case VK_PRIOR:SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);break;case VK_NEXT:SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);break;case VK_UP:SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);break;case VK_DOWN:SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);break;case VK_LEFT:SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP,0);break;case VK_RIGHT:SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0);break;default:break;}


6、字元消息

       由于不同国家和地区的键盘配置不同,程序员不应该利用位移状态信息把按键信息翻译为字元信息。Windows已经完成了这部分工作。

       消息循环中的TranslateMessage函数,就是将按键信息转换为字元信息。

       如果消息为WM_KEYDOWN或WM_SYSKEYDOWN,并且按键与位移状态相组合产生一个字元,则TranslateMessage函数就发送一个字元消息到消息队列中。该字元消息将是GetMessage从消息队列中得到的按键消息之后的下一个消息。

       自己理解:字元消息的最初产生位置就在TranslateMessage函数中,键盘事件触发Windows系统产生的消息只有按键消息,附加上位移状态信息。当键盘上控制按键触发,TranslateMessage不做处理,对于键盘字符按键触发,TranlateMessage将按键转换为字元消息。

        特别注意:尽管TranslateMessage依据能产生字元的按键消息生成字元消息,但是并不意味着用字元消息代替先前的按键消息,而是在TranslateMessage返回时,将按键消息继续通过消息汞传递到窗口过程。

       <1>、字元消息的分类

       字元消息分为四类:非系统字元:WM_CHAR和WM_DEADCHAR

                                       系统字元:WM_SYSCHAR和WM_SYSDEADCHAR

       这四类字元消息的lParam参数与产生字元代码消息的“按键消息”的lParam参数相同。wParam不是虚拟键码,而是ANSI或Unicode字元代码。

       小知识:判断窗口过程是否能够处理Unicode消息,可以调用IsWindowUnicode函数:

                     BOOL fUnicode = IsWindowUnicode(hwnd);

      <2>、消息顺序

       因为TranslaeMessage函数从WM_KEYDOWN和WM_SYSKEYDOWN消息产生字元消息,所以字元消息是夹在按键消息之间传递给窗口过程的。

       例如:在CapsLock未打开时,使用者按下并释放“A”键,窗口过程将按下列顺序接收到下列消息:

       

      进一步分析:使用者按下shift键,再按下A键,释放A键,释放shift键,窗口过程将按下列顺序收到五个消息:

     

     注意:shift键不产生字元消息。

     接下来,用户按下A键,暂不释放,以使自动重复产生一系列的按键,这时每条WM_KEYDOWN消息都会产生一条字元消息,窗口过程将按以下顺序收到一系列消息:

    

    注意:上述消息,如果某条WM_KEYDOWN的重复计数大于1,相应的WM_CHAR消息将具有同样的重复计数(因为它们的lParam参数相同)。

     <3>、关于Ctrl与字母键的组合用作功能表加速键。

            这时候,不会将字母键转换为字元消息,而是转换为ASCII控制代码。

     <4>、处理控制字元

           键盘字元,处理WM_CHAR消息;

           游标键、功能键、Delet、Insert、Shift、Ctrl以及Alt键,处理WM_KEYDOWN消息;

           Tab、Enter、Backspace和Escape键当成控制字元,处理WM_CHAR消息:如下:

           

case WM_CHAR:{      switch(wParam)     {       case '\b'://退格          //其他程序行          break;       case '\t'://tab              //....              break;       case '\n'://换行          //...              break;       case '\r'://回车          //...              break;       default:              //...              break;     }}


      <5>、死字元消息

      了解一下:在某些非英语键盘,有些键用于给字母加音调。因为它们本身不产生字元所以称为“死字元”。按下死字元,发送的消息称为“死字元消息”,通常Windows应用程序忽略“死字元消息”(WM_DEADCHAR和WM_SYSDEADCHAR)。

    

0 0
原创粉丝点击