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

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

一丶Hook

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

  1. 实现Hook的步骤

    ①安装钩子

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

    ③卸载钩子

  2.安装钩子

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

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

     public enum HookType : int
{
/// <summary>
/// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动
///条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。
///WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通
///过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook
///监视所有应用程序消息。
///
///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间
///过滤消息,这等价于在主消息循环中过滤消息。
///
///通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这
///个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循
///环里一样
/// </summary>
WH_MSGFILTER = -,
/// <summary>
/// WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这
///个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook
///来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样
///使用。WH_JOURNALRECORD是system-wide local hooks,它们不会被注射到任何行
///程地址空间
/// </summary>
WH_JOURNALRECORD = ,
/// <summary>
/// WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可
///以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠
///标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘
///事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定
///Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处
///理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实
///时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它们不会被
///注射到任何行程地址空间
/// </summary>
WH_JOURNALPLAYBACK = ,
/// <summary>
/// 在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and
///WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使
///用这个Hook来监视输入到消息队列中的键盘消息
/// </summary>
WH_KEYBOARD = ,
/// <summary>
/// 应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函
///数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及
///其它发送到消息队列中的消息
/// </summary>
WH_GETMESSAGE = ,
/// <summary>
/// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之前调用
/// </summary>
WH_CALLWNDPROC = ,
/// <summary>
/// 在以下事件之前,系统都会调用WH_CBT Hook子过程,这些事件包括:
///1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件;
///2. 完成系统指令;
///3. 来自系统消息队列中的移动鼠标,键盘事件;
///4. 设置输入焦点事件;
///5. 同步系统消息队列事件。
///Hook子过程的返回值确定系统是否允许或者防止这些操作中的一个
/// </summary>
WH_CBT = ,
/// <summary>
/// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动
///条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。
///WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通
///过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook
///监视所有应用程序消息。
///
///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间
///过滤消息,这等价于在主消息循环中过滤消息。
///
///通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这
///个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循
///环里一样
/// </summary>
WH_SYSMSGFILTER = ,
/// <summary>
/// WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。
///使用这个Hook监视输入到消息队列中的鼠标消息
/// </summary>
WH_MOUSE = ,
/// <summary>
/// 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时
/// </summary>
WH_HARDWARE = ,
/// <summary>
/// 在系统调用系统中与其它Hook关联的Hook子过程之前,系统会调用
///WH_DEBUG Hook子过程。你可以使用这个Hook来决定是否允许系统调用与其它
///Hook关联的Hook子过程
/// </summary>
WH_DEBUG = ,
/// <summary>
/// 外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是
///激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子过程。
///WH_SHELL 共有5钟情况:
///1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁;
///2. 当Taskbar需要重画某个按钮;
///3. 当系统需要显示关于Taskbar的一个程序的最小化形式;
///4. 当目前的键盘布局状态改变;
///5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。
///
///按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接
///收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自
///己
/// </summary>
WH_SHELL = ,
/// <summary>
/// 当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE
///Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就
///会调用WH_FOREGROUNDIDLE Hook子过程
/// </summary>
WH_FOREGROUNDIDLE = ,
/// <summary>
/// 监视发送到窗口过程的消息,系统在消息发送到接收窗口过程之后调用
/// </summary>
WH_CALLWNDPROCRET = ,
/// <summary>
/// 监视输入到线程消息队列中的键盘消息
/// </summary>
WH_KEYBOARD_LL = ,
/// <summary>
/// 监视输入到线程消息队列中的鼠标消息
/// </summary>
WH_MOUSE_LL =
}

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

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

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

     安装钩子需要调用的API:

         /// <summary>
/// 安装勾子
/// </summary>
/// <param name="idHook">钩子类型,此处用整形的枚举表示</param>
/// <param name="hookCallBack">钩子发挥作用时的回调函数</param>
/// <param name="moudleHandle">应用程序实例的模块句柄(一般来说是你钩子回调函数所在的应用程序实例模块句柄)</param>
/// <param name="threadID">与安装的钩子子程相关联的线程的标识符
/// <remarks>如果线程ID是0则针对系统级别的,否则是针对当前线程</remarks>
/// </param>
/// <returns>返回钩子句柄</returns>
[DllImport("user32.dll")]
public static extern int SetWindowsHookEx(int idHook, HookProcCallBack hookCallBack, IntPtr moudleHandle, int threadID); public delegate int HookProcCallBack(int nCode, int wParam, IntPtr lParam);

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

  3.监测键盘和鼠标行为

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

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

  4.卸载钩子

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

         /// <summary>
/// 卸载勾子
/// </summary>
/// <param name="handle">要取消的钩子的句柄</param>
/// <returns>卸载钩子是否成功</returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int handle); 

  5.HookManager

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

    给出主要实现代码:

 using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms; using SharpCommon.Windows; /* * 2014-1-28 完善第一个版本
*
* 1.关于KeyPress的解释
* 在控件有焦点的情况下按下键时发生。
* 键事件按下列顺序发生:
KeyDown
KeyPress
KeyUp 非字符键不会引发 KeyPress 事件;但非字符键却可以引发 KeyDown 和 KeyUp 事件。
使用 KeyChar 属性在运行时对键击进行取样,并且使用或修改公共键击的子集。
若要仅在窗体级别处理键盘事件而不允许其他控件接收键盘事件,
* 请将窗体的 KeyPress 事件处理方法中的 KeyPressEventArgs.Handled 属性设置为 true。
*
* 摘自MSDN上的说明
* KeyPressEventArgs 指定在用户按键时撰写的字符。例如,当用户按 Shift + K 时,KeyChar 属性返回一个大写字母 K。
当用户按下任意键时,发生 KeyPress 事件。与 KeyPress 事件紧密相关的两个事件为 KeyUp 和 KeyDown。
* 当用户按下某个键时,KeyDown 事件先于每个 KeyPress 事件发生;当用户释放某个键时发生 KeyUp 事件。
* 当用户按住某个键时,每次字符重复时,KeyDown 和 KeyPress 事件也都重复发生。一个 KeyUp 事件在释放按键时生成。 KeyPressEventArgs 随着 KeyPress 事件的每次发生而被传递。
* KeyEventArgs 随着 KeyDown 和 KeyUp 事件的每次发生而被传递。
* KeyEventArgs 指定是否有任一个组合键(Ctrl、Shift 或 Alt)在另一个键按下的同时也曾按下。
* 此修饰符信息也可以通过 Control 类的 ModifierKeys 属性获得。 将 Handled 设置为 true,以取消 KeyPress 事件。这可防止控件处理按键。 注意注意:
有些控件将会在 KeyDown 上处理某些击键。
* 例如,RichTextBox 在调用 KeyPress 前处理 Enter 键。
* 在这种情况下,您无法取消 KeyPress 事件,而是必须从 KeyDown 取消击键。
*
*
* 2014-1-28 1:00 PM
* 1. 完成了对组合键的监测代码,通过获取KeyState来判断是否按了组合键 */ namespace SharpCommon.Hook
{
public sealed class HookManager
{
#region Event And Field
public event CustomKeyEventHandler KeyUp;
public event CustomKeyEventHandler KeyDown;
public event CustomKeyEventHandler KeyPress; public event MouseEventHandler MouseMove;
public event MouseEventHandler MouseWheel;
public event MouseEventHandler LeftMouseClickUp;
public event MouseEventHandler RightMouseClickUp;
public event MouseEventHandler LeftMouseClickDown;
public event MouseEventHandler RightMouseClickDown;
public event MouseEventHandler LeftMouseDoubleClick;
public event MouseEventHandler RightMouseDoubleClick; private static int _mouseHookHandle;
private static int _keyboardHookHandlel; private static HookProcCallBack _mouseHookCallBack;
private static HookProcCallBack _keyboardHookCallBack; private static readonly HookManager _instance = new HookManager(); private static readonly int _currentThreadID = AppDomain.GetCurrentThreadId();
private static readonly IntPtr _currentMoudleHandle = WindowsAPI.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); #endregion #region Instance private HookManager()
{ } public static HookManager Instance
{
get { return _instance; }
} #endregion #region Install Hook /// <summary>
/// Install the hook.
/// </summary>
/// <param name="installType">Select the hook install type.</param>
public void InstallHook(HookInstallType installType = HookInstallType.MouseAndKeyBoard)
{
switch (installType)
{
case HookInstallType.Mouse:
this.InstallMouseHook();
break;
case HookInstallType.KeyBoard:
this.InstallKeyBoardHook();
break;
case HookInstallType.MouseAndKeyBoard:
this.InstallMouseHook();
this.InstallKeyBoardHook();
break;
}
} #endregion #region Mouse Hook Monitor /// <summary>
/// Install the mouse hook.
/// Default install mouse global hook - [14];
/// </summary>
/// <param name="hookType">Select mouse hook type.</param>
public void InstallMouseHook(HookType hookType = HookType.WH_MOUSE_LL)
{
if (_mouseHookHandle == default(int))
{
_mouseHookCallBack = new HookProcCallBack(this.MouseHookCallBack);
if (hookType == HookType.WH_MOUSE)
{
_mouseHookHandle = HookAPI.SetWindowsHookEx((int)hookType, _mouseHookCallBack, IntPtr.Zero, _currentThreadID);
}
else
{
_mouseHookHandle = HookAPI.SetWindowsHookEx((int)hookType, _mouseHookCallBack, _currentMoudleHandle, );
}
this.CheckHandleIsZero(_mouseHookHandle);
}
} private int MouseHookCallBack(int nCode, int wParam, IntPtr lParam)
{
MouseButtons mouseOperation = MouseButtons.None;
Point mousePoint = (Point)Marshal.PtrToStructure(lParam, typeof(Point)); switch (wParam)
{
case (int)WindowsMessage.WM_LBUTTONDOWN:
mouseOperation = MouseButtons.Left;
this.InvokeMouseEvent(this.LeftMouseClickDown, mouseOperation, mousePoint);
break;
case (int)WindowsMessage.WM_LBUTTONUP:
mouseOperation = MouseButtons.Left;
this.InvokeMouseEvent(this.LeftMouseClickUp, mouseOperation, mousePoint);
break;
case (int)WindowsMessage.WM_LBUTTONDBLCLK:
mouseOperation = MouseButtons.Left;
this.InvokeMouseEvent(this.LeftMouseDoubleClick, mouseOperation, mousePoint);
break;
case (int)WindowsMessage.WM_RBUTTONDOWN:
mouseOperation = MouseButtons.Right;
this.InvokeMouseEvent(this.RightMouseClickDown, mouseOperation, mousePoint);
break;
case (int)WindowsMessage.WM_RBUTTONUP:
mouseOperation = MouseButtons.Right;
this.InvokeMouseEvent(this.RightMouseClickUp, mouseOperation, mousePoint);
break;
case (int)WindowsMessage.WM_RBUTTONDBLCLK:
mouseOperation = MouseButtons.Right;
this.InvokeMouseEvent(this.RightMouseDoubleClick, mouseOperation, mousePoint);
break;
case (int)WindowsMessage.WM_MOUSEMOVE:
this.InvokeMouseEvent(this.MouseMove, mouseOperation, mousePoint);
break;
case (int)WindowsMessage.WM_MOUSEWHEEL:
this.InvokeMouseEvent(this.MouseWheel, mouseOperation, mousePoint);
break;
} return HookAPI.CallNextHookEx(_mouseHookHandle, nCode, wParam, lParam);
} private void InvokeMouseEvent(MouseEventHandler mouseEvent, MouseButtons mouseButton, Point point)
{
if (mouseEvent != null)
{
MouseEventArgs mouseArgs = new MouseEventArgs(mouseButton, , point.X, point.Y, );
mouseEvent(this, mouseArgs);
}
} #endregion #region KeyBoaed Hook Monitor /// <summary>
/// Install the keyboard hook.
/// Default install keyboard global hook - [13].
/// </summary>
/// <param name="hookType">Select keyboard hook type.</param>
public void InstallKeyBoardHook(HookType hookType = HookType.WH_KEYBOARD_LL)
{
if (_keyboardHookHandlel == default(int))
{
_keyboardHookCallBack = new HookProcCallBack(this.KeyBoradHookCallBack);
if (hookType == HookType.WH_KEYBOARD)
{
_keyboardHookHandlel = HookAPI.SetWindowsHookEx((int)hookType, _keyboardHookCallBack, IntPtr.Zero, _currentThreadID);
}
else
{
_keyboardHookHandlel = HookAPI.SetWindowsHookEx((int)hookType, _keyboardHookCallBack, _currentMoudleHandle, );
}
this.CheckHandleIsZero(_keyboardHookHandlel);
}
} private int KeyBoradHookCallBack(int nCode, int wParam, IntPtr lParam)
{
if (nCode >= )
{
CustomKeyBoard keyInfo = (CustomKeyBoard)Marshal.PtrToStructure(lParam, typeof(CustomKeyBoard)); if (this.KeyDown != null
&& (wParam == (int)WindowsMessage.WM_KEYDOWN || wParam == (int)WindowsMessage.WM_SYSKEYDOWN))
{
this.InvokeKeyBoardEvent(this.KeyDown, (Keys)keyInfo.VirtualKeyCode);
} if (this.KeyPress != null && wParam == (int)WindowsMessage.WM_KEYDOWN)
{
this.InvokeKeyBoardEvent(this.KeyPress, (Keys)keyInfo.VirtualKeyCode);
} if (this.KeyUp != null
&& (wParam == (int)WindowsMessage.WM_KEYUP || wParam == (int)WindowsMessage.WM_SYSKEYUP))
{
this.InvokeKeyBoardEvent(this.KeyUp, (Keys)keyInfo.VirtualKeyCode);
}
} return HookAPI.CallNextHookEx(_keyboardHookHandlel, nCode, wParam, lParam);
} private void InvokeKeyBoardEvent(CustomKeyEventHandler keyEvent, Keys keyData)
{
CustomKeyEventArgs customKeyArgs = new CustomKeyEventArgs(keyData);
keyEvent(this, customKeyArgs);
} #endregion #region Common private void CheckHandleIsZero(int handle)
{
if (handle == )
{
int errorID = Marshal.GetLastWin32Error();
throw new Win32Exception(errorID);
}
} public void UninstallHook()
{
if (_mouseHookHandle != default(int))
{
if (HookAPI.UnhookWindowsHookEx(_mouseHookHandle))
{
_mouseHookHandle = default(int);
}
}
if (_keyboardHookHandlel != default(int))
{
if (HookAPI.UnhookWindowsHookEx(_keyboardHookHandlel))
{
_keyboardHookHandlel = default(int);
}
}
} #endregion
}
}

 

  全部代码:下载

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

学习之路三十八:Hook(钩子)的学习的更多相关文章

  1. 学习之路三十九:新手学习 - Windows API

    来到了新公司,一开始就要做个程序去获取另外一个程序里的数据,哇,挑战性很大. 经过两周的学习,终于搞定,主要还是对Windows API有了更多的了解. 文中所有的消息常量,API,结构体都整理出来了 ...

  2. FastAPI 学习之路(十八)表单与文件

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  3. Python小白学习之路(十八)—【内置函数三】

    一.对象操作 help() 功能:返回目标对象的帮助信息 举例: print(help(input)) #执行结果 Help on built-in function input in module ...

  4. Spark学习之路 (十八)SparkSQL简单使用

    一.SparkSQL的进化之路 1.0以前: Shark 1.1.x开始: SparkSQL(只是测试性的)  SQL 1.3.x: SparkSQL(正式版本)+Dataframe 1.5.x: S ...

  5. Spark学习之路 (十八)SparkSQL简单使用[转]

    SparkSQL的进化之路 1.0以前: Shark 1.1.x开始: SparkSQL(只是测试性的) SQL 1.3.x: SparkSQL(正式版本)+Dataframe 1.5.x: Spar ...

  6. Dynamic CRM 2013学习笔记(三十八)流程1 - 操作(action)开发与配置详解

    CRM 2013 里流程有4个类别:操作(action).业务流程(business process flow).对话(dialog)和工作流(workflow).它们都是从 setting –> ...

  7. 学习之路三十二:VS调试的简单技巧

    这段时间园子里讲了一些关于VS的快捷键以及一些配置技巧,挺好的,大家一起学习,一起进步. 这段时间重点看了一下关于VS调试技巧方面的书,在此记录一下学习的内容吧,主要还是一些比较浅显的知识. 1. 调 ...

  8. 学习之路三十五:Android和WCF通信 - 大数据压缩后传输

    最近一直在优化项目的性能,就在前几天找到了一些资料,终于有方案了,那就是压缩数据. 一丶前端和后端的压缩和解压缩流程 二丶优点和缺点 优点:①字符串的压缩率能够达到70%-80%左右 ②字符串数量更少 ...

  9. Hive学习之路 (十八)Hive的Shell操作

    一.Hive的命令行 1.Hive支持的一些命令 Command Description quit Use quit or exit to leave the interactive shell. s ...

随机推荐

  1. Java入门 任务表

    Java IO Java ,MySQL Java Internet 线程,同步 冒泡法排序 1.

  2. Razor视图引擎输出没有编码的 Html 字符串

    优先选择: @Html.Raw(mystring) 在MVC 3中,你可以这样: ViewBag.Stuff = "<li>Menu</li>" 在视图中也 ...

  3. 记录ASP.NET Web API 服务接口响应时间

    实现起来很简单,一个Filter就可以搞定!!! /// <summary> /// 监控接口执行时间 /// </summary> public class TimingAc ...

  4. GitLab:解决Merge Request中Commits不更新的问题

    最近在使用 GitLab 的 Merge Requests 功能进行 Code Review .操作流程是这样的: 1)开发人员A要给一个项目增加一个新功能,先在这个项目上创建一个 Git 分支. 2 ...

  5. MyEclipse 8.5 优化实例

    在用[MyEclipse] 写代码很容易卡死机,尤其是在对JSP文件的<%%>之间写代码的时候,只要一弹出智能提示就立刻卡死,程序失去响应,我以为是MyEclipse版本的问题,结果换了6 ...

  6. JavaScript自定义事件

    很多DOM对象都有原生的事件支持,向div就有click.mouseover等事件,事件机制可以为类的设计带来很大的灵活性,相信.net程序员深有体会.随着web技术发展,使用JavaScript自定 ...

  7. 有关HTML5 Video对象的ontimeupdate事件的问题

    日前在做一个视频播放的页面,其中用到了HTML5的Video对象,这个是HTML5中新增的一个对象,支持多种不同格式的视频在线播放,功能比较强大,而且还扩展了许多事件,可以通过JavaScript脚本 ...

  8. JS open App(未安装就跳转下载页面)

    直接上代码var APPCommon = { downAppURl : "http://**/",//下载APP地址 downWeixin: "http://**&quo ...

  9. Symbols of String Pattern Matching

    Symbols of String Pattern Matching in Introduction to Algorithms. As it's important to be clear when ...

  10. bind() live()和delegate 区别

    Event bubbling (aka event propagation)冒泡 我们的页面可以理解为一棵DOM树,当我们在叶子结点上做什么事情的时候(如click一个a元素),如果我们不人为的设置s ...