Hook是一种拦截特定类型消息的方法,例如注册一个全局鼠标Hook,就可以在事件发生前截取到这个消息。
Windows 中的Hook大概分为……这么多吧。
虽说C#是托管语言,不可以通过本身的类库和方法去创建钩子的,但是调用非托管类库还是没有问题的。要使用的钩子为Object Hook,我们可以使用DllImport引用非托管类库中的方法。安装钩子的方法(SetWindowsHookEx)的类库在user32.dll文件中,函数的原型如下:
1 HHOOK WINAPI SetWindowsHookEx( 2 _In_ int idHook, 3 _In_ HOOKPROC lpfn, 4 _In_ HINSTANCE hInstance, 5 _In_ DWORD dwThreadId 6 );
返回值为钩子的句柄,这个句柄很重要,卸载钩子时要用。在C#中应这样声明:
1 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 2 private static extern IntPtr SetWindowsHookEx( 3 int idHook, //钩子的类型 4 HookProc lpfn, //引发钩子时的回调函数 5 IntPtr hInstance, //应用程序的实例句柄 6 int dwThreadId); //要监听的线程的ID
有安装也当然有卸载,原型为:
1 BOOL WINAPI UnhookWindowsHookEx( 2 _In_ HHOOK hhk 3 );
C#中声明:
1 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 2 private static extern bool UnhookWindowsHookEx( 3 IntPtr idHook); //要卸载钩子的句柄
SetWindowsHookEx第一个参数为钩子类型,一共有15种钩子,这里只给出鼠标和键盘钩子:
1 /// <summary> 2 /// 监视输入到线程消息队列中的键盘消息 3 /// </summary> 4 int WH_KEYBOARD_ALL = 13, 5 6 /// <summary> 7 /// 监视输入到线程消息队列中的鼠标消息 8 /// </summary> 9 int WH_MOUSE_ALL = 14
SetWindowsHookEx还有一个回调函数HookProc,需要我们去声明:
1 private delegate int HookProc(int nCode, int wParam, IntPtr lParam);
回调函数中的nCode表示前端钩子(可以安装多个钩子,但一般不这样做,因为很耗性能,除非你是搞破坏的)传回来的参数,0表示消息被废弃,非0表示消息仍然有效。wParam表示消息类型,一共有N多种消息,这里就不一一列举了,自己看代码。lParam返回消息结构的句柄,通过Marshal.PtrToStructure方法获得消息句柄,键盘和鼠标消息结构分别为:
1 /// <summary> 2 /// 键盘消息结构 3 /// </summary> 4 [StructLayout(LayoutKind.Sequential)] 5 public class KeyboardHookStruct 6 { 7 /// <summary> 8 /// 定一个虚拟键码。该代码必须有一个价值的范围1至254 9 /// </summary> 10 public int vkCode; 11 /// <summary> 12 /// 指定的硬件扫描码 13 /// </summary> 14 public int scanCode; 15 /// <summary> 16 /// 键标志 17 /// </summary> 18 public int flags; 19 /// <summary> 20 /// 消息时间戳间 21 /// </summary> 22 public int time; 23 /// <summary> 24 /// 指定额外信息相关的信息 25 /// </summary> 26 public int dwExtraInfo; 27 } 28 29 /// <summary> 30 /// 鼠标消息结构 31 /// </summary> 32 [StructLayout(LayoutKind.Sequential)] 33 public class MouseHookStruct 34 { 35 /// <summary> 36 /// POINT结构对象,保存鼠标在屏幕上的x,y坐标 37 /// </summary> 38 public Point pt; 39 /// <summary> 40 /// 接收到鼠标消息的窗口的句柄 41 /// </summary> 42 public IntPtr hWnd; 43 /// <summary> 44 /// hit-test值,详细描述参见WM_NCHITTEST消息 45 /// </summary> 46 public int wHitTestCode; 47 /// <summary> 48 /// 指定与本消息联系的额外消息 49 /// </summary> 50 public int dwExtraInfo; 51 }
SetWindowsHookEx为应用程序的实例句柄,所以得再声明一个API函数:
1 [DllImport("kernel32.dll")] 2 private static extern IntPtr GetModuleHandle( 3 string name); //要获取句柄的线程的名字
万事俱备,仅需要两个安装钩子和两个卸载钩子的函数。
1 /// <summary> 2 /// 安装键盘钩子 3 /// </summary> 4 private void InsertKeyBoardHook() 5 { 6 if (KeyBoardHookPtr == IntPtr.Zero) 7 { 8 KeyboardHookProcedure = new HookProc(KeyboardHookProc); 9 KeyBoardHookPtr = SetWindowsHookEx((int)HookType.WH_KEYBOARD_ALL, KeyboardHookProcedure, GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0); //使用全局钩子时,线程号为0 10 if (KeyBoardHookPtr == IntPtr.Zero) throw new Exception("安装钩子失败!"); 11 } 12 } 13 14 /// <summary> 15 /// 卸载键盘钩子 16 /// </summary> 17 private void DeleteKeyBoardHook() 18 { 19 if (KeyBoardHookPtr == IntPtr.Zero) return; 20 if (!UnhookWindowsHookEx(KeyBoardHookPtr)) throw new Exception("卸载钩子失败!"); 21 KeyBoardHookPtr = IntPtr.Zero; 22 } 23 24 25 /// <summary> 26 /// 安装鼠标钩子 27 /// </summary> 28 private void InsertMouseHook() 29 { 30 if (MouseHookPtr == IntPtr.Zero) 31 { 32 MouseHookProcedure = new HookProc(MouseHookProc); 33 MouseHookPtr = SetWindowsHookEx((int)HookType.WH_MOUSE_ALL, MouseHookProcedure, GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0); //使用全局钩子时,线程号为0 34 if (MouseHookPtr == IntPtr.Zero) throw new Exception("安装钩子失败!"); 35 } 36 } 37 38 /// <summary> 39 /// 卸载鼠标钩子 40 /// </summary> 41 private void DeleteMouseHook() 42 { 43 if (MouseHookPtr == IntPtr.Zero) return; 44 if (!UnhookWindowsHookEx(MouseHookPtr)) throw new Exception("卸载钩子失败!"); 45 MouseHookPtr = IntPtr.Zero; 46 }
这里给两个回掉函数例子:
键盘钩子的回调函数,当按下数字键(数字键盘)就弹出提示框:
1 private int KeyboardHookProc(int nCode, int wParam, IntPtr lParam) 2 { 3 if ((nCode >= 0) && (wParam == (int)MsgType.WM_KEYDOWN)) 4 { 5 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); 6 switch ((Keys)MyKeyboardHookStruct.vkCode) 7 { 8 case Keys.NumPad0: 9 case Keys.NumPad1: 10 case Keys.NumPad2: 11 case Keys.NumPad3: 12 case Keys.NumPad4: 13 case Keys.NumPad5: 14 case Keys.NumPad6: 15 case Keys.NumPad7: 16 case Keys.NumPad8: 17 case Keys.NumPad9: 18 19 System.Windows.MessageBox.Show("你按下了数字键"); break; 20 } 21 } 22 return 0; //0为废弃消息,非0为继续传递消息 23 }
鼠标钩子的回调函数,当按下右键就弹出提示框,并显明按下时的位置:
1 private int MouseHookProc(int nCode, int wParam, IntPtr lParam) 2 { 3 if ((nCode >= 0) && (wParam == (int)MsgType.WM_RBUTTONDOWN)) 4 { 5 MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct)); 6 label1.Content = "你按下了鼠标右键,位置在:x " + MyMouseHookStruct.pt.X + " y " + MyMouseHookStruct.pt.Y; //label1为一个label控件 7 } 8 return 0; 9 }
代码下载 密码为:sy4u
原文:http://www.cnblogs.com/Bita/p/5268522.html