第一部分:API函数简介
1. SetWindowsHookEx函数
函数原型
HHOOK SetWindowsHookEx(
int idHook, // hook type
HOOKPROC lpfn, // hook procedure
HINSTANCE hMod, // handle to application instance
DWORD dwThreadId // thread identifier
);
函数功能:该函数将一个应用程序定义的挂钩处理过程安装到挂钩链中去,您可以通过安装挂钩处理过程来对系统的某些类型事件进行监控,这些事件与某个特定的线程或系统中的所有事件相关.具体参数详见MSDN;
举例:
线程钩子:
HHOOK g_hMouse;//全局变量,保存钩子的句柄
LRESULT CALLBACK MouseProc(
int nCode, // hook code
WPARAM wParam, // message identifier
LPARAM lParam // mouse coordinates
)
{
//……
}
g_hMouse=SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());
2. CallNextHookEx函数
函数原型:
LRESULT CallNextHookEx(
HHOOK hhk, // handle to current hook
int nCode, // hook code passed to hook procedure
WPARAM wParam, // value passed to hook procedure
LPARAM lParam // value passed to hook procedure
);
函数功能:调用下一个钩子
3. UnhookWindowsHookEx
函数原型:
BOOL UnhookWindowsHookEx(
HHOOK hhk // handle to hook procedure
);
第二部分:HOOK与DLL
1.线程钩子的创建过程:
1. 声明一个全局的HHOOK类型的变量,用于保存创建钩子的句柄,如:
HHOOK g_hMouse;//全局变量,保存钩子的句柄
2. 在应用程序的初始化函数中,安装钩子;注意SetWindowsHookEx的参数!
g_hMouse=SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());
3. 编写处理钩子消息的回调函数:
LRESULT CALLBACK MouseProc(
int nCode, // hook code
WPARAM wParam, // message identifier
LPARAM lParam // mouse coordinates
)
{
//编写需要处理的部分
return 1;
//不需要处理的部分,调用
return CallNextHookEx(g_hMouse, nCode, wParam, lParam);
}
4. 卸载钩子函数
UnhookWindowsHookEx(g_hMouse);
2.系统钩子的创建过程:
运行机制:DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。#pragma data_seg预处理指令用于设置共享数据段。例如:
#pragma data_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma data_seg()
在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到和共享。再加上一条指令
#pragma comment(linker,"/section:.SharedDataName,rws")
那么这个数据节中的数据可以在所有DLL的实例之间共享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。
系统钩子的创建需要将钩子的放在DLL中,因此其重建构成需要两部分:1.编写程序,生成Dll;2.编写应用程序调用Dll中的函数
2.1 DLL中的代码的编写
方案 1:源代码
1. 新建一个扩展MFC类型DLL工程;
2. 将DLL的句柄参数和要安装钩子的句柄声明为共享数据:
#pragma data_seg("Titlename") //名称任意起
HHOOK glhHook=NULL; //安装的鼠标钩子句柄
HINSTANCE glhInstance=NULL;//DLL实例句柄
#pragma data_seg()
注意:共享数据必须初始化,否则微软编译器会把没有初始化的数据放到.BSS段中,从而导致多个进程之间的共享行为失败。
3. 新建一个CMouseHook的导出类,用于实现钩子的安装和卸载
class AFX_EXT_CLASS CMouseHook:public CObject //AFX_EXT_CLASS宏声明类为导出类
{
public:
CMouseHook();//钩子类的构造函数
~CMouseHook();//钩子类的析构函数
BOOL StartHook(HWND hWnd);//安装钩子函数
BOOL StopHook();//卸载钩子函数
};
注意:AFX_EXT_CLASS用于声明该类为导出类;
4. 编写安装钩子,卸载钩子,钩子过程的函数
安装钩子:glhHook=SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0);
卸载钩子:UnhookWindowsHookEx(glhHook);
钩子过程:
//鼠标钩子函数的实现
LRESULT CALLBACK MouseProc(int nCode,WPARAM wparam,LPARAM lparam)
{
LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lparam;
if (nCode>=0)
{
//处理过程的代码…..
}
return CallNextHookEx(glhHook,nCode,wparam,lparam); //继续传递消息
}
2.2 主程序的调用
新建一个应用程序,将上述生成的.dll文件和.lib文件及类声明的文件.h拷贝到当前目录中,并在头文件中包含生成的类声明.h文件;静态方式加载动态链接库
#pragma comment(lib,"MousehookDll.lib") //隐式链接DLL
然后就可以声明一个由DLL文件导出的类CMouseHook的一个变量;然后在应用程序的初始化函数中调用该变量的StartHook函数安装钩子;在需要取消钩子的地方,调用StopHook()函数;
附录:钩子过程函数参数详解
从上面的步骤中可以看到,钩子的安装和卸载是一个较为固定格式和步骤;真正关键的是:不同的钩子类型,回调函数的参数意义不同,那我们就一一介绍各个参数的意义吧!
a. MouseProc(int nCode,WPARAM wparam,LPARAM lparam)WH_MOUSE
1.nCode 跟所有其他钩子处理函数一样,只要记得当 nCode小于0时:调用CallNextHookEx()就可以了。
HC_ACTION 当nCode等于HC_ACTION时,wParam和lParam 包含鼠标信息 HC_NOREMOVE 当nCode等于HC_NOREMOVE时,wParam和lParam 包含鼠标信息,并且鼠标消息没有从消息队列里移除
2. wParam 指定鼠标消息ID
3. lParam 一个MOUSEHOOKSTRUCT 结构的指针
typedef struct tagMOUSEHOOKSTRUCT {
POINT pt; //保存鼠标在屏幕上的x,y坐标
HWND hwnd; //接收到鼠标消息的窗口的句柄
UINT wHitTestCode; //详细描述参见WM_NCHITTEST消息
ULONG_PTR dwExtraInfo; //指定与本消息联系的额外消息
} MOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT;
b. KeyboardProc(int nCode,WPARAM wparam,LPARAM lparam) WH_KEYBOARD
2.wParam:按键的虚拟键值消息,例如:VK_F1 VK_F2 等;
3. lParam:32位内存,内容描述包括:指定扩展键值,扫描码,上下文,重复次数。
0-15位:描述:按下键盘次数。
16-23位:指定扫描码. 依赖于OEM
24位:当24位为1时候:表示按键是扩展键;当24位为0时候:表示按键是数字键盘按键
25-28位:保留位
29位:上下文键:为1时: ALT按下,其他情况为0
30位:如果是按键按下后发送的消息,30位为0,如果是按键抬起后30位为1;
31位:指定转变状态;31位为0时候,按键正在被按下,为1时候,按键正在被释放
c. GetMsgProc(int nCode,WPARAM wparam,LPARAM lparam) WH_GETMESSAGE
2.wParam:标明消息是否从消息对列中取出,它有如下两个值:
PM_NOREMOVE:未从消息队列中取出
PM_REMOVE:已经从消息队列中取出
3. lParam: 它是指向MSG结构体的一个指针
typedef struct tagMSG {
HWND hwnd; //接收消息的窗口句柄
UINT message; //消息的标识,如WM_CLOSE等
WPARAM wParam;// 指定消息的附加信息
LPARAM lParam; // 不同的消息不一样;
DWORD time; //消息投递到消息队列中的时间
POINT pt; //消息投递到消息队列中时的鼠标位置(屏幕坐标)
} MSG, *PMSG;
d. 未完待续…