-1

I have a problem with this project in C#: When using WinAPI SendInput function

 /// <summary> /// Sends Unicode (UTF16) string to foreground window. /// </summary> /// <param name="inputString">String to be sent to foreground window.</param> internal static void Send(string inputString) { if (inputString == string.Empty) { return; } char[] chars = inputString.ToCharArray(); INPUT[] pInputs = new INPUT[chars.Length * 2]; for (int i = 0; i < chars.Length; i++) { UInt16 unicode = chars[i]; pInputs[i * 2] = new INPUT(); pInputs[i * 2].type = INPUT_KEYBOARD; pInputs[i * 2].ki.wVk = 0; pInputs[i * 2].ki.wScan = unicode; pInputs[i * 2].ki.dwFlags = KEYEVENTF_UNICODE; pInputs[i * 2].ki.time = 0; pInputs[i * 2].ki.dwExtraInfo = SetMessageExtraInfo(IntPtr.Zero); pInputs[i * 2 + 1] = new INPUT(); pInputs[i * 2 + 1].type = INPUT_KEYBOARD; pInputs[i * 2 + 1].ki.wVk = 0; pInputs[i * 2 + 1].ki.wScan = unicode; pInputs[i * 2 + 1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; pInputs[i * 2 + 1].ki.time = 0; pInputs[i * 2 + 1].ki.dwExtraInfo = SetMessageExtraInfo(IntPtr.Zero); } uint nSent = SendInput((uint)chars.Length * 2, pInputs, Marshal.SizeOf(typeof(INPUT))); if (nSent == 0) { Debug.WriteLine("SendInput error " + GetLastError().ToString()); // error 87 : "The parameter is incorrect." } } 

In desktop applications like Notepad or VS the code works fine, but in others it doesn't work with the simple English alphabet and punctuation. A string like "íáó" with special characters works explicitly/universally, but a string like "My car" doesn't. Obviously, the utf-16 values for the chars in "My car" are low, less than 100; the values for á and í are 225 and 237, respectively. Seemingly a superficial difference. Anyone know of a way to get regular English letters to be sent as Unicode to arbitrary windows using SendInput?

3
  • What apps are you having trouble with? Commented May 19, 2018 at 5:11
  • Chrome search boxes, Chrome omnibox, and textboxes in setup apps that install executables on Windows. Commented May 19, 2018 at 6:42
  • Why don't you use UI Automation Commented May 19, 2018 at 9:19

1 Answer 1

1

This is not documentation on MSDN, but when using KEYEVENTF_UNICODE, if a given Unicode codepoint requires the use of UTF-16 surrogates, you need to send both surrogates without KEYEVENTF_KEYUP, then send both surrogates with KEYEVENTF_KEYUP. The code you showed is not accounting for UTF-16 surrogates at all, it is sending down/up input for each surrogate independently. The two surrogates need to be "down" at the same time, then "up" at the same time.

Try something more like this instead (based on a previous answer I posted for the same issue in C++):

/// <summary> /// Sends Unicode (UTF16) string to foreground window. /// </summary> /// <param name="inputString">String to be sent to foreground window.</param> internal static void Send(string inputString) { if (string.IsNullOrEmpty(inputString)) { return; } char[] chars = inputString.ToCharArray(); int len = chars.Length; INPUT[] pInputs = new INPUT[len * 2]; UInt32 ExtraInfo = GetMessageExtraInfo(); int i = 0, idx = 0; while (i < len) { UInt16 ch = chars[i++]; if ((ch < 0xD800) || (ch > 0xDFFF)) { pInputs[idx] = new INPUT(); pInputs[idx].type = INPUT_KEYBOARD; pInputs[idx].ki.wVk = 0; pInputs[idx].ki.wScan = ch; pInputs[idx].ki.dwFlags = KEYEVENTF_UNICODE; pInputs[idx].ki.time = 0; pInputs[idx].ki.dwExtraInfo = ExtraInfo; ++idx; pInputs[idx] = new INPUT(); pInputs[idx].type = INPUT_KEYBOARD; pInputs[idx].ki.wVk = 0; pInputs[idx].ki.wScan = ch; pInputs[idx].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; pInputs[idx].ki.time = 0; pInputs[idx].ki.dwExtraInfo = ExtraInfo; ++idx; } else { UInt16 ch2 = chars[i++]; pInputs[idx] = new INPUT(); pInputs[idx].type = INPUT_KEYBOARD; pInputs[idx].ki.wVk = 0; pInputs[idx].ki.wScan = ch; pInputs[idx].ki.dwFlags = KEYEVENTF_UNICODE; pInputs[idx].ki.time = 0; pInputs[idx].ki.dwExtraInfo = ExtraInfo; ++idx; pInputs[idx] = new INPUT(); pInputs[idx].type = INPUT_KEYBOARD; pInputs[idx].ki.wVk = 0; pInputs[idx].ki.wScan = ch2; pInputs[idx].ki.dwFlags = KEYEVENTF_UNICODE; pInputs[idx].ki.time = 0; pInputs[idx].ki.dwExtraInfo = ExtraInfo; ++idx; pInputs[idx] = new INPUT(); pInputs[idx].type = INPUT_KEYBOARD; pInputs[idx].ki.wVk = 0; pInputs[idx].ki.wScan = ch; pInputs[idx].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; pInputs[idx].ki.time = 0; pInputs[idx].ki.dwExtraInfo = ExtraInfo; ++idx; pInputs[idx] = new INPUT(); pInputs[idx].type = INPUT_KEYBOARD; pInputs[idx].ki.wVk = 0; pInputs[idx].ki.wScan = ch2; pInputs[idx].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; pInputs[idx].ki.time = 0; pInputs[idx].ki.dwExtraInfo = ExtraInfo; ++idx; } } uint nSent = SendInput((uint)pInputs.Length, pInputs, Marshal.SizeOf(typeof(INPUT))); if (nSent == 0) { Debug.WriteLine("SendInput error " + GetLastError().ToString()); } } 
Sign up to request clarification or add additional context in comments.

2 Comments

Sorry, Remy, but this code doesn't do a thing for it. My test strings "M:\Test" and "áí" don't even hit the 'else' section of your code because they are all below 0xD800, and so the results are the same as if through my own code. I'll hang on to this code of yours for reference, but I'm still at a loss.
@JediCommymullah I'm not a C# dev, but what I showed has always worked fine for me in C++. Could be that the apps you are having trouble with simply don't handle keyboard input correctly. Not SendInput's fault.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.