摘要 在家里,婴儿和其它动物可能会重击你的计算机键盘,致使出现各种无法预言的结果。本文中的这个C#示例应用程序将向你展示如何基于Windows钩子API来实现在击键造成任何危害之前捕获它们。
一. 简介
猫和婴儿有很多共同之处。他们都喜欢吃家中养植的植物,都非常讨厌关门。他们也都爱玩弄你的键盘,结果是,你正发送给你的老板的电子邮件可能是以半截句子发送出去的,你的Excel帐户也被加入了一些乱七八糟的内容,并且你还没有注意到,当打开Windows资源管理器时,若干文件已经被移到了回收站!
其解决方案是,开发一个应用程序实现如下功能:只要键盘处于”威胁状态”你就可以进行切换,并确保任何键盘输入活动都不会造成危害。本文想展示如何使用一种低级Windows API钩子在一个C#应用程序中实现键盘”控制”。下图是本文示例程序的一个运行快照。
\n
其实,已经存在许多有关于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事件,但是它还负责把键盘事件传递到另一个监听程序。
因此,实例化该类以捕获所有击键的方法如下:
\n
public static KeyboardHook kh;
[STAThread]
static void Main()
{
//其它代码
using (kh = new KeyboardHook())
{
Application.Run(new Form1());
}\n
[STAThread]
static void Main()
{
//其它代码
using (kh = new KeyboardHook())
{
Application.Run(new Form1());
}\n