2017-10-13 187 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

如果這回答您的問題,請將其標記爲接受的答案。如果沒有,評論,也許我可以進一步幫助。 –