C# 全局鼠标和键盘钩子处理






4.97/5 (599投票s)
该类允许您捕获键盘和鼠标,以及/或者检测它们的活动,即使应用程序在后台运行或根本没有用户界面。
新闻
这篇文章发表于 2004 年,并于 2006 年和 2008 年更新。在此期间,我一直收到大量积极的反馈和推荐。此外,还有许多有用的贡献,通常以论坛代码片段的形式发布。现在,我决定不独自发布另一个版本,而是邀请大家积极参与。所以请充满热情,随时加入项目:globalmousekeyhook.codeplex.com
您可以帮助:- 贡献代码。
- 创建问题项,请求附加功能或报告缺陷。
- 为您感兴趣的功能和修复投票。
- 在不同环境中测试组件。
- 编写开发者文档。
这将使我们能够保持本原创文章的更新。
感谢大家在 CodeProject 论坛上提供的所有精彩评论,并期待您在 globalmousekeyhook.codeplex.com 的精彩贡献!
引言
该类允许您捕获键盘和鼠标,以及/或者检测它们的活动,即使应用程序在后台运行或根本没有用户界面。该类使用 KeyEventArgs
和 MouseEventArgs
引发通用的 .NET 事件,因此您可以轻松获取所需的任何信息。
背景
有许多应用程序在后台运行并检测用户的不活动状态以更改其模式。例如,MSN Messenger(或其他任何即时通讯工具)。我本来打算编写一个这样的应用程序,所以我搜索了 MSDN 并找到了“正好”需要的东西:“318804 - HOW TO: Set a Windows Hook in Visual C# .NET”。这篇文章描述了如何捕获鼠标移动,但它只在应用程序处于活动状态时才有效。在这篇文章的结尾,我发现了这个解释:“.NET Framework 不支持全局钩子。您不能在 Microsoft .NET Framework 中实现全局钩子…”。无论如何,我继续研究,并发现了一些例外。存在 WH_KEYBOARD_LL
和 WH_MOUSE_LL
钩子,它们可以全局安装。因此,我basically 用 WH_MOUSE_LL
替换了 MSDN 示例中的 WH_MOUSE
,它就能工作了。
第二步是将接收到的信息提取到 .NET EventArgs
中并引发相应的事件。
我在 CodeProject 上找到了一个类似的帖子,是 Michael Kennedy 的“Global System Hooks in .NET”,但我 dislike 的是,其中有一个 C++ 的非托管 DLL 是该解决方案的主要部分。这个非托管 DLL 是用 C++ 编写的,而且有许多类,使得将其集成到我自己的小型应用程序中变得复杂。
修订
这篇文章发表于 2004 年,并于 2006 年更新。在此期间,我一直收到大量积极的反馈和推荐。此外,技术也取得了一些进步,例如 .NET Framework 3.5 或 Visual Studio 2008。所以我决定再次更新它。
我对解决方案进行了重构和改进,使其更加灵活、稳定和高效。但这种重构也带来了一些缺点:
- 代码行数和文件数量增加。
- 与旧版 .NET 的向后兼容性丢失。
因此,我打算仍然提供旧版本供下载。
使用代码 [版本 2]
简单方法
如果您正在开发 Windows Forms 应用程序并倾向于拖放式编程,程序集 Gma.UserActivityMonitor.dll 中有一个名为 GlobalEventProvider
的组件。只需将其拖放到您的窗体上,然后通过属性编辑器事件选项卡创建事件。
替代方法
使用 static
类 HookManager
提供的事件。请注意,事件中的 sender
对象始终为 null
。
有关更多使用提示,请参阅所附演示应用程序的源代码。
使用代码 [版本 1]
要将此类用在您的应用程序中,您只需创建一个实例并挂接您想处理的事件。钩子在对象创建时自动安装,但您可以使用相应的 public
方法来停止和开始监听。
UserActivityHook actHook;
void MainFormLoad(object sender, System.EventArgs e)
{
actHook= new UserActivityHook(); // crate an instance
// hang on events
actHook.OnMouseActivity+=new MouseEventHandler(MouseMoved);
actHook.KeyDown+=new KeyEventHandler(MyKeyDown);
actHook.KeyPress+=new KeyPressEventHandler(MyKeyPress);
actHook.KeyUp+=new KeyEventHandler(MyKeyUp);
}
现在,一个处理事件的示例
public void MouseMoved(object sender, MouseEventArgs e)
{
labelMousePosition.Text=String.Format("x={0} y={1}", e.X, e.Y);
if (e.Clicks>0) LogWrite("MouseButton - " + e.Button.ToString());
}
从 [版本 0] 到 [版本 1] 的更改和更新
我想感谢大家在讨论论坛上提供的所有有益评论。在本文于 2004 年 6 月 4 日发布后,出现了很多错误和建议。同样的主题反复出现,我不得不参考讨论中的先前帖子,因此我决定修改代码并发布一个 FAQ。这是最重要更改的列表:
- 项目已转换为 Visual Studio 2005。
- 大写字母问题已解决。
- 鼠标滚轮信息现在包含在事件参数中。
- 更好的异常处理。
- 使用事件参数的
Handled
属性取消键盘事件。 - 函数的 XML 文档。
FAQ [版本 1]
提问
由于调用 SetWindowsHookEx
时出现异常错误,项目无法在 Visual Studio .NET 2005 的调试模式下运行。为什么?这是 .NET 2.0 的问题吗?
答案
编译后的发布版本运行正常,所以不可能是 .NET 2.0 的问题。为了解决这个问题,您只需取消项目属性中名为“启用 Visual Studio 托管进程”的复选框。在菜单:项目 -> 项目属性... -> 调试 -> 启用 Visual Studio 托管进程。
提问
我需要在处理某些按键后抑制它们。
答案
只需在您已处理的按键事件中将 e.Handled
属性设置为 true
。这可以防止按键被其他应用程序处理。
提问
是否可以将您的全局钩子转换为应用程序钩子,只捕获应用程序内的按键和鼠标移动?
答案
是的。只需使用...
private const int WH_MOUSE = 7;
private const int WH_KEYBOARD = 2;
... 替换
private const int WH_MOUSE_LL = 14;
private const int WH_KEYBOARD_LL = 13;
提问
在 Win98 (Windows ME, Windows 95) 上可以工作吗?
答案
可以,也可以不可以。全局钩子 WH_MOUSE_LL
和 WH_KEYBOARD_LL
只能在 Windows NT/2000/XP 下监视。在其他情况下,您只能使用应用程序钩子(WH_MOUSE
和 WH_KEYBOARD
),它们只捕获应用程序内的按键和鼠标移动。
提问
当我通过点击标题栏上的X按钮关闭应用程序时,会有一个延迟。如果我通过其他事件(如按钮点击)关闭应用程序,则工作正常。
答案
这是 Microsoft 的一个已知 bug。它与 Windows 主题有关。如果您禁用 Windows 主题,问题就会消失。另一种选择是让钩子代码在辅助线程中运行。
提问
如何捕获组合键,例如 Ctrl+Shift+A?
答案
您需要跟踪哪些键被按下但未被释放。只有最近按下的键会发送 KeyDown
消息,但其他键在释放时仍会发送 KeyUp
。因此,如果您设置三个标志 IsCtrlDown
、IsShiftDown
和 IsADown
,并在 KeyDown
时将其设置为 true
,在 KeyUp
时设置为 false
,那么表达式 (IsCtrlDown && IsShiftDown && IsADown)
就会得到所需的结果。
提问
它能与 .NET Framework 1.1 和 Visual Studio 2003 一起使用吗?
答案
是的。文件 UserActivityHook.cs 可以在 Visual Studio 2003 .NET 1.1 项目中直接使用,无需任何更改。