第21章 动态链接库和钩子(2)

21.5 Windows钩子

21.5.1 Windows钩子

钩子是Windows消息处理机制中的一个监视点,应用程序可以在这里安装一个监视子程序,这样就可以在系统中的消息流到达目的窗口过程前监控它们。也就是说,钩子是用来截获系统中的消息流,并送给其他应用程序的处理的。

21.5.2 钩子的类型

(1)按作用范围分类

钩子类型

说明

局部钩子

仅钩挂属于自身进程的事件消息

★钩子模块不需要单独的dll,可以放在同一个可执行文件中

远程钩子

又分为两种:

线程钩子(LocalHook)——捕获其它进程中的某一线程的事件

系统钩子(RemoteHook)——捕获所有进程的消息,也叫全局钩子

★钩子回调函数必须放在DLL中:因为只有dll是可以被映射到其他进程的地址空间。这样,HOOK代码才能被系统注入到每个进程里面。在安装这样的钩子后,系统会自动地把该DLL注入到所有的进程空间,这样每个进程发生的特定事件就会被注入进来的Hook代码给拦截。否则,系统是不能从其他进程的地址空间中调用钩子函数,因为两个进程的地址空间是隔离的。

(2)按事件分类

  ① 键盘钩子和低级键盘钩子可以监视各种键盘消息。

  ② 鼠标钩子和低级鼠标钩子可以监视各种鼠标消息。

  ③ 外壳钩子可以监视各种Shell事件消息。比如启动和关闭应用程序。

  ④ 日志钩子可以记录从系统消息队列中取出的各种事件消息。

  ⑤ 窗口过程钩子监视所有从系统消息队列发往目标窗口的消息。

(3)常见的钩子

钩子类型

说明

WH_CALLWNDPROC

WH_CALLWNDPROCRET

①WH_CALLWNDPROC:在SendMessage时,当消息到达目标窗口之前,调用钩子函数

②WH_CALLWNDPROCRET:在SendMessage时,在消息到达目标窗口之后,调用钩子函数。

③WM_CALLWNDPROCRET会收到CWPRETSTRUCT结构体,包含了来自处理消息的窗口过程的返回值,及与这个消息关联的消息参数。

WH_CBT

①激活、建立、销毁、最小化、最大化,移动、改变尺寸等窗口事件

②设置输入焦点事件、系统菜单消息

③同步系统消息队列、完成系统指令

④来自系统消息队列中的移动鼠标,键盘事件

WH_GETMESSAGE

当GetMessage或 PeekMessage函数获取消息后,调用钩子函数,可以用来监视鼠标和键盘输入及其他消息。

WH_KEYBOARD

当GetMessage或PeekMessage,如果得到的是WM_KEYUP或WM_KEYDOWN时,则调用钩子函数

WH_MOUSE

当GetMessage或PeekMessage时,如果得到的是鼠标消息,则调用钩子函数。

WH_HARDWARE

当GetMessage或PeekMessage时,如果得到的是非鼠标和键盘消息,则调用调子函数

WH_MSGFILTER

当用户对滚动条、菜单或对话框所做所有操作时,系统在发送相应的消息之前调用钩子函数(这种钩子只能是局部的)

WH_SYSMSGFILTER

同WH_MSGFILTER,但是系统钩子。

WH_SHELL

当Shell程序准备接收一些通知事件前,调用钩子函数。外壳应用程序是不接受WH_SHELL消息的,要用应用程序能够接收WH_SHELL,必须调用SystemParametersInfo函数注册该消息。WM_SHELL共有5种情况:

①TaskBar需要重绘某个按钮时

②当系统地要显示关于Taskbar的一个程序的最小化形式

③当目前的键盘布局状态改变。

④当按Ctrl+Esc去执行TaskManager(或相等级别的程序)时。

⑤只要有个top-level、unowned窗口被创建、起作用或被摧毁

WH_DEBUG

用来给其他钩子函数除错的

WH_JOURNALRECORD

用来记录发送给系统消息队列的所有消息,被称为日志记录钩子

WH_JOURNALPLAYBACK

用来回放日志记录钩子的系统事件,被称为日志回放钩子。

WH_FOREGROUNDIDLE

应用程序的前台线程处于空闲状态时,调用钩子函数,可以在这里安排一些优先级很低的任务

21.5.3 远程钩子的安装和使用

(1)钩子程序的结构——3个功能模块

功能模块

说明

①主程序

用来实现界面或其他功能

②钩子回调函数

用来接收系统发过来的消息。

对于局部钩子来说,这模块可以处在同一可执行文件中。

对于远程钩子,该模块必须放在一个DLL库中。

③钩子安装和卸载模块

没有特别要求,一般也放在动态链接库中。

(2)钩子的安装:SetWindowsHookEx

参数

含义

int idHook

见前面表格《常见的钩子类型》

HOOKPROC lpfn

钩子函数的指针 ,也即拦截到指定系统消息后的预处理过程,一般定义在DLL中

HINSTANCE hMod

应用程序实例的句柄。如果是全局钩子, hInstance是DLL句柄(DllMain中给的模块地址,就是包含HookProc的动态库加载地址。如果该值为0则只勾自己)

DWORD dwThreadId

要安装钩子的线程ID,指定被监视的线程,如果明确指定了某个线程的ID就只监视该线程,此时的钩子即为线程钩子;如果该参数被设置为0,则表示此钩子为监视系统所有线程的全局钩子。

(2)钩子函数及钩子链

  ①LRESULT CALLBACK HookProc(int nCode,WPARAM wParam,LPARAM lParam);

  ②对于不同的钩子类型,钩子函数的参数的含义是不同的,可参照MSDN

  ③Windows系统中可能同时存在多个同类型的钩子,多个程序同时安装同一种钩子的时候,就会将这些钩子组成一个钩子链,最近加入的钩子放在链表的头部,Windows负责为每一种钩子维护一个钩子链。当一个事件发生时,Windows调用最后安装的钩子,然后由当前钩子的回调函数发起调用下一个钩子的动作,这样就可以将消息传递下来。因为,程序中于不感兴趣的消息应通过CallNextHookEx(hKeyHook,nCode,wParam,lParam)函数传递给下一个钩子。

(3)钩子的卸载:UnhookWindowsHookEx(HHOOK) 函数卸载钩子,其参数为钩子句柄。

(5)键盘钩子的实例程序

【HookTest程序】

  ①利用全局鼠标钩子来实现窗口的悬停效果(类似于QQ,当窗口靠左边时,则隐入。当鼠标靠屏幕左边时,渐显出来。

  ②实现全局键盘钩子实现键盘消息的拦截。

 

/*--------------------------------------------
   键盘钩子 供动态链接库及主程序使用的头文件
           (c)浅墨浓香,2015.6.11
--------------------------------------------*/
#pragma once
#include <Windows.h>

#ifdef _cplusplus
#ifdef API_EXPORT 
#define EXPORT   extern "C" __declspec(dllexport)          //当头文件供动态库本身使用时
#else
#define EXPORT    extern "C" __declspec(dllimport)         //当头文件供调用库的程序使用时
#endif
#else
#ifdef API_EXPORT 
#define EXPORT   __declspec(dllexport)                        //当头文件供动态库本身使用时
#else
#define EXPORT   __declspec(dllimport)                        //当头文件供调用库的程序使用时
#endif
#endif

//安装鼠标钩子
EXPORT HHOOK InstallMouseHook(HWND hwnd);
//御载鼠标钩子
EXPORT void UnInstallMouseHook();
//鼠标钩子回调函数
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);


//安装鼠标钩子
EXPORT HHOOK InstallKeyBoardHook(HWND hwnd);
//御载鼠标钩子
EXPORT void UnInstallKeyBoardHook();
//键盘钩子回调函数
LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam);

//HookLib.c

#define API_EXPORT
#include "HookLib.h"

#define WM_USER_MOUSEHOOK    (WM_USER + 1000)
#define WM_USER_KEYBOARDHOOK (WM_USER + 1001)

//以下两个变量只供当前进程使用,不需要进程间共享.
//g_hInst只供安装钩子的进程使用。
//g_wAscII只调用钩子函数的进程使用。
WORD  g_wAscII;    //保存每次击键时产生的扫描码转化成的ASCII码             
HINSTANCE g_hInst; //动态链接库的句柄

//设置自定义数据段(用来做数据在不同进程中的共享区)
//以下三个变量需要在不同进程共享。
#pragma data_seg(".myshared") 
HWND      g_hWnd            = NULL;
HHOOK     g_hMouseHook        = NULL;
HHOOK     g_hKeyBoardHook    = NULL;
#pragma data_seg() 

//设置自定义数据段的属性(可读、可写、可共享)
#pragma comment (linker,"/SECTION:.myshared,RWS")  //设为可读写、可共享属性

//入口和退出点
int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
    g_hInst = hInstance;
    return TRUE;
}

//安装鼠标钩子
HHOOK InstallMouseHook(HWND hwnd)
{
    //因为安装钩子的进程与钩子函数的进程可能是不同进程,这个变量将在钩子函数里被使用,
    //所以g_hWnd、g_hMouseHook设为进程间共享的变量
    g_hMouseHook = (HHOOK)SetWindowsHookEx(WH_MOUSE, MouseProc, g_hInst, 0);//最后一个参数为NULL,表示全局钩子
    g_hWnd = hwnd; 
    return g_hMouseHook;
}

//御载鼠标钩子
void UnInstallMouseHook()
{
    UnhookWindowsHookEx(g_hMouseHook);
}

//鼠标钩子函数
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    MOUSEHOOKSTRUCT* ms = (MOUSEHOOKSTRUCT*)lParam;
    CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);
    static POINT ptOld;

    if (wParam ==WM_MOUSEMOVE )
    {        
        if (ptOld.x != ms->pt.x && ptOld.y != ms->pt.y)
        {
            ptOld.x = ms->pt.x;
            ptOld.y = ms->pt.y;
            PostMessage(g_hWnd, WM_USER_MOUSEHOOK, wParam, MAKELPARAM(ms->pt.x, ms->pt.y));
        }        
    }
    return 0;
}


//安装键盘钩子
HHOOK InstallKeyBoardHook(HWND hwnd)
{
    
    //因为安装钩子的进程与钩子函数的进程可能是不同进程,这个变量将在钩子函数里被使用,
    //所以g_hWnd、g_hKeyBoardHook设为进程间共享的变量
    g_hKeyBoardHook = (HHOOK)SetWindowsHookEx(WH_KEYBOARD, KeyBoardProc, g_hInst, 0);//最后一个参数为NULL,表示全局钩子
    g_hWnd = hwnd;
    return g_hKeyBoardHook;
}

//御载键盘钩子
void UnInstallKeyBoardHook()
{
    UnhookWindowsHookEx(g_hKeyBoardHook);
}

/*
键盘钩子回调函数:每个按键该函数会被调用两次,即按下或释放时被调用。
lParam与键盘消息含义一致
0-15表示按键的重复次数
16-23按键的扫描码
位24:是否是扩展键(如F1F2等Fx键或小键盘的数字键),如果是为1
25-28未定义
29:Alt,按下为1,否则为0
30按键原来的状态,发送消息前按键如果是按下的为1,否则为0
31位:按键的当前动作,如果按下为0,释放为1.*/
LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    BYTE byKeyState[256];
    UINT uScanCode;
    
    //传给键盘钩子链中的下一个钩子处理
    CallNextHookEx(g_hKeyBoardHook, nCode, wParam, lParam);

    //安插个后门程序,将当前用户对键盘的操作偷偷发给我们的程序
    //以下代码实现将用户的WM_KEYDOWN传为相应的ASCII,并发给我们的程序
    if (lParam & 0x80000000)  //只处理按下时的情况,即第31位为0时
    {    
        GetKeyboardState(byKeyState);     //获取当前键盘状态
        uScanCode = ((lParam &0x000F0000)>> 16);  //取出16-23位的扫描码

        //根据当前的键盘布局,键盘状态,将扫描码转为ASCII码
        ToAscii(wParam, uScanCode, byKeyState, (LPWORD)&g_wAscII, 0);
        
        //将转换
        PostMessage(g_hWnd, WM_USER_KEYBOARDHOOK, wParam, (LPARAM)g_wAscII);
    }
    return 0;
}

//LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
//{
//    BYTE byKeyState[256];
//    UINT uScanCode;
//
//    CallNextHookEx(g_hHook, nCode, wParam, lParam);
//
//}

//HookTest.c——测试程序

#include <Windows.h>
#include "resource.h"
#include "..\HookDll\HookLib.h"

#pragma comment(lib,"..\..\Debug\HookDll.lib")

#define WM_USER_MOUSEHOOK        (WM_USER + 1000)
#define WM_USER_KEYBOARDHOOK    (WM_USER + 1001)


BOOL CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    TCHAR szBuffer[128];
    static TCHAR szKeyBuffer[128];
    static POINT pt;
    HDC hdc;
    PAINTSTRUCT ps;
    RECT  rc;

    int  i;
    static int nWidth, nHeight;

    switch (uMsg)
    {
    case WM_INITDIALOG:
        InstallMouseHook(hwndDlg);    //安装全局鼠标钩子
        InstallKeyBoardHook(hwndDlg); //安装全局键盘钩子
        GetWindowRect(hwndDlg, &rc);
        nWidth = rc.right - rc.left;
        nHeight = rc.bottom - rc.top;
        return TRUE;

    case WM_USER_MOUSEHOOK:  //自定义的鼠标钩子消息

        pt.x = LOWORD(lParam);
        pt.y = HIWORD(lParam);

        GetWindowRect(hwndDlg, &rc);
    
        //实现悬挂窗口的效果
        if (rc.left<=0)  //窗口左边界己经在屏幕左边。
        {
            if (PtInRect(&rc, pt)) //如果鼠标在窗口内,则正常显示窗口
            {
                if (IsWindowVisible(hwndDlg))
                {
                    SetWindowPos(hwndDlg, HWND_TOP, 0, rc.top, nWidth, nHeight, SWP_SHOWWINDOW);
                }
            }
            else  //如果鼠标不在窗口上,则隐入
            {
                if (IsWindowVisible(hwndDlg))
                {
                    for (i = 0; i < nWidth; i++)
                    {
                        SetWindowPos(hwndDlg, HWND_TOP, 0, rc.top, nWidth - i, nHeight, SWP_SHOWWINDOW);
                    }
                    SetWindowPos(hwndDlg, HWND_TOP, 0, rc.top, nWidth - i, nHeight, SWP_HIDEWINDOW); //隐藏起来(含任务栏图标也消失了)
                }
            }
        }

        if (pt.x<=0) //当鼠标靠屏幕右侧时,如果窗口原来隐藏的,则渐显出来。如果之前己经正常显示的,则不做任何处理。
        {
            if (!IsWindowVisible(hwndDlg))
            {
                for (i = 0; i < nWidth; i += 1)
                {
                    SetWindowPos(hwndDlg, HWND_TOP, 0, rc.top, i, nHeight, SWP_SHOWWINDOW);
                }
            }
        }

        InvalidateRect(hwndDlg, NULL, TRUE);
        return 0;
 
    case WM_USER_KEYBOARDHOOK:  //自定义的键盘钩子消息
        wsprintf(szKeyBuffer, TEXT("您当前按下了键盘上的:%c"), lParam);
        InvalidateRect(hwndDlg, NULL, TRUE);
        return TRUE;

    case WM_PAINT:
        hdc = BeginPaint(hwndDlg, &ps);

        SetBkMode(hdc, TRANSPARENT);
        TextOut(hdc, 10, 40, szBuffer, wsprintf(szBuffer, TEXT("当前坐标:x = %d,y = %d"), pt.x, pt.y));
        TextOut(hdc, 10, 60, szKeyBuffer, lstrlen(szKeyBuffer));

        EndPaint(hwndDlg, &ps);
        return TRUE;

    case WM_CLOSE:
        UnInstallMouseHook();    //卸载鼠标钩子
        UnInstallKeyBoardHook(); //卸载键盘钩子
        EndDialog(hwndDlg, 0);
        return TRUE;
    }

    return FALSE;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    DialogBoxParam(hInstance, TEXT("HOOKTEST"), NULL, DlgProc,0);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 HookTest.rc 使用
//
#define IDC_TEXT                        1001

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1002
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//HookTest.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

HOOKTEST DIALOGEX 0, 0, 145, 176
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "鼠标、键盘钩子"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    "HOOKTEST", DIALOG
    BEGIN
    END
END
#endif    // APSTUDIO_INVOKED

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

21.5.4 日志记录钩子

(1)记录发送给系统消息队列的所有消息。

(2)日志记录钩子是一种特殊的钩子,因为它是远程钩子,但不用放在动态链接库中。

【RecHook程序】

  ①如果是WinVista以上,在安装日志钩子时要关闭UAC,方法如下:

  regedit→HKEY_LOCAL_MACHINE Software Microsoft Windows CurrentVersion Policies System将名为“EnableLUA”的注册表项之值改为0,并重启系统。如果要重新开启UAC功能,只需将该项值改为1即可

  ②日志钩子函数中

      //当发生HC_ACTION事件时,lParam指向EVENTMSG结构体

/*
   typedef struct tagEVENTMSG {
      UINT  message;  //消息队列中将要删除去的消息ID
      UINT  paramL;   //消息的wParam
      UINT  paramH;   //消息的lParam
      DWORD time;     //消息发生的时间
      HWND  hwnd;     //消息对应的窗口句柄
   } EVENTMSG, *PEVENTMSG;

*/

#include <Windows.h>
#include "resource.h"

HHOOK  hHook;
HWND   hDlg;
HINSTANCE hInst;

CHAR szAscII[32];
//定义函数原型
//typedef int(__stdcall *Func_RtlAdjustPrivilege)(ULONG, BOOLEAN, BOOLEAN, PBOOLEAN);

LRESULT CALLBACK HookProc(int nCode,  // hook code
                                    WPARAM wParam,  // not used
                                    LPARAM lParam   // message being processed
    )
{
    EVENTMSG* pEm;
    BYTE byKeyState[256];
    UINT uScanCode;
    
    int  iLength;

    CallNextHookEx(hHook, nCode, wParam, lParam);

    //当系统准备从消息队列中删除一条消息时
    if (nCode == HC_ACTION)
    {
        //当发生HC_ACTION事件时,lParam指向EVENTMSG结构体
        /*
        typedef struct tagEVENTMSG {
        UINT  message;  //消息队列中将要删除去的消息ID
        UINT  paramL;   //消息的wParam
        UINT  paramH;   //消息的lParam
        DWORD time;     //消息发生的时间
        HWND  hwnd;     //消息对应的窗口句柄
        } EVENTMSG, *PEVENTMSG;
        */
        pEm = (EVENTMSG*)lParam;
        if (pEm->message == WM_KEYDOWN)
        {
            GetKeyboardState(byKeyState);     //获取当前键盘状态
            uScanCode = ((pEm->paramH & 0x000F0000) >> 16);  //取出16-23位的扫描码

            //根据当前的键盘布局,键盘状态,将扫描码转为ASCII码
            iLength = ToAscii(pEm->paramL, uScanCode, byKeyState, (LPWORD)szAscII, 0);
            szAscII[iLength] = '';

            if (szAscII[0] == 0x0D)
            {
                szAscII[2] = 0x0A;
            }

            //wParam操作是否可撤消,lParam以结束的字符串,用来替换编辑框中选中的文本
            //如果没有选中的文本,则将lParam指向的字符串插入Caret处。
            SendDlgItemMessage(hDlg, IDC_TEXT, EM_REPLACESEL, 0, (LPARAM)szAscII);
        }
    }
    return TRUE;
}

BOOL CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_INITDIALOG:
        hDlg = hwndDlg;
        hHook = SetWindowsHookEx(WH_JOURNALRECORD, HookProc, hInst, 0);
        return TRUE;

    case WM_CLOSE:
        UnhookWindowsHookEx(hHook);
        EndDialog(hwndDlg, 0);
        return TRUE;
    }

    return FALSE;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    /*
    //装载DLL
    HMODULE hModule = GetModuleHandle(TEXT("NtDll.dll"));// GetModuleHandle(TEXT("NtDll.dll")); //LoadLibrary(TEXT("NtDll.dll"))
    if (hModule == NULL) {
    return 0;
    }

    //得到导出函数的地址
    Func_RtlAdjustPrivilege RtlAdjustPrivilege = (Func_RtlAdjustPrivilege)GetProcAddress(hModule, "RtlAdjustPrivilege");
    if (RtlAdjustPrivilege == NULL) {
    return 0;
    }
    */

    /*
    .常量 SE_BACKUP_PRIVILEGE, "17", 公开
    .常量 SE_RESTORE_PRIVILEGE, "18", 公开
    .常量 SE_SHUTDOWN_PRIVILEGE, "19", 公开
    .常量 SE_DEBUG_PRIVILEGE, "20", 公开
    */
    //RtlAdjustPrivilege(20, 1, 0, NULL);
    //FreeLibrary(hModule);

    hInst = hInstance;
    DialogBoxParam(hInstance, TEXT("RECHOOK"), NULL, DlgProc, 0);
    return 0;
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 RecHook.rc 使用
//
#define IDC_TEXT                        1001

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1003
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//RecHook.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

RECHOOK DIALOGEX 0, 0, 145, 176
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "日志记录钩子"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
EDITTEXT        IDC_TEXT, 7, 8, 132, 160, ES_MULTILINE | ES_AUTOVSCROLL| WS_BORDER | WS_VSCROLL | WS_TABSTOP | ES_READONLY
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    "RECHOOK", DIALOG
    BEGIN
    END
END
#endif    // APSTUDIO_INVOKED

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

21.5.4 几点需要说明的地方:

(1)如果对于同一事件(如鼠标消息)既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,然后调用系统钩子。

(2)对同一事件消息可安装多个钩子处理过程,这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。而且最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。

(3)钩子特别是系统钩子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装钩子,在使用完毕后要及时卸载。

原文地址:https://www.cnblogs.com/5iedu/p/4715166.html