2017-10-13 79 views
0

我想在我的程序中使用SetWindowsHookEx()创建全局鼠标热键。到目前为止,我试图创造他们就像我在这里看到的一篇文章,但我真的不知道如何去做这件事。 目前的问题是,我不知道_globalMouseHookCallback是什么。如何在wpf C#中正确创建全局鼠标热键#

这是我至今写:

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

这个全局热键的东西是相当困难的是有像一个教程,很容易解释它像我newbs:P?

编辑 因此,我尝试调整Koby鸭子的例子到我的代码。

这是我的热键类:

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

但只要我说我的事件处理到PreviewKeyUp-和向下事件它给了我一个错误说ProcessKeyUp-和Down的参数与PreviewKeyUp-和Down不同。

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

意思是说,即使应用程序没有焦点,您仍需要捕获键盘和鼠标事件? –

+0

是的,正是 – Celmos

+0

看到我更新的答案。问题是我显然遗漏了sender参数。绝大多数Windows GUI事件的形式为'(object sender,EventArgs args)'。 –

回答

0

编辑:对于输入不管目前的重点窗口(又名 “全球”),请参阅this answer上的Win32 API键盘。

当您的应用程序专注于(也称为“本地”)时,您可以使用预览事件进行输入。

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,都应考虑其含义。如果您绑定了“A”,那么您将无法在文本输入控件中输入“a”或“A”。解决此问题的一种方法是:

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

    // ... 
} 
+0

此方法是否也适用于鼠标热键? – Celmos

+0

OnPreviewMouseDown/Up通过鼠标输入实现相同的功能。要在不使用Win32 API的情况下检测全局鼠标输入,请使用[CaptureMouse](https://msdn.microsoft.com/en-us/library/system.windows.uielement.capturemouse(v = vs.110).aspx)。 –

+0

如果这回答您的问题,请将其标记为接受的答案。如果没有,评论,也许我可以进一步帮助。 –