00. 目录

01. INPUT_RECORD结构

描述控制台输入缓冲区中的输入事件。可以使用ReadConsoleInputPeekConsoleInput函数从输入缓冲区读取这些记录,也可以使用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×05X1鼠标按钮
VK_XBUTTON2 0×06X2鼠标按钮
**- **0×07未定义
VK_BACK0x08的BACKSPACE键
VK_TAB×09TAB键
-0x0A至0B保留的
VK_CLEAR0x0C清除键
VK_RETURN0X0D回车键
-为0x0E-0F未定义
VK_SHIFT为0x10SHIFT键
VK_CONTROL为0x11CTRL键
VK_MENU0×12ALT键
VK_PAUSE0×13暂停键
VK_CAPITAL0×14CAPS LOCK键
-0x1A的未定义
VK_ESCAPE0x1BESC键
VK_CONVERT为0x1CIME转换
VK_NONCONVERT0x1DIME非转换
VK_ACCEPT0X1EIME接受
VK_MODECHANGE为0x1FIME模式更改请求
VK_SPACE为0x20空格键
VK_PRIOR为0x21PAGE UP键
VK_NEXT为0x22PAGE DOWN键
VK_END0×23结束键
VK_HOME0X24Home键
VK_LEFT0x25左箭头键
VK_UP0×26向上箭头键
VK_RIGHT0×27右箭头键
VK_DOWN0×28向下箭头键
VK_SELECT0x29SELECT键
VK_PRINT0x2A打印键
VK_EXECUTE0x2B访问执行键
VK_SNAPSHOT0x2c上PRINT SCREEN键
VK_INSERT0x2DINS键
VK_DELETE0x2E之间DEL键
VK_HELP值为0x2F帮助键
的0x300键
0X311键
0x322键
0x333键
0x344键
0x355键
0x366键
0×377键
0x388键
0x399键
-0x3A-40未定义
的0x41关键
的0x42B键
0x43中C键
0×44D键
0×45E键
0×46F键
0X47G键
0x48H键
×49我关键
0x4AJ键
0x4BK键
0x4CL键
送出0x4dM键
0x4EN键
0x4F哦关键
为0x50P键
0x51Q键
0×52R键
0x53S键
0x54T键
0x55的U键
0x56储存V键
的0x57W键
将0x58X键
0×59Y键
0x5AZ键
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_F10x70F1键
VK_F20x71F2键
VK_F30x72F3键
VK_F40x73F4键
VK_F50x74F5键
VK_F60x75F6键
VK_F70x76F7键
VK_F80x77F8键
VK_F90x78F9键
VK_F100x79的F10键
VK_F110x7AF11键
VK_F120x7BF12键
VK_F130x7CF13键
VK_F140x7DF14键
VK_F15的0x7EF15键
VK_F160x7F的F16键
VK_F170x80的F17键
VK_F180×81F18键
VK_F19为0x82F19键
VK_F200×83F20键
VK_F21的0x84F21键
VK_F220x85F22键
VK_F230x86可以F23键
VK_F2487H的F24键
-0x88-8F未分配
VK_NUMLOCK的0x90NUM LOCK键
VK_SCROLL0x91滚动锁定键
0x92-96OEM特定
-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保留的
0xE1OEM特定
VK_OEM_1020xE2RT 102键键盘上的尖括号键或反斜杠键
0xE3-E4OEM特定
VK_PROCESSKEY为0xE5IME PROCESS键
0xE6OEM特定
VK_PACKET0xE7用于传递Unicode字符,就像它们是击键一样。VK_PACKET键是用于非键盘输入方法的32位虚拟键值的低位字。有关更多信息,请参阅KEYBDINPUTSendInputWM_KEYDOWNWM_KEYUP中的备注
-0xE8未分配
0xE9-F5OEM特定
VK_ATTN0xF6关键
VK_CRSEL0xF7CrSel密钥
VK_EXSEL0xF8的ExSel密钥
VK_EREOF0xF9擦除EOF键
VK_PLAY0xFA回应播放键
VK_ZOOM0xFB的才能缩放键
VK_NONAME0xFC有保留的
VK_PA10xFDPA1键
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;
}

【注意】

05-27 04:28