我正在寻找一种使用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输入时,没有发送到Notepad ++编辑窗口的WM_IME_CHAR
消息。我得到的只有WM_IME_STARTCOMPOSITION
,WM_IME_ENDCOMPOSITION
,WM_IME_REQUEST
,WM_IME_NOTIFY
和WM_IME_SETCONTEXT
。似乎Notepad ++处理与其他程序不同的IME输入。 (另外我相信存在更多类似于Notepad ++的程序,但我还没有找到一个。)
此外,如果字符超出目标操作系统的默认字符编码,则此代码将不起作用(例如,简体中文不适用于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);
我还在.NET中找到了一个SendKeys
类。可以在我的申请中使用吗?
答案 0 :(得分:2)
经过一番搜索,结果发现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);
}
但事实证明它是doesn't work quite well with GTK+ programs。稍微搜索显示它should be fixed a while ago但使用旧版GTK +的程序可能仍会遇到问题。刚试用最新的GTK +与Geany和最新的GTK +很好,而Inkscape拒绝解释输入。
无论如何,如果我忽略了GTK +的问题,使用SendInput
就足够了(并且应该优于WM_IME_CHAR
)。我没有尝试this answer which fixes GTK+'s problem,因为我不知道该怎么做,并认为这是不可行的。