【问题标题】:Underscores do not get displayed with SendInput On C++在 C++ 上使用 SendInput 不显示下划线
【发布时间】:2018-09-23 19:54:33
【问题描述】:

这是我在 stackoverflow 上发布的第一个问题。我一直在研究 C++ 的 SendInput,以便将我的程序“输入”到另一个程序中。我决定先用几个带有下划线的单词将其“输入”到终端中。我发现它输入大小写字母以及句点没有问题。但是在到达下划线后,为下划线字母输入数字 id 95,下划线没有显示,完全就像那个字母没有被按下一样。以下是我从 cplusplus.com 获得的代码,我以此为基础,它功能齐全:

#include <iostream>
#include <windows.h>
using namespace std;

/* HWND = "Window Handle" */
HWND GameWindow = FindWindow(0, "Command Prompt");

/* This is a function to simplify usage of sending keys */
void GenerateKey(int vk, BOOL bExtended) {

    KEYBDINPUT  kb = {0};
    INPUT       Input = {0};

    /* Generate a "key down" */
    if (bExtended) { kb.dwFlags  = KEYEVENTF_EXTENDEDKEY; }
    kb.wVk  = vk;
    Input.type  = INPUT_KEYBOARD;
    Input.ki  = kb;
    SendInput(1, &Input, sizeof(Input));

    /* Generate a "key up" */
    ZeroMemory(&kb, sizeof(KEYBDINPUT));
    ZeroMemory(&Input, sizeof(INPUT));
    kb.dwFlags  =  KEYEVENTF_KEYUP;
    if (bExtended) { kb.dwFlags |= KEYEVENTF_EXTENDEDKEY; }
    kb.wVk = vk;
    Input.type = INPUT_KEYBOARD;
    Input.ki = kb;
    SendInput(1, &Input, sizeof(Input));

    return;
}

int main() {

    /*
       SetForegroundWindow will give the window focus for the
       keyboard/mouse! In other words, you don't have to have
       the game opened upfront in order to emulate key/mouse
       presses, it's very helpful if it's a game that runs
       in fullscreen mode, like StarCraft: Brood War does
    */

    SetForegroundWindow(GameWindow);

    GenerateKey(VK_CAPITAL, TRUE);
    GenerateKey('I', FALSE);
    GenerateKey(' ', FALSE);

    GenerateKey(VK_CAPITAL, TRUE);
    GenerateKey('A', FALSE);
    GenerateKey('M', FALSE);
    GenerateKey(' ', FALSE);

    GenerateKey('C', FALSE);
    GenerateKey('O', FALSE);
    GenerateKey('O', FALSE);
    GenerateKey('L', FALSE);
    GenerateKey('E', FALSE);
    GenerateKey('R', FALSE);
    GenerateKey(' ', FALSE);

    GenerateKey('T', FALSE);
    GenerateKey('H', FALSE);
    GenerateKey('A', FALSE);
    GenerateKey('N', FALSE);
    GenerateKey(' ', FALSE);

    GenerateKey('Y', FALSE);
    GenerateKey('O', FALSE);
    GenerateKey('U', FALSE);
    GenerateKey(' ', FALSE);

    GenerateKey('W', FALSE);
    GenerateKey('I', FALSE);
    GenerateKey('L', FALSE);
    GenerateKey('L', FALSE);
    GenerateKey(' ', FALSE);

    GenerateKey('E', FALSE);
    GenerateKey('V', FALSE);
    GenerateKey('E', FALSE);
    GenerateKey('R', FALSE);
    GenerateKey(' ', FALSE);

    GenerateKey('B', FALSE);
    GenerateKey('E', FALSE);
    GenerateKey('n', FALSE);
    GenerateKey(' ', FALSE);

    GenerateKey(0x3A, FALSE); /* period key */
    GenerateKey(0x0D, FALSE); /* enter key */

    return 0;
}

这是我编写的运行不正确的代码:

#include <iostream>
#include <fstream>
#include <windows.h>

using namespace std;

/* HWND = "Window Handle" */
HWND GameWindow = FindWindow(0, "Command Prompt");

/* This is a function to simplify usage of sending keys */
void GenerateKey(int vk, BOOL bExtended) {

    KEYBDINPUT  kb = {0};
    INPUT       Input = {0};

    /* Generate a "key down" */
    if (bExtended) { kb.dwFlags  = KEYEVENTF_EXTENDEDKEY; }
    kb.wVk  = vk;
    Input.type  = INPUT_KEYBOARD;
    Input.ki  = kb;
    SendInput(1, &Input, sizeof(Input));

    /* Generate a "key up" */
    ZeroMemory(&kb, sizeof(KEYBDINPUT));
    ZeroMemory(&Input, sizeof(INPUT));
    kb.dwFlags  =  KEYEVENTF_KEYUP;
    if (bExtended) { kb.dwFlags |= KEYEVENTF_EXTENDEDKEY; }
    kb.wVk = vk;
    Input.type = INPUT_KEYBOARD;
    Input.ki = kb;
    SendInput(1, &Input, sizeof(Input));

    return;
}

int main() {

    /*
       SetForegroundWindow will give the window focus for the
       keyboard/mouse! In other words, you don't have to have
       the game opened upfront in order to emulate key/mouse
       presses, it's very helpful if it's a game that runs
       in fullscreen mode, like StarCraft: Brood War does
    */

    SetForegroundWindow(GameWindow);

    GenerateKey(VK_CAPITAL, TRUE);
    GenerateKey('N', FALSE);
    GenerateKey(VK_CAPITAL, TRUE);
    GenerateKey('I', FALSE);
    GenerateKey('N', FALSE);
    GenerateKey('J', FALSE);
    GenerateKey('A', FALSE);
    GenerateKey(0xBE, FALSE);   // GenerateKey(0x3A, FALSE); did not work
    GenerateKey(' ', FALSE);
    GenerateKey(VK_CAPITAL, TRUE);
    GenerateKey('H', FALSE);
    GenerateKey(VK_CAPITAL, TRUE);
    GenerateKey('I', FALSE);
    GenerateKey('1', FALSE);
    GenerateKey('2', FALSE);
    GenerateKey('3', FALSE);
    GenerateKey(95 , FALSE);   // GenerateKey('_', FALSE); did not work either
    GenerateKey('4', FALSE);
    GenerateKey('5', FALSE);
    GenerateKey('6', FALSE);
    return 0;
}

这会输出Ninja. Hi123456 而不是Ninja. Hi123_456

其他值得注意的事情:

1)。在“输入”期间(“.”),工作 ID 是 0xBE 而不是 0x3A

2)。这是使用 Mingw 在 Windows 10 上编译的。

我希望这足够彻底,提前谢谢你!

【问题讨论】:

  • 95 是VK_SLEEP,睡眠键的虚拟键码。为什么你又期望它产生下划线?回想一下SendInput 发送的是按键,而不是字符。这些按键产生的字符取决于接收线程的键盘布局。直接按 Tab 右侧的键并不总是产生Q
  • 下划线不是键。这是您在按住 Shift 并按连字符键时得到的字符(假设是美式键盘布局)。
  • 查看Spy++,查看使用键盘时实际发送到窗口的消息。它带有 Visual Studio。

标签: c++ winapi sendinput


【解决方案1】:

虚拟密钥代码0x3A 不是句点字符。事实上,per Microsoft's documentation0x3A 甚至根本没有定义。对于句点字符,您必须改用VK_OEM_PERIOD

VK_OEM_PERIOD
0xBE

对于任何国家/地区,“.”键

话虽如此,用cInputs=1 调用SendInput()通常是一个逻辑错误。正如您的示例代码所做的那样,当您背靠背发送多个输入事件时,肯定总是会出现错误。 SendInput() 存在的全部原因是为了替换keybd_event()(和mouse_event()),它一次只能发送一个输入事件。在模拟多个事件时,您不希望在您的事件之间注入其他事件,反之亦然。 SendInput() 与其他输入机制是原子性的,但是在发送多个事件时,只有在一次发送所有事件时才能保证原子性。

您应该将您的INPUTs 放入一个数组并调用SendInput() ONCE,并将cInputs 设置为您要发送的INPUTs 的总数。

另外,当发送文本字符的键输入时,使用需要使用VkKeyScan/Ex() 来获得正确的虚拟键码和转换状态,虽然使用use the KEYEVENTF_UNICODE flag 更容易,所以你可以发送实际的 Unicode 字符虚拟键码。

试试类似的方法:

#include <iostream>
#include <vector>
#include <string>
#include <windows.h>

/* HWND = "Window Handle" */
HWND GameWindow = FindWindow(NULL, TEXT("Command Prompt"));

void GenerateKeyDown(std::vector<INPUT> &inputQueue, int vk, bool bExtended = false)
{
    INPUT in = {};

    in.type = INPUT_KEYBOARD;
    in.ki.wVk = vk;
    if (bExtended) in.ki.dwFlags = KEYEVENTF_EXTENDEDKEY;

    inputQueue.push_back(in);
}

void GenerateKeyUp(std::vector<INPUT> &inputQueue, int vk, bool bExtended = false)
{
    INPUT in = {};

    in.type = INPUT_KEYBOARD;
    in.ki.wVk = vk;
    if (bExtended) in.ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
    in.ki.dwFlags |= KEYEVENTF_KEYUP;

    inputQueue.push_back(in);
}

void GenerateKey(std::vector<INPUT> &inputQueue, int vk, bool bExtended = false)
{
    INPUT in[2] = {};

    in[0].type = INPUT_KEYBOARD;
    in[0].ki.wVk = vk;
    if (bExtended) in[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;

    in[1] = in[0];
    in[1].ki.dwFlags |= KEYEVENTF_KEYUP;

    inputQueue.insert(inputQueue.end(), in, in+1);
}

void GenerateString(std::vector<INPUT> &inputQueue, const std::wstring &str)
{
    int len = str.length();
    if (len == 0) return;

    inputQueue.reserve(inputQueue.size()+(len*2));

    INPUT in = {};
    in.type = INPUT_KEYBOARD;
    in.ki.dwFlags = KEYEVENTF_UNICODE;

    int i = 0;
    while (i < len)
    {
        WORD ch = (WORD) str[i++];

        if ((ch < 0xD800) || (ch > 0xDFFF))
        {
            in.ki.wScan = ch;
            in.ki.dwFlags &= ~KEYEVENTF_KEYUP;
            inputQueue.push_back(in);

            in.ki.dwFlags |= KEYEVENTF_KEYUP;
            inputQueue.push_back(in);
        }
        else
        {
            WORD ch2 = (WORD) str[i++];

            in.ki.wScan = ch;
            in.ki.dwFlags &= ~KEYEVENTF_KEYUP;
            inputQueue.push_back(in);

            in.ki.wScan = ch2;
            inputQueue.push_back(in);

            in.ki.wScan = ch;
            in.ki.dwFlags |= KEYEVENTF_KEYUP;
            inputQueue.push_back(in);

            in.ki.wScan = ch2;
            inputQueue.push_back(in);
        }
    }
 }

int main()
{
    /*
       SetForegroundWindow will give the window focus for the
       keyboard/mouse! In other words, you don't have to have
       the game opened upfront in order to emulate key/mouse
       presses, it's very helpful if it's a game that runs
       in fullscreen mode, like StarCraft: Brood War does
    */

    SetForegroundWindow(GameWindow);

    std::vector<INPUT> inputQueue;

    /*
    GenerateString(inputQueue, L"I Am cooler than you will ever ben .");
    GenerateKey(inputQueue, VK_RETURN);
    */

    GenerateString(inputQueue, L"NInja. HI123_456");

    /* alternatively:

    GenerateString(inputQueue, L"NInja");
    GenerateKey(inputQueue, VK_OEM_PERIOD);
    GenerateString(inputQueue, L" HI123");

    // see why using KEYEVENTF_UNICODE is easier?
    SHORT ret = VkKeyScanW(L'_');
    BYTE vk = LOBYTE(ret);
    BYTE shift = HIBYTE(ret);
    if (vk != -1)
    {
        SHORT state = GetKeyState(VK_SHIFT);
        bool bIsDown = (state & 0x800);

        if (shift & 1)
        {
            if (!bIsDown)
                GenerateKeyDown(inputQueue, VK_SHIFT);
        }
        else
        {
            if (bIsDown)
                GenerateKeyUp(inputQueue, VK_SHIFT);
        }

        GenerateKey(inputQueue, vk);

        if (shift & 1) 
        {
            if (!bIsDown)
                GenerateKeyUp(inputQueue, VK_SHIFT);
        }
        else
        {
            if (bIsDown)
                GenerateKeyDown(inputQueue, VK_SHIFT);
        }
    }

    GenerateString(inputQueue, L"456");
    */

    SendInput(inputQueue.size(), &inputQueue[0], sizeof(INPUT));

    return 0;
}

【讨论】:

  • 谢谢!对此,我真的非常感激。我希望您不介意我发布了我自己的问题的第二个答案,只是修复了您代码中的错误。看起来我的代码基于我的东西是有缺陷的:(
  • @GavinDownard 虽然您可以对自己的问题发布自己的答案,但简单地指出我的答案中的错误以便我可以修复它们会更礼貌(我有现在完成了)然后你就可以接受了。
猜你喜欢
  • 1970-01-01
  • 2013-08-22
  • 2021-05-28
  • 1970-01-01
  • 1970-01-01
  • 2021-09-15
  • 2017-01-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多