钩子(一)

钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。


 基本概念:
      钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。
  钩子也可以理解为WINDOWS留给我们的后门,比如你想控制键盘,在DOS时代很简单通过INT即可,而WINDOWS时代不允许我们直接操作硬件;由于WINDOWS是消息驱动,所以我们可以拦截键盘消息以达到控制键盘的目的。但是控制自己进程的消息固然很简单,要控制所有进程消息要利用钩子了。将钩子函数放在DLL中,所有的有关键盘的消息都必须经过钩子函数过滤,这样你就可以为所欲为了。
   WINDOWS下的钩子程序就像DOS下的TSR(内存驻留程序)一样,用来截获WINDOWS下的特定的消息,进行相应的处理。比如可以截获键盘输入的消息,来获得键盘输入的信息等。钩子程序可以通过API调用来驻留和脱钩。

 
运行机制:
      每一个Hook(钩子)都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向指定的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。 一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始, 而最早安装的钩子放在最后,也就是后加入的先获得控制权。
  Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。


钩子的分类:
一. 按事件分类
  有如下的几种常用类型
  (1) 键盘钩子和低级键盘钩子可以监视各种键盘消息。
  (2) 鼠标钩子和低级鼠标钩子可以监视各种鼠标消息。
  (3) 外壳钩子可以监视各种Shell事件消息。比如启动和关闭应用程序。
  (4) 日志钩子可以记录从系统消息队列中取出的各种事件消息。
  (5) 窗口过程钩子监视所有从系统消息队列发往目标窗口的消息。
  此外,还有一些特定事件的钩子提供给我们使用,不一一列举。
二. 按使用范围分类
  主要有线程钩子和系统钩子
  (1) 线程钩子监视指定线程的事件消息。
  (2) 系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL)
  中。这是系统钩子和线程钩子很大的不同之处。
  几点需要说明的地方:
  (1) 如果对于同一事件(如鼠标消息)既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,然后调用系统钩子。
  (2) 对同一事件消息可安装多个钩子处理过程,这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。而且最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。
  (3) 钩子特别是系统钩子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装钩子,在使用完毕后要及时卸载。

钩子API详解:

钩子在C#中应用范围很小,只能挂键盘和鼠标钩子,在C#中要用钩子需调用一下几个api:
SetWindowsHookEx
声明:
[DllImport("user32.dll")]
public static extern int 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,钩子子程与所有的线程关联,即为全局钩子。
返回值:
如果函数成功,返回值是处理程序的挂钩。
如果函数失败,返回值是NULL。
UnhookWindowsHookEx
声明如下
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
说明:
一个钩子链中删除程序中安装一个钩子的调用SetWindowsHookEx函数。
参数说明:
idHook 钩子的句柄被删除。 此参数是一个钩子以往处理获得通过调用SetWindowsHookEx得到。
返回值:
布尔
如果函数成功,返回值为非零。
如果函数失败,返回值为零。
CallNextHookEx
声明:
[DllImport("user32.dll")]
public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
说明:
信息传递钩在当前链中的下钩挂钩过程。 一个钩子程序可以调用这个函数之前或之后处理资料的钩。参数说明:
idHook 此参数是一个钩子以往处理获得通过调用SetWindowsHookEx得到。
nCode 钩子代码传递到目前的钩子程序。 下一个钩子程序使用此代码,以确定如何处理钩子信息。
wParam 在wParam的值传递到当前的钩子程序。 这个参数的含义取决于与当前钩子钩链相关的类型。
lParam lParam值传递给当前的钩子程序。 这个参数的含义取决于与当前钩子钩链相关的类型。
返回值:
此值是返回链中的下一个钩子程序。 目前挂钩过程也必须返回这个值。 该返回值的意义取决于挂钩类型。
GetModuleHandle
声明:
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
说明:
获取一个应用程序或动态链接库的模块句柄。
参数说明:
name 在加载的模块(一个.DLL名称或.exe文件)。 如果文件扩展名被省略,默认的库扩展。dll文件被追加。 该文件名称字符串可以包含一个尾随点字符(.)表示,该模块的名称没有扩展名。 该字符串没有指定路径。 当指定一个路径,一定要使用反斜杠(\),而不是正斜杠(/)。 这个名字是比较(案例独立)的模块的名称目前到调用进程的地址空间映射。
返回值:
如果函数成功,返回值是一个指定的模块句柄。
如果函数失败,返回值是NULL。

钩子事例:
熟悉了以上AIP,下面,来应用到程序中,做一个简单的全局快捷键小程序
先写一个钩子类(Hook.cs):

代码
namespace Win32Hook
{
public class Hook
{
// 钩子委托
public delegate int HookProc(int nCode, int wParam, IntPtr lParam);
static int hHook = 0;
//键盘KeyDown事件
public event KeyEventHandler KeyDown;
// 全局钩子监听键盘消息
private const int WH_KEYBOARD_LL = 13;
HookProc KeyBoardHookProcedure;
//键盘Hook结构函数
[StructLayout(LayoutKind.Sequential)]
public class KeyBoardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
//安装钩子
[DllImport("user32.dll")]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
//卸载钩子
[DllImport("user32.dll")]
public static extern bool UnhookWindowsHookEx(int idHook);
//调用下一个钩子
[DllImport("user32.dll")]
public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
//获取动态链接库句柄
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
//安装钩子
public void Start()
{
// 安装键盘钩子
if (hHook == 0)
{
// SetWindowsHookEx 第二个参数其实是一个委托
KeyBoardHookProcedure = new HookProc(KeyBoardHookProc);
// 返回值int 如果返回0,则安装失败
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyBoardHookProcedure, GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);
}
}
//卸载钩子
public void Close()
{
bool retKeyboard = true;
if (hHook != 0)
{
// 返回为bool true为卸载成功,false卸载失败
retKeyboard = UnhookWindowsHookEx(hHook);
}
}
//钩子消息处理方法,此处可以做很多事,比如说,屏蔽快捷键,添加键盘事件
public int KeyBoardHookProc(int nCode, int wParam, IntPtr lParam)
{
bool handled = false;
if (nCode >= 0)
{
KeyBoardHookStruct kbh
= (KeyBoardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyBoardHookStruct));
// KeyDown事件,不单单可以加KeyDown事件,也可以加KeyUp等等
if (KeyDown != null && (wParam == 0x100 || wParam == 0x104))
{
Keys keyData
= (Keys)kbh.vkCode;
KeyEventArgs e
= new KeyEventArgs(keyData);
KeyDown(
this, e);
handled
= handled || e.Handled;
}
}
if (handled)
return 1;
else
// 继续消息循环
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
}
}


本文来自CSDN博客,转载请标明出处:http:
//blog.csdn.net/wangwenzhuang/archive/2010/09/20/5897079.aspx

钩子类写好,下面新建一个窗体,试下钩子吧

拖一个TextBox,设置Dock属性为:Fill,ReadOnly为:true,转到代码:

代码
public partial class Form1 : Form
{
Hook hook;
public Form1()
{
InitializeComponent();
hook
= new Hook();
hook.KeyDown
+= new KeyEventHandler(hook_KeyDown);
hook.Start();
}
void hook_KeyDown(object sender, KeyEventArgs e)
{
textBox1.Text
+= e.KeyCode + "\r\n";
}
}
 
 

运行程序,此时,按下的键盘键值都会记录在程序中

效果图:

事例下载

原文地址:https://www.cnblogs.com/bingyun84/p/1931006.html