0

I find it quite handy that I can use SendInput to mix text and controls into single string and send it to any application like "\nHello\nWorld" would end up with sequence of

hit {Enter}
type "Hello"
hit {enter}
type "World"

In any chat application (for example).

I have the following code

void GenerateKey(WORD vk)
{

    INPUT Input;
    ZeroMemory(&Input, sizeof(Input));
    Input.type = INPUT_KEYBOARD;
    Input.ki.time = 0;
    Input.ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_UNICODE;
    Input.ki.wVk = vk;
    SendInput(1, &Input, sizeof(INPUT));
    return;
}

void SendKeys(const LPCWSTR format, ...)
{
    va_list args;
    va_start(args, format);
    WCHAR buffer[256];
    vswprintf_s(buffer, format, args);
    va_end(args);

    BlockInput(true);
    for (unsigned int i = 0; i<wcslen(buffer); ++i)
        GenerateKey((WORD)VkKeyScan(buffer[i]));
    BlockInput(false);
}

It works pretty fine. But the problem is, it depends on the currently used layout of the keyboard.

I have also tried to use LoadKeyboardLayout, but the following has no effect at all

    BlockInput(true);
    HKL keyboard_layout = LoadKeyboardLayout("00000409", KLF_ACTIVATE);
    for (unsigned int i = 0; i<wcslen(buffer); ++i)
        GenerateKey((WORD)VkKeyScanExW(buffer[i], keyboard_layout));
    BlockInput(false);

I also welcome any ideas to achieve the goal.

SendMessage isn't an option, cause it would require me to get a handle to the window and also to the text box.

LPVOID
  • 306
  • 3
  • 17
  • Uhh `(LPCWSTR)0x409` does not look at all like [MSDN's examples](https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-loadkeyboardlayoutw). – chris Apr 17 '19 at 13:24
  • @chris quote from [MSDN](https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-loadkeyboardlayoutw) page > "For example, U.S. English has a language identifier of 0x0409, so the primary U.S.". Did I get it wrong? – LPVOID Apr 17 '19 at 13:25
  • "so the primary U.S. English layout is named `"00000409"`". It also says at the very beginning that it expects a name, not a pointer whose value bytes form an ID. – chris Apr 17 '19 at 13:29
  • @chris OMG yes you were right :) :) silly me. I got the latter working, but it still doesn't work as expected. Still sends Russian chars instead of English while I have Russian layout on my machine. Any thoughts? – LPVOID Apr 17 '19 at 13:32
  • Unfortunately, I can't say I'm familiar with the workings of input locales or how `SendInput` is implemented. – chris Apr 17 '19 at 13:53

1 Answers1

2

Calling SendInput() with cInputs==1 is (almost) always a bug in user code. Don't do it! One of the main benefits of using SendInput() over keybd_event() is the ability to atomically submit an array of inputs at one time (so no need for using BlockInput() at all).

In this case, create an array of INPUTs for the keystrokes, and then call SendInput() only 1 time. And, per the KEYBDINPUT documentation, KEYEVENTF_UNICODE doesn't take virtual key codes as input, it takes actual Unicode text characters instead (which means you don't have to deal with keyboard layouts manually).

You are also not sending KEYEVENTF_KEYUP events at all. You need those, even when using KEYEVENTF_UNICODE.

See my previous answer to Sending Two or more chars using SendInput for more details.

With that said, try something more like this instead:

#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));
}

void SendKeys(LPCWSTR format, ...)
{
    std::wstring buffer;

    va_list args;
    va_start(args, format);

    buffer.resize(_vscwprintf(format, args) + 1);
    buffer.resize(vswprintf_s(&buffer[0], buffer.size(), format, args));

    va_end(args);

    SendInputString(buffer);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770