兴趣使然,想找到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 }
下面是我的项目截图