经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
C#模拟键盘输入、键状态和监听键盘消息
来源:cnblogs  作者:左眼水星  时间:2024/8/19 15:45:53  对本文有异议

模拟键盘输入

模拟键盘输入的功能需要依赖Windows函数实现,这个函数是SendInput,它是专门用来模拟键盘、鼠标等设备输入的函数。

另外和键盘输入相关的函数还有SendKeys,它是System.Windows.Forms. SendKeys,只能在WinFrom项目中使用,并且它的所有功能都可以由SendInput来实现。

另一个是keybd_event函数,这个函数依然是有用的,但是目前官方已经推荐使用SendInput替代它了。

SendInput的定义
  1. [DllImport("user32.dll")]
  2. static extern uint SendInput(int nInputs,INPUT[] pInputs,int cbSize);

INPUT对象中保存了输入内容,nInputs和cbSize代表pInputs的长度和INPUT结构的大小,这两个参数能帮助SendInput正确解析INPUT对象。返回值0表示失败,非零表示正确执行。

 

INPUT的定义
  1. [StructLayout(LayoutKind.Sequential)]
  2. struct KEYBDINPUT {
  3. public ushort wVk;
  4. public ushort wScan;
  5. public uint dwFlags;
  6. public uint time;
  7. public IntPtr dwExtraInfo;
  8. }
  9. [StructLayout(LayoutKind.Sequential)]
  10. struct HARDWAREINPUT {
  11. public uint uMsg;
  12. public ushort wParamL;
  13. public ushort wParamH;
  14. }
  15. [StructLayout(LayoutKind.Sequential)]
  16. struct MOUSEINPUT {
  17. public int dx;
  18. public int dy;
  19. public uint mouseData;
  20. public uint dwFlags;
  21. public uint time;
  22. public IntPtr dwExtraInfo;
  23. }
  24. [StructLayout(LayoutKind.Explicit)]
  25. struct MOUSEKEYBDHARDWAREINPUT {
  26. [FieldOffset(0)]
  27. public HARDWAREINPUT hi;
  28. [FieldOffset(0)]
  29. public KEYBDINPUT ki;
  30. [FieldOffset(0)]
  31. public MOUSEINPUT mi;
  32. }
  33. [StructLayout(LayoutKind.Sequential)]
  34. struct INPUT {
  35. public uint type;
  36. public MOUSEKEYBDHARDWAREINPUT mkhi;
  37. }

INPUT结构中的type表示消息类型,值为1表示键盘消息。mkhi表示具体的消息内容,它可以模拟三类消息,其中键盘消息使用KEYBDINPUT表示,其它消息类型的结构不在这里介绍(虽然用不到MOUSEINPUT等结构,但是它们的定义不能省略,否则SendInput无法正确解析INPUT中的具体内容)。

?FieldOffset(0)将三个结构的起始都放在0位置,所以只能使用其中一个内容,因为一个INPUT也只能表示一个消息,这样设计可以节省空间。

KEYBDINPUT结构中的wVK表示虚拟键码 ,dwFlags的第一位bit默认0表示键盘按下事件,1表示键盘释放事件。

虚拟键码是一种能让Windows以与设备无关的方式处理键盘的技术,可以简单理解为:键盘上的每个键用一个数字来表示。

 

模拟A键
  1. INPUT[] inputs = new INPUT[2];
  2. inputs[0]=new INPUT {
  3. type=1,
  4. mkhi=new MOUSEKEYBDHARDWAREINPUT {
  5. ki=new KEYBDINPUT {
  6. wVk=0x41
  7. }
  8. }
  9. };
  10. inputs[1]=new INPUT {
  11. type=1,
  12. mkhi=new MOUSEKEYBDHARDWAREINPUT {
  13. ki=new KEYBDINPUT {
  14. wVk=0x41,
  15. dwFlags=2
  16. }
  17. }
  18. };
  19. SendInput(inputs.Length,inputs,Marshal.SizeOf(inputs[0]));

A键的虚拟键码是0x41。type=1表示这是键盘消息,dwFlags=2表示键盘释放事件。

这里INPUT数组模拟的就是使用物理键盘A键的过程。inputs[0]模拟A键按下,inputs[1]模拟A键释放。

 

模拟Ctrl+A
  1. INPUT[] inputs = new INPUT[4];
  2. inputs[0]=new INPUT {
  3. type=1,
  4. mkhi=new MOUSEKEYBDHARDWAREINPUT {
  5. ki=new KEYBDINPUT {
  6. wVk=0x11
  7. }
  8. }
  9. };
  10. inputs[1]=new INPUT {
  11. type=1,
  12. mkhi=new MOUSEKEYBDHARDWAREINPUT {
  13. ki=new KEYBDINPUT {
  14. wVk=0x41
  15. }
  16. }
  17. };
  18. inputs[2]=new INPUT {
  19. type=1,
  20. mkhi=new MOUSEKEYBDHARDWAREINPUT {
  21. ki=new KEYBDINPUT {
  22. wVk=0x41,
  23. dwFlags=2
  24. }
  25. }
  26. };
  27. inputs[3]=new INPUT {
  28. type=1,
  29. mkhi=new MOUSEKEYBDHARDWAREINPUT {
  30. ki=new KEYBDINPUT {
  31. wVk=0x11,
  32. dwFlags=2,
  33. }
  34. }
  35. };
  36. SendInput(inputs.Length,inputs,Marshal.SizeOf(inputs[0]));

0x11是Ctrl的虚拟键码,这里模拟了按下Ctrl键,按下A键,释放A键,释放Ctrl键的过程,实现了Ctrl+A的组合键效果。

 

SendInput除了能模拟击键消息外还可以在文本输入中模拟字符消息。

KEYBDINPUT结构的wScan表示字符内容,将dwFlags的第二位bit置1表示使用wScan属性而非wVK。

文本输入
  1. string ntext = "你好";
  2. INPUT[] inputs = new INPUT[ntext.Length*2];
  3. for(int i = 0;i<ntext.Length;i++) {
  4. ushort ch = ntext[i];
  5. inputs[i*2]=new INPUT {
  6. type=1,
  7. mkhi=new MOUSEKEYBDHARDWAREINPUT {
  8. ki=new KEYBDINPUT {
  9. wScan=ch,
  10. dwFlags=4
  11. }
  12. }
  13. };
  14. inputs[i*2+1]=new INPUT {
  15. type=1,
  16. mkhi=new MOUSEKEYBDHARDWAREINPUT {
  17. ki=new KEYBDINPUT {
  18. wScan=ch,
  19. dwFlags=4|2
  20. }
  21. }
  22. };
  23. }
  24. SendInput(inputs.Length,inputs,Marshal.SizeOf(inputs[0]));

 

键状态

有时需要知道键盘按键的当前状态,可以使用GetKeyState函数。

GetKeyState的定义
  1. [DllImport("user32.dll")]
  2. static extern short GetKeyState(int VKey);

参数是键的虚拟码,对于开关键(Caps Look、Num Lock和Scroll Lock),返回值1表示开启状态。对于其它键返回负数表示按下状态。

CapsLock键状态
  1. short iState = GetKeyState(0x14);

 

监听键盘消息

对于WinForm和WPF程序,要监听输入到本程序的键盘消息直接使用窗口的KeyDown和KeyUp事件即可。

对于其它键盘消息(即给本程序以外的键盘消息),需要使用钩子(hook)。

钩子是Windows系统消息处理机制中的一个节点,可以安装钩子来监听系统中的Windows消息。

Windows消息分很多种,对于特定的一类消息需要使用对应的特定类型的钩子,这里只介绍键盘消息的钩子。

钩子的安装需要调用系统SetWindowsHookEx方法。

 

SetWindowsHookEx的定义
  1. [DllImport("user32.dll")]
  2. static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hmod, int threadID);

idHook等于13表示全局键盘消息钩子,lpfn代表键盘消息处理程序,返回非IntPtr.Zero表示安装成功。

 

安装钩子
  1. delegate int HookProc(int code,IntPtr wParam,IntPtr lParam);
  2. static HookProc KeyboardProc;
  3. static void InstallKeyboardHook() {
  4. KeyboardProc=KeyboardHookCallback;
  5. pKeyboardHook=SetWindowsHookEx(13,keyboardProc,IntPtr.Zero,0);
  6. }

KeyboardHookCallback就是自定义的具体处理键盘消息的方法。

 

消息处理
  1. [DllImport("user32.dll")]
  2. static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
  3. static int KeyboardHookCallback(int code,IntPtr wParam,IntPtr lParam) {
  4. if(code<0)
  5. return CallNextHookEx(IntPtr.Zero,code,wParam,lParam);
  6. int vkCode = Marshal.ReadInt32(lParam);
  7. System.Diagnostics.Debug.Write(vkCode+" ");
  8. long downup = (long)wParam;
  9. switch(downup) {
  10. case 256:
  11. System.Diagnostics.Debug.WriteLine("down");
  12. break;
  13. case 257:
  14. System.Diagnostics.Debug.WriteLine("up");
  15. break;
  16. case 260:
  17. System.Diagnostics.Debug.WriteLine("sys_down");
  18. break;
  19. case 261:
  20. System.Diagnostics.Debug.WriteLine("sys_up");
  21. break;
  22. }
  23. return CallNextHookEx(IntPtr.Zero,code,wParam,lParam);
  24. }

从lParam中读取键的虚拟码(lParam其实是指向类似前文提到的KEYBDINPUT结构的指针),wParam表示击键事件的类型。CallNextHookEx将消息传递给下一个消息处理节点。

?使用前文提到的SendInput方法模拟键盘输入也能被钩子监听到。

?应避免在消息处理过程中进行耗时操作。

 

卸载钩子需要使用UnhookWindowsHookEx

UnhookWindowsHookEx的定义
  1. [DllImport("user32.dll")]
  2. static extern bool UnhookWindowsHookEx(IntPtr pHookHandle);

传入SetWindowsHookEx的返回值即可,返回true则卸载成功。

原文链接:https://www.cnblogs.com/yxllxy/p/18361608

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号