0

I'm working on ToUnicodeEx function and it requires keyboard state as the input parameter. So, I used the GetKeyboardState function to do that. But I noticed when I'm typing key combinations with modifier keys like SHIFT+A there is one character delay. Here is the example.

aaa (holding SHIFT now) aAAAAAAA (release SHIFT) Aaaa

While I was debugging this I noticed GetKeyboardState is causing this delay. How can I handle or prevent this delay?

Here is my whole keyboard hook proc.

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);
}

Here is updated version of hookcallback thanks for Ton Plooij. But still, I have the same problem.

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;
}
Fabian Schultz
  • 18,138
  • 5
  • 49
  • 56
kenarsuleyman
  • 920
  • 1
  • 6
  • 26
  • Keyboard state is updated, after the low-level keyboard hook has returned. I don't know of a way to query synchronous keyboard state in a low-level keyboard hook. – IInspectable Dec 27 '16 at 11:16
  • Is there any way to fix this problem? I tried GetAsyncKetSatate function but still it needs hook return to update buffered key state. – kenarsuleyman Dec 27 '16 at 11:29

4 Answers4

3
AttachThreadInput(thisProgramThreadId, currentWindowThreadID, FALSE);

This does not do what you hope it does. Sometimes. It is a valiant and necessary effort to get the proper values when you call GetKeyboardState(). Took me a while to find the failure mode, it wasn't obvious at all and I could not get the code to fail the same way. It works just fine when a GUI process is in the foreground, try it with Notepad or VS for example.

But not when it is a console mode process.

Explaining that is a bit convoluted, it is actually GetWindowThreadProcessId() that returns misleading information. It tries too hard to keep up the illusion that it is the console process that owns the console window. It doesn't, it is actually the associated conhost.exe process that owns it. It was csrss.exe on old Windows versions, conhost.exe was added in Windows 7 to solve the drag+drop issues caused by UIPI (aka UAC).

But without any decent way to discover the specific conhost.exe process that owns the window, let alone the thread id. The subject of this Q+A. Pretty doubtful it is going to help you.

Only decent advice is the well-known unpleasant one: if you need to reliably translate keystrokes then you need to use a WH_KEYBOARD hook instead of WH_KEYBOARD_LL. So GetKeyboardState() is always accurate, the hook callback runs in-process.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I tried this code with a simple GUI process, still it wasn't working when is not in foreground. To be honest i didn't understand what is wrong with GetWindowThreadProcessId function. This is a global hook why return value effected with thread id? And finally i don't want to make my program dll dependent so i can't use WH_KEYBOARD. Thanks for this great answer. – kenarsuleyman Dec 28 '16 at 14:49
1

A LowLevelKeyboardProc receives WM_KEYUP and WM_KEYDOWN messages in its wParam. You could simply keep track of pressed modifier keys yourself, in this case to detect shift down and up. Store the key state information in a static variable and use this to test if shift is pressed while processing other keys instead of using GetKeyState.

Ton Plooij
  • 2,583
  • 12
  • 15
  • I tried this one but i think ToUnicodeEx function requries 255 key state for lpKeyState param. So only shift or alt key will not be enough for me. https://msdn.microsoft.com/en-us/library/windows/desktop/ms646322(v=vs.85).aspx – kenarsuleyman Dec 27 '16 at 18:35
  • OK, then use GetKeyboardState _after_ you processed your code and use the returned information on the next call? – Ton Plooij Dec 27 '16 at 18:47
  • I modified my hookcallback and added to question but still have same problem. Now i'm calling next hook first and then processing code but same thing. Am i approaching wrong to what you say? – kenarsuleyman Dec 27 '16 at 19:36
  • 1
    Nice approach but I meant something different (but I was a bit terse indeed). My suggestion is to use the original code but don't call the GetKeyboardState before you need it but instead call it after you need it (after the close(foutput)) and store the result in a static buffer. Use this buffer on the next invocation. – Ton Plooij Dec 27 '16 at 19:44
  • But GetKeyboardState is already getting a charecter behind so if i do this result will become aaa(holding shift now)aaAAAAAA(release shift)AAaa right? Maybe i should store pressed keys except modifier keys in static buffer and use on the next invocation with current GetKeyboardState. – kenarsuleyman Dec 27 '16 at 19:50
  • Can you explain what you need to use your hook for? For example, calling ToUnicodeEx in your hook will destroy dead keys so if you need that to work you need another solution (e.g. implement your own ToUnicodeEx). – Ton Plooij Dec 27 '16 at 19:59
  • "implement your own ToUnicodeEx" that's what i'm going to do now. Brilliant idea, thanks. – kenarsuleyman Dec 27 '16 at 20:02
0

You can try GetAsyncKeyState(). This function in my keyboard hook module work perfectly.

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

It seems that this function can catch hardware interrupt when you press keys.

And your GetKeyboardState has something to do with the Windows Registry. It seems to have something to do with belows:

“HKEY_USERS\DEFAULT\ControlPanel\Keyboard”,Modify “KeyboardDelay” value to 0(default 1) And change “KeyboardSpeed” value to 48(dafault 31).

John Scart
  • 11
  • 2
  • “HKEY_USERS\DEFAULT is an UAC protected area but i find same keys in HKCU and changed value as you say and it is working. I can these values on program load and change it programmatically. Accepting this an answer :) – kenarsuleyman Dec 28 '16 at 14:15
  • I don't know what is wrong with Windows but it worked when i just modified the value, now it is not working. I checked registry values still 0 and 48. – kenarsuleyman Dec 28 '16 at 14:35
  • This is strange.I can't explain the reason for this phenomenon.Sorry. – John Scart Dec 29 '16 at 11:44
-1

According to Hans Passant's answer I searched for how can I get correct value from GetWindowThreadProcessId() and succeeded with instead of getting hWnd and windowThreadID every time in hook callback, I get these values to global variable just after program start and used variables in hook callback.

kenarsuleyman
  • 920
  • 1
  • 6
  • 26
  • This does not appear to answer the question. Perhaps you meant to accept Hans' answer. – David Heffernan Dec 30 '16 at 09:03
  • @DavidHeffernan, Hans' answer explains what you need to do to in order to get it to work, but suggests that it may not be possible. This answer explains that it is possible, at least in the OPs particular scenario, and how he did it. Definitely an attempt at an answer, if not a terribly clear one. – Harry Johnston Dec 30 '16 at 23:14