一. 简介
猫和婴儿有很多共同之处。他们都喜欢吃家中养植的植物,都非常讨厌关门。他们也都爱玩弄你的键盘,结果是,你正发送给你的老板的电子邮件可能是以半截句子发送出去的,你的Excel帐户也被加入了一些乱七八糟的内容,并且你还没有注意到,当打开Windows资源管理器时,若干文件已经被移到了回收站!
其解决方案是,开发一个应用程序实现如下功能:只要键盘处于"威胁状态"你就可以进行切换,并确保任何键盘输入活动都不会造成危害。本文想展示如何使用一种低级Windows API钩子在一个C#应用程序中实现键盘"控制"。下图是本文示例程序的一个运行快照。
二. 背景 其实,已经存在许多有关于Windows钩子的文章和示例代码,并且已经有人编写过与本文几乎一样的C++示例程序。然而,当我搜索相应的C#应用程序的源码时,却找到极少的.NET示例,而且没有一个程序能够提供一个方便的自包含的C#类。
.NET框架能够使你以托管方式来存取你最常使用的键盘事件(通过KeyPress,KeyUp和KeyDown)。遗憾的是,这些事件都不能被用来停止Windows组合键(如Alt+Tab或Windows"开始"键),从而允许用户"远离"某一个应用程序。
本文的想法在操作系统级上捕获键盘事件而不是通过框架级来实现。为此,应用程序需要使用Windows API函数来把它自身添加到应用程序"钩子链"中以监听来自操作系统的键盘消息。当它收到这种类型的消息时,该应用程序能够选择性地传递消息,或者进行正常处理,或者"镇压"它以便不再有其它应用程序(包括Windows)来影响它。本文正是想解释其实现机理。
然而,请注意,本文中的代码仅适用于基于NT版本的Windows(NT,2000和XP),并且无法使用这个方法来停用Ctrl+Alt+Delete。有关于如何实现这一点,你可以参考MSDN有关资料。
三. 使用代码 为了易于使用,我在本文中提供了两个独立的zip文件。一个仅包含KeyboardHook类,这是本文介绍的重点。另一个是一个完整的微软Visual C# 2005 Express Edition应用程序工程,名叫"Baby Keyboard Bash",它实现显示击键的名字或彩色的形状以响应于击键。
四. 实例化类 键盘钩子是通过keyboard.cs中的KeyboardHook类来建立和管理的。这个类实现了IDisposable接口,因此,实例化它的最简单的方法是在应用程序的Main()方法中使用using关键字来封装Application.Run()调用。这将确保只要该应用程序开始即建立钩子并且,更重要的是,当该应用程序结束时立即使这个钩子失效。
这个类引发一个事件来警告应用程序已经有键被按下,因此主表单能够存取在Main()方法中创建的KeyboardHook实例就显得非常重要;最简单的方法是把这个实例存储在一个公共成员变量中。
KeyboardHook提供了三种构造器来启用或禁用某些设置:
· KeyboardHook():捕获所有击键,没有任何内容传递到Windows或另外的应用程序。
· KeyboardHook(string param):把参数串转换为Parameters枚举中的值之一,然后调用下面的构造器:
· KeyboardHook(KeyboardHook.Parameters enum):根据从Parameters枚举中选择的值的不同,分别启动下列设置:
o Parameters.AllowAltTab:允许用户使用Alt+Tab切换到另外的应用程序。
o Parameters.AllowWindowsKey:允许用户使用Ctrl+Esc或一种Windows键存取任务栏和开始菜单。
o Parameters.AllowAltTabAndWindows:启用Alt+Tab,Ctrl+Esc和Windows键。
o Parameters.PassAllKeysToNextApp:如果该参数为true,那么所有的击键将被传递给任何其它监听应用程序(包括Windows)。
当击键继续被键盘钩子捕获时,启用Alt+Tab和/或Windows键允许实际使用该计算机者切换到另一个应用程序并且使用
鼠标与之交互。PassAllKeysToNextApp设置有效地禁用了击键捕获;这个类也是建立一个低级键盘钩子并且引发它的KeyIntercepted事件,但是它还负责把键盘事件传递到另一个监听程序。
因此,实例化该类以捕获所有击键的方法如下:
public static KeyboardHook kh; [STAThread] static void Main() { //其它代码 using (kh = new KeyboardHook()) { Application.Run(new Form1()); } |