Sending Two or More Chars Using Sendinput

Sending Two or more chars using SendInput

The first parameter of SendInput() specifies how many INPUT structures you are passing in. You are only passing in 1, but you are telling SendInput() that you are passing in 2.

You cannot specify two separate virtual keys in a single INPUT. You need to declare an array of multiple INPUTs, 2 INPUTs for each virtual key - one for the keydown event, and one for the keyup event. So, in your example, you actually need 4 INPUTs to send 2 virtual keys, as shown in @user4581301's answer.

Now, regarding KEYEVENTF_UNICODE, you don't use virtual keys with it, you use actual Unicode codepoints instead, where they are specified using UTF-16 codeunits, one per INPUT. So that means if you want to send a Unicode codepoint that requires a UTF-16 surrogate pair, you need 2 sets of down/up INPUTs, one set for the high surrogate, and one set for the low surrogate. That caveat is NOT mentioned in the SendInput() documentation, but it is implied by the fact that the vScan field is a 16bit WORD, and that KEYEVENTF_UNICODE events generate WM_CHAR messages, which passes UTF-16 surrogate codeunits as separate messages.

So, to send a string of Unicode characters using KEYEVENTF_UNICODE, you can do something like this:

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

Repetitious characters using send input in delphi

It is invariably a mistake to call SendInput multiple times in a loop in that way. The whole point of SendInput is that it batches up a series of related input events, and sends them as an atomic group. This is stated quite explicitly in the documentation for SendInput and I recommend that you re-read that.

The first thing to change therefore is to use your loop to build an array of input events, and send that array in its entirety with a single call to SendInput, made after your loop completes.

Another problem is that your code currently fakes the key down events, but omits to fake the key up events. Each character that you type involves the key going down, and then coming back up. So, your array needs to be sized to contain twice as many items as characters in the string. And for each character you need to include both key down and key up. Include KEYEVENTF_KEYUP in dwFlags to indicate a key up event.

Yet another problem that I can see is that you are working with uninitialised variables. You set some but not all fields of ki. You need to make sure that the entire record is initialized.

There seems little reason for you to make a copy of the input string. You can work with the input string directly. There is nothing to be gained from making a copy of it.

Finally, did you consider using UI Automation instead of faking input?

Reading UTF-8 file, passing contents to other application using SendInput

You need to convert the UTF-8 data to UTF-16 first, such as with MultiByteToWideChar() or equivalent, and then you can pass the individual UTF-16 codeunits to SendInput() using the KEYEVENTF_UNICODE flag. See my answer to Sending Two or more chars using SendInput for an example of sending UTF-16 strings with SendInput().

How to make SendInput use fixed (en-US) layout regardless of what layouts the user have installed or uses?

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

SendInput strings?

The virtual key codes does not generally correspond to the ASCII alphabet.

If you read e.g. this MSDN reference for virtual key-codes you will see that e.g. lower-case 'a' (which has ASCII value 0x61) corresponds to VK_NUMPAD1 which is the 1 key on the numeric keyboard.

The upper-case ASCII letters do correspond to the correct virtual key codes, so you need to make all letters upper-case when assigning to bob[i].ki.wVk. For all other symbols and characters you need to translate the character to the virtual key code.

Why isn't SendInput() working?

You are working with ANSI string so use VkKeyScanExA, or switch to std::wstring. But don't mix ANSI with UNICODE, and don't use casting to hide the compiler warnings.

You have to reset ip.ki.dwFlags to zero to indicate key down. Then set it to KEYEVENTF_KEYUP.

You also have to check the key state for caplock and shift, etc. Using SendInput in that way is pointless, the first parameter is there to allow sending multiple inputs at once. You might as well use keybd_event

void presskeys(string s) 
{
INPUT ip;
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = 0;
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;
HKL kbl = GetKeyboardLayout(0);
for (unsigned int i = 0; i < s.length(); ++i)
{
char c = s[i];
ip.ki.wVk = VkKeyScanExA(c, kbl); //<== don't mix ANSI with UNICODE
ip.ki.dwFlags = 0; //<= Add this to indicate key-down
SendInput(1, &ip, sizeof(ip));
ip.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &ip, sizeof(ip));
}
}


Related Topics



Leave a reply



Submit