金山词霸抓词机理 -- HOOK消息功能的使用

Win32 全局钩子在 VC5 中的实现  
   Windows 系统是建立在事件驱动的机制上的,说穿了就是整个系统都是通过消息的传递来实现的。而钩子是 Windows 系统中非常重要的系统接口,用它可以截获并处理送给其他应用程序的消息,来完成普通应用程序难以实现的功能。钩子的种类很多,每种钩子可以截获并处理相应的消息,如键盘钩子可以截获键盘消息,外壳钩子可以截取、启动和关闭应用程序的消息等。本文在 VC5 编程环境下实现了一个简单的鼠标钩子程序,并对 Win32 全局钩子的运行机制、 Win32 DLL 的特点、 VC5 环境下的 MFC DLL 以及共享数据等相关知识进行了简单的阐述。
  一. Win32 全局钩子的运行机制

  钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。对每种类型的钩子由系统来维护一个钩子链,最近安装的钩子放在链的开始,而最先安装的钩子放在最后,也就是后加入的先获得控制权。要实现 Win32 的系统钩子,必须调用 SDK 中的 API 函数 SetWindowsHookEx 来安装这个钩子函数,这个函数的原型是 HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hMod,DWORD dwThreadId); ,其中,第一个参数是钩子的类型;第二个参数是钩子函数的地址;第三个参数是包含钩子函数的模块句柄;第四个参数指定监视的线程。如果指定确定的线程,即为线程专用钩子;如果指定为空,即为全局钩子。其中,全局钩子函数必须包含在 DLL (动态链接库)中,而线程专用钩子还可以包含在可执行文件中。得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个 SDK 中的 API 函数 CallNextHookEx 来传递它。钩子函数也可以通过直接返回 TRUE 来丢弃该消息,并阻止该消息的传递。

  二. Win32 DLL 的特点

   Win32 DLL Win16 DLL 有很大的区别,这主要是由操作系统的设计思想决定的。一方面,在 Win16 DLL 中程序入口点函数和出口点函数( LibMain WEP )是分别实现的;而在 Win32 DLL 中却由同一函数 DLLMain 来实现。无论何时,当一个进程或线程载入和卸载 DLL 时,都要调用该函数,它的原型是 BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved); ,其中,第一个参数表示 DLL 的实例句柄;第三个参数系统保留;这里主要介绍一下第二个参数,它有四个可能的值: DLL_PROCESS_ATTACH (进程载入), DLL_THREAD_ATTACH (线程载入), DLL_THREAD_DETACH (线程卸载), DLL_PROCESS_DETACH (进程卸载),在 DLLMain 函数中可以对传递进来的这个参数的值进行判别,并根据不同的参数值对 DLL 进行必要的初始化或清理工作。举个例子来说,当有一个进程载入一个 DLL 时,系统分派给 DLL 的第二个参数为 DLL_PROCESS_ATTACH ,这时,你可以根据这个参数初始化特定的数据。另一方面,在 Win16 环境下,所有应用程序都在同一地址空间;而在 Win32 环境下,所有应用程序都有自己的私有空间,每个进程的空间都是相互独立的,这减少了应用程序间的相互影响,但同时也增加了编程的难度。大家知道,在 Win16 环境中, DLL 的全局数据对每个载入它的进程来说都是相同的;而在 Win32 环境中,情况却发生了变化,当进程在载入 DLL 时,系统自动把 DLL 地址映射到该进程的私有空间,而且也复制该 DLL 的全局数据的一份拷贝到该进程空间,也就是说每个进程所拥有的相同的 DLL 的全局数据其值却并不一定是相同的。因此,在 Win32 环境下要想在多个进程中共享数据,就必须进行必要的设置。亦即把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。

  三. VC5 MFC DLL 的分类及特点

  在 VC5 中有三种形式的 MFC DLL (在该 DLL 中可以使用和继承已有的 MFC ) 可供选择,即 Regular statically linked to MFC DLL (标准静态链接 MFC DLL )和 Regular using the shared MFC DLL (标准动态链接 MFC DLL )以及 Extension MFC DLL (扩展 MFC DLL )。第一种 DLL 的特点是,在编译时把使用的 MFC 代码加入到 DLL 中,因此,在使用该程序时不需要其他 MFC 动态链接类库的存在,但占用磁盘空间比较大;第二种 DLL 的特点是,在运行时,动态链接到 MFC 类库,因此减少了空间的占用,但是在运行时却依赖于 MFC 动态链接类库;这两种 DLL 既可以被 MFC 程序使用也可以被 Win32 程序使用。第三种 DLL 的特点类似于第二种,做为 MFC 类库的扩展,只能被 MFC 程序使用。

  四.在 VC5 中全局共享数据的实现

  在主文件中,用 #pragma data_seg 建立一个新的数据段并定义共享数据,其具体格式为:

   #pragma data_seg "shareddata")

   HWND sharedwnd=NULL;// 共享数据

   #pragma data_seg()

  仅定义一个数据段还不能达到共享数据的目的,还要告诉编译器该段的属性,有两种方法可以实现该目的(其效果是相同的),一种方法是在 .DEF 文件中加入如下语句:

   SETCTIONS

   shareddata READ WRITE SHARED

  另一种方法是在项目设置链接选项中加入如下语句:

   /SECTION:shareddata,rws

  五.具体实现步骤

  由于全局钩子函数必须包含在动态链接库中,所以本例由两个程序体来实现。

   1 .建立钩子 Mousehook.DLL

   (1) 选择 MFC AppWizard(DLL) 创建项目 Mousehook

   (2) 选择 MFC Extension DLL (共享 MFC 拷贝)类型;

   (3) 由于 VC5 没有现成的钩子类,所以要在项目目录中创建 Mousehook.h 文件,在其中建立钩子类:

   class AFX_EXT_CLASS Cmousehook:public CObject

   {

   public:

   Cmousehook();

   // 钩子类的构造函数

   ~Cmousehook();

   // 钩子类的析构函数

   BOOL starthook(HWND hWnd);

   // 安装钩子函数

   BOOL stophook();

  卸载钩子函数

   };

   (4) Mousehook.app 文件的顶部加入 #include"Mousehook.h" 语句;

   (5) 加入全局共享数据变量:

   #pragma data_seg("mydata")

   HWND glhPrevTarWnd=NULL;

   // 上次鼠标所指的窗口句柄

   HWND glhDisplayWnd=NULL;

   // 显示目标窗口标题编辑框的句柄

   HHOOK glhHook=NULL;

   // 安装的鼠标勾子句柄

   HINSTANCE glhInstance=NULL;

   //DLL 实例句柄

   #pragma data_seg()

   (6) DEF 文件中定义段属性:

   SECTIONS

   mydata READ WRITE SHARED

   (7) 在主文件 Mousehook.cpp DllMain 函数中加入保存 DLL 实例句柄的语句:

   DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)

   {

   // 如果使用 lpReserved 参数则删除下面这行

   UNREFERENCED_PARAMETER(lpReserved);

   if (dwReason == DLL_PROCESS_ATTACH)

   {

   TRACE0("MOUSEHOOK.DLL Initializing!/n");

   // 扩展 DLL 仅初始化一次

   if (!AfxInitExtensionModule(MousehookDLL, hInstance))

   return 0;

   new CDynLinkLibrary(MousehookDLL);

   // DLL 加入动态 MFC 类库中

   glhInstance=hInstance;

   // 插入保存 DLL 实例句柄

   }

   else if (dwReason == DLL_PROCESS_DETACH)

   {

   TRACE0("MOUSEHOOK.DLL Terminating!/n");

   // 终止这个链接库前调用它

   AfxTermExtensionModule(MousehookDLL);

   }

   return 1;

   }

   (8) Cmousehook 的成员函数的具体实现:

   Cmousehook::Cmousehook()

   // 类构造函数

   {

   }

   Cmousehook::~Cmousehook()

   // 类析构函数

   {

   stophook();

   }

   BOOL Cmousehook::starthook(HWND hWnd)

   // 安装钩子并设定接收显示窗口句柄

   {

   BOOL bResult=FALSE;

   glhHook=SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0);

   if(glhHook!=NULL)

   bResult=TRUE;

   glhDisplayWnd=hWnd;

   // 设置显示目标窗口标题编辑框的句柄

   return bResult;

   }

   BOOL Cmousehook::stophook()

   // 卸载钩子

   {

   BOOL bResult=FALSE;

   if(glhHook)

   {

   bResult= UnhookWindowsHookEx(glhHook);

   if(bResult)

   {

   glhPrevTarWnd=NULL;

   glhDisplayWnd=NULL;// 清变量

   glhHook=NULL;

   }

   }

   return bResult;

   }

   (9) 钩子函数的实现:

   LRESULT WINAPI MouseProc(int nCode,WPARAM wparam,LPARAM lparam)

   {

   LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lparam;

   if (nCode>=0)

   {

   HWND glhTargetWnd=pMouseHook->hwnd;

   // 取目标窗口句柄

   HWND ParentWnd=glhTargetWnd;

   while (ParentWnd !=NULL)

   {

   glhTargetWnd=ParentWnd;

   ParentWnd=GetParent(glhTargetWnd);

   // 取应用程序主窗口句柄

   }

   if(glhTargetWnd!=glhPrevTarWnd)

   {

   char szCaption[100];

   GetWindowText(glhTargetWnd,szCaption,100);

   // 取目标窗口标题

   if(IsWindow(glhDisplayWnd))

   SendMessage(glhDisplayWnd,WM_SETTEXT,0,(LPARAM)(LPCTSTR)szCaption);

   glhPrevTarWnd=glhTargetWnd;

   // 保存目标窗口

   }

   }

   return CallNextHookEx(glhHook,nCode,wparam,lparam);

   // 继续传递消息

   }

   (10) 编译项目生成 mousehook.dll

   2 .创建钩子可执行程序

   (1) MFC AppWizard(EXE) 创建项目 Mouse

   (2) 选择 基于对话应用 并按下 完成 键;

   (3) 编辑对话框,删除其中原有的两个按钮,加入静态文本框和编辑框,用鼠标右键点击静态文本框,在弹出的菜单中选择 属性 ,设置其标题为 鼠标所在的窗口标题

   (4) Mouse.h 中加入对 Mousehook.h 的包含语句 #Include"../Mousehook/Mousehook.h"

   (5) CMouseDlg.h CMouseDlg 类定义中添加私有数据成员:

   CMouseHook m_hook;// 加入钩子类作为数据成员

   (6) 修改 CmouseDlg::OnInitDialog() 函数:

   BOOL CMouseDlg::OnInitDialog()

   {

   CDialog::OnInitDialog();

   ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

   ASSERT(IDM_ABOUTBOX < 0xF000);

   CMenu* pSysMenu = GetSystemMenu(FALSE);

   if (pSysMenu != NULL)

   {

   CString strAboutMenu;

   strAboutMenu.LoadString(IDS_ABOUTBOX);

   if (!strAboutMenu.IsEmpty())

   {

   pSysMenu->AppendMenu(MF_SEPARATOR);

   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

   }

   }

   SetIcon(m_hIcon, TRUE);//Set big icon

   SetIcon(m_hIcon, FALSE);//Set small icon

   //TODO: Add extra initialization here

   CWnd * pwnd=GetDlgItem(IDC_EDIT1);

   // 取得编辑框的类指针

   m_hook.starthook(pwnd->GetSafeHwnd());

   // 取得编辑框的窗口句柄并安装钩子

   return TRUE;

   //return TRUE unless you set the focus to a control

   }

   (7) 链接 DLL 库,即把 ../Mousehook/debug/Mousehook.lib 加入到项目设置链接标签中;

   (8) 编译项目生成可执行文件;

   (9) Mousehook.DLL 拷贝到 ../mouse/debug 目录中;

   (10) 先运行几个可执行程序,然后运行 Mouse.exe 程序,把鼠标在不同窗口中移动,在 Mouse.exe 程序窗口中的编辑框内将显示出鼠标所在的应用程序主窗口的标题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值