知识大全 深入探讨.NET中的钩子技术

Posted 系统

篇首语:须知少年凌云志,曾许人间第一流。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 深入探讨.NET中的钩子技术相关的知识,希望对你有一定的参考价值。

深入探讨.NET中的钩子技术  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  一 介绍        本文将讨论在 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 根据这些类所设置的模型 你能容易构建系统钩子 针对Win API中任何 种钩子事件类型中的任何一种 另外 这个完整的类库中还有一个编译的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( 鼠标事件: :( ) 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;          //                  使用该类库的情况就是如此 该类库中有两个系统钩子类并且相当容易扩充

  四 构建库        这个库共有两个主要组件 第一部分是一个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)        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 =    JournalPlayback =    //    KeyboardLL =    MouseLL =   ;        首先 我们使用DllImport属性导入SetUserHookCallback函数 作为我们的抽象基钩子类SystemHook的一个静态的外部的方法 为此 我们必须映射一些外部数据类型 首先 我们必须创建一个代理作为我们的函数指针 这是通过定义上面的HookProcessHandler来实现的 我们需要一个函数 它的C++签名为(int WPARAM LPARAM) 在Visual Studio NET C++编译器中 int与C#中是一样的 也就是说 在C++与C#中int就是Int 事情并不总是这样 一些编译器把C++ int作为Int 对待 我们坚持使用Visual Studio NET C++编译器来实现这个工程 因此 我们不必担心编译器差别所带来的另外的定义        接下来 我们需要用C#传递WPARAM和LPARAM值 这些确实是指针 它们分别指向C++的UINT和LONG值 用C#来说 它们是指向uint和int的指针 如果你还不确定什么是WPARAM 你可以通过在C++代码中单击右键来查询它 并且选择 Go to definition 这将会引导你到在windef h中的定义      //从windef h:   typedef UINT_PTR WPARAM;   typedef LONG_PTR LPARAM;        因此 我们选择System UIntPtr和System IntPtr作为我们的变量类型 它们分别相应于WPARAM和LPARAM类型 当它们使用在C#中时   现在 让我们看一下钩子基类是怎样使用这些导入的方法来传递一个回叫函数(代理)到C++中 它允许C++库直接调用你的系统钩子类的实例 首先 在构造器中 SystemHook类创建一个到私有方法InternalHookCallback的代理 它匹配HookProcessedHandler代理签名 然后 它把这个代理和它的HookType传递到C++库以使用SetUserHookCallback方法来注册该回叫函数 如上面所讨论的 下面是其代码实现      public SystemHook(HookTypes type)    _type = type;    _processHandler = new HookProcessedHandler(InternalHookCallback);    SetUserHookCallback(_processHandler _type);           InternalHookCallback的实现相当简单 InternalHookCallback在用一个catch all try/catch块包装它的同时仅传递到抽象方法HookCallback的调用 这将简化在派生类中的实现并且保护C++代码 记住 一旦一切都准备妥当 这个C++钩子就会直接调用这个方法      [MethodImpl(MethodImplOptions NoInlining)]   private void InternalHookCallback(int code UIntPtr wparam IntPtr lparam)   try HookCallback(code wparam lparam);   catch           我们已增加了一个方法实现属性 它告诉编译器不要内联这个方法 这不是可选的 至少 在我添加try/catch之前是需要的 看起来 由于某些原因 编译器在试图内联这个方法 这将给包装它的代理带来各种麻烦 然后 C++层将回叫 而该应用程序将会崩溃        现在 让我们看一下一个派生类是怎样用一个特定的HookType来接收和处理钩子事件 下面是虚拟的MouseHook类的HookCallback方法实现      protected override void HookCallback(int code UIntPtr wparam IntPtr lparam)    if (MouseEvent == null) return;     int x = y = ;     MouseEvents mEvent = (MouseEvents)wparam ToUInt ();     switch(mEvent)      case MouseEvents LeftButtonDown:       GetMousePosition(wparam lparam ref x ref y);       break;      //         MouseEvent(mEvent new Point(x y));           首先 注意这个类定义一个事件MouseEvent 该类在收到一个钩子事件时激发这个事件 这个类在激发它的事件之前 把数据从WPARAM和LPARAM类型转换成 NET中有意义的鼠标事件数据 这样可以使得类的消费者免于担心解释这些数据结构 这个类使用导入的GetMousePosition函数 我们在C++ DLL中定义的用来转换这些值 为此 请看下面几段的讨论        在这个方法中 我们检查是否有人在听这一个事件 如果没有 不必继续处理这一事件 然后 我们把WPARAM转换成一个MouseEvents枚举类型 我们已小心地构造了MouseEvents枚举来准确匹配它们在C++中相应的常数 这允许我们简单地把指针的值转换成枚举类型 但是要注意 这种转换即使在WPARAM的值不匹配一个枚举值的情况下也会成功 mEvent的值将仅是未定义的(不是null 只是不在枚举值范围之内) 为此 请详细分析System Enum IsDefined方法        接下来 在确定我们收到的事件类型后 该类激活这个事件 并且通知消费者鼠标事件的类型及在该事件过程中鼠标的位置        最后注意 有关转换WPARAM和LPARAM值 对于每个类型的事件 这些变量的值和意思是不同的 因此 在每一种钩子类型中 我们必须区别地解释这些值 我选择用C++实现这种转换 而不是尽量用C#来模仿复杂的C++结构和指针 例如 前面的类就使用了一个叫作GetMousePosition的C++函数 下面是C++ DLL中的这个方法      bool GetMousePosition(WPARAM wparam LPARAM lparam int & x int & y)    MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lparam;    x = pMouseStruct >pt x;    y = pMouseStruct >pt y;    return true;           不是尽量映射MOUSEHOOKSTRUCT结构指针到C# 我们简单地暂时把它回传到C++层以提取我们需要的值 注意 因为我们需要从这个调用中返回一些值 我们把我们的整数作为参考变量传递 这直接映射到C#中的int* 但是 我们可以重载这个行为 通过选择正确的签名来导入这个方法      private static extern bool InternalGetMousePosition(UIntPtr wparam IntPtr lparam ref int x ref int y)        通过把integer参数定义为ref int 我们得到通过C++参照传递给我们的值 如果我们想要的话 我们还可以使用out int  

cha138/Article/program/net/201311/11904

相关参考

知识大全 深入探讨.NET中的钩子技术[2]

深入探讨.NET中的钩子技术[2]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  三使用代码  

知识大全 深入探讨.NET中的钩子技术[3]

深入探讨.NET中的钩子技术[3]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &nb

知识大全 深入探讨.NET中的钩子技术[5]

深入探讨.NET中的钩子技术[5]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &nb

知识大全 深入探讨.NET中的钩子技术[4]

深入探讨.NET中的钩子技术[4]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在这个方法中我

知识大全 浅谈VB.NET中的跨进程消息钩子

浅谈VB.NET中的跨进程消息钩子  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  我们都知道在V

知识大全 ASP.NET Web Page应用深入探讨

ASP.NETWebPage应用深入探讨  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  一服务器

知识大全 深入了解ASP.NET中的“空”

深入了解ASP.NET中的“空”  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  空对于许多开发人

知识大全 探讨.NET中的联合结构

探讨.NET中的联合结构  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  导读在进行某些算法的C语

知识大全 深入分析ADO.NET中的DataSet对象

深入分析ADO.NET中的DataSet对象  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  AD

知识大全 如何理解.Net的三层架构

深入探讨:如何理解.Net的三层架构  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  各层的作用