2

我想增加在我的 XNA 游戏中输入西里尔字母的可能性。我不能使用默认 Input.KeyboardState 因为 Input.Keys 枚举只包含英文字母。因此,我使用键盘挂钩来处理 Windows 输入。但它返回键码,可用于英文字母(因为 ASCII 码与键码匹配),但不适用于西里尔字母。对于 "a-я" 可以使用 848 偏移量,但对于更具体的字母,如 "і" 或 "ї" 有不同的值。

是否有有效的方法将键码转换为 ASCII 中的西里尔字母?或者也许有没有讨厌的键盘挂钩的解决方案?

顺便说一句,这里的键盘挂钩类列表:

#region EventArgs

public class CharacterEventArgs : EventArgs
{
    private readonly char character;
    private readonly int lParam;
    private readonly int keyLayout;

    public CharacterEventArgs(char character, int lParam, int keyLayout)
    {
        this.character = character;
        this.lParam = lParam;
        this.keyLayout = keyLayout;
    }

    #region Properties

    public int KeyLayout
    {
        get { return keyLayout; }
    }

    public char Character
    {
        get { return character; }
    }

    public int Param
    {
        get { return lParam; }
    }

    public int RepeatCount
    {
        get { return lParam & 0xffff; }
    }

    public bool ExtendedKey
    {
        get { return (lParam & (1 << 24)) > 0; }
    }

    public bool AltPressed
    {
        get { return (lParam & (1 << 29)) > 0; }
    }

    public bool PreviousState
    {
        get { return (lParam & (1 << 30)) > 0; }
    }

    public bool TransitionState
    {
        get { return (lParam & (1 << 31)) > 0; }
    }

    #endregion
}

public class KeyEventArgs : EventArgs
{
    private Keys keyCode;

    public KeyEventArgs(Keys keyCode)
    {
        this.keyCode = keyCode;
    }

    public Keys KeyCode
    {
        get { return keyCode; }
    }
}

#endregion

#region delegats

public delegate void CharEnteredHandler(object sender, CharacterEventArgs e);
public delegate void KeyEventHandler(object sender, KeyEventArgs e);

#endregion

public static class EventInput
{
    #region events

    /// <summary>
    /// Event raised when a character has been entered.
    /// </summary>
    public static event CharEnteredHandler CharEntered;

    /// <summary>
    /// Event raised when a key has been pressed down. May fire multiple times due to keyboard repeat.
    /// </summary>
    public static event KeyEventHandler KeyDown;

    /// <summary>
    /// Event raised when a key has been released.
    /// </summary>
    public static event KeyEventHandler KeyUp;

    #endregion

    delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

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

    static GameWindow gameWindow;

    #region constants

    const int GWL_WNDPROC = -4;
    const int WM_KEYDOWN = 0x100;
    const int WM_KEYUP = 0x101;
    const int WM_CHAR = 0x102;
    const int WM_IME_SETCONTEXT = 0x0281;
    const int WM_INPUTLANGCHANGE = 0x51;
    const int WM_GETDLGCODE = 0x87;
    const int WM_IME_COMPOSITION = 0x10f;
    const int DLGC_WANTALLKEYS = 4;

    #endregion

    #region Win32-functions

    //to handle input

    [DllImport("Imm32.dll")]
    static extern IntPtr ImmGetContext(IntPtr hWnd);

    [DllImport("Imm32.dll")]
    static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);

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

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


    //to get keyboard layout

    [DllImport("user32.dll", SetLastError = true)]
    static extern int GetWindowThreadProcessId(
        [In] IntPtr hWnd,
        [Out, Optional] IntPtr lpdwProcessId
        );

    [DllImport("user32.dll", SetLastError = true)]
    static extern ushort GetKeyboardLayout(
        [In] int idThread
        );

    #endregion

    static ushort GetKeyboardLayout()
    {
        return GetKeyboardLayout(GetWindowThreadProcessId(gameWindow.Handle, IntPtr.Zero));
    }

    #region Initialize

    /// <summary>
    /// Initialize the TextInput with the given GameWindow.
    /// </summary>
    /// <param name="window">The XNA window to which text input should be linked.</param>
    public static void Initialize(GameWindow window)
    {
        if (initialized)
            throw new InvalidOperationException("TextInput.Initialize can only be called once!");

        gameWindow = window;

        hookProcDelegate = new WndProc(HookProc);
        prevWndProc = (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC,
            (int)Marshal.GetFunctionPointerForDelegate(hookProcDelegate));

        hIMC = ImmGetContext(window.Handle);
        initialized = true;
    }

    #endregion

    static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        IntPtr returnCode = CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);

        switch (msg)
        {
            case WM_GETDLGCODE:
                returnCode = (IntPtr)(returnCode.ToInt32() | DLGC_WANTALLKEYS);
                break;

            case WM_KEYDOWN:
                if (KeyDown != null)
                    KeyDown(null, new KeyEventArgs((Keys)wParam));
                break;

            case WM_KEYUP:
                if (KeyUp != null)
                    KeyUp(null, new KeyEventArgs((Keys)wParam));
                break;

            case WM_CHAR:
                if (CharEntered != null)
                {
                    CharEntered(null, new CharacterEventArgs((char) wParam, lParam.ToInt32(), GetKeyboardLayout()));
                }
                break;

            case WM_IME_SETCONTEXT:
                if (wParam.ToInt32() == 1)
                    ImmAssociateContext(hWnd, hIMC);
                break;

            case WM_INPUTLANGCHANGE:
                ImmAssociateContext(hWnd, hIMC);
                returnCode = (IntPtr)1;
                break;
        }

        return returnCode;
    }
}
4

1 回答 1

2

您是否考虑过使用 Windows 窗体进行字符输入?我不会说俄语,但我只能假设 KeyPress 事件必须在俄语语言环境中正确处理 Cyrillic,而且它比自己发送 Windows 消息要简单得多。

using System.Windows.Forms;

...

var form = (Form)Form.FromHandle(window.Handle);
form.KeyPress += form_KeyPress;

...

private void form_KeyPress(Object sender, KeyPressEventArgs e)
{  
    Console.WriteLine(e.KeyChar);
    e.Handled = true;
}
于 2013-03-29T14:28:59.737 回答