XNA中的中文输入(三)
仅供个人学习使用,请勿转载,勿用于任何商业用途。
把前两部分的代码移植到XNA Game中,最大的问题在于无法访问底层的WinForm窗口,自然也就无法获得窗口消息。网上有一些通过window handle创建一个NativeWindow,然后重载NativeWindow WndProc的方法来获得windos消息。不幸的是把之前的代码添加在这样的构架中,不会有任何作用。顺便说一下,Game把WinForm封装到内部实在是一种不太好的策略。因此,我们不得不直接调用API hook窗口消息。
首先,声明以下函数:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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);
创建一个静态类来完成所有消息捕获任务:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
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似乎接收不到任何按键消息,添加以下代码:
return (IntPtr)DLGC_WANTALLKEYS;
其次,系统默认的IME窗口不会显示了。很好,这正是我们希望的效果。此外,在IME激活的状态下,一直按着键盘键盘,会导致游戏被卡住。这是hook窗口消息得到的bug吗?不是,做个试验,wow也会这样。最后,如果在全屏模式下,还会出现一些问题,这主要是看是否在正确的位置,调用了Initialize。
通过这三篇文章的,你应该已经能够为游戏编写一个最基本的中文输入系统了。最后还有一些函数是你应该留意的:
1. 除了在捕获WM_IME_ENDCOMPOSITION用ImmGetCompositionString获得合成的字符串外,捕获WM_IME_CHAR和WM_CHAR也能获得同样的效果。
2. GetKeyboardLayout可以获得与当前键盘布局相关的消息。
3. ImmGetIMEFileName可以获得当前输入法的标识名称。
4. ImmSetConversionStatus可以切换半角全角状态。
5. 用WM_CHAR处理普通输入,而不要用Xna中的Keyboard。因为XNA中的Keyboard本来就不是为输入字符设计的,你根本无法获得Caps Lock等状态,因此,即使是普通英文输入,也非常麻烦。