C#模拟键盘输入、键状态和监听键盘消息
模拟键盘输入
模拟键盘输入的功能需要依赖Windows函数实现,这个函数是SendInput,它是专门用来模拟键盘、鼠标等设备输入的函数。
另外和键盘输入相关的函数还有SendKeys,它是System.Windows.Forms. SendKeys,只能在WinFrom项目中使用,并且它的所有功能都可以由SendInput来实现。
另一个是keybd_event函数,这个函数依然是有用的,但是目前官方已经推荐使用SendInput替代它了。
SendInput的定义
[DllImport("user32.dll")]
static extern uint SendInput(int nInputs,INPUT[] pInputs,int cbSize);
INPUT对象中保存了输入内容,nInputs和cbSize代表pInputs的长度和INPUT结构的大小,这两个参数能帮助SendInput正确解析INPUT对象。返回值0表示失败,非零表示正确执行。
INPUT的定义
[StructLayout(LayoutKind.Sequential)]
struct KEYBDINPUT {
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct HARDWAREINPUT {
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
}
[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT {
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Explicit)]
struct MOUSEKEYBDHARDWAREINPUT {
[FieldOffset(0)]
public HARDWAREINPUT hi;
[FieldOffset(0)]
public KEYBDINPUT ki;
[FieldOffset(0)]
public MOUSEINPUT mi;
}
[StructLayout(LayoutKind.Sequential)]
struct INPUT {
public uint type;
public MOUSEKEYBDHARDWAREINPUT mkhi;
}
INPUT结构中的type表示消息类型,值为1表示键盘消息。mkhi表示具体的消息内容,它可以模拟三类消息,其中键盘消息使用KEYBDINPUT表示,其它消息类型的结构不在这里介绍(虽然用不到MOUSEINPUT等结构,但是它们的定义不能省略,否则SendInput无法正确解析INPUT中的具体内容)。
⩥FieldOffset(0)将三个结构的起始都放在0位置,所以只能使用其中一个内容,因为一个INPUT也只能表示一个消息,这样设计可以节省空间。
KEYBDINPUT结构中的wVK表示虚拟键码 ,dwFlags的第一位bit默认0表示键盘按下事件,1表示键盘释放事件。
虚拟键码是一种能让Windows以与设备无关的方式处理键盘的技术,可以简单理解为:键盘上的每个键用一个数字来表示。
模拟A键
INPUT[] inputs = new INPUT[2];
inputs[0]=new INPUT {
type=1,
mkhi=new MOUSEKEYBDHARDWAREINPUT {
ki=new KEYBDINPUT {
wVk=0x41
}
}
};
inputs[1]=new INPUT {
type=1,
mkhi=new MOUSEKEYBDHARDWAREINPUT {
ki=new KEYBDINPUT {
wVk=0x41,
dwFlags=2
}
}
};
SendInput(inputs.Length,inputs,Marshal.SizeOf(inputs[0]));
A键的虚拟键码是0x41。type=1表示这是键盘消息,dwFlags=2表示键盘释放事件。
这里INPUT数组模拟的就是使用物理键盘A键的过程。inputs[0]模拟A键按下,inputs[1]模拟A键释放。
模拟Ctrl+A
INPUT[] inputs = new INPUT[4];
inputs[0]=new INPUT {
type=1,
mkhi=new MOUSEKEYBDHARDWAREINPUT {
ki=new KEYBDINPUT {
wVk=0x11
}
}
};
inputs[1]=new INPUT {
type=1,
mkhi=new MOUSEKEYBDHARDWAREINPUT {
ki=new KEYBDINPUT {
wVk=0x41
}
}
};
inputs[2]=new INPUT {
type=1,
mkhi=new MOUSEKEYBDHARDWAREINPUT {
ki=new KEYBDINPUT {
wVk=0x41,
dwFlags=2
}
}
};
inputs[3]=new INPUT {
type=1,
mkhi=new MOUSEKEYBDHARDWAREINPUT {
ki=new KEYBDINPUT {
wVk=0x11,
dwFlags=2,
}
}
};
SendInput(inputs.Length,inputs,Marshal.SizeOf(inputs[0]));
0x11是Ctrl的虚拟键码,这里模拟了按下Ctrl键,按下A键,释放A键,释放Ctrl键的过程,实现了Ctrl+A的组合键效果。
SendInput除了能模拟击键消息外还可以在文本输入中模拟字符消息。
KEYBDINPUT结构的wScan表示字符内容,将dwFlags的第二位bit置1表示使用wScan属性而非wVK。
文本输入
string ntext = "你好";
INPUT[] inputs = new INPUT[ntext.Length*2];
for(int i = 0;i<ntext.Length;i++) {
ushort ch = ntext[i];
inputs[i*2]=new INPUT {
type=1,
mkhi=new MOUSEKEYBDHARDWAREINPUT {
ki=new KEYBDINPUT {
wScan=ch,
dwFlags=4
}
}
};
inputs[i*2+1]=new INPUT {
type=1,
mkhi=new MOUSEKEYBDHARDWAREINPUT {
ki=new KEYBDINPUT {
wScan=ch,
dwFlags=4|2
}
}
};
}
SendInput(inputs.Length,inputs,Marshal.SizeOf(inputs[0]));
键状态
有时需要知道键盘按键的当前状态,可以使用GetKeyState函数。
GetKeyState的定义
[DllImport("user32.dll")]
static extern short GetKeyState(int VKey);
参数是键的虚拟码,对于开关键(Caps Look、Num Lock和Scroll Lock),返回值1表示开启状态。对于其它键返回负数表示按下状态。
CapsLock键状态
short iState = GetKeyState(0x14);
监听键盘消息
对于WinForm和WPF程序,要监听输入到本程序的键盘消息直接使用窗口的KeyDown和KeyUp事件即可。
对于其它键盘消息(即给本程序以外的键盘消息),需要使用钩子(hook)。
钩子是Windows系统消息处理机制中的一个节点,可以安装钩子来监听系统中的Windows消息。
Windows消息分很多种,对于特定的一类消息需要使用对应的特定类型的钩子,这里只介绍键盘消息的钩子。
钩子的安装需要调用系统SetWindowsHookEx方法。
SetWindowsHookEx的定义
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hmod, int threadID);
idHook等于13表示全局键盘消息钩子,lpfn代表键盘消息处理程序,返回非IntPtr.Zero表示安装成功。
安装钩子
delegate int HookProc(int code,IntPtr wParam,IntPtr lParam);
static HookProc KeyboardProc;
static void InstallKeyboardHook() {
KeyboardProc=KeyboardHookCallback;
pKeyboardHook=SetWindowsHookEx(13,keyboardProc,IntPtr.Zero,0);
}
KeyboardHookCallback就是自定义的具体处理键盘消息的方法。
消息处理
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
static int KeyboardHookCallback(int code,IntPtr wParam,IntPtr lParam) {
if(code<0)
return CallNextHookEx(IntPtr.Zero,code,wParam,lParam);
int vkCode = Marshal.ReadInt32(lParam);
System.Diagnostics.Debug.Write(vkCode+" ");
long downup = (long)wParam;
switch(downup) {
case 256:
System.Diagnostics.Debug.WriteLine("down");
break;
case 257:
System.Diagnostics.Debug.WriteLine("up");
break;
case 260:
System.Diagnostics.Debug.WriteLine("sys_down");
break;
case 261:
System.Diagnostics.Debug.WriteLine("sys_up");
break;
}
return CallNextHookEx(IntPtr.Zero,code,wParam,lParam);
}
从lParam中读取键的虚拟码(lParam其实是指向类似前文提到的KEYBDINPUT结构的指针),wParam表示击键事件的类型。CallNextHookEx将消息传递给下一个消息处理节点。
⩥使用前文提到的SendInput方法模拟键盘输入也能被钩子监听到。
⩥应避免在消息处理过程中进行耗时操作。
卸载钩子需要使用UnhookWindowsHookEx
UnhookWindowsHookEx的定义
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr pHookHandle);
传入SetWindowsHookEx的返回值即可,返回true则卸载成功。
C#模拟键盘输入、键状态和监听键盘消息的更多相关文章
- SendInput模拟键盘输入的问题
SendInput模拟键盘输入的问题 http://www.cnblogs.com/yedaoq/archive/2010/12/30/1922305.html 最近接触到这个函数,因此了解了一下, ...
- 使用C#模拟键盘输入、鼠标移动和点击、设置光标位置及控制应用程序的显示
1.模拟键盘输入(SendKeys) 功能:将一个或多个按键消息发送到活动窗口,就如同在键盘上进行输入一样. 语法:SendKeys.Send(string keys);SendKeys.SendWa ...
- VB模拟键盘输入的N种方法
VB模拟键盘输入的N种方法http://bbs.csdn.net/topics/90509805hd378发表于: 2006-12-24 14:35:39用VB模拟键盘事件的N种方法 键盘是我们使用计 ...
- Python模拟键盘输入和鼠标操作
Python模拟键盘输入和鼠标操作 一.Python键盘输入模拟: import win32api import win32con win32api.keybd_event(17,0,0,0) #c ...
- 模拟键盘输入首先要用到一个API函数:keybd_event
转自:http://www.cnblogs.com/cpcpc/archive/2011/02/22/2123055.html 模拟键盘输入首先要用到一个API函数:keybd_event. 模拟按键 ...
- 微信小程序车牌号码模拟键盘输入
微信小程序车牌号码模拟键盘输入练习, 未经允许,禁止转载,抄袭,如需借鉴参考等,请附上该文章连接. 相关资料参考:https://blog.csdn.net/littlerboss/article/d ...
- 用Delphi模拟键盘输入
在Windows大行其道的今天,windows界面程序受到广大用户的欢迎.对这些程序的操作不外乎两种,键盘输入控制和鼠标输入控制.有时,对于繁杂的,或重复性的操作,我们能否通过编制程序来代替手工输入, ...
- C# keybd_event用法 模拟键盘输入
最近有业务需求,需要模拟键盘输入,所以了解了一下C#中keybd_event函数的用法.该函数能够产生WM_KEYUP或WM_KEYDOWN消息,即可以触发键盘事件. 函数引用如下: [DllImpo ...
- VC 模拟键盘输入
转载请注明来源:https://www.cnblogs.com/hookjc/ vc模拟键盘输入keybd_event(VK_LWIN, 0, 0 ,0);keybd_event('M', 0, 0 ...
- 模拟键盘输入 : SendMessage, keybd_event, PostKeybdMessage
转自模拟键盘输入 : SendMessage, keybd_event, PostKeybdMessage 目的 最近项目要求在Windows CE下模拟键盘输入,上网搜索了一下,发现有3个API可以 ...
随机推荐
- 06-Python类与对象
什么是类 百度百科: 类是对象的抽象,对象是对客观事物的抽象. 用通俗的话来说: 类是类别的意思,是数据类型. 对象是类别下的具体事物. 也就是说: 类是数据类型,对象是变量. 比如: 自定义一种数据 ...
- mysql多表删除指定记录
在Mysql4.0之后,mysql开始支持跨表delete. Mysql可以在一个sql语句中同时删除多表记录,也可以根据多个表之间的关系来删除某一个表中的记录. 假定我们有两张表:Product表和 ...
- .NET Core MVC基础之页面传值方式📃
.NET Core MVC基础之页面传值方式 前言 最近工作太忙了,浅浅更新一下.NET基础知识.大部分面试官都会问.NET页面传值的几种方式,那么接下来就来细讲与实现一下吧! 页面传值分成两类 第一 ...
- Vue2 整理(一):基础篇
前言 首先说明:要直接上手简单得很,看官网熟悉大概有哪些东西.怎么用的,然后简单练一下就可以做出程序来了,最多两天,无论Vue2还是Vue3,就都完全可以了,Vue3就是比Vue2多了一些东西而已,所 ...
- 一个用来画拉氏图的简单Python脚本
技术背景 关于拉氏图的更多介绍,可以参考下这篇博客,这里简单引述一部分内容: Ramachandran plot(拉氏图)是由G. N. Ramachandran等人于1963年开发的,用来描述蛋白质 ...
- java后端解决请求跨域
跨域 跨域:指的是浏览器不能执行其他网站的脚本.它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制. 例如:a页面想获取b页面资源,如果a.b页面的协议.域名.端口.子域名不同 ...
- QAnything AI开源的企业级本地知识库问答解决方案,致力于支持任意格式文件或数据库的问答
QAnything AI简介 QAnything ai是一个本地知识库问答系统,旨在支持多种文件格式和数据库,允许离线安装和使用.您可以简单地删除任何格式的任何本地存储文件,并获得准确.快速和可靠的答 ...
- 解决方案 | Chrome/Edge 总是自动修改我的pdf默认打开方式
1.问题描述 最近我的pdf文件总是被chrome打开(如图1),而且点击属性,更改别的pdf阅读器也不管用(如图2),此时的chrome就像个流氓软件一样. 图1 被chrome劫持 图2 点击属性 ...
- [oeasy]python0073_进制转化_eval_evaluate_衡量_oct_octal_八进制
进制转化 回忆上次内容 上次了解的是 整型数字类变量 integer 前缀为i 添加图片注释,不超过 140 字(可选) 整型变量 和 字符串变量 不同 整型变量 是 直接存储二进制形 ...
- Node.js 处理 File
Node.js 处理 File fs 模块 常规使用 运用递归遍历目录树 创建文件和目录 读写文件 path 模块 对于 file 的理解,此处 fs 模块 Node.js 提供了处理文件系统的内置模 ...