学习之路三十八:Hook(钩子)的学习

好久没写文章了,还记得年前面试了一家公司,为了检测一下我的学习能力,给了我一个任务,做一个自动登录并自动操作菜单的程序。

花了几天的时间研究了Hook以及使用WindowsAPI操作程序的知识,现在记录一下,也算是一次温习。

一丶Hook

  在我看来Hook就是监测用户操作键盘(或虚拟键盘)以及鼠标的行为,对于Hook的理解我也不是很深入,也只是一点皮毛。

  1. 实现Hook的步骤

    ①安装钩子

    ②监测键盘和鼠标的操作,用来实现相应的逻辑

    ③卸载钩子

  2.安装钩子

    钩子分两种:键盘钩子和鼠标钩子,而每一种钩子又可以分为全局钩子或局部勾子。

    下面是安装钩子需要的Windows Message常量(网上找的)。

学习之路三十八:Hook(钩子)的学习
  1     public enum HookType : int

  2     {

  3         /// <summary>

  4         /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 

  5         ///条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 

  6         ///WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 

  7         ///过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook 

  8         ///监视所有应用程序消息。 

  9         /// 

 10         ///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 

 11         ///过滤消息,这等价于在主消息循环中过滤消息。 

 12         ///    

 13         ///通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 

 14         ///个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 

 15         ///环里一样

 16         /// </summary>

 17         WH_MSGFILTER = -1,

 18         /// <summary>

 19         /// WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这 

 20         ///个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook 

 21         ///来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样 

 22         ///使用。WH_JOURNALRECORD是system-wide local hooks,它们不会被注射到任何行 

 23         ///程地址空间

 24         /// </summary>

 25         WH_JOURNALRECORD = 0,

 26         /// <summary>

 27         /// WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可 

 28         ///以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠 

 29         ///标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘 

 30         ///事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定 

 31         ///Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处 

 32         ///理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实 

 33         ///时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它们不会被 

 34         ///注射到任何行程地址空间

 35         /// </summary>

 36         WH_JOURNALPLAYBACK = 1,

 37         /// <summary>

 38         /// 在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and  

 39         ///WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使 

 40         ///用这个Hook来监视输入到消息队列中的键盘消息

 41         /// </summary>

 42         WH_KEYBOARD = 2,

 43         /// <summary>

 44         /// 应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函 

 45         ///数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及 

 46         ///其它发送到消息队列中的消息

 47         /// </summary>

 48         WH_GETMESSAGE = 3,

 49         /// <summary>

 50         /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之前调用

 51         /// </summary>

 52         WH_CALLWNDPROC = 4,

 53         /// <summary>

 54         /// 在以下事件之前,系统都会调用WH_CBT Hook子过程,这些事件包括: 

 55         ///1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件; 

 56         ///2. 完成系统指令; 

 57         ///3. 来自系统消息队列中的移动鼠标,键盘事件; 

 58         ///4. 设置输入焦点事件; 

 59         ///5. 同步系统消息队列事件。

 60         ///Hook子过程的返回值确定系统是否允许或者防止这些操作中的一个

 61         /// </summary>

 62         WH_CBT = 5,

 63         /// <summary>

 64         /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动 

 65         ///条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 

 66         ///WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通 

 67         ///过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook 

 68         ///监视所有应用程序消息。 

 69         /// 

 70         ///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 

 71         ///过滤消息,这等价于在主消息循环中过滤消息。 

 72         ///    

 73         ///通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 

 74         ///个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循 

 75         ///环里一样

 76         /// </summary>

 77         WH_SYSMSGFILTER = 6,

 78         /// <summary>

 79         /// WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。 

 80         ///使用这个Hook监视输入到消息队列中的鼠标消息

 81         /// </summary>

 82         WH_MOUSE = 7,

 83         /// <summary>

 84         /// 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时

 85         /// </summary>

 86         WH_HARDWARE = 8,

 87         /// <summary>

 88         /// 在系统调用系统中与其它Hook关联的Hook子过程之前,系统会调用 

 89         ///WH_DEBUG Hook子过程。你可以使用这个Hook来决定是否允许系统调用与其它 

 90         ///Hook关联的Hook子过程

 91         /// </summary>

 92         WH_DEBUG = 9,

 93         /// <summary>

 94         /// 外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是 

 95         ///激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子过程。 

 96         ///WH_SHELL 共有5钟情况: 

 97         ///1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁; 

 98         ///2. 当Taskbar需要重画某个按钮; 

 99         ///3. 当系统需要显示关于Taskbar的一个程序的最小化形式; 

100         ///4. 当目前的键盘布局状态改变; 

101         ///5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。 

102         ///

103         ///按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接 

104         ///收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自 

105         ///106         /// </summary>

107         WH_SHELL = 10,

108         /// <summary>

109         /// 当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE  

110         ///Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就 

111         ///会调用WH_FOREGROUNDIDLE Hook子过程

112         /// </summary>

113         WH_FOREGROUNDIDLE = 11,

114         /// <summary>

115         /// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之后调用

116         /// </summary>

117         WH_CALLWNDPROCRET = 12,

118         /// <summary>

119         /// 监视输入到线程消息队列中的键盘消息

120         /// </summary>

121         WH_KEYBOARD_LL = 13,

122         /// <summary>

123         /// 监视输入到线程消息队列中的鼠标消息

124         /// </summary>

125         WH_MOUSE_LL = 14

126     }
View Code

     而用的最多的也就是:WH_KEYBOARD,WH_MOUSE, WH_KEYBOARD_LL,WH_MOUSE_LL。

     WH_KEYBOARD和WH_MOUSE是全局钩子,而WH_KEYBOARD_LL和WH_MOUSE_LL是针对某个线程的。

     所以说安装全局还是局部钩子取决于传入的消息常量。

     安装钩子需要调用的API:

 1         /// <summary>

 2         /// 安装勾子

 3         /// </summary>

 4         /// <param name="idHook">钩子类型,此处用整形的枚举表示</param>

 5         /// <param name="hookCallBack">钩子发挥作用时的回调函数</param>

 6         /// <param name="moudleHandle">应用程序实例的模块句柄(一般来说是你钩子回调函数所在的应用程序实例模块句柄)</param>

 7         /// <param name="threadID">与安装的钩子子程相关联的线程的标识符

 8         /// <remarks>如果线程ID是0则针对系统级别的,否则是针对当前线程</remarks>

 9         /// </param>

10         /// <returns>返回钩子句柄</returns>

11         [DllImport("user32.dll")]

12         public static extern int SetWindowsHookEx(int idHook, HookProcCallBack hookCallBack, IntPtr moudleHandle, int threadID);

13 

14         public delegate int HookProcCallBack(int nCode, int wParam, IntPtr lParam);

     ☆:上面方法的第二个需要是个委托参数,必须把它设置为静态变量,因为监测钩子相当于一个定时器一直在跑,如果委托变量不是静态的话,会被GC给回收掉的。

  3.监测键盘和鼠标行为

    键盘操作分为:keyDown,keyPress,keyUp;鼠标操作分为:rightClick,leftClick,doubleClick,wheel,move。

    所以为了要监测上面的所有行为,需要使用事件来实现。

  4.卸载钩子

    主要还是调用API就可以了。

1         /// <summary>

2         /// 卸载勾子

3         /// </summary>

4         /// <param name="handle">要取消的钩子的句柄</param>

5         /// <returns>卸载钩子是否成功</returns>

6         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

7         public static extern bool UnhookWindowsHookEx(int handle); 

  5.HookManager

    发现写不下去,不知道该讲什么了,很多细节上都没有讲到,比如每个参数的含义,怎么调用等等,算是给出个思路,我也是下载了很多源码摸索过来的。

    给出主要实现代码:

学习之路三十八:Hook(钩子)的学习
  1 using System;

  2 using System.ComponentModel;

  3 using System.Diagnostics;

  4 using System.Runtime.InteropServices;

  5 using System.Windows.Forms;

  6 

  7 using SharpCommon.Windows;

  8 

  9 /*

 10  

 11  * 2014-1-28 完善第一个版本

 12  * 

 13  *  1.关于KeyPress的解释

 14  *      在控件有焦点的情况下按下键时发生。

 15  *      键事件按下列顺序发生: 

 16             KeyDown

 17             KeyPress

 18             KeyUp

 19 

 20             非字符键不会引发 KeyPress 事件;但非字符键却可以引发 KeyDown 和 KeyUp 事件。

 21             使用 KeyChar 属性在运行时对键击进行取样,并且使用或修改公共键击的子集。

 22             若要仅在窗体级别处理键盘事件而不允许其他控件接收键盘事件,

 23  *         请将窗体的 KeyPress 事件处理方法中的 KeyPressEventArgs.Handled 属性设置为 true。

 24  *         

 25  *      摘自MSDN上的说明

 26  *      KeyPressEventArgs 指定在用户按键时撰写的字符。例如,当用户按 Shift + K 时,KeyChar 属性返回一个大写字母 K。

 27          当用户按下任意键时,发生 KeyPress 事件。与 KeyPress 事件紧密相关的两个事件为 KeyUp 和 KeyDown。

 28  *      当用户按下某个键时,KeyDown 事件先于每个 KeyPress 事件发生;当用户释放某个键时发生 KeyUp 事件。

 29  *      当用户按住某个键时,每次字符重复时,KeyDown 和 KeyPress 事件也都重复发生。一个 KeyUp 事件在释放按键时生成。

 30 

 31          KeyPressEventArgs 随着 KeyPress 事件的每次发生而被传递。

 32  *      KeyEventArgs 随着 KeyDown 和 KeyUp 事件的每次发生而被传递。

 33  *      KeyEventArgs 指定是否有任一个组合键(Ctrl、Shift 或 Alt)在另一个键按下的同时也曾按下。

 34  *      此修饰符信息也可以通过 Control 类的 ModifierKeys 属性获得。

 35 

 36          将 Handled 设置为 true,以取消 KeyPress 事件。这可防止控件处理按键。

 37 

 38          注意注意: 

 39          有些控件将会在 KeyDown 上处理某些击键。

 40  *      例如,RichTextBox 在调用 KeyPress 前处理 Enter 键。

 41  *      在这种情况下,您无法取消 KeyPress 事件,而是必须从 KeyDown 取消击键。

 42  *      

 43  * 

 44  *  2014-1-28 1:00 PM

 45  *      1. 完成了对组合键的监测代码,通过获取KeyState来判断是否按了组合键

 46  

 47  */

 48 

 49 namespace SharpCommon.Hook

 50 {

 51     public sealed class HookManager

 52     {

 53         #region Event And Field

 54         public event CustomKeyEventHandler KeyUp;

 55         public event CustomKeyEventHandler KeyDown;

 56         public event CustomKeyEventHandler KeyPress;

 57 

 58         public event MouseEventHandler MouseMove;

 59         public event MouseEventHandler MouseWheel;

 60         public event MouseEventHandler LeftMouseClickUp;

 61         public event MouseEventHandler RightMouseClickUp;

 62         public event MouseEventHandler LeftMouseClickDown;

 63         public event MouseEventHandler RightMouseClickDown;

 64         public event MouseEventHandler LeftMouseDoubleClick;

 65         public event MouseEventHandler RightMouseDoubleClick;

 66 

 67         private static int _mouseHookHandle;

 68         private static int _keyboardHookHandlel;

 69 

 70         private static HookProcCallBack _mouseHookCallBack;

 71         private static HookProcCallBack _keyboardHookCallBack;

 72 

 73         private static readonly HookManager _instance = new HookManager();

 74 

 75         private static readonly int _currentThreadID = AppDomain.GetCurrentThreadId();

 76         private static readonly IntPtr _currentMoudleHandle = WindowsAPI.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);

 77         

 78         #endregion

 79 

 80         #region Instance

 81 

 82         private HookManager()

 83         { }

 84 

 85         public static HookManager Instance

 86         {

 87             get { return _instance; }

 88         } 

 89 

 90         #endregion

 91 

 92         #region Install Hook

 93 

 94         /// <summary>

 95         /// Install the hook.

 96         /// </summary>

 97         /// <param name="installType">Select the hook install type.</param>

 98         public void InstallHook(HookInstallType installType = HookInstallType.MouseAndKeyBoard)

 99         {

100             switch (installType)

101             {

102                 case HookInstallType.Mouse:

103                     this.InstallMouseHook();

104                     break;

105                 case HookInstallType.KeyBoard:

106                     this.InstallKeyBoardHook();

107                     break;

108                 case HookInstallType.MouseAndKeyBoard:

109                     this.InstallMouseHook();

110                     this.InstallKeyBoardHook();

111                     break;

112             }

113         } 

114 

115         #endregion

116 

117         #region Mouse Hook Monitor

118 

119         /// <summary>

120         /// Install the mouse hook.

121         /// Default install mouse global hook - [14];

122         /// </summary>

123         /// <param name="hookType">Select mouse hook type.</param>

124         public void InstallMouseHook(HookType hookType = HookType.WH_MOUSE_LL)

125         {

126             if (_mouseHookHandle == default(int))

127             {

128                 _mouseHookCallBack = new HookProcCallBack(this.MouseHookCallBack);

129                 if (hookType == HookType.WH_MOUSE)

130                 {

131                     _mouseHookHandle = HookAPI.SetWindowsHookEx((int)hookType, _mouseHookCallBack, IntPtr.Zero, _currentThreadID);

132                 }

133                 else

134                 {

135                     _mouseHookHandle = HookAPI.SetWindowsHookEx((int)hookType, _mouseHookCallBack, _currentMoudleHandle, 0);

136                 }

137                 this.CheckHandleIsZero(_mouseHookHandle);

138             }

139         }

140 

141         private int MouseHookCallBack(int nCode, int wParam, IntPtr lParam)

142         {

143             MouseButtons mouseOperation = MouseButtons.None;

144             Point mousePoint = (Point)Marshal.PtrToStructure(lParam, typeof(Point));

145 

146             switch (wParam)

147             {

148                 case (int)WindowsMessage.WM_LBUTTONDOWN:

149                     mouseOperation = MouseButtons.Left;

150                     this.InvokeMouseEvent(this.LeftMouseClickDown, mouseOperation, mousePoint);

151                     break;

152                 case (int)WindowsMessage.WM_LBUTTONUP:

153                     mouseOperation = MouseButtons.Left;

154                     this.InvokeMouseEvent(this.LeftMouseClickUp, mouseOperation, mousePoint);

155                     break;

156                 case (int)WindowsMessage.WM_LBUTTONDBLCLK:

157                     mouseOperation = MouseButtons.Left;

158                     this.InvokeMouseEvent(this.LeftMouseDoubleClick, mouseOperation, mousePoint);

159                     break;

160                 case (int)WindowsMessage.WM_RBUTTONDOWN:

161                     mouseOperation = MouseButtons.Right;

162                     this.InvokeMouseEvent(this.RightMouseClickDown, mouseOperation, mousePoint);

163                     break;

164                 case (int)WindowsMessage.WM_RBUTTONUP:

165                     mouseOperation = MouseButtons.Right;

166                     this.InvokeMouseEvent(this.RightMouseClickUp, mouseOperation, mousePoint);

167                     break;

168                 case (int)WindowsMessage.WM_RBUTTONDBLCLK:

169                     mouseOperation = MouseButtons.Right;

170                     this.InvokeMouseEvent(this.RightMouseDoubleClick, mouseOperation, mousePoint);

171                     break;

172                 case (int)WindowsMessage.WM_MOUSEMOVE:

173                     this.InvokeMouseEvent(this.MouseMove, mouseOperation, mousePoint);

174                     break;

175                 case (int)WindowsMessage.WM_MOUSEWHEEL:

176                     this.InvokeMouseEvent(this.MouseWheel, mouseOperation, mousePoint);

177                     break;

178             }

179 

180             return HookAPI.CallNextHookEx(_mouseHookHandle, nCode, wParam, lParam);

181         }

182 

183         private void InvokeMouseEvent(MouseEventHandler mouseEvent, MouseButtons mouseButton, Point point)

184         {

185             if (mouseEvent != null)

186             {

187                 MouseEventArgs mouseArgs = new MouseEventArgs(mouseButton, 0, point.X, point.Y, 0);

188                 mouseEvent(this, mouseArgs);

189             }

190         } 

191 

192         #endregion

193 

194         #region KeyBoaed Hook Monitor

195 

196         /// <summary>

197         /// Install the keyboard hook.

198         /// Default install keyboard global hook - [13].

199         /// </summary>

200         /// <param name="hookType">Select keyboard hook type.</param>

201         public void InstallKeyBoardHook(HookType hookType = HookType.WH_KEYBOARD_LL)

202         {

203             if (_keyboardHookHandlel == default(int))

204             {

205                 _keyboardHookCallBack = new HookProcCallBack(this.KeyBoradHookCallBack);

206                 if (hookType == HookType.WH_KEYBOARD)

207                 {

208                     _keyboardHookHandlel = HookAPI.SetWindowsHookEx((int)hookType, _keyboardHookCallBack, IntPtr.Zero, _currentThreadID);

209                 }

210                 else

211                 {

212                     _keyboardHookHandlel = HookAPI.SetWindowsHookEx((int)hookType, _keyboardHookCallBack, _currentMoudleHandle, 0);

213                 }

214                 this.CheckHandleIsZero(_keyboardHookHandlel);

215             }

216         }

217 

218         private int KeyBoradHookCallBack(int nCode, int wParam, IntPtr lParam)

219         {

220             if (nCode >= 0)

221             {

222                 CustomKeyBoard keyInfo = (CustomKeyBoard)Marshal.PtrToStructure(lParam, typeof(CustomKeyBoard));

223 

224                 if (this.KeyDown != null

225                     && (wParam == (int)WindowsMessage.WM_KEYDOWN || wParam == (int)WindowsMessage.WM_SYSKEYDOWN))

226                 {

227                     this.InvokeKeyBoardEvent(this.KeyDown, (Keys)keyInfo.VirtualKeyCode);

228                 }

229 

230                 if (this.KeyPress != null && wParam == (int)WindowsMessage.WM_KEYDOWN)

231                 {

232                     this.InvokeKeyBoardEvent(this.KeyPress, (Keys)keyInfo.VirtualKeyCode);

233                 }

234 

235                 if (this.KeyUp != null

236                     && (wParam == (int)WindowsMessage.WM_KEYUP || wParam == (int)WindowsMessage.WM_SYSKEYUP))

237                 {

238                     this.InvokeKeyBoardEvent(this.KeyUp, (Keys)keyInfo.VirtualKeyCode);

239                 }

240             }

241 

242             return HookAPI.CallNextHookEx(_keyboardHookHandlel, nCode, wParam, lParam);

243         }

244 

245         private void InvokeKeyBoardEvent(CustomKeyEventHandler keyEvent, Keys keyData)

246         {

247             CustomKeyEventArgs customKeyArgs = new CustomKeyEventArgs(keyData);

248             keyEvent(this, customKeyArgs);

249         } 

250 

251         #endregion

252 

253         #region Common

254 

255         private void CheckHandleIsZero(int handle)

256         {

257             if (handle == 0)

258             {

259                 int errorID = Marshal.GetLastWin32Error();

260                 throw new Win32Exception(errorID);

261             }

262         }

263 

264         public void UninstallHook()

265         {

266             if (_mouseHookHandle != default(int))

267             {

268                 if (HookAPI.UnhookWindowsHookEx(_mouseHookHandle))

269                 {

270                     _mouseHookHandle = default(int);

271                 }

272             }

273             if (_keyboardHookHandlel != default(int))

274             {

275                 if (HookAPI.UnhookWindowsHookEx(_keyboardHookHandlel))

276                 {

277                     _keyboardHookHandlel = default(int);

278                 }

279             }

280         } 

281 

282         #endregion

283     }

284 }
View Code

  

  全部代码:下载

 

好了就这么多了,已同步至:个人文章目录索引

你可能感兴趣的:(OO)