【C语言】控制台窗口图形界面编程(八):键盘事件

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;
}


【注意】

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值