2017-10-13 9 views
0

SetWindowsHookEx()を使用してプログラムでグローバルマウスホットキーを作成しようとしています。これまでのところ私はここで見たような1つの記事のようにそれらを作成しようとしましたが、私は実際にこの方法を知っていません。 問題は現在、_globalMouseHookCallbackが何であるかを正確に知ることができません。wpfでグローバルマウスホットキーを正しく作成する方法

これは私がこれまでに書かれたものです:

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?

EDIT 私はコビーダックスの例を自分のコードに適用しようとしました。

これは私のホットキークラスです:

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-とダウンイベントに私のEventHandlerを追加するとすぐに、それは私に言って、エラーを与えましたProcessKeyUp-とDownのパラメータは、PreviewKeyUp-とDownと同じではありません。

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

これは、アプリケーションにフォーカスがない場合でも発生するキーボードイベントとマウスイベントを捕まえたいという意味ですか? –

+0

はい正確には – Celmos

+0

私の更新された回答を見てください。問題は明らかに送信側パラメータを省いたことです。大部分のWindows GUIイベントは、 '(オブジェクト送信者、EventArgs args) '形式です。 –

答えて

0

編集:入力の場合かかわらず、現在フォーカスウィンドウの(別名 "グローバル")、のWin32キーボードAPIにthis answerを参照してください。

あなたのアプリがフォーカスされている間(入力は「ローカル」)、プレビューイベントを使用できます。

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'を入力することはできません。これを回避する1つの方法は次のとおりです。

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

これがあなたの質問に答えるなら、それを受け入れられた答えとしてマークしてください。そうでない場合は、コメントして、おそらく私はさらに助けることができます。 –

関連する問題