IME消息机制

转自网易博客:http://egamesir.wap.blog.163.com/w2/blogDetail.do;jsessionid=FAA3EAD9D7D502FE93FC8817051A9D33.blogp17v1-8010?blogId=1011359886&hostID=egamesir


while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
     
      以上是Windows应用程序最基本的消息循环处理算法(本文先不考虑 TranslateAccelerator)。各种消息的最终处理由 DispatchMessage来完成。 TranslateMessage在此前会进行一些键盘消息预处理操作。在《Windows程序设计第五版》中,Charles Petzold只提到 TranslateMessage的作用是从 WM_KEYDOWNWM_SYSKEYDOWN中产生‘字符消息’( WM_CHARWM_DEADCHARWM_SYSCHARWM_SYSDEADCHAR)。典型的一组击键消息过程为 WM_KEYDOWN—— WM_CHAR—— WM_KEYUP。MSDN中则指明当msg被 TranslateMessage进行一定处理后将返回非零值,否则如果 TranslateMessage不处理这个消息就返回0,此外对于 TranslateMessage的说明不比Charles Petzold多多少。
     
      那么 WM_IME_CHAR是怎么来的呢?这个问题一直困扰着我,在阅读了一堆关于输入法的资料后,我依然对 IME的机制不是很清楚。所以用尽了笨办法调试程序以期寻找一些线索。
     
      在中文输入法下 TranslateMessage并不产生 WM_CHAR。此时它处理的 WM_KEYDOWN消息中的 wParam参数比较特殊,是 VK_PROCESSKEY,而不是一般的virtual-key code。假设我们要打一个‘进’字,如果用微软拼音输入法的话,按键的顺序是 'J' 'I' 'N' 'SPACE' 'SPACE'。当按下'J'时  GetMessage得到一个 WM_KEYDOWN,wParam是 VK_PROCESSKEYTranslateMessage处理过程中会向消息队列里面放入 WM_IME_STARTCOMPOSITION和WM_IME_COMPOSITION。在依次按下'I' 'N' 'SPACE'时只有 WM_IME_COMPOSITION消息。按下最后一个'SPACE'时 TranslateMessage会产生 WM_IME_COMPOSITION和 WM_IME_ENDCOMPOSITION。我用下面的方法结合其他现象得出的结论:
      
      在 TranslateMessage之前和 TranslateMessage之后两处都添加以下代码,调试后察看hFile指向的文件。
     if(msg.message==WM_KEYDOWN&&msg.wParam== VK_PROCESSKEY)
    {
        while(PeekMessage(&msg2,msg.hwnd,0,0,PM_REMOVE))
        {
            glen=wsprintf(buf,L"%d,",msg2.message);
            WriteFile (hFile, buf,length*2 , &rlength, NULL) ;
        }
    }
     
      进一步调试,我看到我创建的 WINPROC在处理按下最后一个'SPACE'产生的 WM_IME_COMPOSITION时,在 DefWindowProc中根据这个 WM_IME_COMPOSITION,产生一个 WM_IME_CHAR,然后再SEND给本地线程,实际上也就是以 WM_IME_CHAR为参数,递归调用了一次 WINPROC,在这次调用的 WINPROC中产生 WM_CHAR,POST给本地线程,达到通知应用程序我们想输入'进'的目的。所以 TranslateMessage对于中文输入也是非常重要的。经过进一步反汇编研究,大概看清了 TranslateMessage的结构类似以下代码:
     BOOL TranslateMessage(const MSG *pmsg)
    {
        if(pmsg->wParam==VK_PROCESSKEY) // #define VK_PROCESSKEY 0x00e5 
            ImmTranslateMessage( pmsg->hwnd, pmsg->message, pmsg->wParam, pmsg->lParam );
        else
            TranslateMessageEx( pmsg, 0 );
    }
 
     反汇编,我又加了两个注释
     77D48BF6  mov        edi,edi
    77D48BF8  push        ebp  
    77D48BF9  mov         ebp,esp 
    77D48BFB  push        esi  
    77D48BFC  mov         esi,dword ptr [ebp+8] 
    77D48BFF  cmp         word ptr [esi+8],0E5h 
    77D48C05  je             77D70A49                     ; 77D70A49 处call ImmTranslateMessage
    77D48C0B  push        0    
    77D48C0D  push        esi  
    77D48C0E  call          77D48A19                     ; 77D48A19是TranslateMessageEx
    77D48C13  pop          esi  
    77D48C14  pop          ebp  
    77D48C15  ret           4   
     
       ImmTranslateMessageTranslateMessageEx我是 dumpbin来的,MSDN中也没有说明.对 ImmTranslateMessage的参数列表我还是比较肯定:
     77D70A49  push        dword ptr [esi+0Ch] 
    77D70A4C  push        dword ptr [esi+8] 
    77D70A4F  push        dword ptr [esi+4] 
    77D70A52  push        dword ptr [esi] 
    77D70A54  call          dword ptr ds:[77DA03A0h] 
   
       TranslateMessageEx的参数列表我不大肯定
     77D48C0B  push        0    
    77D48C0D  push        esi
     
      应该是MSG指针和一个32位的整数,用以下代码处理消息,程序可以正常运行,正常处理字符输入。
     typedef BOOL  (WINAPI *POSTTYPE)(HWND,UINT,WPARAM,LPARAM);
    typedef BOOL  (WINAPI *TRANTYPE)(const MSG *, DWORD);
    PROC pfnImm = GetProcAddress(GetModuleHandleA( "imm32.dll" ), "ImmTranslateMessage" );
    PROC pfnTran = GetProcAddress(GetModuleHandleA( "user32.dll" ), "TranslateMessageEx" );
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if(msg.wParam==VK_PROCESSKEY)
            ((POSTTYPE)pfnImm)(msg.hwnd,msg.message,msg.wParam,msg.lParam); 
        else
            ((TRANTYPE)pfnTran)(&msg,0);    
        DispatchMessage(&msg);
    }

转自网易网友博客

PS:在处理游戏中的中文输入的时候死活收不到  WM_IME_STARTCOMPOSITION 消息,开始的时候没想到什么问题,找了好久。后来猛然想到是虚拟键消息转换的问题TranslateMessage。转一篇文章了解一下。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值