C#虚拟键盘的实现
C#虚拟键盘的实现
前言
在工作中,使用到触摸屏外接键盘不方便的情况,需要上位机调用虚拟键盘完成输入操作,在此做记录分享。网上有很多种方法,我将再实践后陆续更新在这里。
一、通过Keybd_event实现
1.keybd_event函数 简介
微软官方文档地址
合成击键。系统可以使用这种合成的击键来生成WM_KEYUP或WM_KEYDOWN消息。键盘驱动程序的中断处理程序调用keybd_event函数。
原型如下:
void keybd_event(
[in] BYTE bVk,
[in] BYTE bScan,
[in] DWORD dwFlags,
[in] ULONG_PTR dwExtraInfo
);
参数:
bvk : 虚拟钥匙代码。代码必须是介于 1 到 254 之间的值。完整列表虚拟键代码。
bScan:定义该键的硬件扫描码;
dwFlags:控制功能操作的各个方面。此参数可以是以下一个或多个值;
| 定义 | 数值 | 意义 |
|---|---|---|
| KEYEVENTF_KEYDOWN | 0x0000 | 键被按下 |
| KEYEVENTF_EXTENDEDKEY | 0x0001 | 指示这个键是否是扩展键 |
| KEYEVENTF_KEYUP | 0x0002 | 键被释放 |
dwExtraInfo: 与击键关联的附加值。(暂时不知道用处,一般常为0)
PS:
微软键接受结构与扩展标志
硬件扫描码:当用户按下某个键时,
’ 1.键盘会检测到这个动作,并通过键盘控制器把扫描码(scan code)传送到计算机;
’ 键盘扫描码跟具体的硬件有关的,不同厂商对同一个键的扫描码有可能不同。
’ 2.计算机接收到扫描码后,将其交给键盘驱动程序;
’ 3.键盘驱动程序把这个扫描码转换为键盘虚拟码;
’ 虚拟码与具体硬件无关,不同厂商的键盘,同一个键的虚拟码总是相同的。
’ 3.然后,键盘驱动程序把该键盘操作的扫描码和虚拟码以及其它信息传递给操作系统;
’ 4.操作系统将获得的信息封装在一个键盘消息中,并把该键盘消息插入到消息列队。
’ 5.通过Windows的消息系统,该键盘消息被送到某个窗口中;
’ 6.窗口所在的应用程序接收到消息后,可以了解到有关键盘操作的信息,然后决定作出一定的响应
扩展键:扩展键标志指示击键消息是否源自增强型 101/102 键键盘上的附加键之一。 扩展键由键盘右侧的 Alt 和 Ctrl 键组成:数字键盘左侧的 INS、DEL、HOME、END、PAGE UP、PAGE DOWN 和箭头键;NUM LOCK 键;BREAK (CTRL+PAUSE) 键;PRINT SCRN 键;和数字键盘中的除号 (/) 和 ENTER 键。 右侧 SHIFT 键不被视为扩展键,而是具有单独的扫描代码。
如果指定,扫描代码由两个字节组成的序列,其中第一个字节的值为0xE0。
2.虚拟键码对照表
| 返回的常量 | Value | 说明 |
|---|---|---|
| VK_LBUTTON | 0x01 | 鼠标左键 |
| VK_RBUTTON | 0x02 | 鼠标右键 |
| VK_CANCEL | 0x03 | 控制中断处理 |
| VK_MBUTTON | 0x04 | 中间鼠标按钮 (三键鼠标) |
| VK_XBUTTON1 0x05 | X1 鼠标按钮 | |
| VK_XBUTTON2 | 0x06 | X2 鼠标按钮 |
| - | 0x07 | Undefined |
| VK_BACK | 0x08 | BACKSPACE 密钥 |
| VK_TAB | 0x09 | Tab 键 |
| - | 0x0A-0B | 预留 |
| VK_CLEAR | 0x0C | CLEAR 键 |
| VK_RETURN | 0x0D | Enter 键 |
| - | 0x0E-0F | Undefined |
| VK_SHIFT | 0x10 | SHIFT 键 |
| VK_CONTROL | 0x11 | Ctrl 键 |
| VK_MENU | 0x12 | Alt 键 |
| VK_PAUSE | 0x13 | PAUSE 键 |
| VK_CAPITAL | 0x14 | CAPS LOCK 键 |
| VK_KANA | 0x15 | IME Kana 模式 |
| VK_HANGUEL | 0x15 | IME 朝鲜文库埃尔模式 (保持兼容性;使用 VK_HANGUL) |
| VK_HANGUL | 0x15 | IME Hanguel 模式 |
| VK_IME_ON | 0x16 | IME On |
| VK_JUNJA | 0x17 | IME Junja 模式 |
| VK_FINAL | 0x18 | IME 最终模式 |
| VK_HANJA | 0x19 | IME Hanja 模式 |
| VK_KANJI | 0x19 | IME Kanji 模式 |
| VK_IME_OFF | 0x1A | IME 关闭 |
| VK_ESCAPE | 0x1B | ESC 键 |
| VK_CONVERT | 0x1C | IME 转换 |
| VK_NONCONVERT | 0x1D | IME 不转换 |
| VK_ACCEPT | 0x1E | IME 接受 |
| VK_MODECHANGE | 0x1F | IME 模式更改请求 |
| VK_SPACE | 0x20 | 空格键 |
| VK_PRIOR | 0x21 | PAGE UP 键 |
| VK_NEXT | 0x22 | PAGE DOWN 键 |
| VK_END | 0x23 | END 键 |
| VK_HOME | 0x24 | HOME 键 |
| VK_LEFT | 0x25 | 向左键 |
| VK_UP | 0x26 | 向上键 |
| VK_RIGHT | 0x27 | 向右键 |
| VK_DOWN | 0x28 | 向下键 |
| VK_SELECT | 0x29 | SELECT 键 |
| VK_PRINT | 0x2A | PRINT 键 |
| VK_EXECUTE | 0x2B | EXECUTE 键 |
| VK_SNAPSHOT | 0x2C | 打印屏幕键 |
| VK_INSERT | 0x2D | INS 密钥 |
| VK_DELETE | 0x2E | DEL 键 |
| VK_HELP | 0x2F | 帮助密钥 |
| 0x30 | 0 键 | |
| 0x31 | 1 个键 | |
| 0x32 | 2 键 | |
| 0x33 | 3 键 | |
| 0x34 | 4 键 | |
| 0x35 | 5 键 | |
| 0x36 | 6 个键 | |
| 0x37 | 7 键 | |
| 0x38 | 8 键 | |
| 0x39 | 9 键 | |
| - | 0x3A-40 | Undefined |
| 0x41 | A键 | |
| 0x42 | B 键 | |
| 0x43 | C 键 | |
| 0x44 | D 键 | |
| 0x45 | E 键 | |
| 0x46 | F 键 | |
| 0x47 | G 键 | |
| 0x48 | H 键 | |
| 0x49 | I 键 | |
| 0x4A | J 键 | |
| 0x4B | K 键 | |
| 0x4C | L 键 | |
| 0x4D | M 键 | |
| 0x4E | N 键 | |
| 0x4F | O 键 | |
| 0x50 | P 键 | |
| 0x51 | Q 键 | |
| 0x52 | R 键 | |
| 0x53 | S 键 | |
| 0x54 | T 键 | |
| 0x55 | U 键 | |
| 0x56 | V 键 | |
| 0x57 | W 键 | |
| 0x58 | X 键 | |
| 0x59 | Y 键 | |
| 0x5A | Z 键 | |
| VK_LWIN | 0x5B | 左Windows键 (自然键盘) |
| VK_RWIN | 0x5C | 右Windows键 (自然键盘) |
| VK_APPS | 0x5D | 应用程序键 (自然键盘) |
| - | 0x5E | 保留 |
| VK_SLEEP | 0x5F | 计算机休眠键 |
| VK_NUMPAD0 | 0x60 | 数字键盘 0 键 |
| VK_NUMPAD1 | 0x61 | 数字键盘 1 键 |
| VK_NUMPAD2 | 0x62 | 数字键盘 2 键 |
| VK_NUMPAD3 | 0x63 | 数字键盘 3 键 |
| VK_NUMPAD4 | 0x64 | 数字键盘 4 键 |
| VK_NUMPAD5 | 0x65 | 数字键盘 5 键 |
| VK_NUMPAD6 | 0x66 | 数字键盘 6 键 |
| VK_NUMPAD7 | 0x67 | 数字键盘 7 键 |
| VK_NUMPAD8 | 0x68 | 数字键盘 8 键 |
| VK_NUMPAD9 | 0x69 | 数字键盘 9 键 |
| VK_MULTIPLY | 0x6A | 乘键 |
| VK_ADD | 0x6B | 添加密钥 |
| VK_SEPARATOR | 0x6C | 分隔符键 |
| VK_SUBTRACT | 0x6D | 减去键 |
| VK_DECIMAL | 0x6E | 十进制键 |
| VK_DIVIDE | 0x6F | 除键 |
| VK_F1 | 0x70 | F1 键 |
| VK_F2 | 0x71 | F2 键 |
| VK_F3 | 0x72 | F3 键 |
| VK_F4 | 0x73 | F4 键 |
| VK_F5 | 0x74 | F5 键 |
| VK_F6 | 0x75 | F6 键 |
| VK_F7 | 0x76 | F7 键 |
| VK_F8 | 0x77 | F8 键 |
| VK_F9 | 0x78 | F9 键 |
| VK_F10 | 0x79 | F10 键 |
| VK_F11 | 0x7A | F11 键 |
| VK_F12 | 0x7B | F12 键 |
| VK_F13 | 0x7C | F13 键 |
| VK_F14 | 0x7D | F14 键 |
| VK_F15 | 0x7E | F15 键 |
| VK_F16 | 0x7F | F16 键 |
| VK_F17 | 0x80 | F17 键 |
| VK_F18 | 0x81 | F18 键 |
| VK_F19 | 0x82 | F19 键 |
| VK_F20 | 0x83 | F20 键 |
| VK_F21 | 0x84 | F21 键 |
| VK_F22 | 0x85 | F22 键 |
| VK_F23 | 0x86 | F23 键 |
| VK_F24 | 0x87 | F24 键 |
| - | 0x88-8F | 未分配 |
| VK_NUMLOCK | 0x90 | NUM LOCK 密钥 |
| VK_SCROLL | 0x91 | SCROLL LOCK 键 |
| 0x92-96 | OEM 特定 | |
| - | 0x97-9F | 未分配 |
| VK_LSHIFT | 0xA0 | 左 SHIFT 键 |
| VK_RSHIFT | 0xA1 | 右 SHIFT 键 |
| VK_LCONTROL | 0xA2 | 左 Ctrl 键 |
| VK_RCONTROL | 0xA3 | 右 Ctrl 键 |
| VK_LMENU | 0xA4 | 左 Alt 键 |
| VK_RMENU | 0xA5 | 右 ALT 键 |
| VK_BROWSER_BACK | 0xA6 | 浏览器后退键 |
| VK_BROWSER_FORWARD | 0xA7 | 浏览器前进键 |
| VK_BROWSER_REFRESH | 0xA8 | 浏览器刷新键 |
| VK_BROWSER_STOP | 0xA9 | 浏览器停止键 |
| VK_BROWSER_SEARCH | 0xAA | 浏览器搜索键 |
| VK_BROWSER_FAVORITES | 0xAB | 浏览器收藏键 |
| VK_BROWSER_HOME | 0xAC | 浏览器“开始”和“主页”键 |
| VK_VOLUME_MUTE | 0xAD | 静音键 |
| VK_VOLUME_DOWN | 0xAE | 音量减小键 |
| VK_VOLUME_UP | 0xAF | 音量增加键 |
| VK_MEDIA_NEXT_TRACK | 0xB0 | 下一曲目键 |
| VK_MEDIA_PREV_TRACK | 0xB1 | 上一曲目键 |
| VK_MEDIA_STOP | 0xB2 | 停止媒体键 |
| VK_MEDIA_PLAY_PAUSE | 0xB3 | 播放/暂停媒体键 |
| VK_LAUNCH_MAIL | 0xB4 | 启动邮件键 |
| VK_LAUNCH_MEDIA_SELECT | 0xB5 | 选择媒体键 |
| VK_LAUNCH_APP1 | 0xB6 | 启动应用程序 1 键 |
| VK_LAUNCH_APP2 | 0xB7 | 启动应用程序 2 键 |
| - | 0xB8-B9 | 预留 |
| VK_OEM_1 | 0xBA | 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“;:”键 |
| VK_OEM_PLUS | 0xBB | 对于任何国家/地区,“+”键 |
| VK_OEM_COMMA | 0xBC | 对于任何国家/地区,“,键 |
| VK_OEM_MINUS | 0xBD | 对于任何国家/地区,“-”键 |
| VK_OEM_PERIOD | 0xBE | 对于任何国家/地区,“.”键 |
| VK_OEM_2 | 0xBF | 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“/?” 键 |
| VK_OEM_3 | 0xC0 | 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“~”键 |
| - | 0xC1-D7 | 预留 |
| - | 0xD8-DA | 未分配 |
| VK_OEM_4 | 0xDB | 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“[{”键 |
| VK_OEM_5 | 0xDC | 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“|”键 |
| VK_OEM_6 | 0xDD | 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“]}”键 |
| VK_OEM_7 | 0xDE | 用于其他字符;它可能因键盘而异。 对于美国标准键盘,“单引号/双引号”键 |
| VK_OEM_8 | 0xDF | 用于其他字符;它可能因键盘而异。 |
| - | 0xE0 | 保留 |
| 0xE1 | OEM 特定 | |
| VK_OEM_102 | 0xE2 | <>美国标准键盘上的键,或|非美国 102 键键盘上的键 |
| 0xE3-E4 | OEM | |
| VK_PROCESSKEY | 0xE5 | IME PROCESS 密钥 |
| 0xE6 | OEM | |
| VK_PACKET | 0xE7 | 用于将 Unicode 字符当作键击传递。 该 VK_PACKET 键是用于非键盘输入方法的 32 位虚拟键值的低字。 有关详细信息,请参阅“备注”,以及KEYBDINPUTSendInputWM_KEYDOWNWM_KEYUP |
| - | 0xE8 | 未分配 |
| 0xE9-F5 | OEM 特定 | |
| VK_ATTN | 0xF6 | Attn 键 |
| VK_CRSEL | 0xF7 | CrSel 键 |
| VK_EXSEL | 0xF8 | ExSel 密钥 |
| VK_EREOF | 0xF9 | 擦除 EOF 密钥 |
| VK_PLAY | 0xFA | 播放键 |
| VK_ZOOM | 0xFB | 缩放键 |
| VK_NONAME | 0xFC | 预留 |
| VK_PA1 | 0xFD | PA1 键 |
| VK_OEM_CLEAR | 0xFE | 清除键 |
3.函数使用
引入库函数:
//模拟键盘API 键值用byte最准确
[DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
1).单键的输入
在按钮单击事件中写入
keybd_event(VK_Code, 0, 0, 0); \\键按下 KEYEVENTF_KEYDOWN = 0
keybd_event(VK_Code, 0, KEYEVENTF_KEYUP, 0); \\键弹起 KEYEVENTF_KEYUP = 2
举例如下:

此处我代码中打印的输出为:
keybd_event(69, 0, 0, 0);
keybd_event(69, 0, 2, 0);
D(69) = H(45) ——> 0X45指示E键
PS:尽量不要分开写入按钮的鼠标按下和抬起事件中,我尝试了会出现奇奇怪怪的问题,具体原因我也没有搞清楚。
2).组合键的输入
在使用Shift,Alt等这些常用组合特殊键时:
例如:Shift + A键 = 大写A
keybd_event(16, 0, 0, 0); \\键按下 KEYEVENTF_KEYDOWN = 0
keybd_event(65, 0, 0, 0); \\键按下 KEYEVENTF_KEYDOWN = 0
keybd_event(65, 0, KEYEVENTF_KEYUP, 0); \\键弹起 KEYEVENTF_KEYUP = 2
keybd_event(16, 0, KEYEVENTF_KEYUP, 0); \\键弹起 KEYEVENTF_KEYUP = 2
如图例:

当我键入Shift却一直不发弹起信号(无 keybd_event(16, 0, KEYEVENTF_KEYUP, 0))时后面所有按下弹起的按键都将被组合(Shift + xx)
3).中文的输入
shift(按下后弹起)可以切换输入法至中文,正常打字就行

4.代码实例
下载地址。
二、使用winio模拟键盘硬件扫描码(暂未复现)
winio原理
C#操作可看文章
驱动级模拟驱动级模拟:直接读写键盘的硬件端口!