Как правильно создать глобальную горячую клавишу мыши в wpf C#
Я пытаюсь создать глобальные горячие клавиши мыши в моей программе, используя 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);
Но как только я добавил свои обработчики событий в PreviewKeyUp- и Down Event, он выдал ошибку, в которой говорилось, что параметры ProcessKeyUp- и Down не совпадают с PreviewKeyUp- и Down.
PreviewKeyDown += GlobalHotkey.ProcessKeyDown;
PreviewKeyUp += GlobalHotkey.ProcessKeyUp;
1 ответ
Редактировать: Для ввода независимо от текущего окна (также называемого "глобальным"), смотрите этот ответ в API клавиатуры Win32.
Для ввода, когда ваше приложение сфокусировано (или "локально"), вы можете использовать события предварительного просмотра.
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, учтите последствия. Если у вас есть граница "А", вы не сможете ввести "А" или "А" в элементы управления вводом текста. Один из способов обойти это:
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;
}
// ...
}