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.
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 INPUT
s 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);
}