【问题标题】:Sending Two or more chars using SendInput使用 SendInput 发送两个或更多字符
【发布时间】:2015-09-27 02:46:08
【问题描述】:

要发送一个字符,我们可以使用 SendInput。如何使用它发送多个字符?

我尝试了这段代码,但它没有发送任何内容:

INPUT in;
in.type=INPUT_KEYBOARD;
in.ki.wScan=0;
in.ki.time=0;
in.ki.dwExtraInfo=0;
in.ki.wVk=0x53+0x54;

SendInput(2,&in,sizeof(INPUT));

那么,正确的方法是什么?

【问题讨论】:

    标签: c++ winapi


    【解决方案1】:

    如果我没听错的话,你会想要更多类似这样的东西:

    INPUT in[4] = {0}; // four inputs
    
    // first input 0x53
    in[0].type=INPUT_KEYBOARD;
    in[0].ki.wScan=0;
    in[0].ki.dwFlags=0;
    in[0].ki.time=0;
    in[0].ki.dwExtraInfo=0;
    in[0].ki.wVk=0x53;
    
    in[1] = in[0];
    in[1].ki.dwFlags |= KEYEVENTF_KEYUP;
    
    // second input 0x54
    in[2].type=INPUT_KEYBOARD;
    in[2].ki.wScan=0;
    in[2].ki.dwFlags=0;
    in[2].ki.time=0;
    in[2].ki.dwExtraInfo=0;
    in[2].ki.wVk=0x54;
    
    in[3] = in[2];
    in[3].ki.dwFlags |= KEYEVENTF_KEYUP;
    
    SendInput(4,in,sizeof(INPUT));
    

    可能希望将设置 INPUT 结构的繁重工作包装到一个函数中以减少重复。

    【讨论】:

    • 那个dosnt工作,即使它工作,发送变量字符串也太难了
    • @Kordy,如果使用正确,它确实有效(这个答案部分正确 - 它为 keydown 事件发送了两个输入,但没有为相应的 keyup 事件 - 我已经更正了)。对于可变长度字符串,您只需在运行时根据字符串中实际包含的字符数动态分配INPUT 数组,然后用每个字符填充数组。并且在发送字符串时,最好使用KEYEVENTF_UNICODE 标志来发送实际的UTF-16 字符,而不是发送虚拟键码,
    • @RemyLebeau 你能给我一个使用 KEYEVENTF_UNICODE 标志的例子吗?
    • @Kordy:我发布了一个答案,展示了KEYEVENTF_UNICODE
    【解决方案2】:

    SendInput() 的第一个参数指定要传入多少个INPUT 结构。你只传入了 1,但你告诉SendInput() 你传入了 2。

    您不能在单个INPUT 中指定两个单独的虚拟键。您需要为每个虚拟键声明一个包含多个INPUTs、2 个INPUTs 的数组——一个用于keydown 事件,一个用于keyup 事件。因此,在您的示例中,您实际上需要 4 个INPUTs 来发送 2 个虚拟键,如 @user4581301 的答案所示。

    现在,关于 KEYEVENTF_UNICODE,您不使用虚拟键,而是使用实际的 Unicode 代码点,它们使用 UTF-16 代码单元指定,每个 INPUT 一个。所以这意味着如果你想发送一个需要 UTF-16 代理对的 Unicode 代码点,你需要 2 组 down/up INPUTs,一组用于高代理,一组用于低代理。 SendInput() 文档中提到该警告,但 vScan 字段是 16 位 WORD 并且 KEYEVENTF_UNICODE 事件生成 WM_CHAR 消息这一事实暗示了这一点,它将 UTF-16 代理代码单元作为单独的消息传递。

    因此,要使用 KEYEVENTF_UNICODE 发送一串 Unicode 字符,您可以执行以下操作:

    #include <vector>
    #include <string>
    
    void SendInputString(const std::wstring &str)
    {
        int len = str.length();
        if (len == 0) return;
    
        std::vector<INPUT> in(len*2);
        ZeroMemory(&in[0], in.size()*sizeof(INPUT));
    
        int i = 0, idx = 0;
        while (i < len)
        {
            WORD ch = (WORD) str[i++];
    
            if ((ch < 0xD800) || (ch > 0xDFFF))
            {
                in[idx].type = INPUT_KEYBOARD;
                in[idx].ki.wScan = ch;
                in[idx].ki.dwFlags = KEYEVENTF_UNICODE;
                ++idx;
    
                in[idx] = in[idx-1];
                in[idx].ki.dwFlags |= KEYEVENTF_KEYUP;
                ++idx;
            }
            else
            {
                in[idx].type = INPUT_KEYBOARD;
                in[idx].ki.wScan = ch;
                in[idx].ki.dwFlags = KEYEVENTF_UNICODE;
                ++idx;
    
                in[idx].type = INPUT_KEYBOARD;
                in[idx].ki.wScan = (WORD) str[i++];
                in[idx].ki.dwFlags = KEYEVENTF_UNICODE;
                ++idx;
    
                in[idx] = in[idx-2];
                in[idx].ki.dwFlags |= KEYEVENTF_KEYUP;
                ++idx;
    
                in[idx] = in[idx-2];
                in[idx].ki.dwFlags |= KEYEVENTF_KEYUP;
                ++idx;
            }
        }
    
        SendInput(in.size(), &in[0], sizeof(INPUT));
    }
    

    【讨论】:

    • wstringbasic_string&lt;wchar_t&gt; 的缩写。 wchar_tunsigned short 大小相同(Windows、Visual Studio)。 unsigned short 不能保存 >= 0x10000 的值。如果是这样,您的代码将显示 UB,因为它会写到容器末尾之外。我相信也不需要调用ZeroMemory:容器将填充默认(零)初始化结构。
    • 是的,wchar_t 是对的。我正在查看另一段接受代码点而不是代码单元的代码,但忘记调整if。我已经更正了。
    【解决方案3】:

    其他答案似乎不适用于 DirectX 游戏。我在 TrackMania Nations 中测试了以下内容,它工作正常:(记事本等也可以)

    #define _WIN32_WINNT 0x0500
    #include <windows.h>
    #include <string>
    
    void press_some_keys (std::string str) {
        INPUT input[2 * (int)str.size()] = {0};
    
        int cnt = 0;
        for (char ch: str) {
            input[cnt].type = INPUT_KEYBOARD;
            input[cnt].ki.dwFlags = KEYEVENTF_SCANCODE;
            input[cnt].ki.wScan = MapVirtualKey(LOBYTE(VkKeyScan(ch)), 0);
            cnt++;
    
            input[cnt] = input[cnt - 1];
            cnt++;
        }
    
        SendInput(2 * (int)str.size(), input, sizeof(INPUT));
        Sleep(10);
    
        for (cnt = 1; cnt < 2 * (int)str.size(); cnt += 2)
            input[cnt].ki.dwFlags |= KEYEVENTF_KEYUP;
    
        SendInput(2 * (int)str.size(), input, sizeof(INPUT));
    }
    

    我只测试了小写字母字符(没有箭头或其他字符),但这对我来说已经足够了。

    让它工作起来很痛苦,因为我发现最接近的是 Youtube 视频中的单击功能:-)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-17
      • 1970-01-01
      • 2011-02-07
      • 1970-01-01
      • 1970-01-01
      • 2016-03-10
      相关资源
      最近更新 更多