基於visual c++之windows核心程式設計程式碼分析(61)打造自己的Windows輸入法
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;
}