如何在wpf C#中正确创建全局鼠标热键

时间:2017-10-13 21:28:32

标签: c# wpf mouse global-hotkey

我正在尝试使用SetWindowsHookEx()在我的程序中创建全局鼠标热键。到目前为止,我已经尝试创建它们,就像我在这里看到的一篇文章,但是,我真的不知道如何完成它。 问题是目前我还不知道_globalMouseHookCallback是什么。

这是我到目前为止所写的:

class GlobalHotkey
{

    [DllImport("user32.dll")]
    static extern IntPtr SetWindowsHookEx(int idHook, HookProc callback, IntPtr hInstance, uint threadId);

    [DllImport("user32.dll")]
    static extern bool UnhookWindowsHookEx(IntPtr hInstance);

    [DllImport("user32.dll")]
    static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);

    internal delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

    private IntPtr _hGlobalMouseHook;


    MainWindow _m;

    private const int WH_KEYBOARD_LL = 13;
    private const int WH_MOUSE_LL = 14;

    private const int WM_LBUTTONDOWN = 0x0201;
    private const int WM_LBUTTONUP = 0x0202;
    private const int WM_RBUTTONDOWN = 0x0204;
    private const int WM_RBUTTONUP = 0x0205;

    private static IntPtr hook = IntPtr.Zero;

    public GlobalHotkey(MainWindow m)
    {
        _m = m;
    }

    public void SetUpHook()
    {
        _m.rtbLog.AppendText("Setting up global Hotkey \n");

        _globalMouseHookCallback = LowLevelMouseProc;

        _hGlobalMouseHook = SetWindowsHookEx(WH_MOUSE_LL, _globalMouseHookCallback, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);

        if (_hGlobalMouseHook == IntPtr.Zero)
        {
            _m.rtbLog.AppendText("Unable to set up global mouse hook\n");
        }
    }

    public void ClearHook()
    {
        _m.rtbLog.AppendText("Deleting global mouse hook\n");

        if (_hGlobalMouseHook != IntPtr.Zero)
        {
            if (!UnhookWindowsHookEx(_hGlobalMouseHook))
            {
                _m.rtbLog.AppendText("Unable to delete global mouse hook\n");
            }

            _hGlobalMouseHook = IntPtr.Zero;
        }

    }

    public int LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0)
        {
            var wmMouse = wParam;

            if (wmMouse == (IntPtr)WM_LBUTTONDOWN)
            {
                _m.rtbLog.AppendText("Right Mouse down");
            }
            if (wmMouse == (IntPtr)WM_LBUTTONUP)
            {
                _m.rtbLog.AppendText("Left Mouse up");
            }

            if (wmMouse == (IntPtr)WM_RBUTTONDOWN)
            {
                _m.rtbLog.AppendText("Right Mouse down");
            }
            if (wmMouse == (IntPtr)WM_RBUTTONUP)
            {
                _m.rtbLog.AppendText("Right Mouse up");
            }
        }

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

这个全球性的热门话题非常难,就像有一个教程可以轻松解释像我这样的新人:P?

修改 所以我尝试将Koby Ducks示例改编为我的代码。

这是我的Hotkey课程:

class GlobalHotkey
{

    MainWindow _m;

    private static readonly object sSyncObj = new object();
    private static readonly HashSet<Key> sDownKeys = new HashSet<Key>();
    private static readonly Dictionary<Key, Action> sPressActions = new Dictionary<Key, Action>();
    private static readonly Dictionary<Key, Action> sReleaseActions = new Dictionary<Key, Action>();

    public GlobalHotkey(MainWindow m)
    {
        _m = m;

    }

    public static void ProcessKeyDown(KeyEventArgs args)
    {
        var key = args.Key;
        var action = default(Action);
        lock (sSyncObj)
        {
            if (!sDownKeys.Contains(key))
            {
                sDownKeys.Add(key);

                if (sPressActions.TryGetValue(key, out action))
                {
                    args.Handled = true;
                }
            }
        }

        action.Invoke();
    }

    public static void ProcessKeyUp(KeyEventArgs args)
    {
        var key = args.Key;
        var action = default(Action);
        lock (sSyncObj)
        {
            if (sDownKeys.Remove(key))
            {
                if (sReleaseActions.TryGetValue(key, out action))
                {
                    args.Handled = true;
                }
            }
        }

        action.Invoke();
    }

    public static void AttachPressAction(Key key, Action action)
    {
        if (action == null)
        {
            throw new ArgumentNullException(nameof(action));
        }

        lock (sSyncObj)
        {
            sPressActions.Add(key, action);
        }
    }

    public static bool DetachPressAction(Key key)
    {
        lock (sSyncObj)
        {
            return sPressActions.Remove(key);
        }
    }

    public static void AttachReleaseAction(Key key, Action action)
    {
        if (action == null)
        {
            throw new ArgumentNullException(nameof(action));
        }

        lock (sSyncObj)
        {
            sReleaseActions.Add(key, action);
        }
    }

    public static bool DetachReleaseAction(Key key)
    {
        lock (sSyncObj)
        {
            return sReleaseActions.Remove(key);
        }
    }
}

我创造了我的行动

public void MyTestAction()
    {
        rtbLog.AppendText("The B key was pressed");
    }
myAction = new Action(MyTestAction);

但是当我将我的Eventhandler添加到PreviewKeyUp-和Down事件时,它给我一个错误,指出ProcessKeyUp-和Down的参数与PreviewKeyUp-和Down不同。

PreviewKeyDown += GlobalHotkey.ProcessKeyDown;
PreviewKeyUp += GlobalHotkey.ProcessKeyUp;

1 个答案:

答案 0 :(得分:0)

编辑:无论当前关注的窗口(又名&#34;全局&#34;)如何输入,请参阅Win32键盘API上的this answer

对于您的应用集中注意力的输入(又名&#34;本地&#34;),您可以使用预览事件。

public static class HotKeySystem
{
    public static void ProcessKeyDown(object sender, KeyEventArgs args)
    {
        var key = args.Key;
        var action = default(Action);
        lock (sSyncObj) {
            if (!sDownKeys.Contains(key)) {
                sDownKeys.Add(key);
                if (sPressActions.TryGetValue(key, out action)) {
                    args.Handled = true;
                }
            }
        }
        // Invoke outside of the lock.
        action?.Invoke();
    }
    public static void ProcessKeyUp(object sender, KeyEventArgs args)
    {
        var key = args.Key;
        var action = default(Action);
        lock (sSyncObj) {
            if (sDownKeys.Remove(key)) {
                if (sReleaseActions.TryGetValue(key, out action)) {
                    args.Handled = true;
                }
            }
        }
        // Invoke outside of the lock.
        action?.Invoke();
    }
    public static void AttachPressAction(KeyCode key, Action action)
    {
        if (action == null) {
            throw new ArgumentNullException(nameof(action));
        }
        lock (sSyncObj) {
            sPressActions.Add(key, action);
        }
    }
    public static bool DetachPressAction(KeyCode key)
    {
        lock (sSyncObj) {
            return sPressActions.Remove(key);
        }
    }
    public static void AttachReleaseAction(KeyCode key, Action action)
    {
        if (action == null) {
            throw new ArgumentNullException(nameof(action));
        }
        lock (sSyncObj) {
            sReleaseActions.Add(key, action);
        }
    }
    public static bool DetachReleaseAction(KeyCode key)
    {
        lock (sSyncObj) {
            return sReleaseActions.Remove(key);
        }
    }

    private static readonly object sSyncObj = new object();
    // The keys that are currently down.
    private static readonly HashSet<KeyCode> sDownKeys = new HashSet<KeyCode>();
    // Actions triggered when a key was up, but is now down.
    private static readonly Dictionary<KeyCode, Action> sPressActions = new Dictionary<KeyCode, Action>();
    // Actions triggered when a key was down, but is now up.
    private static readonly Dictionary<KeyCode, Action> sReleaseActions = new Dictionary<KeyCode, Action>();
}

// When possible, subclass your windows from this to automatically add hotkey support.
public class HotKeyWindow : Window
{
    protected override void OnPreviewKeyDown(KeyEventArgs args)
    {
        HotKeySystem.ProcessKeyDown(this, args);
        base.OnPreviewKeyDown(args);
    }
    protected override void OnPreviewKeyUp(KeyEventArgs args)
    {
        HotKeySystem.ProcessKeyUp(this, args);
        base.OnPreviewKeyUp(args);
    }
}

// When not possible, attach event handlers like this:
window.PreviewKeyDown += HotKeySystem.ProcessKeyDown;
window.PreviewKeyUp += HotKeySystem.ProcessKeyUp;

// Use it like this:
HotKeySystem.AttachPressAction(KeyCode.F1, () => {
    // F1 hotkey functionality.
});

无论您是使用此方法还是Win32 API,请考虑其含义。如果你有&#39; A&#39;绑定,然后你无法输入&#39; a&#39;或者&#39; A&#39;进入文本输入控件。解决这个问题的一种方法是:

public static void ProcessKeyDown(object sender, KeyEventArgs args)
{
    // Detect keyboard input controls you may have issues with.
    // If one has keyboard focus, skip hotkey processing.
    if (Keyboard.FocusedElement is TextBox) {
        return;
    }

    // ...
}