原文见:
http://www.sijiqing.com/vbgood/forum/forum_posts.asp?TID=13586&PN=1&TPN=1
tp非常遗憾的是这个dll不能吃掉鼠标消息,请问该如何处理?谢谢各位大虾

解决方案 »

  1.   

    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 
      

  2.   

    五.具体实现步骤   由于全局钩子函数必须包含在动态链接库中,所以本例由两个程序体来实现。   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;    //
    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(); //取得鼠标单击次数的函数
      

  3.   

    (4)修改Spy.cpp文件代码如下(黑体部分为添加内容)#include "stdafx.h"#include <afxdllx.h>#include "spyhook.h"……//省略部分机器生成代码#pragma data_seg("PublicData") //定义全局数据段HHOOK hhook=NULL; //钩子句柄HINSTANCE pInstance=NULL; //钩子模块句柄UINT MouseClick=0; //记录鼠标单击次数的变量#pragma data_seg()……//省略部分机器生成代码extern "C" int APIENTRYDllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved){ if (dwReason == DLL_PROCESS_ATTACH){ ……//省略部分机器生成代码new CDynLinkLibrary(SpyDLL);pInstance=hInstance; //取得模块句柄}else if (dwReason == DLL_PROCESS_DETACH){ TRACE0("SPY.DLL Terminating!\n");AfxTermExtensionModule(SpyDLL);}return 1; }extern "C" LRESULT CALLBACK MouseProc(int code,WPARAM wParam,LPARAM lParam) //钩子处理函数{ if (code < 0) //若code<0,直接调用CallNextHookEx返回return CallNextHookEx(hhook, code, wParam, lParam);if(wParam==WM_LBUTTONDOWN){ MouseClick++; //记录鼠标单击次数}return CallNextHookEx(hhook, code, wParam,lParam);}extern "C" BOOL WINAPI StartHook() //启动钩子函数{ hhook=SetWindowsHookEx(WH_MOUSE,MouseProc,pInstance,0); //挂上钩子if(hhook!=NULL)return TRUE;else return FALSE;}extern "C" BOOL WINAPI StopHook() //撤销钩子函数{ return UnhookWindowsHookEx(hhook); //撤销钩子}extern "C" int WINAPI GetResult() //返回鼠标单击次数{ return MouseClick;}(5)修改Spy.def文件如下LIBRARY "SPY"DEs criptION 'SPY Windows Dynamic Link Library'EXPORTSStartHook @1StopHook @2GetResult @3(6)编译Project,生成Spy.dll文件和Spy.Lib文件2、建立使用钩子的应用程序
    生成一个单文档的可执行文件(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放到生成的可执行文件的目录下,便可运行程序。运行时,选择“监控”菜单中的“启动”菜单项,钩子便开始工作,监视鼠标的活动情况;选择“撤销”菜单项,系统便撤销钩子;选择“取出”菜单项,程序便报告在监控期间,用户单击鼠标左键的次数。
      

  4.   

    Delphi版:
    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.