我有一个使用Win32 RegisterHotKey方法的WPF应用程序和一个HwndSource.AddHook消息处理程序。
如果不使用HwndSourceHook的HwndSourceHook参数来执行任何操作,我将尝试添加不处理热键的功能。
但是,将handled设置为false或true并不会改变行为,因为行为总是拦截已注册的热键,并且不会将它们传递给按下的应用程序。
我有另一个使用Windows Application.AddMessageFilter处理程序的应用程序,在这种情况下,当返回false指示消息未被处理时,它可以很好地处理相同的热键。
在这里可以找到源:https://github.com/trevorlloydelliott/eve-switcher/blob/master/HotkeyHandler.cs
我甚至把我的逻辑简化成这样:
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
const int WM_HOTKEY = 0x0312;
if (msg == WM_HOTKEY)
{
handled = false;
return IntPtr.Zero;
}
}我还尝试不使用窗口和HwndSource钩子,而是使用ComponentDispatcher.ThreadFilterMessage事件的方法来拦截所有WM_HOTKEYs并设置为false,如下所示:
private void ComponentDispatcher_ThreadFilterMessage(ref MSG msg, ref bool handled)
{
const int WM_HOTKEY = 0x0312;
if (msg .message == WM_HOTKEY)
{
handled = false;
}
}这也无法将热键传递给基础应用程序。在我的测试中,我正在尝试打开记事本的Tab热键。我的应用程序会将Tab热键注册到RegisterHotKey,然后设置为false,但是热键永远不会到达记事本。一旦我关闭我的应用程序,它就会到达记事本。
我使用RegisterHotKey的方式和在我的其他Windows应用程序中一样。
发布于 2021-03-04 21:06:08
Win32 RegisterHotKey()定义了一个系统范围的热键.热键是全局的,在常规键盘输入处理之前进行处理,这意味着如果成功注册热键,按该键将导致您获得热键消息而不是普通的键盘输入消息。如果发生热键按下事件,则其他应用程序将不会看到该键按下。那是故意的。
RegisterHotKey是使用全局热键的正确方法。关键在于,通过为应用程序指定一个热键,可以确保该键在您的系统上运行的所有应用程序中都是唯一的,并且您的应用程序将始终接收该键的keypress事件。
使用一个简单的键(例如Tab)作为全局热键会造成与具有焦点的应用程序的本地热键冲突的问题。因此,全局热键应该能够使用户避免与他通常使用的应用程序发生冲突。
但是,在WPF中还有另一种处理热键的方法。通过安装带有WH_KEYBOARD_LL方法调用的低级SetWindowsHookEx键盘挂钩,您可以使用它。它使您能够监视输入队列中的键盘输入事件。
下面是使用钩子的类:
using System;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Input;
namespace KeyboardHooks
{
public delegate void HotkeyPressedEventHandler(object sender, HotkeyPressedEventArgs e);
public class HotkeyPressedEventArgs : EventArgs
{
public Key Key { get; }
public ModifierKeys Modifiers { get; }
public bool Handled { get; set; }
public HotkeyPressedEventArgs(Key key, ModifierKeys modifiers)
{
Key = key;
Modifiers = modifiers;
}
}
public class HotkeyManager
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
[StructLayout(LayoutKind.Sequential)]
private class KeyboardHookStruct
{
/// <summary>
/// Specifies a virtual-key code. The code must be a value in the range 1 to 254.
/// </summary>
public int vkCode;
/// <summary>
/// Specifies a hardware scan code for the key.
/// </summary>
public int scanCode;
/// <summary>
/// Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
/// </summary>
public int flags;
/// <summary>
/// Specifies the time stamp for this message.
/// </summary>
public int time;
/// <summary>
/// Specifies extra information associated with the message.
/// </summary>
public int dwExtraInfo;
}
private delegate int HookProc(int nCode, int wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern int CallNextHookEx(
int idHook,
int nCode,
int wParam,
IntPtr lParam);
private int keyboardHook = 0;
public HotkeyManager()
{
}
public event HotkeyPressedEventHandler HotkeyPressed;
public void Start()
{
// install Keyboard hook only if it is not installed and must be installed
if (keyboardHook == 0)
{
// Create an instance of HookProc.
keyboardHook = SetWindowsHookEx(
WH_KEYBOARD_LL,
KeyboardHookProc,
Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
0);
// If SetWindowsHookEx fails.
if (keyboardHook == 0)
{
// Returns the error code returned by the last unmanaged function called using platform invoke that has the DllImportAttribute.SetLastError flag set.
int errorCode = Marshal.GetLastWin32Error();
// do cleanup
Stop(false);
// Initializes and throws a new instance of the Win32Exception class with the specified error.
throw new Win32Exception(errorCode);
}
}
}
public void Stop(bool throwExceptions)
{
// if keyboard hook set and must be uninstalled
if (keyboardHook != 0)
{
// uninstall hook
int retKeyboard = UnhookWindowsHookEx(keyboardHook);
// reset invalid handle
keyboardHook = 0;
// if failed and exception must be thrown
if (retKeyboard == 0 && throwExceptions)
{
//Returns the error code returned by the last unmanaged function called using platform invoke that has the DllImportAttribute.SetLastError flag set.
int errorCode = Marshal.GetLastWin32Error();
//Initializes and throws a new instance of the Win32Exception class with the specified error.
throw new Win32Exception(errorCode);
}
}
}
private int KeyboardHookProc(int nCode, int wParam, IntPtr lParam)
{
bool handled = false;
if (nCode >= 0 && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
if (HotkeyPressed != null)
{
// read structure KeyboardHookStruct at lParam
KeyboardHookStruct khStruct =
(KeyboardHookStruct) Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
if (khStruct != null)
{
Key key = KeyInterop.KeyFromVirtualKey(khStruct.vkCode);
HotkeyPressedEventArgs args = new HotkeyPressedEventArgs(key, Keyboard.Modifiers);
HotkeyPressed.Invoke(this, args);
handled = args.Handled;
}
}
}
// if event handled in application do not handoff to other listeners
if (handled)
{
return 1;
}
return CallNextHookEx(keyboardHook, nCode, wParam, lParam);
}
}
}使用实例:
HotkeyManager hotkeyManager = new HotkeyManager();
hotkeyManager.HotkeyPressed += HotkeyManagerOnHotkeyPressed;
hotkeyManager.Start();
private void HotkeyManagerOnHotkeyPressed(object sender, HotkeyPressedEventArgs e)
{
if (e.Key == Key.Tab && e.Modifiers == ModifierKeys.None)
{
Console.WriteLine("Tab pressed!");
//e.Handled = true;
}
}关于WinForm的PreFilterMessage和WPF的HwndSourceHook之间的区别,我猜第一个是在消息传递给任何事件处理程序之前调用的,而第二个本身就是一个事件处理程序。所以他们的行为不同。
奖金。还有另一种方法可以将未处理的热键按键传递给其他应用程序,但是它不太可靠。如果全局热键未被处理,您可以注销它,用该键发送系统键盘事件,然后再注册热键。基本上,您做了以下工作:
if (!handled)
{
UnregisterHotkey(hotkey.Gesture);
KeyboardMessage.Send(hotkey.Gesture.Key);
RegisterHotkey(hotkey.Gesture);
}KeyboardMessage类,您可以找到这里。
https://stackoverflow.com/questions/66395223
复制相似问题