在Windows控制台中,我们通常要处理一些用户输入,例如按键,鼠标(不常用,但是有),缓冲区更改等。
Windows系统为了减轻程序员的负担,提供了ReadConsoleInput函数
在docs.microsoft.com中这样描述ReadConsoleInput:
Reads data from a console input buffer and removes it from the buffer.
从一个控制台缓冲区读取数据并将其从缓冲区移除。
在头文件中,ReadConsoleInput有两种可能的声明:
#define ReadConsoleInput ReadConsoleInputA
#define ReadConsoleInput ReadConsoleInputW
当Unicode被声明时,为下面一种。
ReadConsoleInputA或ReadConsoleInputW声明如下:
BOOL WINAPI ReadConsoleInput(
_In_ HANDLE hConsoleInput, //控制台输入句柄,由GetStdHandle获得
_Out_ PINPUT_RECORD lpBuffer, //指向INPUT_RECORD(可以为数组)的指针
_In_ DWORD nLength, //接收消息的个数
_Out_ LPDWORD lpNumberOfEventsRead //用于存储成功获取消息个数的指针
);
函数成功,则返回非零值,反之为零。
其中,结构体INPUT_RECORD声明如下:
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取值如下:
#define KEY_EVENT 0x0001
#define MOUSE_EVENT 0x0002
#define WINDOW_BUFFER_SIZE_EVENT 0x0004
#define MENU_EVENT 0x0008
#define FOCUS_EVENT 0x0010
1.KeyEvent
这是最常用的输入事件
KEY_EVENT_RECORD结构体声明如下:
typedef struct _KEY_EVENT_RECORD {
BOOL bKeyDown; //指示按键是否按下,对于Ctrl+C/Break无效
WORD wRepeatCount; //按键重复次数,不累加
WORD wVirtualKeyCode; //虚拟键码
WORD wVirtualScanCode; //虚拟扫描码
union {
WCHAR UnicodeChar; //Unicode码,ReadConsoleInputW使用
CHAR AsciiChar; //ASCII码,ReadConsoleInputA使用
} uChar;
DWORD dwControlKeyState;//控制键状态
} KEY_EVENT_RECORD, *PKEY_EVENT_RECORD;
控制键状态dwControlKeyState可由以下值合并:
标识符 | 值 | 说明 |
---|---|---|
RIGHT_ALT_PRESSED | 0x0001 | 右Alt键被按下 |
LEFT_ALT_PRESSED | 0x0002 | 左Alt键被按下 |
RIGHT_CTRL_PRESSED | 0x0004 | 右Ctrl键被按下 |
LEFT_CTRL_PRESSED | 0x0008 | 左Ctrl键被按下 |
SHIFT_PRESSED | 0x0010 | Shift键被按下 |
NUMLOCK_ON | 0x0020 | Num Lock状态为开 |
SCROLLLOCK_ON | 0x0040 | Scroll Lock状态为开 |
CAPSLOCK_ON | 0x0080 | Caps Lock状态为开 |
ENHANCED_KEY | 0x0100 | 指示该键为拓展键 |
示例:用户输入q键时退出程序
#include <Windows.h>
int main(){
INPUT_RECORD sinfo;
DWORD recnum;
HANDLE hIn=GetStdHandle(STD_OUTPUT_HANDLE);
while (GetConsoleInput(hIn, &sinfo, 1, &recnum)){
if (sinfo.EventType != KEY_EVENT) continue;//无视其他类型输入
//判断按键状态(以防按键被触发两次)
//if (!sinfo.Event.KeyEvent.bKeyDown) continue;
if (sinfo.Event.KeyEvent.uChar.UnicodeChar == 'q') break;
}
}
在获取键盘事件时,有一点比较特殊:
用户输入Ctrl+C/Break时,ReadConsoleInput会抛出C++错误,C并不接收C++错误(VS会接收),但此时ReadConsoleInput返回的INPUT_RECORD中Event.KeyEvent.bKeyDown为0,即使Ctrl+C在按下状态
虚拟键码低8位为3(A1->B2->C3,以此推例,Break视作C),第10(RIGHT_CTRL_PRESSED)或第11(LEFT_CTRL_PRESSED)位为1
0000xx00 00000011
所以,接收Ctrl+C/Break退出循环的方法如下
#include <stdio.h>
#include <Windows.h>
BOOL ReceiveHandler(DWORD dwCtrlType);
int main() {