(我指导的C#程序设计论文,已于《软件报》发表)
在电脑实验课上,经常会有学生利用各种机会偷偷上网玩游戏,不仅影响自己的学习,也影响整个教学,所以一直希望能有相关的程序自动监控并阻止学生上网玩游戏。这段时间学习C#,了解到键盘钩子(Hook)可以监视键盘的输入,所以尝试着编写了一个程序,用它来自动监控学生上网玩游戏。
有关C#
HOOK的功能,网上有许多相关的文章介绍,但都是纯理论方面的解释,具体实例不多。希望本文起到抛砖引玉的作用,对有心了解C#环境下HOOK编程的程序员有所帮助。
一、程序设计思想
为了能够监视到学生正在上网玩游戏,需要知道当前的IE窗口是否打开,并且知道学生的键盘输入情况。一般玩游戏都要频繁使用四个方向键和空格键,本程序将简单统计这五个键的使用频率,当在一定时间内按键频率达到设定标准时,即认为学生在玩游戏,关闭其IE窗口。因此我们需要解决三个问题:截获学生键盘的输入情况、获取系统当前正在运行的进程列表和停止IE进程。在本方案中,利用系统钩子中的WH_KEYBOARD_LL
Hook来截取键盘输入;利用GetProcesses()来获取进程;利用Kill()来停止进程。
二、编程实施方案
1 编程的重点是利用系统钩子中的WH_KEYBOARD_LL
Hook来截取键盘输入,我们将这部分功能单独在UserActivityHook.cs中编写,然后通过计时器控件进行定时调用,以便统计规定时间内键盘的输入情况。UserActivityHook.cs中的程序代码主要包括以下几部分。
① 定义UserActivityHook,其中Start()将负责安装Hook,Stop()负责卸载HOOK。
public class UserActivityHook : object {
public UserActivityHook() {
Start();
}
~UserActivityHook() {
Stop();
}
|
② 对程序中使用到的一些事件、变量、常量等进行定义和初始化。
public event KeyEventHandler KeyDown;
public delegate int HookProc(int nCode, Int32
wParam, IntPtr lParam);
static int hKeyboardHook = 0;
public const int WH_KEYBOARD_LL = 13;
//13表示采用全局钩子监听键盘消息
HookProc KeyboardHookProcedure;
//将KeyboardHookProcedure声明为HookProc类
//对键盘消息,简单定义为以下结构
[StructLayout(LayoutKind.Sequential)]
public class KeyboardHookStruct{
public int
vkCode; //指定虚拟代码,代码值为 1 至
254。
public int scanCode; //指定键的实际硬件扫描代码。
}
[DllImport("user32")]
public static extern int GetKeyboardState(byte[]
pbKeyState);
private const int WM_KEYDOWN = 0x100;
private const int WM_SYSKEYDOWN = 0x104;
|
③
为了能够正常使用HOOK,我们需要调用库user32.dll,以便使用SetWindowsHookEx、UnhookWindowsHookEx
来安装、卸载hook,并且使用CallNextHookEx将hook信息传递给hook链中的下一个hook进程。相应的语句格式是:
public static extern int SetWindowsHookEx(int
idHook, HookProc lpfn,
IntPtr hInstance, int threadId);
public static extern bool UnhookWindowsHookEx(int
idHook);
public static extern int CallNextHookEx(int idHook,
int nCode,
Int32 wParam, IntPtr lParam);
|
④ Hook在使用之前,需要进行安装,具体代码如下:
public void Start(){
if(hKeyboardHook == 0) {
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,
KeyboardHookProcedure,
Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
0);
if(hKeyboardHook == 0 )
Stop();
}
}
|
⑤
HOOK在使用完之后需要使用UnHookWindowsHookEx()来卸载,否则会造成麻烦。这里我们用Stop()来卸载键盘钩子,具体代码如下:
public void Stop(){
bool retKeyboard = true;
if (hKeyboardHook != 0) {
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
}
}
|
⑥ 现在,开始编写整个程序的主要部分KeyboardHookProc()以便监视键盘,截获键盘输入。
private int KeyboardHookProc(int nCode, int wParam,
IntPtr lParam){
if ((nCode >= 0)
&& (KeyDown!=null )) {
//将 lParam 数据转换成 KeyboardHookStruct 结构数据
KeyboardHookStruct MyKeyboardHookStruct =
(KeyboardHookStruct)Marshal.PtrToStructure(lParam,
typeof(KeyboardHookStruct));
if
(KeyDown != null && (wParam ==
WM_KEYDOWN || wParam ==
WM_SYSKEYDOWN)) {
Keys keyData =
(Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyDown(this, e);
}
}
return CallNextHookEx(hKeyboardHook, nCode, wParam,
lParam);
}
|
2
完成有关键盘监视部分的程序之后,接着我们只需要在FORM1窗体中添加一个计时器控件,设置其属性,然后双击该控件打开编程窗口,编写相关程序调用UserActivityHook以便每隔一定的时间对截获的键盘输入进行统计就可以实现程序功能了。
① 在原有的using基础下添加以下using语句。
using System.Runtime.InteropServices;
using System.Threading;
using System.Globalization;
using System.Diagnostics;
|
② 定义初始化相关的变量、过程、数组。
private UserActivityHook
hook;//定义键盘HOOK程序,具体代码见UserActivityHook.cs文件
private struct kk //定义一个结构,用于统计输入的键的次数
{
public int count;
public string button;
}
private kk[] keycount;
|
③
在Form1()中初始化keycount数组,用于存放五个常用游戏键的统计信息,可根据需要添加监控的键。如
keycount[0].button = "Up";
④
编写hook_KeyDown(),对截获的键进行判断,如果属于5个按键之一,则相应的击键次数加1,同时判断击键次数是否达到规定的数目,如果达到规定的数目,则调用stopprocess()关闭相应的进程。
⑤
编写stopprocess()来获取系统中当前正在运行的进程,并检查是否打开了IE窗口,是,则关闭IE进程,这部分程序可以根据需要修改,以关闭其他进程。主要代码是:
private void stopprocess(){
Process[]
procList = new Process[100];
procList =
Process.GetProcesses();
int j =
procList.Length;
for (int i =
0; i < j; i++) {
string strProcName = procList[i].ProcessName;
|
int iProcID = procList[i].Id;
if (procList[i].ProcessName == "IEXPLORE" ||
procList[i].ProcessName == "iexplore")
procList[i].Kill();
}
}
|
⑥ 最后,我们将对计时器控件timer1的tick事件进行编程,以便每隔一定的时间自动调用UserActivityHook,截获键盘输入的键并对截获的键进行统计。
private void timer1_Tick(object sender, EventArgs
e) {
for (int i =
0; i < 5; i++)
//对5个键的按键次数初始化为零
keycount[i].count = 0;
hook = new
UserActivityHook();
hook.KeyDown
+= new KeyEventHandler(hook_KeyDown);
}
|
到此为止,程序已编写完毕,调试以后,运行bin\debug目录下的可执行程序stopgame.exe,再运行IE,试着按四个方向键和空格键,是不是IE被关闭了?本程序只是简单地关闭IE,读者可以修改程序以关闭其他进程。