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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8640208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:54:37  来源:igfitidea点击:

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

c++winapiinputkeyboarduser-input

提问by judeclarke

What is the fastest way to determine a key press and also how to determine if a key is being held? It appears that window messaging is slow. Please provide an example of how to do so, and why it is faster than an alternative.

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

To be clear, this for a real time loop (a simulation) so I am looking for the fastest way to determine if a key has been pressed and also to check to see if it is being held.

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

回答by Bukes

GetAsyncKeyState()is what you're looking for. It reads the physical state of the keyboard, regardless of the input queue state. If the high-bit is set, then the key was down at the time of the call.

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

Also, for the record, Windows is not a real-time operating system. If your application requires real-time precision, you may want to select another platform.

此外,根据记录,Windows 不是实时操作系统。如果您的应用程序需要实时精度,您可能需要选择其他平台。

回答by selbie

If you just want to poll the keyboard state so as to discover which keys are up/down as well as the shift/alt/ctrlstate, just call GetKeyboardState(MSDN reference).

如果你只是想查询键盘状态,以发现哪些键上/下还有shift/ alt/ctrl状态,只需拨打GetKeyboardState参考MSDN)。

When I worked in a game studio, this is exactly how we got keyboard state for each frame. Should be applicable to your simulation code.

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

回答by Andrew

TL;DR: you can use GetAsyncKeyState for checking if a key is currentlydown, but for best application responsiveness to key presses and releases, you want to use the Win32 pipeline code near the bottom of my post.

TL;DR:您可以使用 GetAsyncKeyState 来检查某个键当前是否关闭,但为了获得对按键按下和释放的最佳应用程序响应,您需要使用我帖子底部附近的 Win32 管道代码。

GetAsyncKeyStateworks perfectly fine for determining if a key is currentlydown, but in terms of determining whether a key was first pressed or released and how many timesthis was done, GetAsyncKeyState misses keystrokes in a CPU-intensive application, even after storing the previous key state.

GetAsyncKeyState非常适合确定某个键当前是否按下,但在确定某个键是第一次按下还是松开以及执行了多少次时,GetAsyncKeyState 会错过 CPU 密集型应用程序中的击键,即使在存储了前一个键之后状态。

This was what I tried:

这是我尝试过的:

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 is no good either, as it does not keep track of the number of key presses or releases. As Erik Philips said in his answer, these are unbufferedsolutions, which are no good if you are e.g. writing a game. You would have to process all keystrokes faster than they are received.

GetKeyboardState 也不好,因为它不跟踪按键按下或释放的次数。正如 Erik Philips 在他的回答中所说,这些是无缓冲的解决方案,如果您正在编写游戏,则没有好处。您必须比接收到的速度更快地处理所有击键。

Now, my code above works decently well, and may be suitable for many people, but I much prefer not to miss a single keystroke. I hateusing applications that are unresponsive. I think the best solution for Win32 applications is to catch WM_KEYDOWNand WM_KEYUPmessages in the pipeline and process them. What's nice is that WM_KEYDOWN also provides an auto-repeat count, which could be useful for applications that support entering text (e.g. chat, IDE's, etc.). This also adds a slight complication, which is mentioned in the WM_KEYDOWN documentation:

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

Because of the autorepeat feature, more than one WM_KEYDOWN message may be posted before a WM_KEYUP message is posted. The previous key state (bit 30) can be used to determine whether the WM_KEYDOWN message indicates the first down transition or a repeated down transition.

由于自动重复功能,在发布 WM_KEYUP 消息之前可能会发布多个 WM_KEYDOWN 消息。先前的键状态(位 30)可用于确定 WM_KEYDOWN 消息是指示第一次向下转换还是重复向下转换。

There are also Windows keyboard hooks you could look into, but those are more difficult to use. They're good for receiving global key presses though.

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

回答by Erik Philips

Considering that all inter-windows communications are through windows messaging (keyboard events, mouse events, pretty much all events you can imagine), there isn't a lower level way to access the keyboard events (unless you write your own keyboard driver) that I know of.

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

DirectX still uses the windows keyboard messaging to provide DirectX programmers easier access to keyboard events.

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

Updated

更新

My note about DirectX was not to use it, but that when Microsoft wanted to make an interface for programmers to use for real time games, they still wrote DirectX on top of the Windows Message Queue.

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

I would suggest taking a look at how to write a program that can read directly from the message queue. I believe there is a good example Code Project Windows Message Handling - Part 1.

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

Your two options are to either read from the message queue (buffered) or read directly from the keyboard state (as Bukes states) which means your own loop could techinically miss a keyboard event for any number of reasons.

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