深入探讨.NET中的钩子技术

分类: VC.NET   出处:iocblog整理  更新时间:2008-10-26   添加到收藏  

    一、 介绍
  
    本文将讨论在.net应用程序中全局系统钩子的使用。为此,我开发了一个可重用的类库并创建一个相应的示例程序(见下图)。
  
  
  
    你可能注意到另外的关于使用系统钩子的文章。本文与之类似但是有重要的差别。这篇文章将讨论在.net中使用全局系统钩子,而其它文章仅讨论本地系统钩子。这些思想是类似的,但是实现要求是不同的。
  
    二、 背景
  
    如果你对windows系统钩子的概念不熟悉,让我作一下简短的描述:
  
    ・一个系统钩子允许你插入一个回调函数-它拦截某些windows消息(例如,鼠标相联系的消息)。
  
    ・一个本地系统钩子是一个系统钩子-它仅在指定的消息由一个单一线程处理时被调用。
  
    ・一个全局系统钩子是一个系统钩子-它当指定的消息被任何应用程序在整个系统上所处理时被调用。
  已有若干好文章来介绍系统钩子概念。在此,不是为了重新收集这些介绍性的信息,我只是简单地请读者参考下面有关系统钩子的一些背景资料文章。如果你对系统钩子概念很熟悉,那么你能够从本文中得到你能够得到的任何东西。
  
    ・关于msdn库中的钩子知识。
  
    ・dino esposito的《cutting edge-windows hooks in the .net framework》。
  
    ・don kackman的《在c#中应用钩子》。
  
    本文中我们要讨论的是扩展这个信息来创建一个全局系统钩子-它能被.net类所使用。我们将用c#和一个dll和非托管c++来开发一个类库-它们一起将完成这个目标。
  
    三、 使用代码
  
    在我们深入开发这个库之前,让我们快速看一下我们的目标。在本文中,我们将开发一个类库-它安装全局系统钩子并且暴露这些由钩子处理的事件,作为我们的钩子类的一个.net事件。为了说明这个系统钩子类的用法,我们将在一个用c#编写的windows表单应用程序中创建一个鼠标事件钩子和一个键盘事件钩子。
  
    这些类库能用于创建任何类型的系统钩子,其中有两个预编译的钩子-mousehook和keyboardhook。我们也已经包含了这些类的特定版本,分别称为mousehookext和keyboardhookext。根据这些类所设置的模型,你能容易构建系统钩子-针对win32 api中任何15种钩子事件类型中的任何一种。另外,这个完整的类库中还有一个编译的html帮助文件-它把这些类归档化。请确信你看了这个帮助文件-如果你决定在你的应用程序中使用这个库的话。
  
    mousehook类的用法和生命周期相当简单。首先,我们创建mousehook类的一个实例。
  
  mousehook = new mousehook();//mousehook是一个成员变量
  
    接下来,我们把mouseevent事件绑定到一个类层次的方法上。
  
  mousehook.mouseevent+=new mousehook.mouseeventhandler(mousehook_mouseevent);
  // ...
  private void mousehook_mouseevent(mouseevents mevent, int x, int y){
   string msg =string.format("鼠标事件:{0}:({1},{2}).",mevent.tostring(),x,y);
   addtext(msg);//增加消息到文本框
  }
  
    为开始收到鼠标事件,简单地安装下面的钩子即可。
  
  mousehook.installhook();
  
    为停止接收事件,只需简单地卸载这个钩子。
  
  mousehook.uninstallhook();
  
    你也可以调用dispose来卸载这个钩子。
  
    在你的应用程序退出时,卸载这个钩子是很重要的。让系统钩子一直安装着将减慢系统中的所有的应用程序的消息处理。它甚至能够使一个或多个进程变得很不稳定。因此,请确保在你使用完钩子时一定要移去你的系统钩子。我们确定在我们的示例应用程序会移去该系统钩子-通过在form的dispose方法中添加一个dispose调用。
  
  protected override void dispose(bool disposing) {
   if (disposing) {
    if (mousehook != null) {
     mousehook.dispose();
     mousehook = null;
    }
    // ...
   }
  }
  
  
    使用该类库的情况就是如此。该类库中有两个系统钩子类并且相当容易扩充。[www.iocblog.net 来源]

      四、 构建库
  
    这个库共有两个主要组件。第一部分是一个C#类库-你可以直接使用于你的应用程序中。该类库,反过来,在内部使用一个非托管的C++ DLL来直接管理系统钩子。我们将首先讨论开发该C++部分。接下来,我们将讨论怎么在C#中使用这个库来构建一个通用的钩子类。就象我们讨论C++/C#交互一样,我们将特别注意C++方法和数据类型是怎样映射到.NET方法和数据类型的。
  
     你可能想知道为什么我们需要两个库,特别是一个非托管的C++ DLL。你还可能注意到在本文的背景一节中提到的两篇参考文章,其中并没有使用任何非托管的代码。为此,我的回答是,"对!这正是我写这篇文章的原因"。当你思考系统钩子是怎样实际地实现它们的功能时,我们需要非托管的代码是十分重要的。为了使一个全局的系统钩子能够工作,Windows把你的DLL插入到每个正在运行的进程的进程空间中。既然大多数进程不是.NET进程,所以,它们不能直接执行.NET装配集。我们需要一种非托管的代码代理 -Windows可以把它插入到所有将要被钩住的进程中。
  
    首先是提供一种机制来把一个.NET代理传递到我们的C++库。这样,我们用C++语言定义下列函数(SetUserHookCallback)和函数指针(HookProc)。
  
  int SetUserHookCallback(HookProc userProc, UINT hookID)
  typedef void (CALLBACK *HookProc)(int code, WPARAM w, LPARAM l)
   [www.iocblog.net 来源]
    SetUserHookCallback的第二个参数是钩子类型-这个函数指针将使用它。现在,我们必须用C#来定义相应的方法和代理以使用这段代码。下面是我们怎样把它映射到C#。
  
  private static extern SetCallBackResults
  SetUserHookCallback(HookProcessedHandler hookCallback, HookTypes hookType)
  protected delegate void HookProcessedHandler(int code, UIntPtr wparam, IntPtr lparam)
  public enum HookTypes {
   JournalRecord = 0,
   JournalPlayback = 1,
   // ...
   KeyboardLL = 13,
   MouseLL = 14
  };
  
     首先,我们使用DllImport属性导入SetUserHookCallback函数,作为我们的抽象基钩子类SystemHook的一个静态的外部的方法。为此,我们必须映射一些外部数据类型。首先,我们必须创建一个代理作为我们的函数指针。这是通过定义上面的 HookProcessHandler来实现的。我们需要一个函数,它的C++签名为(int,WPARAM,LPARAM)。在Visual Studio .NET C++编译器中,int与C#中是一样的。也就是说,在C++与C#中int就是Int32。事情并不总是这样。一些编译器把C++ int作为Int16对待。我们坚持使用Visual Studio .NET C++编译器来实现这个工程,因此,我们不必担心编译器差别所带来的另外的定义。 
   

[1] [2] [3] 下一页


Tag: 钩子技术 ,hook



文章整理:iocblog
版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。