标题：系统钩子
什么是钩子
其实Windows系统是建立在事件驱动的机制上的，说穿了就是整个系统都是通过消息的传递来实现的。而钩子是Windows系统中非常重要的系统接口，用它可以截获并处理送给 其他应用程序的消息，来完成普通应用程序难以实现的功能。
钩子可以监视系统或进程中的各种事件消息，截获发往目标窗口的消息并进行处理。这样，我们就可以在系统中安装自定义的钩子，监视系统中特定事件的发生，完成特定的功能，比如截获键盘、鼠标的输入，屏幕取词，日志监视等等。
可见，利用钩子可以实现许多特殊而有用的功能。因此，对于高级编程人员来说，掌握钩子的编程方法是很有必要的。 
钩子的种类很多，每种钩子可以截获并处理相应的消息，如键盘钩子可以截获键盘消息，外壳钩子可以截取、启动和关闭应用程序的消息等。
如图所示是一全局钩子示意图
在实例程序中运用WH_GETMESSAGE钩子，这个钩子监视投递到消息队列中的Windows消息。
  全局钩子的原理图
钩子可以分为线程钩子和系统钩子, 线程钩子监视指定线程的事件消息, 系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序，所以钩子函数必须放在独立的动态链接库(DLL) 中。实现钩子机制的几个关键技术1．      windows的钩子程序，需要用到几个sdk中的api函数。下面列出这几个函数的原型及说明：
hhook setwindowshookex(int idhook,hook_proc lpfn,hinstance hmod,dword dwthreadid);
参数说明如下：
idhook：钩子的类型
lpfn：钩子处理函数地址
hmod：包含钩子函数的模块句柄
dwthreadid：钩子的监控线程
函数说明：函数将在系统中挂上一个由idhook指定类型的钩子，监控并处理相应的特定消息。
bool unhookwindowshookex(hhook hhk);
函数说明：函数将撤销由hhk指定的钩子。
lresult callnexthookex( hhook hhk, int ncode,wparam wparam,lparam lparam );
函数说明：函数将消息向下传递，下一个钩子处理将截获这一消息。
2．      由于钩子的处理涉及到模块及进程间的数据地址问题，一般情况是把钩子整合到一个动态链接库（dll）中，VC中有三种形式的MFC DLL可供选择，即Regular statically linked to MFC DLL（标准静态链接MFC DLL）、Regular using the shared MFC DLL（标准动态链接MFC DLL）以及Extension MFC DLL（扩展MFC DLL）。第一种DLL在编译时把使用的MFC代码链接到DLL中，执行程序时不需要其他MFC动态链接类库的支持，但体积较大；第二种DLL在运行时动态链接到MFC类库，因而体积较小，但却依赖于MFC动态链接类库的支持；这两种DLL均可被MFC程序和Win32程序使用。第三种DLL的也是动态连接，但做为MFC类库的扩展，只能被MFC程序使用。
另外,要设立一个全局数据共享数据段，以存贮一些全局变量，保留上次钩子消息事件发生时的状态。
3．      Win32 DLL的入口和出口函数都是DLLMain。只要有进程或线程载入和卸载DLL时，都会调用该函数，其原型是：
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved); 其中，第一个参数表示DLL的实例句柄；第三个参数系统保留；第二个参数指明了当前调用该动态连接库的状态，它有四个可能的值：DLL_PROCESS_ATTACH（进程载入）、DLL_THREAD_ATTACH（线程载入）、DLL_THREAD_DETACH（线程卸载）、DLL_PROCESS_DETACH（进程卸载）。在DLLMain函数中可以通过对传递进来的这个参数的值进行判别，根据不同的参数值对DLL进行必要的初始化或清理工作。由于在Win32环境下，所有进程的空间都是相互独立的，这减少了应用程序间的相互影响，但大大增加了编程的难度。当进程在动态加载DLL时，系统自动把DLL地址映射到该进程的私有空间，而且也复制该DLL的全局数据的一份拷贝到该进程空间，每个进程所拥有的相同的DLL的全局数据其值却并不一定是相同的。当DLL内存被映射到进程空间中，每个进程都有自己的全局内存拷贝，加载DLL的每一个新的进程都重新初始化这一内存区域，也就是说进程不能再共享DLL。因此，在Win32环境下要想在多个进程中共享数据，就必须进行必要的设置。一种方法便是把这些需要共享的数据单独分离出来，放置在一个独立的数据段里，并把该段的属性设置为共享，建立一个内存共享的DLL。用钩子机制实现截获鼠标左右键按压次数建立钩子程序时需要把钩子处理整合到动态链接库中，所以例程中需要建立两个project。
1．      钩子处理动态链接库
（1）      选择mfc appwizard(dll)创建一个新project，命名为“spy”。
（2）      选择mfc extension dll类型。
（3）      创建一个新的头文件，命名为“hook.h”，修改它的代码如下：
extern "C" LRESULT CALLBACK mouseproc(int code,WPARAM wparam,LPARAM lparam); //钩子处理函数
extern "C" bool WINAPI starthook(); //启动钩子函数
extern "C" bool WINAPI stophook(); //撤销钩子函数
extern "C" int WINAPI getresultl(); //取得鼠标左键单击次数的函数
extern "C" int WINAPI getresultr(); //取得鼠标右键单击次数的函数
（4）      修改spy.cpp程序代码如下:
#include "hook.h"    //包含头文件hook
#pragma data_seg("publicdata") //定义全局数据段
HHOOK hhook=NULL; //钩子句柄
HINSTANCE pinstance=NULL;//钩子模块句柄
UINT mouseclickl=0; //记录鼠标左键单击次数的变量
UINT mouseclickr=0;//记录鼠标右键单击次数
#pragma data_seg()
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{ if (dwReason == DLL_PROCESS_ATTACH)
{……　　//省略部分机器生成代码
new CDynLinkLibrary(SpyDLL);
pinstance=hInstance;//取得模块句柄
}
……; 　　
}
extern "C" LRESULT CALLBACK mouseproc(int code,      WPARAM wparam,LPARAM lparam)//钩子处理函
{
if (code<0)    //若code〈0，则直接调用callnexthookex返回
return CallNextHookEx(hhook, code, wparam, lparam);
if(wparam==WM_LBUTTONDOWN)
{      mouseclickl++;//记录鼠标左键单击次数      }
if(wparam==WM_RBUTTONDOWN)
{            mouseclickr++;//记录鼠标右键单击次数      }
return CallNextHookEx(hhook, code, wparam,lparam);
}
extern "C" bool WINAPI starthook()//启动钩子函数
{
hhook=SetWindowsHookEx(WH_MOUSE,mouseproc,pinstance,0);//挂上钩子
if(hhook!=NULL)
return true;
else return false;
}
extern "C" bool WINAPI stophook() //撤销钩子函数
{      return UnhookWindowsHookEx(hhook); //撤销钩子}
extern "C" int WINAPI getresultl()//返回鼠标左键单击次数
{      return mouseclickl;}
extern "C" int WINAPI getresultr()//返回鼠标右键单击次数
{      return mouseclickr;}
（5）      修改spy.def程序代码如下:
exports
stophook @2
starthook @1
getresultl @3
getresultr @4
(6)   编译project，生成spy.dll文件和spy.lib文件。在windows系统中用vc++实现钩子机制2．      建立使用钩子的应用程序
（1）      生成一个单文档的可执行文件（exe）的project。
（2）      修改资源中的主选单，增加一个选单项“监控”，下有三个子选单项，分别为“启动”、“撤销”和“取出”。
（3）      在project中加入spy.lib文件。
（4）      分别修改“启动”、“撤销”和“取出”选单项的command响应函数如下：
#include "E:\DevStudio\MyProjects\spy\hook.h"  //路径可不同
void CMainFrame::OnMenuitem32771()  //“启动”选单项的响应函数
{      starthook(); }
void CMainFrame::OnMenuitem32772()  //“撤销”选单项的响应函数
{       stophook();}
void CMainFrame::OnMenuitem32773()  //“取出”选单项的响应函数
{      int resultl=getresultl();
int resultr=getresultr();
char buffer[80];
wsprintf(buffer,"在程序运行期间，你共单击鼠标左键%d次，右键%d次!",resultl,resultr);
::MessageBox(this->m_hWnd,buffer,"message",MB_OK); 
}
编译这个project，并把spy.dll放到生成的可执行文件目录下，便可运行程序。运行时，选择“监控”选单中的“启动”选单项，钩子便开始工作，监视鼠标的活动情况；选择“撤销”选单项，系统便撤销钩子；选择“取出”选单项，程序便报告在监控期间，用户分别单击鼠标左键和右键的次数。