TextBox を IME 前後参照変換に対応させる (System.Windows.Forms)

TextBox に入力済みの文字列を考慮して IMEかな漢字変換できるようにします。「IMEの前後参照変換機能に対応するには」が大変参考になりました。ありがとうございます。

【花が】「さいた」 →[花が咲いた]
【布を】「さいた」 →[布を裂いた]
【時間を】「さいた」 →[時間を割いた]

IMEの前後参照変換機能に対応するには

という風に、文脈を読んだ変換をしてくれます。ちなみにこの機能は IME がサポートしている必要があります。以下のソースコードコンパイルし、Windows 7 上の ATOK を使って試してみました*1

コードはあんまり自信ないです。ご利用の際は注意してください。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace Zengo
{
  class TextBoxEx : TextBox
  {
    private const int WM_IME_REQUEST = 0x0288;
    private const int IMR_DOCUMENTFEED = 0x0007;

    public struct RECONVERTSTRING
    {
      public uint dwSize;
      public uint dwVersion;
      public uint dwStrLen;
      public uint dwStrOffset;
      public uint dwCompStrLen;
      public uint dwCompStrOffset;
      public uint dwTargetStrLen;
      public uint dwTargetStrOffset;
    }


    protected override void WndProc(ref Message m)
    {
      // Multiline のことは考えてない。
      // てきとー実装なのでとりあえず動くだけ。
      unsafe
      {
        if (m.Msg == WM_IME_REQUEST && (int)m.WParam == IMR_DOCUMENTFEED)
        {
          uint len = (uint)SelectionStart;

          if (m.LParam.ToInt32() != 0)
          {
            RECONVERTSTRING* reconv = (RECONVERTSTRING*)m.LParam.ToPointer();

            char* paragraph = (char*)((byte*)reconv + sizeof(RECONVERTSTRING));

            reconv->dwSize = (uint)sizeof(RECONVERTSTRING);
            reconv->dwVersion = 0;
            reconv->dwStrLen = len;
            reconv->dwStrOffset = (uint)sizeof(RECONVERTSTRING);

            reconv->dwCompStrLen = 0;
            reconv->dwCompStrOffset = len * sizeof(char);

            reconv->dwTargetStrLen = len;
            reconv->dwTargetStrOffset = len * sizeof(char);

            for (int i = 0; i < SelectionStart; i++)
            {
              paragraph[i] = Text[i];
            }
          }

          m.Result = new IntPtr(sizeof(RECONVERTSTRING) + len * sizeof(char));
          return;
        }
      }

      base.WndProc(ref m);
    }
  }
}

ポインタつかっちゃっててなぜだか負けた気がする・・・。

*1:ATOK では「変換補助」の「カーソル位置前後の文章を参照して変換する」を有効化する必要があります。