SendInput模拟键盘输入的问题

 http://www.cnblogs.com/yedaoq/archive/2010/12/30/1922305.html

最近接触到这个函数,因此了解了一下,总结一下列在这。

我了解它的出发点是如何通过它向活动窗口输入字符,这是很多程序都有的功能(我猜Visual Assist X就用了这个功能)。

根据MSDN,此函数模拟按键操作,将一些消息插入键盘或鼠标的输入流中,Windows对它进行处理,生成相应的WM_KEYDOWN或WM_KEYUP事件,这些事件与普通键盘输入一起进入应用程序的消息循环,它们不仅可以转换为WM_CHAR消息,还可以转换为其它(诸如加速键)等消息。

使用它来发送字符消息,并没有看起来那么简单。这有两个需要考虑的问题:

1. 输入法的转换。例如需要向活动窗口发送一些英文字符,我们可能想象这样来实现:获取对应键盘字符的虚拟键码,发送一个SendInput。但是如果活动窗口正在使用一个输入法,那么我们发送出去的消息,会进入输入法的Composition窗口,最终被转换为象形文字或被丢弃。只有当输入法关闭时,程序运行的效果才会像我们期望的那样,在活动窗口中显示出英文字符。

2. 对于中文字符,应该怎么发送给活动窗口?由于SendInput模拟的是WM_KEYDOWN和WM_KEYUP事件,按照一般的思路,我们是否应该获取中文字符的输入法编码(拼音或五笔码),然后向活动窗口发送编码相关的SendInput?那这不仅要求活动窗口开启输入法,甚至还要获知它的编码方式。

如上所述,若直接如想象中那样使用SendInput来输入字符,则必须分析活动窗口的输入法状态。而且输入英文时,要求关闭输入法,输入中文时,又要求打开输入法。若真要以这样的思路来实现,则必定是难以成功的。

那么,有没有不依赖活动窗口输入法状态的方式呢?

其实是有的,使用SendInput模拟键盘输入时,其参数是KEYBDINPUT结构,通过将其dwFlags成员设置KEYEVENTF_UNICODE就可以了。使用此方式,只需将KEYBDINPUT.wScan设置为字符的Unicode编码即可。对于英文字符,不需要关闭活动窗口的输入法;对于中文字符,也不要求活动窗口打开输入法和将字符转换为输入法编码。

MSDN对此方式的说明为:INPUT_KEYBOARD支持非键盘的输入方式,例如手写识别或语音识别,通过KEYEVENTF_UNICODE标识,这些方式与键盘(文本)输入别无二致。如果指定了KEYEVENTF_UNICODE,SendInput发送一个WM_KEYDOWN或WM_KEYUP消息给活动窗口的线程消息队列,消息的wParam参数为VK_PACKET。GetMessage或PeedMessage一旦获得此消息,就把它传递给TranslateMessage,TranslateMessage根据wScan中指定的Unicode字符产生一个WM_CHAR消息。若窗口是ANSI窗口,则Unicode字符会自动转换为相应的ANSI字符。

任何需要向活动窗口输入字符(包括英文)的功能均应使用这种方式来实现。事实上,键盘消息转换为字符消息的过程是很复杂的,这可能与键盘布局、区域、换档状态等诸多因素有关,这也是Windows要使用TranslateMessage来转换消息的原因。因此,不应该试图通过击键事件来意图向活动窗口输入特定的字符。

经测试,SendInput还有两个值得注意的地方:

1. 没有为KEYBDINPUT.dwFlags指定KEYEVENTF_KEYUP标识时,SendInput将生成WM_KEYDOWN消息,否则生成WM_KEYUP消息,由于只有WM_KEYDOWN会转换为字符消息,因此,若以输入字符为目标,则不应指定KEYEVENTF_KEYUP标识。

2. 如果我们想达到实际做一次击键所产生的效果:顺序产生一个WM_KEYDOWN和一个WM_KEYUP事件。则必须分别以不指定KEYEVENTF_KEYUP和指定KEYEVENTF_KEYUP的方式执行一次SendInput操作。SendInput允许在一次调用中发送多个模拟消息:

INPUT input[2]; 
  memset(input, 0, 2 * sizeof(INPUT));

input[0].type = INPUT_KEYBOARD; 
  input[0].ki.wVk = data;

input[1].type = INPUT_KEYBOARD; 
  input[1].ki.wVk = data; 
  input[1].ki.dwFlags = KEYEVENTF_KEYUP;

SendInput(2, input, sizeof(INPUT));

但实际上,这将导致不产生任何消息。这两个消息必须分开发送,如下所示:

INPUT input[2]; 
  memset(input, 0, 2 * sizeof(INPUT));

input[0].type = INPUT_KEYBOARD; 
  input[0].ki.wVk = data; 
  SendInput(1, input, sizeof(INPUT));

input[1].type = INPUT_KEYBOARD; 
  input[1].ki.wVk = data; 
  input[1].ki.dwFlags = KEYEVENTF_KEYUP;

SendInput(1, input + 1, sizeof(INPUT));

关于第二点内容,我很有疑问。因为之前有人在网上帖的代码是合并发送的,想必有人这么做过并且成功了。我不清楚是否与系统或其它因素有关。我也曾试图尝试解决此问题,但没有成功:

1. 根据MSDN,KEYBDINPUT.time是一个时间戳,如果为零,系统将使用它自己的时间戳。因此我怀疑两个一起发送的事件,是不是因为其时间戳相同,而被忽略掉了。于是我在上述代码中显式设置了该属性,再合并发送,结果依然是没有产生任何消息。

2. 我分别尝试了两种情况:合并发送的两条消息都没有指定KEYEVENTF_KEYUP(期望得到两个相同的字符输入);合并发送的两条消息具有不同的虚拟键码且都不指定KEYEVENTF_KEYUP(期望获得两个不同的字符输入)。结果依然失败,没有产生任何消息。

我不清楚这是否意味着:对于键盘输入,不允许将消息合并发送。

若您了解其中缘由或测试到与我不同的现象,请与我联系:yedaoq@126.com

相关知识:

1. 输入法也可以处理SendInput发送的Unicode消息,具体方式不详。见MSDN中ImmGetProperty方法的参考:当dwIndex参数为IGP_PROPERTY时,IME_PROP_ACCEPT_WIDE_VKEY是一个可能的返回值,它表示IME会处理SendInput函数以VK_PACKET注入的Unicode字符,若返回值无该标识,则Unicode字符会直接发送给应用程序。

SendInput模拟键盘输入的问题的更多相关文章

  1. VB模拟键盘输入的N种方法

    VB模拟键盘输入的N种方法http://bbs.csdn.net/topics/90509805hd378发表于: 2006-12-24 14:35:39用VB模拟键盘事件的N种方法 键盘是我们使用计 ...

  2. 使用C#模拟键盘输入、鼠标移动和点击、设置光标位置及控制应用程序的显示

    1.模拟键盘输入(SendKeys) 功能:将一个或多个按键消息发送到活动窗口,就如同在键盘上进行输入一样. 语法:SendKeys.Send(string keys);SendKeys.SendWa ...

  3. Python模拟键盘输入和鼠标操作

    Python模拟键盘输入和鼠标操作 一.Python键盘输入模拟: import win32api import win32con win32api.keybd_event(17,0,0,0)  #c ...

  4. 模拟键盘输入 : SendMessage, keybd_event, PostKeybdMessage

    转自模拟键盘输入 : SendMessage, keybd_event, PostKeybdMessage 目的 最近项目要求在Windows CE下模拟键盘输入,上网搜索了一下,发现有3个API可以 ...

  5. 模拟键盘输入首先要用到一个API函数:keybd_event

    转自:http://www.cnblogs.com/cpcpc/archive/2011/02/22/2123055.html 模拟键盘输入首先要用到一个API函数:keybd_event. 模拟按键 ...

  6. 微信小程序车牌号码模拟键盘输入

    微信小程序车牌号码模拟键盘输入练习, 未经允许,禁止转载,抄袭,如需借鉴参考等,请附上该文章连接. 相关资料参考:https://blog.csdn.net/littlerboss/article/d ...

  7. 用Delphi模拟键盘输入

    在Windows大行其道的今天,windows界面程序受到广大用户的欢迎.对这些程序的操作不外乎两种,键盘输入控制和鼠标输入控制.有时,对于繁杂的,或重复性的操作,我们能否通过编制程序来代替手工输入, ...

  8. 【转】C# winform 加载网页 模拟键盘输入自动接入访问网络

    [转]C# winform 加载网页 模拟键盘输入自动接入访问网络 声明: 本文原创,首发于博客园 http://www.cnblogs.com/EasyInvoice/p/6070563.html  ...

  9. C# keybd_event用法 模拟键盘输入

    最近有业务需求,需要模拟键盘输入,所以了解了一下C#中keybd_event函数的用法.该函数能够产生WM_KEYUP或WM_KEYDOWN消息,即可以触发键盘事件. 函数引用如下: [DllImpo ...

随机推荐

  1. SVD java 算法实现

    https://github.com/Richard-Cao/MatrixSvdDemo/blob/master/app/src/main/java/me/ele/caolicheng/matrixs ...

  2. CentOS6配置国内yum源

    在安装完CentOS后为了加快安装.更新rpm包的速度.需要将yum源改为国内源,国内比较快的源有中科大.163.sohu源.下面修改为163源为例子: 首先进入源的配置目录:执行 cd /etc/y ...

  3. Android USB Host与HID通讯

    前端时间捣鼓一个HID的硬件, 需要和android通信, 网上搜索了一圈,收获不小. 比较好的文章是:      Android USB Host与HID通讯 Android Service创建US ...

  4. 7.Git的版本退回

    现在,你已经学会了修改文件,然后把修改提交到Git版本库,现在,再练习一次,修改readme.txt文件如下: Git is a distributed version control system. ...

  5. YbSoftwareFactory 代码生成插件【二十五】:Razor视图中以全局方式调用后台方法输出页面代码的三种方法

    上一篇介绍了 MVC中实现动态自定义路由 的实现,本篇将介绍Razor视图中以全局方式调用后台方法输出页面代码的三种方法. 框架最新的升级实现了一个页面部件功能,其实就是通过后台方法查询数据库内容,把 ...

  6. 写Python脚本语言时,“SyntaxError: Non-ASCII character '\xe6' in file”错误的处理

    如图是一段简单的文件操作代码,以为打开文件"test1221_1.py",若不存在此文件,则创建此文件.接下来并输出相关信息. 当少写了黄色方框圈起来的一行看似不经意的带有编码格式 ...

  7. Unity中各类物理投射性能横向比较

    最近在优化摄像机部分代码,抽了个时间对物理投射这块进行了系统性的测试,发现了不少东西 测试工程下载地址: http://files.cnblogs.com/files/hont/RaycastTest ...

  8. MySQL为什么需要一个主键

    主键 表中每一行都应该有可以唯一标识自己的一列(或一组列). 一个顾客可以使用顾客编号列,而订单可以使用订单ID,雇员可以使用雇员ID 或 雇员社会保险号. 主键(primary key) 一列(或一 ...

  9. PetGenie

    大概六.七年前当我还在学 Asphyre 的时候,有看过一个以之编写的类似对对碰的“宠物对对碰”小游戏,虽然很简单,但我当时还是小小的沉溺过数个小时.而不久前,在闲逛论坛时无意看到了个以 FireMo ...

  10. Dagger2 使用初步

    Dagger2 是一个Android依赖注入框架,由谷歌开发,最早的版本Dagger1 由Square公司开发.依赖注入框架主要用于模块间解耦,提高代码的健壮性和可维护性.Dagger 这个库的取名不 ...