`
freesoftman
  • 浏览: 318823 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

浅谈输入法编程

阅读更多

 首先我们需要明白输入法是什么东西。目前常用的输入法基本上有两种类型:外挂式(如早期的万能五笔)及输入法接口式(Input Method Editor-IME)。外挂式比较简单,就是一个exe文件,通过模拟一些Windows输入消息来给当前处于活动状态的编辑窗口输入文字,一个显著的优点是输入法只要启动一次,就可以在所有进程中使用;但缺点不不容忽视,首先实现起来也不容易,一个更大的不足是兼容性不够好,通常一个Windows版本需要一人对应的输入法版本,此外这类输入法为了能够截获用户输入,通常需要挂接键盘钩子,容易造成系统不稳定或者效率不高。大部分的输入法还是采用IME来实现,下面本文主要讨论一下IME编程需要注意的问题及解决办法。

  IME是什么?IME是在Windows平台上使用的标准的输入法接口规范。它实质是一个DLL,Windows为这个DLL定义一系列的接口,不同的接口实现指定的功能。程序员在编写输入法程序时只需要实现这些接口并导出就可以作为输入法使用。关于具体接口的定义不是本文的重点,如果您需要了解只需要在网络中搜索“输入法编程指南”就可以明白 ,更多信息参考MSDN。

  刚开始输入法编程最棘手的问题通常是程序框架搭好了却不知道如何使用及调试。这里涉及到一个很重要的问题就是输入法的安装。输入法就是Windows的一个插件,需要先进行注册,Windows才能识别并使用。为此您需要先将您生成的DLL复制到系统目录(WindowsSystem32)再调用API ImmInstallIME就可以实现了,在我的实践中是先编一个简单的程序来做安装工作,在每次输入法重新编译完成以后调用一次以完成输入法的注册。这里还有一个需要注意的问题是:Windows提供了一种机制,它允许输入法程序一旦启动就就不再退出,这就意味着如果你的程序代码经过修改需要重新安装时将不得不重新启动电脑。在IME定义的接口中有一个接口是提供IME的初始化的,它就是BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo,LPTSTR lpszUIClass,LPCTSTR lpszOption),下面的代码来自我写的输入法:

BOOL WINAPI ImeInquire(LPIMEINFO
lpIMEInfo,LPTSTR lpszUIClass,LPCTSTR lpszOption)
{
  lpIMEInfo->dwPrivateDataSize = sizeof(CONTEXTPRIV);//系统根据它为INPUTCONTEXT.hPrivate分配空间
  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,CLSNAME_UI);
  return TRUE;
}

  lpIMEInfo->fdwProperty告诉Windows系统您编写的输入法的一些特征,注意一下IME_PROP_END_UNLOAD这个标志,有了它您编写的输入法会随着启动您的输入法的应用程序(如NotePad)的退出而退出,否则它将长驻于系统中,这也是为什么很多输入法在升级安装时需要首先重新启动电脑的原因。

  在这个接口中还有一点需要特别注意,那就是lpIMEInfo->dwPrivateDataSize,至少我是经过很多次测试才基本证实Windows根据该值为INPUTCONTEXT.hPrivate分配空间。此外如果您修改了这个接口,按照我个人的经验是需要重新调用ImmInstallIME来安装。

  在安装完成后,在输入法列表中应该已经有了您自己的输入法。点击调试,由于它是一个DLL,您需要先选择一个宿主程序,一般选择“记事本”,以调试方式启动“记事本”后,在这个“记事本”中打开您的输入法,您就可以在源代码中设置断点了。需要说明的是,VC6.0调试DLL不太好用,首先需要打上SP5或者SP6,这样也不能够在DLL启动的时候就设置断点,推荐使用.net来调试。

输入法上下文(HIMC):HIMC是什么?在输入法编程时必然要接触到输入法上下文这个术语,刚接触时听起来实在是半懂不懂。由于输入法是一个插件,它需要和调用它的应用程序通讯,在输入法中生成的编码及重码信息保存在哪里应用程序才能正确的读取呢?答案就在于输入法上下文。输入法上下文是由User.exe(一个系统进程)为应用程序分配的内存句柄,在应用程序中启动的输入法在这块内存中写入数据,User.exe再将数据传递到应用程序。

  UIWnd:在IME中需要导出一个接口,原型如LRESULT WINAPI UIWndProc(HWND hUIWnd, UINT message,WPARAM wParam, LPARAM lParam),hUIWnd是由User.exe传过来的窗口句柄,它是输入法中创建的窗口如编码窗口,重码窗口,状态栏窗口的宿主(Owner),初学输入法编程的人可能会问这个窗口显示在哪里呢?其实它并不是一个普通的窗口,它只是一个用来传递Windows消息的窗口(Message Only),在使用时,您不需要关心它在哪里,只需要使用它就好了。

  一个IME需要导出19个(Win98版本)接口,但是对于一个只需要实现一般意义的文字输入的软件,您只需要实现几个基本的接口就可以让输入法正常工作了。下面逐一介绍一下这几个接口。

/**********************************************************************/
/* ImeSelect() */
/* Return Value: */
/* TRUE - successful, FALSE - failure */
/**********************************************************************/
BOOL WINAPI ImeSelect(HIMC hIMC,BOOL fSelect)
在这个接口中,系统通知输入法当前是否打开了输入法输入。一般输入法启动时会调用一次,在一些软件(如EmEditor)中提供打开与关闭输入法的功能就是通过这个接口实现的。如果打开输入法,一般会在这个接口中做一些数据的初始化工作。/***********************************************************************/
/*系统调用这个接口来判断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)

观察注释您可以看到在个接口是用来判断用户敲击的哪个键需要处理,哪个键又应该交给系统自己处理,如果输入法需要自己处理用户输入的键,则在这个接口中返回true,否则返回false。/****************************************************************************************************************/
/* function:应用程序调用这个接口来进行输入上下文的转换,输入法程序在这个接口中转换用户的输入 */
/* 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)
这个接口可以说是输入法最重要的部分,程序员需要在这个接口中实现编码与重码的转换,转换完成或者显示在编码窗口及重码窗口,或者发送到应用程序。由于在这个接口中没有传入窗口句柄,如果通知输入法程序的窗口更新显示呢?当然我们可以使用全局变量,在此我个人推荐的方法是使用IME消息(没有什么道理),您将消息类型、参数保存到lpdwTransKey指示的缓冲区中,User.exe会根据消息类型做相应的处理并传递到UIWnd这个窗口中。

  那么如何输入文字呢?要输入文字需要3个消息配合使用,分别是WM_IME_STARTCOMPOSITION、WM_IME_COMPOSITION和WM_IME_ENDCOMPOSITION,它们分别指示开始输入编码,输入编码或者结果(视参数而异)及编码输入完成。在开始编写输入法的时候,为了省事,我的输入法在用户确定要输入一个重码时才连续调用这3个消息以向编码器中输入文字。由于WM_IME_STARTCOMPOSITION和WM_IME_ENDCOMPOSITION需要成对使用,这种方法可以确保它们配对。最初这种方式工作得很好,但是后来发现在一些软件中出现兼容性问题。如“智能五笔”在“遨游”中就存在这个问题,在“遨游”中的地址栏中打开“智能五笔”,当需要使用回退键来删除错误输入的编码时,会发现删除的不是编码窗口中的编码而是编辑器中的文字。这是因为类似“遨游”这类软件主动接管了按键输入如处理一些控制键,当它发现这些控制键不在WM_IME_STARTCOMPOSITION和WM_IME_ENDCOMPOSITION这两个消息之间时就自己处理控制键而不是先交给User.exe了。因此正确的流程应该是在开始输入编码时发送WM_IME_STARTCOMPOSITION,输入结束后发送WM_IME_ENDCOMPOSITION消息。

/**********************************************************************/
/* UIWndProc() */
/* IME UI window procedure */
/**********************************************************************/
LRESULT WINAPI UIWndProc(HWND hUIWnd, UINT message,WPARAM wParam, LPARAM lParam)
这是一个非常重要的接口,基本上一它负责各种消息的传递。一般您需要在这个接口中根据不同的消息类型,实现输入法窗口(如编码窗口、重码窗口、状态栏窗口)的显示、隐藏及更新等操作。这个接口实现的功能可能非常复杂,视情况而异,在此就不做更加深入的说明了。在使用时可以参见示例工程。BOOL WINAPI ImeConfigure(HKL hKL,HWND hWnd, DWORD dwMode, LPVOID lpData)这是最后一个需要注意的接口,在显示输入法属性配置时会Windows会调用这个接口。

  基本的接口就介绍到这里,下面谈一谈我个人在编写输入法程序时遇到的一些问题或者发现的一些需要注意的地方。

  1、关于输入法窗口:阅读一些输入法的代码会奇怪,为什么输入法窗口在创建时需要指定WM_DISABLE属性呢?原来是因为如果不指定这个属性标志,在打开输入法时,会导致当前的应用程序失去输入焦点。但是指定了这个标志后,输入法窗口不能收到鼠标消息怎么办?解决的方法就在于WM_SETCURSOR这个消息。这个消息不管窗口是否可用,只要有鼠标在窗口内窗口都会收到。您可以在这个消息中模拟鼠标消息也可以选择调用SetCapture这个函数,这样窗口就可以收到鼠标消息了。

  2、关于窗口模式:使用了几种输入法后,你会发现,有的输入法的编码窗口和重码窗口是一个窗口,有的又是两个窗口,它们有什么区别?或者有的人会觉得这个问题很可笑,但是当您研究了一段输入法可能就会发现您也有类似的问题:因为在输入法的导出接口中关于用户界面的函数就有4个,其它3个分类对应3个窗口回调函数。事实上它们并没有本质的区别,关键在于您的输入法的使用范围。一些软件(如某些游戏)为了界面的整体美观,不希望用户在打开输入法时显示输入法自己的窗口,而是希望输入法按照它的意愿将输入法窗口需要显示的内容显示在它创建的窗口中,英文称之为IME Aware。由于我自己的输入法目标不是在游戏中使用,所在并没有按照这个规矩来管理输入法窗口,而是为了简化,将编码窗口和重码窗口显示的内容放到了一个窗口中。

3、关于自定义消息:UIWndProc在WM_IME_NOTIFY中提供了一个IMN_PRIVATE,最初我理解为这个消息应该和WM_USER一样,当我需要不只一人自定义消息时只需要在这个ID的基础上增加值就好了。但事实是您定义的值可能是系统已经占用的(视Windows的版本而异),您能够使用的自定义消息应该只有这一个,为了指示多个消息类型,我使用的方法是在WM_IME_NOTIFY的LPARAM中进行区分。

  4、调试信息输出:一般编写输入法都不会使用MFC,为了输出调试信息,一般只能使用OutputDebugString这个API,在示例代码中的helper.c中我编写了一个模拟TRACE的函数Helper_Trace,您可以用这个函数来将调试信息输出到调试窗口。

  5、最后再谈一谈输入法类型:前面提到输入法分为外挂式和IME两种,但是目前一些输入法发展了第3种类型,那就是结合这两种类型的优点。例如拼音加加,启动拼音加加您会发现进程列表里会多一个拼音加加的服务进程,其实它才是拼音加加输入法的内核即数据处理部分。拼音加加的IME部分只是一个外壳,它提供传统的IME输入法一样的系统兼容性。在我的输入法中也采用了这种结构,使用内存文件映射及普通的Windows消息结合来实现两个进程间的通讯。您可以在我的输入法的源代码中找到进程间的通讯源代码及输入法代码。

  好象没有更多的经验可言,总之,输入法其实并不神秘,在我看来,只要能够在VC中跟踪代码,我就不相信我会搞不定它!

  一家之言,如果有什么错误,还请大家批评指正。

  关于示例代码:示例代码是我编写的一个最基本的输入法程序的框架,它显示您输入的编码,并显示一个固定的重码,输入空格后实现该重码上屏的功能。通常我们能找到的代码是一个完整的工程,这样对于初学输入法编程的人可能会陷入大量的非输入法编码框架的阅读中,对于实际的输入法编程并没有多大的意义。这份代码就是为了让您摆脱那些无谓代码的阅读。

 

  如果需要更加完整的输入法代码,推荐参考“自由拼音”的源代码,当然也可以参考我写的输入法《启程输入之星》的代码,您可以在我的网站上找到下载,http://www.setoutsoft.cn。这份代码的界面部分我自认为是当前的输入法中写得非常出色的。

 

原文来自于http://tech.ddvip.com/2008-01/120025624240333.html

<!-- 分页 --><!-- 分页 -->
<!-- 分页 --><!-- 分页 -->
分享到:
评论

相关推荐

    输入法编程指南 输入法编程指南 输入法编程指南 输入法编程指南

    输入法编程指南 输入法编程指南 输入法编程指南

    自由拼音输入法源码.rar

    在信息技术领域,编程是一项至关重要的技能,而深入理解输入法的开发更是提升技术水平的重要途径。本文将基于“自由拼音输入法源码.rar”这个压缩包文件,探讨其中涉及的VC(Visual C++)开发技术和自由拼音输入法的...

    路路通输入法编程指南

    【路路通输入法编程指南】是一篇针对输入法编程的教程文章,旨在帮助读者理解输入法的工作机制和编程过程。作者以个人经历为切入点,表明即使没有编程基础也能通过学习掌握输入法编程。 首先,文章指出输入法编程...

    WINDOWS下输入法编程

    WINDOWS下输入法编程 WINDOWS下输入法编程是指在WINDOWS操作系统下开发输入法相关的程序,涉及到各种WINDOW消息及其对应的处理方法。本章节将详细讲解WINDOWS下输入法编程的知识点。 一、WM_IME_SETCONTEXT消息 ...

    输入法编程指南

    输入法编程指南

    输入法编程指南.pdf

    ### 输入法编程指南知识点概述 #### 一、Windows95中的IME架构与功能增强 - **IME概述**:IME(Input Method Editor)即输入法编辑器,在Windows95中表现为一个动态链接库(DLL),其功能相较于Windows 3.1版本有...

    Windows IME 输入法编程

    Windows IME编程涉及到创建自定义的输入法,以满足特定的输入需求或者提供更高效的输入体验。以下是对这个主题的详细讲解: 1. **IME架构** - Windows IME系统由多个组件组成,包括IME核心、候选窗口、转换引擎和...

    输入法编程指南 创建自己的输入法

    【输入法编程指南 创建自己的输入法】 在计算机操作系统中,输入法引擎(Input Method Editor,IME)是用于在多种语言环境中输入文字的关键组件。本文档主要聚焦于Windows 95时代的IME编程,虽然技术可能已经过时,...

    输入法编程

    标题中的“输入法编程”指的是在计算机软件开发中创建自定义输入法的过程。这涉及到对操作系统内核级接口的理解,尤其是与输入法引擎(IME,Input Method Editor)相关的部分。IME是操作系统提供的一种服务,允许...

    输入法编程开发文档详细资料另一份

    输入法编程是一个涉及计算机科学、人机交互以及自然语言处理的复杂领域,它涉及到如何将用户的键入转化为准确、高效的文本输出。这份“输入法编程开发文档详细资料”显然是一个宝贵的资源,对于初学者或者想要深入...

    delphi 的输入法编程检测输入法

    在Delphi编程环境中,开发与输入法相关的应用是一项常见的任务,尤其在创建打字比赛软件或其他需要精细控制用户输入的程序时。输入法(IME,Input Method Editor)是操作系统提供的一种服务,允许用户通过非拉丁字符...

    IME.zip_ime_切换输入法_输入法 _输入法编程

    本文将深入探讨“IME.zip_ime_切换输入法_输入法_输入法编程”这个主题,介绍如何通过编程实现在输入框获取和失去焦点时自动切换输入法。 首先,我们需要了解输入法的切换机制。在Windows系统中,输入法切换通常是...

    IME输入法编程[文].pdf

    IME 输入法编程 IME 输入法编程是指在 Windows 操作系统中,开发者可以使用 IME(Input Method Editor)来实现输入法的编程。IME 输入法编程主要用于实现输入法的输入、编辑和显示。 在 Windows 操作系统中,IME ...

    程序员专用 编程输入法

    标题中的“程序员专用编程输入法”指的是专门为程序员设计的特殊输入法工具,旨在提升编码效率。这类输入法通常具有以下特点: 1. **快捷代码输入**:程序员输入法能够通过快捷键、组合键或者自定义短语来快速输入...

    输入法编程 教材 input method program

    输入法编程 教材 input method program scarcely there are someone do input method programming,so scarecely there are primary programmers know the way it works

    win32输入法编程方法

    ### Win32输入法编程方法 #### 概述 本文主要介绍了基于Windows 32位操作系统API的输入法编程方法,特别关注了与输入法相关的API函数及其详细用法。通过对这些API函数的理解和运用,可以实现对输入法的一些高级控制...

    Windows IME输入法编程.rar

    Windows IME输入法编程涉及到创建、修改或扩展内置的输入法系统,以满足特定的语言输入需求。这个"Windows IME输入法编程.rar"压缩包可能包含了关于开发自定义输入法的相关资源和教程。 在Windows平台上,IME主要...

    Visual C++编程控制输入法

    本代码实现了在Windows应用程序中动态的控制输入法的技术。在DELPHI中许多控件都有控制输入法的属性,用户在设计时只要设置好这个属性就可以了,但在VC中并不直接提供对输入法的控制...对输入法编程有很好的参考价值。

    小键盘的编码---输入法编程的软键盘

    在IT领域,输入法编程是计算机软件开发的一个重要分支,主要关注如何设计和实现用户友好的文本输入方式。本文将详细探讨“小键盘的编码”这一主题,它在输入法编程中的软键盘应用。 小键盘,通常位于计算机主键盘的...

Global site tag (gtag.js) - Google Analytics