2
votes

For a usb keyboard configuration tool I need to intercept all keyboard input and detect which modifier keys and normal keys are pressed simultaneously. Therefore I use a windows low level hook (WH_KEYBOARD_LL) which works fine except that I´m not able to determine if WIN-Key (VK_LWIN / VK_RWIN) is pressed (control / shift and alt is working).

I made a little commandline tool to show the problem:

#include <Windows.h>
#include <iostream>

using namespace std;

HHOOK hKeyboardHook;


LRESULT CALLBACK LowLevelKeyboardProc( int nCode, WPARAM wParam, LPARAM lParam )
{
    if (nCode < 0 || nCode != HC_ACTION )
        return CallNextHookEx( hKeyboardHook, nCode, wParam, lParam);

    KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam;

    if(wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
    {
        // working
        if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
            cout << "CONTROL" << endl;
        if(GetAsyncKeyState(VK_SHIFT) & 0x8000)
            cout << "SHIFT" << endl;
        if(GetAsyncKeyState(VK_MENU) & 0x8000) // ALT
            cout << "ALT" << endl;

        // VK_xWIN not working at all
        if((GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000))
            cout << "WIN" << endl;

        // working for ALTGR/right-handed ALT
        if((GetAsyncKeyState(VK_LCONTROL) & 0x8000) || (GetAsyncKeyState(VK_RCONTROL) & 0x8000))
            cout << "LRCONTROL" << endl;

        // not working at all
        if((GetAsyncKeyState(VK_LSHIFT) & 0x8000) || (GetAsyncKeyState(VK_RSHIFT) & 0x8000))
            cout << "LRSHIFT" << endl;
        if((GetAsyncKeyState(VK_LMENU) & 0x8000) || (GetAsyncKeyState(VK_RMENU) & 0x8000))
            cout << "LRMENU" << endl;
    }

    //return CallNextHookEx( hKeyboardHook, nCode, wParam, lParam);
    return 1;
}



int main(int argc, char* argv[])
{
    hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0 );

    MSG message;
    while (GetMessage(&message,NULL,0,0)) {
        TranslateMessage( &message );
        DispatchMessage( &message );
    }

    UnhookWindowsHookEx(hKeyboardHook);
    return 0;
}

If I return "1" from LowLevelKeyboardProc every key press is "swallowed" (except for CTRL+ALT+DEL and WIN+L). If I call the next hook at the end of the callback function the behaviour changes (and the keys obviously aren´t swallowed any more). Then if WIN key is pressed together with another key I get the information that WIN key is pressed.

What do I have to do to intercept all keyboard input and detect a WIN-key press (using GetAsyncKeyState)? Or is there another way to get all (inkl. WIN) pressed modifier keys?

2

2 Answers

0
votes

The documentation for LowLevelKeyboardProc says the following about the return value:

If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure.

So, return 1 says "I've processes the information, and there is nothing else that should be done with these keys".

I'm not certain how you fix the problem with detection of WIN key on it's own - I suspect the system just simply doesn't forward that has a keyboard event until the second key is pressed.

0
votes

I didn't find out why I can't get the state of the windows key from within the hook, but I implemented a simple workaround which I don't want to keep back from you.

The callback function gets called if I press/release the windows key. So I just save the state of the key by myself and use this information on following key presses.

So the changed callback function looks like:

bool leftWinKeyPressed = false;
bool rightWinKeyPressed = false;

LRESULT CALLBACK LowLevelKeyboardProc( int nCode, WPARAM wParam, LPARAM lParam )
{
    if (nCode < 0 || nCode != HC_ACTION )
        return CallNextHookEx( hKeyboardHook, nCode, wParam, lParam);

    KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam;

    // save state of win keys manually... (needs to be tested some more)
    if(p->vkCode == VK_LWIN)
        leftWinKeyPressed = (wParam == WM_KEYDOWN)?true:false;
    else if(p->vkCode == VK_RWIN)
        rightWinKeyPressed = (wParam == WM_KEYDOWN)?true:false;

    if(wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
    {
        // not beautifull but working...
        if(leftWinKeyPressed || rightWinKeyPressed)
            cout << "WIN" << endl;

        // for example
        if(leftWinKeyPressed && p->vkCode == 68 )
            cout << "LEFT WINDOWS + D";
    }

    return 1;
}

To clean it up I will probably do this for all modifier keys and use a bit field to store the state of the modifier keys. So I'm not dependent on GetAsyncKeyState and it's weird behaviour. But if someone finds out why it behaves that way, please let me know.