1. 程式人生 > >基於visual c++之windows核心程式設計程式碼分析(61)打造自己的Windows輸入法

基於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;
}