简体   繁体   English

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

[英]How properly create a global mouse hotkey in wpf C#

I am trying to create global mouse hotkeys in my program using SetWindowsHookEx() . 我正在尝试使用SetWindowsHookEx()在程序中创建全局鼠标热键。 So far I have tried to create them like in one post I have seen here but, I dont really know how to finsh this. 到目前为止,我试图像在这里看到的一篇文章中那样创建它们,但是,我真的不知道该如何解决。 The problem is currently that I dont exactly know what _globalMouseHookCallback is. 目前的问题是我不完全知道_globalMouseHookCallback是什么。

This is what I have written so far: 到目前为止,这是我写的:

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);
    }
}

This global hotkey stuff is pretty hard is there like a tutorial that explains it easily for newbs like me :P ? 这些全局热键的内容非常难,是否像教程一样为像我这样的新手轻松地解释它:P吗?

EDIT So I tried adapting Koby Ducks example to my code. 编辑因此,我尝试将Koby Ducks示例修改为我的代码。

This is my Hotkey class: 这是我的热键课程:

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);
        }
    }
}

And I created my action 我创造了我的行动

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

But as soon as I added my Eventhandlers to the PreviewKeyUp- and Down Event it gave me an error saying that the Parameters of ProcessKeyUp- and Down, are not the same as PreviewKeyUp- and Down. 但是,一旦我将Eventhandlers添加到PreviewKeyUp-和Down事件中,它就给我一个错误,指出ProcessKeyUp-和Down的参数与PreviewKeyUp-和Down不同。

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

Edit: For input regardless of the currently focused window(aka "global"), see this answer on the Win32 keyboard API. 编辑:对于无论当前焦点位于哪个窗口(也称为“全局”)的输入,请参见Win32键盘API上的此答案

For input while your app is focused(aka "local"), you could use preview events. 要在应用程序处于焦点状态(也称为“本地”)时进行输入,可以使用预览事件。

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.
});

Regardless of if you're using this method or the Win32 API, consider the implications. 无论您使用的是此方法还是Win32 API,都应考虑其含义。 If you have 'A' bound, then you won't be able to input 'a' or 'A' into text input controls. 如果绑定了“ A”,则将无法在文本输入控件中输入“ a”或“ A”。 One way to work around this is: 解决此问题的一种方法是:

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;
    }

    // ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM