Win32 IME 编程心得【转】
一些術語
- IME: Input Method Editor/Engine, 輸入法編輯器, 引擎
- IMM: Input Method Manager, 輸入法管理器
- Comp: Composition String, 一般是用戶輸入的字串, 比如拼音輸入香'字要打xiang', 這個`xiang'就是用戶輸入的Comp. 你可以告訴IMM當前的Comp是什么, 這樣如果應用能自己顯示Comp/Cands的話它就自己顯示; 你也可以不告訴IMM, 這樣你自己負責顯示就行了.
- Cands: Candidates, 候選詞組
- Commit: 提交上屏
其他一些軟鍵盤啊, GuideLine啊之類的對我的輸入法沒什么用, 我就全部無視了.
簡介
Win32下的輸入法編程概括地來說就是要寫一個DLL. 這個DLL要實現并在.def文件中指定輸出M$指定的一些API. Win32在裝載你的輸入法DLL時會檢查是不是每個API都能查詢到, 如果不是的話, 這個輸入法就不會被成功的裝載.
建議下載2600.1106版本的win32 DDK, 里面有區位輸入法的源程序, 我的影舞筆就是參考這個程序寫的. 可以看看里面的wingb.def文件, 總共輸出了將近20個API. 其實大部分都沒什么用, 直接套就行了, 重要的也就5~6個.
$cat wingb.def LIBRARY ? ? ? ? WINGBEXPORTSImeConversionListImeConfigureImeDestroyImeEscapeImeInquireImeProcessKeyImeSelectImeSetActiveContextImeSetCompositionStringImeToAsciiExNotifyIMEImeRegisterWordImeUnregisterWordImeGetRegisterWordStyleImeEnumRegisterWordUIWndProcStatusWndProcCompWndProcCandWndProc事實上win32 DLL還有一個隱含的輸出函數, 就是DLL的入口函數, 一般都是名為DllMain的一個函數, 但是在區位輸入法里這個函數的名字是ImeDllInit. 你可以在你的makefile (或類似于vc6的.dsp/vc789的.vcproj等文件)里指定入口函數的名字.
初始化的大概的順序是:
至此, 用戶就可以開始使用你的輸入法了, 這之后有意義的順序是大概這樣的:
我為了自己編程方便, 就沒有通知應用程序自己去顯示comp和cands. 同時也是因為我覺得實在是沒有必要.
DllMain
這個函數肯定是最重要的, 一個DLL沒有這個入口函數的話就不是DLL了. 在DDK的區位輸入法代碼里有一個sources的文件, 里面有一行:
DLLENTRY=ImeDllInit你如果用別的build系統, 比如Visual Studio或者mingw, 你就應該自己配置你的入口函數是哪個.
這個函數在dll load的時候需要初始化你的輸入法里要用到的全局變量, 以及注冊win32的幾個窗口類. 在區位輸入法里注冊了四個窗口類, UI, Status, Comp, Cand. 其中UI窗口是一個純消息窗口, 也是win32 IME必須要求的一個窗口. 這個窗口的類名會在ImeInquire里傳給win32 IME以便讓win32 IME知道它應該去跟誰通訊. 我的影舞筆輸入法把Comp和Cand的窗口合并為一個了.
如果這個函數返回false的話那這個DLL就會load失敗, 當前嘗試load你的輸入法的這個程序就沒法用你的輸入法了. 所以在這個函數里你可以干一些很``酷'' 的事情, 比如, 在測試階段, 你可以指定只有notepad才能成功load你的輸入法, 通過GetModuleFileName你可以得到當前調用的程序的路徑, 如果不是notepad, 那就不讓它用你的輸入法. 然后呢, 你在win32的控制面板→區域設置里指定你當前正在測試開發的輸入法為默認的輸入法, 這樣你一打開notepad, 就可以開始測試你的輸入法了, 而不需要按一下輸入法切換鍵才能開始測. 雖然只是省下按一個鍵, 但是也是值得的, 因為相信你會按很多次的. 而這時候其他的程序不會受影響, 你可以隨便殺死notepad.exe, 做下一輪的開發, 測試迭代.
(win32下一個DLL被load了的話, 是不允許替換這個dll文件的. 所以當你發現一個bug, 做了修正, 你沒法把build出來的這個新的dll拷到系統路徑里, 必須先把所有的load了這個dll的程序殺死. 如果你的輸入法不是默認的, 那你每次都要按一下切換鍵才能開始測試; 如果它是默認的但是你不把除了notepad的其他程序排除的話, 你每次都要殺死很多程序, 比如explorer.exe等. 尤其是如果你只能手工一個一個的刪的話, 很快你會瘋掉的. 你甚至都不知道哪個程序load了你的輸入法. 只能一個一個的猜? 如果你知道sysinternal的process explorer的話, 那你還可以用一下它的查找功能).
還有一個特別有用的功能是, 即使你的輸入法還有bug, 但是如果這個bug只是針對某個程序的話(或者說某個程序有bug, 但只針對你的輸入法:-), 你可以把這個程序排除在外. 比如, Cygwin下的X窗口程序都是由xwin.exe來畫窗口的, 這些窗口都不能處理win32的輸入法, 但是win32的輸入法切換鍵又能把輸入法的狀態欄給切出來, 很明顯沒什么意義, 我就把xwin.exe在我的輸入法里排除了. 又比如, 所有的DOS窗口的輸入法處理都是由一個叫conime.exe的程序處理的, 這個程序好像會對我的輸入法提很非分的要求, 我干脆就把它也拒之門外:-) 以后我就打定主意在終端窗口里再也不用輸入法了, 呵呵! (造成這個的原因是前面提到的conime是個`懂''輸入法的應用, 它太`懂''了, 它要求你必須設置comp串/cands告訴它知道, 你還不能自己顯示! 我懷疑這個應用的imm是不是壓根就不會幫你創建那個ime消息窗口. 一句話, 它太霸道了).
做這樣的選擇, 我的生活會更簡單.
ImeInquire
這個函數是除了DllMain后第一個會被win32 IMM調用的函數. IMM通過調用這個函數知道你的輸入法有什么特性. 比如, 除了按鍵消息外, 你是不是還想處理鍵放開的消息. 以下是我的影舞筆的此函數代碼(注釋版)
BOOL WINAPI ImeInquire(LPIMEINFO lpImeInfo, LPTSTR lpszWndCls, DWORD dwSystemInfoFlags) {if (!lpImeInfo) { //簡單出錯處理return FALSE;}lpImeInfo->dwPrivateDataSize = 0; //IMM會根據這個值自動為你的輸入法分配一塊內存, 你可以用它來保存一些你的context數據. 我嫌這玩意兒太"聰明"了, 不用之.lpImeInfo->fdwProperty = IME_PROP_KBD_CHAR_FIRST | IME_PROP_UNICODE | IME_PROP_IGNORE_UPKEYS | IME_PROP_SPECIAL_UI;// IME_PROP_KBD_CHAR_FIRST 是說IMM調用你的ImeProcessKey和ImeToAsciiEx函數之前是不是把按鍵消息的char值算出來, 在第一個整型參數的前兩個字節里傳給你, 其實你也可以自己算的// 如果你不設這個標志的話那第一個參數就只有低位的兩個字節有意義 (好像是所按的鍵的虛擬鍵值).// IME_PROP_UNICODE, 意義應該很明顯了, 一般肯定得設上// IME_PROP_IGNORE_UPKEYS, 就是上文說的要不要處理鍵放開的消息. 不設這個標志就是要處理, 設了就是不處理(IMM針對鍵放開的消息就不會來調你的ImeProcessKey和ImeToAsciiEx了).// IME_PROP_SPECIAL_UI, 不記得什么意思了, 可以上google查一下.lpImeInfo->fdwConversionCaps =IME_CMODE_NATIVE | IME_CMODE_NOCONVERSION;lpImeInfo->fdwSentenceCaps = 0;lpImeInfo->fdwUICaps = UI_CAP_ROT90;lpImeInfo->fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_MAKEREAD;lpImeInfo->fdwSelectCaps = (DWORD) 0;// 這之上的這段代碼是什么意思我也不明白, 但是這樣寫對我的影舞筆就夠用了, 所以我也懶得去弄明白.lstrcpy(lpszWndCls, get_ui_class_name().c_str());// 這里你要把你的UI窗口的類名拷到IMM傳給你的輸出參數里.return (TRUE);// 一定要返回true, 沒試過這里返回false會怎樣. }LPIMEINFO的定義可以在ddk的immdev.h里查到.
ImeProcessKey
win32 IMM在收到一個鍵盤消息之后, 會先問一下這個函數, 你的IME是不是想處理這個鍵, 如果你在這個函數里返回true, 意思就是你想處理, 那么imm就會接著調下一個函數ImeToAsciiEx, 否則它就會自己處理這個鍵盤消息.
ImeToAsciiEx
這個函數的返回值有點意思. 返回值應該是你這個函數的這次調用一共給win32產生的多少個消息. 比如用戶輸入了一個完整的五筆編碼, 希望提交他/她選中的候選詞了, 你就把要提交的數據(一個unicode字符串)寫到輸入法上下文的提交字串的內存句柄中, 再把 (WM_IME_COMPOSITION, 0, GCS_COMP|GCS_RESULT|GCS_RESULTREAD) 這樣一個消息添加到輸入法上下文的消息內存中, 把要返回多少個消息加一.
以下是輸入法上下文結構的定義:
typedef struct tagINPUTCONTEXT {HWND ? ? ? ? ? ? ? ?hWnd;BOOL ? ? ? ? ? ? ? ?fOpen;POINT ? ? ? ? ? ? ? ptStatusWndPos;POINT ? ? ? ? ? ? ? ptSoftKbdPos;DWORD ? ? ? ? ? ? ? fdwConversion;DWORD ? ? ? ? ? ? ? fdwSentence;union ? {LOGFONTA ? ? ? ?A;LOGFONTW ? ? ? ?W;} lfFont;COMPOSITIONFORM ? ? cfCompForm;CANDIDATEFORM ? ? ? cfCandForm[4];HIMCC ? ? ? ? ? ? ? hCompStr;HIMCC ? ? ? ? ? ? ? hCandInfo;HIMCC ? ? ? ? ? ? ? hGuideLine;HIMCC ? ? ? ? ? ? ? hPrivate;DWORD ? ? ? ? ? ? ? dwNumMsgBuf;HIMCC ? ? ? ? ? ? ? hMsgBuf;DWORD ? ? ? ? ? ? ? fdwInit;DWORD ? ? ? ? ? ? ? dwReserve[3]; } INPUTCONTEXTUIWndProc
這個函數里要處理IME消息. 其實UI窗口根本沒有UI, 沒有圖形! 這個窗口是一個純消息窗口, 你不會收到wm_paint的消息. 所以我直接把區位輸入法里處理wm_paint的回調函數刪了. 我覺得把這個窗口命名為ImePureMsgWnd更合適一些.
話說回來, 這個IME第一重要的窗口是誰創建的呢? 答案是IMM. 你在區位輸入法的代碼里不會看到這個窗口被CreateWindowEx. 在DllMain里你會注冊這個窗口的類, 在ImeInquire里你會把這個窗口的類名傳給IMM. 之后你就會收到這個窗口的創建消息了. 說明肯定是IMM負責創建了這個窗口.
這個函數根據收到的消息要負責創建Comp, Status等窗口, 移動這些窗口的位置, 等等.
CompWndProc 和 StatusWndProc
這兩個窗口函數最重要的當然是負責畫窗口了.
其他不怎么重要的API
- ImeConfigure 這個函數就是按理你應該彈一個對話框給用戶配置你的輸入法的地方了, 像我們這種高級電腦用戶, 對這種功能直接無視.
- ImeConversionList 這個函數是讓另一個輸入法來反查一個漢字在你的這個輸入法里是怎么打出來的. 比如區位輸入法可以指定用微軟拼音來反查一個漢字的拼音. 在區位輸入法的unicode模式下按`9999'';輸入香'', 輸入完后還會繼續顯示編輯窗, 內容是`xiang''. 這么無厘頭的功能, 無視!
- ImeDestroy 沒什么好說的,
- ImeEscape 也沒什么好說的, 沒什么鳥用. 以下是我的代碼:
- ImeSelect Google出來的文檔是說在這個函數里初始化或析構你的私有數據, 好吧, 我前面已經說了, 我沒什么私有數據, 所以這個函數也可以簡化了.
- ImeSetActiveContext
- ImeRegisterWord
- ImeUnregisterWord
- ImeGetRegisterWordStyle
- ImeEnumRegisterWord
- ImeSetCompositionString
- NotifyIME
可以上這兒去看文檔. 這些函數都沒有什么用.
一些注意事項
win32輸入法編程的陷阱還是挺多的, 搞不好會很迷惑.
往注冊表里添的時候內容大概是這樣的: (E0330804的0804比較重要, 這代表這是中文輸入法, 更關鍵的是你的資源文件.rc里面也有0804, 如果這個不匹配的話這個ime也是load不了的. 前面的e033可以隨便換).
Windows Registry Editor Version 5.00[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\E0330804] "Ime File"="ywb.dll" "Layout File"="kbdus.dll" "Layout Text"="Chinese (Simplified) - YWB"影舞筆的運行方法
編譯用VS2008, 同時還要求安裝了python3.1, 安裝路徑必須是C:/python31, 同時你必須把代碼co到Q:\gcode\scim-cs下, 如果沒有Q:盤可以用subst.exe掛一個. 當然也可以自己改一下源代碼.
原文:http://code.google.com/p/windows-config/wiki/Win32IME
總結
以上是生活随笔為你收集整理的Win32 IME 编程心得【转】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言试题及答案解析,C语言期末考试题(
- 下一篇: pip下载报错:pip._vendor.