LoginSignup
4
9

More than 5 years have passed since last update.

C#でIMEの入力を受けるユーザーコントロールの作成

Posted at

時々作りたくなるのですが、毎回忘れて調べるのに苦労する、C#でIMEの入力を受けるコントロールの作り方メモです。

 class SpTextEditorComponent : System.Windows.Forms.UserControl
    {

        #region IME関係

        private const int WM_IME_COMPOSITION = 0x010F;
        private const int GCS_RESULTREADSTR = 0x0200;
        private const int WM_IME_STARTCOMPOSITION = 0x10D; // IME変換開始
        private const int WM_IME_ENDCOMPOSITION = 0x10E;   // IME変換終了
        private const int WM_IME_NOTIFY = 0x0282;
        private const int WM_IME_SETCONTEXT = 0x0281;

        public enum ImmAssociateContextExFlags : uint
        {
            IACE_CHILDREN = 0x0001,
            IACE_DEFAULT = 0x0010,
            IACE_IGNORENOCONTEXT = 0x0020
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct C_RECT
        {
            public int _Left;
            public int _Top;
            public int _Right;
            public int _Bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct C_POINT
        {
            public int x;
            public int y;
        }

        const uint CFS_POINT = 0x0002;

        public struct COMPOSITIONFORM
        {
            public uint dwStyle;
            public C_POINT ptCurrentPos;
            public C_RECT rcArea;
        }

        [DllImport("Imm32.dll")]
        private static extern IntPtr ImmGetContext(IntPtr hWnd);
        [DllImport("Imm32.dll")]
        private static extern int ImmGetCompositionString(IntPtr hIMC, int dwIndex, StringBuilder lpBuf, int dwBufLen);
        [DllImport("Imm32.dll")]
        private static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC);
        [DllImport("imm32.dll")]
        private static extern IntPtr ImmCreateContext();
        [DllImport("imm32.dll")]
        private static extern bool ImmAssociateContextEx(IntPtr hWnd, IntPtr hIMC, ImmAssociateContextExFlags dwFlags);
        [DllImport("imm32.dll")]
        public static extern int ImmSetCompositionWindow(IntPtr hIMC, ref COMPOSITIONFORM lpCompositionForm);

        IntPtr himc = IntPtr.Zero;


        private void InitializeComponent()
        {
            this.SuspendLayout();
        }

        protected override void Dispose(bool disposing)
        {
            if (himc != IntPtr.Zero)
            {
                ImmReleaseContext(this.Handle, himc);
                himc = IntPtr.Zero;
            }
            base.Dispose(disposing);
        }

        ~SpTextEditorComponent()
        {
            if (himc != IntPtr.Zero)
            {
                ImmReleaseContext(this.Handle, himc);
                himc = IntPtr.Zero;
            }
        }


        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_IME_SETCONTEXT:
                    {
                        //Imeを関連付ける
                        IntPtr himc = ImmCreateContext();
                        ImmAssociateContextEx(this.Handle, himc, ImmAssociateContextExFlags.IACE_DEFAULT);
                        base.WndProc(ref m);
                        break;
                    }
                case WM_IME_STARTCOMPOSITION:
                    {
                        //入力コンテキストにアクセスするためのお約束
                        IntPtr hImc = ImmGetContext(this.Handle);

                        //コンポジションウィンドウの位置を設定
                        COMPOSITIONFORM info = new COMPOSITIONFORM();
                        info.dwStyle = CFS_POINT;
                        info.ptCurrentPos.x = 10;
                        info.ptCurrentPos.y = 10;
                        ImmSetCompositionWindow(hImc, ref info);

                        //コンポジションウィンドウのフォントを設定
                        //ImmSetCompositionFont(hImc, m_Focus->GetFont()->GetInfoLog());

                        //入力コンテキストへのアクセスが終了したらロックを解除する
                        ImmReleaseContext(Handle, hImc);


                        base.WndProc(ref m);
                        break;
                    }
                default:
                    //IME以外のメッセージは元のプロシージャで処理
                    base.WndProc(ref m);
                    break;
            }
        }

        #endregion


        public SpTextEditorComponent()
        {
            InitializeComponent();
        }
}

一部余分なコードや余分なインポートがありますが、ご勘弁を。
以下のことをしておけば最低限動くようです。

  • WndProcをオーバーライド
    • WM_IME_SETCONTEXT メッセージで
      • ImmCreateContext を呼んでコンテキストを作り、ImmAssociateContextEx でハンドルと結びつける
    • WM_IME_STARTCOMPOSITION メッセージで
      • ImmSetCompositionWindow を使い候補ウィンドウの位置を設定する
    • 解放のタイミング(Dispose?)で
      • ImmReleaseContext を呼んで解放

入力文字は、KeyPressイベントで一文字ずつ入ってきます。

4
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
9