c++ - 通过检查其返回值作为 -32767 和 0x8000 来使用 GetAsyncKeyState() 有什么区别?

标签 c++ windows winapi

我对 GetAsyncKeyState() 有多少不同的答案感到非常困惑,我知道它的作用,但令人困惑的部分是它的返回值。我已经看到了许多关于人们如何使用它的不同示例,但主要的两个示例如下:
第一:

if (GetAsyncKeyState(KEY) == -32767)
第二:
if (GetAsyncKeyState(KEY) & 0x8000)
关于这一点的第一个令人困惑的地方是在使用 Windows 计算器和转换 -32767 时。到十六进制,它等于 0x8001 ,所以他们已经不是在比较同一件事了。据我所知(如果我理解错误,请纠正我),0x8000正在检查当前是否按下了键,但 0x8001正在做同样的事情,同时告诉你它在上次之后被按下 GetAsyncKeyState()在那把 key 上被调用了。
这是第二个混淆/问题的一些示例代码,这些不同的方法本质上做同样的事情,但同时获得不同的结果......
#include <iostream>
#include <Windows.h>
int main() {
        
            while (true) {
                for (int KEY = 8; KEY <= 190; KEY++)
                {
                    if (GetAsyncKeyState(KEY) == -32767) {
                        std::cout << char(KEY) << std::endl;
                    }
                }
            }
            return 0;
        }
正如您在上面的代码中看到的,我使用“第一个”选项来检查键是否被按下。使用此选项,无论您输入多快(仅在 120wpm 下测试),它都不会重复字母,只要您握住它们的时间不会比普通人握住字母的时间长得奇怪,如果您握住它们您只需重复它(将其打印到屏幕上)即可获得它的预期行为。它在以正常速度或高达 120wpm 测试的快速打字时完美运行,没有重复键(我将解释我所说的“重复键”是什么意思)。这是一些使用“第二种”方式的代码......
#include <iostream>
#include <Windows.h>    
int main() {

    while (true) {
        for (int KEY = 8; KEY <= 190; KEY++)
        {
            if (GetAsyncKeyState(KEY) & 0x8000) {
                std::cout << char(KEY) << std::endl;
            }
        }
    }
    return 0;
}
有了这个,我遇到了一个问题,无论我点击键有多快,它至少会重复几次,如果你想明白我的意思,你可以自己尝试,但作为一个例子:
如果我输入 H一旦这将分别是 First 和 Second 的结果:
First:
H
Second:
H
H
H
H
H
.....etc maybe a few more times maybe one or two less times..... highly inconsistent
为什么即使理论上他们应该做同样的事情,他们也会有非常不同的结果(即使 -327670x8000 不同,这是除了 0x8000 之外我更常见的事情) .

最佳答案

函数GetAsyncKeyState返回 SHORT ,这是一个 16 位有符号整数。
返回值的最高位表示该键当前是否被按下。你可以用 GetAsyncKeyState(KEY) & 0x8000 测试这个位.
最低位也有一个含义:它指定自上次调用 GetAsyncKeyState 以来该键是否被按下过。 .但此功能仅用于向后兼容 16-bit Windows不应依赖现代 32 位或 64 位应用程序。因此,它应该被忽略。见 official Microsoft documentation for this function了解更多信息。如果你真的想测试这个位(你通常不应该这样做),那么你可以用表达式 GetAsyncKeyState(KEY) & 0x0001 来做。 .
表达方式

GetAsyncKeyState(KEY) == -32767
只有在设置了最高位和最低位并且所有其他位为零时才会为真。
这是不好的编程习惯。即使此代码当前适用于所有版本的 Microsoft Windows,也不应假设所有其他位都为零,因为其他位的含义未定义。例如,Microsoft 可能会在 Windows 的 future 版本中为这些当前未使用的位之一分配含义。因此,作为一般规则,只应测试定义的位。
如上所述,只定义了两个位,但一个是不可靠的,仅用于向后兼容;因此不应使用它。因此,只应使用表达式 GetAsyncKeyState(KEY) & 0x8000 测试最高位。 .
您的第一个程序与第二个程序的行为不同的原因是您的第一个程序需要设置两个位,而您的第二个程序只需要设置一个位并忽略另一位。换句话说,如果 GetAsyncKeyState,你的第一个程序只打印一些东西。报告该按钮当前正被按下并且自上次调用该函数后被按下,而第二个程序只需要该按钮当前被按下。
微软文档所说的“自上次通话后被按下”是什么意思还不是很清楚。这可能意味着 WM_KEYDOWN消息是自上次函数调用以来生成的。这可能就是为什么你的第一个程序只在你开始按住按钮时打印一些东西,而不是在按住按钮时连续打印一些东西。
即使您的第一个程序完全符合您的要求,出于上述原因,它也是不可靠的。因此,如果您只希望每次击键一个通知,我建议您不要使用 GetAsyncKeyState ,而是处理 WM_KEYDOWN您的 message loop 中的消息.

关于c++ - 通过检查其返回值作为 -32767 和 0x8000 来使用 GetAsyncKeyState() 有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64901061/

相关文章:

c# - 2 部分 Windows 应用程序 : "windows service" + GUI to configure it

c++ - MSDN CommonFileDialogModes,为我的目的而改变

C++ - 错误 C2568 - 表达式必须具有整数或无作用域的枚举类型

c++ - 教 child C++ 时使用 "include stdafx.h"?

c++ - 遍历文件失败

c++ - Cloud Files API 支持哪些 Windows 版本?

c - 使用 pthread_kill() 有危险吗?

c++ - 不兼容的类声明 c++

windows - CMake 在 Windows 上找不到库

Python - 编译后有两个进程?