<知识分享>
输入事件中的键盘事件通常有字符事件和按键事件,这些事件的附带信息构成了键盘输入的信息,而想要读取这些信息,是要通过API函数ReadConsoleInput来获取的,函数原型如下:
- BOOL ReadConsoleInput( //读取输入信息
- HANDLE hConsoleInput, //句柄
- PINPUT_RECORD lpBuffer, //输入事件结构体的指针
- DWORD nLength, //要读取的记录数
- LPDWORD lpNumberOfEventsRead //用来接受成功读取记录数的指针
- ); //如果该函数成功调用,返回非零值
- //输入事件结构体的指针可以是结构体数组的首地址,这样就可以一次性读取多个记录数。
下面介绍几个和读取键盘输入事件有关的结构体,各结构体原型如下:
- typedef struct _INPUT_RECORD //输入事件结构体
- {
- WORD EventType; //事件类型
- union
- {
- KEY_EVENT_RECORD KeyEvent; //按键事件
- MOUSE_EVENT_RECORD MouseEvent; //鼠标事件
- WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
- MENU_EVENT_RECORD MenuEvent;
- FOCUS_EVENT_RECORD FocusEvent;
- } Event; //具体的事件
- } INPUT_RECORD;
- /*
- 其中事件类型EventType的值有5种
- KEY_EVENT 代表Event包含一个KEY_EVENT_RECODE结构体
- MOUSE_EVENT 代表Event包含一个MOUSE_EVENT_RECODE结构体
- WINDOW_BUFFER_SIZE_EVENT 代表Event包含一个WINDOW_BUFFER_SIZE_EVENT_RECORD结构体
- MENU_EVENT 代表Event包含一个MENU_EVENT_RECORD结构体
- FOCUS_EVENT 代表Event包含一个FOCUS_EVENT_RECORD结构体
- */
- typedef struct _KEY_EVENT_RECORD //键盘事件结构体
- {
- BOOL bKeyDown; //按键状态,true代表键按下,false代表键释放
- WORD wRepeatCount; //按键次数
- WORD wVirtualKeyCode; //虚拟键
- WORD wVirtualScanCode; //虚拟键扫描码
- union
- {
- WCHAR UnicodeChar; //解释成Unicode宽字符
- CHAR AsciiChar; //解释成ASCII码字符
- } uChar;
- DWORD dwControlKeyState; //控制键状态
- } KEY_EVENT_RECORD;
- /*
- 控制键各状态的值
- ENHANCED_KEY 扩展键被按下
- LEFT_ALT_PRESSED 左Alt键被按下
- LEFT_CTRL_PRESSED 左Ctrl键被按下
- RIGHT_ALT_PRESSED 右Alt键被按下
- RIGHT_CTRL_PRESSED 右Ctrl键被按下
- NUMLOCK_ON 数字锁定被打开
- SCROLLLOCK_ON 滚动锁定被打开
- CAPSLOCK_ON 大写锁定被打开
- SHIFT_PRESSED Shift键被按下
- */
当输入事件为键盘事件时,事件类型就为键盘事件,为其他事件时,事件类型就为对应的事件。另外,键盘上每个有意义的键都对应着一个唯一的扫描码,虽然扫描码可以作为键的标识,但是它是依赖于具体的设备的。因此,在应用程序中,使用的往往是与具体设备无关的虚拟键代码。这种虚拟键代码是一种与具体设备无关的键盘编码。而控制键状态比如大写锁定开启状态,Ctrl键按下状态等、、、
下面是部分常用虚拟键代码表:
- /*
- 虚拟键代码 值 键名称
- -----------------------------------------------------
- VK_BACK 0x08 退格键
- VK_TAB 0x09 Tab键
- VK_RETURN 0x0D 回车键
- VK_SHIFT 0x10 Shift键
- VK_LSHIFT 0xA0 左Shift键
- VK_RSHIFT 0xA1 右Shift键
- VK_CONTROL 0x11 Ctrl键
- VK_LCONTROL 0xA2 左Ctrl键
- VK_RCONTROL 0xA3 右Ctrl键
- VK_MENU 0x12 Alt键
- VK_LMENU 0xA4 左Alt键
- VK_RMENU 0xA5 右Alt键
- VK_PAUSE 0x13 Pause键
- VK_CAPITAL 0x14 Caps Lock键
- VK_NUMLOCK 0x90 Num Lock键
- VK_SCROLL 0x91 Scroll Lock键
- VK_ESCAPE 0x1B Esc键
- VK_SPACE 0x20 空格键
- VK_PRIOR 0x21 Page Up键
- VK_NEXT 0x22 Page Down键
- VK_END 0x23 End键
- VK_HOME 0x24 Home键
- VK_LEFT 0x25 左方向键
- VK_UP 0x26 上方向键
- VK_RIGHT 0x27 右方向键
- VK_DOWN 0x28 下方向键
- VK_DELETE 0x2E Delete键
- VK_INSERT 0x2D Insert键
- '0' 0x30 0键(非小键盘)
- '1' 0x31 1键(非小键盘)
- '2' 0x32 2键(非小键盘)
- ... ... ...
- '9' 0x39 9键(非小键盘)
- 'A' 0x41 A键
- 'B' 0x42 B键
- ... ... ...
- 'Z' 0x5A Z键
- VK_SLEEP 0x5F Sleep键
- VK_NUMPAD0 0x60 小键盘0键
- VK_NUMPAD1 0x61 小键盘1键
- VK_NUMPAD2 0x62 小键盘2键
- ... ... ...
- VK_NUMPAD9 0x69 小键盘9键
- VK_MULTIPLY 0x6A 小键盘乘键*
- VK_ADD 0x6B 小键盘加键+
- VK_SUBTRACT 0x6D 小键盘减键-
- VK_DIVIDE 0x6F 小键盘除键/
- VK_DECIMAL 0x6E 小键盘点键.
- VK_F1 0x70 F1键
- VK_F2 0x71 F2键
- ... ... ...
- VK_F12 0x7B F12键
- VK_F13 0x7C F13键 注:别问我,我也不知道什么电脑有这么多键
- ... ... ...
- VK_F24 0x87 F24键
- VK_OEM_1 0xBA ;:键
- VK_OEM_2 0xBF /?键
- VK_OEM_3 0xC0 ·~键
- VK_OEM_4 0xDB [{键
- VK_OEM_5 0xDC \|键
- VK_OEM_6 0xDD ]}键
- VK_OEM_7 0xDE '"键
- VK_OEM_PLUS 0xBB =+键
- VK_OEM_MINUS 0xBD -_键
- VK_OEM_COMMA 0xBC ,<键
- VK_OEM_PERIOD 0xBE .>键
- */
以上是部分常用的微软虚拟键盘码表,想要知道更详细的,请参见MSDN。其中各个虚拟键的具体使用情况根据各人编译器或IDE等的不同而有所差异。下面是一个实现按Esc键就输出Esc的样例程序:
- #include <stdio.h>
- #include <stdlib.h>
- #include <windows.h>
- #include <conio.h>
- #define true 1
- #define false 0
- int main()
- {
- HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE); //获得标准输入设备句柄
- INPUT_RECORD keyrec; //定义输入事件结构体
- DWORD res; //定义返回记录
- for (;;)
- {
- ReadConsoleInput(handle_in, &keyrec, 1, &res); //读取输入事件
- if (keyrec.EventType == KEY_EVENT) //如果当前事件是键盘事件
- {
- if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) //当前事件的虚拟键为Esc键
- {
- printf("Esc ");
- }
- }
- }
- return 0;
- }
在上面的样例程序中,当你按下Esc键后又马上释放,程序会输出两次Esc,因为有两次事件的虚拟键代码都是Esc键的代码,一次是按下,一次是释放。如果要实现按下键后出现反应,释放不出现反应,那么将上例程序中第18行代码的条件改成
- if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE
- && keyrec.Event.KeyEvent.bKeyDown == true) //表示当前为键按下而不是键释放
就行了。
根据控制键的状态我们可以实现不同的状态输出不同的值以及组合键的实现,下面的样例程序在大写锁定打开时输入A键则输出大写字母A,否则输出小写字母a。而在Shift键被按下的状态是则输出Shift+A以及Shift+a。样例程序如下
- #include <stdio.h>
- #include <stdlib.h>
- #include <windows.h>
- #include <conio.h>
- #define true 1
- #define false 0
- int main()
- {
- HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE); //获得标准输入设备句柄
- INPUT_RECORD keyrec; //定义输入事件结构体
- DWORD res; //定义返回记录
- for (;;)
- {
- ReadConsoleInput(handle_in, &keyrec, 1, &res); //读取输入事件
- if (keyrec.EventType == KEY_EVENT) //如果当前事件是键盘事件
- {
- if (keyrec.Event.KeyEvent.wVirtualKeyCode == 'A'
- && keyrec.Event.KeyEvent.bKeyDown == true) //当按下字母A键时
- {
- if (keyrec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) //Shift键为按下状态
- {
- printf("Shift+");
- }
- if (keyrec.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON) //大写锁定为打开状态
- {
- printf("A ");
- }
- else //大写锁定关闭状态
- {
- printf("a ");
- }
- }
- }
- }
- return 0;
- }
由上例需要了解到的是,各个控制键状态的的确定并不是使用等于符号==而是按位与&运算符,因为在同一时刻可能有多种控制键状态值,比如各种锁定都被打开且各种控制键也被同时按下。使用&运算符则显得尤其高明,方便查询各个控制键的状态而不使之出现冲突。呵呵,不服不行啊,感慨一下,还是要多学习一下别人高明的地方,比如灵活运用位运算符实现各种功能等等······