全局热键与低级键盘钩子的优缺点

全局热键是指系统范围内的按键事件,即无论代码在何处,只要用户按下了特定的按键组合,程序就可以做出响应。在Windows系统中,可以通过Desktop API提供的RegisterHotkey函数来实现全局热键。然而,使用全局热键时需要注意以下几点限制:

1. 当注册一个按键时,实际上是覆盖了它原有的功能。例如,如果覆盖了F5键,用户可能会因为无法刷新浏览器页面而感到困扰。

2. 不同的程序不能注册相同的全局热键组合。如果尝试注册一个已经被其他运行中的程序占用的热键,会遇到WinAPI错误。

3. 有些按键组合是无法注册的,例如Ctrl+Alt+Del。

尽管低级键盘钩子可以绕过这些限制,但它有两个潜在的问题:

1. 代码可能会被杀毒软件标记为间谍软件(因为键盘记录)。

2. 它可能会减慢整个系统的键盘输入处理速度。

对于第一个限制,几乎无能为力,但第二个限制可以通过理解代码在做什么来避免。

当按下键盘上的一个键时,全局键盘钩子会在键盘输入到达最终目的地(即焦点应用程序)之前处理它。钩子是同步运行的。如果一个钩子运行缓慢,那么输入就会被延迟,这会让用户感到烦恼。

假设不想阻止任何按键到达其他钩子和程序,那么直观的解决方案是在单独的线程中处理输入。

以下是从开源库NonInvasiveKeyboardHook中提取的LowLevelKeyboardProc代码:

private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { var vkCode = Marshal.ReadInt32(lParam); // 为了防止减慢键盘输入,使用单独的线程处理键盘输入 ThreadPool.QueueUserWorkItem(this.HandleSingleKeyboardInput, new KeyboardParams(wParam, vkCode)); } return CallNextHookEx(_hookId, nCode, wParam, lParam); }

一个简单的方法可能是反转顺序,例如:

var returnValue = CallNextHookEx(_hookId, nCode, wParam, lParam); ProcessInputInSameThread(); return returnValue;

虽然调用CallNextHookEx确实会恢复当前按键的流程,但下一个按键的输入将不会被处理,直到阻塞代码完成并且函数返回。

如果目标是实现全局热键,而标准的WinAPI热键系统无法满足需求,那么开源的NonInvasiveKeyboardHook库是一个很好的选择。它提供了一个KeyboardHookManager,可以注册特定的按键组合(即不能将其用于间谍软件)。

建议只使用一个KeyboardHookManager实例。

以下是如何使用该库的基本步骤:

1. 实例化并启动一个KeyboardHookManager

var keyboardHookManager = new KeyboardHookManager(); keyboardHookManager.Start();

2. 停止它(稍后可以通过调用.Start再次启动):

keyboardHookManager.Stop();

3. 注册热键:

注册一个没有修饰键的热键:

keyboardHookManager.RegisterHotkey(0x60, () => { Debug.WriteLine("NumPad0 detected"); });

注册一个带有单个修饰键的热键:

keyboardHookManager.RegisterHotkey(NonInvasiveKeyboardHookLibrary.ModifierKeys.Control, 0x60, () => { Debug.WriteLine("Ctrl+NumPad0 detected"); });

注册一个带有多个修饰键的热键:

keyboardHookManager.RegisterHotkey(NonInvasiveKeyboardHookLibrary.ModifierKeys.Control | NonInvasiveKeyboardHookLibrary.ModifierKeys.Alt, 0x60, () => { Debug.WriteLine("Ctrl+Alt+NumPad0 detected"); });

或者作为修饰键的枚举:

keyboardHookManager.RegisterHotkey(new[] {NonInvasiveKeyboardHookLibrary.ModifierKeys.Control, NonInvasiveKeyboardHookLibrary.ModifierKeys.Alt}, 0x60, () => { Debug.WriteLine("Ctrl+Alt+NumPad0 detected"); });

4. 注销热键:

可以根据其唯一的按键组合或使用RegisterHotKey返回的全局唯一标识符来注销热键:

keyboardHookManager.RegisterHotkey(0x60, () => { Debug.WriteLine("NumPad0 detected"); }); keyboardHookManager.UnregisterHotkey(0x60);

或者:

var hotkeyId = keyboardHookManager.RegisterHotkey(0x60, () => { Debug.WriteLine("NumPad0 detected"); }); keyboardHookManager.UnregisterHotkey(hotkeyId); keyboardHookManager.UnregisterAll();
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485