C语言控制台窗口图形界面编程(七):键盘事件

       输入事件中的键盘事件通常有字符事件和按键事件,这些事件的附带信息构成了键盘输入的信息,而想要读取这些信息,是要通过API函数ReadConsoleInput来获取的,函数原型如下:

1 BOOL ReadConsoleInput(              //读取输入信息
2     HANDLE hConsoleInput,           //句柄
3     PINPUT_RECORD lpBuffer,         //输入事件结构体的指针
4     DWORD nLength,                  //要读取的记录数
5     LPDWORD lpNumberOfEventsRead    //用来接受成功读取记录数的指针
6 );  //如果该函数成功调用,返回非零值
7 //输入事件结构体的指针可以是结构体数组的首地址,这样就可以一次性读取多个记录数。


       下面介绍几个和读取键盘输入事件有关的结构体,各结构体原型如下:

 1 typedef struct _INPUT_RECORD    //输入事件结构体
 2 {
 3     WORD  EventType;    //事件类型
 4     union
 5     {
 6         KEY_EVENT_RECORD          KeyEvent;     //按键事件
 7         MOUSE_EVENT_RECORD        MouseEvent;   //鼠标事件
 8         WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
 9         MENU_EVENT_RECORD         MenuEvent;
10         FOCUS_EVENT_RECORD        FocusEvent;
11     } Event;    //具体的事件
12 } INPUT_RECORD;
13 /*
14 其中事件类型EventType的值有5种
15 KEY_EVENT                   代表Event包含一个KEY_EVENT_RECODE结构体
16 MOUSE_EVENT                 代表Event包含一个MOUSE_EVENT_RECODE结构体
17 WINDOW_BUFFER_SIZE_EVENT    代表Event包含一个WINDOW_BUFFER_SIZE_EVENT_RECORD结构体
18 MENU_EVENT                  代表Event包含一个MENU_EVENT_RECORD结构体
19 FOCUS_EVENT                 代表Event包含一个FOCUS_EVENT_RECORD结构体
20 */
21 
22 typedef struct _KEY_EVENT_RECORD    //键盘事件结构体 
23 {
24     BOOL  bKeyDown;             //按键状态,true代表键按下,false代表键释放
25     WORD  wRepeatCount;         //按键次数
26     WORD  wVirtualKeyCode;      //虚拟键
27     WORD  wVirtualScanCode;     //虚拟键扫描码
28     union
29     {
30         WCHAR UnicodeChar;      //解释成Unicode宽字符
31         CHAR  AsciiChar;        //解释成ASCII码字符
32     } uChar;
33     DWORD dwControlKeyState;    //控制键状态
34 } KEY_EVENT_RECORD;
35 /*
36 控制键各状态的值
37 ENHANCED_KEY        扩展键被按下
38 LEFT_ALT_PRESSED    左Alt键被按下
39 LEFT_CTRL_PRESSED   左Ctrl键被按下
40 RIGHT_ALT_PRESSED   右Alt键被按下
41 RIGHT_CTRL_PRESSED  右Ctrl键被按下
42 NUMLOCK_ON          数字锁定被打开
43 SCROLLLOCK_ON       滚动锁定被打开
44 CAPSLOCK_ON         大写锁定被打开
45 SHIFT_PRESSED       Shift键被按下
46 */

       当输入事件为键盘事件时,事件类型就为键盘事件,为其他事件时,事件类型就为对应的事件。另外,键盘上每个有意义的键都对应着一个唯一的扫描码,虽然扫描码可以作为键的标识,但是它是依赖于具体的设备的。因此,在应用程序中,使用的往往是与具体设备无关的虚拟键代码。这种虚拟键代码是一种与具体设备无关的键盘编码。而控制键状态比如大写锁定开启状态,Ctrl键按下状态等、、、


       下面是部分常用虚拟键代码表:

 1 /*
 2 虚拟键代码      值          键名称
 3 -----------------------------------------------------            
 4 VK_BACK         0x08        退格键
 5 VK_TAB          0x09        Tab键
 6 VK_RETURN       0x0D        回车键
 7 VK_SHIFT        0x10        Shift键
 8 VK_LSHIFT       0xA0        左Shift键
 9 VK_RSHIFT       0xA1        右Shift键
10 VK_CONTROL      0x11        Ctrl键
11 VK_LCONTROL     0xA2        左Ctrl键
12 VK_RCONTROL     0xA3        右Ctrl键
13 VK_MENU         0x12        Alt键
14 VK_LMENU        0xA4        左Alt键
15 VK_RMENU        0xA5        右Alt键
16 VK_PAUSE        0x13        Pause键
17 VK_CAPITAL      0x14        Caps Lock键
18 VK_NUMLOCK      0x90        Num Lock键
19 VK_SCROLL       0x91        Scroll Lock键
20 VK_ESCAPE       0x1B        Esc键
21 VK_SPACE        0x20        空格键
22 VK_PRIOR        0x21        Page Up键
23 VK_NEXT         0x22        Page Down键
24 VK_END          0x23        End键
25 VK_HOME         0x24        Home键
26 VK_LEFT         0x25        左方向键
27 VK_UP           0x26        上方向键
28 VK_RIGHT        0x27        右方向键
29 VK_DOWN         0x28        下方向键
30 VK_DELETE       0x2E        Delete键
31 VK_INSERT       0x2D        Insert键
32 '0'             0x30        0键(非小键盘)
33 '1'             0x31        1键(非小键盘)
34 '2'             0x32        2键(非小键盘)
35 ...             ...         ...
36 '9'             0x39        9键(非小键盘)
37 'A'             0x41        A键
38 'B'             0x42        B键
39 ...             ...         ...
40 'Z'             0x5A        Z键
41 VK_SLEEP        0x5F        Sleep键
42 VK_NUMPAD0      0x60        小键盘0键
43 VK_NUMPAD1      0x61        小键盘1键
44 VK_NUMPAD2      0x62        小键盘2键
45 ...             ...         ...
46 VK_NUMPAD9      0x69        小键盘9键
47 VK_MULTIPLY     0x6A        小键盘乘键*
48 VK_ADD          0x6B        小键盘加键+
49 VK_SUBTRACT     0x6D        小键盘减键-
50 VK_DIVIDE       0x6F        小键盘除键/
51 VK_DECIMAL      0x6E        小键盘点键.
52 VK_F1           0x70        F1键
53 VK_F2           0x71        F2键
54 ...             ...         ...
55 VK_F12          0x7B        F12键
56 VK_F13          0x7C        F13键      注:别问我,我也不知道什么电脑有这么多键
57 ...             ...         ...
58 VK_F24          0x87        F24键
59 VK_OEM_1        0xBA        ;:键
60 VK_OEM_2        0xBF        /?键
61 VK_OEM_3        0xC0        ·~键
62 VK_OEM_4        0xDB        [{键
63 VK_OEM_5        0xDC        |键
64 VK_OEM_6        0xDD        ]}键
65 VK_OEM_7        0xDE        '"键
66 VK_OEM_PLUS     0xBB        =+键
67 VK_OEM_MINUS    0xBD        -_键
68 VK_OEM_COMMA    0xBC        ,<键
69 VK_OEM_PERIOD   0xBE        .>键 
70 */

       以上是部分常用的微软虚拟键盘码表,想要知道更详细的,请参见MSDN。其中各个虚拟键的具体使用情况根据各人编译器或IDE等的不同而有所差异。下面是一个实现按Esc键就输出Esc的样例程序:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <windows.h>
 4 #include <conio.h>
 5 #define true 1
 6 #define false 0
 7 
 8 int main()
 9 {
10     HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);      //获得标准输入设备句柄
11     INPUT_RECORD keyrec;        //定义输入事件结构体
12     DWORD res;      //定义返回记录
13     for (;;)
14     {
15         ReadConsoleInput(handle_in, &keyrec, 1, &res);      //读取输入事件
16         if (keyrec.EventType == KEY_EVENT)      //如果当前事件是键盘事件
17         {
18             if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) //当前事件的虚拟键为Esc键
19             {
20                 printf("Esc ");
21             }
22         }
23     }
24     return 0;
25 }

       在上面的样例程序中,当你按下Esc键后又马上释放,程序会输出两次Esc,因为有两次事件的虚拟键代码都是Esc键的代码,一次是按下,一次是释放。如果要实现按下键后出现反应,释放不出现反应,那么将上例程序中第18行代码的条件改成

1 if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE
2     && keyrec.Event.KeyEvent.bKeyDown == true)     //表示当前为键按下而不是键释放

就行了。


       根据控制键的状态我们可以实现不同的状态输出不同的值以及组合键的实现,下面的样例程序在大写锁定打开时输入A键则输出大写字母A,否则输出小写字母a。而在Shift键被按下的状态是则输出Shift+A以及Shift+a。样例程序如下

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <windows.h>
 4 #include <conio.h>
 5 #define true 1
 6 #define false 0
 7 
 8 int main()
 9 {
10     HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);      //获得标准输入设备句柄
11     INPUT_RECORD keyrec;        //定义输入事件结构体
12     DWORD res;      //定义返回记录
13     for (;;)
14     {
15         ReadConsoleInput(handle_in, &keyrec, 1, &res);      //读取输入事件
16         if (keyrec.EventType == KEY_EVENT)      //如果当前事件是键盘事件
17         {
18             if (keyrec.Event.KeyEvent.wVirtualKeyCode == 'A'
19                 && keyrec.Event.KeyEvent.bKeyDown == true)   //当按下字母A键时
20             {
21                 if (keyrec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)    //Shift键为按下状态
22                 {
23                     printf("Shift+");
24                 }
25                 if (keyrec.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON)  //大写锁定为打开状态
26                 {
27                     printf("A ");
28                 }
29                 else        //大写锁定关闭状态
30                 {
31                     printf("a ");
32                 }
33             }
34         }
35     }
36     return 0;
37 }

       由上例需要了解到的是,各个控制键状态的的确定并不是使用等于符号==而是按位与&运算符,因为在同一时刻可能有多种控制键状态值,比如各种锁定都被打开且各种控制键也被同时按下。使用&运算符则显得尤其高明,方便查询各个控制键的状态而不使之出现冲突。呵呵,不服不行啊,感慨一下,还是要多学习一下别人高明的地方,比如灵活运用位运算符实现各种功能等等······

原文地址:https://www.cnblogs.com/JZQT/p/3802434.html