为了安装全局钩子,创建一个DLL工程。包含安装钩子,卸载钩子,钩子函数。
1.安装钩子
HHOOK SetWindowsHookEx( <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">int idHook,</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">//键盘钩子钩子类型为WH_KEYBOARD</span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> HOOKPROC </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">lpfn,</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">//钩子函数地址 </span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">HINSTANCE hMod,</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">//钩子函数所在DLL的实例句柄 </span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">DWORD dwThreadId</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">//指定线程,0表示系统范围内的安装钩子);</span>
如何获取钩子函数所在DLL的实例句柄?
VirtualQuery函数可以取得调用进程虚拟地址空间中指定内存页的状态
DWORD VirtualQuery(
LPCVOID lpAddress, //指向要查找的地址
PMEMORY_BASIC_INFORMATION lpBuffer,//返回该地址所在页面以及与它相邻具有相同属性的页面信息,返回信息到MEMORY_BASIC_INFORMATION结构
DWORD dwLength //结构的长度
);
typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
DWORD RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION;
typedef MEMORY_BASIC_INFORMATION *PMEMORY_BASIC_INFORMATION;
这里只需要用到AllocationBase,表示VirtualAlloc函数分配的基地址。由此我们得到了DLL的实例句柄。
综合上述方法,安装卸载钩子函数如下
BOOL WINAPI SetKeyHook(BOOL bInstall,DWORD dwThreadId,HWND hWndCaller)
{
BOOL m_ok;
g_hwnd=hWndCaller;
if (bInstall)
{
g_hhook=::SetWindowsHookEx(WH_KEYBOARD,HookProc,Modulefromaddress(HookProc),dwThreadId);
m_ok=(g_hhook!=NULL);
}
else
{
m_ok=::UnhookWindowsHookEx(g_hhook);
g_hhook=NULL;
}
return m_ok;
}
2.键盘钩子函数
LRESULT CALLBACK HookProc(int nCode,WPARAM wParam,LPARAM lParam)
下面重点介绍这个函数参数:
(1)nCode:查MSDN,这个参数用来决定如何处理得到的消息,<0时返回CallNextHookEx,把消息传递到下去。还有下面两个值
HC_ACTION:表示后面两个参数包含按下键值的消息
HC_NOREMOVE:表示后面两个参数包含键值消息,且消息没有移出消息队列。
(2)wParam:虚拟简直产生的消息
(3)lParam:值范围0~31。29=1:按下了ALT;30=1,信息发送前某键被按下,=0表示某键弹起
钩子函数具体代码入下:
LRESULT CALLBACK HookProc(int ncode,WPARAM wParam,LPARAM lParam)
{
if (ncode<0||ncode==HC_NOREMOVE)
{
return ::CallNextHookEx(g_hhook,ncode,wParam,lParam);
}
if (lParam&0x40000000) //0x40000000,第30位=1,键被按下时
{
return ::CallNextHookEx(g_hhook,ncode,wParam,lParam);
}
::PostMessage(g_hwnd,HM_KEY,wParam,lParam);
return ::CallNextHookEx(g_hhook,ncode,wParam,lParam);
}
其中HM_KEY为自定义消息,利用WM_USER,此处可定义在WM_USER~0X7FFF。
0 through WM_USER –1 | 用于系统消息 |
WM_USER through 0x7FFF | 用户自定义窗口类消息 |
WM_APP through 0xBFFF | 用户可用于应用程序的消息 |
0xC000 through 0xFFFF | 串消息 |
Greater than 0xFFFF | 为系统以后所用 |
发送消息用PostMessage函数,它将消息放入与创建窗口的线程相关联的消息队列后立即返回。当线程产生HM_KEY消息的时候,调用消息响应函数,将在应用程序部分说明。
3.这里需要用到共享数据段,因为DLL将被映射到不同进程的地址空间,每个进程空间中,钩子函数都要使用钩子句柄和主窗口句柄,为了调用CallNextHookEx以及向主窗口 发送消息。当钩子成功安装后,将DLL加载进其他进程的地址空间,但是在这些地址空间中钩子句柄和主窗口句柄没有正确设置,因为没有线程为他们赋值。
共享数据段中的数据在所有进程中共享一块内存。一个进程中设置了共享数据段的数据,其他进程中的同一数据段的数据也会改变。
#pragma data_seg("YCIShared")
HWND g_hwnd=NULL;
HHOOK g_hhook=NULL;
#pragma data_seg()
4.关于DLL导出函数
#define KEYHOOK_API extern "C" _declspec(dllexport)
在C++中,加入extern "C",否则会找不到重载函数。
不要写成下面的形式,会有KEYHOOK_API重定义的警告
.cpp
#define KEYHOOK_API
.h
#ifdef KEYHOOK_API
#define KEYHOOK_API extern "C" _declspec(dllexport)
#else
#define KEYHOOK_API extern "C" _declspec(dllimport)
#endif
5.在.def文件,定义导出函数和共享代码段。
因为在应用程序需要调用SetKeyHook,通过它安装卸载钩子,实现一系列功能!
EXPORTS
SetKeyHook
SECTIONS
YCIShared Read Write Shared
至此DLL工作就完成了
接下来编写应用程序,创建MFC应用程序,我们只需要编写上面提及的消息响应函数
1.定义函数
afx_msg long OnHookKey(WPARAM wParam, LPARAM lParam);
函数体为你想要实现的响应动作
2.绑定自定义消息和消息相应函数,代码写于 BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间
ON_MESSAGE(HM_KEY,OnHookKey)
3.
调用DLL导出的SetKeyHook函数,代码写到DLG类初始化函数中!
SetKeyHook(TRUE,0,m_hWnd);
参数 : TRUE:确认安装钩子
0:在系统范围内安装钩子
m_hWnd:是windows自定义的窗口句柄,最后将把消息Post到这个句柄所标志的窗口
这是很久之前学习的内容,可是没有深入理解不久就忘记了。看似简单的一个程序,实际动手编写也花费很多时间,我终于体会到各位大神们所说的,无论程序看起来多简单,都要自己动手实际,因为看上去简单,等到实现时会发现各种问题。
重新学习,不足之处,烦请指正。
具体代码查看《windows程序设计》第九章