输入法开发
一、关于Windows 95混合语言IME
在Windows 95中,IME是一个动态链接库(DLL),与Windows 3.1远东版本IME不同的是,每一个运行的IME相当于混合语言键盘布局中的一种。与Windows 3.1 IME相比较,Windows 95混合语言IME提供下列增强功能:
?运行时相当于混合语言环境的一个部件
?为每一个应用程序任务提供多重输入上下文
?为每一个应用程序线程提供一个活动的IME
?通过应用程序消息循环给应用程序提供信息(消息顺序不能改变)
?为无IME支持应用程序和部分IME支持应用程序提供有力的支持
要得到全部的增强功能,应用程序需要支持Windows 95 IME应用程序I,F。
本文档描述了Windows 95 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支持应用程序,具有以下优点(与对应的Windows3.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类是Windows95远东版本预定义的窗口类,就像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_PRIVATESetWindowLong(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可能使用注音符号作为解释字符(这里指台湾中文版Windows95,即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接口
在Windows95中,IME与设备驱动程序一样是动态链接库(DLL),输入法管理器 (IMM)应该处理所有安装的IME。因为IME在运行时是可以改变的,不需要重新启动系统,IMM有一个结构用于维护每一个IME的所有入口点。IME 函数列表是所有远东版本Windows95公共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
这是我翻译的windows2000 DDK中IME输入法编程的英文资料(前10页),没有翻译完,不再翻译了。这个资料的很多
和网上发布的MSDN中windows95下的IME输入法编程资料相同,原版英文资料后面的内容更多的是程序上的东西,不是原理上的,只有程序员才需要,一般也能看懂,所以就不译了。
---------------------------------------------------
Win32多语言IME概述--------为IME开发者提供。
版本1.41 1999.04.01
这个文档介绍了如何为win95/98/nt/2000开发一个IME程序的基本知识。它也是一个IME开发者可以使用的win32 下的多语言IME- API参考资料,
下面是讲述的主题:
Overview:概述
IME User Interface --IME用户接口
IME Input Context --IME输入上下文
Generating Messages—产生消息
ImeSetCompositionString—ime程序设置编码字符串
Soft Keyboard—软键盘
Reconversion—撤销和复原能力支持
IME Menu Functions—IME菜单函数
IME Help File—IME帮助文件
Windows NT/2000 Issues—winNT/2000专门议题
IME File Format and Data Structures --IME文件格式和数据结构
一、Overview:概述
从win95/winNT 4.0开始,IME程序作为一个动态链接库(DLL)被提供使用,不同于远东版本的win3.1中的IME程序。每一个IME作为一个多语言的键盘方案使用。为了区别于win3.1,新的win32多语言输入法管理器(IMM)和输入法(IME)在操作系统的构造体系中加入了下面的高级特性:
? 作为一个支持多语言的环境部件运行。
? 为每一个应用程序的任务提供多重输入上下文。(一个任务可以有多个线程,IME是按线程分配的,有多少个线程就可以有多少个IME,输入上下文是按任务提供的,为了支持任务中的多个线程和IME,输入上下文必须可以提供多重的服务)
? 为每一个活动的应用程序线程保持一个活动的IME
? 通过消息循环给应用程序发送消息(消息运行秩序没有被破坏) ? 为IME感知程序和不感知程序都提供强大的IME支持。
为了充分完整利用这些高级特性,每一个应用程序都需要支持新的win32 IMM/IME应用程序接口。
为了最大程度上兼容现有的win95/NT4.0中的IME程序,win98/win2000中的IME在设计上没有改变过多过深。无论如何,为了提供更好的系统综合性能和支持更多的智能IME程序,新的特性已经被增加进来。
注意:
IME开发者在DDK中必须使用immdev.h,它是imm.h或者其它开发工具的超集 Win98 / 2000中的新 IMM/IME特性
在win98/2000中的IMM/IME的架构体系中包含了win95/winNT4.0的设计。无论如何,为了支持智能IME开发和IME的智能感知(integration),IMM/IME中的一些东西已经被改变。
新的IME函数允许应用程序和IMM/IME直接通信,这些函数包括: ImmAssociateContextEx
ImmDisableIME
ImmGetImeMenuItems
新的函数允许IME和(IMM、应用程序)直接通信,这些函数包括: ImmRequestMessage
ImeGetImeMenuItems
撤销和复原能力支持
这是一个新的IME特性,允许你撤销和复原已经被插入到应用程序文件中的字符串,这个函数将帮助智能IME程序获得更多的关于转换结果的信息,并且改善转换的精度和改进程序的执行。这个特性需要应用程序和IME二者协作完成。
增加了IME菜单项到系统提供的输入上下文菜单图标中,这个新特性提供了一个IME把它自己的菜单放置到系统提供的输入上下文菜单条和应用程序中的功能。
IME的新旗位标志
下面的新位支持新的模式:
IME_CMODE_FIXED
IME_SMODE_CONVERSATION
IME_PROP_COMPLETE_ON_UNSELECT
改进应用程序对IME的编辑能力的控制。
通过两个新消息:EM_SETIMESTATUS and EM_GETIMESTATUS,应用程序可以管理IME的状态,达到控制IME编辑的目的。
改变IME的图标和揭示消息
通过三个新消息: INDICM_SETIMEICON, INDICM_SETIMETOOLTIPS,
INDICM_REMOVEDEFAULTMENUITEMS,IME能在系统的任务栏中改变系统的图标和提示消息。
两个新的IMR消息
IMR_QUERYCHARPOSITION and IMR_DOCUMENTFEED 帮助IME和应用程序得到位置和文档消息。
? 对64位程序和消息的适应
两个新消息(TRANSMSG and TRANSMSGLIST) 被增加进IMM32.它们被输入法上下文INPUTCONTEXT 和ImeToAsciiEx函数使用,接收IME转换消息 .
? IME_PROP_ACCEPT_WIDE_VKEY 这个新属性被增加进win2000,所以IME可以操作发送到输入法API函数中的带有Unicode的字符。ImeProcessKey 和ImeToAsciiEx 两个API函数也被修改为可以操作带有Unicode的字符。这个夹带的Unicode字符能被应用程序和手写版程序通过在输入队列中放入Unicode字符串使用。
Win32 IME 构成
一个新的Win32 IME必须提供两个组件。一个是IME转换接口,一个是IME用户接口。IME转换接口作为一组IME模块中的出口函数被提供,这些函数被IMM调用。IME用户接口以windows窗口的形式被提供,这些窗口接受消息并且提供IME的用户接口。 IME 感知应用程序
现在新Win32 IME架构的一个主要的高级特性是它在应用程序和IME之间提供了更好的逻辑联系。后面有一个例子说明一个应用程序是如何和IME关联在一起的。
不感知IME的应用程序
这种应用程序从来不打算控制IME,无论如何,就象它们访问DBCS字符一样,用户可以使用IME输入任何DBCS字符给应用程序。
IME 半感知程序
这种应用程序的特点是会控制IME的多种输入上下文和编码窗口,比如打开或者关闭它们,但是它不会显示IME的任何用户接口。
IME 完全感知程序
这种应用程序的特点是要完全响应用户通过IME给出的任何显示信息。
在win95/NT 4.0或者更高的版本中,操作系统将为IME不感知应用程序也提供IME支持,操作系统将为它提供一个默认IME窗口和默认输入上下文。
一个半感知的应用程序将建立自己的IME窗口,也会调用一个应用程序IME窗口,使用预先定义的系统IME类,它可以操作或者不操作属于它自己的由操作系统提供给它的输入上下文
一个完全的IME感知程序将自己操作输入上下文并且把不通过使用IME窗口的输入上下文给出的任何信息显示出来。
IME 用户接口
IME用户接口包括IME窗口、UI窗口、UI窗口的组件。
特性
每一个IME类是一个预先定义的全局类,它们输出一些IME中的用户接口组件,每一个IME类的标准特征是同样的,被其它程序共同控制。它的windows实例能通过使用CreateWindowEx函数建立。作为静态控制,IME类窗口自己不能响应用户的输入,但是可以接收各种各样的控制信息去实现完整的IME用户接口行为。每一个应用程序可以建立它自己的IME窗口,通过使用IME类或者通过调用ImmGetDefaultIMEWnd函数取得默认的IME窗口。区别于win3.1,每一个要控制IME的应用程序和这些窗口操作(一个完全感知IME的应用程序),现在能实现下面的优良功能:
新的IME包含了候选列表窗口。每一个应用程序能有它自己的UI窗口实例,所以用户可以在使用IME的任何操作过程中停下来去选择另一个应用程序。在win3.1中的日文输入法中,用户必须首先退出之前的应用程序,然后去选择另一个应用程序。
自从IME用户接口窗口被包含进应用程序的窗口句柄以来,IME可以为应用程序提供一些默认功能。例如:这个功能可以为IME窗口提供自动响应、自动跟踪窗口建立的位置、为每一个应用程序提供
格式的指示消息。
尽管系统只提供了一种IME类,但是有两种类型的IME窗口。一个是通过系统提供的DefWindowProc函数为IME不感知程序建立的窗口。DefWindowProc函数建立的IME用户接口被所有的IME不感知程序的线程窗口共享,并且被在这个程序文件中的默认IME窗口调用。另一种窗口通过IME感知应用程序建立,并且被应用程序IME窗口调用。 默认的应用程序IME窗口
在线程初始化的时候,系统建立一个默认的IME窗口,它自动的被提供给每一个线程。然后这个窗口为每一个IME不感知程序操作任何IME用户接口。
当IME或者IMM产生WM_IME_xxx messages消息的时候,每一个IME不感知程序传递消息给DefWindowProc。然后,DefWindowProcB发送需要的消息给默认的IME窗口,为每一个IME不感知程序提供默认的IME用户接口功能。每一个IME感知程序对它打算不拦截处理的IME消息也可以使用这个窗口。每一个应用程序在需要的时候,都可以使用它自己的IME窗口。 IME 类
Win32系统在系统中提供了一个IME类。这个类是通过作为用户的预先编辑类定义。系统的IME类,操作全部的IME中的UI,并且操作传给IME和应用程序的全部控制消息,也包括操作IMM函数。通过使用这个类,一个应用程序能建立它自己的IME用户接口。这个系统IME类,它自己不会被任何IME类替代,只是作为一个预先定义的类被保存。 这个类有一个实际上操作WM_IME_SELECT消息的windows程序。这个消息获取最新被选择的IME在注册表中的hKL值。这个系统IME类重新获取存放在hKL中的每一个IME,找到其中被选择的这个IME类名。使用这个名字,系统IME类建立一个正确的UI窗口,并且激活这个IME程序。
从IME中产生的UI类
在这个设计中,每一个IME都希望向操作系统注册它自己的UI类。每一个IME提供这个UI类,用来响应实现IME需要的特殊功能。当IME附加在进程上的时候,IME可以注册这个供它自己使用的类。这发生在使用DLL_PROCESS_ATTACH参数调用DllEntry函数的时候。然后IME必须在lpszClassName参数中设置UI类名,这个参数是ImeInquire函数的第二个参数
这个UI类应该用CS_IME类型注册到样式字段中,以让每一个应用程序可以通过IME类使用
它。这个UI类名(包括空字符)可以由最多16个字符组成.在未来的版本中可能会增加字
符个数。
UI类的cbWndExtra参数长度必须是2 * sizeof(LONG)。WndExtra这个参数的用途是由操
作系统定义的。(例如还有IMMGWL_IMC 、IMMGWL_PRIVATE)
IME在应用程序中运行的时候,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;
}
UI Window
IME类的IME窗口通过应用程序或者操作系统建立(实际上IME类是操作系统内置自带的)。当IME窗口建立的时候,由IME自身提供的UI窗口被创建,并且被IME窗口拥有。 每一个UI窗口包含当前的输入上下文。当UI窗口收到一个WM_IME_xxx消息的时候,这个输入上下文能通过调用GetWindowLong(IMMGWL_IMC)函数得到。UI窗口可以查阅输入上下文并且操作消息。除了在处理WM_CREATE消息以外,在UI窗口程序过程的任何时候,任何消息中调用GetWindowLong(IMMGWL_IMC)都是有效的。
UI窗口的cbWndExtra参数不能为IME提供附加数据。当IME需要使用窗口实例的附加字节的时候,UI窗口使用带有IMMGWL_PRIVATE 参数的SetWindowLong 函数和GetWindowLong with函数. IMMGWL_PRIVATE 参数为窗口的实例提供了一个LONG型附加变量。当UI窗口需要多于LONG型附加变量的数据供自己私有使用时,UI窗口可以在IMMGWL_PRIVATE参数区域放入一个内存句柄(在IMMGWL_PRIVATE参数后加一个句柄作参数)。UI窗口程序能使用操作系统提供的默认窗口过程函数DefWindowProc,但是UI窗口不能向DefWindowProc传递任何WM_IME_xxx消息。即使WM_IME_xxx消息没有被UI窗口过程使用,UI窗口也不能把它传递给DefWindowProc。
下面的例子示范了如何为UI窗口分配和使用一块私有的内存区。
LRESULT UIWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HIMC hIMC;
HGLOBAL hMyExtra;
switch(msg){
case WM_CREATE:
// Allocate the memory block for the window instance. hMyExtra = GlobalAlloc(GHND,size_of_MyExtra); if (!hMyExtra)
MyError();
// Set the memory handle into IMMGWL_PRIVATE //在这里增加了一个句柄(LONG)hMyExtra作为第3个参数,OS就自动把它放进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函数,ImmSetConversionMode函数创建一个WM_IME_NOTIFY消息通知给NotifyIME函数和 UI窗口。如果UI窗口要改变转换模式的显示,UI窗口应该等待WM_IME_NOTIFY消息。
UI窗口的部件
UI窗口通过查阅当前的输入上下文,能注册和显示编码窗口、状态窗口。 UI窗口的部件的类型必须包括CS_IME位。一个UI窗口实例,从当前的输入上下文获取关于编码字符串、字体、坐标位置的信息。
当一个应用程序窗口获得焦点的时候,系统给这个窗口提供输入上下文,并且设置当前的输入上下文给UI窗口。系统然后发送一个WM_IME_SETCONTEXT 消息和输入上下文的句柄给应用程序。应用程序然后传递这个消息给UI窗口。如果当前的输入上下文被其它的输入上下文替换,UI窗口应该重绘编码窗口。任何时候当前的输入上下文被改变,UI窗口会显示出一个正确的编码窗口。因此,IME的运行状态是有保障的。
一个UI窗口能建立一个子窗口或者弹出窗口显示它的状态、编码字符串,或者候选列表。无论如何,这些窗口必须被UI窗口所有,并且是作为限制能力的窗口(disabled windows)被建立。通过IME建立的任何窗口不应该获得焦点。
IME 输入上下文
每一个窗口关联着一个IME输入上下文。IMM使用这个输入上下文维持IME的状态、数据等
等,并用它在IME和应用程序间通信。
默认的输入上下文
通过默认,系统为每一个线程建立了一个默认输入上下文。所有线程的IME不感知窗口共享这个输入上下文。
应用程序建立的输入上下文
一个应用程序窗口可以把它的窗口句柄关联到一个输入上下文,去维持IME的任何状态,包括中间的编码字符串。一个应用程序一次只能把输入上下文关联到一个窗口句柄,无论何时,当这个窗口被激活的时候,系统自动选择这个输入上下文。用这种方式,应用程序从复杂的进出焦点进程中解脱出来。
使用输入上下文
当一个应用程序或者系统建立一个新的输入上下文,系统会准备好新输入上下文和它IMC(IMCC)部件。这些部件包括:hCompStr, hCandInfo, hGuideLine, hPrivate, hMsgBuf。基本上,IME不需要建立输入上下文和输入上下文的部件。IME能改变它们的大小和锁定他们得到它们的指针。
访问输入上下文(HIMC)
当一个应用程序访问输入上下文,IME必须调用ImmLockIMC函数得到输入上下文的指针。ImmLockIMC函数增加IMM对IMC的锁定计数。ImmUnlockIMC函数减少IMM对IMC的锁定计数。
访问输入上下文的部件(HIMCC)
当一个应用程序访问输入上下文的部件,IME必须调用ImmLockIMCC函数得到输入上下文部件IMCC的指针。ImmLockIMCC函数增加IMM对IMC的锁定计数。ImmUnlockIMCC函数减少IMM对IMC的锁定计数。ImmReSizeIMCC函数能重设IMCC为在输入法参数中指定的大小。 有时候,IME需要建立一个新的输入上下文部件。IME可以调用ImmCreateIMCC函数这样做。销毁一个新的输入上下文部件,IME可以调用ImmDestroyIMCC函数。
下面的例子显示了如何访问IMCC,并且改变部件的大小。
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必须产生WM_IME_STARTCOMPOSITION(开始写作)消息。如果IME改变了写作的字符,IME必须产生WM_IME_COMPOSITION(写作)消息。
IME产生消息的方式有两个:一个是通过ImeToAsciiEx提供的lpdwTransKey缓冲区,另一个是通过调用ImmGenerateMessage函数。
使用lpTransMsgList 产生消息
由IME创始的事件在实现中被视作为给和输入上下文关联在一起的窗口产生消息。基本上,IME使用ImeToAsciiEx函数提供的lpTransMsgList参数产生消息,当ImeToAsciiEx函数被调用的时候,IME把消息放进lpTransMsgList缓冲区。
在ImeToAsciiEx函数中被指定的lpTransMsgList缓冲区是由操作系统提供的。这个函数能够把所有消息一次性放置到这个缓冲区。放置的消息数量是由缓冲区的第一个双字节给定的。无论如何,如果ImeToAsciiEx函数要产生比给定的数字更多的消息,ImeToAsciiEx函数可以把所有的消息放置入输入上下文中的hMsgBuf成员中,然后 ImeToAsciiEx函数返回存放的消息数量。
当ImeToAsciiEx函数的返回值大于lpTransMsgList的指定值,系统不会从lpTransMsgList中取出消息。代替的是,操作系统查找输入上下文中的hMsgBuf中存放的消息,此时,系统只会把lpTransMsgList中的数据当作ImeToAsciiEx函数的一个参数传递,而不看作是IME产生的消息。
下面是实现ImeToAsciiEx函数的代码例子
UINT
ImeToAsciiEx(
uVirKey,
uScanCode,
lpbKeyState,
lpTransMsgList,
fuState,
hIMC
)
{
DWORD dwMyNumMsg = 0;
. . .
// Set the messages that the IME wants to generate.
pTransMsgList->TransMsg[0].message =msg;
pTransMsgList->TransMsg[0].wParam = wParam;
pTransMsgList->TransMsg[0].lParam = lParam;
// Count the number of the messages that the IME wants to generate.
dwMyNumMsg++;
. . .
return dwMyNumMsg;
}
使用消息缓冲区产生消息
即使ImeToAsciiEx函数没有被调用,通过使用输入上下文中的消息缓冲区hMsgBuf,IME程序仍然能产生消息给和输入上下文关联在一起的窗口。这个消息缓冲区作为一个内存句柄操作,并且IME把消息放入这个内存区。IME然后调用ImmGenerateMessage函数,把放在这个内存区的消息发送给正确的窗口。
下面是实现ImmGenerateMessage函数的代码例子
MyGenerateMesage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAMlParam)
{
LPINPUTCONTEXT lpIMC;
HGLOBAL hTemp;
LPTRANSMSG lpTransMsg;
DWORD dwMyNumMsg = 1;
// Lock the Input Context.
lpIMC = ImmLockIMC(hIMC);
if (!lpIMC) „.. // Error!
// re-allocate the memory block for the message buffer. hTemp = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + dwMyNumMsg) *
sizeof(TRANSMSG));
if (!hTemp) „.// Error!
lpIMC->hMsgBuf = hTemp;
// Lock the memory for the message buffer. lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf); if (!lpTransMsg) „. // Error!
// Set the messages that the IME wants to generate. lpTransMsg[lpIMC->dwNumMsg].message = msg; lpTransMsg[lpIMC->dwNumMsg].wParam = wParam; lpTransMsg[lpIMC->dwNumMsg].lParam = lParam;
// Set the number of the messages.
lpIMC->dwNumMsgBuf += dwMyNumMsg;
// Unlock the memory for the message buffer and the Input Context.
ImmUnlockIMCC(lpIMC->hMsgBuf);
ImmLockIMC(hIMC);
// Call ImmGenerateMessage function.
ImmGenerateMessage(hIMC);
}