C++/CLI マルチバイト対応アプリケーションで.Netコントロールを使うとFEP変換が文字化けする

IMEなどのFEPは変換対象の文字セットをどのように判断しているのだろう。
これがわからずにだいぶ苦労してしまった。
前回VC++から.Net Framework対応のFlexGridを使う手段を示したが、これには大きな欠点があった。
FlexGridの編集モードでテキストをFEP経由で入力すると文字化けしてしまうのだ。
どうやらFEPがターゲット(FlexGrid)をShift-JIS対応と勘違いして漢字を送ってくるからだ。
FlexGridはもちろんUnicode対応である。
IMEにターゲットがUnicodeであることを伝えたいが、その方法がわからない。
IMEを制御して変換を補正させることも考えたが、アプリケーションがIME依存になることは避けたい。
そこで先ほどのWM_CHARをフックする方法に辿りついた。
ここで送られてくるShift-JISをUnicodeに変換すればいい。
あとはこの変換処理を行うタイミングだ。
FlexGridの場合はStartEdit、BeforeEdit、KeyDownEditあたりで、Editorプロパティに設定される入力コントロールに対してフックをかければうまくいくようだ。
先ほどのWM_CHAR万能フッククラスを以下のようにコードセット変換用に書き換えた。

class SjisHook : NativeWindow {
  private const int WM_CHAR = 0x102;
  private List<byte> _mbStack = new List<byte>();
  enum GetWindow_Cmd : uint {
    GW_HWNDFIRST = 0,
    GW_HWNDLAST = 1,
    GW_HWNDNEXT = 2,
    GW_HWNDPREV = 3,
    GW_OWNER = 4,
    GW_CHILD = 5,
    GW_ENABLEDPOPUP = 6
  }
  [DllImport("user32.dll", SetLastError = true)]
  static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);
  public SjisHook(Control c) {
    IntPtr handle = c.Handle;
    if (c is ComboBox) {
      handle = GetWindow(c.Handle, GetWindow_Cmd.GW_CHILD);
    }
    AssignHandle(handle);
    c.HandleDestroyed += new EventHandler(OnHandleDestroyed);
    c.HandleCreated += new EventHandler(OnHandleCreated);
  }
  internal void OnHandleDestroyed(object sender, EventArgs e) {
    ReleaseHandle();
    this._mbStack.Clear();
  }
  internal void OnHandleCreated(object sender, EventArgs e) {
    if (sender is Control) {
      AssignHandle(((Control)sender).Handle);
      this._mbStack.Clear();
    }
  }
  protected override void WndProc(ref System.Windows.Forms.Message m) {
    switch (m.Msg) {
    case WM_CHAR:
      int w = m.WParam.ToInt32();
      if (this._mbStack.Count == 0 && ((w >= 0x81 && w <= 0x9f) || (w >= 0xe0 && w <= 0xfc))) {
        this._mbStack.Add((byte)w);
        return;
      } else if (this._mbStack.Count == 1) {
        this._mbStack.Add((byte)w);
        try {
          if (ConvertCodeSetAndProc(this._mbStack.ToArray<byte>(), m)) {
            return;
          }
        } finally {
          this._mbStack.Clear();
        }
      } else if ((w >= 0x21 && w <= 0x7e) || (w >= 0xa1 && w <= 0xdf)) {
        if(ConvertCodeSetAndProc(new byte[] {(byte)w}, m)) {
          return;
        }
      } else if (Char.IsControl((char)w)) {
        base.WndProc(ref m);
        return;
      } else {
        this._mbStack.Clear();
      }
        break;
      }
      base.WndProc(ref m);
  }
  private bool ConvertCodeSetAndProc(byte[] bytes, System.Windows.Forms.Message m) {
    bool ret = false;
    byte[] by = System.Text.Encoding.Unicode.GetBytes(System.Text.Encoding.GetEncoding(932).GetString(bytes));
    if (by.Length == 2) {
      Message wm = Message.Create(m.HWnd, m.Msg, new System.IntPtr((by[1] << 8) | by[0]), m.LParam);
      base.WndProc(ref wm);
      ret = true;
    }
    return ret;
  }
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です