00. 目录
01. INPUT_RECORD结构
描述控制台输入缓冲区中的输入事件。可以使用ReadConsoleInput或PeekConsoleInput函数从输入缓冲区读取这些记录,也可以使用WriteConsoleInput函数将这些记录写入输入缓冲区。
类型声明:
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
输入事件类型的句柄和存储在Event成员中的事件记录。
该成员可以是以下值之一。
值 | 含义 |
---|---|
FOCUS_EVENT 0x0010 | 该事件成员包含一个FOCUS_EVENT_RECORD结构。这些事件在内部使用,应该被忽略。 |
KEY_EVENT 0x0001 | 该事件成员包含一个KEY_EVENT_RECORD结构有关键盘事件的信息。 |
MENU_EVENT 0x0008 | 该事件成员包含一个MENU_EVENT_RECORD结构。这些事件在内部使用,应该被忽略。 |
MOUSE_EVENT 0x0002 | 所述事件构件包含MOUSE_EVENT_RECORD结构用约鼠标移动或按键按压事件的信息。 |
WINDOW_BUFFER_SIZE_EVENT 0x0004 | 该事件成员包含一个WINDOW_BUFFER_SIZE_RECORD结构有关控制台屏幕缓冲区的新大小信息。 |
事件
事件信息。此成员的格式取决于EventType成员指定的事件类型。
02. KEY_EVENT_RECORD结构
描述控制台INPUT_RECORD结构中的键盘输入事件。
typedef struct _KEY_EVENT_RECORD {
BOOL bKeyDown;
WORD wRepeatCount;
WORD wVirtualKeyCode;
WORD wVirtualScanCode;
union {
WCHAR UnicodeChar;
CHAR AsciiChar;
} uChar;
DWORD dwControlKeyState;
} KEY_EVENT_RECORD;
bKeyDown
如果按下该键,则该成员为TRUE。否则,此成员为FALSE(密钥已释放)。
wRepeatCount
重复计数,表示正在按住某个键。例如,当按下某个键时,您可能会获得五个事件,该成员等于1,一个事件的成员等于5,或者此成员的多个事件大于或等于1。
wVirtualKeyCode
虚拟键码识别在设备无关的方式给定的键。
wVirtualScanCode
给定键的虚拟扫描代码,表示键盘硬件生成的与设备相关的值。
uChar
以下成员的联盟。
UnicodeChar
翻译的Unicode字符。
AsciiChar
翻译的ASCII字符。
dwControlKeyState
控制键的状态。该成员可以是以下一个或多个值。
值 | 含义 |
---|---|
CAPSLOCK_ON 0x0080 | 大写锁定被打开 |
ENHANCED_KEY 0x0100 | 扩展键被按下 |
LEFT_ALT_PRESSED 0x0002 | 按下左ALT键。 |
LEFT_CTRL_PRESSED 0x0008 | 按下左CTRL键。 |
NUMLOCK_ON 0x0020 | 数字锁定被打开 |
RIGHT_ALT_PRESSED 0x0001 | 按下右ALT键。 |
RIGHT_CTRL_PRESSED 0x0004 | 按下右CTRL键。 |
SCROLLLOCK_ON 0x0040 | 滚动锁定被打开 |
SHIFT_PRESSED 0x0010 | 按下SHIFT键。 |
虚拟键值码表
下表显示了系统使用的虚拟键代码的符号常量名称,十六进制值以及鼠标或键盘等效项。代码按数字顺序列出。
恒/值 | 描述 |
---|---|
VK_LBUTTON 0×01 | 鼠标左键 |
VK_RBUTTON 0×02 | 鼠标右键 |
VK_CANCEL 0×03 | 控制中断处理 |
VK_MBUTTON 0×04 | 鼠标中键(三键鼠标) |
VK_XBUTTON1 0×05 | X1鼠标按钮 |
VK_XBUTTON2 0×06 | X2鼠标按钮 |
**- **0×07 | 未定义 |
VK_BACK0x08的 | BACKSPACE键 |
VK_TAB×09 | TAB键 |
**-**0x0A至0B | 保留的 |
VK_CLEAR0x0C | 清除键 |
VK_RETURN0X0D | 回车键 |
**-**为0x0E-0F | 未定义 |
VK_SHIFT为0x10 | SHIFT键 |
VK_CONTROL为0x11 | CTRL键 |
VK_MENU0×12 | ALT键 |
VK_PAUSE0×13 | 暂停键 |
VK_CAPITAL0×14 | CAPS LOCK键 |
**-**0x1A的 | 未定义 |
VK_ESCAPE0x1B | ESC键 |
VK_CONVERT为0x1C | IME转换 |
VK_NONCONVERT0x1D | IME非转换 |
VK_ACCEPT0X1E | IME接受 |
VK_MODECHANGE为0x1F | IME模式更改请求 |
VK_SPACE为0x20 | 空格键 |
VK_PRIOR为0x21 | PAGE UP键 |
VK_NEXT为0x22 | PAGE DOWN键 |
VK_END0×23 | 结束键 |
VK_HOME0X24 | Home键 |
VK_LEFT0x25 | 左箭头键 |
VK_UP0×26 | 向上箭头键 |
VK_RIGHT0×27 | 右箭头键 |
VK_DOWN0×28 | 向下箭头键 |
VK_SELECT0x29 | SELECT键 |
VK_PRINT0x2A | 打印键 |
VK_EXECUTE0x2B访问 | 执行键 |
VK_SNAPSHOT0x2c上 | PRINT SCREEN键 |
VK_INSERT0x2D | INS键 |
VK_DELETE0x2E之间 | DEL键 |
VK_HELP值为0x2F | 帮助键 |
的0x30 | 0键 |
0X31 | 1键 |
0x32 | 2键 |
0x33 | 3键 |
0x34 | 4键 |
0x35 | 5键 |
0x36 | 6键 |
0×37 | 7键 |
0x38 | 8键 |
0x39 | 9键 |
**-**0x3A-40 | 未定义 |
的0x41 | 关键 |
的0x42 | B键 |
0x43中 | C键 |
0×44 | D键 |
0×45 | E键 |
0×46 | F键 |
0X47 | G键 |
0x48 | H键 |
×49 | 我关键 |
0x4A | J键 |
0x4B | K键 |
0x4C | L键 |
送出0x4d | M键 |
0x4E | N键 |
0x4F | 哦关键 |
为0x50 | P键 |
0x51 | Q键 |
0×52 | R键 |
0x53 | S键 |
0x54 | T键 |
0x55的 | U键 |
0x56储存 | V键 |
的0x57 | W键 |
将0x58 | X键 |
0×59 | Y键 |
0x5A | Z键 |
VK_LWIN0x5B | 左键Windows键(自然键盘) |
VK_RWIN0x5C | 右键Windows键(自然键盘) |
VK_APPS0x5D | 应用键(自然键盘) |
**-**0x5E | 保留的 |
VK_SLEEP0x5F的 | 电脑睡眠键 |
VK_NUMPAD00X60 | 数字小键盘0键 |
VK_NUMPAD10x61 | 数字小键盘1键 |
VK_NUMPAD20X62 | 数字小键盘2键 |
VK_NUMPAD30x63 | 数字小键盘3键 |
VK_NUMPAD40x64 | 数字键盘4键 |
VK_NUMPAD50x65 | 数字键盘5键 |
VK_NUMPAD60x66 | 数字小键盘6键 |
VK_NUMPAD70×67 | 数字小键盘7键 |
VK_NUMPAD80x68 | 数字小键盘8键 |
VK_NUMPAD90×69 | 数字小键盘9键 |
VK_MULTIPLY的0x6A | 乘以键 |
VK_ADD0x6B | 添加密钥 |
VK_SEPARATOR0x6C | 分隔键 |
VK_SUBTRACT0x6D | 减去关键 |
VK_DECIMAL0x6E | 十进制键 |
VK_DIVIDE0x6F | 划分密钥 |
VK_F10x70 | F1键 |
VK_F20x71 | F2键 |
VK_F30x72 | F3键 |
VK_F40x73 | F4键 |
VK_F50x74 | F5键 |
VK_F60x75 | F6键 |
VK_F70x76 | F7键 |
VK_F80x77 | F8键 |
VK_F90x78 | F9键 |
VK_F100x79的 | F10键 |
VK_F110x7A | F11键 |
VK_F120x7B | F12键 |
VK_F130x7C | F13键 |
VK_F140x7D | F14键 |
VK_F15的0x7E | F15键 |
VK_F160x7F的 | F16键 |
VK_F170x80的 | F17键 |
VK_F180×81 | F18键 |
VK_F19为0x82 | F19键 |
VK_F200×83 | F20键 |
VK_F21的0x84 | F21键 |
VK_F220x85 | F22键 |
VK_F230x86可以 | F23键 |
VK_F2487H的 | F24键 |
**-**0x88-8F | 未分配 |
VK_NUMLOCK的0x90 | NUM LOCK键 |
VK_SCROLL0x91 | 滚动锁定键 |
0x92-96 | OEM特定 |
**-**0x97-9F | 未分配 |
VK_LSHIFT0XA0 | 左SHIFT键 |
VK_RSHIFT0xA1 | 右SHIFT键 |
VK_LCONTROL0xA2 | 左控制键 |
VK_RCONTROL0xA3执行 | 右控制键 |
VK_LMENU0xA4 | 左MENU键 |
VK_RMENU的0xA5 | 右键MENU |
VK_BROWSER_BACK0xA6 | 浏览器返回键 |
VK_BROWSER_FORWARD0xA7 | 浏览器转发键 |
VK_BROWSER_REFRESH0xA8 | 浏览器刷新键 |
VK_BROWSER_STOP0xA9 | 浏览器停止键 |
VK_BROWSER_SEARCH和0xAA | 浏览器搜索键 |
VK_BROWSER_FAVORITES是0xAB | 浏览器收藏夹键 |
VK_BROWSER_HOME0xAC | 浏览器开始和主页键 |
VK_VOLUME_MUTE0xAD,将 | 音量静音键 |
VK_VOLUME_DOWN0xAE | 降低音量键 |
VK_VOLUME_UP0xAF执行 | 提高音量键 |
VK_MEDIA_NEXT_TRACK0XB0 | 下一曲目键 |
VK_MEDIA_PREV_TRACK0xB1 | 上一曲目键 |
VK_MEDIA_STOP0xB2 | 停止媒体键 |
VK_MEDIA_PLAY_PAUSE0xB3 | 播放/暂停媒体键 |
VK_LAUNCH_MAIL0xB4 | 启动邮件密钥 |
VK_LAUNCH_MEDIA_SELECT0xB5执行 | 选择媒体键 |
VK_LAUNCH_APP10xB6 | 启动应用程序1键 |
VK_LAUNCH_APP20xB7 | 启动应用程序2密钥 |
**-**0xB8-B9 | 保留的 |
VK_OEM_10xBA | 用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,使用’;:'键 |
VK_OEM_PLUS为0xBB | 对于任何国家/地区,请使用“+”键 |
VK_OEM_COMMA0xBC | 对于任何国家/地区,请使用“,”键 |
VK_OEM_MINUS0xBD | 对于任何国家/地区,请使用“ - ”键 |
VK_OEM_PERIOD0xBE | 对于任何国家/地区,’。’ 键 |
VK_OEM_2为0xBF | 用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,’/?’ 键 |
VK_OEM_3将0xC0 | 用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,使用’〜'键 |
**-**0xC1-D7 | 保留的 |
**-**0xD8-DA | 未分配 |
VK_OEM_4位于0xDB | 用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,’[{'键 |
VK_OEM_5的0xDC | 用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,’\ |’ 键 |
VK_OEM_60xDD | 用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,使用’]}'键 |
VK_OEM_7写0xDE | 用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,“单引号/双引号”键 |
VK_OEM_80xDF | 用于杂项字符; 它可以因键盘而异。 |
**-**取0xE0 | 保留的 |
0xE1 | OEM特定 |
VK_OEM_1020xE2 | RT 102键键盘上的尖括号键或反斜杠键 |
0xE3-E4 | OEM特定 |
VK_PROCESSKEY为0xE5 | IME PROCESS键 |
0xE6 | OEM特定 |
VK_PACKET0xE7 | 用于传递Unicode字符,就像它们是击键一样。VK_PACKET键是用于非键盘输入方法的32位虚拟键值的低位字。有关更多信息,请参阅KEYBDINPUT,SendInput,WM_KEYDOWN和WM_KEYUP中的备注 |
**-**0xE8 | 未分配 |
0xE9-F5 | OEM特定 |
VK_ATTN0xF6 | 关键 |
VK_CRSEL0xF7 | CrSel密钥 |
VK_EXSEL0xF8的 | ExSel密钥 |
VK_EREOF0xF9 | 擦除EOF键 |
VK_PLAY0xFA回应 | 播放键 |
VK_ZOOM0xFB的才能 | 缩放键 |
VK_NONAME0xFC有 | 保留的 |
VK_PA10xFD | PA1键 |
VK_OEM_CLEAR0xFE的 | 清除键 |
官方参考手册: https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes
03. ReadConsoleInput函数
从控制台输入缓冲区读取数据并将其从缓冲区中删除。
函数声明:
BOOL WINAPI ReadConsoleInput(
_In_ HANDLE hConsoleInput,
_Out_ PINPUT_RECORD lpBuffer,
_In_ DWORD nLength,
_Out_ LPDWORD lpNumberOfEventsRead
);
功能:
从控制台输入缓冲区读取数据并将其从缓冲区中删除。
参数:
hConsoleInput 控制台输入缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。
lpBuffer 指向接收输入缓冲区数据的INPUT_RECORD结构数组的指针。
nLength 数组元素中lpBuffer参数 指向的数组大小。
lpNumberOfEventsRead 指向接收读取的输入记录数的变量的指针。
返回值:
如果函数成功,则返回值为非零值。
如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。
官方参考网址:https://docs.microsoft.com/en-us/windows/console/readconsoleinput
04. 示例程序
示例程序一:
用户按下ESC键,则终端输出ESC
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <Windows.h>
#include <conio.h>
int main(void)
{
//定义句柄变量
HANDLE hOut = NULL;
HANDLE hIn = NULL;
//定义输入事件结构体
INPUT_RECORD keyRecord;
//用于存储读取记录
DWORD res;
//获取标准输出句柄
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
//获取标准输入句柄
hIn = GetStdHandle(STD_INPUT_HANDLE);
while (1)
{
//读取输入事件
ReadConsoleInput(hIn, &keyRecord, 1, &res);
//如果当前事件是键盘事件
if (keyRecord.EventType == KEY_EVENT)
{
//单击鼠标左键
if (keyRecord.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
{
printf("用户按下了ESC键");
}
}
}
//关闭句柄
CloseHandle(hOut);
CloseHandle(hIn);
//system("pause");
getchar();
return 0;
}
在上面的样例程序中,当你按下Esc键后又马上释放,程序会输出两次Esc,因为有两次事件的虚拟键代码都是Esc键的代码,一次是按下,一次是释放。如果要实现按下键后出现反应,释放不出现反应。
代码优化如下:
//如果当前事件是键盘事件
if (keyRecord.EventType == KEY_EVENT)
{
//单击按键左键 如果是按下就输出, 如果是释放就不输出
if (keyRecord.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE
&& keyRecord.Event.KeyEvent.bKeyDown == 1)
{
printf("用户按下了ESC键");
}
}
示例程序二:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <Windows.h>
#include <conio.h>
int main(void)
{
//定义句柄变量
HANDLE hOut = NULL;
HANDLE hIn = NULL;
//定义输入事件结构体
INPUT_RECORD keyRecord;
//用于存储读取记录
DWORD res;
//获取标准输出句柄
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
//获取标准输入句柄
hIn = GetStdHandle(STD_INPUT_HANDLE);
while (1)
{
//读取输入事件
ReadConsoleInput(hIn, &keyRecord, 1, &res);
//如果当前事件是键盘事件
if (keyRecord.EventType == KEY_EVENT)
{
//单击按键左键 如果是按下就输出, 如果是释放就不输出
if (keyRecord.Event.KeyEvent.wVirtualKeyCode == 'B'
&& keyRecord.Event.KeyEvent.bKeyDown == 1)
{
if (keyRecord.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON)
{
printf("B");
}
else
{
printf("b");
}
}
}
}
//关闭句柄
CloseHandle(hOut);
CloseHandle(hIn);
//system("pause");
getchar();
return 0;
}
【注意】
各个控制键状态的的确定并不是使用等于符号==而是按位与&运算符,因为在同一时刻可能有多种控制键状态值,比如各种锁定都被打开且各种控制键也被同时按下。因为使用位操作,方便查询各个控制键的状态而不使之出现冲突。