• Ring3层的鼠标和键盘Hook


      Hook 是一种拦截特定类型消息的方法,例如注册一个全局鼠标 Hook ,就可以在事件发生前截取到这个消息。

      Windows 中的 Hook 大概分为……这么多吧。

      

      虽说C#是托管语言,不可以通过本身的类库和方法去创建钩子的,但是调用非托管类库还是没有问题的。我们可以使用 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 中的 hInstance 为应用程序的实例句柄,所以得再声明一个 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 (VS2015项目)

  • 相关阅读:
    省、市、地区三级联动
    window.open(url); 传递参数,中文乱码问题
    TortoiseSVN打分支、合并分支、切换分支
    用SoupUI导出webservice 客户端代码遇到的问题:由于 accessExternalSchema 属性设置的限制而不允许 'file' 访问, 因此无法读取方案文档 'xjc.xsd'。
    oracle 创建定时任务
    分布式服务框架原理与实践_李林锋著_笔记
    idea部署到tomcat不打印log
    idea登录github报错 Can't login: Connection reset
    idea部署web工程到tomcat_无法访问此网站问题排查
    使用myeclipse tomcat插件部署web项目时报错 an internal error occurred during add deployment . java.lang.nullpointerexception
  • 原文地址:https://www.cnblogs.com/Bita/p/5268522.html
Copyright © 2020-2023  润新知