缘起

在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工作原理的论文,ibus和fcitx都是相应的google代码库,估计源码和文档都应该很全。个人感觉,用scim的人不如ibus和fcitx多了,开发也不及后两者了。但是scim有这么一篇2页的论文,省去看ibus之类的文档和源码,而且既然同样都是输入法框架,总会存在一些共性的特征,相差不到那里去。

2. SCIM

基于XIM结构的输入法与X Windows系统结合过于紧密,无法支持国际化语言环境,SCIM就是针对这些问题的。Smart Common Input Method(SCIM),SCIM输入法架构的优越性: 1.支持语言多 2.面向对象设计 3.高度模块化,功能模块,插件形式 4 编程接口简单 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(参考*uix中BSD Socket API,是一种进程间通信的方式)方式。应用层协议封装在Transaction中,它主要负责把特定的请求或事件等打包成数据包和从数据包中取出请求或事件。Socket实现数据的实际传输。服务器端使用SocketServer,客户端使用SocketClient。无论是服务器端还是客户端,Transaction并不关心,因为它使用的是Socket的抽象接口。
  • l 输入法Panel:对于输入法来说,Panel是不可缺少的一部分。Panel的主要功能是提供给用户比较直观的感受,如候选字,联想词组等,也提供了一些辅助功能,如全角半角切换,中英文切换、标点切换、查看帮助信息等。Panel是有图形用户界面的,且必须要与特定的GUI绑定起来,SCIM虽然实现了一个基于GTK的Panel,但它是一个完全独立的工具,不管用哪一个GUI实现Panel,大部分代码都是相同或者相似的,只要稍做修改即可。SCIM把Panel模块的代码封装在PanelAgent和PanelClient这两个类中。前者实现所有和面板相关的socket协议,后者处理所有面板和前端的socket通信,前端用PanelClient来Panel后台进程进行交互。
  • l Helper模块:SCIM把一些新的输入法方式,如手写输入法等进行特殊处理,通过Helper集成进来。SCIM提供了一个HelperAgent类,这个类的功能是把手写输入法的结果提交给应用程序。

2.2. 工作原理

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

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

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

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

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

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

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

FrontEnd和IMEngine之间的数据通信采用了一种松散的接口形式,即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++重写,效率得到很大的提高,兼容性要远远强于Scim。iBus可以让程序员专心地编写输入法功能的代码,而不是把过多的精力浪费在与系统交互上,从而增强输入法的可移植性。

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

Ibus在Ubuntu系统上安装:http://wiki.ubuntu.org.cn/IBus

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

4. Fcitx

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

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

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

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

输入法(IME)实现原理相关推荐

  1. 拼音输入法的数学原理

    拼音输入法的数学原理 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 引言 过去的25年里,中文输入法经历从自然音节编码输入,到偏旁笔画拆字输入,再回归自然音 ...

  2. 数学之美11--拼音输入法的数学原理

    第21章 拼音输入法的数学原理 1.输入法与编码: a.对汉字的编码分为两个部分: i.对拼音的编码 ii.对消除歧义的编码 b.拼音输入法的优势: i.不需要专门的学习 ii.输入自然,不会中断思维 ...

  3. 关于输入法的工作原理及编程

    缘起 在windows下使用的sougou输入法,除了经常弹出广告,没有什么异常行为,Linux下的中文输入法似乎没那么强大好用了.起初用的是Ibus中的pinyin,后来因为机缘巧合装了yong输入 ...

  4. 《数学之美》第21章 拼音输入法的数学原理

    1 输入法和编码 将一个方块形状的汉字输入到计算机中,本质上是一个将人为约定的信息记录编码--汉字,转换成计算机约定的编码(国际码或者UTF-8)的信息转换过程. 对汉字的编码分为两部分:对拼音的编码 ...

  5. XP日文输入法IME/文件打包下载

    WINXP的日语输入法不正常的解决方法及相关文件下载 问题:如果你的WINXP的日语输入法不正常:安装好微软自带的输入法后,只能输入英文无法输入日文,也是只见光标在闪怎么按罗马音敲键盘就是没反映, 问 ...

  6. 微信表情与输入法无缝切换(原理篇)

    对于做过表情与输入法切换的就应该知道,它们之间正常切换的话会存在对话框掉下去的情况.如果不知道可以去对比易信的表情界面与输入法的切换,跟微信的表情界面与输入法切换的区别,明显效果不可同日而语. 废话不 ...

  7. 数学之美:拼音输入法的数学原理

    现代输入法大多首选拼音输入法,其原因便是拼音符合人的认知规律,尽管需要多输入几个字母,但速度并不慢.它不需要专门去学习,输入时不用中断思维去考虑,而且对于前后鼻音具有很好的冗余性. 而拼音转汉字的算法 ...

  8. 《数学之美》第二十一章——拼音输入法的数学原理

    提高输入法的效率主要在于两方面: 输入汉字的平均击键次数接近理论上的最小值 寻找一个键的时间不要太长 1. 输入法与编码 讨论了全拼输入法对比于双拼输入法的优势,主要在于双拼输入法有更多的歧义性,虽然 ...

  9. 九宫格手机输入法分类和原理

    除了英文输入法外用在手机中的中文输入法目前主要有:T9智能输入法.字能输入法.iTAP输入法等等. T9输入法 以输入字符高效快捷著称,T9输入法全名为智能输入法,字库容量九千多字,支持十多种语言,包 ...

最新文章

  1. python as_matrix()
  2. 基于HOG-LBP特征融合的头肩检测研究
  3. 【C 语言】数组 ( 数组取值操作 | array[i][j] 用法 等价于 *( *(array = i) + j ) 用法 | 下标操作到指针操作演化过程 )
  4. python3练习-装饰器
  5. 推荐一个 Service Mesh 专栏
  6. LVS在淘宝环境中的应用
  7. MATLAB求线性代数的参数范围,MATLAB科学计算04(线性代数问题求解一)
  8. 【SpringCloud】 spring cloud 2021年 技术 更新换代 停更 等信息
  9. 三、 redis进阶篇
  10. python django+bootstrap4+mysql智慧交通系统构建
  11. sql2008表支持多少列_数据库表分区是怎么回事?
  12. ESP32开发 2.添加.c.h并修改CMakeLists,来定制自己的工程
  13. ps|grep命令详解
  14. Latex中使用实心圆点列表
  15. Handler execution resulted in exception: Content type 'application/json;charset=UTF-8' not supported
  16. 啊哈添柴挑战Java1827. 顺序输出(难)
  17. python虚函数_虚函数和纯虚函数的区别
  18. elastica php yii,Yii 1.1.*集成elasticsearch php 客户端Elastica
  19. 不,压缩包密码忘记(没有网盘提取码)你还有希望
  20. 5G、AIOT趋势下,带给安防产业的影响与发展机遇

热门文章

  1. 关于微软学术搜索项目
  2. 微软学术搜索的新功能设想:用户账户系统——史经浩
  3. Cent OS几个好玩的命令
  4. 嵌入式Linux中间件,高可用性(HA)和嵌入式管理中间件:Enea Element详解
  5. 爬取正方教务管理系统获取学生信息
  6. linux视频日记软件下载,Linux(Ubunt)使用日记------常用软件汇总(不定时更新)
  7. 电脑搜索不到部分wifi,搜索不到部分2.4G频率的wif,手机开热点电脑搜不到wifi。
  8. python 卡方检验批量筛选_卡方检验(python代码实现)
  9. dhcp服务器显示dns服务器更新挂起,如何动态更新DNS记录
  10. Qt利用深度优先搜索实现迷宫寻宝