下面就是DLL的源代码了:
首先是声明一些共公的数据结构的单元,这个单元在DLL中用,也在程序中用的:
unit wdSpyCommon;
{*******************************************
* brief: 消息Spy用到的数据结构等的声明文件
* autor: linzhenqun
* date: 2005-9-25
* email: linzhengqun@163.com
* blog: http://blog.csdn.net/linzhengqun
********************************************}
interface
uses
Windows, Messages;
resourcestring
err_ProcInvalid = 'the Message spy procedure are invalid.';
err_ShareMem = 'could not create share memory.';
const
Msg_Null_Value = 0;
Msg_Type_Sent = 1;
Msg_Type_Post = 2;
Msg_Type_Return = 3;
type
{ 受监察的消息结构 }
PMsgInfo = ^TMsgInfo;
TMsgInfo = packed record
hwnd: HWND;
message: UINT;
wParam: WPARAM;
lParam: LPARAM;
time: DWORD;
pt: TPoint;
lResult: LRESULT;
MsgType: UINT;
end;
implementation
end.
接着是消息Spy的DLL中的源代码:
unit wdMsgSpy;
{*******************************************
* brief: 消息Spy的SDK
* autor: linzhenqun
* date: 2005-9-24
* email: linzhengqun@163.com
* blog: http://blog.csdn.net/linzhengqun
********************************************}
interface
uses
Messages, Windows, Classes, SysUtils, wdSpyCommon;
const
MapingFile_Name = 'MsgSpy_FC 819A 73-2718-47E2-BF78-6810562CDA65';
type
//共享内存
PShareMem = ^TShareMem;
TShareMem = record
HRevWnd: THandle; //存放接收消息的窗口句柄
HWndSpy: THandle; //被监察的窗口句柄
end;
{ 开始监察消息,AHSpyWnd为受监察的窗口,AHRevWnd为接收监察到的消息的窗口 }
function StartSpyMessage(AHSpyWnd, AHRevWnd: THandle): Boolean; stdcall;
{ 停止监察消息 }
procedure StopSpyMessage; stdcall;
var
HMsgProc, HWndProc, HWndRetProc: THandle; //相应钩子的句柄
PSMem: PShareMem; //共享内存块
hMapFile: THandle; //内存映射文件的句柄。
implementation
//将截获的消息结构发送到目标窗口
procedure SendData(AMsgInfo: PMsgInfo); stdcall;
var
pcds: PCopyDataStruct;
begin
New(pcds);
pcds^.cbData := SizeOf(TMsgInfo);
pcds^.lpData := AMsgInfo;
SendMessage(PSMem^.HRevWnd, WM_COPYDATA, 0, LongInt(pcds));
Dispose(pcds);
end;
{ WH_GETMESSAGE的钩子过程 }
function GetMsgProc(code: Integer; wP: WPARAM; lP: LPARAM): LRESULT; stdcall;
var
LMsgInfo: PMsgInfo;
begin
{只有截获的消息的窗口句柄等于被监察的窗口句柄,才进行下一步,
接着,当被监察的窗口不是接收消息的窗口时执行下一步操作;或者当被监察窗口
就是接收消息的窗口时,截获的消息不是WM_CopyData,执行下一步。
这么做是为了避免进入发送消息与截获消息的死循环}
if code = HC_ACTION then
if (PMsg(lp)^.hwnd = PSMem^.HWndSpy) then
if ((PSMem^.HWndSpy = PSMem^.HRevWnd) and (PMsg(lp)^.message <> WM_COPYDATA))
or (PSMem^.HWndSpy <> PSMem^.HRevWnd) then
begin
New(LMsgInfo);
LMsgInfo.hwnd := PMsg(lp)^.hwnd;
LMsgInfo.message := PMsg(lp)^.message;
LMsgInfo.wParam := PMsg(lp)^.wParam;
LMsgInfo.lParam := PMsg(lp)^.lParam;
LMsgInfo.pt := PMsg(lp)^.pt;
LMsgInfo.time := PMsg(lp)^.time;
LMsgInfo.lResult := Msg_Null_Value;
LMsgInfo.MsgType := Msg_Type_Post;
SendData(LMsgInfo);
Dispose(LMsgInfo);
end;
Result := CallNextHookEx(HMsgProc, code, wP, lP);
end;
{ WH_CALLWNDPROC的钩子过程 }
function CallWndProc(code: Integer; wP: WPARAM; lP: LPARAM): LRESULT; stdcall;
var
LMsgInfo: PMsgInfo;
begin
if code = HC_ACTION then
if (PCWPStruct(lp)^.hwnd = PSMem^.HWndSpy) then
if ((PSMem^.HWndSpy = PSMem^.HRevWnd) and (PCWPStruct(lp)^.message <> WM_COPYDATA))
or (PSMem^.HWndSpy <> PSMem^.HRevWnd) then
begin
New(LMsgInfo);
LMsgInfo^.hwnd := PCWPStruct(lp)^.hwnd;
LMsgInfo^.message := PCWPStruct(lp)^.message;
LMsgInfo^.wParam := PCWPStruct(lp)^.wParam;
LMsgInfo^.lParam := PCWPStruct(lp)^.lParam;
LMsgInfo^.pt := Point(0, 0);
LMsgInfo^.time := Msg_Null_Value;
LMsgInfo^.lResult := Msg_Null_Value;
LMsgInfo^.MsgType := Msg_Type_Sent;
SendData(LMsgInfo);
Dispose(LMsgInfo);
end;
Result := CallNextHookEx(HWndProc, code, wP, lP);
end;
{ WH_CALLWNDPROCRET的钩子过程 }
function CallWndRetProc(code: Integer; wP: WPARAM; lP: LPARAM): LRESULT; stdcall;
var
LMsgInfo: PMsgInfo;
begin
if code = HC_ACTION then
if (PCWPRetStruct(lp)^.hwnd = PSMem^.HWndSpy) then
if ((PSMem^.HWndSpy = PSMem^.HRevWnd) and (PCWPRetStruct(lp)^.message <> WM_COPYDATA))
or (PSMem^.HWndSpy <> PSMem^.HRevWnd) then
begin
New(LMsgInfo);
LMsgInfo^.hwnd := PCWPRetStruct(lp)^.hwnd;
LMsgInfo^.message := PCWPRetStruct(lp)^.message;
LMsgInfo^.wParam := PCWPRetStruct(lp)^.wParam;
LMsgInfo^.lParam := PCWPRetStruct(lp)^.lParam;
LMsgInfo^.pt := Point(0, 0);
LMsgInfo^.time := Msg_Null_Value;
LMsgInfo^.lResult := PCWPRetStruct(lp)^.lResult;
LMsgInfo^.MsgType := Msg_Type_Return;
SendData(LMsgInfo);
Dispose(LMsgInfo);
end;
Result := CallNextHookEx(HWndRetProc, code, wP, lP);
end;
function StartSpyMessage(AHSpyWnd, AHRevWnd: THandle): Boolean;
begin
Result := False;
try
if (HMsgProc <> 0) or (HWndProc <> 0) or (HWndRetProc <> 0) then
Exit;
PSMem^.HWndSpy := AHSpyWnd;
PSMem^.HRevWnd := AHRevWnd;
HMsgProc := SetWindowsHookEx(WH_GETMESSAGE, @GetMsgProc, HInstance, 0);
HWndProc := SetWindowsHookEx(WH_CALLWNDPROC, @CallWndProc, HInstance, 0);
HWndRetProc := SetWindowsHookEx(WH_CALLWNDPROCRET, @CallWndRetProc, HInstance, 0);
if (HMsgProc = 0) or (HWndProc = 0) or (HWndRetProc = 0) then
begin
StopSpyMessage;
Exit;
end;
except
Exception.Create(err_ProcInvalid);
end;
Result := True;
end;
procedure StopSpyMessage;
begin
UnhookWindowsHookEx(HMsgProc);
UnhookWindowsHookEx(HWndProc);
UnhookWindowsHookEx(HWndRetProc);
HMsgProc := 0;
HWndProc := 0;
HWndRetProc := 0;
end;
initialization
//创建共享内存块
hMapFile := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, MapingFile_Name);
if hMapFile = 0 then
hMapFile := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
SizeOf(TShareMem), MapingFile_Name);
PSMem := MapViewOfFile(hMapFile, FILE_MAP_WRITE or FILE_MAP_READ, 0, 0, 0);
if PSMem = nil then
begin
CloseHandle(hMapFile);
Exception.Create(err_ShareMem);
end;
finalization
//释放共享内存块
UnMapViewOfFile(PSMem);
CloseHandle(hMapFile);
end.
解决了上面的技术问题,再来看源代码是很容易理解的,就钩子的安装和解除本身很简单,难的是进程间的数据共享和通信问题。但这些现在已经不是问题了,不是吗。
DLL的导出函数当然就是:
exports
StartSpyMessage,
StopSpyMessage;
然后在显示消息的窗口截获WM_COPYDATA,处理显示的问题就行了。不过WM_COPYDATA其他窗口也可以发送的,所以处理WM_COPYDATA的过程一定要作一下异常处理才行。
好了,要完整的程序代码,请发邮件给我吧:linzhengqun@163.com
后记
这真是一篇很长的文章,写这篇文章的过程也是自我提高的过程,学到非常多的东西。在这里推荐几本书,能够更系统的学到这些知识:
首先当然是《Windows高级编程第四版》,没有另一本书能够讲得比这本更精彩深入了。研究底层的读者必读之书。
其次是《Delphi5开发人员指南》,其中有关于日志钩子的应用以及内存映射文件的用法,可以一看。
还有一些网上的文章,也值得借鉴,但我总觉得讲不到重点去,比如全局钩子,希望这一篇能够让读者更好的运用全局钩子。
我在努力,我在进步,希望你们也是。Happy programme!