Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Low-level Keyboard Hooks/SendInput with Winkey+L possible? (workstation lockout is intercepted in Vista and higher)

I work on a project called UAWKS (Unofficial Apple Wireless Keyboard Support) that helps Windows users use Apple's bluetooth keyboard. One of the main goals of UAWKS is to swap the Cmd key (which behaves as Winkey in Windows) with Ctrl, allowing users to do Cmd+C for copy, Cmd+T for new tab, etc.

It is currently developed using AutoHotkey, which worked pretty well under Windows XP. However, on Vista and Windows 7, Cmd+L causes problems:

  • Regardless of low-level keyboard hooks, Win+L is always intercepted by Windows and normally locks the workstation...
  • You can disable workstation locking with this registry hack, but pressing Win+L still can't be rebound in AHK
  • Pressing Win+L leaves Winkey in the Keydown state until the next (additional) Winkey Up. Simulating a Keyup event doesn't seem to work either!

It seems that Win+L is a special chord that messes everything else up.

I've looked through the AHK source code, and they try to address this problem in SendKey() in keyboard_mouse.cpp (near line 883 in v1.0.48.05), but it doesn't work. I wrote up my own low-level keyboard hook application in C#, and I see the same problem.

Has anyone else run into this? Is there a workaround?

like image 842
Brian Jorgensen Avatar asked May 25 '10 15:05

Brian Jorgensen


1 Answers

I figured out a way to do this in C#. There are four states involved in a possible Win+L keypress sequence (None, Win, Win+L, L). Whenever the Win+L state is reached, set a flag ("winLSet" below). Whenever all of the keys have been released, we check for this flag and simulate the press if it's been set.

The final piece of the puzzle is to simulate the WinKey's KeyUp before the Ctrl-L (no KeyDown). I've tried similar approaches in AutoHotkey and it never worked, but it seems to work perfectly here.

The code is below. Please see explanatory notes at the bottom if you plan to use this code.

public partial class MainWindow : Window
{
    LowLevelKeyboardHook hook;

    bool winKeyDown;
    bool lKeyDown;
    bool winLSet;

    public MainWindow()
    {
        InitializeComponent();

        hook = new LowLevelKeyboardHook();

        hook.KeyDown += OnKeyDown;
        hook.KeyUp += OnKeyUp;
    }

    void OnKeyDown(object sender, LowLevelKeyEventArgs e)
    {
        e.EventHandled = true;

        switch (e.Key)
        {
            case Key.L:
                lKeyDown = true;
                UpdateWinLState();
                e.EventHandled = winKeyDown;
                break;

            case Key.LWin:
                winKeyDown = true;
                UpdateWinLState();
                InputSimulator.SimulateKeyDown(VirtualKeyCode.LCONTROL);
                break;

            case Key.LeftCtrl:
                InputSimulator.SimulateKeyDown(VirtualKeyCode.LWIN);
                break;

            default:
                e.EventHandled = false;
                break;
        }
    }

    void OnKeyUp(object sender, LowLevelKeyEventArgs e)
    {
        e.EventHandled = true;

        switch (e.Key)
        {
            case Key.L:
                lKeyDown = false;
                UpdateWinLState();
                e.EventHandled = winKeyDown;
                break;

            case Key.LWin:
                winKeyDown = false;
                UpdateWinLState();
                InputSimulator.SimulateKeyUp(VirtualKeyCode.LCONTROL);
                break;

            case Key.LeftCtrl:
                InputSimulator.SimulateKeyUp(VirtualKeyCode.LWIN);
                break;

            default:
                e.EventHandled = false;
                break;
        }
    }

    void UpdateWinLState()
    {
        if (winKeyDown && lKeyDown)
        {
            winLSet = true;
        }
        else if (!winKeyDown && !lKeyDown && winLSet)
        {
            winLSet = false;

            InputSimulator.SimulateKeyUp(VirtualKeyCode.LWIN);

            InputSimulator.SimulateModifiedKeyStroke(
                VirtualKeyCode.LCONTROL,
                (VirtualKeyCode)'L');
        }
    }
}

For posterity: please note that this code uses InputSimulator and LowLevelKeyboardHook, which are not from the .NET Framework. LowLevelKeyboardHook is a class I wrote a while back that exposes global KeyDown and KeyUp events as C# events. There are similar examples here, here, and a bunch can be found here.

Also notice that I'm using System.Windows.Input.Key, not System.Windows.Forms.Keys, which could confuse some people. System.Windows.Input.Key is the new enumeration of keys in .NET 3.0 and above, while System.Windows.Forms.Keys is the old enumeration from Windows Forms.

like image 91
Brian Jorgensen Avatar answered Oct 06 '22 00:10

Brian Jorgensen