C++ Windows Hook 消息钩子 详解

来源:互联网 发布:origin pro for mac 编辑:程序博客网 时间:2024/04/25 19:59

本文完整测试工程的源码免积分下载地址:http://download.csdn.net/detail/zy_dreamer/5336484

我们先来简单的了解一下基本概念:

Hook是WINDOWS提供的一种消息处理机制,它使得程序员可以使用子过程来监视系统消息,并在消息到达目标过程前得到处理。

HOOK链 
WINDOWS提供了几种不同类型的HOOKS;不同的HOOK可以处理不同的消息。例如,WH_MOUSE HOOK用来监视鼠标消息。 
WINDOWS为这几种HOOKS维护着各自的HOOK链。HOOK链是一个由应用程序定义的回调函数队列,当某种类型的消息发生时,WINDOWS向此种类型的HOOK链的第一个函数发送该消息,在第一函数处理完该消息后由该函数向链表中的下一个函数传递消息,依次向下。如果链中某个函数没有向下传送该消息,那么链表中后面的函数将得不到此消息。(对于某些类型的HOOK,不管HOOK链中的函数是否向下传递消息,与此类型HOOK联系的所有HOOK函数都会收到系统发送的消息)
HOOK过程 
为了拦截特定的消息,你可以使用SetWindowsHookEx函数在该类型的HOOK链中安装你自己的HOOK函数。

该函数MSDN中的描述

The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread. SyntaxHHOOK SetWindowsHookEx(          int idHook,    HOOKPROC lpfn,    HINSTANCE hMod,    DWORD dwThreadId);ParametersidHook[in] Specifies the type of hook procedure to be installed. This parameter can be one of the following values. WH_CALLWNDPROCInstalls a hook procedure that monitors messages before the system sends them to the destination window procedure. For more information, see the CallWndProc hook procedure.WH_CALLWNDPROCRETInstalls a hook procedure that monitors messages after they have been processed by the destination window procedure. For more information, see the CallWndRetProc hook procedure.WH_CBTInstalls a hook procedure that receives notifications useful to a computer-based training (CBT) application. For more information, see the CBTProc hook procedure.WH_DEBUGInstalls a hook procedure useful for debugging other hook procedures. For more information, see the DebugProc hook procedure.WH_FOREGROUNDIDLEInstalls a hook procedure that will be called when the application's foreground thread is about to become idle. This hook is useful for performing low priority tasks during idle time. For more information, see the ForegroundIdleProc hook procedure. WH_GETMESSAGEInstalls a hook procedure that monitors messages posted to a message queue. For more information, see the GetMsgProc hook procedure.WH_JOURNALPLAYBACKInstalls a hook procedure that posts messages previously recorded by a WH_JOURNALRECORD hook procedure. For more information, see the JournalPlaybackProc hook procedure.WH_JOURNALRECORDInstalls a hook procedure that records input messages posted to the system message queue. This hook is useful for recording macros. For more information, see the JournalRecordProc hook procedure.WH_KEYBOARDInstalls a hook procedure that monitors keystroke messages. For more information, see the KeyboardProc hook procedure.WH_KEYBOARD_LLWindows NT/2000/XP: Installs a hook procedure that monitors low-level keyboard input events. For more information, see the LowLevelKeyboardProc hook procedure.WH_MOUSEInstalls a hook procedure that monitors mouse messages. For more information, see the MouseProc hook procedure.WH_MOUSE_LLWindows NT/2000/XP: Installs a hook procedure that monitors low-level mouse input events. For more information, see the LowLevelMouseProc hook procedure.WH_MSGFILTERInstalls a hook procedure that monitors messages generated as a result of an input event in a dialog box, message box, menu, or scroll bar. For more information, see the MessageProc hook procedure.WH_SHELLInstalls a hook procedure that receives notifications useful to shell applications. For more information, see the ShellProc hook procedure.WH_SYSMSGFILTERInstalls a hook procedure that monitors messages generated as a result of an input event in a dialog box, message box, menu, or scroll bar. The hook procedure monitors these messages for all applications in the same desktop as the calling thread. For more information, see the SysMsgProc hook procedure.lpfn[in] Pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a hook procedure in a DLL. Otherwise, lpfn can point to a hook procedure in the code associated with the current process. hMod[in] Handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process. dwThreadId[in] Specifies the identifier of the thread with which the hook procedure is to be associated. If this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread. Return ValueIf the function succeeds, the return value is the handle to the hook procedure. If the function fails, the return value is NULL. To get extended error information, call GetLastError.
这里我来简单的解释一下:

函数的功能描述是:SetWindowsHookEx函数安装一个程序自定义的钩子过程到钩子链。你可以安装一个钩子过程来监视系统的特定消息。可以监视某一个特定的线程的消息,或者是与这个消息相关的当前运行的所有线程。

函数一共有四个参数
参数1:idHook
定义了钩子过程的类型,也就是这个钩子将要监视哪种消息
参数2:lpfn
它是一个钩子过程的指针。如果dwThreadId也就是第四个参数的值是0或者是由其它进程创建的线程的ID,那么这个参数必须是指向一个位于DLL中的钩子过程的指针,否则这个指针可以是当前进程代码空间中定义的一个钩子过程的地址。
参数3:hMod
是一个包含了lpfn所指向的钩子过程的DLL的句柄。如果第四个参数线程ID的值是由当前进程创建的线程的ID值并且钩子过程是在当前进程中定义的,那么这个参数必须设置成NULL。
参数4:dwThreadId
指明钩子过程要监视的线程ID。如果是0那么钩子过程将监视所有桌面运行的线程。
返回值:
成功返回钩子过程的句柄。
失败返回NULL,获得额外信息,调用GetLastError

lpfn指向钩子回调函数的函数地址,这个函数用来对钩子检测到的消息进行处理。MSDN中函数的原型如下,这里以键盘检测函数的原型为例:

The KeyboardProc hook procedure is an application-defined or library-defined callback function used with theSetWindowsHookEx function. The system calls this function whenever an application calls the GetMessage orPeekMessage function and there is a keyboard message (WM_KEYUP or WM_KEYDOWN) to be processed.

The HOOKPROC type defines a pointer to this callback function. KeyboardProc is a placeholder for the application-defined or library-defined function name.

Syntax

LRESULT CALLBACK KeyboardProc(          int code,    WPARAM wParam,    LPARAM lParam);

Parameters

code
[in] Specifies a code the hook procedure uses to determine how to process the message. If code is less than zero, the hook procedure must pass the message to the CallNextHookEx function without further processing and should return the value returned by CallNextHookEx. This parameter can be one of the following values.
HC_ACTION
The wParam and lParam parameters contain information about a keystroke message.
HC_NOREMOVE
The wParam and lParam parameters contain information about a keystroke message, and the keystroke message has not been removed from the message queue. (An application called thePeekMessage function, specifying the PM_NOREMOVE flag.)
wParam
[in] Specifies the virtual-key code of the key that generated the keystroke message.
lParam
[in] Specifies the repeat count, scan code, extended-key flag, context code, previous key-state flag, and transition-state flag. For more information about the lParam parameter, see Keystroke Message Flags. This parameter can be one or more of the following values.
0-15
Specifies the repeat count. The value is the number of times the keystroke is repeated as a result of the user's holding down the key.
16-23
Specifies the scan code. The value depends on the OEM.
24
Specifies whether the key is an extended key, such as a function key or a key on the numeric keypad. The value is 1 if the key is an extended key; otherwise, it is 0.
25-28
Reserved.
29
Specifies the context code. The value is 1 if the ALT key is down; otherwise, it is 0.
30
Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
31
Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released.

Return Value

If code is less than zero, the hook procedure must return the value returned by CallNextHookEx.

If code is greater than or equal to zero, and the hook procedure did not process the message, it is highly recommended that you call CallNextHookEx and return the value it returns; otherwise, other applications that have installed WH_KEYBOARD hooks will not receive hook notifications and may behave incorrectly as a result. If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure.

Remarks

An application installs the hook procedure by specifying the WH_KEYBOARD hook type and a pointer to the hook procedure in a call to the SetWindowsHookEx function. 

keyboardProc钩子过程是定义在应用程序中或是库文件中的回调函数,它有SetWindowsHookEx来使用。当应用程序中调用了GetMessage或是PeekMessage方法时并且处理的是键盘消息(WM_KEYUP或WM_KEYDOWN)HOOKPROC类型定义了一个指向回调函数的指针。从原型定义中我们可以看出:typedef LRESULT (CALLBACK* HOOKPROC)(int code, WPARAM wParam, LPARAM lParam);

它其实就是在WinUser.h中定义的一个函数指针

一个简单的回调函数的例子如下(键盘钩子为例)

// 钩子回调函数(钩子过程)LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam){    if (nCode < 0 || nCode == HC_NOREMOVE)//向下传递消息使得hook链上的其他函数可以处理该消息return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);    if (lParam & 0x40000000)// Check the previous key state{return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);}//将自定义消息发送到主窗口//wParam 定义了虚拟键代码//lParam 定义了按键数据    ::PostMessage(g_hWnd, WM_KEYSTROKE, wParam, lParam);    return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);}
上面PostMessage函数中的g_hWnd是主窗口的句柄,就是我们用来处理检测到的消息的那个窗口。
WM_KEYSTROKE是我们自己定义的一个消息宏,当钩子检测到消息后,就将这个消息发送到主窗口来进行处理。
#define WM_KEYSTROKE (WM_USER + 101)

我们定义了下面一个函数来安装或卸载一个消息钩子。

//安装或者卸载钩子的函数BOOL WINAPI SetKeyboardHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller){BOOL bOk;g_hWnd = hWndCaller;if (bInstall){g_hHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc, ModuleFromAddress(KeyboardHookProc), dwThreadId);//安装消息钩子bOk = (g_hHook != NULL);}else {bOk = ::UnhookWindowsHookEx(g_hHook);g_hHook = NULL;}return bOk;}
ModuleFromAddress函数帮助我们获得模块地址:

HMODULE WINAPI ModuleFromAddress(PVOID pv) {MEMORY_BASIC_INFORMATION mbi;if (::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0){return (HMODULE)mbi.AllocationBase;}else{return NULL;}}
由于上面我们定义的消息钩子的回调函数是和安装钩子的程序代码定义在一起的,而不是定义在外部DLL中,所以这个钩子只能监视本线程的消息,而不是一个全局消息钩子。要想实现能对所有程序的键盘消息进行检测,必须将消息钩子的回调函数定义在DLL模块中。
DLL中的定义如下:

.h

#ifdef HOOKDLL_EXPORTS#define HOOKDLL_API __declspec(dllexport)#else#define HOOKDLL_API __declspec(dllimport)#endif//自定义消息,当键盘按键按下和释放时发送消息#define WM_KEYSTROKE (WM_USER + 101)#define WM_KEYINPUT  (WM_USER + 102)//导出函数BOOL HOOKDLL_API WINAPI SetKeyboardHook(BOOL bInstall, DWORD dwThreadId = 0, HWND hWndCaller = NULL);
.cpp

#include <windows.h>#define HOOKDLL_EXPORTS#include "HookDll.h"// Shared data among all instances.#pragma data_seg(".HOOKDATA")HWND g_hWnd = NULL;        // Window handleHHOOK g_hHook = NULL;// Hook handle// Get module from addressHMODULE WINAPI ModuleFromAddress(PVOID pv) {MEMORY_BASIC_INFORMATION mbi;if (::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0){return (HMODULE)mbi.AllocationBase;}else{return NULL;}}// 钩子回调函数(钩子过程)LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam){    if (nCode < 0 || nCode == HC_NOREMOVE)//向下传递消息使得hook链上的其他函数可以处理该消息return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);    if (lParam & 0x40000000)// Check the previous key state{return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);}//将自定义消息发送到主窗口//wParam 定义了虚拟键代码//lParam 定义了按键数据    ::PostMessage(g_hWnd, WM_KEYSTROKE, wParam, lParam);    return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);}//安装或者卸载钩子的函数BOOL WINAPI SetKeyboardHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller){BOOL bOk;g_hWnd = hWndCaller;if (bInstall){g_hHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc, ModuleFromAddress(KeyboardHookProc), dwThreadId);//安装消息钩子bOk = (g_hHook != NULL);}else {bOk = ::UnhookWindowsHookEx(g_hHook);g_hHook = NULL;}return bOk;}
.def

LIBRARYCppHooKDllEXPORTS   SetKeyboardHookSECTIONS.HOOKDATA   Read Write Shared

在需要安装全局钩子的程序中调用:

::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc, ModuleFromAddress(KeyboardHookProc), 0)






原创粉丝点击