c#中hook的初次尝试
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的初次尝试相关推荐
- Redis 初次尝试
Redis 初次尝试 第一次接触redis,也不知道要写些什么.就玩了下将redis列表中的数据存入mysql数据库中. 首先有三个文件: redis.php 添加数据进redis: insert_c ...
- 安装apache服务出错,无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题...
错误信息:无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题 错误场景:在使用["D:\Program Files\httpd-2.4.20-x ...
- 无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题...
最新在系统安装一些软件发现提示无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题 后来查找原因,是因为系统原因,(我可能安装了一个倒翻的操作系统)那就是缺 ...
- cmd启动数据库时,出现 (无法启动此程序,因为计算机中丢失VCRUNTIME140_1.dll 尝试重新安装此程序以解决此问题 )解决方法。
cmd启动数据库时,出现 (无法启动此程序,因为计算机中丢失VCRUNTIME140_1.dll 尝试重新安装此程序以解决此问题 )解决方法. 参考文章: (1)cmd启动数据库时,出现 (无法启动此 ...
- 无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题
无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序以解决此问题 参考文章: (1)无法启动此程序,因为计算机中丢失VCRUNTIME140.dll 尝试重新安装此程序 ...
- 初次尝试python爬虫,爬取小说网站的小说。
本次是小阿鹏,第一次通过python爬虫去爬一个小说网站的小说. 下面直接上菜. 1.首先我需要导入相应的包,这里我采用了第三方模块的架包,requests.requests是python实现的简单易 ...
- 无法启动此程序,因为计算机中丢失api-ms-win-crt-locale-l1-1-0.dll,尝试重新安装此程序以解决此问题
准备做APP自动化,安装好所有东西之后,开始准备敲adb命令来看看环境是不是部署成功了,但很多人会在刚开始的时候敲这个命令会出现一个问题,就是用cmd敲adb时,会弹出提示,系统错误,无法启动此程序, ...
- python怎么模拟app_初次尝试Python启动模拟器中的APP程序
首先启动我们已经安装好的模拟器,将需要测试的.apk安装包直接拖入模拟器,它会自动安装:然后启动Appium桌面应用程序,它的默认端口是4723,而appium.webdriver驱动中默认端口是44 ...
- SQL Server 2012中的Contained Database尝试
简介 SQL Server 2012新增的Contained Database是为了解决数据库在不同SQL Server实例之间迁移的问题.在以往的情况下,数据库本身并不包含一些实例级别的配置参数(比 ...
最新文章
- LeetCode简单题之设计停车系统
- Jvm 系列(二):Jvm 内存结构
- 迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章
- ITK:在图像区域上运行图像过滤器
- 数据库-优化-数据库结构的优化-表范式化优化
- 【SQL】查询DateTime类型的某一年数据
- 柴静《认识的人 了解的事》
- 华为鸿蒙系统是否上线,网友曝华为鸿蒙系统已经上线!官方回应:空欢喜一场...
- (转)java动态代理与aop
- C++ opengl 启动光照
- 二级VB培训笔记07:通用对话框
- css3中transform-style的用法
- libgdx学习记录5——演员Actor
- 使用SSE指令集优化memcpy
- matlab6数学建模基础教程,《数学建模基础教程》.pdf
- 机器人电焊电流电压怎么调_机器人二保焊自动焊机是怎么调节的?
- 双重所有格和不定代词
- 【vue】mint-ui中navbar下划线不显示的问题
- oracle 一个表上的多个触发器的执行顺序
- 海康2017校招C++开发岗位笔试题