钩子是Windows中消息处理机制的一个要点,通过安装各种钩子,应用程序能够设置相应的子例程
来监视系统里的消息传递以及在这些消息到达目的地之前截获它们并根据用户要求做出相应处理。钩子
的种类很多其作用也不同,如键盘钩子可以截获键盘消息,鼠标钩子可以截获鼠标消息,外壳钩子可以
截获启动和关闭应用程序的消息,日志钩子可以监视和记录输入事件。钩子分为线程专用钩子和全局钩
子,线程专用钩子只监视指定的线程,要监视系统中的所有线程,必须用到全局钩子。对于全局钩子,
钩子函数必须包含在独立的动态链接库(DLL)中,这样才能被各种相关联的应用程序调用。
一、钩子函数简介
Windows提供API函数SetwindowsHookEx来建立一个Hook,通过这个函数可以将一个程序添加到Hook
链中监视Windows消息,函数语法为:
SetwindowsHookEx (idHook:Integer;lpfn:TFNHookProc;hmod: HINST;dwThreadId:DWORD)
其中参数idHook指定建立的监视函数类型。通过Windows MSDN帮助可以看到,SetwindowsHookEx函
数提供15种不同的消息监视类型,而WH_JOURNALRECORD和WH_JOURNALPLAYBACK是用来监视键盘和鼠标操
作的。参数lpfn指定消息函数,在相应的消息产生后,系统会调用该函数并将消息值传递给该函数供处
理。函数的一般形式为:
Hookproc (icode:Integer;wparam:WPARAM;lparam:LPARAM):LRESULT;stdcall;
其中icode为系统指示标记,wParam和lParam为附加参数,根据不同的消息监视类型而不同。只要
在程序中建立这样一个函数再通过SetwindowsHookEx函数将它加入到消息监视链中就可以处理消息了。
在不需要监视系统消息时可以调用UnHookWindowsHookEx来解除对消息的监视。WH_JOURNALRECORD和
WH_JOURNALPLAYBACK类型是两种相反的Hook类型,前者获得鼠标、键盘动作消息,后者回放鼠标、键盘
消息。
二、钩子程序的实例
在本程序中建立了两个消息函数,一个用于纪录鼠标、键盘的动作并保存,另一个用于将保存的动
作返给系统回放。在Delphi中建立一个工程,在Form1上添加3个按钮用于程序操作。另外再添加一个
Edit控件用于验证操作
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end ;
var
Form1: TForm1;
EventArr: array [ 0 .. 1000 ] of EVENTMSG;
EventLog: Integer;
PlayLog: Integer;
hHook, hPlay: Integer;
recOK: Integer;
canPlay: Integer;
bDelay: Bool;
implementation
{ $R *.DFM }
function PlayProc(iCode: Integer; wParam: wParam; lParam: lParam): LRESULT;
stdcall ;
begin
canPlay : = 1 ;
Result : = 0 ;
if iCode < 0 then // 必须将消息传递到钩子链的下一个处理程序
Result : = CallNextHookEx(hPlay, iCode, wParam, lParam)
else if iCode = HC_SYSMODALON then
canPlay : = 0
else if iCode = HC_SYSMODALOFF then
canPlay : = 1
else if ((canPlay = 1 ) and (iCode = HC_GETNEXT)) then
begin
if bDelay then
begin
bDelay : = False;
Result : = 50 ; // 控制回放的速度,数字越大,越慢
end ;
pEventMSG(lParam)^ : = EventArr[PlayLog];
end
else if ((canPlay = 1 ) and (iCode = HC_SKIP)) then
begin
bDelay : = True;
PlayLog : = PlayLog + 1 ;
end ;
if PlayLog >= EventLog then
begin
UNHookWindowsHookEx(hPlay);
end ;
end ;
function HookProc(iCode: Integer; wParam: wParam; lParam: lParam): LRESULT;
stdcall ;
begin
recOK : = 1 ; // 可以记录
Result : = 0 ;
if iCode < 0 then // 必须将消息传递到钩子链的下一个处理程序
Result : = CallNextHookEx(hHook, iCode, wParam, lParam)
else if iCode = HC_SYSMODALON then
recOK : = 0
else if iCode = HC_SYSMODALOFF then
recOK : = 1
else if ((recOK > 0 ) and (iCode = HC_ACTION)) then
begin
EventArr[EventLog] : = pEventMSG(lParam)^;
EventLog : = EventLog + 1 ;
if EventLog >= 1000 then
begin
UNHookWindowsHookEx(hHook);
end ;
end ;
end ;
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.Caption : = ' 纪录 ' ;
Button2.Caption : = ' 停止 ' ;
Button3.Caption : = ' 回放 ' ;
Button2.Enabled : = False;
Button3.Enabled : = False;
end ;
procedure TForm1.Button1Click(Sender: TObject);
begin
EventLog : = 0 ;
// 建立键盘、鼠标操作消息纪录链
hHook : = SetwindowsHookEx(WH_JOURNALRECORD, HookProc, HInstance, 0 );
Button2.Enabled : = True;
Button1.Enabled : = False;
end ;
procedure TForm1.Button2Click(Sender: TObject);
begin
UNHookWindowsHookEx(hHook);
hHook : = 0 ;
Button1.Enabled : = True;
Button2.Enabled : = False;
Button3.Enabled : = True;
end ;
procedure TForm1.Button3Click(Sender: TObject);
begin
PlayLog : = 0 ;
// 建立键盘鼠标操作消息纪录回放链
hPlay : = SetwindowsHookEx(WH_JOURNALPLAYBACK, PlayProc, HInstance, 0 );
Button3.Enabled : = False;
end ;
end .
运行程序,点击“纪录”按钮开始纪录操作,这时你可以在文本控件中输入一些文字,然后点击“
停止”按钮停止纪录,再点击“回放”按钮就可以将先前所做的操作回放。在上面的程序中,HookProc
是纪录操作的消息函数,每当有鼠标、键盘消息发生时,系统都会调用该函数,消息信息就保存在地址
lParam中,PlayProc是消息回放函数,当系统执行消息回放时调用该函数,程序就将先前纪录的消息值
返回到lParam指向的区域中,系统就会执行该消息,从而实现了消息回放。
三、小结
Hook是应用程序在Microsoft Windows 消息处理过程中设置的用来监控消息流并且处理系统中尚未
到达目的窗口的某一类型消息过程的机制。这可以帮助应用程序实现实现某些特殊目的,如控制键盘或
鼠标的输入消息,监视窗口的打开关闭以及实现记录宏等等,应用灵活,功能强大。如果Hook过程在应
用程序中实现,若应用程序不是当前窗口时,该Hook就不起作用;如果Hook在DLL中实现,程序在运行中
动态调用它,它能实时对系统进行监控.