首先要实现DLL:
在dll内实现钩子函数这是毫无疑问的。而安装钩子和卸载钩子的函数既可以写在主程序内,也可以写在DLL内。写在主程序内时只可以在主程序内安装钩子。而在dll内实现则可以让所有载入该dll的程序安装钩子。如当某进程将该DLL载入的时候,可以在DllMain中创建一个线程,让他调用安装钩子的函数,实现为此进程内的线程安装钩子的目的。为了拓展程序的功能,实现代码重用,最好是将钩子函数写在DLL内。另外这也可以实现模块化。一旦需求发生更改可以只修改DLL内的代码,而不需要改变主程序。
当钩子函数被调用的时候,也就是我们被拦截的消息已被触发,如何让主程序得到这个通知呢 ?
我们可以在其他进程内的钩子函数内给主程序的窗口发送消息。但如何发送呢?
PostMessage可以实现这个功能。
看原型:
BOOL WINAPI PostMessage(HWND hWnd,UINT Msg,WPARAM wparam,LPARAM lParam); |
hWnd即为要接受消息的窗口句柄。
Msg为要发送的消息。
wParam和lParam为消息的附加参数。
虽然可以使用PostMessage实现向主程序的窗口发送消息,但是我们如何获得主程序的窗口句柄呢?我们知道钩子函数是在DLL内实现的,而DLL会被加载到各个进程内。在其他进程要想得到主程序的窗口句柄这是一个问题。
在《windows核心编程系列》谈谈内存映射文件中,我们谈到了在可执行文件内使用共享段,可以实现同一个可执行文件的多个实例共享共享段内的数据的目的。那么在DLL使用共享段呢?哈哈,或许你已经猜出来了,由于DLL被映射到了各个进程,将数据放在DLL的共享段,可以实现在各个进程内共享DLL内共享段数据的目的。
我们的解决方法就是:在DLL内建立共享段,将主程序的窗口句柄放在共享段中。在主程序调用安装钩子的函数时可以将共享段内的窗口句柄赋为主程序的窗口句柄。从而达到在各个进程内共享数据的目的。到此,我们又学习一种在进程间共享数据的方法,另一种方法是利用内存映射文件。
<SPAN style="FONT-SIZE: 18px"> #pragma data_seg("shared") #pragma data_seg() #pragma comment(linker,"/SECTION:shared,RWS") </SPAN> |
怎么多了个hHook,hHook是创建的钩子的句柄。由于在钩子函数中会调用CallNextHookEx将消息传给钩子链的下一结点。二者都是在其他进程调用的,因此我们也必须把钩子的句柄设为共享。
DLL内创建钩子的代码:
<SPAN style="FONT-SIZE: 18px"> KEYHOOKDLL_API bool SetHook(</SPAN> |
<SPAN style="FONT-SIZE: 18px"> bool IsInstall,//true表示安装钩子,false表示卸载钩子。</SPAN> |
<SPAN style="FONT-SIZE: 18px"> HWND hWnd, //主程序窗口句柄,用于在主程序内传入设置。</SPAN> |
<SPAN style="FONT-SIZE: 18px"> int ThreadId)//要安装钩子的线程。 { ::hWnd=hWnd;//将当前窗口句柄赋给DLL共线段内的窗口句柄。 if(IsInstall) { hHook=SetWindowsHookEx( WH_KEYBOARD,KeyHookProc,GetModuleHandle </SPAN> |
<SPAN style="FONT-SIZE: 18px"> ("keyhookdll"),ThreadId); } } }</SPAN> |
创建的钩子类型为WH_KEYBOARD,他可以拦截WM_KEYDOWN 和WM_KEYUP 消息。
创建钩子函数功能很简单,仅仅安装钩子和设置共享段内的数据。Thread为要安装钩子的线程。主程序在调用时传入0,表示为所有线程安装钩子。