GetKeyboardState一键延迟

时间:2016-12-25 21:15:14

标签: c++ winapi keyboard-hook

我正在使用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;
}

4 个答案:

答案 0 :(得分:3)

AttachThreadInput(thisProgramThreadId, currentWindowThreadID, FALSE);

这不符合你的期望。有时。调用GetKeyboardState()时获取正确的值是一种勇敢和必要的努力。花了一段时间才找到失败模式,它根本不明显,我无法让代码以同样的方式失败。当GUI进程位于前台时,它可以正常工作,例如使用Notepad或VS进行尝试。

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

解释这有点令人费解,实际上GetWindowThreadProcessId()会返回误导性信息。它太难以保持这种错觉,即它是拥有控制台窗口的控制台进程。它没有,它实际上是拥有它的关联conhost.exe进程。在旧的Windows版本上是csrss.exe,在Windows 7中添加了conhost.exe来解决由UIPI(又名UAC)引起的拖放问题。

但没有任何体面的方法来发现拥有该窗口的特定conhost.exe进程,更不用说线程ID了。 this Q+A的主题。非常怀疑它会帮助你。

只有好的建议才是众所周知的令人不快的建议:如果你需要可靠地翻译击键,那么你需要使用WH_KEYBOARD钩子而不是WH_KEYBOARD_LL。因此,GetKeyboardState()始终准确,钩子回调在进程中运行。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

您可以尝试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(dafault 31)。

答案 3 :(得分:-1)

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