【问题标题】:SendInput sequence to create unicode character failsSendInput 序列创建 unicode 字符失败
【发布时间】:2011-01-14 13:32:13
【问题描述】:

我正在使用需要将击键发送到第三方应用程序的屏幕键盘。它们在 Windows XP 上运行。需要支持一小部分在美式英语键盘上不可用的字符(例如“å”或 ñ)。在查看 SendInput 之后,似乎最安全的做法是将字符的十六进制 unicode 值作为击键序列发送。我编写了发送“Alt”和“Add”键按下事件的代码,然后是四个字符的 unicode 序列的键按下和向上事件,Alt 键 ORed,最后是“Add”和“Alt”键按下事件。在我的 C# 测试应用程序中。我正在使用 KeyPreview,果然,所有事件都在通过,但是我得到的只是哔声,没有字符。我通过手动输入击键捕获了相同的信息,KeyPreview信息相同,并且出现了字符。

可以这样使用 SendInput 吗?我没有使用钩子来检查数据,但我看到帖子表明 SendInput 事件附加了某种“注入”标志,也许这会导致序列失败?

此演示代码成功发送按键事件,但旨在生成 unicode 字符的按键事件序列失败。

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

【问题讨论】:

    标签: c# unicode localization keyboard keyboard-events


    【解决方案1】:
    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");
    

    【讨论】:

    • 这很好用,除了一个包含重复字符(如“aa”)的字符串可能会出现问题(只会显示一个“a”)。解决方案很简单,根据stackoverflow.com/questions/1643676/…,您需要在击键之间插入一小段时间缓冲区(向下、向上、等待、向下、向上、等待等)。这可以使用不同的方法来完成,如下所述:stackoverflow.com/questions/91108/…
    【解决方案2】:

    您可以通过按住 Alt 键并在数字键盘上输入 4 位 Unicode 代码点来生成它们。 å = Alt + 0229, ñ = Alt + 0241。使用 Charmap.exe 小程序查找其他代码。

    【讨论】:

    • 不幸的是 Alt+DDDD 取决于当前的语言环境,我试图避免切换语言环境。我确实尝试发送 Alt 加上十进制 unicode 值,它也不会导致显示字符。
    • Unicode 代码点对文化不敏感。请务必发送键盘键,而不是打字键。
    【解决方案3】:

    显然,代表 Unicode 字符的按键序列的处理是在无法通过 SendInput 访问的级别上完成的。我更改了代码以在 dwFlags 上设置 unicode 标志,并在 wScan 数据参数上设置 unicode 值。在使用几种欧洲和亚洲语言进行测试后,我设法说服自己,这会产生与多次击键方法相同的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-01
      • 2013-05-26
      • 2017-03-09
      • 1970-01-01
      • 2012-06-04
      • 2014-08-16
      • 1970-01-01
      • 2018-06-14
      相关资源
      最近更新 更多