keybd_event

  函数功能:该函数合成一次击键事件。系统可使用这种合成的击键事件来产生WM_KEYUP或WM_KEYDOWN消息,键盘驱动程序中断处理程序调用keybd_event函数。在Windows NT中该函数己被使用SendInput来替代它。
函数原型;VOID keybd_event(BYTE bVk,BYTE bScan,DWORD dwFlags,DWORD dwExtralnfo);
  参数:
  bVk:   定义一个虚拟键码。键码值必须在1~254之间。
 bScan: 
定义该键的硬件扫描码。
 dwFlags:定义函数操作的各个方面的一个标志位集。应用程序可使用如下一些预定义常数的组合设置标志位。
  
KEYEVENTF_EXTENDEDKEY:若指定该值,则扫描码前一个值为OXEO(224)的前缀字节。
  
KEYEVENTF_KEYUP:若指定该值,该键将被释放;若未指定该值,该键将被按下。
 dwExtralnfo:定义与击键相关的附加的32位值。
 返回值:该函数无返回值。
#include
#include
void main()
{
  Sleep(3000);
 
keybd_event(16,0,0,0); //按下Shift键
  keybd_event('A',0,0,0);
//按下a键
  keybd_event('A',0,KEYEVENTF_KEYUP,0); //松开a键
 
keybd_event(16,0,KEYEVENTF_KEYUP,0); //松开Shift键
 
//构成组合键---->按下Shift的同时按下a,形成 A
}
    备注:尽管keybd_event传递一个与OEM相关的硬件扫描码给系统,但应用程序不能用此扫描码。系统在内部将扫描码转换成虚拟键码,并且在传送给应用程序前清除键码的UP/down位。应用程序可以摸拟PRINTSCREEN键的按下来获得一个屏幕快照,并把它存放到剪切板中。若要做到这一点,则要将keybd_event的bVk参数置为VK_SNAPSHOT,bScan参数置为0(用以获得全屏快照)或hScan置为1(仅获得活动窗口的快照)。Windows
CE:WindowsCE支持dwFlags参数附加的标志位。即使用KEYEVENTF_SILENT标志模拟击键,而不产生敲击的声音。 Windows
CE不支持KEYEVENTF_EXTENDEDKEY标志。

    速查:Windows NT:3.1及以上版本;Windows:95及以上版本 ;Windows
CE:1.0及以上版本;头文件:winuser.h;库文件:user32.lib。

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(期望获得两个不同的字符输入)。结果依然失败,没有产生任何消息。

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

相关知识:

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

在VC中使用SendInput函数实现中文的自动输入

首先是,头文件必须包含以下两个:
#include
#include

前者是SendInput函数要用到,后者是字符串转换的时候要用到。

void SendAscii(wchar_t data, BOOL
shift)
{
  INPUT input[2];
  memset(input, 0, 2 *
sizeof(INPUT));
 
  if (shift)
 
{
    input[0].type =
INPUT_KEYBOARD;
    input[0].ki.wVk =
VK_SHIFT;
    SendInput(1, input,
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));

if (shift)
 
{
    input[0].type =
INPUT_KEYBOARD;
    input[0].ki.wVk =
VK_SHIFT;
    input[0].ki.dwFlags =
KEYEVENTF_KEYUP;
    SendInput(1, input,
sizeof(INPUT));  
  }
}

void SendUnicode(wchar_t
data)
{
  INPUT input[2];
  memset(input, 0, 2 *
sizeof(INPUT));
 
  input[0].type =
INPUT_KEYBOARD;
  input[0].ki.wVk = 0;
 
input[0].ki.wScan = data;
  input[0].ki.dwFlags =
0x4;//KEYEVENTF_UNICODE;
 
  input[1].type =
INPUT_KEYBOARD;
  input[1].ki.wVk = 0;
 
input[1].ki.wScan = data;
  input[1].ki.dwFlags = KEYEVENTF_KEYUP |
0x4;//KEYEVENTF_UNICODE;
 
  SendInput(2, input,
sizeof(INPUT));
}

//为方便使用,下面这个函数包装了前两个函数。
void SendKeys(CString
msg)
{
  short vk;
  BOOL shift;

USES_CONVERSION;
  wchar_t*
data = T2W(msg.GetBuffer(0));
  int len = wcslen(data);

for(int i=0;i
 
{
    if (data[i]>=0 &&
data[i]<256) //ascii字符
   
{
      vk =
VkKeyScanW(data[i]);

if (vk == -1)
     
{
       
SendUnicode(data[i]);
     
}
     
else
     
{
       
if (vk <
0)
       
{
         
vk = ~vk +
0x1;
       
}
       
       
shift = vk >> 8 &
0x1;
       
       
if (GetKeyState(VK_CAPITAL) &
0x1)
       
{
         
if (data[i]>='a' && data[i]<='z' || data[i]>='A' &&
data[i]<='Z')
         
{
           
shift =
!shift;
         
}
       
}

SendAscii(vk & 0xFF,
shift);
     
}
    }
   
else //unicode字符
   
{
     
SendUnicode(data[i]);
    }
 
}
}

直接调用SendKeys函数就可以在当前光标的位置自动输入指定的字符串,下面的例子演示了如何自动打开记事本程序并输入一段话:
void
CSendInputDlg::OnTest()
{
  ShellExecute(NULL, NULL,
"notepad.exe", NULL, NULL, SW_SHOWNORMAL);
 
 
Sleep(500); //为了确保记事本程序打开完毕,稍等片刻

CWnd *pWnd = FindWindow(NULL, "无标题 -
记事本");
  if (pWnd)
 
{
   
pWnd->SetForegroundWindow();
   
SendKeys("我是sway,我爱中国!\nI love China!\nEmail: xmujava@163.com\t\n2010-05-21 
\b\b");
  }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////

SendInput模拟键盘和鼠标事件

INPUT kbinput[5];
   ZeroMemory(
&kbinput, sizeof(INPUT)*5 );

kbinput[0].type = INPUT_KEYBOARD;
  
kbinput[0].ki.wVk = 'Z';

kbinput[1].type = INPUT_KEYBOARD;
  
kbinput[1].ki.wVk = 'W';

kbinput[2].type = INPUT_KEYBOARD;
  
kbinput[2].ki.wVk = 'J';
   //kbinput[2].ki.dwFlags =
KEYEVENTF_KEYUP;

kbinput[3].type=INPUT_MOUSE;
  
kbinput[3].mi.dx=100;
   kbinput[3].mi.dy=100;
  
kbinput[3].mi.mouseData=0;
  
kbinput[3].mi.dwFlags=MOUSEEVENTF_RIGHTDOWN;

kbinput[4].type=INPUT_MOUSE;
  
kbinput[4].mi.dx=100;
   kbinput[4].mi.dy=100;
  
kbinput[4].mi.mouseData=0;
  
kbinput[4].mi.dwFlags=MOUSEEVENTF_RIGHTUP;

UINT uRet = SendInput( 5, kbinput, sizeof(INPUT) );

keybd_event 被 SendInput 替代的更多相关文章

  1. keybd_event、SendInput笔记

    void keybd_event(BYTE bVk, BYTE bScan, DWORD dwFlags, ULONG_PTR dwExtraInfo); bVk:虚拟键码 bScan:键的硬件扫描码 ...

  2. keybd_event 转载

    转自 http://apps.hi.baidu.com/share/detail/14468670 Option Explicit Private Declare Sub keybd_event Li ...

  3. keybd_event 对应表

    Option Explicit Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bSc ...

  4. VB调用sendinput API

    http://files.cnblogs.com/files/liuzhaoyzz/VB%E8%B0%83%E7%94%A8sendinput_API.rar sendinput只支持发送字符或者组合 ...

  5. 在VC中使用SendInput函数实现中文的自动输入

    很早以前写了一个刷卡程序,功能是定时监控读卡器,当发现有IC卡放到读卡器上后,自动识别出卡号,然后带着这个卡号搜索一个英文用户名和卡号的对照表,最后把英文用户名直接自动输入到当前光标所在的位置.本来程 ...

  6. 用lucene替代mysql读库的尝试

    采用lucene对mysql中的表建索引,并替代全文检索操作. 备注:代码临时梳理很粗糙,后续修改. import java.io.File; import java.io.IOException; ...

  7. Notepad2替代系统自带的记事本

    事情是这样的,平时我经常把一些文字复制到记事本中编辑好了再复制到目标位置,可以在系统自带的记事本中替换删除一些内容,记事本小巧,占用很少的资源,我很喜欢:但今天复制的内容中有很多数字和一些我不想要的内 ...

  8. 在Wcf中应用ProtoBuf替代默认的序列化器

    Google的ProtoBuf序列化器性能的牛逼已经有目共睹了,可以把它应用到Socket通讯,队列,Wcf中,身为dotnet程序员一边期待着不久后Grpc对dotnet core的支持更期待着Wc ...

  9. Autoit中用PrintWindow替代ScreenCapture函数实现截图

    想截取躲在后面的窗体或控件,找到了PrintWindow函数,幸运的是Autoit3也对此进行了封装以方便使用. 于是乎,将帮助文件里的_WinAPI_PrintWindow()实例改写了一下,以替代 ...

随机推荐

  1. Ex 6_21 最小点覆盖问题_第八次作业

    子问题定义: 对于图中的每个结点,有两种状态,即属于最小点覆盖和不属于最小点覆盖,定义minSet[i][0]表示结点i属于点覆盖,并且以i为根的树的最小点覆盖的大小.minSet[i][1]表示点i ...

  2. python 全栈开发,Day107(CRM初始,权限组件之权限控制,权限系统表设计)

    一.CRM初始 CRM,客户关系管理系统(Customer Relationship Management).企业用CRM技术来管理与客户之间的关系,以求提升企业成功的管理方式,其目的是协助企业管理销 ...

  3. java:从指定问价中读取80个字节写入指定文件中

    import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; public class F ...

  4. Rookey.Frame v1.0快速开发平台-整体介绍

    Rookey.Frame v1.0是一套基于.NET MVC的极速开发框架,支持简单逻辑模块零代码编程.支持二次开发,具有高扩展性.高复用性.高伸缩性. 框架特点 (1)简单逻辑模块实现零代码编程,通 ...

  5. python常用内建模块--collections

    1.namedtuple #namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素.#这样一来,我们用n ...

  6. Python 模块介绍

    一.模块:用一坨代码实现了某个功能的代码集合. 二.模块分为三种 1.自定义模块 2.内置标准模块(又称标准库) 3.开源模块(上传方式,百度PyPi) 开源模块安装方式: a.yum b.pip c ...

  7. 【Java】 剑指offer(61) 扑克牌的顺子

      本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连 ...

  8. routing路由模式

    一:介绍 1.模式 2.应用场景 如果exchangge与队列中的key相同,消息就发送过去. 这个就是需要将交换机与队列增加key. 3.路由类型 上节课的订阅模式中的路由类型是Fanout. 这篇 ...

  9. 打印不同对象的字节表示 ( 对int*强制转换成unsigned char*的理解 )

    此文章参考<深入理解计算机系统>P31. 先看如下代码:  12345的十六进制表示为:0x00003039 #include <stdio.h> int main() { ; ...

  10. 洛谷 P1464 Function【记忆化搜索】

    题目链接 题目描述 对于一个递归函数w(a,b,c) 如果a<=0 or b<=0 or c<=0就返回值1. 如果a>20 or b>20 or c>20就返回w ...