2016-12-25 50 views
0

我正在使用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函數,但它仍然需要鉤子返回來更新緩衝鍵狀態。 –

回答

3
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。感謝這個偉大的答案。 –

1

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並添加了問題,但仍然有同樣的問題。現在我先調用下一個鉤子,然後處理代碼,但同樣的事情。我說錯了嗎? –

0

您可以嘗試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

這很奇怪,我無法解釋造成這種現象的原因。 –

-1

據漢斯帕桑特的回答我搜索我怎樣才能從GetWindowThreadProcessId()正確的值和,而不是讓每一次hWndwindowThreadID在鉤子回調,我只是程序啓動後獲取這些值到全局變量成功和使用變量鉤回調。

+0

這似乎沒有回答這個問題。也許你打算接受漢斯的回答。 –

+0

@DavidHeffernan,漢斯的回答解釋了你需要做些什麼才能使它起作用,但表明它可能是不可能的。這個答案解釋說,至少在OP的特定情況下是可能的,他是如何做到的。絕對是一個答案的嘗試,如果不是十分清楚的話。 –