• 用VC++编写勾子程序(转)


    在Windows系统中,勾子(hook)是一种特殊的消息处理机制。勾子可以监视系统或进程中的各种事件消息,截获发往目标窗口的消息并进行处理。这样,我们就可以在系统中安装自定义的勾子,监视系统中特定事件的发生,完成特定的功能,比如截获键盘、鼠标的输入,屏幕取词,日志监视等等。可见,利用勾子可以实现许多特殊而有用的功能。因此,对于高级编程人员来说,掌握勾子的编程方法是很有必要的。

    勾子的类型
      1. 按事件分类,有如下的几种常用类型

      (1) 键盘勾子和低级键盘勾子可以监视各种键盘消息。

      (2) 鼠标勾子和低级鼠标勾子可以监视各种鼠标消息。

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

      (4) 日志勾子可以记录从系统消息队列中取出的各种事件消息。

      (5) 窗口过程勾子监视所有从系统消息队列发往目标窗口的消息。

      此外,还有一些特定事件的勾子提供给我们使用,不一一列举。

      2. 按使用范围分类,主要有线程勾子和系统勾子

      (1) 线程勾子监视指定线程的事件消息。

      (2) 系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。这是系统勾子和线程勾子很大的不同之处。

       几点需要说明的地方:

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

      (2) 对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。

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

    编写勾子程序
       编写勾子程序的步骤分为三步:定义勾子函数、安装勾子和卸载勾子。

      1.定义勾子函数

      勾子函数是一种特殊的回调函数。勾子监视的特定事件发生后,系统会调用勾子函数进行处理。不同事件的勾子函数的形式是各不相同的。下面以鼠标勾子函数举例说明勾子函数的原型:

    LRESULT CALLBACK MouseProc
    ( int nCode, WPARAM wParam, LPARAM lParam)

      参数wParam和 lParam包含鼠标消息的信息,比如鼠标位置、状态等。nCode包含有关消息本身的信息,比如是否从消息队列中移出。

       我们先在勾子函数中实现自定义的功能,然后调用函数 CallNextHookEx.把勾子信息传递给勾子链的下一个勾子函数。CallNextHookEx.的原型如下:

    LRESULT CallNextHookEx
    ( HHOOK hhk, int nCode,
    WPARAM wParam, LPARAM lParam )

      参数 hhk是勾子句柄。nCode、wParam和lParam 是勾子函数。下面的例子是一个简单的监视鼠标消息的勾子函数:

    LRESULT CALLBACK MouseProc
    (int nCode, WPARAM wParam, LPARAM lParam)
    {
    //跟踪鼠标移动时的位置变化信息
    if(wParam==WM_MOUSEMOVE || wParam==
    WM_NCMOUSEMOVE)
    { //是鼠标移动消息
    point=((MOUSEHOOKSTRUCT *)
    lParam)->pt;//取鼠标信息
    TRACE2(“Position is :%d,Y:%d\n",point.x,point.y);
    }
    //传递勾子信息
    return CallNextHookEx(hHook,nCode,wParam,lParam);
    }

      2.安装勾子

      在程序初始化的时候,调用函数SetWindowsHookEx安装勾子。其函数原型为:

    HHOOK SetWindowsHookEx
    ( int idHook,HOOKPROC lpfn,
    INSTANCE hMod,DWORD dwThreadId )

      参数idHook表示勾子类型,它是和勾子函数类型一一对应的。比如,WH_KEYBOARD表示安装的是键盘勾子,WH_MOUSE表示是鼠标勾子等等。

      Lpfn是勾子函数的地址。

      HMod是勾子函数所在的实例的句柄。对于线程勾子,该参数为NULL;对于系统勾子,该参数为勾子函数所在的DLL句柄。

       dwThreadId 指定勾子所监视的线程的线程号。对于全局勾子,该参数为NULL。

      SetWindowsHookEx返回所安装的勾子句柄。

      3.卸载勾子

       当不再使用勾子时,必须及时卸载。简单地调用函数 BOOL UnhookWindowsHookEx( HHOOK hhk)即可。

       值得注意的是线程勾子和系统勾子的勾子函数的位置有很大的差别。线程勾子一般在当前线程或者当前线程派生的线程内,而系统勾子必须放在独立的动态链接库中,实现起来要麻烦一些。

    线程勾子的编程实例
      按照上面介绍的方法实现一个线程级的鼠标勾子。勾子跟踪当前窗口鼠标移动的位置变化信息。并输出到窗口。

      (1)在VC++6.0中利用MFC APPWizard(EXE)生成一个不使用文档/视结构的单文档应用mousehook。打开childview.cpp文件,加入全局变量:

    HHOOK hHook;//鼠标勾子句柄
    CPoint point;//鼠标位置信息
    CChildView *pView;
    // 鼠标勾子函数用到的输出窗口指针

      在CChildView::OnPaint()添加如下代码:

    CPaintDC dc(this);
    char str[256];
    sprintf(str,“x=%d,y=%d",point.x,point.y);
    //构造字符串
    dc.TextOut(0,0,str); //显示字符串

      (2)childview.cpp文件中定义全局的鼠标勾子函数。

    LRESULT CALLBACK MouseProc
    (int nCode, WPARAM wParam, LPARAM lParam)
    {//是鼠标移动消息
    if(wParam==WM_MOUSEMOVE||wParam
    ==WM_NCMOUSEMOVE)
    {
    point=((MOUSEHOOKSTRUCT *)lParam)->pt;
    //取鼠标信息
    pView->Invalidate(); //窗口重画
    }
    return CallNextHookEx(hHook,nCode,wParam,lParam);
    //传递勾子信息
    }
    (3)CChildView类的构造函数中安装勾子。
    CChildView::CChildView()
    {
    pView=this;//获得输出窗口指针
    hHook=SetWindowsHookEx
    (WH_MOUSE,MouseProc,0,GetCurrentThreadId());
    }
    (4)CChildView类的析构函数中卸载勾子。
    CChildView::~CChildView()
    {
    if(hHook)
    UnhookWindowsHookEx(hHook);
    }

    系统勾子的编程实例
       编写系统勾子与编写线程勾子的步骤相似,不过必须把勾子函数放在DLL中。因此,读者需要了解编写动态链接库的基础知识。这里的例子实现一个系统级的键盘勾子,监视系统中所有应用的键盘输入消息并记录在文件中。这样需要两个工程,一个是主程序,一个是动态链接库。

      (1)在DLL中定义键盘勾子函数。

      在VC++6.0中利用MFC APPWizard(DLL)生动态链接库工程keyhook。然后选择DLL 的类型为使用共享MFC DLL的常规DLL。打开 keyhook.cpp文件,加入全局说明:

    #pragma data_seg(“SharedVar")
    //DLL输出的全局变量
    extern “C" __declspec(dllexport) HHOOK hHook=NULL;
    #pragma data_seg()
    // DLL 自用的全局变量
    char buffer=0;
    FILE *fl;
    定义全局的键盘勾子函数:
    //DLL输出的勾子函数
    extern “C" __declspec(dllexport)
    LRESULT CALLBACK KeyboardProc(
    int nCode,
    WPARAM wParam, //虚键代码
    LPARAM lParam // 击键消息的信息
    )
    {
    if(lParam&0x80000000)
    {//如果某键按下
    buffer=MapVirtualKey(wParam,2);
    // 把虚键代码变为字符
    //输出到文件
    fl=fopen( “keyfile.txt", “a+" );
    fwrite(&buffer, sizeof(char), 1, fl);
    fclose(fl);
    }
    return CallNextHookEx(hHook,nCode,wParam,lParam);
    }

      编译得到keyhook.dll和库文件keyhook.lib 。使用 keyhook.lib是为了让主程序可以方便地隐式调用DLL中的输出函数。

       (2)在VC++6.0中利用MFC APPWizard(EXE)生成一个不使用文档/视结构单文档应用getkey。打开MainFrm.cpp文件。加入全局的输入变量和函数的说明:

    //要从DLL中输入的变量和函数
    extern “C" __declspec(dllimport) HHOOK hHook;//
    extern “C" __declspec(dllimport)
    LRESULT CALLBACK KeyboardProc
    (int nCode, WPARAM wParam, LPARAM lParam);

      (3)在CMainFrame类的构造函数中安装勾子。

    CMainFrame::CMainFrame()
    {
    hHook=SetWindowsHookEx(WH_KEYBOARD,
    KeyboardProc,
    GetModuleHandle(“keyhook.dll"),
    0);
    }

      (4)在CMainFrame类的析构函数中卸载勾子。
    CMainFrame::~CMainFrame()
    {
    if(hHook)
    UnhookWindowsHookEx(hHook);
    }

      编译这个工程的时候,注意包含第一步得到的库文件 keyhook.lib。keyhook.dll和getkey.exe应放在同一目录下运行

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/carl2380/archive/2009/11/04/4766273.aspx

  • 相关阅读:
    DataTables中自增序号的实现
    MVC中调用模态框之后导致JS失效
    teamviewer13破解版
    屏幕录制专家破解版
    Navicat Premium 12 (内含破解补丁)
    Navicat新建连接出现10060 "Unknown error" 错误
    Windows添加永久路由
    VMware虚拟机下Linux网络配置——桥接模式
    文件基本属性
    ifconfig: command not found
  • 原文地址:https://www.cnblogs.com/carl2380/p/2093330.html
Copyright © 2020-2023  润新知