C# Hook钩子实例代码之截取键盘输入,需要的朋友可以参考下

一.关于本文 

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

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

二.钩子的简单介绍

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

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

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

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

三。开始

这里就以上文提到的简单例子做个线程钩子。

第一步:声明API函数

#region 第一步:声明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();
        #endregion

声明一下API函数,以后就可以直接调用了。

第二步:声明、定义。

#region 第二步:声明,定义委托
        public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
        static int hKeyboardHook = 0;//如果hKeyboardHook不为0则说明钩子安装成功
        HookProc KeyboardHookProcedure;
        #endregion

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

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

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

第三步:写钩子子程

#region 第三步:编写钩子子程
        //钩子子程就是钩子所要做的事情。

        private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr IParam)
        {
            if (nCode >= 0)
            {
                textBox1.Text = "hello,fangqm.cn";
                return 1;
            }
            return CallNextHookEx(hKeyboardHook, nCode, wParam, IParam);
        }
        #endregion

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

nCode参数是钩子代码,钩子子程使用这个参数来确定任务,这个参数的值依赖于Hook类型。

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

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

第四步:正式启用钩子:安装钩子、卸载钩子 
准备工作都完成了,剩下的就是把钩子装入钩子链表。 
我们可以写两个方法在程序中合适位置调用。代码如下:

#region 第四步:正式启用钩子
        //钩子安装
        public void HookStart()
        {
            if (hKeyboardHook == 0)//如果hKeyboardHook==0,钩子安装失败
            {
                  //创建HookProc实例
                KeyboardHookProcedure = new HookProc(KeyboardHookProc);
                //设置线程钩子
                hKeyboardHook = SetWindowsHookEx(2, KeyboardHookProc, IntPtr.Zero, GetCurrentThreadId());
                if (hKeyboardHook == 0)
                {
                    //终止钩子
                    throw new Exception("安装钩子失败");
                }
            }
        }
        //钩子卸载
        public void HookStop()
        {
            bool retKeyboard = true;
            if (hKeyboardHook != 0)
            {
                retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
                hKeyboardHook = 0;
            }
            if (!retKeyboard)
                throw new Exception("钩子卸载失败");
        }
        #endregion

安装钩子和卸载钩子关键就是SetWindowsHookEx和UnhookWindowsHookEx方法。 
SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId) 函数将钩子加入到钩子链表中,说明一下四个参数: 
idHook 钩子类型,即确定钩子监听何种消息,上面的代码中设为2,即监听键盘消息并且是线程钩子,如果是全局钩子监听键盘消息应设为13,线程钩子监听鼠标消息设为7,全局钩子监听鼠标消息设为14。 
lpfn 钩子子程的地址指针。如果dwThreadId参数为0 或是一个由别的进程创建的线程的标识,lpfn必须指向DLL中的钩子子程。 除此以外,lpfn可以指向当前进程的一段钩子子程代码。钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。 
hInstance应用程序实例的句柄。标识包含lpfn所指的子程的DLL。如果threadId 标识当前进程创建的一个线程,而且子程代码位于当前进程,hInstance必须为NULL。可以很简单的设定其为本应用程序的实例句柄。 
threaded 与安装的钩子子程相关联的线程的标识符。如果为0,钩子子程与所有的线程关联,即为全局钩子。
上面代码中的SetWindowsHookEx方法安装的是线程钩子,用GetCurrentThreadId()函数得到当前的线程ID,钩子就只监听当前线程的键盘消息。 
UnhookWindowsHookEx (int idHook) 函数用来卸载钩子,卸载钩子与加入钩子链表的顺序无关,并非后进先出。

四。安装全局钩子

上文使用的是线程钩子,如果要使用全局钩子在钩子的安装上略有不同。如下: 
SetWindowsHookEx( 13,KeyboardHookProcedure, 
           Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0) 
这条语句即定义全局钩子。 
子程消息处理 
        钩子子程可以得到两个关于消息信息的参数wPrama、lParam。怎么将这两个参数转成我们更容易理解的消息呢。 
        对于鼠标消息,我们可以定义下面这个结构:

public struct MSG
{
     public Point p;
     public IntPtr HWnd;
     public uint wHitTestCode;
     public int dwExtraInfo;
} 

对于键盘消息,我们可以定义下面这个结构:

public struct KeyMSG
{
     public int vkCode;
     public int scanCode;
     public int flags;
     public int time;
     public int dwExtraInfo;
} 

然后我们可以在子程里用下面语句将lParam数据转换成MSG或KeyMSG结构数据 
MSG m = (MSG) Marshal.PtrToStructure(lParam, typeof(MSG)); 
KeyMSG m = (KeyMSG) Marshal.PtrToStructure(lParam, typeof(KeyMSG));

这样可以更方便的得到鼠标消息或键盘消息的相关信息,例如p即为鼠标坐标,HWnd即为鼠标点击的控件的句柄,vkCode即为按键代码。 
注:这条语句对于监听鼠标消息的线程钩子和全局钩子都可以使用,但对监听键盘消息的线程钩子使用会出错,目前在找原因。 
        如果是监听键盘消息的线程钩子,我们可以根据lParam值的正负确定按键是按下还是抬起,根据wParam值确定是按下哪个键。

// 按下的键
Keys keyData = (Keys)wParam;
if(lParam.ToInt32() > 0)
{
     // 键盘按下
}
if(lParam.ToInt32() < 0)
{
     // 键盘抬起
} 

如果是监听键盘消息的全局钩子,按键是按下还是抬起要根据wParam值确定。 
wParam = = 0x100 // 键盘按下 
wParam = = 0x101 // 键盘抬起

时间: 2024-09-30 16:10:46

C# Hook钩子实例代码之截取键盘输入,需要的朋友可以参考下的相关文章

基于C#实现的HOOK键盘钩子实例代码

本文所述为基于C#实现的HOOK实例,该实例可用来屏蔽系统热键.程序主要实现了安装钩子.传递钩子.卸载钩子等功能.在传递钩子中:<param name="pHookHandle">是您自己的钩子函数的句柄.用该句柄可以遍历钩子链</param><param name="nCode">把传入的参数简单传给CallNextHookEx即可</param><param name="wParam"&g

JavaScript 身份证号有效验证详解及实例代码

JavaScript 身份证号有效验证详解及实例代码 这篇文章主要介绍了JavaScript 身份证号有效验证详解及实例代码的相关资料,需要的朋友可以参考下 JavaScript验证身份证号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <%@ page language="jav

实现密码框默认文字效果实例代码

实现密码框默认文字效果实例代码:大家都知道很多文本框在默认情况下都有默认的提示文本,例如"请输入姓名"之类的语言,当点击文本框的时候,会清除提示语,比较人性化.但是在密码框中实现此效果可能就有点麻烦了,因为密码框不是以明文显示的,下面就介绍一下代码实例解决此问题.代码如下: <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="

Javascript 实现简单计算器实例代码

Javascript 实现简单计算器实例代码 这篇文章主要介绍了Javascript 实现简单计算器实例代码的相关资料,需要的朋友可以参考下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

jQuery清除缓存实例代码

jQuery清除缓存实例代码:缓存是个好东西,但是有些情况下则会出现一些麻烦,比如验证码不能刷新等等,尤其是在IE浏览器下面,本章节就简单介绍一下如何清除缓存以避免一些不必要的困扰,解决方案一:使用ajax方法的时候可以设置cache的参数为false.代码如下: $.ajaxSetup ({ cache: false }); $(fucntion({ $.ajaxSetup ({ cache: false }); })) 特别说明:不能够设置为全局的.解决方案二:在请求的链接后面添加随机数,比

C#键盘输入回车键实现点击按钮效果的方法

本文实例讲述了C#键盘输入回车键实现点击按钮效果的方法,在C#项目开发中非常具有实用价值.分享给大家供大家参考. 具体实现方法如下: 把Form的KeyPreview设为true 然后设置KeyDown,KeyUp或KeyPress事件.在KeyDown事件中截获. 主要功能代码如下: this.KeyDown += new KeyEventHandler(textBox1_KeyDown); private void textBox1_KeyDown(object sender, KeyEve

HOOK API (一)——HOOK基础+一个鼠标钩子实例

HOOK API (一)——HOOK基础+一个鼠标钩子实例 0x00 起因 最近在做毕业设计,有一个功能是需要实现对剪切板的监控和进程的防终止保护.原本想从内核层实现,但没有头绪.最后决定从调用层入手,即采用HOOK API的技术来挂钩相应的API,从而实现预期的功能.在这样的需求下,就开始学习了HOOK API. 0x01什么是HOOK API HOOK(钩子,挂钩)是一种实现Windows平台下类似于中断的机制[24].HOOK机制允许应用程序拦截并处理Windows消息或指定事件,当指定的

【转】Hook钩子C#实例

[转]Hook钩子C#实例 转过来的文章,出处已经不知道了,但只这篇步骤比较清晰,就贴出来了. 一.写在最前 本文的内容只想以最通俗的语言说明钩子的使用方法,具体到钩子的详细介绍可以参照下面的网址: http://www.microsoft.com/china/community/program/originalarticles/techdoc/hook.mspx 二.了解一下钩子 从字面上理解,钩子就是想钩住些东西,在程序里可以利用钩子提前处理些Windows消息. 例子:有一个Form,Fo

Hook 简单的实例--拦截鼠标和键盘消息(一)

Hook(钩子)就是对Windows系统的一些消息或是API函数进行拦截和监控的处理平台,让可以根据程序员的设置获取其感兴趣的信息. 这里主要是介绍一下Hook拦截鼠标消息和键盘消息. 下面是CALLBACK Proc 回调函数 和 CallNextHookEx函数 LRESULT CALLBACK HookProc ( int nCode, //指定是否需要处理该消息 WPARAM wParam, LPARAM lParam //包含该消息的附加消息 , ); 这个回调函数的名字可以随你取,但