SendInput模拟键盘输入的问题 <转>

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

我了解它的出发点是如何通过它向活动窗口输入字符,这是很多程序都有的功能(我猜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字符会直接发送给应用程序。

时间: 2024-09-18 14:36:44

SendInput模拟键盘输入的问题 <转>的相关文章

在街机模拟器上,无法模拟键盘输入

问题描述 我写了一个程序,按下按钮后,会激活前面指定的窗体(比如说记事本),同时模拟键盘输入一个字母,记事本中就出现那个字符了.可是用在模拟器(PC玩街机的那种,软件名是KAWAKS1.6)上面就是不行,比如说KAWAKS上输入5是投币动作,可是我程序模拟发送一个5过去,他就是没反应,我用的是SENDKEYS的SEND方法,请高手求解,急等中,谢谢啦! 解决方案 解决方案二:不会帮顶解决方案三:帮顶,等待高手!解决方案四:sendinput解决方案五:SENDINPUT无效,好像模拟器是直接截取

C#控制台如何模拟键盘输入,以使之前的ReadLine无效?

问题描述 如题.线程A用ReadLine检测键盘输入.中止线程A,开启线程B,继续用ReadLine检测键盘输入.由于线程A的ReadLine,需要一次额外的输入,才能开始线程B的ReadLine.如何取消前一个ReadLine的阻塞? 解决方案 解决方案二:既然说"用ReadLine检测键盘输入",然后又说"一次额外的输入",那么你到底要不要判断回车键啊?解决方案三:该回复于2015-05-31 23:30:49被版主删除解决方案四:引用1楼sp1234的回复:

webbrowser模拟键盘输入出现的问题

问题描述 HtmlElementhel=webBrowser1.Document.GetElementById("XXX");hel.Focus();设置焦点的时候,出现了一个问题,如果当前窗口不在激活状态,比如,最小化了或者在看其他窗口,就不能模拟输入数值.有什么办法吗,大神们 解决方案

利用SendInput模拟鼠标键盘 &lt;转&gt;

SendInputSendInput可以将指定的鼠标键盘消息插入到系统消息队列,从而实现对鼠标键盘的模拟.有很多程序对SendInput进行了屏蔽,但不是所有的.所以这里介绍一下SendInput的使用.我已经将主要的模拟功能写在了一个单元文件中:SIMouseKeyboard.pas,调用该单元文件中的相关函数就可以实现鼠标键盘的模拟.该单元文件的下载见本楼末尾.SendInput的参数其实很简单,在Windows.pas就有函数的声明如下:function SendInput(cInputs

模拟键盘硬件输入汉字的问题

问题描述 模拟键盘硬件输入汉字,假如当前中文输入法是搜狗拼音输入法,我想模拟键盘输入"练习"两个汉字,于是模拟键盘输入了字母"lianxi",这时在输入法的汉字选择窗口中出现了"1联系2练习3怜惜4联席5连"等,那我怎么用程序判断"练习"应该按哪个数字键呢?没分了,谢谢大家! 解决方案 解决方案二:难道周六大家都在睡懒觉吗?

python基于windows平台锁定键盘输入的方法_python

本文实例讲述了python基于windows平台锁定键盘输入的方法.分享给大家供大家参考.具体分析如下: pywin32中没有BlockInput这个函数.VC++中有,发现这个方法就可以了. 该代码可阻断windows平台下的鼠标键盘输入,如下所示: # coding: UTF-8 import time from ctypes import * user32 = windll.LoadLibrary('user32.dll') user32.BlockInput(True); time.sl

java-模拟键盘输入向其他应用程序编辑框输入字符串

问题描述 模拟键盘输入向其他应用程序编辑框输入字符串 JAVA 程序中模拟键盘输入向其他应用程序(SAP)编辑框输入字符 要有源码 解决方案 不知道你具体要做什么,但是JAVA 程序中模拟键盘输入的例子代码你可以看看这文章http://blog.csdn.net/yu555666/article/details/1634564 解决方案二: 参考com.jniwrapper.win32.FunctionName 你的需求应该是调用WinApi函数,比如GetWindow,FindWindow,S

模拟键盘-怎么在webbrowser中在鼠标点击的位置处得到焦点

问题描述 怎么在webbrowser中在鼠标点击的位置处得到焦点 我在webbrowser中载入淘宝界面,在搜索框处进行了模拟鼠标点击,我想在搜索框处获取焦点,然后模拟键盘输入文字.关键是焦点怎么获得.它不是控件,不能用this.控件.focus. 解决方案 我没试过,你看下面可以吗? 我的思路: 给文本框加载一个KeyPress事件,(例:enter事件) private void enter(object sender, KeyPressEventArgs e) { if (e.KeyCha

用应用程序模拟键盘和鼠标按键

本文配套源码 在Windows大行其道的今天,windows界面程序受到广大用户的欢迎.对这些程序的操作不外乎两种,键盘输入控制和鼠标输入控制.有时,对于繁杂的,或重复性的输入操作,我们能否通过编制程序来代替手工输入,而用程序来模拟键盘及鼠标的输入呢?答案是肯定的.这里主要是通过两个Windows API函数来实现的.下面以VC++为例来介绍一下如何实现这两个功能. 模拟键盘我们用Keybd_event这个api函数,模拟鼠标按键用mouse_event函数.在VC里调用api函数是 既简单又方