2

我正在寻找一种使用 Windows API 模拟非 ASCII 字符(例如 CJK 字符)输入的方法。keybd_event或者因为输入的字符可能不是有效的虚拟键而SendInput 无法工作。

我想要的是

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

我试过的

我试图将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和. Notepad++ 处理 IME 输入的方式似乎与其他程序不同。(另外我相信还有更多类似于 Notepad++ 的程序,但我还没有找到。)WM_IME_NOTIFYWM_IME_SETCONTEXT

此外,如果字符超出目标操作系统的默认字符编码(例如,简体中文不适用于 Big5),则此代码将不起作用。

我想阻止的事情

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

附加信息

我意识到 Windows 7 Tablet PC 输入面板非常好地执行文本插入(甚至适用于 GTK+ 应用程序)。我试图模仿它发送的内容,但它似乎只是重复了最后一个 IME 输入的字符。即使它看起来与在 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);

我还在SendKeys.NET 中找到了一个类。它可以在我的应用程序中使用吗?

4

1 回答 1

2

经过一番搜索,事实证明它SendInput实际上可以用于发送 Unicode 输入KEYEVENTF_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+ 问题的这个答案,因为我不知道该怎么做,并且认为它不可行。

于 2013-04-01T15:33:43.580 回答