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_KEYDOWN0x0000键被按下
KEYEVENTF_EXTENDEDKEY0x0001指示这个键是否是扩展键
KEYEVENTF_KEYUP0x0002键被释放

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_LBUTTON0x01鼠标左键
VK_RBUTTON0x02鼠标右键
VK_CANCEL0x03控制中断处理
VK_MBUTTON0x04中间鼠标按钮 (三键鼠标)
VK_XBUTTON1 0x05X1 鼠标按钮
VK_XBUTTON20x06X2 鼠标按钮
-0x07Undefined
VK_BACK0x08BACKSPACE 密钥
VK_TAB0x09Tab 键
-0x0A-0B预留
VK_CLEAR0x0CCLEAR 键
VK_RETURN0x0DEnter 键
-0x0E-0FUndefined
VK_SHIFT0x10SHIFT 键
VK_CONTROL0x11Ctrl 键
VK_MENU0x12Alt 键
VK_PAUSE0x13PAUSE 键
VK_CAPITAL0x14CAPS LOCK 键
VK_KANA0x15IME Kana 模式
VK_HANGUEL0x15IME 朝鲜文库埃尔模式 (保持兼容性;使用 VK_HANGUL)
VK_HANGUL0x15IME Hanguel 模式
VK_IME_ON0x16IME On
VK_JUNJA0x17IME Junja 模式
VK_FINAL0x18IME 最终模式
VK_HANJA0x19IME Hanja 模式
VK_KANJI0x19IME Kanji 模式
VK_IME_OFF0x1AIME 关闭
VK_ESCAPE0x1BESC 键
VK_CONVERT0x1CIME 转换
VK_NONCONVERT0x1DIME 不转换
VK_ACCEPT0x1EIME 接受
VK_MODECHANGE0x1FIME 模式更改请求
VK_SPACE0x20空格键
VK_PRIOR0x21PAGE UP 键
VK_NEXT0x22PAGE DOWN 键
VK_END0x23END 键
VK_HOME0x24HOME 键
VK_LEFT0x25向左键
VK_UP0x26向上键
VK_RIGHT0x27向右键
VK_DOWN0x28向下键
VK_SELECT0x29SELECT 键
VK_PRINT0x2APRINT 键
VK_EXECUTE0x2BEXECUTE 键
VK_SNAPSHOT0x2C打印屏幕键
VK_INSERT0x2DINS 密钥
VK_DELETE0x2EDEL 键
VK_HELP0x2F帮助密钥
0x300 键
0x311 个键
0x322 键
0x333 键
0x344 键
0x355 键
0x366 个键
0x377 键
0x388 键
0x399 键
-0x3A-40Undefined
0x41A键
0x42B 键
0x43C 键
0x44D 键
0x45E 键
0x46F 键
0x47G 键
0x48H 键
0x49I 键
0x4AJ 键
0x4BK 键
0x4CL 键
0x4DM 键
0x4EN 键
0x4FO 键
0x50P 键
0x51Q 键
0x52R 键
0x53S 键
0x54T 键
0x55U 键
0x56V 键
0x57W 键
0x58X 键
0x59Y 键
0x5AZ 键
VK_LWIN0x5B左Windows键 (自然键盘)
VK_RWIN0x5C右Windows键 (自然键盘)
VK_APPS0x5D应用程序键 (自然键盘)
-0x5E保留
VK_SLEEP0x5F计算机休眠键
VK_NUMPAD00x60数字键盘 0 键
VK_NUMPAD10x61数字键盘 1 键
VK_NUMPAD20x62数字键盘 2 键
VK_NUMPAD30x63数字键盘 3 键
VK_NUMPAD40x64数字键盘 4 键
VK_NUMPAD50x65数字键盘 5 键
VK_NUMPAD60x66数字键盘 6 键
VK_NUMPAD70x67数字键盘 7 键
VK_NUMPAD80x68数字键盘 8 键
VK_NUMPAD90x69数字键盘 9 键
VK_MULTIPLY0x6A乘键
VK_ADD0x6B添加密钥
VK_SEPARATOR0x6C分隔符键
VK_SUBTRACT0x6D减去键
VK_DECIMAL0x6E十进制键
VK_DIVIDE0x6F除键
VK_F10x70F1 键
VK_F20x71F2 键
VK_F30x72F3 键
VK_F40x73F4 键
VK_F50x74F5 键
VK_F60x75F6 键
VK_F70x76F7 键
VK_F80x77F8 键
VK_F90x78F9 键
VK_F100x79F10 键
VK_F110x7AF11 键
VK_F120x7BF12 键
VK_F130x7CF13 键
VK_F140x7DF14 键
VK_F150x7EF15 键
VK_F160x7FF16 键
VK_F170x80F17 键
VK_F180x81F18 键
VK_F190x82F19 键
VK_F200x83F20 键
VK_F210x84F21 键
VK_F220x85F22 键
VK_F230x86F23 键
VK_F240x87F24 键
-0x88-8F未分配
VK_NUMLOCK0x90NUM LOCK 密钥
VK_SCROLL0x91SCROLL LOCK 键
0x92-96OEM 特定
-0x97-9F未分配
VK_LSHIFT0xA0左 SHIFT 键
VK_RSHIFT0xA1右 SHIFT 键
VK_LCONTROL0xA2左 Ctrl 键
VK_RCONTROL0xA3右 Ctrl 键
VK_LMENU0xA4左 Alt 键
VK_RMENU0xA5右 ALT 键
VK_BROWSER_BACK0xA6浏览器后退键
VK_BROWSER_FORWARD0xA7浏览器前进键
VK_BROWSER_REFRESH0xA8浏览器刷新键
VK_BROWSER_STOP0xA9浏览器停止键
VK_BROWSER_SEARCH0xAA浏览器搜索键
VK_BROWSER_FAVORITES0xAB浏览器收藏键
VK_BROWSER_HOME0xAC浏览器“开始”和“主页”键
VK_VOLUME_MUTE0xAD静音键
VK_VOLUME_DOWN0xAE音量减小键
VK_VOLUME_UP0xAF音量增加键
VK_MEDIA_NEXT_TRACK0xB0下一曲目键
VK_MEDIA_PREV_TRACK0xB1上一曲目键
VK_MEDIA_STOP0xB2停止媒体键
VK_MEDIA_PLAY_PAUSE0xB3播放/暂停媒体键
VK_LAUNCH_MAIL0xB4启动邮件键
VK_LAUNCH_MEDIA_SELECT0xB5选择媒体键
VK_LAUNCH_APP10xB6启动应用程序 1 键
VK_LAUNCH_APP20xB7启动应用程序 2 键
-0xB8-B9预留
VK_OEM_10xBA用于其他字符;它可能因键盘而异。 对于美国标准键盘,“;:”键
VK_OEM_PLUS0xBB对于任何国家/地区,“+”键
VK_OEM_COMMA0xBC对于任何国家/地区,“,键
VK_OEM_MINUS0xBD对于任何国家/地区,“-”键
VK_OEM_PERIOD0xBE对于任何国家/地区,“.”键
VK_OEM_20xBF用于其他字符;它可能因键盘而异。 对于美国标准键盘,“/?” 键
VK_OEM_30xC0用于其他字符;它可能因键盘而异。 对于美国标准键盘,“~”键
-0xC1-D7预留
-0xD8-DA未分配
VK_OEM_40xDB用于其他字符;它可能因键盘而异。 对于美国标准键盘,“[{”键
VK_OEM_50xDC用于其他字符;它可能因键盘而异。 对于美国标准键盘,“|”键
VK_OEM_60xDD用于其他字符;它可能因键盘而异。 对于美国标准键盘,“]}”键
VK_OEM_70xDE用于其他字符;它可能因键盘而异。 对于美国标准键盘,“单引号/双引号”键
VK_OEM_80xDF用于其他字符;它可能因键盘而异。
-0xE0保留
0xE1OEM 特定
VK_OEM_1020xE2<>美国标准键盘上的键,或|非美国 102 键键盘上的键
0xE3-E4OEM
VK_PROCESSKEY0xE5IME PROCESS 密钥
0xE6OEM
VK_PACKET0xE7用于将 Unicode 字符当作键击传递。 该 VK_PACKET 键是用于非键盘输入方法的 32 位虚拟键值的低字。 有关详细信息,请参阅“备注”,以及KEYBDINPUTSendInputWM_KEYDOWNWM_KEYUP
-0xE8未分配
0xE9-F5OEM 特定
VK_ATTN0xF6Attn 键
VK_CRSEL0xF7CrSel 键
VK_EXSEL0xF8ExSel 密钥
VK_EREOF0xF9擦除 EOF 密钥
VK_PLAY0xFA播放键
VK_ZOOM0xFB缩放键
VK_NONAME0xFC预留
VK_PA10xFDPA1 键
VK_OEM_CLEAR0xFE清除键

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#操作可看文章
驱动级模拟驱动级模拟:直接读写键盘的硬件端口!

三、使用钩子(Hook)(暂未复现)

C# 实现屏幕键盘 (SCREENKEYBOARD)