10

In a few words: blocking Win up after Win + Tab makes Windows think Win is still down, so then pressing S with the Win key up for example will open the search charm rather than just type "s"... until the user presses Win again. Not blocking it means the Windows Start menu will show up. I'm in a conundrum!


I have no trouble hooking into shortcuts using Alt + Tab using LowLevelKeyboardHook, or Win + Some Ubounded Key using RegisterHotKey. The problem happens only with the Win key using LowLevelKeyboardHook.

In the example below, I'm taking over the Win up event when the Win + Tab combination is detected. This results in making every following keystrokes behave as if the Win key was still down.

        private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode != HC_ACTION)
                return CallNextHookEx(_hookID, nCode, wParam, lParam);

            var keyInfo = (Kbdllhookstruct)Marshal.PtrToStructure(lParam, typeof(Kbdllhookstruct));

            if (keyInfo.VkCode == VK_LWIN)
            {
                if (wParam == (IntPtr)WM_KEYDOWN) {
                    _isWinDown = true;
                } else {
                    _isWinDown = false;

                    if (_isWinTabDetected) {
                        _isWinTabDetected = false;
                        return (IntPtr)1;
                    }
                }
            }
            else if (keyInfo.VkCode == VK_TAB && _isWinDown) {
                _isWinTabDetected = true;

                if (wParam == (IntPtr)WM_KEYDOWN) {
                    return (IntPtr)1;
                } else {
                    _isWinTabDetected = true;
                    Console.WriteLine("WIN + TAB Pressed");
                    return (IntPtr)1;
                }
            }

            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
    }
}

You can find the complete code here (note that it should replace your Program.cs in an empty WinForms project to run): https://gist.github.com/christianrondeau/bdd03a3dc32a7a718d62 - press Win + Tab and the Form title should update each time the shortcut is pressed.

Note that the intent of hooking into this specific combination is to provide an Alt + Tab alternative without replacing Alt + Tab itself. An answer providing the ability to launching custom code using Win + Tab will also be accepted.

Here are my ideas, for which I could not find documentation. All would potentially answer my question successfully.

  • Tell Windows to "cancel" the Win up without actually triggering it
  • Prevent Windows from launching the Start menu once
  • Hook directly in the Windows' Win + event rather than manually hooking into the keystrokes (This would be by far my first choice if that exists)
Christian Rondeau
  • 4,143
  • 5
  • 28
  • 46

2 Answers2

4

System need to know you release the Windows key. I check the difference between my own hook who doesn't have this problem and the only diffence between your and mine is this line :

if (_isWinTabDetected) {
    _isWinTabDetected = false;
     return (IntPtr)1; //THIS LINE 
}
TheMightyX2Y
  • 1,473
  • 1
  • 16
  • 24
  • This solution won't work, because when Windows receives the Win up without a shortcut, it launches the Windows start menu. The only reason you don't see that in the example is because I used a `MessageBox`, and it seems the `MessageBox` will cancel the Windows menu. I'll change the example to write something in the `Form1` instead... – Christian Rondeau Jul 09 '15 at 01:08
2

This appears to do exactly what you want (omit RWin if you wish).

Please be considerate and unregister this KB hook when your app loses focus!

    [DllImport("user32.dll")]
    static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey);

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode == HC_ACTION)
        {
            var keyInfo = (Kbdllhookstruct) Marshal.PtrToStructure(lParam, typeof (Kbdllhookstruct));
            if ((int) wParam == WM_KEYDOWN
                && keyInfo.VkCode == VK_TAB
                && (GetAsyncKeyState(Keys.LWin) < 0 || GetAsyncKeyState(Keys.RWin) < 0))
            {
                _mainForm.Text = "Win + Tab was pressed " + (++_winTabPressCounter) + " times";
                return (IntPtr) 1;
            }
        }

        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

I tried several things before discovering this technique. This post was the most helpful https://stackoverflow.com/a/317550/55721

Community
  • 1
  • 1
dss539
  • 6,804
  • 2
  • 34
  • 64
  • So in short: hook the Win key down, instead of the Win key up, and don't prevent other keystrokes :) I removed `if (wParam == (IntPtr)WM_KEYDOWN) { return (IntPtr)1; }` and the other `return (IntPtr)1;` in the `if (keyInfo.VkCode == VK_LWIN)` block, and that worked too, though I think your code is simpler and cleaner. I've been stuck for 9 months on this, so my heartfelt thanks for taking the time to look into this! (And the solution was simple, obviously...) – Christian Rondeau Jul 25 '15 at 01:09
  • Hmm, I did not try what you suggest. I considered it, but assumed that trapping winkey down would interfere with other winkey functions, like winkey+R. Does your approach allow that? – dss539 Jul 25 '15 at 04:51
  • Yes it does. In other words, I "record" the Win Down instead of checking the Win Down state when the Tab Down happens. This simply made the code harder to read for the same behavior, so... yours is better! – Christian Rondeau Jul 25 '15 at 16:10