zoukankan      html  css  js  c++  java
  • 鼠标键盘钩子捕获(初版)

    兴趣使然,想找到C#鼠标键盘钩子代码在网上查找资料发现C#鼠标钩子的资料不太完善,在实际操作过程中发现有些效果也不太理想,

    所以自己进行了修改完善,当然,学识有限,还有很多地方需要完善,

    欢迎指正!

    这个项目不能直接启动,需要使用Ctrl+F5非调试启动或者直接启动bin文件下编译程序才不出错

    :这个只是学习参考,可别动坏脑筋

      1 using System;
      2 
      3 using System.Runtime.InteropServices;
      4 using System.Windows.Forms;
      5 using System.Reflection;
      6 
      7 namespace autoInput.Support
      8 {
      9     /// <summary>
     10     /// 键盘钩子
     11     /// [以下代码来自某网友,并由“Murphy丶悦”改造完善]
     12     /// [仅供参考学习,请勿进行非法操作]
     13     /// </summary>
     14     public class KeyboardHook
     15     {
     16         public event KeyEventHandler KeyDownEvent;
     17         public event KeyPressEventHandler KeyPressEvent;
     18         public event KeyEventHandler KeyUpEvent;
     19         public event MouseEventHandler MouseDownDouble;
     20         public event MouseEventHandler MouseDown;
     21         public event MouseEventHandler MouseDownRight;
     22         public event MouseEventHandler Mousemov;
     23         public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
     24         static int hKeyboardHook = 0; //声明键盘钩子处理的初始值
     25         static int hMouseHook = 0;//申明鼠标钩子处理的初始值
     26         //值在Microsoft SDK的Winuser.h里查询
     27         public const int WH_KEYBOARD_LL = 13;   //线程键盘钩子监听鼠标消息设为2,全局键盘监听鼠标消息设为13
     28         HookProc KeyboardHookProcedure; //声明KeyboardHookProcedure作为HookProc类型
     29         HookProc MouseHookProceduremz; //声明MouseHookProcedure作为HookProc类型
     30 
     31         //键盘结构
     32         [StructLayout(LayoutKind.Sequential)]
     33         public class KeyboardHookStruct
     34         {
     35             public int vkCode;  //定一个虚拟键码。该代码必须有一个价值的范围1至254
     36             public int scanCode; // 指定的硬件扫描码的关键
     37             public int flags;  // 键标志
     38             public int time; // 指定的时间戳记的这个讯息
     39             public int dwExtraInfo; // 指定额外信息相关的信息
     40         }
     41         //使用此功能,安装了一个钩子
     42         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
     43         public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
     44 
     45 
     46         //调用此函数卸载钩子
     47         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
     48         public static extern bool UnhookWindowsHookEx(int idHook);
     49 
     50 
     51         //使用此功能,通过信息钩子继续下一个钩子
     52         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
     53         public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
     54 
     55         // 取得当前线程编号(线程钩子需要用到)
     56         [DllImport("kernel32.dll")]
     57         static extern int GetCurrentThreadId();
     58 
     59         //使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效
     60         [DllImport("kernel32.dll")]
     61         public static extern IntPtr GetModuleHandle(string name);
     62 
     63         //引入系统的双击时间
     64         [DllImport("user32.dll")]
     65         public static extern int GetDoubleClickTime();
     66 
     67         public void Start()
     68         {
     69             //安装鼠标钩子
     70             if (hMouseHook == 0)
     71             {
     72                 MouseHookProceduremz = new HookProc(MouseHookProcedure);
     73                 hMouseHook = SetWindowsHookEx(14, MouseHookProceduremz, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
     74                 if (hMouseHook == 0)
     75                 {
     76                     Stopmouse();
     77                     MessageBox.Show("安装鼠标钩子失败");
     78                 }
     79             }
     80             // 安装键盘钩子
     81             if (hKeyboardHook == 0)
     82             {
     83                 KeyboardHookProcedure = new HookProc(KeyboardHookProc);
     84                 hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
     85                 // hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
     86                 //键盘线程钩子
     87                 // SetWindowsHookEx( 2,KeyboardHookProcedure, IntPtr.Zero,GetCurrentThreadId());
     88                 //指定要监听的线程idGetCurrentThreadId(),
     89                 //键盘全局钩子,需要引用空间(using System.Reflection;)`
     90                 //关于SetWindowsHookEx (int idHook, HookProc lpfn, IntPtrhInstance, int threadId)函数将钩子加入到钩子链表中,说明一下四个参数:
     91                 //idHook 钩子类型,即确定钩子监听何种消息,上面的代码中设为2,即监听键盘消息并且是线程钩子,如果是全局钩子监听键盘消息应设为13,
     92                 //线程钩子监听鼠标消息设为7,全局钩子监听鼠标消息设为14。
     93                 //lpfn钩子子程的地址指针。如果dwThreadId参数为0 或是一个由别的进程创建的线程的标识,lpfn必须指向DLL中的钩子子程。 除此以外,
     94                 //lpfn可以指向当前进程的一段钩子子程代码。钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。hInstance应用程序实例的句柄。
     95                 //标识包含lpfn所指的子程的DLL。如果threadId 标识当前进程创建的一个线程,而且子
     96                 //程代码位于当前进程,hInstance必须为NULL。可以很简单的设定其为本应用程序的实例句柄。threadId 与安装的钩子子程相关联的线程的标识符
     97                 //如果为0,钩子子程与所有的线程关联,即为全局钩子
     98                 //如果SetWindowsHookEx失败
     99                 if (hKeyboardHook == 0)
    100                 {
    101                     Stop();
    102                     MessageBox.Show("安装键盘钩子失败");
    103                 }
    104             }
    105         }
    106         private void Stopmouse()
    107         {
    108             bool retKeyboard = true;
    109             if (hMouseHook != 0)
    110             {
    111                 retKeyboard = UnhookWindowsHookEx(hMouseHook);
    112                 hMouseHook = 0;
    113             }
    114             if (!(retKeyboard)) MessageBox.Show("卸载鼠标钩子失败");
    115         }
    116         #region 鼠标钩子结构体
    117         //声明一个Point的封送类型 
    118         [StructLayout(LayoutKind.Sequential)]
    119         public struct POINT
    120         {
    121             public int x;
    122             public int y;
    123         }
    124         //声明鼠标钩子的封送结构类型 
    125         [StructLayout(LayoutKind.Sequential)]
    126         public struct MouseHookStruct
    127         {
    128             public POINT pt;
    129             public int hWnd;
    130             public int wHitTestCode;
    131             public int dwExtraInfo;
    132         }
    133         //定义鼠标常数
    134         private const int WM_MOUSEMOVE = 0x200;//鼠标移动 512(int)
    135         private const int WM_LBUTTONDOWN = 0x201;//鼠标左键 513(int)
    136         private const int WM_RBUTTONDOWN = 0x204;//鼠标右键 516(int)
    137         private const int WM_MBUTTONDOWN = 0x207;//鼠标中健 519(int)
    138         private const int WM_LBUTTONUP = 0x202;//左键弹起 514(int)
    139         private const int WM_RBUTTONUP = 0x205;//右键弹起 517(int)
    140         private const int WM_MBUTTONUP = 0x208;//中健弹起 520(int)
    141         private const int WM_LBUTTONDBLCLK = 0x203;//双击左键 515(int)
    142         private const int WM_RBUTTONDBLCLK = 0x206;//双击右键 518(int)
    143         private const int WM_MBUTTONDBLCLK = 0x209;//双击中健 521(int)
    144 
    145         //对于鼠标双击的动作捕获,都是认为可以捕获WM_LBUTTONDBLCLK这个消息,通过该消息来设定是否为双击,
    146         //但实际挂在钩子后,Windows并不会直接捕获到WM_LBUTTONDBLCLK这个消息,而是两次WM_LBUTTONDOWN消息,
    147         //因此,如果要实现双击的捕获,可考虑计算两次点击的时间间隔,和系统的双击时间间隔进行比较,从而判定是否为双击。
    148 
    149         //在鼠标钩子中右键双击两次可以捕获到两次单击右键,
    150         //但是在这个类拿到消息并进行鼠标常数更改为双击后在窗口中只能显示出单击一次
    151         //另在各类资料中显示的中键鼠标常数全部无法捕获到
    152         #endregion
    153         //记录初始时间进行时间复位
    154         private static int InitialTime = 0;//对单击两次后时间复位,防止无限判定双击事件
    155         //上一次单击对应按键时间
    156         private static int LeftClickTime = 0;//上一次单击左键时间
    157         private static int RightClickTime = 0;//上一次单击右键时间
    158         private static int MiddleClickTime = 0;//上一次单击中键时间
    159         //上一次单击事件坐标
    160         private int lastX = 0;//上一次鼠标X轴位置
    161         private int lastY = 0;//上一次鼠标Y轴位置
    162         public int i = 0;
    163         private int MouseHookProcedure(int nCode, int wParam, IntPtr lParam)
    164         {
    165             int clickCount = 0;
    166             if ((nCode >= 0) && (MouseDown != null || MouseDownDouble != null || Mousemov != null || MouseDownRight != null))
    167             {
    168                 MouseButtons button = MouseButtons.None;
    169 
    170                 MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
    171                 int X = MyMouseHookStruct.pt.x;//当前单击事件X轴坐标
    172                 int Y = MyMouseHookStruct.pt.y;//当前单击事件Y轴坐标
    173 
    174                 //进行时间比对;手动对鼠标按键命令更改
    175                 if (wParam == WM_LBUTTONDOWN)//对单击左键进行判定
    176                     wParam = changeGorge(wParam, LeftClickTime, WM_LBUTTONDBLCLK, X, Y);
    177                 if(wParam== WM_RBUTTONDOWN)//对单击右键进行判定
    178                     wParam= changeGorge(wParam, RightClickTime, WM_RBUTTONDBLCLK, X, Y);
    179                 if (wParam == WM_MBUTTONDOWN)//对单击中键进行判定
    180                     wParam = changeGorge(wParam, MiddleClickTime, WM_MBUTTONDBLCLK, X, Y);
    181 
    182                 switch (wParam)
    183                 {
    184                     //左键按下
    185                     case WM_LBUTTONDOWN:
    186                         button = MouseButtons.Left;
    187                         clickCount = 1;
    188                         break;
    189                     //左键抬起
    190                     case WM_LBUTTONUP:
    191                         button = MouseButtons.Left;
    192                         clickCount = 1;
    193                         break;
    194                     //双击左键
    195                     case WM_LBUTTONDBLCLK:
    196                         button = MouseButtons.Left;
    197                         clickCount = 2;
    198                         break;
    199                     //中键按下
    200                     case WM_MBUTTONDOWN:
    201                         button = MouseButtons.Middle;
    202                         clickCount = 1;
    203                         break;
    204                     //中键抬起
    205                     case WM_MBUTTONUP:
    206                         button = MouseButtons.Middle;
    207                         clickCount = 1;
    208                         break;
    209                     //双击中键
    210                     case WM_MBUTTONDBLCLK:
    211                         button = MouseButtons.Middle;
    212                         clickCount = 2;
    213                         break;
    214                     //右键按下
    215                     case WM_RBUTTONDOWN:
    216                         button = MouseButtons.Right;
    217                         clickCount = 1;
    218                         break;
    219                     //右键抬起
    220                     case WM_RBUTTONUP:
    221                         button = MouseButtons.Right;
    222                         clickCount = 1;
    223                         break;
    224                     //双击右键
    225                     case WM_RBUTTONDBLCLK:
    226                         button = MouseButtons.Right;
    227                         clickCount = 2;
    228                         break;
    229                     case WM_MOUSEMOVE:
    230                         clickCount = 0;
    231                         break;
    232                 }
    233 
    234                 MouseEventArgs e = new MouseEventArgs(button, clickCount, MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y, 0);
    235 
    236                 //raise KeyDown;
    237                 if (MouseDown != null && wParam == WM_LBUTTONDOWN)//单击事件
    238                 {
    239                     MouseDown(this, e);
    240                 }
    241                 if (this.MouseDownDouble != null && wParam == WM_LBUTTONDBLCLK)//双击事件
    242                 {
    243                     MouseDownDouble(this, e);
    244                 }
    245                 if (this.Mousemov != null && wParam == WM_LBUTTONDOWN && wParam == WM_MOUSEMOVE) //拖动事件
    246                 {
    247                     Mousemov(this, e);
    248                 }
    249                 if (this.MouseDownRight != null && wParam == WM_RBUTTONDOWN)//右击事件
    250                 {
    251                     MouseDownRight(this, e);
    252                 }
    253             }
    254 
    255             //如果返回1,则结束消息,这个消息到此为止,不再传递。
    256             //如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者 
    257             return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
    258         }
    259 
    260         public void Stop()
    261         {
    262             bool retKeyboard = true;
    263             if (hKeyboardHook != 0)
    264             {
    265                 retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
    266                 hKeyboardHook = 0;
    267             }
    268             if (!(retKeyboard)) MessageBox.Show("卸载键盘钩子失败");
    269         }
    270         //ToAscii职能的转换指定的虚拟键码和键盘状态的相应字符或字符
    271         [DllImport("user32")]
    272         public static extern int ToAscii(int uVirtKey, //[in] 指定虚拟关键代码进行翻译。
    273                                          int uScanCode, // [in] 指定的硬件扫描码的关键须翻译成英文。高阶位的这个值设定的关键,如果是(不压)
    274                                          byte[] lpbKeyState, // [in] 指针,以256字节数组,包含当前键盘的状态。每个元素(字节)的数组包含状态的一个关键。如果高阶位的字节是一套,关键是下跌(按下)。在低比特,如果设置表明,关键是对切换。在此功能,只有肘位的CAPS LOCK键是相关的。在切换状态的NUM个锁和滚动锁定键被忽略。
    275                                          byte[] lpwTransKey, // [out] 指针的缓冲区收到翻译字符或字符。
    276                                          int fuState); // [in] Specifieswhether a menu is active. This parameter must be 1 if a menu is active, or 0otherwise.
    277 
    278         //获取按键的状态
    279         [DllImport("user32")]
    280         public static extern int GetKeyboardState(byte[] pbKeyState);
    281 
    282 
    283         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    284         private static extern short GetKeyState(int vKey);
    285 
    286         private const int WM_KEYDOWN = 0x100;//KEYDOWN
    287         private const int WM_KEYUP = 0x101;//KEYUP
    288         private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN
    289         private const int WM_SYSKEYUP = 0x105;//SYSKEYUP
    290 
    291         private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
    292         {
    293             // 侦听键盘事件
    294             if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))
    295             {
    296                 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
    297                 // raise KeyDown
    298                 if (KeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
    299                 {
    300                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
    301                     KeyEventArgs e = new KeyEventArgs(keyData);
    302                     KeyDownEvent(this, e);
    303                 }
    304 
    305                 //键盘按下
    306                 if (KeyPressEvent != null && wParam == WM_KEYDOWN)
    307                 {
    308                     byte[] keyState = new byte[256];
    309                     GetKeyboardState(keyState);
    310 
    311                     byte[] inBuffer = new byte[2];
    312                     if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)
    313                     {
    314                         KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
    315                         KeyPressEvent(this, e);
    316                     }
    317                 }
    318 
    319                 // 键盘抬起
    320                 if (KeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
    321                 {
    322                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
    323                     KeyEventArgs e = new KeyEventArgs(keyData);
    324                     KeyUpEvent(this, e);
    325                 }
    326 
    327             }
    328             //如果返回1,则结束消息,这个消息到此为止,不再传递。
    329             //如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者
    330             return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
    331         }
    332 
    333         /// <summary>
    334         /// 对单击事件进行判定是否为双击事件并进行修改
    335         /// </summary>
    336         /// <param name="wParams">串口内的单击事件消息</param>
    337         /// <param name="lastClickTime">上一次单击事件事件</param>
    338         /// <param name="doubleClick">正确双击事件消息</param>
    339         /// <param name="X">上一次单击X轴坐标</param>
    340         /// <param name="Y">上一次单击Y轴坐标</param>
    341         /// <returns></returns>
    342         public int changeGorge(int wParam,int lastClickTime,int doubleClick,int X,int Y)
    343         {
    344             int deltaMs = GetDoubleClickTime() + lastClickTime;
    345             if (deltaMs >= dateTimeToInt(DateTime.Now) && lastX == X && lastY == Y)//在系统双击判定时间内同一坐标单击两次更改为双击事件
    346             {
    347                 wParam = doubleClick;
    348                 if (lastClickTime == LeftClickTime)
    349                     LeftClickTime = InitialTime;
    350                 else if (lastClickTime == RightClickTime)
    351                     RightClickTime = InitialTime;
    352                 else if (lastClickTime == MiddleClickTime)
    353                     MiddleClickTime = InitialTime;
    354             }
    355             else
    356             {
    357                 if (lastClickTime == LeftClickTime)
    358                 {
    359                     LeftClickTime = dateTimeToInt(DateTime.Now);
    360                     RightClickTime = InitialTime;
    361                     MiddleClickTime = InitialTime;
    362                 }
    363                 else if (lastClickTime == RightClickTime)
    364                 {
    365                     RightClickTime = dateTimeToInt(DateTime.Now);
    366                     LeftClickTime = InitialTime;
    367                     MiddleClickTime = InitialTime;
    368                 }
    369                 else if (lastClickTime == MiddleClickTime)
    370                 {
    371                     MiddleClickTime = dateTimeToInt(DateTime.Now);
    372                     LeftClickTime = InitialTime;
    373                     RightClickTime = InitialTime;
    374                 }
    375             }
    376             lastX = X;
    377             lastY = Y;
    378             return wParam;
    379         }
    380         /// <summary>
    381         /// 转换时间格式为HHmmssfff
    382         /// </summary>
    383         /// <param name="time">需转换的时间</param>
    384         /// <returns></returns>
    385         private int dateTimeToInt(DateTime time)
    386         {
    387             //转换时间格式为int类型并精确到毫秒
    388             return Convert.ToInt32(time.ToString("HHmmssfff"));
    389         }
    390         ~KeyboardHook()
    391         {
    392             Stop();
    393         }
    394     }
    395 }

    下面是显示界面,使用的是WinFrom窗口,在窗口内用一个文本框和一个定时器

     1 using System;
     2 using System.Windows.Forms;
     3 
     4 using autoInput.Support;
     5 
     6 namespace autoInput
     7 {
     8     public partial class autoInput : Form
     9     {
    10         public autoInput()
    11         {
    12             InitializeComponent();
    13         }
    14 
    15         KeyboardHook k_hook;
    16         private void autoInput_Load(object sender, EventArgs e)
    17         {
    18             k_hook = new KeyboardHook();
    19             k_hook.KeyDownEvent += new KeyEventHandler(hook_KeyDown);//全局按键事件
    20             k_hook.MouseDown += k_hook_MouseDown;//全局鼠标单击事件
    21             k_hook.MouseDownDouble += k_hook_MouseDown;//双击事件
    22             k_hook.Mousemov += k_hook_MouseDown;//拖动事件
    23             k_hook.MouseDownRight += k_hook_MouseDown;//右击事件
    24             k_hook.Start();//安装键盘钩子
    25         }
    26 
    27         public string button = string.Empty;
    28         public string keyboardRecordText = string.Empty;
    29         //单击事件
    30         void k_hook_MouseDown(object sender, MouseEventArgs e) 
    31         {
    32             if (keyboardRecordText != string.Empty && e.Button.ToString() != button)
    33                 showText();
    34             button = e.Button.ToString();
    35             keyboardRecordText=string.Format("Button:{0} Clicks:{1} \tX:{2} Y:{3} \t{4} \n", e.Button, e.Clicks, e.X, e.Y, DateTime.Now.ToString("HH:mm:ss.fff"));
    36 
    37             if (e.Clicks == 2)
    38             {
    39                 showText();
    40                 keyboardRecordText = string.Empty;
    41 
    42             }
    43             else
    44             {
    45                 this.timer.Enabled = true;
    46                 timer.Interval = KeyboardHook.GetDoubleClickTime();
    47             }
    48         }
    49 
    50         /// <summary>
    51         /// 键盘事件
    52         /// </summary>
    53         /// <param name="sender"></param>
    54         /// <param name="e"></param>
    55         private void hook_KeyDown(object sender, KeyEventArgs e)
    56         {
    57             if (e.KeyValue == (int)Keys.Space)
    58             {
    59                 keyboardRecord.Text += " ";
    60             }
    61             else if (e.KeyValue == (int)Keys.Enter)
    62             {
    63                 keyboardRecord.Text += "\n";
    64             }
    65             else
    66             {
    67                 keyboardRecord.Text += e.KeyData.ToString();
    68             }
    69         }
    70 
    71         /// <summary>
    72         /// 定时器
    73         /// </summary>
    74         /// <param name="sender"></param>
    75         /// <param name="e"></param>
    76         private void timer_Tick(object sender, EventArgs e)
    77         {
    78             showText();
    79             this.timer.Enabled = false;
    80         }
    81         public void showText()
    82         {
    83             //keyboardRecord窗口程序内文本框的名称
    84             keyboardRecord.Text += keyboardRecordText;
    85             keyboardRecordText = string.Empty;
    86         }
    87     }
    88 }

    下面是我的项目截图

  • 相关阅读:
    团队作业八——第二次团队冲刺(Beta版本)第6天
    团队作业八——第二次团队冲刺(Beta版本)第5天
    团队作业八——第二次团队冲刺(Beta版本)第4天
    个人作业5——软工个人总结
    个人作业4——alpha阶段个人总结
    个人作业3-案例分析
    软件工程网络15结对编程作业(201521123062)
    软件工程网络15个人阅读作业2(201521123062 杨钧宇)
    软件工程网络15个人阅读作业1(201521123062 杨钧宇)
    Java课程设计-随机密码生成器
  • 原文地址:https://www.cnblogs.com/NBDWDYS2214143926/p/6597117.html
Copyright © 2011-2022 走看看