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

时间:2011-12-27 00:40:48

标签: c++ winapi input keyboard user-input

确定按键的最快方法是什么,以及如何确定按键是否被按住?窗口消息传递似乎很慢。请提供一个如何操作的示例,以及为什么它比替代方案更快。

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

4 个答案:

答案 0 :(得分:21)

GetAsyncKeyState()正是您要找的。无论输入队列状态如何,它都会读取键盘的物理状态。如果设置了高位,则在通话时键已关闭。

// 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不是实时操作系统。如果您的应用程序需要实时精确度,您可能需要选择其他平台。

答案 1 :(得分:6)

如果您只想轮询键盘状态以便发现哪些键是向上/向下以及 shift / alt / ctrl 州,只需致电GetKeyboardStateMSDN reference)。

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

答案 2 :(得分:3)

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_KEYDOWNWM_KEYUP消息并处理它们。有趣的是,WM_KEYDOWN还提供自动重复计数,这对于支持输入文本的应用程序(例如聊天,IDE等)非常有用。这也增加了一个轻微的复杂性,这在WM_KEYDOWN文档中提到:

  

由于自动重复功能,多条WM_KEYDOWN消息   可以在发布WM_KEYUP消息之前发布。以前的关键   state(bit 30)可用于确定是否为WM_KEYDOWN消息   表示第一次向下转换或重复向下转换。

您还可以查看Windows键盘挂钩,但这些挂钩更难以使用。它们很适合接收全球按键。

答案 3 :(得分:2)

考虑到所有的窗口间通信都是通过Windows消息传递(键盘事件,鼠标事件,几乎所有你能想象的事件),没有一种较低级别的方式来访问键盘事件(除非你编写自己的键盘司机)我知道。

DirectX仍然使用Windows键盘消息传递来为DirectX程序员提供更轻松的键盘事件访问。

已更新

我对DirectX的注意事项不是使用它,但是当微软希望为程序员创建一个用于实时游戏的界面时,他们仍然会在Windows消息队列之上编写DirectX。

我建议看看如何编写一个可以直接从消息队列中读取的程序。我相信有一个很好的例子Code Project Windows Message Handling - Part 1

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