Clayman's Graphics Corner

DirectX,Shader & Game Engine Programming

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

      XNA中的中文输入(三)

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

 

     把前两部分的代码移植到XNA Game中,最大的问题在于无法访问底层的WinForm窗口,自然也就无法获得窗口消息。网上有一些通过window handle创建一个NativeWindow,然后重载NativeWindow WndProc的方法来获得windos消息。不幸的是把之前的代码添加在这样的构架中,不会有任何作用。顺便说一下,GameWinForm封装到内部实在是一种不太好的策略。因此,我们不得不直接调用API hook窗口消息。

         首先,声明以下函数:

 

代码
[DllImport("user32.dll",CharSet = CharSet.Unicode)]
static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

[DllImport(
"user32.dll", CharSet = CharSet.Unicode)]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

  

         创建一个静态类来完成所有消息捕获任务:

 

代码
public static class InputCaputure
{
    
delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    
static bool initialized;
    
static IntPtr prevWndProc;
    
static WndProc hookProcDelegate;
    
static IntPtr hIMC;

     
public static void Initialize(GameWindow window)
    {
        
if (initialized)
            
throw new InvalidOperationException("InputCaputure.Initialize can only be called once!");

        hookProcDelegate 
= new WndProc(HookProc);
        prevWndProc 
= (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC, (int)Marshal.GetFunctionPointerForDelegate(hookProcDelegate));
        hIMC 
= ImmGetContext(window.Handle);
        initialized 
= true;
    }

    
static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        
switch (msg)
        {             
            
case WindowMessage.ImeSetContext:
                {
                    
//add code here
                    return (IntPtr)1;
                }

            
case WindowMessage.ImeStartCompostition:
                
//add code here

                
return (IntPtr)0;

            
case ............

            
default:
                
return CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
        }
    }
}

 

         好了,现在把之前的代码搬到HookProc中,它将捕获并处理所有我们关心的消息,之后,再把所有消息传递给Game

         移植完所有代码之后,你会发现几个问题。首先,虽然成功激活了IME窗口,但IME似乎接收不到任何按键消息,添加以下代码:

 

case WM_GETDLGCODE:
    
return (IntPtr)DLGC_WANTALLKEYS;  

 

         其次,系统默认的IME窗口不会显示了。很好,这正是我们希望的效果。此外,在IME激活的状态下,一直按着键盘键盘,会导致游戏被卡住。这是hook窗口消息得到的bug吗?不是,做个试验,wow也会这样。最后,如果在全屏模式下,还会出现一些问题,这主要是看是否在正确的位置,调用了Initialize

         通过这三篇文章的,你应该已经能够为游戏编写一个最基本的中文输入系统了。最后还有一些函数是你应该留意的:

1. 除了在捕获WM_IME_ENDCOMPOSITIONImmGetCompositionString获得合成的字符串外,捕获WM_IME_CHARWM_CHAR也能获得同样的效果。

2. GetKeyboardLayout可以获得与当前键盘布局相关的消息。

3. ImmGetIMEFileName可以获得当前输入法的标识名称。

4. ImmSetConversionStatus可以切换半角全角状态。

5. WM_CHAR处理普通输入,而不要用Xna中的Keyboard。因为XNA中的Keyboard本来就不是为输入字符设计的,你根本无法获得Caps Lock等状态,因此,即使是普通英文输入,也非常麻烦。

 

posted on 2009-12-18 03:16  clayman  阅读(3293)  评论(16编辑  收藏  举报