IMM(Input Method Manager)只在安裝了亞洲語言套件之後才能使用。
通過調用GetSystemMetrics(SM_IMMENABLED)知道IMM是否使能。
一共由三部分組成:
status window IME狀態列 表示正在處於中文輸入狀態可以知道是什麼IME
composition window 當你開始輸入字母的時候,顯示字母
candidates window 緊靠在composition window下面,指示可能的字元組合(就是中文備選)
最終中文通過WM_IME_CHAR訊息發送到對應的程式。
IME Window Class是系統預定義的視窗類別。一般用於IME-aware程式定製IME只用。
當一個視窗啟用時,作業系統發送WM_IME_SETCONTEXT到程式。如果是IME-unaware程式,程式會把它傳遞給
DefWindowProc函數,然後由其發送給預設的IME。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
下面我們來實現一個IME架構
#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; // 當IME退出時是否卸載客戶DLL 0-是,1-否DWORD LoadNextWhenActive=1; // 當本IME啟用時,是否自動開啟下一個IME 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); // IME退出時卸載客戶DLL } break; default: break; }return true;}//************************************************************//基本IME視窗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標準IME擴充服務 V1.0 ","關於IME擴充",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){// IME初始化過程 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); // 注意該IME基本視窗類別必須註冊,否則IME不能正常運行 return TRUE;}/*系統調用這個介面來判斷IME是否處理當前鍵盤輸入HIMC hIMC:輸入上下文UINT uKey:索引值LPARAM lKeyData: unknownCONST 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(); // 在切換IME時判斷是否需要載入客戶DLL if (!hIMC) { return (FALSE); }if (fSelect==TRUE && LoadNextWhenActive!=0){//ActivateKeyboardLayout((HKL)HKL_NEXT,0); // 不要在該介面中使用此函數切換到下一個IME,否則函數返回時IME又會切換回去} return TRUE;}/*使一個輸入上下文啟用或者失活,並通知IME最新的輸入上下文,可以在此做一些初始化工作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;}/*應用程式調用這個介面來進行輸入內容相關的轉換,IME程式在這個介面中轉換使用者的輸入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;}//由應用程式發給IME的訊息,IME可以在此響應用程式的請求//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() *//* *//* IME介面視窗的視窗處理過程 *//* *//**********************************************************************///需匯出函數LRESULT WINAPI UIWndProc(HWND hUIWnd,UINT message,WPARAM wParam,LPARAM lParam){ return 0;}//需匯出函數LRESULT WINAPI StatusWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam){// IME狀態條的視窗處理過程return 0;}//需匯出函數LRESULT WINAPI CompWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam){// IME顯示候選字的視窗的的視窗處理過程return 0;}//需匯出函數LRESULT WINAPI CandWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam){// IME編碼視窗的視窗處理過程return 0;}
我們如何安裝IME呢
#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擴充服務";//釋放IME檔案ReleaseFile();//安裝HKL MyIME = ImmInstallIME(MyIMEFileName,MyLayoutText);if (MyIME){MessageBox(NULL,"安裝成功","安裝成功",MB_OK);}else{MessageBox(NULL,"安裝失敗","安裝失敗",MB_OK);}return 0;}