【问题标题】:What is the fastest way to determine a key press and key holding in Win32?在 Win32 中确定按键和按键保持的最快方法是什么?
【发布时间】:2012-01-28 05:44:29
【问题描述】:

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

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

【问题讨论】:

  • “窗口消息传递似乎很慢。”真的吗?这很难确定,因为程序不可能计算用户按下键的时间和处理窗口消息的时间之间的延迟。这是传闻你有没有以某种方式测量它太慢了?
  • 我已经在实时循环中对其进行了测量,通常需要几个滴答声才能启动。而且我在过去几年里也听到过很多次。
  • 我有一些非交互式实时系统的经验和丰富的 Win32 API 经验。我也有很多在 StackOverflow 上帮助人们的经验。一条建议:如果您希望得到完整的答案,请不要做出广泛而模糊的不合理声明或使用主观术语。明确。在这种情况下,详细说明您的测试以及测试的快慢会有很大帮助。
  • 我并不是要争论,但是,输入的消息循环以比其对应部分(例如 GetKeyState)慢而闻名,我敢说这是最基本的事情之一您首先学习 Windows 输入编程。
  • @judeclarke 否决问题不一定是滥用。如果您认为反对票是合理的,请尝试从中学习并保持开放的心态。否则不要担心。它只是 -2 代表,通过提出更多高质量的问题并提供出色的答案,您很快就会得到它。也就是说,如果您想报告某人的行为,请注意标记为版主注意是要走的路。但请记住,对于特定帖子的否决,我们无能为力。我会清理这篇文章中的 cmets,希望现在就足够了。

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


【解决方案1】:

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

【讨论】:

  • 你能举个例子吗?
  • 根据要求更新了样本。
  • 经过多次测试,这似乎正是我想要的。这肯定比简单的窗口消息传递效果更好。感谢您的帮助。
  • 这对于检查按键的当前状态非常有效,但是在检查按键和释放时会丢失按键。有关更多信息,请参阅我的答案。
  • 嗯。 test high bit 将是 if ( ( 1 &lt;&lt; 15 ) &amp; tabKetState )
【解决方案2】:

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

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

【讨论】:

  • 更新。我可能会否决我自己的答案,以支持下面的@Bukes 回复。根据 MSDN 的 GetKeyboardState:应用程序可以调用此函数来检索所有虚拟键的当前状态。当线程从其消息队列中删除键盘消息时,状态会发生变化。当键盘消息发布到其他线程的消息队列或从其他线程的消息队列中检索时,状态不会改变。
  • 考虑到上述情况,如果您在模拟的每一帧上都泵送消息(GetMessage 和 DispatchMessage,那么您可能可以使用 GetKeyboardState。否则,GetAsyncKeyState。
  • 感谢您的帮助,我在每一帧都发送消息。我最终选择了 Bukes 的答案,但你绝对有帮助(所以我投了赞成票,因为你的答案也很可能是其他人的一个场景)。
【解决方案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(第 30 位)可以用来判断 WM_KEYDOWN 消息是否 表示第一次向下转换或重复向下转换。

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

【讨论】:

  • 只是说:SHORT 是 16 位,(1 &lt;&lt; 16) 转换为 SHORTzero。你不是说(1 &lt;&lt; 15) 是指SHORT 的高位吗?
  • 你确定 SHORT 是 16 位吗?
  • 根据定义。除非有人做傻事,比如typedef long SHORT;
  • 根据“short”的定义,是的,但不是 SHORT。 SHORT 不是语言类型,它是 typedef,“根据定义”。我在想我使用的“SHORT”实际上可能已经很长了。
  • 为安全起见,请使用(1 &lt;&lt; (sizeof(SHORT)*8-1))
【解决方案4】:

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

DirectX 仍然使用 Windows 键盘消息传递,以便 DirectX 程序员更轻松地访问键盘事件。

更新

我对 DirectX 的说明不是要使用它,而是当微软想为程序员制作一个用于实时游戏的界面时,他们仍然在 Windows 消息队列之上编写 DirectX。

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

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

【讨论】:

  • 如果 DirectX 是指 DirectInput,它已被弃用。如果 DirectX 是指 XInput,则有许多消息来源说要在 XInput\DirectInput 上使用原始输入。这些和消息还有其他选择,例如 GetKeyState 和 GetKeyboardState,但是,我不确定这些是否是最佳解决方案。
  • XInput 只是令人畏惧,永远不要使用它,除非您希望您的游戏也令人畏惧。 DirectInput 是旧的和有限的。 RawInput 是要走的路。快速,事件,您不会丢失输入,适用于所有设备。
猜你喜欢
  • 1970-01-01
  • 2020-12-15
  • 2020-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多