Home > database >  Windows Low-Level keyboard hook, allow (Win H) not other Win hotkeys
Windows Low-Level keyboard hook, allow (Win H) not other Win hotkeys

Time:01-10

We have a kiosk app where we would like to allow use of the Win H hotkey to bring up Voice Typing, but still filter out other Win ... hotkeys.

The hook is set with SetWindowsHookEx(WH_KEYBOARD_LL, callback, hInstance, 0);

Log from pressing Win H:

// w = bool, GetAsyncKeyState for left and right windows keys
// h = bool, ...for "h" key
// key = KBDLLHOOKSTRUCY* pkh->vkCode,  72 = h, 91 = left Windows key
// flags = KBDLLHOOKSTRUCT* pkh->flags
// pressed Windows, then H, then released
w=0  h=0  key= 91  flags = 1
w=-1  h=0  key= 91  flags = 1
... repeats ....
w=-1  h=0  key= 91  flags = 1
w=-1  h=0  key= 72  flags = 0 
w=-1  h=-1  key= 72  flags = 80
w=-1  h=0  key= 91  flags = 81

What I'm finding while trying different permutations of code in the callback is, either I get no keypress, or I get a plain h (so Voice Typing is not launched), or all other Win ... hotkeys get through not just Win H.

This treats the press as a plain h, so apparently at least some of the Win key events before the h events must be allowed past the filter:

if (nCode == HC_ACTION) 
{
    BOOL bWinKeyDown = (GetAsyncKeyState(VK_LWIN) >> 15) | (GetAsyncKeyState(VK_RWIN) >> 15);
    if (pkh->vkCode == 'H')
    {
        // w=-1  h=0  key= 72  flags = 0 
        // w=-1  h=-1  key= 72  flags = 80
        return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
    }
    else if ((pkh->vkCode == VK_LWIN || pkh->vkCode == VK_RWIN))
    {
        // also just "h" if we allow through the w=-1  h=0  key= 91  flags = 1 events
        // if ((bWinKeyDown && (pkh->flags == 1))   )
        //    return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
        //  else
        return -1;
    }
}
return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);

This lets all Win ... hotkeys through, it seems like the first event acts as a "dead key" that gets applied to any next letter key:

if (nCode == HC_ACTION)
{
    BOOL bWinKeyDown = (GetAsyncKeyState(VK_LWIN) >> 15) | (GetAsyncKeyState(VK_RWIN) >> 15);
    if (pkh->vkCode == 'H')
    {
        // w=-1  h=0  key= 72  flags = 0 
        // w=-1  h=-1  key= 72  flags = 80
        return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
    }
    else if ((pkh->vkCode == VK_LWIN || pkh->vkCode == VK_RWIN))
    {
        if ((!bWinKeyDown && (pkh->flags == 1))) // // w=0  h=0  key= 91  flags = 1 - first event in log
            return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
        else
            return -1;
    }

Is there any way to trap all Win ... hotkey except for Win H?

CodePudding user response:

You can just add a test for WM_KEYDOWN and add test when Win key is down and H is pressed down.

Returning non-zero value is not recommended, and the code below ends up eating a message, which is no good. But this is only eating the message for key down state. So everything should be okay once that key is released.

LRESULT CALLBACK kbd_proc(int nCode, WPARAM wp, LPARAM lp)
{
    if (nCode == HC_ACTION)
    {
        KBDLLHOOKSTRUCT* pkh = (KBDLLHOOKSTRUCT*)lp;
        BOOL bWinKeyDown = (GetAsyncKeyState(VK_LWIN) >> 15) | 
                    (GetAsyncKeyState(VK_RWIN) >> 15);
        if (pkh->vkCode != 'H' && wp == WM_KEYDOWN && bWinKeyDown)
        {
            OutputDebugString(L"not Win H...\n");
            return 1;
        }
    }
    return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
}

CodePudding user response:

According to my test, win h produces four messages, Index 1 to 4. enter image description here You can trap them at the second message. The following code which traps all Win ... hotkey except for win h works for me.

LRESULT CALLBACK Proc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION)
    {
        KBDLLHOOKSTRUCT* s= (KBDLLHOOKSTRUCT*)lParam;
        std::cout << " Index=" << nwinhooksdll   << "wParam="<< wParam <<" s->vkCode=" << s->vkCode << " s->scanCode=" << s->scanCode << " s->flags=" << s->flags << " s->dwExtraInfo=" << s->dwExtraInfo << "\n";
        if (GetAsyncKeyState(VK_LWIN))
        {
            if (s->vkCode == 'H')
            {

           }
            else
            {
                return 1;
            }
        }
    }

    return CallNextHookEx(_hook, nCode, wParam, lParam);
}
  • Related