GetKeyboardState一键延迟

问题描述:

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

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; 
} 
+0

键盘状态在低级键盘挂钩返回后更新。我不知道在低级键盘钩子中查询同步键盘状态的方法。 – IInspectable

+0

有什么方法可以解决这个问题吗?我尝试了GetAsyncKetSatate函数,但它仍然需要钩子返回来更新缓冲键状态。 –

AttachThreadInput(thisProgramThreadId, currentWindowThreadID, FALSE); 

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

但是不是当它是一个控制台模式的过程。

解释说有点复杂,它实际上是GetWindowThreadProcessId(),它返回误导性信息。它试图保持幻想,认为它是拥有控制台窗口的控制台进程。它并不是,它实际上是拥有它的相关conhost.exe进程。它是旧版本Windows上的csrss.exe,在Windows 7中添加了conhost.exe来解决UIPI(aka UAC)造成的拖放问题。

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

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

+0

我用一个简单的GUI过程试过这段代码,当它不在前台时它仍然不工作。 说实话,我不明白GetWindowThreadProcessId函数有什么问题。这是一个全局钩子为什么返回值影响线程ID?最后我不想让我的程序DLL依赖,所以我不能使用WH_KEYBOARD。感谢这个伟大的答案。 –

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

+0

我试过这一个,但我认为ToUnicodeEx函数requries 255关键状态为lpKeyState参数。所以只有移位或者按键对我来说是不够的。 https://msdn.microsoft.com/en-us/library/windows/desktop/ms646322(v=vs.85).aspx –

+0

确定,然后使用GetKeyboardState _after_处理您的代码并在下一次使用返回的信息呼叫? –

+0

我修改了我的hookcallback并添加了问题,但仍然有同样的问题。现在我先调用下一个钩子,然后处理代码,但同样的事情。我说错了吗? –

您可以尝试GetAsyncKeyState()。 这个功能在我的键盘钩子模块中工作完美。

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

看来,这个功能可以捕捉硬件中断,当你按下键。

而你的GetKeyboardState与Windows注册表有关。 它似乎有事情做与初级讲座:

“HKEY_USERS \ DEFAULT \ ControlPanel控制面板\键盘”,修改“KeyboardDelay”的值改为0(默认为1) 变“KeyboardSpeed”值48(dafault 31)。

+0

“HKEY_USERS \ DEFAULT是一个UAC保护区域,但我在HKCU中找到了相同的密钥并根据您的说法改变了值,并且它正在工作。我可以在程序加载这些值,并以编程方式更改它。接受这个答案:) –

+0

我不知道Windows有什么问题,但它工作时,我只是修改了值,现在它不工作。我检查了注册表值仍然是0和48. –

+0

这很奇怪,我无法解释造成这种现象的原因。 –

据汉斯帕桑特的回答我搜索我怎样才能从GetWindowThreadProcessId()正确的值和,而不是让每一次hWndwindowThreadID在钩子回调,我只是程序启动后获取这些值到全局变量成功和使用变量钩回调。

+0

这似乎没有回答这个问题。也许你打算接受汉斯的回答。 –

+0

@DavidHeffernan,汉斯的回答解释了你需要做些什么才能使它起作用,但表明它可能是不可能的。这个答案解释说,至少在OP的特定情况下是可能的,他是如何做到的。绝对是一个答案的尝试,如果不是十分清楚的话。 –