模拟非ascii字符的输入

本文关键字:输入 字符 ascii 模拟 | 更新日期: 2023-09-27 18:06:43

我正在寻找一种方法来模拟使用Windows api输入非ascii字符(例如CJK字符)。keybd_eventSendInput 不能工作,因为输入的字符可能不是有效的虚拟密钥。

我想要的

我想通过使用Windows API模拟Unicode字符的输入,而不需要编写IME或TSF提供程序。

What I've try

我已经尝试将WM_IME_CHAR消息发送到当前聚焦的窗口:

// Gets the currently focused input control (if any)
internal static IntPtr GetActiveWindowControl()
{
    IntPtr activeWin = GetForegroundWindow();
    if (activeWin == IntPtr.Zero)
    {
        return IntPtr.Zero;
    }
    uint threadId = GetWindowThreadProcessId(activeWin, IntPtr.Zero);
    GUITHREADINFO guiThreadInfo = new GUITHREADINFO();
    guiThreadInfo.cbSize = Marshal.SizeOf(guiThreadInfo);
    GetGUIThreadInfo(threadId, ref guiThreadInfo);
    if (guiThreadInfo.hwndFocus == IntPtr.Zero)
    {
        return activeWin; // Example: console
    }
    else
    {
        return guiThreadInfo.hwndFocus;
    }
}
internal void SimulateInput()
{
    // Encoding.Default on my computer is CP950 (Big5)
    byte[] b = Encoding.Default.GetBytes("我"); // one single Chinese character
    IntPtr activeHwnd = GetActiveWindowControl();
    if (activeHwnd != IntPtr.Zero)
    {
        int ch = 0;
        for (int i = 0; i < b.Length; i++) // Assumed b has <=2 elements
        {
            ch = (ch << 8) | b[i];
        }
        uint WM_IME_CHAR = 0x0286;
        SendMessage(activeHwnd, WM_IME_CHAR, (IntPtr)ch, (IntPtr)0)
    }
}

遇到的问题到目前为止,这段代码似乎在大多数情况下都工作得很好,但是在notepad++上不起作用。我得到的不是想要的字符,而是垃圾输入。

使用spy++的进一步调查显示,当使用IME输入时,没有WM_IME_CHAR消息发送到notepad++编辑窗口。我得到的都是WM_IME_STARTCOMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_REQUEST, WM_IME_NOTIFYWM_IME_SETCONTEXT。似乎notepad++处理IME输入与其他程序不同。(我也相信还有更多类似于notepad++的程序,但我还没有找到一个)

同样,如果字符不在目标操作系统的默认字符编码范围内(例如,简体中文不支持Big5),此代码将不起作用。

我想阻止的事情

  • 将字符放入剪贴板并调用粘贴到目标窗口

我意识到Windows 7平板电脑的输入面板执行文本插入非常好(甚至工作在GTK+应用程序)。我试图模仿它发送的内容,但它似乎只重复最后一次输入的字符。它不工作,即使它看起来完全一样,在spy++中看到。

SendMessage(active, WM_IME_STARTCOMPOSITION, (IntPtr)0, (IntPtr)0);
//SendMessage(active, WM_IME_COMPOSITION, (IntPtr)ch, (IntPtr)0x0800);
SendMessage(active, WM_IME_COMPOSITION, (IntPtr)0xA7DA, (IntPtr)0x0800);
SendMessage(active, WM_IME_NOTIFY, (IntPtr)0x010D, (IntPtr)0);
SendMessage(active, WM_IME_ENDCOMPOSITION, (IntPtr)0, (IntPtr)0);
SendMessage(active, WM_IME_NOTIFY, (IntPtr)0x010E, (IntPtr)0);

我还在。net中找到了一个SendKeys类。可以在我的申请中使用吗?

模拟非ascii字符的输入

经过更多的搜索,发现SendInput实际上可以使用KEYEVENTF_UNICODE一起发送Unicode输入。

现在我的代码看起来像这样(InputSender是我自己的类):
internal static void InputString(string str)
{
    char[] chars = str.ToCharArray();
    InputSender.Input[] inputs = new InputSender.Input[chars.Length * 2];
    for (int i = 0; i < chars.Length; i++)
    {
        ushort ch = (ushort)chars[i];
        // Key down
        inputs[i * 2].type = InputSender.InputType.Keyboard;
        inputs[i * 2].ki.wScan = ch;
        inputs[i * 2].ki.dwFlags = InputSender.KeyboardEventFlags.Unicode;
        // Key up
        inputs[i * 2 + 1].type = InputSender.InputType.Keyboard;
        inputs[i * 2 + 1].ki.wScan = ch;
        inputs[i * 2 + 1].ki.dwFlags = InputSender.KeyboardEventFlags.Unicode | InputSender.KeyboardEventFlags.KeyUp;
    }
    InputSender.SendInput(inputs);
}

但是事实证明它不能很好地与GTK+程序一起工作。稍微搜索一下就会发现它应该在一段时间之前修复,但是使用旧版本GTK+的程序可能仍然会遇到问题。刚刚用Geany试用了最新的GTK+,最新的GTK+没问题,而Inkscape拒绝解释输入。

无论如何,如果我忽略GTK+的问题,使用SendInput就足够了(并且应该比WM_IME_CHAR更好)。我没有尝试过这个解决GTK+问题的答案,因为我不知道如何做到这一点,并且认为这是不可行的。