1.写在最前

本文的内容只想以最通俗的语言说明钩子的使用方法,具体到钩子的详细介绍可以参照下面的网址:

http://www.microsoft.com/china/community/program/originalarticles/techdoc/hook.mspx

2.了解一下钩子

从字面上理解,钩子就是想钩住些东西,在程序里可以利用钩子提前处理些Windows消息。

例子:有一个Form,Form里有个TextBox,我们想让用户在TextBox里输入的时候,不管敲键盘的哪个键,TextBox里显示的始终为“A”,这时我们就可以利用钩子监听键盘消息,先往Windows的钩子链表中加入一个自己写的钩子监听键盘消息,只要一按下键盘就会产生一个键盘消息,我们的钩子在这个消息传到TextBox之前先截获它,让TextBox显示一个“A”,之后结束这个消息,这样TextBox得到的总是“A”。

消息截获顺序:既然是截获消息,总要有先有后,钩子是按加入到钩子链表的顺序决定消息截获顺序。就是说最后加入到链表的钩子最先得到消息。

截获范围:钩子分为线程钩子和全局钩子,线程钩子只能截获本线程的消息,全局钩子可以截获整个系统消息。我认为应该尽量使用线程钩子,全局钩子如果使用不当可能会影响到其他程序。

3.通俗的范例

第一步:声明、定义委托。

public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
static int hKeyboardHook = 0; 
HookProc KeyboardHookProcedure;

先解释一下委托,钩子必须使用标准的钩子子程,钩子子程就是一段方法,就是处理上面例子中提到的让TextBox显示“A”的操作。

钩子子程必须按照HookProc(int nCode, Int32 wParam, IntPtr lParam)这种结构定义,三个参数会得到关于消息的数据。

当使用SetWindowsHookEx函数安装钩子成功后会返回钩子子程的句柄,hKeyboardHook变量记录返回的句柄,如果hKeyboardHook不为0则说明钩子安装成功。

第二步:声明API函数

使用钩子,需要使用WindowsAPI函数,所以要先声明这些API函数。

// 安装钩子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
// 卸载钩子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
// 继续下一个钩子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam); 
// 取得当前线程编号
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId(); 

第三步:写钩子子程

钩子子程就是钩子所要做的事情。

private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if (nCode >= 0)
    {
        textbox1.Text = “A”;
        return 1;
    }
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam); 
}

我们写一个方法,返回一个int值,包括三个参数。如上面给出的代码,符合钩子子程的标准。

nCode参数是钩子代码,钩子子程使用这个参数来确定任务,这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。

wParam和lParam参数包含了消息信息,我们可以从中提取需要的信息。

方法的内容可以根据需要编写,我们需要TextBox显示“A”,那我们就写在这里。当钩子截获到消息后就会调用钩子子程,这段程序结束后才往下进行。截获的消息怎么处理就要看子程的返回值了,如果返回1,则结束消息,这个消息到此为止,不再传递。如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者。

第四步:安装钩子、卸载钩子

准备工作都完成了,剩下的就是把钩子装入钩子链表。

我们可以写两个方法在程序中合适位置调用。代码如下:

public void HookStart()
        {
 
            if (hKeyboardHook == 0)
            {
                // 创建HookProc实例
                KeyboardHookProcedure = new HookProc(KeyboardHookProc);
                // 设置线程钩子
                hKeyboardHook = SetWindowsHookEx(2, KeyboardHookProcedure, IntPtr.Zero,
                                             GetCurrentThreadId());
                // 如果设置钩子失败
                if (hKeyboardHook == 0)
                {
                    HookStop();
                    throw new Exception("SetWindowsHookEx failed.");
                }
            }
        }
// 卸载钩子
        public void HookStop()
        {
 
            bool retKeyboard = true;
            if (hKeyboardHook != 0)
            {
                retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
                hKeyboardHook = 0;
            }
            if (!( retKeyboard)) throw new Exception("UnhookWindowsHookEx  failed.");
        }

安装钩子和卸载钩子关键就是SetWindowsHookEx和UnhookWindowsHookEx方法。

SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId) 函数将钩子加入到钩子链表中,说明一下四个参数:

idHook 钩子类型,即确定钩子监听何种消息,上面的代码中设为2,即监听键盘消息并且是线程钩子,如果是全局钩子监听键盘消息应设为13,线程钩子监听鼠标消息设为7,全局钩子监听鼠标消息设为14。

lpfn 钩子子程的地址指针。如果dwThreadId参数为0 或是一个由别的进程创建的线程的标识,lpfn必须指向DLL中的钩子子程。 除此以外,lpfn可以指向当前进程的一段钩子子程代码。钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。

hInstance应用程序实例的句柄

在程序中调用

public Form1()

{

InitializeComponent();

HookStart();

}

转载于:https://www.cnblogs.com/zhanghtt/archive/2012/02/22/2363696.html

c#中hook的初次尝试相关推荐

  1. Redis 初次尝试

    Redis 初次尝试 第一次接触redis,也不知道要写些什么.就玩了下将redis列表中的数据存入mysql数据库中. 首先有三个文件: redis.php 添加数据进redis: insert_c ...

  2. 安装apache服务出错,无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题...

    错误信息:无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题 错误场景:在使用["D:\Program Files\httpd-2.4.20-x ...

  3. 无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题...

    最新在系统安装一些软件发现提示无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题 后来查找原因,是因为系统原因,(我可能安装了一个倒翻的操作系统)那就是缺 ...

  4. cmd启动数据库时,出现 (无法启动此程序,因为计算机中丢失VCRUNTIME140_1.dll 尝试重新安装此程序以解决此问题 )解决方法。

    cmd启动数据库时,出现 (无法启动此程序,因为计算机中丢失VCRUNTIME140_1.dll 尝试重新安装此程序以解决此问题 )解决方法. 参考文章: (1)cmd启动数据库时,出现 (无法启动此 ...

  5. 无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题

    无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题 参考文章: (1)无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序 ...

  6. 初次尝试python爬虫,爬取小说网站的小说。

    本次是小阿鹏,第一次通过python爬虫去爬一个小说网站的小说. 下面直接上菜. 1.首先我需要导入相应的包,这里我采用了第三方模块的架包,requests.requests是python实现的简单易 ...

  7. 无法启动此程序,因为计算机中丢失api-ms-win-crt-locale-l1-1-0.dll,尝试重新安装此程序以解决此问题

    准备做APP自动化,安装好所有东西之后,开始准备敲adb命令来看看环境是不是部署成功了,但很多人会在刚开始的时候敲这个命令会出现一个问题,就是用cmd敲adb时,会弹出提示,系统错误,无法启动此程序, ...

  8. python怎么模拟app_初次尝试Python启动模拟器中的APP程序

    首先启动我们已经安装好的模拟器,将需要测试的.apk安装包直接拖入模拟器,它会自动安装:然后启动Appium桌面应用程序,它的默认端口是4723,而appium.webdriver驱动中默认端口是44 ...

  9. SQL Server 2012中的Contained Database尝试

    简介 SQL Server 2012新增的Contained Database是为了解决数据库在不同SQL Server实例之间迁移的问题.在以往的情况下,数据库本身并不包含一些实例级别的配置参数(比 ...

最新文章

  1. LeetCode简单题之设计停车系统
  2. Jvm 系列(二):Jvm 内存结构
  3. 迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章
  4. ITK:在图像区域上运行图像过滤器
  5. 数据库-优化-数据库结构的优化-表范式化优化
  6. 【SQL】查询DateTime类型的某一年数据
  7. 柴静《认识的人 了解的事》
  8. 华为鸿蒙系统是否上线,网友曝华为鸿蒙系统已经上线!官方回应:空欢喜一场...
  9. (转)java动态代理与aop
  10. C++ opengl 启动光照
  11. 二级VB培训笔记07:通用对话框
  12. css3中transform-style的用法
  13. libgdx学习记录5——演员Actor
  14. 使用SSE指令集优化memcpy
  15. matlab6数学建模基础教程,《数学建模基础教程》.pdf
  16. 机器人电焊电流电压怎么调_机器人二保焊自动焊机是怎么调节的?
  17. 双重所有格和不定代词
  18. 【vue】mint-ui中navbar下划线不显示的问题
  19. oracle 一个表上的多个触发器的执行顺序
  20. 海康2017校招C++开发岗位笔试题

热门文章

  1. Jmeter如何进行http接口测试
  2. 时间序列分析(2)| ARMA模型的(偏)自相关函数
  3. python 语音识别接口_python实现阿里云语音识别api
  4. 金融数据分析余挖掘实战1.9-1.10补充
  5. 今天被公司安排给候选者进行初面,分享我的6道面试题
  6. 我是学渣,但是我零基础自学web前端成功了
  7. 一个没有经验的前端工程师,写CSS的时候有什么常见通病?
  8. 分页地址的地址结构怎么理解?
  9. W - C/C++练习7---求某个范围内的所有素数
  10. S - C语言实验——余弦