hook提供一种机制,使得程序可以在消息的传递过程中加入自己的处理方法。由于hook增加了消息传递过程中的处理,所以会影响系统性能,应该仅在必须的时候使用hook
Windows提供多种不同的hook,每一种访问不同的消息处理,例如,程序可以使用WH_MOUSE来监视鼠标消息。对每种hook,Windows都提供了单独的hook chain。A hook chain is a list of pointers to special, application-defined callback functions called hook procedures。当消息产生时挂接到特定的钩子上,Windows将消息逐个传递给消息链上定义的钩子函数。钩子函数可以截取的消息取决于钩子的定义,有些钩子函数可以只能监视消息,有些则可以修改或取消消息。
为了处理hook,必须声明钩子函数并使用SetWindowsHookEx来安装到hook chain的最前端。
hook procedure的格式如下:
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
nCode的值取决于hook类型,每种hook都有不同的nCode值域。wParam和lParam取决于nCode
hook procedure可以通过CallNextHookEx将消息传递到下一个hook procedure。
hook procedure如果为global,则监视系统中所有线程的消息;如果特定于线程,则只监视特定线程的消息。全局钩子函数必须在DLL中实现;如果钩子函数监视自身创建的线程,则可以在程序内实现钩子函数;如果监视的是不同进程的消息,则必须在DLL中实现。
钩子类型:
==================================================
◎ WH_CALLWNDPROC and WH_CALLWNDPROCRET Hooks
==================================================
WH_CALLWNDPROC钩子监视SendMessage消息的传递,WM_CALLWNDPROCRET在消息被处理、函数返回后被调用(看名称多了一个ret),它们都只能监视消息而不能修改。WH_CALLWNDPROC需要用到的结构:
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
nCode:如果nCode为HC_ACTION则proc必须处理消息,如果小于0,则必须不作处理的传递给CallNextHookEx并且result:=CallNextHookEx
wParam:如果消息是当前进程发送的,则值为非零,否则为零。这两个参数都不知道效果,感觉第三个比较有用
lParam:指向CWPSTRUCT结构的指针
typedef struct tagCWPSTUCT{LPARAM lParam; WPARAM wParam; UINT message; HWND hwnd;} CWPSTRUCT
hwnd标识将要接收消息的窗体,其他的都是SendMessage的参数
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);
idHook:type of hook
lpfn:hook procedure,如果是监视其他进程的hook,hook proc必须在dll中实现
hMod:标识包含hook procedure的DLL。如果是程序内的hook procedure则必须为NULL
dwThreadId:标识hook procedure要处理的thread,如果为0,则处理所有线程。
返回值:如果成功返回hook procedure的handle,如果失败返回NULL
应用方法:点击按钮时改变窗体caption,当加载hook后,会弹出对话框显示修改后的标题
procedure TForm1.FormCreate(Sender: TObject);
begin
FMsg := RegisterWindowMessage('ANUSERMSG'); //注册自定义消息
Application.OnMessage := OnCustomMessage; //消息发生时进行处理
EnableCWPHook; //启用hook,函数在DLL中实现
end;
procedure TForm1.OnCustomMessage(var msg: TMsg; var handled: Boolean);
begin
if (msg.message = FMsg) and (not handled) then
begin
handled := True; //有什么作用?
ShowMessage(PChar(msg.lParam)); //自定义消息的参数在lParam中传递
end;
end;
按钮点击事件:SendMessage(Handle, WM_SETTEXT, 0, Integer(PChar('abc'))); //发送消息,hook proc中捕捉WM_SETTEXT消息并发送自定义消息
窗体释放的时候取消hook:DisableCWPHook;
DLL中的实现
procedure EnableCWPHook;
begin
user_msg := RegisterWindowMessage('ANUSERMSG'); //注册自定义消息,必须在DLL和EXE中都进行注册
hHookProc := SetWindowsHookEx(WH_CALLWNDPROC, CallWNDProc, HInstance, 0); //调用函数注册钩子
end;
function CallWNDProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var dw: DWORD;
begin
if PCWPSTRUCT(lParam).message = WM_SETTEXT then //lParam见上面的结构说明
begin
dw := BSM_ALLCOMPONENTS;
//广播消息,以便APP可以接收
BroadcastSystemMessage(BSF_POSTMESSAGE, @dw, user_msg, 0, PCWPSTRUCT(lParam).lParam);
end;
Result := 0; // the return value of CallWNDProc should always be zero.
end;
==================================================
◎WH_KEYBOARD Hook
==================================================
WH_KEYBOARD钩子可以监视键盘消息,当程序调用GetMessage或PeekMessage时,会得到WM_KEYDOWN和WM_KEYUP消息,此种hook截获这个消息供程序处理
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);
code:表示如何处理消息。HC_ACTION、HC_NOREMOVE,如果code<0,必须调用CallNextHookEx来处理并返回处理结果
wParam:键盘的virtual-key code
lParam:32位数值,详细含义可以查阅 Keystroke Message Flags
返回值:如果返回值为0,则消息在hook chain上传递,若返回值不为0,则消息不再继续传递(按键失效)
示例,监视按键(仅字母),在edit中显示按键
HKeyHook := SetWindowsHookEx(WH_KEYBOARD, @KeyBoardProc, 0, GetCurrentThreadId);
function KeyBoardProc(iCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; //注意调用方式
var c: char; iCaps: Integer; pc: PChar;
begin
Result := 0; //允许消息的继续传递
if iCode < 0 then //iCode<0必须不作处理并返回处理结果
begin
Result := CallNextHookEx(HKeyHook, iCode, wParam, lParam);
Exit;
end;
GetMem(pc, 20);
GetKeyNameText(lParam, pc, 20); //获得按键名称,如:Caps Lock, Shift, Right Alt
if wParam in [$41..$5A] then //只处理字母
begin
iCaps := (GetKeyState(VK_SHIFT) shr 31) xor (GetKeyState(VK_CAPITAL) and 1); //检查shift和caps状态确定大小写
c := Char(wParam + (1 - iCaps) * $20); //小写字母要加20h
Form1.edt2.Text := c + ' 0x' + IntToHex(Ord(c), 2) + ' lParam:0x' + IntToHex(lParam, 8);
end;
Form1.edt3.Text := pc;
FreeMem(pc);
end;
==================================================
◎WH_MOUSE Hook
==================================================
WM_MOUSE钩子可以用来监视鼠标消息
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
nCode:同KeyboardHookProc,nCode<0只能传递处理
wParam:鼠标消息的标识,鼠标单击、双击右击等
lParam:MOUSEHOOKSTRUCT结构的指针
返回值:0继续传递,1不传递
typedef stuct tagMOUSEHOOKSTRUCT{POINT pt; HWND hwnd; UINT wHitTestCode; DWORD dwExtraInfo;} MOUSEHOOKSTRUCT;
pt:光标位置
hwnd:将要接受鼠标消息的窗口handle
wHitTestCode:标识鼠标所在位置 HTxx常量
dwExtraInfo:额外信息
示例,光标移动时在窗体caption显示光标位置信息。当光标不在当前窗口时,是无法收到光标消息的,因此使用全局hook
function MouseHookProc(iCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var hookStruct: PMOUSEHOOKSTRUCT; sText: PChar; i: Integer;
begin
Result := 0;
if iCode < 0 then
begin
Result := CallNextHookEx(hHookProc, iCode, wParam, lParam);
Exit;
end;
hookStruct := PMOUSEHOOKSTRUCT(lParam);
i := (hookStruct.wHitTestCode); //具体值查询WM_NCHITTEST
sText := PChar(Format('X:%.4d Y:%.3d %d %d', [hookStruct.pt.X, hookStruct.pt.Y, i, wParam]));
SendMessage(hWindow, WM_SETTEXT, 0, Integer(sText));
end;
hWindow := FindWindow('TForm1', nil);
==================================================
◎WH_SHELL Hook
==================================================
shell程序使用WH_SHELL钩子获得重要的信息。windows在shell程序将要被激活或顶级窗口被创建和删除是调用WH_SHELL
LRESULT CALLBACK ShelProc( int nCode, WPARAM wParam, LPARAM lParam);
nCode:NT可用的只有以下三个
HSHELL_ACTIVATESHELLWINDOW:shell应当激活主窗体。wParam=0
HSHELL_WINDOWCREATED:顶级无主窗体被创建,当调用时窗体已存在。wParam为窗体的handle
HSHELL_DESTROYED:顶级无主窗体将要被销毁,调用时窗体仍存在。lParam为窗体handle
返回值:0
不知道何所谓shell程序,仅提供窗体实现当双击文件夹时修改窗体caption,显示计数
function shellProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Result := 0;
if nCode < 0 then
begin
Result := CallNextHookEx(hHookProc2, nCode, wParam, lParam);
Exit;
end;
if nCode = HSHELL_WINDOWCREATED then
begin
Inc(i); // i 为全局变量,计数用
SendMessage(hWindow, WM_SETTEXT, 0, Integer(PChar('create ' + IntToStr(i))));
//SendMessage(wParam, WM_CLOSE, 0, 0); 这句执行的话,那可就……^_^
end;
end;
==================================================
◎WH_CBT Hook
==================================================
当系统发生以下事件时,调用WH_CBT hook函数:before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the input focus; or before synchronizing with the system message queue. CBT hook主要应用于computer-based traing程序
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam);
nCode:见win32help CBTProc 一节
wParam, lParam:含义依赖于nCode
示例作一个比shell hook中更过分的,禁止所有窗口的创建
if nCode = HCBT_CREATEWND then Result := 1 //返回 1 表示不允许创建
else Result := 0;
程序中的调用也换换口味
procedure TForm1.FormCreate(Sender: TObject);
var
hInstDll: HINST;
hookProc: Pointer;
begin
hInstDll := LoadLibrary('CBTHook.dll');
hookProc := GetProcAddress(hInstDll, 'CBTHookProc');
hookProcResult := SetWindowsHookEx(WH_CBT, hookProc, 0, GetCurrentThreadId);
end;
==================================================
◎WH_DEBUG Hook
==================================================
==================================================
◎WH_FOREGROUNDIDLE Hook
==================================================
==================================================
◎WH_GETMESSAGE Hook
==================================================
==================================================
◎WH_JOURNALPLAYBACK Hook
==================================================
==================================================
◎WH_JOURNALRECORD Hook
==================================================
==================================================
◎WH_MSGFILTER and WH_SYSMSGFILTER Hooks