c++windowskeyboardsetwindowshookex

How to use Windows LLKeyboard Hook for Combination Keypresses in C++


I have a working Windows hook to detect the simple keypress combinations of both LCTRL + x, and LCTRL + v. The only problem I have is when I press LCTRL, then release, then press x or v, my program thinks they are still being pressed together. Here is my Hook Callback function:

HHOOK hookHandle;
KBDLLHOOKSTRUCT hookdata;

LRESULT __stdcall HookCallback(int code, WPARAM wparam, LPARAM lparam)
{
    if (code >= 0)
    {
        if (wparam == WM_KEYDOWN)
        {
            hookdata = *((KBDLLHOOKSTRUCT*)lparam);
            switch(hookdata.vkCode)
            {
                case(88):
                    if(GetAsyncKeyState(VK_LCONTROL))
                    {
                        std::cout<<"CTRL X PRESSED";
                    }
                    break;
                case (86):
                    if(GetAsyncKeyState(VK_LCONTROL))
                    {
                       std::cout<<"CTRL V PRESSED";
                    }
                    break;
            }
        }
    }
    return CallNextHookEx(hookHandle, code, wparam, lparam);
}

Example Input

Test 1) Press LCTRL, Press X
Test 2) Press LCTRL, Release LCTRL, Press X

Expected Output

Test 1) CTRL X PRESSED
Test 2) No Output

Actual Output

Test 1) CTRL X PRESSED (WORKING)
Test 2) CTRL X PRESSED (NOT WORKING)

Solution

  • You're incorrectly checking GetAsyncKeyState's return value:

    If the function succeeds, the return value specifies whether the key was pressed since the last call to GetAsyncKeyState, and whether the key is currently up or down. If the most significant bit is set, the key is down, and if the least significant bit is set, the key was pressed after the previous call to GetAsyncKeyState. However, you should not rely on this last behavior; for more information, see the Remarks.

    Change the check to GetAsyncKeyState(...) & 0x8000 to fix that by checking only the highest bit.

    A small thing: Microsoft made the VK values the same as the ASCII code. Since complete portability is of small concern here, you can use 'X' and 'V' instead of 88 and 86 to better express intent.