IME 编程相关
以下内容摘自http://msdn.microsoft.com/zh-cn/goglobal/bb688135.aspx
在 Win32 中处理输入语言的方法
Microsoft Developer Network (MSDN) 文档(位于 http://msdn.microsoft.com)和编程 API 用一种名为“输入法区域设置标识符”的变量类型代表输入语言,它在先前的文档中被称为“键盘布局处理”(HKL) 并且现在仍被用作类型标识符。HKL 是一种古老的名称,当时只能从键盘进行输入。输入法区域设置标识符名称是一个 32 位的值,它由语言标识符(低 WORD)和设备标识符(高 WORD)的十六进制值组成。(参见下方的图 7。)例如,美国英语的语言标识符为 0x0409,因此美国英语主布局被命名为 "00000409"。美国英语布局的变体(例如 Dvorak 布局)被命名为 "00010409"、"00020409" 等。设备标识符并不仅限于键盘和 IME;现在可通过一些较复杂的机制(例如语音和文字识别引擎)来输入数据。例如,在 Windows XP 中作为一种系统服务提供的 Microsoft Windows Text Services Framework (TSF),它可以实现与来源无关的高级文字输入。(有关 TSF 的更多信息,请参阅 Text Services Framework)。
图 7:代表输入语言的 HKL 变量。
处理输入语言最简单的方式是在需要用户输入的所有场合都使用操作系统提供的标准控件。例如,通过使用 Unicode 编辑控件或 Rich edit 控件,可以使应用程序能够处理多种语言的文字输入。操作系统会以一种对应用程序透明的方式自动处理输入语言。文字 API 使用的是一种标准的多行编辑控件,它可以避免处理输入语言时的麻烦。
对于一些需要完全控制输入语言处理方式的高级应用程序(例如文本编辑器),它们应该监视(并能够响应)用户的改动。当用户通过单击任务栏上的语言指示符或按下“左边 Alt+Shift”键来选择某种输入语言时,输入语言不会自动改变 – 两个操作都会生成一个活动的应用程序必须接受或拒绝的请求。为了响应热键组合或鼠标单击任务栏上的语言指示符后产生的事件,系统会向焦点窗口发送一个 WM_INPUTLANGCHANGEREQUEST 消息,如下图所示。如果应用程序接受该消息并将其传递给DefWindowProc,系统将启动切换输入语言的操作并发送一个 WM_INPUTLANGCHANGE 消息。如果输入法是 Text Services Framework (TSF) 的一部分,则此过程会略有不同,这时只发送 WM_INPUTLANGCHANGE。当系统成功完成改变后,会生成一个 WM_INPUTLANGCHANGE 消息。WM_INPUTLANGCHANGE 消息的 lParam 变量包含新输入语言的输入法区域设置标识符(即 HKL)。
图 8:WM_INPUTLANGCHANGEREQUEST 和 WM_INPUTLANGCHANGE 消息传播流程图。
不支持多语言的应用程序会拒绝 WM_INPUTLANGCHANGEREQUEST 消息。它可能会拒绝任何或全部 WM_INPUTLANGCHANGEREQUEST 消息,也可能会先执行一些测试。例如,此消息的 wParam 变量是一个 Boolean 值 bLangInSystemCharset,它将指出请求的输入语言是否能够在当前的系统区域设置中呈现出来。在处理 Unicode 应用程序时呈现输入语言并不麻烦,但是,非 Unicode 应用程序应该监视此值(实际上也是如此),否则它们会显示错误的字符。
与生成 WM_INPUTLANGCHANGEREQUEST 消息来响应用户请求的系统类似,应用程序也会通过调用 ActivateKeyboardLayout API 来启动改变输入语言的操作。如果用户编辑包含拉丁文和希腊文的文档,当用户将插入点从拉丁文字移动到希腊文字时,将会自动激活希腊文输入法。(参见下方的表 4。)同样,当此用户将插入点移回拉丁文字时,应用程序将激活默认的基于拉丁文的输入法。
图 9:当光标被定位到希腊文文本流中时,活动的键盘布局会切换到希腊文。
处理输入法的其他 Win32 API 如下方的表 4 所示:
表 4
在设计允许用户切换键盘布局的功能时,要清楚由于不同布局的键盘上字母排列各不相同,因此用来生成快捷键组合的按键可能也会有所变化。例如,法语键盘默认为 AZERTY 布局,而英语布局采用 QWERTY 映射。因此,建议您在快捷键组合中使用数字和功能键(F4、F5 等),而不要使用字母。
除了使您的应用程序能够处理各种输入语言外,您还需要使其能够支持 IME。(请注意,如果您使用标准 API 进行输入,您的应用程序将会自动处理 IME。)通过启用 IME 支持,您的用户将可以输入象形文字,例如,从各种东亚书写系统输入。以下各节将探究 IME 的用途,并提供支持 IME 的最佳实际示例和技术解决方案。
IME 系统的工作方式
Windows 2000 和 Windows XP 中的 IME 模块适合于向应用程序传递用户输入信息的大型机制,而且像其他输入法一样,最简单、最安全的输入内容处理方式是使用标准系统控件,例如编辑字段和 Rich edit 控件等。如果您不是要编写 IME 软件包或自定义自己的 IME 用户界面 (UI),您可以使用标准输入 API,它可以为您处理 IME 的所有复杂情况。
对某种输入语言来说,是使用 IME 还是使用键盘来输入对用户而言都完全透明。无论用户是切换 IME 还是西方键盘布局,过程都完全相同。这两种操作都是通过单击任务栏上的语言指示符或输入某个快捷键组合来完成。而且,应用程序不关心使用的输入法,因为切换 IME 与切换键盘布局会生成同样的消息:WM_INPUTLANGCHANGEREQUEST(如果 IME 不是 TSF 的一部分)和 WM_INPUTLANGCHANGE。应用程序可通过调用 ActivateKeyboardLayout 来激活特定的 IME。IMM 可以作为中介来管理 IME 与应用程序之间的通信。当用户使用 IME 键入信息时,每个按键都发出一条带有 GCS_COMPSTR 标记的 WM_IME_COMPOSITION 消息,指出合成字符串有更新。消息的 WPARAM 值将返回字符串的第一个字符,其余字符可以通过带有相同 GCS_COMPSTR 标记的 ImmGetCompositionString API 进行检索。然后当用户按下 Enter 键或单击某个字符在文档中放置它时,默认情况下,IME 会发出一条带有 GCS_RESULTSTR 标记的 WM_IME_COMPOSITION 消息。(您可以使用相同的 API 和 GCS_RESULTSTR 标记来检索提交的字符串。)如果后面的 WM_IME_COMPOSITION 消息被发送到 DefWindowProc,则对于所提交字符串中的每个字符,它都会发出一条包含实际字符的 WM_IME_CHAR 消息。对于非 Unicode 窗口,如果 WM_IME_CHAR 消息包括一个双字节字符并且应用程序将此消息传递给 DefWindowProc,则 IME 会将此消息转换为两条 WM_CHAR 消息,每条包含双字节字符中的一个字节。如果应用程序略过任何一条消息,则它将由应用程序的 DefWindowProc 函数进行处理,此函数会通知 IMM 该消息被忽略。IME 然后会通过多个 WM_CHAR 消息逐字节重新发送字符或字符串。对于 Unicode 窗口,WM_IME_CHAR 和 WM_CHAR 是相同的。
以下各节将讨论对于 Windows 中所运行应用程序的三个离散的 IME 支持级别:不支持、部分支持和完全自定义的支持。应用程序可以小规模自定义 IME 支持(例如通过重新定位窗口),也可以彻底改变 IME UI 的外观。
无 IME 支持:不识别 IME 的应用程序通常会忽略所有特定于 IME 的 Windows 消息。大多数针对单字节语言的应用程序不能识别 IME。
不识别 IME 的应用程序会通过预定义的全局类(对应被称为 "IME")来继承活动 IME 的默认 UI。这一全局类具有与基于 Windows 的任何其他常用控件相同的特性。对于每个线程而言,Windows 2000 和 Windows XP 都会根据 IME 全局类自动创建一个窗口,所有不识别 IME 的线程窗口都会共用此默认的 IME 窗口。不识别 IME 的应用程序将关于 IME 的消息传递给 DefWindowProc 函数时,DefWindowProc 会将其发送到默认 IME 窗口。
部分 IME 支持:识别 IME 的应用程序可以创建其自己的 IME 窗口。部分支持 IME 的应用程序可以使用此应用程序 IME 窗口来控制特定的 IME 行为。例如,通过调用函数 ImmIsUIMessage,应用程序可以将与 IME 的 UI 相关的消息传递给应用程序的 IME 窗口,应用程序可以在这里处理这些消息。下列代码(包括适当的错误处理和可能更多已处理的消息)会出现在应用程序的 IME 窗口的窗口过程中:
HIMC hIMC; LPVOID lpBufResult; COMPOSITIONFORM cf; DWORD dwBufLen; if (ImmIsUIMessage(hIMEWnd, uMsg, wParam, lParam) == TRUE) { switch(uMsg) { case WM_IME_COMPOSITION: if (lParam & GCS_RESULTSTR { hIMC = ImmGetContext(hWnd); dwBufLen = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, NULL) + sizeof(TCHAR); lpBufResult =?malloc(dwBufLen); if(ImmGetCompositionString(hIMC, GCS_RESULTSTR, lpBufResult, dwBufLen) > 0) { // ... // process the text in lpBufResult // ... } else // a negative error value was returned { // ... // handle an error // ... } free(lpBufResult); ImmReleaseContext(hWnd, hIMC); } break; } } return 0; }
同样的窗口过程可能会调用 SendMessage 来重新配置状态、复合或候选窗口,或打开、关闭状态窗口。
SendMessage(hIMEWnd, WM_IME_CONTROL,
IMC_SETCOMPOSITIONWINDOW, "cf);
允许应用程序变更窗口位置或属性的其他 API 函数包括ImmSetCandidateWindow、ImmSetCompositionFont、ImmSet-CompositionString、ImmSetCompositionWindow 以及 ImmSetStatusWindowPos。包含部分 IME 支持的应用程序可使用这些函数来设置 IME UI 窗口的样式和位置,但是 IME 动态链接库 (DLL) 仍负责绘制这些窗口 – IME UI 的常规外观将保持不变。
完全 IME 支持则截然不同,完全识别 IME 的应用程序将接管通过 IME DLL 绘制 IME 窗口(状态、复合以及候选窗口)的工作。此类应用程序可以完全自定义这些窗口的外观,包括确定其屏幕位置以及选择使用哪些字体和字体样式来显示窗口中的字符。对于文字处理程序以及主要功能是文字处理的类似程序,这会使它们从流畅的 IME 交互以及创建“自然的”用户交互界面中受益,因此显得特别方便和有效。IME DLL 仍将决定在 IME 复合窗口和候选窗口中显示的字符,并且它还会运用算法来猜测字符并在 IME 字典中查找它们。FULLIME 是一个自定义 IME UI 的示例,可以在 Microsoft Windows Platform SDK 中找到它,网址为http://msdn.microsoft.com。
完全识别 IME 的应用程序将通过下列方式捕获与 IME 相关的消息
- 它们调用 GetMessage 来检索中间 IME 消息。
- 它们在应用程序 WindowProc 中处理这些消息。
- 它们调用 TranslateMessage(IMM 的一部分)将消息传递到 IME DLL。像键盘驱动程序一样,IME 也需要同步静止键。请记住,如果使用标准输入调用(例如对 Rich Edit 控件的调用),部分 IME 支持可为您处理一切问题。
您已确信您的应用程序可以处理不同的输入语言和方法。确保您的应用程序可以支持多语言输入、输出以及显示的另一项任务是满足复杂脚本提出的固有要求。在随后的各节中,您将会看到与复杂脚本相关联的各种语言特点,并将了解到 Windows 对处理复杂脚本所提供的支持。