XNA中的中文输入(二)

 XNA中的中文输入(二)

仅供个人学习使用,请勿转载,勿用于任何商业用途。

 

         全屏模式下,由于GDIDirectX会发生冲突,我们需要自己渲染IME窗口。很多人都觉得渲染IME窗口是件很复杂的事情,但仔细观察一下CustomUI或者WOW中的IME窗口,其实就是一个简单的text或者label控件而已!


      只要得到IME中的字符信息,接下来就很简单了.为此,我们需要处理以下4个消息:

         第一个是WM_IME_STARTCOMPOSITION,这个消息在按下第一个字符,开始一次新的字符组合时触发,可以把类似清除字符串buffer的工作放在这里。

         接下来,WM_IME_COMPOSITION是一个很重要的消息。SDK文档里说“The IMM sends a WM_IME_COMPOSITION message to the application when the user enters a keystroke to change the composition string.lParam参数说明IME发生了什么样的变化,这里我们只需关心GCS_COMPSTR或者GCS_COMPREADSTR,对于中文来说,这两个标志所表示的内容都是一致的,表示输入字符发生了变化。可以用ImmGetCompositionString获得此时的IME输入字符。

         IME窗口发生变化时,向程序发送WM_IME_NOTIFY消息,wParam参数说明发生了什么变化。一般来说只需要关心IMN_CHANGECANDIDATE,它表示IME窗口中的候选字符发生了变化。此时需要用ImmGetCandidateList获得候选字符。

         最后,当完成输入时,处理WM_IME_ENDCOMPOSITION消息,同样使用ImmGetCompositionString获得最终生成的中文字符。

         基本的步骤就那么简单,下面是一些实现细节,主要是P/Invoke时可能遇到的问题。首先是两个函数的声明。

ExpandedBlockStart.gif
 [DllImport( " imm32.dll " , CharSet  =  CharSet.Unicode, SetLastError  =   true )]
 
public   static   extern   int  ImmGetCompositionString(IntPtr hIMC,  int  CompositionStringFlag, StringBuilder buffer,  int  bufferLength);

 [DllImport(
" imm32.dll " , CharSet  =  CharSet.Unicode, EntryPoint  =   " ImmGetCandidateList " )]
 
public   static   extern   uint  ImmGetCandidateList(IntPtr hIMC,  uint  deIndex, IntPtr candidateList,  uint  dwBufLen);

 

         注意,必须显式指定CharSet属性,否则导入的总是Ansi版本的API

         先看ImmGetCompositionString函数,它的第一个参数是input context句柄,第二个参数表示我们希望取得IME中的哪种字符串。一般来说在WM_IME_COMPOSITION时,应该使用GCS_COMPSTRGCS_COMPREADSTR,在WM_IME_ENDCOMPOSITION时使用GCS_RESULTSTR。函数将把得到的字符串放在第三个参数中。最后则是字符串长度,如果这个值为0,则返回字符串的长度,因此你可能先查询字符串长度,然后在取字符串。

         ImmGetCandidateListImmGetCompositionString的用法非常相似。这里比较有技术性的地方在于返回的CandidateList参数。首先需要为这个结构做以下声明:

ExpandedBlockStart.gif CandidateList
[StructLayoutAttribute(LayoutKind.Sequential)]
public   struct  CandidateList
{
    
public   uint  dwSize;
    
public   uint  dwStyle;
    
public   uint  dwCount;
    
public   uint  dwSelection;
    
public   uint  dwPageStart;
    
public   uint  dwPageSize;
    
///  DWORD[1]
    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst  =   1 , ArraySubType  = UnmanagedType.U4)]
    
public   uint [] dwOffset;
}

          
        CandidateList保存了IME中的候选字符串,但仔细看CandidateList的成员,似乎没有任何一个与字符串相关。文档中的说明也只会让你更迷糊。这是一个非常特殊的结构,以下是当有4个候选字符串时,CandidateList在内存中的布局:

 swSize|dwStyle|…….|dwPageSize|dwOffset|Offset1|Offset2|Offset3|string0|string1|string2|string3

         可以看到,所有字符串信息实际上附加在dwOffset后面,dwOffset记录了第一个字符串相对于CandidateList的偏移值,如果有n个字符串,则后面会有n-132位的值,分别表示第n个字符串相对于对CandidateList的偏移值。在这些32位的值之后,则是每个字符串实际的值。也就是说dwOffset记录了string0的偏移值,offset3记录了string3的便宜值。注意,当swSize1时,CandidateList又是另外一种布局,具体可参考sdk文档。显然,只有直接访问物理地址,才能得到每个字符串。如何在C#里访问物理地址呢?以下是解析CandidateList的代码:

ExpandedBlockStart.gif
if (m.Msg  ==  WindowMessage.ImeNotify)
{
    
if (m.WParam.ToInt32()  ==  IMN_CHANGECANDIDATE )
    {
        CandidateList candidate;
        IntPtr ptr;
        
uint  size  =  IMM.ImmGetCandidateList(imeContext,  0 , IntPtr.Zero,  0 );
        
if (size  >   0 )
        {
            ptr 
=  Marshal.AllocHGlobal(( int )size);
            size 
=  IMM.ImmGetCandidateList(imeContext,  0 , ptr, size);
            candidate 
=  (CandidateList)Marshal.PtrToStructure(ptr,  typeof (CandidateList));
            
if (candidate.dwCount  >   1 )
            {
                
for  ( int  i  =   0 ; i  <  candidate.dwCount; i ++ )
                {
                    
int  stringOffset  =  Marshal.ReadInt32(ptr,  24   +   4   *  i);
                    IntPtr addr 
=  (IntPtr)(ptr.ToInt32()  +  stringOffset);
                    
string  str  =  Marshal.PtrToStringUni(addr );
                    Console.WriteLine(str);
                 }
            }
            
else ......
            Marshal.FreeHGlobal(ptr);
        }
    }
}

 

       现在已经可以成功调用IME,并且获得IME中的字符了。下一次,我们将讨论如何在XNA的Game框架下使用IME。

 

更新12.28.09:

   在Vista和Windows 7下,ms用Text Service Framework取代了IMM,虽然大部分IMM API仍然可用,但由于TSF内部的原因,可能有一些兼容性问题。更好的方法是直接使用TSF。

 

 

转载于:https://www.cnblogs.com/clayman/archive/2009/12/18/1626812.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值