How library works


Before ShortcutManager is used for the first time, keyboard hook is created using Win API. This is done calling Start() method in static constructor.

private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);

[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(int code, HookProc func, IntPtr hInstance, int threadID);

private static void Start()
{
    Running = true;

    // register hook proc delegate
    _hookProc = new HookProc(HookProcProcess);

    // get current process
    using (Process process = Process.GetCurrentProcess())
    {
        // get current process module
        using (ProcessModule module = process.MainModule)
        {
            // set windows hook on keyboard using external method
            _hook = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc, GetModuleHandle(module.ModuleName), 0);
        }
    }
}

External method SetWindowsHookEx registers our handler HookProcProcess. This method processes input data and fires subscribed handler if conditions are met.

[DllImport("user32.dll")]
private static extern short GetKeyState(VirtualKeys nVirtKey);

[DllImport("user32.dll")]
private static extern int CallNextHookEx(IntPtr hhook, int code, IntPtr wParam, IntPtr lParam);

private static int HookProcProcess(int code, IntPtr wParam, IntPtr lParam)
{
    bool handled = false;

    if (Shortcuts.Count > 0 && (int)wParam == WM_KEYDOWN || (int)wParam == WM_SYSKEYDOWN)
    {
        // convert input data
        var keyHook = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
        var key = (Keys)keyHook.vkCode;

        // create template ShortcutInfo
        var recreate = new ShortcutInfo();
        recreate.Key = (Key)((int)key);

        // shift key is pressed
        if ((GetKeyState(VirtualKeys.VK_SHIFT) & 0x80) == 0x80)
        {
            recreate.Modifiers |= KeyModifiers.Shift;
        }

        // control key is pressed
        if ((GetKeyState(VirtualKeys.VK_CONTROL) & 0x80) == 0x80)
        {
            recreate.Modifiers |= KeyModifiers.Control;
        }

        // alt key is pressed
        if ((GetKeyState(VirtualKeys.VK_MENU) & 0x80) == 0x80)
        {
            recreate.Modifiers |= KeyModifiers.Alt;
        }

        foreach (ShortcutInfo item in Shortcuts.GetByTemplate(recreate))
        {
            if (item.Form != null && item.Form != Form.ActiveForm)
            {
                continue;
            }

            bool eventHandled = false;
            item.Handler(item, ref eventHandled);

            handled = eventHandled;
            if (handled) break;
        }
    }

    // hook is handled
    if (handled)
    {
        return 1;
    }

    // call next hook
    return CallNextHookEx(_hook, code, wParam, lParam);
}

The last important thing is unhook keyboard on application exit. Subscription to event Application.ApplicationExit is placed into static constructor.

[DllImport("user32.dll")]
private static extern int UnhookWindowsHookEx(IntPtr hhook);

static ShortcutManager()
{
    Application.ApplicationExit += ApplicationExit;
    Start();
}

private static void ApplicationExit(object sender, EventArgs e)
{
    // unhook and clear
    if (Running)
    {
        Stop();
    }
}

private static void Stop()
{
    Running = false;

    // unhook by call external method
    UnhookWindowsHookEx(_hook);

    // clear handle and delegate
    _hook = IntPtr.Zero;
    _hookProc = null;
}

Last edited Nov 15, 2011 at 2:58 PM by CSharpDeveloping, version 2

Comments

No comments yet.