Windows 输入法编辑器 (IME)
原著 :Microsoft
一、关于 Windows 混合语言 IME在 Windows 中 ,IME 是一个动态链接库 (DLL),与 Windows 3.1 远东版本 IME 不同的是 ,每一个运行的 IME 相当于混合语言键盘布局中的一种。与 Windows 3.1 IME 相比较 ,Windows 混合语言 IME 提供下列增强功能 :
●运行时相当于混合语言环境的一个部件
●为每一个应用程序任务提供多重输入上下文
●为每一个应用程序线程提供一个活动的 IME
●通过应用程序消息循环给应用程序提供信息 ( 消息顺序不能改变 )●为无 IME 支持应用程序和部分 IME 支持应用程序提供有力的支持
要得到全部的增强功能 ,应用程序需要支持 Windows IME 应用程序 I / F 。
本文档描述了 Windows IME 体系结构的应用程序 I / F 。
1 、 IME 的结构
Windows 95 IME 必须提供两个部件 :IME 转换接口和 IME 用户接口。 IME 转换接口由一组 IME 模块引出函数提供 ,这些函数被 IMM( 输入法管理器——译者注 ) 调用。 IME 用户接口由一组窗口提供 ,这些窗口接收消息并提供 IME 的用户界面。
2 、 IME 支持应用程序 (IME 感知应用程序——译者注 )
应用程序有下列类型 :
●无 IME 支持应用程序 : 这种应用程序不控制 IME,然而 ,如果应用程序接受 DBCS 字符 ,用户可以通过 IME 在应用程序中输入 DBCS 字符。
●部分 IME 支持应用程序 : 这种应用程序只控制不同的 IME 上下文 ,例如打开和关闭 IME 、写作窗口等等 ,但是不重新显示任何 IME 用户界面。
●完全 IME 支持应用程序 : 这种应用程序负责管理通过 IME 显示给应用程序的任何信息。
在 Windows 95 中 ,一个无 IME 支持应用程序有一个缺省的 IME 窗口和一个缺省的输入上下文。
部分 IME 支持应用程序使用预定义的“ IME ”类创建自己的 IME 窗口 ,可以管理或者不管理自己的输入上下文。
完全 IME 支持应用程序自己管理输入上下文 ,显示输入上下文给出的任何需要的信息 ,不使用 IME 窗口。
二、 IME 用户界面
IME 用户界面包括 IME 窗口、用户界面 (UI) 窗口以及 UI 窗口的部件。
1 、特征
IME 类是实现 IME 用户界面部分的预定义全局窗口类。“ IME ”类与预定义的公共控制窗口类有许多相同的特点 ,IME 窗口实例与静态控制一样通过 CreateWindowEx 函数创建 ,IME 类窗口自己不响应用户输入 ,取而代之的是接收不同类型的控制消息实现全部 IME 用户接口。应用程序可以使用 IME 类创建自己的 IME 窗口 ,还可以使用 ImmGetDefaultIMEWnd 函数获取缺省 IME 窗口。创建自己的 IME 窗口或者使用缺省 IME 窗口的应用程序被称为 IME 支持应用程序 ,具有以下优点 ( 与对应的 Windows 3.1 应用程序比较 ):
●包括候选字列表窗口 ( 候选窗口 ),每一个应用程序可以有自己的用户界面窗口实例 ,使得用户可以在任何输入过程的中途停止并切换到另一个应用程序。在 Windows 3.1 日文版本中 ,用户切换到另一个应用程序是必须放弃当前输入过程。
●因为 IME 用户界面窗口包括应用程序窗口句柄 ,IME 用户界面窗口可以为应用程序提供缺省行为。例如当应用程序移动时 IME 用户界面窗口自动移动 ,自动跟随窗口中的插入符号位置 ,为每一个应用程序标示模式等等。
即使系统仅仅只提供一个 IME 类 ,IME 窗口仍然有两种类型。一种类型是系统为无 IME 支持应用程序创建的 IME 窗口 ,DefWindowProc 函数为该窗口处理消息 ,DefWindowProc 函数的 IME 用户接口被线程的所有无 IME 支持窗口共享 ,在文档中 ,这种窗口称为缺省 IME 窗口。另一种类型是 IME 支持应用程序创建的 IME 窗口 ,在文档中 ,IME 支持应用程序创建的 IME 窗口称作应用程序 IME 窗口。
2 、缺省和应用程序 IME 窗口
当线程初始化时系统创建缺省 IME 窗口 ,这就是说 ,线程自动获取缺省 IME 窗口。缺省 IME 窗口为无 IME 支持应用程序提供 IME 用户界面 ,当 IME 或者 IMM 生成一个 IME 消息 (WM_IME_*) 时 ,无 IME 支持应用程序传递该消息到 DefWindowProc 函数 ,DefWindowProc 函数发送需要的消息到为应用程序提供缺省 IME 用户界面的缺省 IME 窗口。 IME 支持应用程序当不从 IME 获取消息时也可以使用缺省 IME 窗口 ,需要时可以使用自身的 IME 窗口。
3 、 IME 类
IME 类是 Windows 95 远东版本预定义的窗口类 ,就像 Edit 是预定义的窗口类一样。预定义的 IME 类实现全部的 IME 用户接口 ,处理所有来自 IME 和包含 IMM 函数的应用程序的消息 ,应用程序使用 IME 类创建自己的 IME 窗口。系统 IME 类不能被被任何 IME 替换。
窗口过程与 IME 类通过 WM_IME_SELECT 消息交互 ,该消息包括新选中的 IME 的键盘布局 ,IME 类使用键盘布局查找到每一个 IME 定义的类名。使用类名 ,IME 类为当前活动的 IME 创建 IME 用户界面窗口。
4 、 IME UI 类
每一个 IME 必须向系统注册自己的用户界面 (UI) 类 ,UI 类提供 IME 相关功能。当 IME 附加在进程上时 IME 注册自己的 UI 类 ,这就是说 ,当 DLLEntry 函数被调用 DLL_PROCESS_ATTACH 功能时 ,IME 必须在对 ImeInquire 函数的调用过程中指定 UI 类名。 UI 类应该使用 CS_IME 窗口风格注册以使得每一个应用程序都可以使用 UI 类。 UI 类名 ( 包括空终结符 ) 可以使用 16 位的 TCHAR 字符 ,这个限制可能延续到 Windows 的未来版本。
当注册一个 UI 类时 ,应该指定 8 个字节的窗口附加数据 ( 这就是说 ,设置 WNDCLASSEX 类的 cbWndExtra 成员的值为 2*sizeof(LONG)),系统使用该窗口附加数据。
IME 可以在为应用程序执行任务时注册任何类和创建任何窗口。
下面的实例显示了怎样注册 IME 窗口类 :
BOOL WINAPI DLLEntry (
HINSTANCE hInstDLL,
DWORD dwFunction,
LPVOID lpNot)
{
switch (dwFunction) {
case DLL_PROCESS_ATTACH:
hInst= hInstDLL;
wc.style = CS_MYCLASSFLAG | CS_IME;
wc.lpfnWndProc = MyUIServerWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 2 * sizeof(LONG);
wc.hInstance = hInst;
wc.hCursor = LoadCursor( NULL, IDC_ARROW);
wc.hIcon = NULL;
wc.lpszMenuName = (LPSTR) NULL;
wc.lpszClassName = (LPSTR) szUIClassName;
wc.hbrBackground = NULL;
if(!RegisterClass((LPWNDCLASS)&wc))
return FALSE;
wc.style = CS_MYCLASSFLAG | CS_IME;
wc.lpfnWndProc = MyCompStringWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = cbMyWndExtra;
wc.hInstance = hInst;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = NULL;
wc.lpszMenuName = (LPSTR) NULL;
wc.lpszClassName = (LPSTR) szUICompStringClassName;
wc.hbrBackground = NULL;
if(!RegisterClass((LPWNDCLASS)&wc))
return FALSE;
break;
case DLL_PROCESS_DETACH:
UnregisterClass(szUIClassName,hInst);
UnregisterClass(szUICompStringClassName,hInst);
break;
}
return TRUE;
}
5 、 UI 窗口
IME 类对应的 IME 窗口被应用程序或者系统创建 ,当 IME 窗口被创建时 ,IME 自身提供的 UI 窗口被创建并被 IME 窗口所拥有。每一个 UI 窗口有一个当前的输入上下文 ,当 UI 窗口接收到 IME 消息 (WM_IME_*) 时 ,可以通过调用 GetWindowLong 函数和指定 IMMGWL_IMC 索引值查找到输入上下文 ,UI 窗口可以根据输入上下文处理消息 ,UI 窗口可以在除响应 WM_CREATE 消息以外的任何时间查找到输入上下文。
IME 不允许改变 UI 窗口的窗口附加数据 ,如果 UI 窗口的某个实例需要窗口附加数据 ,可以使用 IMMGWL_PRIVATE 参数值调用 SetWindowLong 和 GetWindowLong 函数 ,IMMGWL_PRIVATE 参数值提供为 UI 窗口的某个实例存取附加数据中 LONG 类型值的能力 ,如果需要大于 LONG 类型值的附加数据 ,可以保存一个内存块的句柄到 IMMGWL_PRIVATE 域。
UI 窗口过程可以使用 DefWindowProc 函数 ,但是 UI 窗口不允许传递 IME 消息给 DefWindowProc 函数 ,即使某个 IME 消息没有被处理 ,UI 窗口也不允许传递该消息给 DefWindowProc 函数。
LRESULT UIWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HIMC hIMC;
HGLOBAL hMyExtra;
switch(msg){
case WM_CREATE:
// Allocate the memory bloack for the window instance.
hMyExtra = GlobalAlloc(GHND,size_of_MyExtra);
if (!hMyExtra)
MyError();
// Set the memory handle into IMMGWL_PRIVATE
SetWindowLong(hWnd, IMMGWL_PRIVATE, (LONG)hMyExtra);
break;
case WM_IME_xxxx:
// Get IMC;
hIMC = GetWindowLong(hWnd,IMMGWL_IMC);
// Get the memory handle for the window instance.
hMyExtra = GetWindowLong(hWnd, IMMGWL_PRIVATE);
lpMyExtra = GlobalLock(hMyExtra);
GlobalUnlock(hMyExtra);
break;
case WM_DESTROY:
// Get the memory handle for the window instance.
hMyExtra = GetWindowLong(hWnd, IMMGWL_PRIVATE);
// Free the memory block for the window instance.
GlobalFree(hMyExtra);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
UI 窗口必须在当前选定的输入上下文中执行动作 ,当一个窗口被激活时 ,UI 窗口接收到提供当前输入上下文的消息 ,此后 ,UI 窗口运行在当前选中的输入上下文上。输入上下文必须包括 UI 窗口显示写作窗口、状态窗口等需要的所有信息。
UI 窗口要求输入上下文 ,但是窗口不必自己更新输入上下文。当 UI 窗口需要更新输入上下文时 ,应该调用 IMM 函数 ,因为输入上下文由 IMM 函数管理 ,当输入上下文更新时 ,IMM 和 IME 接收到通知消息。
例如 ,有时 UI 窗口当鼠标单击时需要改变输入上下文的转换模式 ,为了设置转换模式 ,UI 窗口调用 ImmSetConversionMode 函数 ,该函数为 NotifyIME 生成一个通知消息并发送 WM_IME_NOTIFY 消息到 UI 窗口 ,如果 UI 窗口改变转换模式的显示 ,UI 窗口会等待处理 WM_IME_NOTIFY 消息。
6 、 UI 窗口的部件
UI 窗口可以根据输入上下文注册和显示写作窗口和状态窗口 ,UI 窗口的部件类的窗口风格必须包括 CS_IME 。 UI 窗口的一个窗口实例从当前输入上下文接收例如写作字符串、字体、位置等信息 ,当应用程序的一个窗口获得焦点时 ,系统获取该窗口自己的输入上下文并将当前输入上下文传递给 UI 窗口 ,系统发送 WM_IME_SETCONTEXT 消息和输入上下文的句柄给应用程序 ,应用程序传递该消息给 UI 窗口。如果当前输入上下文被更新 ,UI 窗口应该重新绘制写作窗口 ,无论何时输入上下文改变 ,UI 窗口都应该显示正确的写作窗口 ,可以保证 IME 的状态。
UI 窗口可以创建子窗口或者弹出式窗口显示状态、写作字符串或者候选字列表 ,这些窗口必须是 UI 窗口的附属窗口 ,而且必须创建为不可接收输入 (Disable) 窗口 ,任何 IME 创建的窗口都不应该获取焦点。
三、输入上下文
1 、缺省输入上下文
缺省情况下系统给每个线程一个输入上下文 ,该输入上下文被线程的所有无 IME 支持窗口共享。
2 、输入上下文与窗口的交互
应用程序的一个窗口可以使用窗口句柄与输入上下文交互以维护任何 IME 状态 ,包括中间写作字符串。一旦应用程序使得输入上下文与窗口句柄交互 ,无论何时窗口被激活 ,系统自动选中输入上下文。使用这个特点 ,应用程序可以轻松地完成 Windows 3.1 下必须的复杂切换处理。
3 、使用输入上下文
当应用程序或者系统创建新的输入上下文时 ,系统准备新的输入上下文 ,新的输入上下文已经包括 IMCC,这个 IMC 的部件由 hCompStr 、 hCandInfo 、 hGuideLine 、 hPrivate 和 hMsgBuf 组成。 IME 基本上不需要创建输入上下文和输入上下文的部件 ,不过 IME 可以改变它们的大小 ,可以通过锁定它们查找到部件的指针。
⑴存取 HIMC为了存取输入上下文 ,IME 必须调用 ImmLockIMC 函数以查找到输入上下文的指针 ,ImmLockIMC 函数给 IMC 增加 imm 锁定计数 ,ImmUnlockIMC 函数减少之。
⑵存取 HIMCC
为了存取输入上下文中的一个部件 ,IME 必须调用 ImmLockIMCC 函数获取 IMCC 的指针 ,ImmLockIMCC 函数给 IMCC 增加 imm 锁定计数 ,ImmUnlockIMCC 函数减少之 ,ImmReSizeIMCC 函数可以修改 IMCC 的大小以指定新的大小。
某些情况下 ,IME 可能需要自己创建输入上下文的一个部件 ,这种情况下 ,IME 可以调用 ImmCreateIMCC 函数获取 IMCC 的句柄 ,这个 IMCC 可以是 INPUTCONTEXT 结构的成员 (hCompStr 、 hCandInfo 、 hGuideLine 、 hPrivate 或者 hMsgBuf) 。
ImmDestroyIMCC 清除输入上下文的一个部件。
⑶怎样使用输入上下文
下面的实例显示了怎样使用输入上下文
LPINPUTCONTEXT lpIMC;
LPCOMOSITIONSTRING lpCompStr;
HIMCC hMyCompStr;
if (hIMC) { // It is not NULL context.
lpIMC = ImmLockIMC(hIMC);
if (!lpIMC) {
MyError( "Can not lock hIMC");
return FALSE;
}
// Use lpIMC->hCompStr.
lpCompStr = (LPCOMPOSITIONSTRING) ImmLockIMCC(lpIMC->hCompStr);
// Access lpCompStr.
ImmUnlockIMCC(lpIMC->hCompStr);
// ReSize lpIMC->hCompStr.
if (!(hMyCompStr = ImmReSizeIMCC(lpIMC->hCompStr,dwNewSize)) {
MyError("Can not resize hCompStr");
ImmUnlockIMC(hIMC);
return FALSE;
}
lpIMC->hCompStr = hMyCompStr;
ImmUnlockIMC(hIMC);
}
四、生成消息
IME 需要生成 IME 消息。当 IME 开始转换时 ,IME 必须生成 WM_IME_STARTCOMPOSITION 消息 ,如果 IME 改变了写作字符串 ,IME 必须生成 WM_IME_COMPOSITION 消息 ,IME 引发的事件导致生成消息给与输入上下文进行交互的窗口。 IME 基本上使用 ImeToAsciiEx 函数参数提供的 lpdwTransKey 缓冲区生成消息 ,当 ImeToAsciiEx 函数被调用时 IME 存储消息到 lpdwTransKey 缓冲区中 ,不过即使 ImeToAsciiEx 函数没有被调用 ,IME 也可以生成消息给使用输入上下文的消息缓冲区与输入上下文交互的窗口。输入上下文有一个内存块的句柄作为消息缓冲区 ,IME 存储消息到被消息缓冲区句柄提供的内存块中 ,以后 IME 调用 ImmGenerateMessage 函数 ,ImmGenerateMessage 函数发送保存在消息缓冲区中的消息到适当的窗口。
1 、在 ImeToAsciiEx 函数中使用消息缓冲区
下面的实例显示了怎样通过传递缓冲区到 ImeToAsciiEx 函数生成消息 :
UINT ImeToAsciiEx(uVirKey, uScanCode, lpbKeyState, lpdwTransBuf,
fuState , hIMC )
{
DWORD dwMyNumMsg = 0;
// Set the messages that the IME needs to generate.
*lpdwTransBuf++ = (DWORD) msg;
*lpdwTransBuf++ = (DWORD) wParam;
*lpdwTransBuf++ = (DWORD) lParam;
// Count the number of the messages that the IME needs to generate.
dwMyNumMsg++;
return dwMyNumMsg;
}
系统提供 lpdwTransBuf 参数指定的缓冲区 ,IMEToAsciiEx 函数可以一次存储所有的消息到该缓冲区中 ,缓冲区的第一个双字给出存储在缓冲区中的消息个数。如果 ImeToAsciiEx 函数需要生成比这个给定的个数更多的消息 ,函数可以存储所有的消息到输入上下文的 hMsgBuf 域中 ,然后函数 ImeToAsciiEx 返回消息个数。当 ImeToAsciiEx 函数的返回值大于 lpdwTransBuf 中指定的值时 ,系统不从 lpdwTransBuf 中取出消息 ,系统查找作为 ImeToAsciiEx 函数参数传递的输入上下文中的 hMsgBuf 域。
2 、使用消息缓冲区
下面的实例显示了怎样使用消息缓冲区 :
MyGenerateMesage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAMlParam)
{
LPINPUTCONTEXT lpIMC;
HGLOBAL hTemp;
LPDWORD lpdwMsgBuf;
DWORD dwMyNumMsg = 1;
// Lock the input context.
lpIMC = ImmLockIMC(hIMC);
if (!lpIMC)
// Error!
// re-allocate the memory bloack for the message buffer.
hTemp = ImmReSizeIMCC(lpIMC->hMsgBuf,
(lpIMC->dwNumMsgBuf + dwMyNumMsg) * sizeof(DWORD) * 3);
if (!hTemp)
// Error!
lpIMC->hMsgBuf = hTemp;
// Lock the memory for the message buffer.
lpdwMsgBuf = ImmLockIMCC(lpIMC->hMsgBuf);
if (!lpdwMsgBuf)
// Error!
lpdwNumMsgBuf += 3 * lpIMC->dwNumMsgBuf.
// Set the number of the messages.
lpIMC->dwNumMsgBuf += dwMyNumMsg;
// Set the messages that the IME needs to generate.
*lpdwMsgBuf++ = (DWORD) msg;
*lpdwMsgBuf++ = (DWORD) wParam;
*lpdwMsgBuf++ = (DWORD) lParam;
// Unlock the memory for the message buffer and the input context.
ImmUnlockIMCC(lpIMC->hMsgBuf);
ImmLockIMC(hIMC);
// Call ImmGenerateMessage function.
ImmGenerateMessage(hIMC);
}
3 、 WM_IME_COMPOSITION 消息
当 IME 生成 WM_IME_COMPOSITION 消息时 ,IME 指定 lParam 参数为 GCS 位。 GCS 位的意义是 COMPOSITIONSTRING 结构中的有效成员 ,即使 IME 没有更新 ,成员目前仍然有效 ,IME 也会设置 GCS 位。
为 IME 定义服务
当 IME 生成 WM_IME_COMPOSITION 消息时 ,IME 可能会立刻改变字符串、属性以及子句信息。 IME 使用下列定义 :
GCS_COMP
GCS_COMPREAD
GCS_RESULT
GCS_RESULTREAD
五、关于 ImeSetCompositionString 函数
1 、 ImeSetCompositionString 函数能力
如果 IME 没有 ImeSetCompositionString 函数能力 ,IME 将不能在 IMEINFO 结构中指定任何 SCS 能力。如果 IME 可以处理 ImeSetCompositionString 函数 ,IME 设置 SCS_COMPSTR 位。如果 IME 可以通过写作字符串生成解释 ( 本文中的“解释”是单词“ reading ”的直译 ,真正意义可能是“原始输入的” ,例如输入的汉语拼音字母字符串 ,下同 ) 字符串 ,IME 可以设置 SCS_CAP_MAKEREAD 位。
如果 IME 有 SCS_CAP_COMPSTR 能力 ,ImeSetCompositionString 函数将被调用 ,IME 从应用程序获取新的写作字符串并生成 WM_IME_COMPOSITION 消息。
如果 IME 有 SCS_CAP_MAKEREAD 能力 ,IME 可以通过写作字符串建立解释字符串。
2 、关于 SCS_SETSTR
如果 ImeSetCompositionString 函数的 dwIndex 参数值为 SCS_SETSTR,IME 可以清除 hIMC 中的 COMPOSITIONSTR 结构中所有的域。
如果 IME 需要 ,IME 可以更新候选信息并生成候选消息 IMN_OPENCANDIDATE 、 IMN_CHANGECANDIDATE 或者 IMN_CLOSECANDIDATE 。
如果 ImeSetCompositionString 函数的 lpRead 参数有效 ,IME 应该通过 lpRead 参数中的解释字符串建立写作字符串 ,另外 IME 为新的写作字符串和 lpRead 参数中的解释字符串建立属性和子句信息 ,IME 生成 lParam 参数为 (GCS_COMP|GCS_COMPREAD) 的 WM_IME_COMPOSITION 消息。有时 IME 需要自动确定建立上述信息 ,这种情况下 ,IME 可以生成 lParam 参数以 (GCS_RESULT|GCS_RESULTREAD) 代替 GCS_COMPxxx 的消息。
如果 ImeSetCompositionString 函数的 lpComp 参数有效 ,IME 应该通过 lpComp 参数中的写作字符串建立写作属性和子句信息 ,IME 生成 lParam 参数为 GCS_COMP 的 WM_IME_COMPOSITON 消息。如果 IME 有 SCS_CAP_MAKEREAD 能力 ,IME 应该同时建立解释字符串 ,IME 生成 lParam 参数为 (GCS_COMP|GCS_COMPREAD) 的 WM_IME_COMPOSITION 消息。有时 IME 需要自动确定建立上述信息 ,这种情况下 ,IME 可以生成 lParam 参数以 (GCS_RESULT|GCS_RESULTREAD) 代替 GCS_COMPxxx 的消息。
如果 lpRead 参数和 lpComp 参数同时有效 ,IME 应该建立写作字符串和解释字符串 ,这时 IME 不需要完全按照 lpRead 参数和 lpComp 参数。如果 IME 不能建立应用程序指定的 lpRead 参数和 lpComp 参数之间的关系 ,IME 应该修正写作字符串 ,IME 为新的写作字符串和 lpRead 参数指定的解释字符串建立属性和子句信息 ,IME 生成 lParam 参数为 (GCS_COMP|GCS_COMPREAD) 的 WM_IME_COMPOSITION 消息。有时 IME 需要自动完成建立上述信息 ,这种情况下 ,IME 可以生成 lParam 参数以 (GCS_RESULT|GCS_RESULTREAD) 代替 GCS_COMPxxx 的消息。
3 、关于 SCS_CHANGEATTR
SCS_CHANGEATTR 只影响属性信息 ,IME 不应该更新写作字符串、写作字符串的子句信息、写作字符串的解释以及写作字符串的解释子句信息。
首先 IME 检查新的属性并判断新的属性是否可用 ,然后 IME 设置属性到 hIMC 中的 COMPOSITIONSTRING 结构中 ,最后 IME 生成 WM_IME_COMPOSITION 消息。
如果需要 ,IME 可以更新候选信息并生成候选消息 IMN_OPENCANDIDATE 、 IMN_CHANGECANDIDATA 、 IMN_CLOSECANDIDATE 。
IME 不能确定写作字符串。
如果 ImeSetCompositionString 函数的 lpRead 参数有效 ,IME 使用 lpRead 参数中的新属性。 IME 也应该为当前写作字符串建立写作字符串的新属性 ,这时子句信息不被修改。
写作字符串、属性、子句信息、解释字符串、解释属性和解释子句信息必须有效。 IME 生成 lParam 参数为 (GCS_COMP|GCS_COMPREAD) 的 WM_IME_COMPOSITION 消息 ,如果 IME 不能接受 lpComp 参数中的新属性 ,IME 不需要生成任何消息并返回 FALSE 。
如果 ImeSetCompositionString 函数的 lpComp 参数有效 ,IME 使用 lpComp 参数中的新属性 ,这时子句信息不被修改。
如果 IME 有 SCS_CAP_MAKEREAD 能力 ,并且解释字符串有效 ,IME 应该为当前写作字符串的解释建立写作字符串的解释的新属性。
如果 lpRead 参数和 lpComp 参数同时有效 ,并且如果 IME 能够接受新的信息 ,IME 设置新的信息到 hIMC 中的 COMPOSITION 结构中并生成 lParam 参数为 (GCS_COMP|GCS_COMPREAD) 的 WM_IME_COMPOSITION 消息。
4 、关于 SCS_CHANGECLAUSE
SCS_CHANGECLAUSE 影响写作字符串和写作字符串的解释的字符串和属性。
如果需要 ,IME 可以更新候选信息并生成候选消息 IMN_OPENCANDIDATE 、 IMN_CHANGECANDIDATA 、 IMN_CLOSECANDIDATE 。
IME 不能确定写作字符串。
如果 ImeSetCompositionString 函数的 lpRead 参数有效 ,IME 使用 lpRead 参数中的解释子句信息。 IME 必须修正写作字符串的解释的属性 ,IME 可以更新写作字符串、属性和写作字符串的子句信息 ,IME 生成 lParam 参数为 (GCS_COMP|GCS_COMPREAD) 的 WM_IME_COMPOSITION 消息。
如果 ImeSetCompositionString 函数的 lpComp 参数有效 ,IME 使用新的子句信息。 IME 必须修正写作字符串和写作字符串的属性 ,IME 可以更新解释属性和解释的子句信息 ,IME 生成 lParam 参数为 (GCS_COMP|GCS_COMPREAD) 的 WM_IME_COMPOSITION 消息。
如果 lpRead 参数和 lpComp 参数同时有效 ,并且如果 IME 能够接受新的信息 ,IME 设置新的信息到 hIMC 中的 COMPOSITION 结构中并生成 lParam 参数为 (GCS_COMP|GCS_COMPREAD) 的 WM_IME_COMPOSITION 消息。
六、软键盘
1 、关于软键盘
一些 IME 有特殊的解释字符 ,例如一个 IME 可能使用注音符号作为解释字符 ( 这里指台湾中文版 Windows 95,即 CWin95 中的注音符号 ,PWin95 中可能指汉语拼音字母或者音调符号——译者注 ),另一个 IME 使用了一些字根符号 ( 原文单词是“ radials ” ,但实际可能是“ radicals ”——译者注 ) 作为解释字符 ,IME 可以提供一个软键盘显示这些特殊解释字符使得用户不必逐键记忆解释字符。
IME 需要根据不同的变换状态改变键表示的解释字符 ,使用软键盘可以通知用户键的改变。在选择候选字时 ,IME 可以只显示那些选择键给用户。
2 、使用软键盘
IME 可能需要为软键盘创建一个更好的用户界面 ,或者可能需要系统预定义的软键盘 ,如果 IME 需要使用系统预定义的软键盘 ,IME 需要在调用 ImeInquire 函数时将 IMEINFO 结构的 fdwUICaps 成员指定为 UI_CAP_SOFTKBD 。
IME 可以调用 ImmCreateSoftKeyboard 函数为软键盘创建窗口 ,还可以调用 ImmShowSoftKeyboard 函数显示或者隐藏软键盘。软键盘窗口是 UI 窗口的一个组件 ,所以软键盘窗口应该附属于 UI 窗口。
IME 可能需要决定是否在无论何时焦点移走的情况下删除窗口 ,软键盘可能占有一些系统资源 ( 可能需要释放——译者注 )
软键盘有不同的类型 ,一种类型可能是为特定的国家或者特定的目的设计的。为每一种类型的软键盘改变解释字符的方式可能不同 ,有两种改变解释字符的方式 : 使用 IMC_SETSOFKBDSUBTYPE 或者 IMC_SETSOFKBDDATA 。不同类型的软键盘有不同的窗口过程并存在不同的用户界面给用户。
七、 IME 接口
在 Windows 95 中 ,IME 与设备驱动程序一样是动态链接库 (DLL),输入法管理器 (IMM) 应该处理所有安装的 IME 。因为 IME 在运行时是可以改变的 ,不需要重新启动系统 ,IMM 有一个结构用于维护每一个 IME 的所有入口点。
IME 函数列表是所有远东版本 Windows 95 公共 IME 功能函数的描述 ,这些函数不应该在应用程序中直接调用。
UI 窗口中的 IMM 函数
下面是可以在 UI 窗口中调用的 IMM 函数 :
ImmGetCompositionWindow
ImmSetCompositionWindow
ImmGetCandidateWindow
ImmSetCandidateWindow
ImmGetCompositionString
ImmSetCompositionString
ImmGetCompositionFont
ImmSetCompositionFont
ImmGetNumCandidateList
ImmGetCandidateList
ImmGetGuideLine
ImmGetConversionStatus
ImmGetConversionList
ImmGetOpenStatus
ImmSetConversionStatus
ImmSetOpenStatus
ImmNotifyIME
ImmCreateSoftKeyboard
ImmDestroySoftKeyboard
ImmShowSoftKeyboard