原文见:
http://www.sijiqing.com/vbgood/forum/forum_posts.asp?TID=13586&PN=1&TPN=1
tp非常遗憾的是这个dll不能吃掉鼠标消息,请问该如何处理?谢谢各位大虾
http://www.sijiqing.com/vbgood/forum/forum_posts.asp?TID=13586&PN=1&TPN=1
tp非常遗憾的是这个dll不能吃掉鼠标消息,请问该如何处理?谢谢各位大虾
·贺成士 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
Windows的钩子机制
我们知道,Windows系统是建立在消息传递机制的基础上的,几乎所有的程序活动都由消息来驱动。钩子机制可以看作是一个消息中转站,控制系统发出的消息的处理和传递。利用钩子,我们可以截获系统发给应用程序的消息,经过处理后决定是否将消息再发给下一个应用程序。利用钩子的这一特性,我们可以创建一个监控程序,收集和控制系统发出的消息。
Windows钩子程序的编制 编制Windows的钩子程序,需要用到几个SDK中的API函数。下面列出这几个函数的原型及说明:HHOOK SetWindowsHookEx( int idHook, HOOK_PROC lpfn, HINSTANCE hMod,DWORD dwThreadID);参数说明: idHook :钩子的类型lpfn :钩子处理函数地址hMod :包含钩子函数的模块句柄dwThreadID :钩子的监控线程函数说明:函数将在系统中挂上一个由idHook指定类型的钩子,监控并处理相应的特定消息。BOOL UnhookWindowsHookEx( HHOOK hhk );函数说明:函数将撤销由hhk指定的钩子。LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam,LPARAM lParam );函数说明:函数将消息向下传递,下一个钩子处理将截获这一消息。由于钩子的处理涉及到模块及进程间的数据地址问题,一般处理是把钩子整合到一个动态链接库(DLL)中,并设立一个全局数据共享数据段,以存贮一些全局变量,保留上次钩子消息事件发生时的状态。全局共享数据段可以用如下的格式定义:#pragma data_seg("PublicData")HHOOK hhook=NULL; //全局共享数据#pragma data_seg()在本文所附带的范例程序中,演示了如何编制一个鼠标钩子(WH_MOUSE)程序。这个程序监视了Windows系统的鼠标消息,在监控期间,程序可以用户单击鼠标左键的次数。其它类型的钩子程序的编写过程与范例程序类似。
范例程序的建立与代码分析 正如上面所说的,建立钩子程序时需要把钩子处理整合到动态链接库中,所以例程中需要建立两个Project。
建立钩子处理动态链接库
选择MFC AppWizard(DLL)创建一个新Project,命名为”Spy”;
选择MFC Extension DLL类型 (3)创建一个新的头文件,命名为”Hook.h”,修改它的代码如下extern "C" LRESULT CALLBACK MouseProc(int code,WPARAM wParam,LPARAM lParam); //钩子处理函数extern "C" BOOL WINAPI StartHook(); //启动钩子函数extern "C" BOOL WINAPI StopHook(); //撤销钩子函数extern "C" int WINAPI GetResult(); //取得鼠标单击次数的函数
生成一个单文档的可执行文件(EXE)的Project
修改资源中的主菜单,增加一个菜单项“监控”,下有三个子菜单项,分别为“启动”,“撤销”,“取出”
在Project中加入Spy.Lib文件和Hook.h文件
分别修改“启动”,“撤销”,“取出”菜单项的Command响应函数如下: #include “hook.h”……//省略部分机器生成代码void CMainFrame::OnStartSpy() //”启动”菜单项的响应函数{ StartHook();}void CMainFrame::OnReleaseSpy() //”撤销“菜单项的响应函数{ StopHook();}void CMainFrame::OnGet() //“取出”菜单项的响应函数{ int Result=GetResult();char buffer[40];wsprintf(buffer,"在程序运行期间,你共单击鼠标%d次",Result);::MessageBox(this->m_hWnd,buffer,"Message",MB_OK); }
编译这个Project,并把Spy.dll放到生成的可执行文件的目录下,便可运行程序。运行时,选择“监控”菜单中的“启动”菜单项,钩子便开始工作,监视鼠标的活动情况;选择“撤销”菜单项,系统便撤销钩子;选择“取出”菜单项,程序便报告在监控期间,用户单击鼠标左键的次数。
library MouseHook;uses
SysUtils,
WinTypes,
WinProcs,
Messages;{$R *.res}
const
LBDOWN=WM_USER+WM_LBUTTONDOWN;
LBUP=WM_USER+WM_LBUTTONUP;
var
IsHooked:boolean;
HookHandle:hhook;
DesktopWin:hwnd;
hWndLong:longint;
function HookProc(Code:integer;wParam:wparam;lParam:lparam):LRESULT;stdcall;
var
cur:TPoint;
begin
if(Code=HC_Action)then
if(wParam=WM_LBUTTONDOWN)then
begin
MessageBeep(MB_ICONASTERISK);
GetCursorPos(Cur);
SendMessage(hWndLong,LBDOWN,WindowFromPoint(Cur),lParam);
end; if(Code=HC_Action)then
if(wParam=WM_LBUTTONUP)then
begin
MessageBeep(MB_ICONASTERISK);
GetCursorPos(Cur);
SendMessage(hWndLong,LBUP,WindowFromPoint(Cur),lParam);
end; Result:=CallNextHookEx(HookHandle,Code,wParam,lParam);
end;function RegHook(FunCallEx:longint):boolean;stdcall;//********************************
begin
Result:=false;
if ishooked then
exit;
hWndLong:=FunCallEx;
Desktopwin:=GetDesktopWindow;
HookHandle:=SetWindowsHookEx(WH_MOUSE,HookProc,HInstance,0);
Result:=HookHandle<>0;
end;function RemoveHook:boolean;stdcall;//***************************
begin
Result:=false;
if(not IsHooked)and(HookHandle<>0)then
Result:=UnhookWindowsHookEx(HookHandle);
IsHooked:=false;
end;exports
RegHook,RemoveHook,HookProc;begin
IsHooked:=false;
end.