zoukankan      html  css  js  c++  java
  • 全局鼠标键盘钩子的使用

    前言:

      windows中大部分的应用程序都是基于消息机制的,它们都有一个消息过程函数,根据不同的消息完成不同的功能。而消息钩子是windows提供的一种消息过滤和预处理机制,可以用来截获和监视系统中的消息。按照钩子作用范围不同,又可以分为局部钩子和全局钩子。局部钩子是针对某个线程的,而全局钩子是作用于整个系统的基于消息的应用。全局钩子需要使用DLL文件,在DLL文件中实现相应的钩子函数。

    由于windows操作是基于消息的,那么鼠标和键盘的操作也是通过消息传递到目标窗口的,因此可以按装全局鼠标键盘钩子,使鼠标键盘在传递到目标窗口之前拦截、钩住它,为我们提供一个做操作的机会,可以触发某个事件方法,可以阻止它传递、也可以不做任何处理。

    不管是鼠标钩子还是键盘钩子,都需要先注册windows全局钩子。

    方法:

     首先需要调用user32.dll包

    SetWindowsHookEx:安装全局钩子

    UnhookWindowsHookEx:卸载全局钩子

    CallNextHookEx:调用下一个钩子

    参数:

    private static int hMouseHook = 0;    

    private const int WM_MOUSEMOVE = 0x200; //鼠标移动,本次没有使用这个参数,而是用的MouseEventArgs()方法监听鼠标移动
    private const int WM_LBUTTONDOWN = 0x201;   //左键按下
    private const int WM_RBUTTONDOWN = 0x204;    //右键按下
    private const int WM_MBUTTONDOWN = 0x207;    //中键按下
    private const int WM_LBUTTONUP = 0x202;    //左键抬起
    private const int WM_RBUTTONUP = 0x205;    //右键抬起
    private const int WM_MBUTTONUP = 0x208;    //中键抬起

    private const int WM_LBUTTONDBLCLK = 0x203;  //左键单击,本次未使用
    private const int WM_RBUTTONDBLCLK = 0x206;  //右键单击,本次未使用
    private const int WM_MBUTTONDBLCLK = 0x209;  //中键单击,本次未使用

    完整流程:

    第一步、定义windows全局钩子类:

     1     public class Win32Api
     2     {
     3 
     4         public delegate int HookProc( int nCode, IntPtr wParam, IntPtr lParam);
     5 
     6         /// <summary>
     7         /// 安装钩子。把一个应用程序定义的钩子子程安装到钩子链表中。函数成功则返回钩子子程的句柄,失败返回NULL
     8         /// </summary>
     9         /// <param name="idHook">钩子的类型。它决定了 HOOKPROC 被调用的时机</param>
    10         /// <param name="lpfn">指向钩子回调函数的指针。如果最后一个参数 dwThreadId 为0或者是其它进程创建的线程标识符,则 lpfn 参数必须指向DLL中的钩子回调函数,即 HOOKPROC 函数必须在DLL中实现。否则,lpfn 可以指向与当前进程相关联的代码中的钩子过程</param>
    11         /// <param name="hInstance">包含由 lpfn 参数指向的钩子回调函数的DLL句柄。如果 dwThreadId 参数指定由当前进程创建线程,并且钩子回调函数位于当前进程关联的代码中,则 hmod 参数必须设置为 NULL。</param>
    12         /// <param name="threadId">与钩子程序关联的线程标识符(指定要 HOOK 的线程 ID)。如果此参数为0,则钩子过程与系统中所有线程相关联,即全局消息钩子</param>
    13         /// <returns></returns>
    14         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    15         public static extern int SetWindowsHookEx(int idHook,HookProc lpfn,IntPtr hInstance, int threadId);
    16 
    17         /// <summary>
    18         /// 卸载钩子。函数成功则返回非0,失败返回NULL
    19         /// </summary>
    20         /// <param name="idHook">钩子的类型</param>
    21         /// <returns></returns>
    22         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    23         public static extern bool UnhookWindowsHookEx(int idHook);
    24 
    25         /// <summary>
    26         /// 调用下一个钩子。调用钩子链中的下一个挂钩过程,调用成功返回值是下一个钩子的回调函数,否则为0。当前钩子程序也必须返回此值。
    27         /// </summary>
    28         /// <param name="idHook">钩子的类型</param>
    29         /// <param name="nCode">钩子代码。就是给下一个钩子要交待的内容</param>
    30         /// <param name="wParam">要传递的参数。由钩子类型决定是什么参数</param>
    31         /// <param name="lParam">要传递的参数。由钩子类型决定是什么参数</param>
    32         /// <returns></returns>
    33         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    34         public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
    35 
    36     }
    View Code

    注:鼠标钩子函数和键盘钩子函数都会公用上面这个全局钩子类

    第二步、定义鼠标钩子方法和键盘钩子方法

    鼠标钩子:

      1 class MouseHook
      2     {
      3         private Point point;
      4         private Point Point
      5         {
      6             get { return point; }
      7             set
      8             {
      9                 if (point != value)
     10                 {
     11                     point = value;
     12                     if (MouseMoveEvent != null)
     13                     {
     14                         var e = new MouseEventArgs(MouseButtons.None, 0, point.X, point.Y, 0);
     15                         MouseMoveEvent(this, e);
     16                     }
     17                 }
     18             }
     19         }
     20         private int hHook;
     21         private static int hMouseHook = 0;
     22         private const int WM_MOUSEMOVE = 0x200;
     23         private const int WM_LBUTTONDOWN = 0x201;
     24         private const int WM_RBUTTONDOWN = 0x204;
     25         private const int WM_MBUTTONDOWN = 0x207;
     26         private const int WM_LBUTTONUP = 0x202;
     27         private const int WM_RBUTTONUP = 0x205;
     28         private const int WM_MBUTTONUP = 0x208;
     29         private const int WM_LBUTTONDBLCLK = 0x203;
     30         private const int WM_RBUTTONDBLCLK = 0x206;
     31         private const int WM_MBUTTONDBLCLK = 0x209;
     32 
     33         public const int WH_MOUSE_LL = 14; //idHook值的参数,14为系统级,截获全局鼠标消息。详细SetWindowsHookEx函数的idHook参照https://www.cnblogs.com/ndyxb/p/12883292.html
     34         public Win32Api.HookProc hProc;
     35         public MouseHook()
     36         {
     37             this.Point = new Point();
     38         }
     39 
     40         /// <summary>
     41         /// 安装鼠标钩子
     42         /// </summary>
     43         /// <returns></returns>
     44         public int SetHook()
     45         {
     46             hProc = new Win32Api.HookProc(MouseHookProc);
     47             hHook = Win32Api.SetWindowsHookEx(WH_MOUSE_LL, hProc, IntPtr.Zero, 0);
     48             return hHook;
     49         }
     50 
     51         /// <summary>
     52         /// 卸载鼠标钩子
     53         /// </summary>
     54         public void UnHook()
     55         {
     56             Win32Api.UnhookWindowsHookEx(hHook);
     57         }
     58 
     59         /// <summary>
     60         /// 执行鼠标钩子
     61         /// </summary>
     62         /// <param name="nCode"></param>
     63         /// <param name="wParam"></param>
     64         /// <param name="lParam"></param>
     65         /// <returns></returns>
     66         private int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
     67         {
     68             MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
     69             if (nCode < 0)
     70             {
     71                 return Win32Api.CallNextHookEx(hHook, nCode, wParam, lParam);
     72             }
     73             else
     74             {
     75                 MouseButtons button = MouseButtons.None;
     76                 int clickCount = 0;
     77                 switch ((Int32)wParam)
     78                 {
     79                     case WM_LBUTTONDOWN:
     80                         button = MouseButtons.Left;
     81                         clickCount = 1;
     82                         MouseDownEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
     83                         break;
     84                     case WM_RBUTTONDOWN:
     85                         button = MouseButtons.Right;
     86                         clickCount = 1;
     87                         MouseDownEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
     88                         break;
     89                     case WM_MBUTTONDOWN:
     90                         button = MouseButtons.Middle;
     91                         clickCount = 1;
     92                         MouseDownEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
     93                         break;
     94                     case WM_LBUTTONUP:
     95                         button = MouseButtons.Left;
     96                         clickCount = 1;
     97                         MouseUpEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
     98                         break;
     99                     case WM_RBUTTONUP:
    100                         button = MouseButtons.Right;
    101                         clickCount = 1;
    102                         MouseUpEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
    103                         break;
    104                     case WM_MBUTTONUP:
    105                         button = MouseButtons.Middle;
    106                         clickCount = 1;
    107                         MouseUpEvent(this, new MouseEventArgs(button, clickCount, point.X, point.Y, 0));
    108                         break;
    109                 }
    110 
    111                 this.Point = new Point(MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y);
    112                 return Win32Api.CallNextHookEx(hHook, nCode, wParam, lParam);
    113             }
    114         }
    115 
    116 
    117         [StructLayout(LayoutKind.Sequential)]
    118         public class POINT
    119         {
    120             public int x;
    121 
    122             public int y;
    123 
    124         }
    125         [StructLayout(LayoutKind.Sequential)]
    126         public class MouseHookStruct
    127         {
    128 
    129             public POINT pt;
    130 
    131             public int hwnd;
    132 
    133             public int wHitTestCode;
    134 
    135             public int dwExtraInfo;
    136 
    137         }
    138 
    139 
    140 
    141 
    142         public delegate void MouseMoveHandler(object sender, MouseEventArgs e);
    143         public event MouseMoveHandler MouseMoveEvent;
    144         public delegate void MouseDownHandler(object sender, MouseEventArgs e);
    145         public event MouseDownHandler MouseDownEvent;
    146         public delegate void MouseUpHandler(object sender, MouseEventArgs e);
    147         public event MouseUpHandler MouseUpEvent;
    148     }
    View Code

    键盘钩子:

      1 class KeyHook
      2     {
      3         public event KeyEventHandler KeyDownEvent;
      4         public event KeyPressEventHandler KeyPressEvent;
      5         public event KeyEventHandler KeyUpEvent;
      6 
      7         
      8         static int hKeyboardHook = 0; //声明键盘钩子处理的初始值
      9                                       //值在Microsoft SDK的Winuser.h里查询
     10         public const int WH_KEYBOARD_LL = 13;   //线程键盘钩子监听鼠标消息设为2,全局键盘监听鼠标消息设为13
     11         Win32Api.HookProc KeyboardHookProcedure; //声明KeyboardHookProcedure作为HookProc类型
     12                                                     //键盘结构
     13 
     14         [StructLayout(LayoutKind.Sequential)]
     15         public class KeyboardHookStruct
     16         {
     17             public int vkCode;  //定一个虚拟键码。该代码必须有一个价值的范围1至254
     18             public int scanCode; // 指定的硬件扫描码的关键
     19             public int flags;  // 键标志
     20             public int time; // 指定的时间戳记的这个讯息
     21             public int dwExtraInfo; // 指定额外信息相关的信息
     22         }
     23         
     24 
     25         // 取得当前线程编号(线程钩子需要用到)
     26         [DllImport("kernel32.dll")]
     27         static extern int GetCurrentThreadId();
     28 
     29         //使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效
     30         [DllImport("kernel32.dll")]
     31         public static extern IntPtr GetModuleHandle(string name);
     32 
     33         /// <summary>
     34         /// 安装键盘钩子
     35         /// </summary>
     36         public void Start()
     37         {
     38             // 安装键盘钩子
     39             if (hKeyboardHook == 0)
     40             {
     41                 KeyboardHookProcedure = new Win32Api.HookProc(KeyboardHookProc);
     42                 hKeyboardHook = Win32Api.SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
     43                 //hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
     44                 //************************************
     45                 //键盘线程钩子
     46                 Win32Api.SetWindowsHookEx(13, KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId());//指定要监听的线程idGetCurrentThreadId(),
     47                                                                                                //键盘全局钩子,需要引用空间(using System.Reflection;)
     48                                                                                                //如果SetWindowsHookEx失败
     49                 if (hKeyboardHook == 0)
     50                 {
     51                     Stop();
     52                     throw new Exception("安装键盘钩子失败");
     53                 }
     54             }
     55         }
     56 
     57         /// <summary>
     58         /// 卸载键盘钩子
     59         /// </summary>
     60         public void Stop()
     61         {
     62             bool retKeyboard = true;
     63 
     64 
     65             if (hKeyboardHook != 0)
     66             {
     67                 retKeyboard = Win32Api.UnhookWindowsHookEx(hKeyboardHook);
     68                 hKeyboardHook = 0;
     69             }
     70             
     71             if (!(retKeyboard)) throw new Exception("卸载钩子失败!");
     72         }
     73         //ToAscii职能的转换指定的虚拟键码和键盘状态的相应字符或字符
     74 
     75 
     76 
     77 
     78         [DllImport("user32")]
     79         public static extern int ToAscii(int uVirtKey, //[in] 指定虚拟关键代码进行翻译。
     80                                          int uScanCode, // [in] 指定的硬件扫描码的关键须翻译成英文。高阶位的这个值设定的关键,如果是(不压)
     81                                          byte[] lpbKeyState, // [in] 指针,以256字节数组,包含当前键盘的状态。每个元素(字节)的数组包含状态的一个关键。如果高阶位的字节是一套,关键是下跌(按下)。在低比特,如果设置表明,关键是对切换。在此功能,只有肘位的CAPS LOCK键是相关的。在切换状态的NUM个锁和滚动锁定键被忽略。
     82                                          byte[] lpwTransKey, // [out] 指针的缓冲区收到翻译字符或字符。
     83                                          int fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.
     84         //获取按键的状态
     85         [DllImport("user32")]
     86         public static extern int GetKeyboardState(byte[] pbKeyState);
     87 
     88         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
     89         private static extern short GetKeyState(int vKey);
     90 
     91 
     92         /// <summary>
     93         /// 执行键盘钩子
     94         /// </summary>
     95         /// <param name="nCode"></param>
     96         /// <param name="wParam"></param>
     97         /// <param name="lParam"></param>
     98         /// <returns></returns>
     99         private int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
    100         {
    101             // 侦听键盘事件
    102             if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))
    103             {
    104                 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
    105                 // 键盘按下
    106                 if (KeyDownEvent != null && ((Int32)wParam == (Int32)KeyEvent.WM_KEYDOWN || (Int32)wParam == (Int32)KeyEvent.WM_SYSKEYDOWN))
    107                 {
    108                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
    109                     KeyEventArgs e = new KeyEventArgs(keyData);
    110                     KeyDownEvent(this, e);
    111                 }
    112 
    113                 //键盘点击
    114                 if (KeyPressEvent != null && (Int32)wParam == (Int32)KeyEvent.WM_KEYDOWN)
    115                 {
    116                     byte[] keyState = new byte[256];
    117                     GetKeyboardState(keyState);
    118 
    119                     byte[] inBuffer = new byte[2];
    120                     if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)
    121                     {
    122                         KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
    123                         KeyPressEvent(this, e);
    124                     }
    125                 }
    126 
    127                 // 键盘抬起
    128                 if (KeyUpEvent != null && ((Int32)wParam == (Int32)KeyEvent.WM_KEYUP || (Int32)wParam == (Int32)KeyEvent.WM_SYSKEYUP))
    129                 {
    130                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
    131                     KeyEventArgs e = new KeyEventArgs(keyData);
    132                     KeyUpEvent(this, e);
    133                 }
    134 
    135             }
    136             //如果返回1,则结束消息,这个消息到此为止,不再传递。
    137             //如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者
    138             return Win32Api.CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
    139         }
    140 
    141         ~KeyHook()
    142         {
    143             Stop();
    144         }
    145 
    146       
    147     }
    View Code

    第三步、点击调用鼠标键盘钩子:

    点击调用鼠标钩子

     1 //鼠标事件监听
     2 public partial class MouseEventHook
     3 {
     4     MouseHook mh;     //定义全局的鼠标监听方法
     5     Point p1 = new Point(0, 0);   //定义全局鼠标坐标
     6     Point p2 = new Point(0, 0);   //多定义个坐标可以显示鼠标按下与抬起之间移动的距离,这个也可不要
     7 
     8     //点击按钮开始鼠标监听
     9     private void button1_Click(object sender, EventArgs e)
    10     {
    11            mh = new MouseHook();
    12            mh.SetHook();     //安装鼠标钩子
    13 
    14            mh.MouseMoveEvent += my_MouseMoveEvent;     //绑定鼠标移动时触发的事件
    15            mh.MouseDownEvent += my_MouseDownEvent;    //绑定鼠标按下时触发的事件
    16            mh.MouseUpEvent += my_MouseUpEvent;            //绑定鼠标抬起时触发的事件
    17     }
    18 
    19     
    20     //点击按钮停止鼠标监听
    21     private void button2_Click(object sender, EventArgs e)
    22     {
    23         if (mh != null) mh.UnHook();
    24         MessageBox.Show("鼠标监听停止!");
    25     }
    26 
    27 
    28 
    29         // 鼠标移动触发的事件
    30         private void my_MouseMoveEvent(object 
    31  sender,MouseEventArgs e)
    32         {
    33             Point p = e.Location;   //获取坐标
    34             string movexy = p.ToString();
    35             richTextBox1.AppendText(movexy + "," + stopWatch.ElapsedMilliseconds + "
    ");   //将坐标记录到RichTextBox控件中
    36         }
    37 
    38 
    39          //按下鼠标键触发的事件
    40         private void my_MouseDownEvent(object sender, MouseEventArgs e)
    41         {
    42             if (e.Button == MouseButtons.Left)
    43             {
    44                 LeftTag = true;
    45                 richTextBox1.AppendText("按下了左键
    ");    //将按键操作记录到RichTextBox控件中
    46             }
    47             if (e.Button == MouseButtons.Right)
    48             {
    49                 RightTag = true;
    50                 richTextBox1.AppendText("按下了右键
    ");
    51             }
    52             p1 = e.Location;
    53             
    54         }
    55 
    56 
    57         //松开鼠标键触发的事件
    58         private void my_MouseUpEvent(object sender, MouseEventArgs e)
    59         {
    60             p2 = e.Location;
    61             double value = Math.Sqrt(Math.Abs(p1.X - p2.X) * Math.Abs(p1.X - p2.X) + Math.Abs(p1.Y - p2.Y) * Math.Abs(p1.Y - p2.Y));
    62             if (e.Button == MouseButtons.Left)
    63             {
    64                 richTextBox1.AppendText("松开了左键  " + LineNum + "
    ");
    65 
    66             }
    67             if (e.Button == MouseButtons.Right)
    68             {
    69                 richTextBox1.AppendText("松开了右键  " + LineNum + "
    ");
    70             }
    71             richTextBox1.AppendText("移动了" + value + "距离
    ");
    72             RightTag = false;
    73             LeftTag = false;
    74             p1 = new Point(0, 0);
    75             p2 = new Point(0, 0);
    76         }
    77 
    78 
    79 
    80 }    
    View Code

     点击调用键盘钩子

     1 public partial class KeyEventHook
     2 {
     3         KeyHook k_hook;
     4         KeyEventHandler myKeyDownHandeler;
     5         KeyEventHandler myKeyUpHandeler;
     6         
     7         /// <summary>
     8         /// 开始键盘监听
     9         /// </summary>
    10         public void startKeyListen()
    11         {
    12             k_hook = new KeyHook();
    13             myKeyDownHandeler = new KeyEventHandler(hook_KeyDown);
    14             k_hook.KeyDownEvent += myKeyDownHandeler;//钩住键盘按下
    15             myKeyUpHandeler = new KeyEventHandler(hook_KeyUp);
    16             k_hook.KeyUpEvent += myKeyUpHandeler;//钩住键盘抬起
    17             k_hook.Start();//安装键盘钩子
    18         }
    19 
    20         /// <summary>
    21         /// 结束键盘监听
    22         /// </summary>
    23         public void stopKeyListen()
    24         {
    25             if (myKeyDownHandeler != null)
    26             {
    27                 k_hook.KeyDownEvent -= myKeyDownHandeler;//取消按键事件
    28                 myKeyDownHandeler = null;
    29                 k_hook.Stop();//关闭键盘钩子
    30             }
    31         }
    32 
    33 
    34 
    35         /// <summary>
    36         /// 键盘按下时就调用这个
    37         /// </summary>
    38         /// <param name="sender"></param>
    39         /// <param name="e"></param>
    40         public void hook_KeyDown(object sender, KeyEventArgs e)
    41         {
    42             if (e.KeyCode.Equals(Keys.Escape))         //如果按下Esc键可执行if里面的操作
    43             {
    44                        //按下特定键后执行的代码
    45             }
    46             Log.LogMouseEvent(e.KeyCode.ToString() + ",键盘按下" );      //这个Log.LogMouseEvent是我用来记录键盘按下抬起的日志,
    47             
    48         }
    49 
    50         /// <summary>
    51         /// 有键盘抬起时就调用这个
    52         /// </summary>
    53         /// <param name="sender"></param>
    54         /// <param name="e"></param>
    55         public void hook_KeyUp(object sender, KeyEventArgs e)
    56         {
    57              Log.LogMouseEvent(e.KeyCode.ToString() + ",键盘抬起");      //这个Log.LogMouseEvent是我用来记录键盘按下抬起的日志
    58                 
    59         }
    60 }
    View Code

    总结:

    监听流程为:安装钩子——》触发钩子后调用相应的绑定事件——》调用下一个钩子——》监听完成,卸载钩子

    钩子在程序退出之后会自动卸载,不过那样容易出错而且不好控制,最好还是手动卸载

    END

  • 相关阅读:
    Thinking in Java Reading Note(9.接口)
    Thinking in java Reading Note(8.多态)
    Thinking in Java Reading Note(7.复用类)
    SQL必知必会
    Thinking in Java Reading Note(5.初始化与清理)
    Thinking in Java Reading Note(2.一切都是对象)
    鸟哥的Linux私房菜笔记(1.基础)
    Thinking in Java Reading Note(1.对象导论)
    CoreJava2 Reading Note(2:I/O)
    CoreJava2 Reading Note(1:Stream)
  • 原文地址:https://www.cnblogs.com/kk138/p/14505659.html
Copyright © 2011-2022 走看看