1安装钩子:
HHOOK SetWindowsHookEx ( int idHook, // 钩子的类型,即它处理的消息类型
HOOKPROC lpfn, // 钩子子程的地址指针。如果dwThreadId参数为0 或是一个由别的进程创建的线程的标识,lpfn必须指向DLL中的钩子子程。除此以外,lpfn可以指向当前进程的一段钩子子程代码。钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。
HINSTANCE hMod, //应用程序实例的句柄。标识包含lpfn所指的子程的DLL,
如果dwThreadId 标识当前进程创建的一个线程,而且子程代码位于当前进程,
hMod必须为NULL。可以很简单的设定其为本应用程序的实例句柄。
DWORD dwThreadId //与安装的钩子子程相关联的线程的标识符。如果为0,
钩子子程与所有的线程关联,即为全局钩子。);
函数成功则返回钩子子程的句柄,失败返回NULL。
LRESULT CallNextHookEx (
HHOOK hhk; //当前钩子的句柄,由SetWindowsHookEx()函数返回。
int nCode; //传给钩子过程的事件代码。
WPARAM wParam; // wParam和lParam 分别是传给钩子子程的wParam值,
LPARAM lParam; //其具体含义与钩子类型有关。);
2卸载钩子:
BOOL UnHookWindowsHookEx ( HHOOK hhk );函数成功返回TRUE,否则返回FALSE。
几点说明:
(1)如果对于同一事件(如鼠标消息)既安装了线程勾子又安装了系统勾子,那么系统会自动先调用线程勾子,然后调用系统勾子。
(2)对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。
(3)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。
例1:与当前进程当前线程关联的键盘钩子
全局变量:
HHOOK g_hKeyboard = NULL;
LRESULT CALLBACK KeyboardProc (
int Code; WPARAM wParam;LPARAM lParam)
{ If(VK_SPACE == wParam) //如果是空格键则截取
Return 1;
If(VK_F2 == wParam) //如果是F2则结束程序
{ ::SendMessage(w_hWnd,WM_CLOSE,0,0);
UnHookWindowsHookEx(g_hKeyboard); }
Else //否则传给下一个钩子过程
Return CallNextHookEx(g_hKeyboard,code,wParam,lParam);
}
初始化函数OnInitDialog中:g_hKeyboard = SetWindowsHookEx
(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());
例2:与所有的线程关联的鼠标钩子和键盘钩子
(1)先写一个动态链接库
建立一个Win32 Dynamic-Liink Library工程(An empty DLL project),名称为Hook;新建C++ Source File文件 名称为Hook,添加如下代码:
#include <windows.h>
HHook g_hMouse = NULL;
HHook g_hKeyboard = NULL;
HWND g_hWnd;
LRESULT CALLBACK MouseProc (int Code; WPARAM wParam;LPARAM lParam)
{ return 1; }
LRESULT CALLBACK KeyboardProc
(int Code; WPARAM wParam;LPARAM lParam)
{ If(VK_F2 == wParam) //如果是F2则结束程序
{ SendMessage(g_hWnd,WM_CLOSE,0,0);
UnHookWindowsHookEx(g_hKeyboard);
UnHookWindowsHookEx(g_hMouse); }
return 1;
}
void SetHook(HWND hwnd)
{
g_hWnd = hwnd;
// GetModuleHandle获取指定模块的模块句柄
g_hMouse = SetWindowsHookEx
(WM_MOUSE, MouseProc, GetModuleHandle(“Hook”), 0);
g_hKeyboard = SetWindowsHookEx
(WH_KEYBOARD,KeyboardProc,,GetModuleHandle(“Hook”), 0);
}
在工程目录下接下来新建一个文本文件,重命名为“Hook.def”,将其增加到工程中,添加如下代码:
LIBRARY Hook
EXPORTS
SetHook @2 //@2函数序号
编译工程,将在Debug目录中生成dll和lib文件。
(2)将dll和lib文件拷贝到新的工程目录中,并在新工程中连接lib文件。
在新工程中的OnInitDialog中调用SetHook(m_hWnd);
OnInitDialog函数上面加上_declspec(dllimport) void SetHook(HWND hwnd);
例3:让程序窗口始终显示在最前面
1动态链接库中
Hook.cpp文件:
#include <windows.h>
HHook g_hMouse = NULL;
HHook g_hKeyboard = NULL;
#pragma data_seg("SharedDataName") // SharedDataName为新创建的节的名字
HWND g_hWnd = NULL; //将变量g_hWnd放到新创建的字节中(必须被初始化)
#pragma data_seg()
#pragma comment(linker,"/section:.SharedDataName,RWS"), // RWS为读写共享
LRESULT CALLBACK MouseProc (int Code; WPARAM wParam;LPARAM lParam)
{ return 1; }
LRESULT CALLBACK KeyboardProc
(int Code; WPARAM wParam;LPARAM lParam)
{ If(VK_F2 == wParam) //如果是F2则结束程序
{ SendMessage(g_hWnd,WM_CLOSE,0,0);
UnHookWindowsHookEx(g_hKeyboard);
UnHookWindowsHookEx(g_hMouse); }
return 1;
}
void SetHook(HWND hwnd)
{
g_hWnd = hwnd;
// GetModuleHandle获取指定模块的模块句柄
g_hMouse = SetWindowsHookEx
(WM_MOUSE, MouseProc, GetModuleHandle(“Hook”), 0);
g_hKeyboard = SetWindowsHookEx
(WH_KEYBOARD,KeyboardProc,,GetModuleHandle(“Hook”), 0);
}
Hook.def文件内容:
LIBRARY Hook
EXPORTS
SetHook @2
SEGMENTS
SharedDataName READ WRITE SHARED //将SharedDataName设为读写共享
2在OnInitDialog中:
int cxScreen, cyScreen;
cxScreen = GetSystemMetrics(SM_CXSCREEN);
cyScreen = GetSystemMetrics(SM_CYSCREEN);
SetWindowPos(&wndToMost, 0, 0, cxScreen, cyScreen, SWP_SHOWWINDOW);
SetHook(m_hWnd);
(上面的程序所有的进程都可以共享变量g_hWnd,所以无论切换到那个界面(切换界面后仍然显示的是本程序的界面,虽然活动界面不是本界面),按F2都可以终止程序。)
在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的;而在Win32环境中,情况却发生了变化,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的实例之间共 享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。
钩子
最新推荐文章于 2025-08-01 00:44:40 发布
钩子