00. 目录


文章目录


  • 00. 目录
  • 01. INPUT_RECORD结构
  • 02. KEY_EVENT_RECORD结构
  • 03. ReadConsoleInput函数
  • 04. 示例程序



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_BACK​0x08的

BACKSPACE键

VK_TAB​×09

TAB键

**-**0x0A至0B

保留的

VK_CLEAR​0x0C

清除键

VK_RETURN​0X0D

回车键

**-**为0x0E-0F

未定义

VK_SHIFT​为0x10

SHIFT键

VK_CONTROL​为0x11

CTRL键

VK_MENU​0×12

ALT键

VK_PAUSE​0×13

暂停键

VK_CAPITAL​0×14

CAPS LOCK键

**-**0x1A的

未定义

VK_ESCAPE​0x1B

ESC键

VK_CONVERT​为0x1C

IME转换

VK_NONCONVERT​0x1D

IME非转换

VK_ACCEPT​0X1E

IME接受

VK_MODECHANGE​为0x1F

IME模式更改请求

VK_SPACE​为0x20

空格键

VK_PRIOR​为0x21

PAGE UP键

VK_NEXT​为0x22

PAGE DOWN键

VK_END​0×23

结束键

VK_HOME​0X24

Home键

VK_LEFT​0x25

左箭头键

VK_UP​0×26

向上箭头键

VK_RIGHT​0×27

右箭头键

VK_DOWN​0×28

向下箭头键

VK_SELECT​0x29

SELECT键

VK_PRINT​0x2A

打印键

VK_EXECUTE​0x2B访问

执行键

VK_SNAPSHOT​0x2c上

PRINT SCREEN键

VK_INSERT​0x2D

INS键

VK_DELETE​0x2E之间

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_LWIN​0x5B

左键Windows键(自然键盘)

VK_RWIN​0x5C

右键Windows键(自然键盘)

VK_APPS​0x5D

应用键(自然键盘)

**-**0x5E

保留的

VK_SLEEP​0x5F的

电脑睡眠键

VK_NUMPAD0​0X60

数字小键盘0键

VK_NUMPAD1​0x61

数字小键盘1键

VK_NUMPAD2​0X62

数字小键盘2键

VK_NUMPAD3​0x63

数字小键盘3键

VK_NUMPAD4​0x64

数字键盘4键

VK_NUMPAD5​0x65

数字键盘5键

VK_NUMPAD6​0x66

数字小键盘6键

VK_NUMPAD7​0×67

数字小键盘7键

VK_NUMPAD8​0x68

数字小键盘8键

VK_NUMPAD9​0×69

数字小键盘9键

VK_MULTIPLY​的0x6A

乘以键

VK_ADD​0x6B

添加密钥

VK_SEPARATOR​0x6C

分隔键

VK_SUBTRACT​0x6D

减去关键

VK_DECIMAL​0x6E

十进制键

VK_DIVIDE​0x6F

划分密钥

VK_F1​0x70

F1键

VK_F2​0x71

F2键

VK_F3​0x72

F3键

VK_F4​0x73

F4键

VK_F5​0x74

F5键

VK_F6​0x75

F6键

VK_F7​0x76

F7键

VK_F8​0x77

F8键

VK_F9​0x78

F9键

VK_F10​0x79的

F10键

VK_F11​0x7A

F11键

VK_F12​0x7B

F12键

VK_F13​0x7C

F13键

VK_F14​0x7D

F14键

VK_F15​的0x7E

F15键

VK_F16​0x7F的

F16键

VK_F17​0x80的

F17键

VK_F18​0×81

F18键

VK_F19​为0x82

F19键

VK_F20​0×83

F20键

VK_F21​的0x84

F21键

VK_F22​0x85

F22键

VK_F23​0x86可以

F23键

VK_F24​87H的

F24键

**-**0x88-8F

未分配

VK_NUMLOCK​的0x90

NUM LOCK键

VK_SCROLL​0x91

滚动锁定键

0x92-96

OEM特定

**-**0x97-9F

未分配

VK_LSHIFT​0XA0

左SHIFT键

VK_RSHIFT​0xA1

右SHIFT键

VK_LCONTROL​0xA2

左控制键

VK_RCONTROL​0xA3执行

右控制键

VK_LMENU​0xA4

左MENU键

VK_RMENU​的0xA5

右键MENU

VK_BROWSER_BACK​0xA6

浏览器返回键

VK_BROWSER_FORWARD​0xA7

浏览器转发键

VK_BROWSER_REFRESH​0xA8

浏览器刷新键

VK_BROWSER_STOP​0xA9

浏览器停止键

VK_BROWSER_SEARCH​和0xAA

浏览器搜索键

VK_BROWSER_FAVORITES​是0xAB

浏览器收藏夹键

VK_BROWSER_HOME​0xAC

浏览器开始和主页键

VK_VOLUME_MUTE​0xAD,将

音量静音键

VK_VOLUME_DOWN​0xAE

降低音量键

VK_VOLUME_UP​0xAF执行

提高音量键

VK_MEDIA_NEXT_TRACK​0XB0

下一曲目键

VK_MEDIA_PREV_TRACK​0xB1

上一曲目键

VK_MEDIA_STOP​0xB2

停止媒体键

VK_MEDIA_PLAY_PAUSE​0xB3

播放/暂停媒体键

VK_LAUNCH_MAIL​0xB4

启动邮件密钥

VK_LAUNCH_MEDIA_SELECT​0xB5执行

选择媒体键

VK_LAUNCH_APP1​0xB6

启动应用程序1键

VK_LAUNCH_APP2​0xB7

启动应用程序2密钥

**-**0xB8-B9

保留的

VK_OEM_1​0xBA

用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,使用’;:'键

VK_OEM_PLUS​为0xBB

对于任何国家/地区,请使用“+”键

VK_OEM_COMMA​0xBC

对于任何国家/地区,请使用“,”键

VK_OEM_MINUS​0xBD

对于任何国家/地区,请使用“ - ”键

VK_OEM_PERIOD​0xBE

对于任何国家/地区,’。’ 键

VK_OEM_2​为0xBF

用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,’/?’ 键

VK_OEM_3​将0xC0

用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,使用’〜'键

**-**0xC1-D7

保留的

**-**0xD8-DA

未分配

VK_OEM_4​位于0xDB

用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,’[{'键

VK_OEM_5​的0xDC

用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,’\ |’ 键

VK_OEM_6​0xDD

用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,使用’]}'键

VK_OEM_7​写0xDE

用于杂项字符; 它可以因键盘而异。 对于美国标准键盘,“单引号/双引号”键

VK_OEM_8​0xDF

用于杂项字符; 它可以因键盘而异。

**-**取0xE0

保留的

0xE1

OEM特定

VK_OEM_102​0xE2

RT 102键键盘上的尖括号键或反斜杠键

0xE3-E4

OEM特定

VK_PROCESSKEY​为0xE5

IME PROCESS键

0xE6

OEM特定

VK_PACKET​0xE7

用于传递Unicode字符,就像它们是击键一样。VK_PACKET键是用于非键盘输入方法的32位虚拟键值的低位字。有关更多信息,请参阅​KEYBDINPUT​​,​SendInput​​,​WM_KEYDOWN​​和​WM_KEYUP中的​备注

**-**0xE8

未分配

0xE9-F5

OEM特定

VK_ATTN​0xF6

关键

VK_CRSEL​0xF7

CrSel密钥

VK_EXSEL​0xF8的

ExSel密钥

VK_EREOF​0xF9

擦除EOF键

VK_PLAY​0xFA回应

播放键

VK_ZOOM​0xFB的才能

缩放键

VK_NONAME​0xFC有

保留的

VK_PA1​0xFD

PA1键

VK_OEM_CLEAR​0xFE的

清除键

官方参考手册: 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;
}

【注意】


各个控制键状态的的确定并不是使用等于符号==而是按位与&运算符,因为在同一时刻可能有多种控制键状态值,比如各种锁定都被打开且各种控制键也被同时按下。因为使用位操作,方便查询各个控制键的状态而不使之出现冲突。