Question

I'm working with an on-screen keyboard that needs to send key strokes to a third party application. They are running on Windows XP. A small set of characters that are not available on the US English keyboard need to be supported (such as "å" or ñ). After reviewing SendInput it seemed like the safest thing would be to send the hex unicode value of the character as a key stroke sequence. I wrote code that sends an "Alt" and "Add" key down event, followed by key down and up events for the four character unicode sequence with the Alt key ORed, then finally "Add" and "Alt" key up events. In my C# test app. I am using KeyPreview and sure enough, all of the events are coming through however all I get is a beep, no character. I have captured the same information from entering the key strokes manually, the KeyPreview information is identical, and the character appears.

Is it possible to use SendInput in this way? I haven't used a hook to examine the data but I've seen posts that indicate SendInput events have some sort of "injected" flag attached, maybe this causes the sequence to fail?

This demo code successfully sends key events, but a sequence of key events intended to generate a unicode character fails.

    private const uint KEYEVENTF_KEYDOWN = 0x0000;
    private const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
    private const uint KEYEVENTF_KEYUP = 0x0002;

    private const int INPUT_KEYBOARD = 1;

    [DllImport ("user32.dll", SetLastError = false)]
    static extern IntPtr GetMessageExtraInfo ();

    [DllImport ("user32.dll", SetLastError = true)]
    static extern uint SendInput (uint nInputs, [MarshalAs (UnmanagedType.LPArray, SizeConst = 1)] INPUT[] pInputs, int cbSize);

    [StructLayout (LayoutKind.Sequential, Size = 24)]
    private struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout (LayoutKind.Explicit)]
    private struct INPUT
    {
        [FieldOffset (0)]
        public int type;
        [FieldOffset (4)]
        public KEYBDINPUT ki;
    }

    private void PressKey (Keys k)
    {
        PressKeyDown (k);
        PressKeyUp (k);
    }

    private void PressKeyDown (Keys k)
    {
        INPUT input = new INPUT ();
        input.type = INPUT_KEYBOARD;
        input.ki.wVk = (byte)k;
        input.ki.wScan = 0;
        input.ki.time = 0;

        uint flags = KEYEVENTF_KEYDOWN;
        if ((33 <= (byte)k && (byte)k <= 46) || (91 <= (byte)k) && (byte)k <= 93)
            flags |= KEYEVENTF_EXTENDEDKEY;
        input.ki.dwFlags = flags;

        input.ki.dwExtraInfo = GetMessageExtraInfo ();

        Output ("Sending key down {0}. Flags:{1}", k, flags);

        INPUT[] inputs = new INPUT[] { input };
        uint result = SendInput ((uint)inputs.Length, inputs, Marshal.SizeOf (typeof (INPUT)));

        if ((uint)inputs.Length != result)
            MessageBox.Show ("PressKeyDown result = " + Marshal.GetLastWin32Error ());
    }

    private void PressKeyUp (Keys k)
    {
        INPUT input = new INPUT ();
        input.type = INPUT_KEYBOARD;
        input.ki.wVk = (byte)k;
        input.ki.wScan = 0;
        input.ki.time = 0;

        uint flags = KEYEVENTF_KEYUP;
        if ((33 <= (byte)k && (byte)k <= 46) || (91 <= (byte)k) && (byte)k <= 93)
            flags |= KEYEVENTF_EXTENDEDKEY;
        input.ki.dwFlags = flags;

        input.ki.dwExtraInfo = GetMessageExtraInfo ();

        Output ("Sending key up {0}", k);

        INPUT[] inputs = new INPUT[] { input };
        uint result = SendInput ((uint)inputs.Length, inputs, Marshal.SizeOf (typeof (INPUT)));

        if ((uint)inputs.Length != result)
            MessageBox.Show ("PressKeyUp result = " + Marshal.GetLastWin32Error ());
    }

    private void TestSend ()
    {
        System.Threading.Thread.CurrentThread.Join (1000);

        Keys k = Keys.Menu;
        PressKeyDown (k);

        System.Threading.Thread.Sleep (100);

        k = Keys.Add;
        k |= Keys.Alt;
        PressKeyDown (k);

        System.Threading.Thread.Sleep (100);

        k = Keys.NumPad0;
        k |= Keys.Alt;
        PressKey (k);

        System.Threading.Thread.Sleep (100);

        k = Keys.NumPad0;
        k |= Keys.Alt;
        PressKey (k);

        System.Threading.Thread.Sleep (100);

        k = Keys.E;
        k |= Keys.Alt;
        PressKey (k);

        System.Threading.Thread.Sleep (100);

        k = Keys.NumPad5;
        k |= Keys.Alt;
        PressKey (k);

        System.Threading.Thread.Sleep (100);

        PressKeyUp (Keys.Add);
        PressKeyUp (Keys.Menu);
    }
Was it helpful?

Solution 3

Apparently the processing of a sequence of key presses to represent a unicode character is done at a level that is not accessible through SendInput. I changed my code to set the unicode flag on dwFlags and set the unicode value on the wScan data parameter. I've managed to convince myself after testing with several European and Asian languages that this creates the same results as the multiple keystroke method.

OTHER TIPS

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Simulate
{
    public class Simulate
    {
        [DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
        static extern UInt32 SendInput(UInt32 numberOfInputs, INPUT[] input, Int32 sizeOfInputStructure);

        [StructLayout(LayoutKind.Sequential, Size = 24)]
        struct KEYBDINPUT
        {
            public UInt16 Vk;
            public UInt16 Scan;
            public UInt32 Flags;
            public UInt32 Time;
            public UInt32 ExtraInfo;
        }

        [StructLayout(LayoutKind.Explicit)]
        private struct INPUT
        {
            [FieldOffset(0)]
            public int Type;
            [FieldOffset(4)]
            public KEYBDINPUT ki;
        } 

        public static void TextInput(string text)
        {
            char[] chars = text.ToCharArray();

            for (int i = 0; i < chars.Length; i++)
            {
                UInt16 unicode = chars[i];

                INPUT down = new INPUT();
                down.Type = 1; //INPUT_KEYBOARD
                down.ki.Vk = 0;
                down.ki.Scan = unicode;
                down.ki.Time = 0;
                down.ki.Flags = 0x0004; //KEYEVENTF_UNICODE
                down.ki.ExtraInfo = 0;

                INPUT up = new INPUT();
                up.Type = 1; //INPUT_KEYBOARD
                up.ki.Vk = 0;
                up.ki.Scan = unicode;
                up.ki.Time = 0;
                up.ki.Flags = 0x0004; //KEYEVENTF_UNICODE
                up.ki.ExtraInfo = 0;

                INPUT[] input = new INPUT[2];
                input[0] = down;
                input[1] = up;
                SendInput(1, input, Marshal.SizeOf(typeof(INPUT)));
            }
        }
    }
}

// Call the API :
Simulate.TextInput("AbCçDeFgĞhİiJkLmNoÖpQrSşTuÜvXyZ - äÄß_0123456789");

You can generate them by holding down the Alt key and typing in the 4 digit Unicode codepoint on the numeric keypad. å = Alt + 0229, ñ = Alt + 0241. Find other codes with the Charmap.exe applet.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top