-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?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62

1 Answers1

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());
    }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 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. – Jedi Commymullah May 19 '18 at 01:30
  • 1
    @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. – Remy Lebeau May 19 '18 at 01:54