首先感谢:
http://blog.csdn.net/shuilan0066/article/details/6884483输入法 编程分析
http://blog.csdn.net/mspinyin/article/details/6141599输入法的注册、安装和卸载
http://www.setoutsoft.cn/Html/?256.html 浅谈输入法编程
http://wenku.baidu.com/view/3d179422bcd126fff7050b9d.html输入法漫谈
这些文章作者的无私奉献。
TSF(Text Service Framework)WIN7
写这篇文章的动机是:由于Win7 64 位系统的普及,按照上面这些文章来编写自己的输入法会遇到很多意想不到的问题。至今网络上没有找到现成详细的解决方案。我通过一周的摸索和总结,深知靠自己摸索编译成功并安装一个可以在win7 64位系统下运行的输入法是多么的艰难。 特将自己的经验拿出来和大家分享。
编写输入法有几种方式,如外挂式,IME式,TSF式,今天我们主要介绍IME式( 输入法接口式(Input Method Editor-IME))
1,输入法是什么东西?
编写输入法其实就是编写一个DLL ,一个导出一些操作系统约定函数的DLL,操作系统通过这些函数和我们的程序交互,将用户输入的编码转换成汉字通过消息传送给应用程序(如记事本,word,)。用现在的话来说就是实现一个“接口”。
(1)操作系统如何和我们的Dll交互呢?
(2)我们编写的这个Dll就是IME
输入法工作原理 如下图:
(上图来自于:http://blog.csdn.net/shuilan0066/article/details/6883629 输入法工作原理)
2,具体我们应该做哪些工作呢?
(1)首先编写一个DLL,导出IME规定的函数。
注意这个DLL除了必须导出IME规定的函数外,还要满足如下条件。
包含一个.rc 的资源文件。包含一个Version资源
如下图划红线的部分必须设置成:FILETYPE : VFT_DRV FILESUBTYPE : VFT2_DRV_INPUTMETHOD
否则输入法无法加载。
(2)如何导出这些函数呢?
通过def文件导出函数。然后如下图设置。 使用def文件导出dll函数的好处是,导出的函数名不会变化。
(3)要导出哪些函数,这些函数有什么用呢?
IME要求导出的有十几个函数。可真正重要的只有几个。
输入法初始化:
ImeInquire: 刚选择某输入法时,IMM调用此函数,获得输入法相关信息
BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo,LPTSTR lpszUIClass,LPCTSTR lpszOption)
{
if (!lpIMEInfo) return (FALSE);
lpIMEInfo->dwPrivateDataSize = 0;
lpIMEInfo->fdwProperty = IME_PROP_KBD_CHAR_FIRST |
#ifdef _UNICODE
IME_PROP_UNICODE |
#endif
IME_PROP_SPECIAL_UI |
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, UICLASSNAME);
return TRUE;
}
ImeSelect: 打开或关闭输入法时被调用,在此函数中对输入法上下文进行初始化或恢复释放,
在打开输入法时 fSelect为TRUE, 在关闭输入法时fSelect为FALSE
将键盘消息转换为相应汉字:
ImeProcessKey :处理键盘消息: IMM通过此函数,对键盘消息进行分类筛选,一类可以直接发给应用程序,一类需要发送给IME进行转换 ,返回值为FALSE,说明键盘消息被直接发送给了应用程序; 返回值为TRUE 说明键盘消息被发送给了IME,被发送IME后,IMM会立即调用ImeToAsciiEx对键盘消息进行转换。
ImeToAsciiEx :输入法编程最重要部分----- 此函数将进过ImeProcessKey 筛选,通过IMM传递过来的键盘消息转换为composition写作窗口中的字符串,然后再查找码表,更新候选窗口,最后选择某候选字符作为最终结果,通过消息传递给应用程序。
(4)我们的输入法怎么显示和更新写作窗户,候选窗口,怎么把用户选择的汉字传递给应用程序,通过什么机制呢?
写作窗口:就是显示和编辑用户输入字符串的窗口,比如输入的是拼音
侯选窗口:就是显示符合用户输入编码的汉字,供用户选择。通常写作窗口和获选窗户可以合二为一。
首先我们要了解两个概念:
<1>UIWnd 和UIWndProc
<2>输入法上下文(HIMC)
我们都知道windos是通过消息来运作的,要显示,更新 输入法的写作和候选窗口,就需要有消息循环。
而输入法是一个插件,他需要依靠应用程序中用户的输入消息,来控制输入法写作和候选窗口的显示和更新。
可应用程序的消息怎样才能传递给输入法呢?
IME要求我们自建的输入法dll导出一个接口,原型如LRESULT WINAPI UIWndProc(HWND hUIWnd, UINT message,WPARAM wParam, LPARAM lParam),
而User.exe(一个系统进程) 会创建一个UIWnd ,这个窗口不会显示,就是它充当应用程序和输入法之间windos消息的传递。在这个窗口的消息循环中会调用我们输入法dll导出的接口:UIWndProc。 传递过来的HWND hUIWnd 就是这个窗口的句柄,它是输入法中创建的窗口如写作窗口,候选窗口的宿主(Owner)。
上面我们说了应用程序怎么把消息传递给输入法,下面我们说说 输入法怎么把它的消息和数据传递给应用程序?
User.exe(一个系统进程) 会为应用程序分配一片内存,而HIMC就是这片内存的句柄,我们称之为输入法上下文。我们将输入法要传递给应用程序的消息和数据存入这片内存,再由User.exe 传递给应用程序。
下面我们我简单介绍一下消息和数据传递的具体方法。
首先要获得HIMC,通过UIWnd的句柄g_hUIWnd 使用ImmLockIMC
HIMC hIMC = (HIMC)GetWindowLong(g_hUIWnd, IMMGWL_IMC);
*lpIMC = (LPINPUTCONTEXT)ImmLockIMC(*hIMC)
然后向HIMC中写入消息
if (IsWindow(lpIMC->hWnd))
{
if (!(lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf,(lpIMC->dwNumMsgBuf+1) * sizeof(TRANSMSG))))
goto Error;
if (!(lpTransMsg = (LPTRANSMSG)ImmLockIMCC(lpIMC->hMsgBuf)))
goto Error;
//将消息写入HIMC中
lpTransMsg += (lpIMC->dwNumMsgBuf);
lpTransMsg->message=message;
lpTransMsg->wParam=wParam;
lpTransMsg->lParam=lParam;
lpIMC->dwNumMsgBuf++;
//将消息发送到IME,IME再决定是自己处理还是继续发给应用程序
ImmGenerateMessage(hIMC);
}
向HIMC中写入数据,将用户选择的汉字传递给应用程序。
lpIMECompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);
{
lpResultStr = GetResultStr();//用户选择的汉字字符串
if (!lpResultStr) return FALSE;
//将用户选择的字符串写入HIMC中
_tcscpy((LPTSTR)((LPBYTE)(lpIMECompStr) + (lpIMECompStr)->dwResultStrOffset), lpResultStr);
lpIMECompStr->dwResultStrLen = _tcslen(lpResultStr);
}
ImmUnlockIMCC(lpIMC->hCompStr);
同时将用户选择的汉字传递给应用程序需要消息的协助。
分别是WM_IME_STARTCOMPOSITION、WM_IME_COMPOSITION和WM_IME_ENDCOMPOSITION,它们分别指示开始输入编码,输入编码或者结果(视参数而异)及编码输入完成 ,WM_IME_STARTCOMPOSITION和WM_IME_ENDCOMPOSITION需要成对使用。
当发送WM_IME_COMPOSITION (GCS_COMPREAD|GCS_COMP|GCS_CURSORPOS|GCS_DELTASTART|GCS_RESULTREAD|GCS_RESULT),消息时候,IME将HIMC中lpIMECompStr 中的汉字通过WM_IME_CHAR, 消息传递给应用程序。或者转换成WM_CHAR消息传递给应用程序。
上面总结了输入法编程的基本原理。
3,输入法的安装和调试
64位系统 的输入法安装和32位系统不同。64位系统兼容32位系统的应用程序,可输入法不行,64位的应用程序必须有64位的输入法,而32位程序必须有32位的输入法。
所以我们要在64位系统中安装输入法,首先必须将我们的输入法dll编译成64位和32位两种。
(1)如何编译64位的程序呢?使用64位的操作系统,在VS2010中打开 菜单:生成-》配置管理器
(2)如何安装输入法?
将编译的dll扩展名改成.ime
< 1>拷贝输入法dll到系统目录。将64位编译的dll放入 c:\windows\system32 目录,将32位编译的放入C:\Windows\SysWOW64目录
<2>将输入法注册,网上说可以使用ImmInstallIME 函数,可我在64位系统上始终没有成功。于是使用手动创建注册表项来实现输入法的注册
A.在注册表中HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Keyboard Layouts,创建一个新的Key,名字为xxxx0804 (低位表示语言,这里0804表示简体中文;高位表示设备句柄,0000表示默认的physical layout,如00000804表示简体中文英文键盘)。譬如:
[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Keyboard Layouts/E0200804]
"IME File"="***.IME" //你的输入法dll的文件名
"Layout File"="kbdus.dll"
"Layout Text"="****输入法 2010"
B. Enable这个输入法,譬如:
[HKEY_CURRENT_USER/Keyboard Layout/Preload]
"1"="00000804"
"2"="e0200804" //按照已有最大的序号+1 = 上面你创建的key的名字:E0200804
C,重新启动系统
按照上面的方法按照好输入法以后,我们就可以使用VS2010对输入法进行调试。
<1> 使用附加到进程的方式
然后再窗口选择 写字板,或者word,等进程,
然后,设置断点,在记事本中选择你自己编写的输入法,输入编码,进行调试。
<2>设置命令参数为记事本程序的路径。c:\\windows\\system32\\notepad.exe
(4)关于编译源程序可能会遇到的问题
1、确保应用 Imm32.lib
2、如果 IMM.H 的位置在项目中,应在 additional include directories 中指定为./