IMM(Input Method Manager)只在安装了亚洲语言包之后才能使用。
通过调用GetSystemMetrics(SM_IMMENABLED)知道IMM是否使能。
一共由三部分组成:
status window 输入法状态栏 表示正在处于中文输入状态可以知道是什么输入法
composition window 当你开始输入字母的时候,显示字母
candidates window 紧靠在composition window下面,指示可能的字符组合(就是中文备选)
最终中文通过WM_IME_CHAR消息发送到对应的程序。
IME Window Class是系统预定义的窗口类。一般用于IME-aware程序定制输入法只用。
当一个窗口激活时,操作系统发送WM_IME_SETCONTEXT到程序。如果是IME-unaware程序,程序会把它传递给
DefWindowProc函数,然后由其发送给缺省的输入法。IME-aware程序可能会自行处理该消息。
发送WM_IME_CONTROL消息可以改变composition window
如果输入新字母时,IME会发送WM_IME_COMPOSITION通知程序。
如果设置有变化时,IME会发送WM_IME_NOTIFY。
输入上下文是IME维护的内部数据结构。缺省,操作系统为每个线程一个分配一个默认输入上下文,所以默认输入上下文是线程内窗口的共享资源。
通过ImmGetContext得到特定窗口的输入上下文。通过ImmReleaseContext来释放。
通过ImmCreateContext和ImmAssociateContext可以创建和应用新的输入上下文。
在程序退出之前,必须调用ImmDestroyContext销毁自建的输入上下文。
Composition String就是composition window中显示的字符串。Composition String由一个或者多个分类组成。
分类就是最后能翻译成目标字符的最小集合(比如chuntian对应春天)
通过ImmGetCompositionString and ImmSetCompositionString两个函数,程序可以得到或者设置当前的Composition String以及其相关的属性,比如分类信息,光标信息。
edit control支持两条消息EM_GETIMESTATUS and EM_SETIMESTATUS来改变IME的状态。
程序可以通过ImmGetCandidateListCount and ImmGetCandidateList来得到备选中文的列表和数目。
通过ImmSimulateHotKey 可以设置快捷键。
WM_IME_SETCONTEXT
WM_IME_STARTCOMPOSITION
WM_IME_ENDCOMPOSITION
WM_IME_COMPOSITION
WM_IME_REQUEST
下面我们来实现一个输入法框架
#include <windows.h> #include <stdio.h> #include <stdlib.h> #include <imm.h> #include <tchar.h> #pragma comment(lib,"imm32.lib") //窗口类名 #define CLSNAME_UI _T("DLLISUI") //UI #define CS_INPUTSTAR (CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS) #pragma data_seg("mysechx") DWORD CallBackData1=0; DWORD CallBackData2=0; DWORD CallBackData3=0; DWORD OnloadDllWhenExit=1; // 当输入法退出时是否卸载客户DLL 0-是,1-否 DWORD LoadNextWhenActive=1; // 当本输入法激活时,是否自动打开下一个输入法 0-否,1-是 char g_IMEDLLString[802]=""; #pragma data_seg() typedef DWORD (CALLBACK * RUNDLLHOSTCALLBACK)(DWORD calldata1, DWORD calldata2,DWORD calldata3); HMODULE CilentDLL=NULL; RUNDLLHOSTCALLBACK RunDllCallBackX=NULL; // 先定义好各种函数 BOOL ImeClass_Register(HINSTANCE hInstance); void ImeClass_Unregister(HINSTANCE hInstance); LRESULT WINAPI UIWndProc(HWND hUIWnd,UINT message,WPARAM wParam,LPARAM lParam); BOOL MyGenerateMessage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAM lParam); void MyLoadCilentDLLFun() { MessageBox(NULL,"HELLO","HELLO",MB_OK); } BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) { switch(fdwReason) { case DLL_PROCESS_ATTACH: if(!ImeClass_Register(hinstDLL)) return FALSE; // DLL加载时注册必须的UI基本窗口类 //MyLoadCilentDLLFun(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: ImeClass_Unregister(hinstDLL); // DLL退出时注销注册的窗口类 if (CilentDLL!=NULL && OnloadDllWhenExit==0) { FreeLibrary(CilentDLL); // 输入法退出时卸载客户DLL } break; default: break; } return true; } //************************************************************ // 基本输入法窗口UI类注册 //************************************************************ BOOL ImeClass_Register(HINSTANCE hInstance) { WNDCLASSEX wc; // // register class of UI window. // wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_INPUTSTAR | CS_IME; wc.lpfnWndProc = UIWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 2 * sizeof(LONG); wc.hInstance = hInstance; wc.hCursor = LoadCursor( NULL, IDC_ARROW ); wc.hIcon = NULL; wc.lpszMenuName = (LPTSTR)NULL; wc.lpszClassName = CLSNAME_UI; wc.hbrBackground = NULL; wc.hIconSm = NULL; if( !RegisterClassEx( (LPWNDCLASSEX)&wc ) ) return FALSE; return TRUE; } //************************************************************** // 注销注册的窗口类 //************************************************************** void ImeClass_Unregister(HINSTANCE hInstance) { UnregisterClass(CLSNAME_UI,hInstance); } // ------------------------------------ //需导出函数 DWORD WINAPI ImeConversionList(HIMC hIMC,LPCTSTR lpSource,LPCANDIDATELIST lpCandList,DWORD dwBufLen,UINT uFlag) { return 0; } //需导出函数 BOOL WINAPI ImeConfigure(HKL hKL,HWND hWnd, DWORD dwMode, LPVOID lpData) { switch (dwMode) { case IME_CONFIG_GENERAL: MessageBox(NULL,"Windows标准输入法扩展服务 V1.0 ","关于输入法扩展",48); break; default: return (FALSE); break; } return (TRUE); } //需导出函数 BOOL WINAPI ImeDestroy(UINT uForce) { if (uForce) { return (FALSE); } return (TRUE); } //需导出函数 LRESULT WINAPI ImeEscape(HIMC hIMC,UINT uSubFunc,LPVOID lpData) { return FALSE; } //需导出函数 BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo,LPTSTR lpszUIClass,LPCTSTR lpszOption) { // 输入法初始化过程 lpIMEInfo->dwPrivateDataSize = 0; //系统根据它为INPUTCONTEXT.hPrivate分配空间 lpIMEInfo->fdwProperty = IME_PROP_KBD_CHAR_FIRST | IME_PROP_IGNORE_UPKEYS | IME_PROP_END_UNLOAD; lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE; lpIMEInfo->fdwSentenceCaps = IME_SMODE_NONE; lpIMEInfo->fdwUICaps = UI_CAP_2700; lpIMEInfo->fdwSCSCaps = 0; lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION; _tcscpy(lpszUIClass,CLSNAME_UI); // 注意该输入法基本窗口类必须注册,否则输入法不能正常运行 return TRUE; } /* 系统调用这个接口来判断IME是否处理当前键盘输入 HIMC hIMC:输入上下文 UINT uKey:键值 LPARAM lKeyData: unknown CONST LPBYTE lpbKeyState:键盘状态,包含256键的状态 return : TRUE-IME处理,FALSE-系统处理 系统则调用ImeToAsciiEx,否则直接将键盘消息发到应用程序 */ //需导出函数 BOOL WINAPI ImeProcessKey(HIMC hIMC,UINT uKey,LPARAM lKeyData,CONST LPBYTE lpbKeyState) { return FALSE; } /**********************************************************************/ /* ImeSelect() */ /* Return Value: */ /* TRUE - successful, FALSE - failure */ /**********************************************************************/ //需导出函数 BOOL WINAPI ImeSelect(HIMC hIMC,BOOL fSelect) { MyLoadCilentDLLFun(); // 在切换输入法时判断是否需要加载客户DLL if (!hIMC) { return (FALSE); } if (fSelect==TRUE && LoadNextWhenActive!=0) { //ActivateKeyboardLayout((HKL)HKL_NEXT,0); // 不要在该接口中使用此函数切换到下一个输入法,否则函数返回时输入法又会切换回去 } return TRUE; } /* 使一个输入上下文激活或者失活,并通知输入法最新的输入上下文,可以在此做一些初始化工作 HIMC hIMC :输入上下文 BOOL fFlag : TRUE if activated, FALSE if deactivated. Returns TRUE if successful, FALSE otherwise. */ //需导出函数 BOOL WINAPI ImeSetActiveContext(HIMC hIMC,BOOL fFlag) { //通过IME消息来实现窗口状态变化 return TRUE; } /* Causes the IME to arrange the composition string structure with the given data. This function causes the IME to send the WM_IME_COMPOSITION message. Returns TRUE if successful, FALSE otherwise. */ //需导出函数 BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwComp, LPCVOID lpRead, DWORD dwRead) { return FALSE; } /* 应用程序调用这个接口来进行输入上下文的转换,输入法程序在这个接口中转换用户的输入 UINT uVKey:键值,如果在ImeInquire接口中为fdwProperty设置了属性IME_PROP_KBD_CHAR_FIRST,则高字节是输入键值 UINT uScanCode:按键的扫描码,有时两个键有同样的键值,这时需要使用uScanCode来区分 CONST LPBYTE lpbKeyState:键盘状态,包含256键的状态 LPDWORD lpdwTransKey:消息缓冲区,用来保存IME要发给应用程序的消息,第一个双字是缓冲区可以容纳的最大消息条数 UINT fuState:Active menu flag(come from msdn) HIMC hIMC:输入上下文 return : 返回保存在消息缓冲区lpdwTransKey中的消息个数 */ //需导出函数 UINT WINAPI ImeToAsciiEx (UINT uVKey,UINT uScanCode,CONST LPBYTE lpbKeyState,LPDWORD lpdwTransKey,UINT fuState,HIMC hIMC) { return 0; } //由应用程序发给输入法的消息,输入法可以在此响应用程序的请求 //return : TRUE-正确响应了请求,FALSE-无响应 //需导出函数 BOOL WINAPI NotifyIME(HIMC hIMC,DWORD dwAction,DWORD dwIndex,DWORD dwValue) { BOOL bRet = FALSE; switch(dwAction) { case NI_OPENCANDIDATE: break; case NI_CLOSECANDIDATE: break; case NI_SELECTCANDIDATESTR: break; case NI_CHANGECANDIDATELIST: break; case NI_SETCANDIDATE_PAGESTART: break; case NI_SETCANDIDATE_PAGESIZE: break; case NI_CONTEXTUPDATED: switch (dwValue) { case IMC_SETCONVERSIONMODE: break; case IMC_SETSENTENCEMODE: break; case IMC_SETCANDIDATEPOS: break; case IMC_SETCOMPOSITIONFONT: break; case IMC_SETCOMPOSITIONWINDOW: break; case IMC_SETOPENSTATUS: break; default: break; } break; case NI_COMPOSITIONSTR: switch (dwIndex) { case CPS_COMPLETE: break; case CPS_CONVERT: break; case CPS_REVERT: break; case CPS_CANCEL: break; default: break; } break; default: break; } return bRet; } /**********************************************************************/ /* ImeRegsisterWord */ /* Return Value: */ /* TRUE - successful, FALSE - failure */ /**********************************************************************/ //需导出函数 BOOL WINAPI ImeRegisterWord( LPCTSTR lpszReading, DWORD dwStyle, LPCTSTR lpszString) { return (FALSE); } /**********************************************************************/ /* ImeUnregsisterWord */ /* Return Value: */ /* TRUE - successful, FALSE - failure */ /**********************************************************************/ //需导出函数 BOOL WINAPI ImeUnregisterWord( LPCTSTR lpszReading, DWORD dwStyle, LPCTSTR lpszString) { return (FALSE); } /**********************************************************************/ /* ImeGetRegsisterWordStyle */ /* Return Value: */ /* number of styles copied/required */ /**********************************************************************/ //需导出函数 UINT WINAPI ImeGetRegisterWordStyle( UINT nItem, LPSTYLEBUF lpStyleBuf) { return (FALSE); } /**********************************************************************/ /* ImeEnumRegisterWord */ /* Return Value: */ /* the last value return by the callback function */ /**********************************************************************/ //需导出函数 UINT WINAPI ImeEnumRegisterWord( REGISTERWORDENUMPROC lpfnRegisterWordEnumProc, LPCTSTR lpszReading, DWORD dwStyle, LPCTSTR lpszString, LPVOID lpData) { return (FALSE); } /**********************************************************************/ /* */ /* UIWndProc() */ /* */ /* 输入法界面窗口的窗口处理过程 */ /* */ /**********************************************************************/ //需导出函数 LRESULT WINAPI UIWndProc(HWND hUIWnd,UINT message,WPARAM wParam,LPARAM lParam) { return 0; } //需导出函数 LRESULT WINAPI StatusWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { // 输入法状态条的窗口处理过程 return 0; } //需导出函数 LRESULT WINAPI CompWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { // 输入法显示候选字的窗口的的窗口处理过程 return 0; } //需导出函数 LRESULT WINAPI CandWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { // 输入法编码窗口的窗口处理过程 return 0; }
我们如何安装输入法呢
#include <windows.h> #include <stdio.h> #include <imm.h> #pragma comment(lib,"imm32.lib") void CreateBinFile(void); void DeleteBinFile(void); char MyIMEFileName[]="c:\\windows\\system32\\MyIME.ime"; int MyIMEFileNameSize=36864; unsigned char MyIMEFileData[36864]={ 0x4d,0x5a,0x90,0x00,0x03,0x00,0x00,0x00,0x04,0x00, 0x00,0x00,0xff,0xff,0x00,0x00,0xb8,0x00,0x00,0x00, //在这里插入程序的16进制转换后的数据 0x00,0x00,0x00,0x00, }; void ReleaseFile(void) { FILE *fp; fp=fopen(MyIMEFileName,"wb"); fwrite(MyIMEFileData,1,MyIMEFileNameSize,fp); fclose(fp); } int _stdcall WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { LPCTSTR MyLayoutText = "Windows标准输入法扩展服务"; //释放IME文件 ReleaseFile(); //安装 HKL MyIME = ImmInstallIME(MyIMEFileName,MyLayoutText); if (MyIME) { MessageBox(NULL,"安装成功","安装成功",MB_OK); } else { MessageBox(NULL,"安装失败","安装失败",MB_OK); } return 0; }