GetKeyboardState 一键延时

GetKeyboardState one key delay

我正在开发 ToUnicodeEx 函数,它需要键盘状态作为输入参数。所以,我使用 GetKeyboardState 函数来做到这一点。但是我注意到,当我使用 SHIFT+A 等修饰键键入组合键时,会有一个字符延迟。这是例子。

aaa(现在按住SHIFT)aAAAAAAA(松开SHIFT)Aaaa

当我调试这个时,我注意到 GetKeyboardState 导致了这个延迟。我该如何处理或防止这种延迟?

这是我的整个键盘钩子过程。

void proc(KBDLLHOOKSTRUCT kbdStruct) {


    fdebug = fopen("debug.txt", "a");
    foutput= fopen("output.txt", "a");
    WCHAR pwszBuff[9];
    WCHAR key[9];
    char str[8];
    BOOL isDead = FALSE;
    BYTE lpKeyState[256];
    HWND currentHwnd = GetForegroundWindow();
    LPDWORD currentProcessID = 0;
    DWORD currentWindowThreadID = GetWindowThreadProcessId(currentHwnd, currentProcessID);
    DWORD thisProgramThreadId = GetCurrentThreadId();
    hkl = GetKeyboardLayout(thisProgramThreadId);
    if (AttachThreadInput(thisProgramThreadId, currentWindowThreadID, TRUE))
    {
        GetKeyboardState(lpKeyState);

        AttachThreadInput(thisProgramThreadId, currentWindowThreadID, FALSE);
    }
    else
    {
        GetKeyboardState(lpKeyState);
    }

    int ret = ToUnicodeEx(kbdStruct.vkCode, kbdStruct.scanCode, lpKeyState, pwszBuff, 8, 0, hkl);
    fprintf(fdebug, "vkCode: %d\n", (int)kbdStruct.vkCode);
    fprintf(fdebug, "ret: %d\n", (int)ret);
    fprintf(fdebug, "lastIsDead: %d\n", (int)lastIsDead);
    fprintf(fdebug, "lastIsMod: %d\n", (int)lastIsMod);
    fprintf(fdebug, "lastVKCode: %d\n", (int)lastVKCode);
    if (ret == -1) {
        isDead = TRUE;
        ClearKeyboardBuffer(kbdStruct.vkCode, kbdStruct.scanCode, hkl);
    }
    else if (ret == 0) {

    }
    else {
        memcpy(&key, &pwszBuff, sizeof(pwszBuff));
        WideCharToMultiByte(CP_UTF8, 0, key, -1, str, sizeof(str), NULL, NULL);
        fprintf(fdebug, "str: %s\n", str);
    }

    if (lastVKCode != 0 && lastIsDead == TRUE) {
        ToUnicodeEx(lastVKCode, lastScanCode, lastKeyState, pwszBuff, 4, 0, hkl);
        memcpy(&key, &pwszBuff, sizeof(pwszBuff));
        WideCharToMultiByte(CP_UTF8, 0, key, -1, str, sizeof(str), NULL, NULL);
        fprintf(fdebug, "str: %s\n", str);
        lastVKCode = 0;

    }
    fprintf(fdebug, "%s", "---------------------------------------------------\n");

    fprintf(foutput, "LSHIFT: %d\n", (int)lpKeyState[160]);
    fprintf(foutput, "RSHIFT: %d\n", (int)lpKeyState[161]);
    fprintf(foutput, "%s", "---------------------------------------------------\n\n");

    lastVKCode = kbdStruct.vkCode;
    lastScanCode = kbdStruct.scanCode;
    lastIsDead = isDead;
    fclose(fdebug);
    fclose(foutput);
}

这是 hookcallback 的更新版本,感谢 Ton Plooij。但是,我还是遇到了同样的问题。

LRESULT __stdcall HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
    LRESULT ret = CallNextHookEx(_hook, nCode, wParam, lParam);
    if (nCode >= 0)
    {
        if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
        {
            hookStruct = *((KBDLLHOOKSTRUCT*)lParam);
            proc(hookStruct);

        }
    }
    return ret;
}

LowLevelKeyboardProc 在其 wParam 中接收 WM_KEYUP 和 WM_KEYDOWN 消息。您可以自己简单地跟踪按下的修饰键,在这种情况下可以检测上下移动。将键状态信息存储在静态变量中,并使用它来测试在处理其他键时是否按下了 shift,而不是使用 GetKeyState。

您可以试试 GetAsyncKeyState()。 我的键盘钩子模块中的这个函数完美运行。

https://msdn.microsoft.com/en-us/library/windows/desktop/ms646293(v=vs.85).aspx

好像这个函数可以在按键时捕获硬件中断

并且您的 GetKeyboardState 与 Windows 注册表有关。 它似乎与以下内容有关:

“HKEY_USERS\DEFAULT\ControlPanel\Keyboard”,修改“KeyboardDelay”值为0(默认1) 并将“KeyboardSpeed”值更改为 48(默认 31)。

AttachThreadInput(thisProgramThreadId, currentWindowThreadID, FALSE);

这并没有达到您希望的效果。有时。在调用 GetKeyboardState() 时获取正确的值是一项勇敢而必要的努力。我花了一段时间才找到失败模式,它一点也不明显,我无法让代码以同样的方式失败。当 GUI 进程在前台时它工作得很好,例如用记事本或 VS 试试。

但是当它是控制台模式进程时不是

解释的有点绕,其实是GetWindowThreadProcessId()returns误导信息。它努力保持控制台进程拥有控制台 window 的错觉。它不是,它实际上是拥有它的关联 conhost.exe 进程。它在旧的 Windows 版本上是 csrss.exe,在 Windows 7 中添加了 conhost.exe 以解决由 UIPI(又名 UAC)引起的拖放问题。

但是没有任何合适的方法来发现拥有 window 的特定 conhost.exe 进程,更不用说线程 ID 了。 的主题。很怀疑它能帮到你。

只有体面的建议是众所周知的令人不快的建议:如果您需要可靠地翻译击键,那么您需要使用 WH_KEYBOARD 挂钩而不是 WH_KEYBOARD_LL。所以 GetKeyboardState() 总是准确的,挂钩回调在进程中运行。

根据 Hans Passant 的回答,我搜索了如何从 GetWindowThreadProcessId() 中获取正确的值并成功地获得了 hWndwindowThreadID 每次在挂钩回调中,我得到在程序启动后将这些值设置为全局变量并在挂钩回调中使用变量。