Windows控制台下C_C++更好的按键检测
不说了,直接上代码,下面解释:#include <stdio.h>
#include <Windows.h>
int main()
{
HANDLE in_handle = GetStdHandle(STD_INPUT_HANDLE);
HANDLE out_handle = GetStdHandle(STD_OUTPUT_HANDLE);
INPUT_RECORD what_key;
DWORD save_key;
bool have_press_esc = false;
while (true)
{
ReadConsoleInput(in_handle, &what_key, 1, &save_key);
if (what_key.EventType == KEY_EVENT && what_key.Event.KeyEvent.bKeyDown == TRUE)
{
if (what_key.Event.KeyEvent.wVirtualKeyCode == 0x41) //0x41 is A's virtual key code. It's not a ASCII code.
{
printf("You pressed the A key.\n");
}
else if (what_key.Event.KeyEvent.wVirtualKeyCode == 0x1B) //0x1B is ESC's virtual key code. VK_ESCAPE == 0x1B
{
if (have_press_esc == false)
{
have_press_esc = true;
printf("Press the ESC key again to exit.");
}
else if (have_press_esc == true)
{
break;
}
}
}
}
CloseHandle(in_handle);
CloseHandle(out_handle);
return 0;
} 当然,比起_kbhit()+_getch()的方法或者GetAsyncKeyState(),这个方法未免太麻烦了点 ,但它使用起来比前两者好
因为_kbhit()+_getch()读取的是ASCII的值,无法使用虚拟键码,而GetAsyncKeyState()是通过直接读取键盘中断实现的,但这也使得它无法中断和停顿,会在一些地方出现各种各样的错误
更何况,上述的两者都无法判断出键盘按下与松开两种状态 ReadConsoleInput()是个在c/c++控制台编程下很有用的函数,这里我们用它来读取键盘事件 首先我们先定义两个HANDLE结构体,从GetStdHandle()来获得他们的初始值 然后再定义一个INPUT_HANDLE结构体,目的是将从ReadConsoleInput()中读取到的数据记录下来 得到记录下来的数据后,我们通过访问结构体的成员(在文中是what_key.EventType和what_key.Event.KeyEvent.bKeyDown)来判断事件类型和具体的事件
其中what_key.EventType是判断读取到内容是否为键盘事件,如果为键盘事件,则值为KEY_EVENT
而what_key.Event.KeyEvent则用于判断是按下键位还是松开键位,文中的bKeyDown指的是按下键位 what_key.Event.KeyEvent.wVirtualKeyCode则指的是按下的键的虚拟键码值,可以找一张虚拟键码表来对比一下键位 craters 发表于 2022-6-12 00:45
当然,比起_kbhit()+_getch()的方法或者GetAsyncKeyState(),这个方法未免太麻烦了点 ,但它使用起来比前两 ...
GetAsyncKeyState() 判断的就是按键的按下与松开的两种状态,和键盘中断无关,并且其返回值最高 bit 可以用于判断按键状态的变化,按键自上次检测以来是否上升沿。
而你这个方式唯一的好处就是可以清空 STDIN 的读缓冲区,除此以外没有优势。 0xAA55 发表于 2022-6-12 20:09
GetAsyncKeyState() 判断的就是按键的按下与松开的两种状态,和键盘中断无关,并且其返回值最高 bit 可以 ...
问题是,GetAsyncKeyState()似乎是直接通过读取键盘的消息队列来实现的,即使你目前没有在执行GetAsyncKeyState(),只要你按下了按键,等到之后执行GetAsyncKeyState()时会把之前你按过的键都一一判断一遍 0xAA55 发表于 2022-6-12 20:09
GetAsyncKeyState() 判断的就是按键的按下与松开的两种状态,和键盘中断无关,并且其返回值最高 bit 可以 ...
话说,GetAsyncKeyState走键盘中断这个说法是怎么来的哦?我以前上学的秦海玉老尸也说它是键盘中断实现,但随着我毕业多年以后的研究结果,发现它并非键盘中断(可能键盘驱动那一层会用到中断,但应用层的GetAsyncKeyState只是读取系统保存到内存上的256个按钮状态而已)。
另外ReadConsoleInput感觉比较GUI界面的键盘消息的用法,毕竟CUI程序的控制台窗口不是自己进程的,不能直接接收它的窗口消息,所以通过Console API来转发。 craters 发表于 2022-6-13 11:46
问题是,GetAsyncKeyState()似乎是直接通过读取键盘的消息队列来实现的,即使你目前没有在执行GetAsyncKe ...
感觉不像是读取键盘的消息队列,因为就算我完全不处理消息队列里面的消息,也不影响我使用这个函数来读取实时的按键信息;与此同时,我在处理消息队列的时候,我依然能在消息处理过程中处理之前没处理的按键消息。即使你目前没有在执行GetAsyncKeyState(),只要你按下了按键,等到之后执行GetAsyncKeyState()时会把之前你按过的键都一一判断一遍你说的后半句我没理解意思。它确实能做到在你两次调用之间如果发生按键的按下和弹出后,会返回信息看到有个按键被按下过的状态,但这个并不一定是你说的“一一判断一遍”。 0xAA55 发表于 2022-6-13 20:14
感觉不像是读取键盘的消息队列,因为就算我完全不处理消息队列里面的消息,也不影响我使用这个函数来读取 ...
我曾经写过一个控制台游戏,用的就是GetAsyncKeyState()来判断键位,然后我发现游戏暂停时按下按键恢复游戏后GetAsyncKeyState()居然还会根据我按过的按键进行相应的运行
另外GetAsyncKeyState()无法获取按下的究竟是哪个键,_kbhit()+_getch()获取到的不但不是虚拟键值表,而且竟是ASCII码,所以我选择了上述的方法
何况GetAsyncKeyState()还是非阻塞函数,在我的程序里面老是乱套 系统消息 发表于 2022-6-13 18:54
话说,GetAsyncKeyState走键盘中断这个说法是怎么来的哦?我以前上学的秦海玉老尸也说它是键盘中断实现, ...
这个方法确实比较GUI了,所以我在网上查到的绝大部分资料都是GetAsyncKeyState()或_kbhit()+_getch(),不符合我的用途,直到某次我无意在知乎上看到了ReadConsoleInput() craters 发表于 2022-6-13 21:09
我曾经写过一个控制台游戏,用的就是GetAsyncKeyState()来判断键位,然后我发现游戏暂停时按下按键恢复游 ...
你一定是记错函数了。
GetAsyncKeyState() 函数并不按照你描述的那样工作,并且它的参数就是你要判断状态的按键的虚拟键值。它并不会扫描你整个键盘或者鼠标,而是根据你要判断的键值来返回其是否按下的状态。 0xAA55 发表于 2022-6-13 22:01
你一定是记错函数了。
GetAsyncKeyState() 函数并不按照你描述的那样工作,并且它的参数就是你要判断状 ...
刚刚试了一下,确实是的,顺便一提如何在不使用Sleep()的情况下避免出现按一次反应几下的结果 craters 发表于 2022-6-13 22:36
刚刚试了一下,确实是的,顺便一提如何在不使用Sleep()的情况下避免出现按一次反应几下的结果 ...
同时也要支持按住不动也行的效果,使用Sleep()的话有可能在Sleep()执行时按下了键位导致没有反应的结果 craters 发表于 2022-6-13 22:36
刚刚试了一下,确实是的,顺便一提如何在不使用Sleep()的情况下避免出现按一次反应几下的结果 ...
其实 GetAsyncKeyState() 不是给控制台用的,而是给游戏用的,用于读取游戏按键的状态。
你说的检测多次,那是你自己的程序逻辑的问题。这个函数是立即返回的,而非阻塞式。所以你循环的有多快,它检测的次数就越多。
使用 Sleep 仅仅是用于减慢你的循环的速度,你真正需要理解的是“边沿”的逻辑,即“上升沿检测”和“电平检测”概念的区别。你的代码是“电平检测”,但你想做的是“上升沿检测”。在软件上,你需要定义变量来存储旧的按键状态,用于比对。 craters 发表于 2022-6-13 22:36
刚刚试了一下,确实是的,顺便一提如何在不使用Sleep()的情况下避免出现按一次反应几下的结果 ...
GetAsyncKeyState会返回多种返回值,比较常用的是返回 -32767 和 -32768,-32767 表示按下键盘按键这个动作产生,-32768表示按住不放的过程。
页:
[1]