使用SendInput发送两个或多个字符

Sending Two or more chars using SendInput

本文关键字:两个 字符 SendInput 使用      更新时间:2023-10-16

要发送一个字符,我们可以使用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));

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

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

不能在单个INPUT中指定两个独立的虚拟密钥。您需要声明一个由多个INPUT s组成的数组,每个虚拟键有2个INPUT s——一个用于keydown事件,一个用于key up事件。因此,在您的示例中,您实际上需要4个INPUT来发送2个虚拟密钥,如@user4581301的回答所示。

现在,关于KEYEVENTF_UNICODE,您不使用虚拟键,而是使用实际的Unicode代码点,它们是使用UTF-16代码单元指定的,每个INPUT一个。因此,这意味着,如果要发送需要UTF-16代理项对的Unicode代码点,则需要两组向下/向上的INPUTs,一组用于高代理项,另一组用于低代理项。这个警告是SendInput()文档中提到的NOT,但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));
}

如果我说得对,你需要更多类似以下内容的东西:

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结构的繁重工作打包为一个函数,以减少重复。

其他答案似乎在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视频中的一个点击功能:-(