在Win32中确定按键和按住键的最快方法是什么?

What is the fastest way to determine a key press and key holding in Win32?

本文关键字:方法 是什么 Win32      更新时间:2023-10-16

确定按下键的最快方法是什么,以及如何确定是否持有键?窗口消息传递似乎很慢。请提供一个示例,说明如何这样做,以及为什么它比其他选择更快。

要明确的是,这是一个实时循环(模拟),所以我正在寻找最快的方法来确定一个键是否已被按下,并检查它是否被持有。

GetAsyncKeyState()是你正在寻找的。不管输入队列的状态如何,它都会读取键盘的物理状态。如果设置了高位,则在呼叫时密钥处于down状态。

// Fetch tab key state.
SHORT tabKeyState = GetAsyncKeyState( VK_TAB );
// Test high bit - if set, key was down when GetAsyncKeyState was called.
if( ( 1 << 15 ) & tabKeyState )
{
    // TAB key down... 
}

另外,需要说明的是,Windows不是实时操作系统。如果您的应用程序需要实时精度,您可能需要选择另一个平台。

如果您只是想轮询键盘状态,以便发现哪些键是上/下以及shift/alt/ctrl状态,只需调用GetKeyboardState (MSDN参考)。

当我在游戏工作室工作时,这正是我们如何获得每一帧的键盘状态。应该适用于您的模拟代码。

TL;DR:你可以使用GetAsyncKeyState来检查一个键是否当前按下,但为了获得最佳的应用程序对按键和释放的响应,你想使用Win32管道代码在我的文章的底部。

GetAsyncKeyState在确定键是否当前按下时工作得很好,但是在确定键是否首次按下或释放以及执行了多少次方面,GetAsyncKeyState在cpu密集型应用程序中会错过击键,即使在存储之前的键状态之后。

这就是我所尝试的:

static const unsigned int NumberOfKeys = 256U;
bool previousKeyboardState[NumberOfKeys];
//Get the current state of each key as the application starts to ensure that keys held down beforehand are not processed as pressed keys.
for (unsigned int keyNum = 0U; keyNum < NumberOfKeys; ++keyNum)
{
    previousKeyboardState[keyNum] = isKeyDown(keyNum);
}
//Works fine.
bool isKeyDown(int key)
{
    return (GetAsyncKeyState(key) & (1 << 16));
}
//Misses key presses when application is bogged down.
bool isKeyFirstPressed(int key)
{
    bool previousState = previousKeyboardState[key];
    previousKeyboardState[key] = isKeyDown(key);
    return (previousKeyboardState[key] && !previousState);
}
//Misses key releases when application is bogged down.
bool isKeyFirstReleased(int key)
{
    bool previousState = previousKeyboardState[key];
    previousKeyboardState[key] = isKeyDown(key);
    return (!previousKeyboardState[key] && previousState);
}

//Example usage:
if (isKeyDown(VK_W))
{
    //W key.
}
if (isKeyFirstReleased(VK_SNAPSHOT))
{
    //Print screen.
}

GetKeyboardState也不好,因为它不跟踪按键或释放的次数。正如Erik Philips在他的回答中所说,这些都是未缓冲的解决方案,如果你正在编写一款游戏,这些解决方案并不好。您必须处理所有击键的速度要比接收它们的速度快。

现在,我上面的代码工作得很好,可能适合很多人,但我更希望不要错过一个键击。我讨厌使用无响应的应用程序。我认为Win32应用程序的最佳解决方案是捕获管道中的WM_KEYDOWN和WM_KEYUP消息并处理它们。好的是WM_KEYDOWN还提供了一个自动重复计数,这对于支持输入文本的应用程序(例如聊天,IDE等)可能很有用。这也增加了一点复杂性,这在WM_KEYDOWN文档中提到:

由于自动重复功能,有多个WM_KEYDOWN消息可以在WM_KEYUP消息发布之前发布。前一个键state(位30)可以用来确定是否WM_KEYDOWN消息第一次向下转换或重复向下转换。

也有Windows键盘挂钩,你可以看看,但那些更难以使用。

考虑到所有窗口间的通信都是通过windows消息传递(键盘事件,鼠标事件,几乎所有你能想象到的事件),我知道没有更低级别的方法来访问键盘事件(除非你编写自己的键盘驱动程序)。

DirectX仍然使用windows键盘消息传递,以使DirectX程序员更容易访问键盘事件。

更新

我对DirectX的注意是不要使用它,但是当微软想要为程序员制作一个用于实时游戏的界面时,他们仍然在Windows Message Queue之上编写DirectX。

我建议看一下如何编写一个可以直接从消息队列中读取的程序。我相信有一个很好的例子代码项目Windows消息处理-第一部分。

您的两个选择是从消息队列中读取(缓冲)或直接从键盘状态中读取(如Bukes状态),这意味着您自己的循环可能由于各种原因在技术上错过键盘事件。