输入法(IME)实现原理

缘起

windows下使用的sougou输入法,除了经常弹出广告,没有什么异常行为,Linux下的中文输入法似乎没那么强大好用了。起初用的是Ibus中的pinyin,后来因为机缘巧合装了yong输入法,发现挺好用的,就一直用着。渐渐的发现yong输入法在一些自由软件中不起作用,有时还行为诡异,虽然大多数的时候,工作的很正常。本着程序员好奇的心态,我想打开输入法这个黑匣子,看看里面有些什么。

正文

1. 简介

输入法的工作原理是击键消息(window中消息)首先送给输入法管理器(前端),输入法管理器调用输入法的转换函数(输入法引擎),输入法的转换函数得到击键消息并保存在自己的数组中,当符合规定条件时(如五笔输入法输入:四键、两键+空格等),输入法把适当的字、词返回给输入法管理器,输入法管理器再把结果字、词传给关联的应用窗口。

简单的来说,输入法就是一个查表操作,输入的信息是键盘信息,根据击键消息到表中查找对应的汉字。举个例子:将码表导入到Excel。假设码表是基于拼音的,码表分两列,一列是拼音,一列是汉字串,那么用鼠标点击编辑->查找,在查找内容框中敲入shishi,点击查找下一个按钮,黑色矩形框立即就跳到A列的shishi的行上面,对应的B列就是试试、事事、实施、时世、史实……”等等这些汉字串,输入法的原理就是这样[2]PS:计算机程序中都可以简化为查表程序,这个表可能是数组,字符串或者数据库。表驱动编程本质上就是从数据的角度出发,根据数据来编程(参考《代码大全 第二版》)。

由于输入法编程中涉及消息处理机制,而消息处理机制是操作系统的核心机制,理解起来比较麻烦。但是,学习输入法编程,要带着问题学,活学活用,学用结合,急用先学,立杆见影,在字上狠下功夫。为了把输入法编程的精髓真正学到手,要反复学习输入法编程的结构和算法,有些概念、算法最好要背熟,反复学习,反复运用。”[2] PS:这段话可以简化为:世上无难事,只要肯攀登。

关于在windows下学习输入法编程的资料搜集如下

1.输入法工作原理:http://blog.csdn.NET/shuilan0066/article/details/6883629

2.输入法漫谈:http://wenku.baidu.com/view/3d179422bcd126fff7050b9d.html(备注:原文发布在某论坛上,地址为:)

Linux下的输入法框架无非Scim/Ibus/Fcitx这三类。在网上搜了一通,仅发现一篇关于Scim工作原理的论文,ibusfcitx都是相应的google代码库,估计源码和文档都应该很全。个人感觉,用scim的人不如ibusfcitx多了,开发也不及后两者了。但是scim有这么一篇2页的论文,省去看ibus之类的文档和源码,而且既然同样都是输入法框架,总会存在一些共性的特征,相差不到那里去。

2. SCIM

基于XIM结构的输入法与X Windows系统结合过于紧密,无法支持国际化语言环境,SCIM就是针对这些问题的。Smart Common Input Method(SCIM)SCIM输入法架构的优越性: 1.支持语言多 2.面向对象设计 3.高度模块化,功能模块,插件形式 编程接口简单 5. 独立于图形界面,便于开发和移植

2.1. SCIM的结构设计

SCIM包含的模块:

  • l 配置模块(Config):配置模块最主要的功能就是为应用程序配置共享信息,以防冲突。所有的配置信息都由Config统一进行管理,其他模块存取配置信息都需要通过向它发送请求。SCIM的配置文件由Config文件和Global文件组成,前者保存的是与输入法相关的配置信息,输入法引擎、快捷键等参数,后者保存的是与输入法无关的配置信息,socket超时、socket地址等信息。SCIM中的配置模块有不同的实现,但这些实现都必须遵循ConfigBase接口规范
  • l 前端模块(FrontEnd):前端模块的主要功能是接受输入法服务进程中来自客户端应用程序的的请求,然后把请求转发给具体的输入法引擎或者配置模块,最后把处理的结果返回给刚才请求的客户端应用程序(在这里,输入法是作为一个服务进程来实现的)SCIM输入法前端也有不同的实现,这些实现都遵循FrontEndBase接口规范
  • l 引擎模块(IMEngine):输入法引擎的功能是实现具体的输入法。比如要实现全拼输入法,五笔输入法,首先需要实现一个输入法引擎,然后将其编译成一个动态库,放在指定的目录之中。所以其它输入法都是以单独的软件包发行的,不属于SCIM的核心。SCIM中的输入法引擎也有多种实现,这些实现都遵循IMEngineInstanceBase接口
  • l 进程间通信模块(IPC):SCIM采用的本地socket(参考*uixBSD Socket API,是一种进程间通信的方式)方式。应用层协议封装在Transaction中,它主要负责把特定的请求或事件等打包成数据包和从数据包中取出请求或事件。Socket实现数据的实际传输。服务器端使用SocketServer,客户端使用SocketClient。无论是服务器端还是客户端,Transaction并不关心,因为它使用的是Socket的抽象接口。
  • l 输入法Panel:对于输入法来说,Panel是不可缺少的一部分。Panel的主要功能是提供给用户比较直观的感受,如候选字,联想词组等,也提供了一些辅助功能,如全角半角切换,中英文切换、标点切换、查看帮助信息等。Panel是有图形用户界面的,且必须要与特定的GUI绑定起来,SCIM虽然实现了一个基于GTKPanel,但它是一个完全独立的工具,不管用哪一个GUI实现Panel,大部分代码都是相同或者相似的,只要稍做修改即可。SCIMPanel模块的代码封装在PanelAgentPanelClient这两个类中。前者实现所有和面板相关的socket协议,后者处理所有面板和前端的socket通信,前端用PanelClientPanel后台进程进行交互。
  • l Helper模块:SCIM把一些新的输入法方式,如手写输入法等进行特殊处理,通过Helper集成进来。SCIM提供了一个HelperAgent,这个类的功能是把手写输入法的结果提交给应用程序。

2.2. 工作原理

SCIM,输入法有三大功能模块:前端(FrontEnd),引擎(IMEngine)和后端(BackEnd)。这三大模块分别实现的功能是:

FrontEnd:主要负责用户界面的显示,以及与客户程序进行交互,将客户程序的按键请求转发给IMEngine,执行IMEngine发出的各种命令,如绘制预编辑字符串等等。FrontEndBase基类负责管理所有IMEngineInstance实例。

IMEngine:接收FrontEnd发送的按键事件,然后向FrontEnd发送相应的命令,如显示预编辑字符串、向客户程序提交字符串等等。

BackEnd:管理所有的IMEngine。如全拼、双拼、五笔、手写等。

SCIM中最关键的部分是FrontEndIMEngine。这两部分的实现及其之间的通讯方式(本地socketSCIM较其它输入法平台最不同的地方。

FrontEnd主要完成用户界面的绘制、与客户应用程序交互,FrontEndBase基类负责管理所有IMEngineInstance实例这三方面工作。每个IMEngineInstance 实例用唯一的id进行标识,FrontEndBase类提供的函数和IMEngineInstanceid完成对IMEngineInstance的所有操作。FrontEnd派生类不需直接处理IMEngineInstance实例或者其指针。

IMEngine分为IMEngineFactoryIMengineInstance两个接口类。要实现具体的输入法,比如纵横汉字输入法,就必须提供这两个类的具体实现。IMengineFactory除了负责管理如词库等的输入法涉及的公共数据,还负责创建IMEngineInstance实例。IMEngineInstance类则负责把实际的按键转换为字符串。

FrontEndIMEngine之间的数据通信采用了一种松散的接口形式,signal-slot技术,这样的方式可以简化编程接口。输入法引擎所需的所有动作都经由signal-slot发送给FrontEnd,而输入法引擎需要处理的事件则直接由FrontEnd调用IMEngineInstance类的相应函数来传递。

2.3. 吐槽

参考[4]论文中存在一些文字错误,重复以及术语的前后不一致,明显有整合和拼凑的感觉,看了一下其参考文献,百度文库和一篇csdn的博客赫然在列,让我对该中文期刊的质量表示怀疑,去看了一下引用的源后,果然是整合的,前半截+图来自文库,后半截来自博客,对于这种拼接法搞学术,表示强烈的鄙视,虽然我也经常干这种事情,但是我copy & paste的技术绝对比他们好,感觉有点五十步笑百步。

强烈建立参考原出处,原出处的文章具有一致性和连贯性,不是拼接的,而且更加图文并茂:

1.SCIM输入法架构分析(上)http://blog.csdn.Net/absurd/article/details/1151404

2.SCIM输入法架构分析(下)http://blog.csdn.net/absurd/article/details/1151524

3. IBUS

Intelligent Input BUS(iBus)是一个输入法平台,可以理解为是一个已经写好的与系统交互的库,各种输入法运行在iBus上。iBus曾经是Python写的,现在已经用C++重写,效率得到很大的提高,兼容性要远远强于ScimiBus可以让程序员专心地编写输入法功能的代码,而不是把过多的精力浪费在与系统交互上,从而增强输入法的可移植性。

Ibuswiki上的介绍:http://en.wikipedia.org/wiki/Intelligent_Input_Bus (百度百科上的介绍过时太久了)

IbusUbuntu系统上安装:http://wiki.ubuntu.org.cn/IBus

Ibus项目地址:http://code.google.com/p/ibus/

4. Fcitx

Fcitxwiki介绍:http://en.wikipedia.org/wiki/Fcitx

Fcitx还和搜狗合作开发Linux上的搜狗输入法,其在Ubuntu(Linux Mint)的安装参考http://1.techblog.sinaapp.com/?p=283#more-283

Fcitxgoogle项目地址:http://code.google.com/p/fcitx/

FcitxGitHub地址:https://github.com/fcitx/fcitx

Fcitx的安装:http://wiki.ubuntu.org.cn/Fcitx

5. Yong输入法

Yong输入法基于ibus的,但是又和Ibus上的输入法ibus-pinyin之类的不太一样。

输入法论坛及下载:http://yong.dgod.net/

安装可参考:http://blog.csdn.net/xiajian2010/article/details/9625131 

具体的,我也不是特明白其实现机制,先留给伏笔,等以后我明白了再加。

仔细查看了yong提供的帮助和相关的文档,发现在当前Home目录下的.yong中存在这样的一个文件pinyin.usr,打开后可以看到如下模式的文本:

{0}aihe 爱喝

{-}anquanjiang 安全将

{0}anshenye 安神液

{0}ansong 暗送

{0}aojiao 傲骄

{0}badi 拔地

{0}baiban 白板

{0}bailv 百虑

{0}baishikuai 百十块

{0}baiyou 柏油

{1}baiyou 柏由

{0}baiyulan 白玉兰

在看到有一些自己常用的输入后,想到yong的帮助提到的造词和删词的功能(快捷键操作太难用),估计就是通过在文件中设置{}中的数字实现,{-}表示删除,{0}{1}行为不太明白,在文件中添加新词表示造词。修改该文件后,发现并没有能禁止,将yong进程杀死,再重启就起效了,看来yong默认会将该文件加载到内存中去,回想上面介绍的输入法就是查表操作,这个pinyin.usr就是这样的表,除了这个表以外,还有个pinyin的主表是安装目录yong/mb/下的pinyin.txtpypre.bin,打开pinyin.txt后,其文件内容是:

name=ƴ??

key=abcdefghijklmnopqrstuvwxyz

len=63

wildcard=?

pinyin=1

split='

hint=0

user=pinyin.usr

assist=mb/yong.txt 2

code_a1=p..

[DATA]

a ?? ?? ?? ?? ?? ?? ߹

aba ????

adou ????

aduwu ??????

[DATA]目录下的字母对应的字符为不可识别,估计是对怕pypre.bin中的二进制的数据的索引,但又觉得不太像。

根据帮助文档的,将一些不需要的文件删除了,只剩下3M了,同样也能工作,感觉非常好。安装目录yong保留的文件如下:

yong目录下的:libl.so(基础库),libmb.so(码表库),yong(主程序),yong.conf(配置文件),yong.ini,yong-tool.sh(安装卸载脚本),yong.chm(帮助文档),keyboard.ini(软件盘配置文件),normal.txt

skin目录全部保留

mb目录只保留我需要的拼音输入法(pinyin.ini,pinyin.txt,pypre.bin)和english.txt

gtk-im目录下的动态连接库也需要保留,不然连输入框都不显示。

yong-config二进制程序也要保留,这个图形配置界面的程序。

备注:看到yong官方将输入法更新到了2.1,不清楚作者怎么设置版本号的,试用了一段时间,发现很不稳定,常常打字不上屏,害我要将进程杀死再重启,相当麻烦,我又退回1.7,用了很久,非常稳定。

后记

本来想好好的了解一下输入法,以及我使用的Yong输入法的,结果只是在搜集资料,了解一些很宽范的东西。在搜集资料的时候,看到一个关于学习方法的观点:重基础胜于技巧,看书胜于看杂志,看代码胜于看文章,和朋友讨论胜于上论坛,写BLOG胜于看BLOG,以及学习方法无好坏之分,只有合适不合适之分,所谓天下殊途同归,一致百虑

二手文献和原始文献之间,存在一条加工的沟,而且并不是所有的二手文献都比原始文献好。

Linux Mint的界面蛮好看的,有机会尝试一下。

参考文献

[1]SCIM输入法架构分析:http://wenku.baidu.com/view/a33b1484bceb19e8b8f6baea.html

[2]输入法漫谈:http://wenku.baidu.com/view/3d179422bcd126fff7050b9d.html

[3]关于Ibus拼音的开发:https://www.byvoid.com/blog/join-develop-ibus-pinyin/

[4]SCIM输入法架构及其工作原理,王丽君,李培峰,China Academic Journal Electronic Publishing House 

[5] 我的学习方法:http://blog.csdn.net/absurd/article/details/6475353



一、实现原理

1.中文输入法的组成

微软 Windows 系统中输入法由程序(DLL)名称为:*.ime文件和码表文件(字典)

名称为*.mb文件组成。


2.中文输入法的界面

http://wjy.hanwenhua.com/images/Projec6.gif


3.在 Windows 任务栏“EN图标”中增加输入法名称

这也就是 Setup 程序的关键,实际上,可以利用 Win32Api 函数 ImmInstallIME() 。

该函数的原形是:

HKL ImmInstallIME( LPCTSTR lpszIMEFileName, LPCTSTR lpszLayoutText);

前一个参数 lpszIMEFileName 是“.ime”文件的路径,Win9x 为“\System”下,

WinNT/2000为“\System32”下。后一个参数 lpszLayoutText  是输入法的明称。如 HKL MyIme=ImmInstallIME("Windows\\system\\3jaja.ime","3++输入法"); 

如果 MyIme 不返回 NULL ,则调用成功。


4.“.ime”文件的实现

程序中的输出函数(必须)即文件“.def”中的函数,以下为函数的名称和原形,部分函数给出了原函数,

希望对你能有所帮助。

 

Cpp代码   收藏代码
  1. //初始化输入法函数  
  2. BOOL WINAPI ImeInquire(LPIMEINFO lpImeInfo,LPTSTR lpszWndCls,DWORD lpszOptions)  
  3. {  
  4.     if (!lpImeInfo)  
  5.         return (FALSE);  
  6.     lpImeInfo->dwPrivateDataSize = sizeof(PRIVCONTEXT);  
  7.     lpImeInfo->fdwProperty = IME_PROP_KBD_CHAR_FIRST | IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_IGNORE_UPKEYS;  
  8.     lpImeInfo->fdwConversionCaps = IME_CMODE_NATIVE|IME_CMODE_NATIVE|IME_CMODE_FULLSHAPE|IME_CMODE_CHARCODE | IME_CMODE_SOFTKBD | IME_CMODE_NOCONVERSION;  
  9.     lpImeInfo->fdwSentenceCaps = 0;  
  10.     lpImeInfo->fdwUICaps = UI_CAP_ROT90 | UI_CAP_SOFTKBD;  
  11.     lpImeInfo->fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_MAKEREAD;  
  12.     lpImeInfo->fdwSelectCaps = (DWORD)0;  
  13.     lstrcpy(lpszWndCls, (LPTSTR)szUIClassName);  
  14.     return (TRUE);  
  15. }        
  16.   
  17. //自定义汉字编码格式,未作处理。  
  18. DWORD WINAPI ImeConversionList(HIMC hIMC,LPCTSTR lpszSrc,DWORD uBufLen,UINT uFlag)  
  19. {  
  20.     return (0);  
  21. }  
  22. //设置输入法状态函数,如光标跟随等  
  23. BOOL WINAPI ImeConfigure(HKL hKL,HWND hAppWnd,DWORD dwMode,LPVOID  lpData)  
  24. {  
  25.     switch (dwMode)   
  26.     {  
  27.     case IME_CONFIG_GENERAL:  
  28.         DialogBox(hInst, MAKEINTRESOURCE(SETIME), (HWND)hAppWnd, (DLGPROC)ImeSetDlgProc);  
  29.         break;  
  30.     default:  
  31.         return (FALSE);  
  32.         break;  
  33.     }  
  34.     return (TRUE);  
  35. }  
  36. //退出  
  37. BOOL WINAPI ImeDestroy(UINT uReserved)  
  38. {  
  39.     if (uReserved)   
  40.         return (FALSE);   
  41.     return (TRUE);  
  42. }   
  43.   
  44. //应用接口函数  
  45. LRESULT WINAPI ImeEscape(HIMC hIMC,UINT uSubFunc,LPVOID lpData)  
  46. {  
  47.     LRESULT lRet;  
  48.     switch (uSubFunc)   
  49.     {  
  50.     case IME_ESC_QUERY_SUPPORT:  
  51.         if ( lpData == NULL )  
  52.             return FALSE;  
  53.         switch (*(LPUINT)lpData)  
  54.         {  
  55.         case IME_ESC_QUERY_SUPPORT:  
  56.         case IME_ESC_MAX_KEY:  
  57.         case IME_ESC_IME_NAME:  
  58.         case IME_ESC_GETHELPFILENAME:  
  59.             return (TRUE);  
  60.         case IME_ESC_SEQUENCE_TO_INTERNAL:  
  61.         case IME_ESC_GET_EUDC_DICTIONARY:  
  62.         case IME_ESC_SET_EUDC_DICTIONARY:  
  63.         case IME_INPUTKEYTOSEQUENCE:  
  64.             return (FALSE);           
  65.         default:  
  66.             return (FALSE);  
  67.         }  
  68.         break;  
  69.     case IME_ESC_SEQUENCE_TO_INTERNAL:  
  70.     case IME_ESC_GET_EUDC_DICTIONARY:  
  71.     case IME_ESC_SET_EUDC_DICTIONARY:  
  72.     case IME_INPUTKEYTOSEQUENCE:  
  73.         return (FALSE);  
  74.     case IME_ESC_MAX_KEY:  
  75.         return ((WORD) 4);  
  76.     case IME_ESC_GETHELPFILENAME:  
  77.         {  
  78.             TCHAR szIMEGUDHlpName[MAXSTRLEN];  
  79.             if (lpData == NULL )  
  80.                 return FALSE;  
  81.             szIMEGUDHlpName[0] = 0;  
  82.             GetWindowsDirectory((LPTSTR)szIMEGUDHlpName, MAXSTRLEN);  
  83.             lstrcat((LPTSTR)szIMEGUDHlpName, TEXT("file://HELP//3JaJa.hlp"));  
  84.             lstrcpy((char*)lpData, (char*)szIMEGUDHlpName);  
  85.             return TRUE;  
  86.         }  
  87.     default:  
  88.         return (FALSE);  
  89.     }  
  90.     return (lRet);  
  91. }   
  92.   
  93. //启动输入法设置功能  
  94. BOOL WINAPI ImeSelect(HIMC hIMC,BOOL fSelect)  
  95. {  
  96.     LPINPUTCONTEXT lpIMC;  
  97.     BOOL           fRet;  
  98.     if (!hIMC)   
  99.         return (FALSE);  
  100.     lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);  
  101.     if (!lpIMC)   
  102.         return (FALSE);  
  103.     fRet = Select(hIMC, lpIMC, fSelect);  
  104.     ImmUnlockIMC(hIMC);  
  105.     return (fRet);  
  106. }  
  107. //设置输入活动状态  
  108. BOOL WINAPI ImeSetActiveContext(HIMC hIMC,BOOL fOn)  
  109. {  
  110.     if (fOn&&hIMC)   
  111.     {  
  112.         LPINPUTCONTEXT lpIMC;  
  113.         lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);  
  114.         if (!lpIMC)   
  115.             return (FALSE);  
  116.         InitContext(lpIMC);  
  117.         ImmUnlockIMC(hIMC);  
  118.     }  
  119.     return (TRUE);  
  120. }  
  121. //用户按键处理函数,例如中英文状态,返回 FALSE 为英文状态,按键不作处理。  
  122. BOOL WINAPI ImeProcessKey(HIMC hIMC,UINT uVirtKey,LPARAM lParam,CONST LPBYTE lpbKeyState);  
  123.   
  124. //输入法编码字符处理。  
  125. BOOL WINAPI ImeSetCompositionString(HIMC hIMC,DWORD dwIndex,LPVOID lpComp,DWORD dwCompLen,LPVOID lpRead,DWORD  dwReadLen);  
  126. //将用户按键转换为汉字编码  
  127. UINT WINAPI ImeToAsciiEx(UINT uVirtKey,UINT uScanCode,CONST LPBYTE lpbKeyState,LPTRANSMSGLIST lpTransBuf,UINT fuState,HIMC hIMC)             
  128.   
  129. //处理IME消息函数  
  130. BOOL WINAPI NotifyIME(HIMC hIMC,DWORD dwAction,DWORD dwIndex,DWORD dwValue);       
  131. //自定义汉字编码格式,未作处理。  
  132. BOOL WINAPI ImeRegisterWord(LPCTSTR lpszReading,DWORD dwStyle,LPCTSTR lpszString)  
  133. {  
  134.     return (FALSE);  
  135. }  
  136. //自定义汉字编码格式,未作处理。  
  137. BOOL WINAPI ImeUnregisterWord(LPCTSTR lpszReading,DWORD dwStyle,LPCTSTR lpszString)  
  138. {  
  139.     return (FALSE);  
  140. }  
  141. //自定义汉字编码格式,未作处理。  
  142. UINT WINAPI ImeGetRegisterWordStyle(UINT nItem,LPSTYLEBUF lpStyleBuf)  
  143. {  
  144.     return (FALSE);  
  145. }  
  146. //自定义汉字编码格式,未作处理。  
  147. UINT WINAPI ImeEnumRegisterWord(REGISTERWORDENUMPROC lpfnRegisterWordEnumProc,  
  148.                                 LPCTSTR lpszReading,DWORD dwStyle,LPCTSTR lpszString,LPVOID lpData)  
  149. {  
  150.     return (FALSE);  
  151. }  
  152. //UI窗口过程  
  153. LRESULT CALLBACK UIWndProc(HWND   hUIWnd,UINT   uMsg,WPARAM wParam,LPARAM lParam)  
  154. {  
  155.     switch (uMsg)   
  156.     {  
  157.     case WM_CREATE:  
  158.         CreateUIWindow(hUIWnd);  
  159.         break;  
  160.     case WM_DESTROY:  
  161.         DestroyUIWindow(hUIWnd);  
  162.         break;  
  163.     case WM_IME_STARTCOMPOSITION:  
  164.         ......  
  165.     }  
  166. }  
  167. //状态窗口过程  
  168. LRESULT CALLBACK StatusWndProc(HWND hStatusWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);  
  169. //编码窗口过程  
  170. LRESULT CALLBACK CompWndProc(HWND hCompWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);  
  171. //侯选汉字窗口过程  
  172. LRESULT CALLBACK CandWndProc(HWND hCandWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);  
 

5.头文件及链接文件

记得要定义 IMM32.h 及链接文件 IMM32.lib。至于有关“ .ime”程序中必须用到的结构,大家可参考 MSDN 中的有关文档。


二、常用函数

BOOL WINAPI ImmGenerateMessage( //将汉字串法发送到与当前输入法相关联的应用软件中

HIMC hIMC

);//成功为TRUE,否则为FALSE

LRESULT WINAPI ImmRequestMessage( //向应用程序发送WM_IME_REQUEST消息

HIMC hIMC, //与当前输入法相关联的应用软件的句柄

WPARAM wParam, //与WM_IME_REQUEST相关的wP

LPARAM lParam //与WM_IME_REQUEST相关的LP

);

LPINPUTCONTEXT WINAPI ImmLockIMC( //获取当前IMC的INPUTCONTEXT结构信息,增加IMC 计数器

HIMC hIMC

);//成功返回INPUTCONTEXT 结构指针,否则为NULL

BOOL WINAPI ImmUnlockIMC( //释放IMC计数器

HIMC hIMC );//返回:如果IMC计数器被减少到0了,返回FALSE,否则为TRUE.

注意:ImmLockIMC与ImmUnlockIMC必须成对出现,必须是相同的HIMC

HIMCC WINAPI ImmGetIMCLockCount( //取计数器值

HIMC hIMC );//如果成功返回HIMC的计数器值,否则为NULL.

HIMCC WINAPI ImmCreateIMCC( //创建INPUTCONTEXT结构的一个成员

DWORD dwSize //成员的缓冲区长度

);//如果成功返回IMC的成员句柄,否则为NULL

HIMCC WINAPI ImmDestroyIMCC( //删除IMC成员缓冲区

HIMCC hIMCC

);//如果成功返回NULL,否则等于该HIMCC.

LPVOID WINAPI ImmLockIMCC( //取IMCC缓冲地址,同时使IMCC的计数器值增加

HIMCC hIMCC );

BOOL WINAPI ImmUnlockIMCC( //递减IMCC计数器

HIMCC hIMCC );//如果IMCC的计数器值为零,则返回 FALSE,否则为TRUE.

10、HIMCC WINAPI ImmReSizeIMCC( //重新设置IMC的成员的缓冲区大小

HIMCC hIMCC, //IMC的成员句柄

DWORD dwSize //新缓冲区大小

);//如果成功,返回新的HIMCC,否则为 NULL.

DWORD WINAPI ImmGetIMCCSize( //取IMC成员的缓冲区大小

HIMCC hIMCC );//返回IMC成员的缓冲区大小

12、DWORD WINAPI ImmGetIMCCLockCount( //返回IMC计数器值

HIMCC hIMCC

);//成功返回该IMCC的计数器值,否则为0

BOOL WINAPI ImmGetHotKey( //取输入法状态键,该函数供控制面板使用

DWORD dwHotKeyID,

LPUINT lpuModifiers,

LPUINT lpuVKey,

LPHKL lphKL

)

BOOL WINAPI ImmSetHotKey( //设置输入法的热键

DWORD dwHotKeyID,

UINT uModifiers,

UINT uVKey,

hKL hKL

)

HWND WINAPI ImmCreateSoftKeyboard( //产生一个软键盘

UINT uType, //软件盘上的键码含义的定义方式

UINT hOwner, //该输入法的UI窗口

int x, //x坐标

int y //y坐标

);//成功返回软键盘的窗口句柄

BOOL WINAPI ImmDestroySoftKeyboard( //销毁软键盘

HWND hSoftKbdWnd //软键盘窗口句柄

);//成功为TRUE,法哦则为FALSE.

17、BOOL WINAPI ImmShowSoftKeyboard( //显示或隐藏软键盘

HWND hSoftKbdWnd, //软键盘窗口句柄

int nCmdShow //SW_HIDE=隐藏,SW_SHOWNOACTIVATE=显示

);//如构成功返回 TRUE. 否则为 FALSE.


二、 IME  文件中的常用结构

1、IMEINFOstruct tagIMEInfo { //输入法的接口信息

DWORD dwPrivateDataSize;//用户设计的数据结构的字节数 

DWORD fdwProperty; //输入法对键盘事件的相应特性

DWORD fdwConversionCaps;//当前输入法具有的功能特性,如有软键盘、标点、中西文切换等功能 

DWORD fdwSentenceCaps; 

DWORD fdwUICaps; // 用户界面能力:支持软键盘等

DWORD fdwSCSCaps; // 用户设置编码串的能力

DWORD fdwSelectCaps; // 输入法切换时是否使用以前输入法的模式

} IIMEINFO;

2、COMPOSITIONSTR 用于编码管理

typedef struct tagCOMPOSITIONSTR { 

DWORD dwSize; //当前编码信息需要的存储空间

DWORD dwCompReadAttrLen;   //读入的编码属性长度

DWORD dwCompReadAttrOffset; //读入的编码的位置

DWORD dwCompReadClsLen;     //读入的子串长度

DWORD dwCompReadClsOffset; //读入的子串的位置

DWORD dwCompReadStrLen;    //读入的编码长度

DWORD dwCompReadStrOffset; //读入的编码的位置

DWORD dwCompAttrLen; //编码属性长度

DWORD dwCompAttrOffset; //编码属性在内存的位置

DWORD dwCompClsLen; //编码子串长度

DWORD dwCompClsOffset; //编码子串在内存的位置

DWORD dwCompStrLen; //编码串长度

DWORD dwCompStrOffset; //编码串在内存的位置

DWORD dwCursorPos; //当前光标位置

DWORD dwDeltaStart; //被修改编码的位置

DWORD dwResultReadClsLen; //读入结果子串长度

DWORD dwResultReadClsOffset; //读入结果子串在内存的位置

DWORD dwResultReadStrLen; //读入的编码长度

DWORD dwResultReadStrOffset;  //读入的编码在内存的位置

DWORD dwResultClsLen; //结果子串长度

DWORD dwResultClsOffset; //结果子串在内存的位置

DWORD dwResultStrLen; //结果串长度

DWORD dwResultStrOffset; //结果串在内存的位置

DWORD dwPrivateSize; //用户自定义数据长度

DWORD dwPrivateOffset; //用户自定义数据在内存的位置

} COMPOSITIONSTR;

3、CANDIDATEINFO 用于编码选择管理

typedef struct tagCANDIDATEINFO { 

DWORD dwSize; //数据所占内存大小

DWORD dwCount; //数据个数

DWORD dwOffset[32]; //各个编码列表的内存位置

DWORD dwPrivateSize; //自定义数据尺寸

DWORD dwPrivateOffset; //缓冲区位置

} CANDIDATEINFO;

4、GUIDELINE

typedef struct tagGUIDELINE {

DWORD dwSize;

DWORD dwLevel;

DWORD dwIndex;

DWORD dwStrLen;

DWORD dwStrOffset;

DWORD dwPrivateSize;

DWORD dwPrivateOffset; 

} GUIDELINE;

5、CANDIDATELIST 编码选择列表信息

typedef struct tagCANDIDATELIST { 

DWORD dwSize; // 用字节表示的内存大小

DWORD dwStyle; // 列表串的取值方式

DWORD dwCount; // 当前列表个数

DWORD dwSelection; // 当前选择的列表序号

DWORD dwPageStart; // 在列表窗口中所显示的列表的起始序号

DWORD dwPageSize; // 一页显示的列表个数

DWORD dwOffset[]; // 列表数据存放区地址

} CANDIDATELIST;

6、COMPOSITIONFORM 窗口位置、大小信息:

typedef tagCOMPOSITIONFORM { 

DWORD dwStyle; //管理窗口方式

POINT ptCurrentPos; //给定坐标

RECT rcArea; 

}COMPOSITIONFORM;

7、CANDIDATEFORM 列表窗口信息

typedef tagCANDIDATEFORM { 

DWORD dwIndex; //列表窗口序号

DWORD dwStyle; //属性:

POINT ptCurrentPos; //坐标位置

REC rcArea; 

} CANDIDATEFORM;

12、INPUTCONTEXT IMC 数据存放区

typedef struct tagINPUTCONTEXT { 

HWND hWnd; //使用该IMC的窗口

BOOL fOpen; //IME的打开与关闭状态

POINT ptStatusWndPos; //状态窗口的位置

POINT ptSoftKbdPos; //软键盘的位置

DWORD fdwConversion; //IME状态(活动、不活动,全角等)

DWORD fdwSentence; //编码方式

union {

 LOGFONTA A;

 LOGFONTW W;

} lfFont; //字体

COMPOSITIONFORM cfCompForm; //编码格式结构

CANDIDATEFORM cfCandForm[4]; //列表选择结构

HIMCC hCompStr;HIMCC hCandInfo;

HIMCC hGuideLine

HIMCC hPrivate; 

DWORD dwNumMsgBuf; //存放在hMsgBuf中的消息数

HIMCC hMsgBuf; //存放的消息

DWORD fdwInit //系统根据此值来初始本结构相应的信息

DWORD dwReserve[3]; //未定义

} INPUTCONTEXT;       

  • 12
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现一个手写输入法,你需要了解以下几点: 1. 手写识别算法:手写识别算法可以使用深度学习模型,例如卷积神经网络(CNN)或循环神经网络(RNN),也可以使用传统的机器学习算法,例如支持向量机(SVM)或随机森林(Random Forest)。这个部分需要有一定的数学知识和机器学习经验。 2. 输入法框架:你需要使用一个输入法框架,例如Windows的IME或Android的输入法框架。这个部分需要了解操作系统的输入法架构和API,以及对应的框架的使用方法。 3. 用户界面:你需要实现一个用户界面,让用户可以输入手写的汉字。你可以使用C++的GUI库,例如Qt或MFC,来实现这个部分。 下面是一个简单的示例,演示如何在Windows平台使用C++和MFC实现一个简单的手写输入法。假设你已经有了一个手写识别模型,可以将手写输入转换为汉字字符串。 1. 首先,你需要创建一个MFC应用程序项目。在Visual Studio中,选择“新建项目”,然后选择“MFC应用程序”。 2. 在应用程序向导中,选择“单文档”应用程序类型,并勾选“使用MFC的ActiveX控件”选项。 3. 在“选项”对话框中,选择“ActiveX控件”选项卡,然后点击“添加”按钮。选择“Microsoft InkEdit Control 1.5”,然后点击“确定”按钮。 4. 在资源视图中,双击“IDD_HANDWRITING_DIALOG”对话框,打开对话框编辑器。 5. 在对话框编辑器中,拖拽“InkEdit Control”控件到对话框中。调整控件的大小和位置,使其适合你的需要。 6. 在类向导中,添加一个成员变量,类型为CInkEdit。这个变量将用于与InkEdit控件交互。 7. 重载对话框的OnInitDialog函数,在函数中获取InkEdit控件的指针,并设置一些控件属性。示例代码如下: ``` BOOL CHandwritingDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 获取InkEdit控件的指针 m_pInkEdit = (CInkEdit*)GetDlgItem(IDC_INKEDIT); // 设置控件属性 m_pInkEdit->EnableInkInput(); m_pInkEdit->SetAutoComplete(FALSE); m_pInkEdit->SetDefaultStrokes(); m_pInkEdit->SetDefaultColors(); return TRUE; // return TRUE unless you set the focus to a control } ``` 8. 在类中添加一个函数,用于将手写输入转换为汉字字符串。示例代码如下: ``` CString CHandwritingDlg::RecognizeHandwriting() { // 假设你已经有了一个手写识别模型 // 将手写输入转换为汉字字符串 CString strResult = Recognize(m_pInkEdit->GetInkData()); return strResult; } ``` 9. 在类中添加一个响应函数,用于处理“识别”按钮的点击事件。在函数中调用RecognizeHandwriting函数,并将结果显示在对话框中。示例代码如下: ``` void CHandwritingDlg::OnRecognize() { CString strResult = RecognizeHandwriting(); SetDlgItemText(IDC_RESULT, strResult); } ``` 10. 编译和运行程序,测试手写输入和识别的功能。 这只是一个简单的示例,实际实现中还需要处理更多的细节和异常情况。同时,你也需要根据操作系统和输入法框架的不同,做出相应的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值