• keybd_event 被 SendInput 替代


     keybd_event
      函数功能:该函数合成一次击键事件。系统可使用这种合成的击键事件来产生WM_KEYUP或WM_KEYDOWN消息,键盘驱动程序的中断处理程序调用keybd_event函数。在Windows NT中该函数己被使用SendInput来替代它。

    -------------------------------------------------- 

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

    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字符会直接发送给应用程序。 

    --------------------------------------------------------------------------------------

    http://xylvhp.blog.163.com/blog/static/31123614201101104644542/

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

    2011-01-11 12:46:44| 分类: C++ 
    |字号
    订阅


    首先是,头文件必须包含以下两个:
    #include <winable.h>
    #include <atlconv.h> 

    前者是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 = http://www.cnblogs.com/-clq/archive/2011/10/01/T2W(msg.GetBuffer(0));
    int len = wcslen(data);

    for(int i=0;i<len;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,我爱中国! I love China! Email: xmujava@163.com 2010-05-21 ");
    }






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

    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=http://www.cnblogs.com/-clq/archive/2011/10/01/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=http://www.cnblogs.com/-clq/archive/2011/10/01/0;
    kbinput[4].mi.dwFlags=MOUSEEVENTF_RIGHTUP;

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

  • 相关阅读:
    Linux进程状态——top,ps中看到进程状态D,S,Z的含义
    openstack-neutron基本的网络类型以及分析
    openstack octavia的实现与分析(二)·原理,架构与基本流程
    网络虚拟化基础协议·Geneve
    openstack octavia的实现与分析(一)·openstack负载均衡的现状与发展以及lvs,Nginx,Haproxy三种负载均衡机制的基本架构和对比
    Linux下多网卡绑定bond及模式介绍
    Python调试器-pdb的使用
    DELL R720服务器安装Windows Server 2008 R2 操作系统图文详解
    JAVA WEB实现前端加密后台解密
    JAVA实现AES和MD5加密
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/3147483.html
Copyright © 2020-2023  润新知