TA的每日心情 | 奋斗 2020-6-5 22:18 |
---|
签到天数: 22 天 [LV.4]偶尔看看III
|
欢迎您注册加入!这里有您将更精采!
您需要 登录 才可以下载或查看,没有账号?注册
x
Windows钩子是使用DLL编写的。在高层,钩子由需要监视其他应用程序或系统活动的应用程序使用;在低层,钩子是windows消息传递中的点,函数根据这个路径被注入或附加,以过滤某些类型的消息,使之达到目的地。一旦捕获了这些消息,就可以对其进行修改、记录或丢弃。函数本身称为过滤器。这些过滤器是按照正在过滤的事件的类型进行分类的。当一个过滤函数被附加到一个钩子时,称为“sethook”。肯定有些时候设置了同一类型的多个钩子。为了处理这种情况,Windows记录着过滤函数链的情况。最近添加的过滤函数是链中的第一个函数,因此,它最先有机会过滤任何消息通信量。然后,这个函数执行它的工作并把消息发送到链中的下个钩子过程,后者完全放弃该消息。下面先列出Wondows钩子的类型:
WH_CALLWNDPROC 在消息被发送到其目的窗口例程之前监视所有的Windows消息
WH_CALLWNDPROCRET 监视已经被目的Windows例程处理后的消息
WH_CBT 监视Computer-Base-Training应用程序所用的消息
WH_DEBUG 辅助调试其他钩子函数
WH_GETMESSAGE 监视投递到消息队列中的Windows消息
WH_JOURNALPLAYBACK 投递以前由
WH_JOURNALRECORD异常分支例程处理的消息
WH_JOURNALRECORD 记录投递到系统消息队列的输入消息
WH_KEYBOARD 监视键盘的活动
WH_KEYBOARD_LL 监视低层键盘输入事件(只适用于Windows 2000/XP)
WH_MOUSE 监视鼠标消息
WH_MOUSE_LL 监视低层鼠标输入事件(只适用于Windows 2000/XP)
WH_MSGFILTER 监视作为对话框、消息框、菜单或滚动条中输入事件的结果而生成的消息
WH_SHELL 接受对外壳应用程序有意义的事件通知
WH_SYSMSGFILTER 类似于WH_MSGFILTER,监视作为对话框、消息框、菜单或滚动条中输入事件的结果而生成的消息。然而WH_SYSMSGFILTER为所有活动的应用程序监视这些消息
现在要来讲解 SetWindowsHookEx函数
为了设置一个钩子,必须调用SetWindowsHookEx函数,原型如下:
HHOOK
SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId)
第一个参数 IdHook代表一个常量,该常量标识了正要设置的钩子的类型。
第二个参数 lpfn 指向一个函数的HOOKPROC类型的指针。
第三个参数hMod包含过滤函数的模块的HINSTANCE 。
第四个参数为线程ID。返回值是一个钩子的句柄,如果函数没有成功则返回值为NULL,需要调用GetLastError函数,以确定失败的原因。
UnhookWindowsHookEx函数
如果应用程序设置了windows钩子,那么在该程序终止前也需要由它从钩子链中去掉一个钩子。这是通过UnhookWindowsHookEx函数来实现。原型如下
BOOL UnhookWindowsHookEx(HHOOK hHook)
CallNextHook函数
当为给定钩子类型设置了一个过滤函数后,该过滤函数放在钩子类型的过滤函数的链头。过滤函数完成其任务后,就可以通过CallNextHook函数调用链中
下一个过滤函数。如果不想继续进行处理,例如编写了一个键盘钩子并且想放弃某次击键,就不要调用该函数。然而,如果简单地记录一个事件,并且想让消息
继续沿其消息路径前进,应用程序则需要在处理完消息后调用CallNextHook函数,否则其他消息将得不到处理。如果这种类型的钩子没有其他过滤函数,Windows会把这个消息传递到适当的消息队列中。
下面来举一个简单的例子来说明钩子的运用,我们来编写一个键盘钩子程序,在这个例子中所有按下“F1”键将要被捕捉,大家熟悉以后可以试着学习捕获消息队列中的windows消息,这对于编写辅助程序非常有用,当然有些消息是程序中自定义的,然而你仍然可以通过一写技巧来捕捉到其中的某些消息。这是后话。
首先为键盘钩子创建将包含过滤函数的DLL,在项目工程中创建一个名为KeybdHook的MFC的常规DLL。打开KeybdHook.h文件,在CkeybdHookApp类声明之前加入一些声明:
#define EXPORTED_DLL_FUNCTION _declspec(dllexport) _stdcall //提供一种导出具有_stdcall调用机制的函数的简单方式
LRESULT _stdcall KbdHookProc(int nCode, WPARAM wParam , LPARAM lParam); //实际的过滤函数
在KeybdHook.cpp中声明一些全局变量如下:
#pragma data_seg(".SHARDAT")
static HWND
ghWndMain = 0;
static HHOOK
ghKeyHook = NULL;
#pragma data_seg()
HIINSTANCE
ghInstance = 0;
HOOKPROC glpfnHookProc = 0;
好了,KbdHookProc函数的实现是这样的:
LRESULT EXPORTED_DLL_FUNCTION KbdHookProc(int nCode, WPARAM wParam , LPARAM lParam)
{
BOOL bHandledKeyStroke = FALSE;
if(((DWORD)lParam & 0x40000000) && (HC_ACTION == nCode))
{
switch(wParam)
{
case VK_F1:
AfxMessageBox("捕捉到了 F1 按键!");
bHandleKeyStroke = TRUE;
break;
default:
break;
}
}
return (bHandledKeystroke ? TRUE: ::CallNextHookEx(ghKeyHook,nCode,wParam,lParam));
}
nCode可能包含的值之一是HC_ACTION常量。实际上这个参数只能是两个值之一 :HC_ACTION 和 HC_NOREMOVE。 在这两种情况下,WPARAM 和 LPARAM变量将包含关于击键的信息。如果nCode小于0,该过滤函数不应该处理击键,而是该调用CallNextHookEx函数。
接下来导出DLL函数。首先打开KeybdHook.h文件,声明一个用于安装或设置键盘钩子的函数:
BOOL _stdcall InstallKeyBoardHook(HWND hWnd);
实现如下:
BOOL EXPORTED_DLL_FUNCTION InstallKeyBoardHook(HWND hWnd)
{
BOOL bSuccess = FALSE;
if(!ghKeyHook)
{
ghWndMain
= hWnd;
glpfnHookProc
= (HOOKPROC) KbdHookProc;
bSuccess = (NULL!=(ghKeyHook = ::SetWindowsHookEx(WH_KEYBOARD,glpfnHookProc,ghInstance,NULL)));
}
return bSuccess;
}
还要导出一个函数来释放钩子:
BOOL EXPORTED_DLL_FUNCTION DeInstallKeyboardHook()
{
if(ghKeyHook)
{
if (TRUE ==(0 != ::UnhookWindowsHookEx(ghKeyHook)))
{
ghKeyHook = NULL;
}
}
return (NULL == ghKeyHook);
}
如果大家看一看由MFC生成的DLL的代码很容易看出来,DLL类是从CWinApp 类派生来的,并且实现了InitInstance成员函数,应该在这里提供DLL所需的初试化代码。
使用CWinApp类的优势是可以像对其他任何CWinApp派生类那样进行DLL编程。例如,可以重载DLL的CWinApp::InitInstance函数,并提供全局初始化。虽然MFC提供了
默认的DllMain实现,但一般还是建议用户在DLL的CWinApp派生类的InitInstance函数中自己进行初始化,在很多情况下是必须这么做的。
BOOL CKeybdHookApp::InitInstance()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
ghInstance = AfxGetInstanceHandle();
return TRUE;
}
int CKeybdHookApp::ExitInstance()
{
DeInstallKeyboardHook();
return CWinApp::ExitInstance();
}
这样一个对键盘按键消息捕捉的钩子函数就全部建立好了。之所以用DLL是方便客户(指应用程序或其他DLL)导出调用。以后为了测试键盘钩子,可以创建一个基于对话框
的应用程序KeybdHookClient。假定为单击OK按扭事件创建一个消息处理程序,在处理程序中调用DLL的InstallKeyboardHook导出函数:
void CkeybdHookClientDlg::OnOK()
{
InstallKeyboardHook(GetSafeHwnd());
}
重载对话框OnCancel函数
void CkeybdHookClientDlg::OnCancel()
{
DeInstallKeyboardHook();
CDialog::OnCancel();
}
别忘记在KeybdClientDlg.cpp文件前加上 #include "KeybdHook.h"
要测试这个钩子程序,可以找一个使用F1的程序即可。当你启动你的KeybdHookClient应用程序,再在其他的软件程序中按"F1"时,就会弹出一个消息框,系统告诉你捕捉了按键消息。
|
|