文本输入框作为一个最基本的UI控件,被众多UI框架默认支持。Windows下最简单的就是CEditWTL封装),也有更为复杂的CRichEditWTL封装)。文本输入框是基本控件中最难实现的控件之一,估计这也是Chrome浏览器For Windows一直使用原生文本输入框封装,而不是自行实现的原因。很多情况下,默认的输入框足够使用,对于一些简单的限制(例如只能输入数字)也可以通过对原生控件进一步封装实现。不过如果要深度定制就估计只有自行实现了。

如果只要求支持ASCII吗,那就轻而易举了,直接监听WM_CHARFor Windows消息即可。不过这个世界语言太多了,作为一个红朝子民,至少得支持汉语吧!可是汉语并不能通过监听WM_CHAR获得。所以下面就来探讨一下Windows操作系统下中文的输入。

目前主流操作系统都是默认支持ASCII输入,而对于其他语言的输入则必须借助输入法来实现。为了支持多语言输入,主流的UI框架都会集成输入法嵌入的支持模块,而对输入法的友好程度也反映了该UI框架的水平。估计很多Linux用户Gnome环境)以前会很烦在用QT实现的Opera浏览器中用输入法。

Windows的输入框架在这里(http://blog.csdn.net/dengting/archive/2002/08/17/14638.aspx )讲的比较详细,对于其他语言的输入,Windows并不是通过WM_CHAR消息传递,而是通过输入法发出的WM_IME_CHAR消息提供,所以我们可以监听这个消息来获得相应的汉字。不过在发送WM_IME_CHAR消息之前,Windows会先发送WM_IME_COMPOSITION消息。大概流程如下:

  1. 用户按下键盘,Windows发送WM_CHAR

  2. 如果当前使用输入法输入,则让输入法重组,获得第三方语言字符(串)

  3. 输入法发送WM_IME_COMPOSITION、WM_IME_CHAR等消息。UI控件截取这些消息,获得汉字。

Windows并不会同时发送WM_IME_COMPOSITION、WM_IME_CHAR和WM_CHAR消息,而是依次发送。首先会发送WM_IME_COMPOSITION消息,UI控件可以通过ImmGetCompositionString函数获得输入的字符串。如果希望继续发送后续消息,则调用Windows默认的处理函数。此时Windows会接着发送WM_IME_CHAR消息,UI控件可以通过它的参数获得单个中文字符,如果希望继续发送后续消息,则调用Windows默认的处理函数。此时Windows会接着发送WM_CHAR消息。很显然,只有经的上层同意,Windows才会发送后续的消息。

既然对于ASCII输入和中文输入需要监听不同的事件,那就必须有一个办法判断用户当前是使用英文键盘还是使用输入法输入。这可以通过Windows API函数ImmIsIME判断。此外关于中英文切换摘录了一段如下:

http://topic.csdn.net/t/20020509/02/707135.html 】

中英文输入法切换?    
function   boolean   ImmSimulateHotKey   (UnsignedLong   hWnd,   
UnsingedLong   dwHotKeyID)   library   “IMM32.dll“   
function   unsignedlong   GetKeyboardLayout   (unsignedlongwLayout)library   “user32.dll“   
function   boolean   ImmIsIME(unsignedLong   hklKeyboardLayout)library   “IMM32.DLL“

英文输入法切换

constant int IME_THotKey_IME_NonIME_Toggle=112uint hklCurrentunsignedlong hnd hklCurrent = GetKeyboardLayout(0)if ImmIsIME(hklCurrent) then hnd = Handle(parent) ImmSimulateHotKey(hnd,IME_THotKey_IME_NonIME_Toggle)end if

中文输入法切换

constant int IME_THotKey_IME_NonIME_Toggle=112uint hklCurrentunsignedlong hnd hklCurrent = GetKeyboardLayout(0)if not ImmIsIME(hklCurrent) then hnd = Handle(parent) ImmSimulateHotKey(hnd,IME_THotKey_IME_NonIME_Toggle)end if

写了一个sample练练笔(WTL实现),有兴趣可以瞄瞄,有什么错误欢迎指正。支持ascii和unicode,不过只支持ascii可见字符、汉字和退格键,没有实现光标,方向键等。此外该sample同时处理了WM_IME_COMPOSITION消息和WM_IME_CHAR消息,所以用户输入一个汉字串后会显示两个相同的,实际应用应该选择其中之一。考虑到ascii方式汉字占用字节和英文字符不一样,所以退格键需要特殊处理,见源码。

使用方式示例:

InputStatic istatic_istatic_.Create(m_hWnd,CRect(0,250,500,400),NULL,WS_CHILD | WS_VISIBLE);istatic_.SetFocus();

之所以要SetFocus,是因为sample使用CStatic,该控件无法获得焦点。(注意:该sample仅仅只是一个测试代码,不要拿来和真正文本输入框对比)

(InputStatic.h)

/** Author: 邱金武<qiujinwu456@gmail.com>*/#ifndef INPUT_STATIC__H_#define INPUT_STATIC__H_#include <string>#include <atlbase.h>#include "atlapp.h"#include <atlwin.h>#include "atlctrls.h"#ifndef _UNICODEtypedef std::string ISString;#elsetypedef std::wstring ISString;#endifclass InputStatic: public CWindowImpl< InputStatic ,CStatic>{public: InputStatic(const ISString & initStr = _T("")); BEGIN_MSG_MAP( NormalButton ) MESSAGE_HANDLER(WM_CHAR, OnChar) MESSAGE_HANDLER(WM_IME_CHAR, OnImeChar) MESSAGE_HANDLER(WM_IME_COMPOSITION, OnImeCompositionChar) END_MSG_MAP()private: LRESULT OnChar(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); LRESULT OnImeChar(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled); LRESULT OnImeCompositionChar(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/,BOOL& bHandled); ISString m_content_;};#endif

(InputStatic.cpp)

/** Author: 邱金武<qiujinwu456@gmail.com>*/#include "InputStatic.h"#ifndef _UNICODE//见: http://blog.chinaunix.net/u2/70445/showart_1133335.htmlstatic bool endWithGBCode(const std::string & strIn){ unsigned char ch1; unsigned char ch2; if (strIn.size() >= 2) { ch1 = (unsigned char)strIn.at(strIn.size() - 1); ch2 = (unsigned char)strIn.at(strIn.size() - 2);#ifdef USE_GB2312 //GB2312 if (ch1>=176 && ch1<=247 && ch2>=160 && ch2<=254)#else //GBK if (ch1>=129 && ch1<=254 && ch2>=64 && ch2<=254)#endif return true; else return false; } else return false;}#endifInputStatic::InputStatic(const ISString & initStr): m_content_(initStr){}LRESULT InputStatic::OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled){ //处理可显示ascii字符 if(wParam >= ' ' && wParam <= '~') { this->m_content_ += wParam; this->SetWindowText(this->m_content_.c_str()); } //处理退格键 else if(VK_BACK == wParam) { if(!m_content_.empty()) {#ifndef _UNICODE //考虑到ansi方式中文字符和ascii具有不同的长度,所以这里需要特殊处理 if(endWithGBCode(m_content_)) m_content_ = m_content_.substr(0,m_content_.size() - 2); else m_content_ = m_content_.substr(0,m_content_.size() - 1);#else m_content_ = m_content_.substr(0,m_content_.size() - 1);#endif this->SetWindowText(m_content_.c_str()); } } return 1;}LRESULT InputStatic::OnImeCompositionChar(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/,BOOL& bHandled){ //继续发送消息,否则收不到WM_IME_CHAR消息 bHandled = FALSE; HIMC hImc; DWORD dwSize; TCHAR *Buf; hImc = ImmGetContext(GetActiveWindow()); dwSize = ImmGetCompositionString(hImc, GCS_RESULTSTR, NULL, 0); if(dwSize) { Buf = reinterpret_cast<TCHAR*>(new char[dwSize + sizeof(TCHAR)]); memset(Buf,0,dwSize + sizeof(TCHAR)); ImmGetCompositionString(hImc, GCS_RESULTSTR, (LPVOID)Buf, dwSize); this->m_content_ += Buf; delete [] Buf; this->SetWindowText(m_content_.c_str()); } ImmReleaseContext(GetActiveWindow(), hImc); return 1;}LRESULT InputStatic::OnImeChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled){ //继续发送消息,否则收不到WM_CHAR消息 bHandled = FALSE;#ifndef _UNICODE char imeChar[3]; imeChar[0] = (char)(wParam>>8); imeChar[1] = (char)wParam; imeChar[2] = '\0';#else wchar_t imeChar = wParam;#endif this->m_content_ += imeChar; this->SetWindowText(m_content_.c_str()); return 1;}

如果需要做类似密码输入框的处理,不管使用什么输入法,总之只接收英文字符。此时可以将WM_IME_COMPOSITION、WM_IME_CHAR消息的处理函数留空,并使用Windows默认的处理函数。

原文地址:http://www.qiujinwu.com/ui/windows%E4%B8%AD%E6%96%87%E8%BE%93%E5%85%A5/

【转载】VC IME 通信的更多相关文章

  1. 转载 VC 2010下安装OpenCV2.4.4

    说明: 1.安装平台:32位XP,VS2010: 2.OpenCV 2.4.4不支持VC 6.0: 3.网上有很多用CMake编译OpenCV的安装教程,这里建议先不要自己编译,如果使用预编译好的库有 ...

  2. 转载 VC轻松解析XML文件 - CMarkup类的使用方法

    VC轻松解析XML文件 - CMarkup类的使用方法http://www.cctry.com/thread-3866-1-1.html VC解析XML文件的工具有很多,CMarkup, tinyXM ...

  3. VC 串口通信类

    为了节省时间,我就贴出来吧 头文件 SerialPort.h /******************************************************************** ...

  4. VC com 通信实例

    HANDLE hCom;//全局變量串口句柄 COMMTIMEOUTS TimeOuts; DCB dcb; 按鈕代碼() { hCom=CreateFile(L“COM1”,// 串口名稱 GENE ...

  5. 【转载】socket通信-C#实现tcp收发字符串文本数据

    在日常碰到的项目中,有些场景需要发送文本数据,也就是字符串,比如简单的聊天文字,JSON字符串等场景.那么如何如何使用SharpSocket来收发此类数据呢?其中要掌握的关键点是什么呢? 点击查看原博 ...

  6. (转载)用vs2010开发基于VC++的MFC 串口通信一*****两台电脑同一个串口号之间的通信

    此文章以visual C++数据採集与串口通信測控应用实战为參考教程 此文章适合VC++串口通信入门 一.页面布局及加入控件 1, 安装好vs2010如图 2, 新建一个基于VC++的MFC项目com ...

  7. VC++使用socket进行TCP、UDP通信实例总结

    1.        两台计算机通信需要协议,通信的两台计算机IP必须唯一 2.        同一个计算机可以进行多个应用程序与其他计算机通信,IP地址唯一,而端口号是区别同一计算机(同一IP)的唯一 ...

  8. 一个由印度人编写的VC串口类

    http://www.cnblogs.com/lwngreat/p/4098374.html 软件介绍 一个由印度人编写的VC串口类(也是一种VC串口控件),他还配合这个类写了VC 串口通信方面的一些 ...

  9. [转]Web 通信 之 长连接、长轮询(long polling)

    本篇文章转载自Web 通信之长连接.长轮询(longpolling),版权归作者所有. 转者按:随着技术的发展,在HTML5中,可以通过WebSocket技术来完成长连接的开发,虽然如此,本文依然存在 ...

随机推荐

  1. bzoj千题计划268:bzoj3131: [Sdoi2013]淘金

    http://www.lydsy.com/JudgeOnline/problem.php?id=3131 如果已知 s[i]=j 表示有j个<=n数的数码乘积=i 那么就会有 s[a1]*s[a ...

  2. bzoj千题计划236:bzoj2300: [HAOI2011]防线修建

    http://www.lydsy.com/JudgeOnline/problem.php?id=2300 维护动态凸包,人懒用的set 用叉积判断,不要用斜率 #include<set> ...

  3. 一个中国地图的SVG,可以带参数

    <script src="http://files.cnblogs.com/files/LoveOrHate/jquery.min.js"></script> ...

  4. asp.net C#母版页和内容页事件排版加载顺序生命周期

    asp.net C#母版页和内容页事件排版加载顺序生命周期 关于ASP页面Page_Load发生在事件之前而导致的问题已经喜闻乐见,对于问题的解释也很全面,但是如何解决问题则较少有人说明,我就再 简单 ...

  5. charles https抓包 (安卓安装证书)

    的Android APP使用的都是http请求,之后改成了https,就出现了以下情况,无法正常读取抓取的内容 下面阐述一下,正确的安装步骤,为出现类似情况的朋友提供一个参考: 1.第一步: 最后点击 ...

  6. 基于I2C总线的0.96寸OLED显示屏驱动

    资料未整理,先占位置,以后补充

  7. AtomicInteger源码

    一.概念 AtomicInteger,一个提供原子操作的Integer的类.在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字.而Ato ...

  8. 新手学习爬虫之创建第一个完整的scrapy工程-糗事百科

    创建第一个scrapy工程-糗事百科 最近不少小伙伴儿,问我关于scrapy如何设置headers的问题,时间久了不怎么用,还真有的忘,全靠记忆去写了,为了方便大家参考,也方便我以后的查阅,这篇文章就 ...

  9. 基于 Apache 在本地配置多个虚拟主机

    如何使用 Apache 在本地配置出多个虚拟主机呢?而且使用不同的“域名”来访问本地不同的站点呢? 一般情况下,咱们都使用 localhost 来访问本机上的服务器,在我们的 C:/WINDOWS/s ...

  10. 转载:Linux批量远程管理主机命令_pssh用法详解

    原文:http://www.linuxidc.com/Linux/2011-12/49635p2.htm (一) 使用前提 中心主机连接远程主机可以通过ssh密钥无密码连接 (二) 命令格式 pssh ...