FEP変換時文字化け対策の改訂

「C++/CLI マルチバイト対応アプリケーションで.Netコントロールを使うとFEP変換が文字化けする」で紹介したSjisHookクラスで不具合があった。
JIS半角コードと一致するUnicodeに変換した場合に文字化けがおこる。
原因はWM_CHARでフック時、Unicodeに変換したWM_CHARメッセージをbase.WndProcに送ると、何故かこのメッセージが再送されてしまう。
結果的に、再送メッセージ(実はUnicode)をJIS半角コードと誤認し、これをUnicodeに変換してしまうわけだ。
再送される理由がわからない。

とりあえず応急処置として以下のように改良した。

class SjisHook : NativeWindow
{
  private const int WM_CHAR = 0x102;
  private List<byte> _mbStack = new List<byte>();
  private static bool _sendUnicodeNow = false;
  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 (_sendUnicodeNow)
      {
        base.WndProc(ref m);
        return;
      }
      else 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);
      _sendUnicodeNow = true;
      base.WndProc(ref wm);
      _sendUnicodeNow = false;
      ret = true;
    }
    return ret;
  }
}

コメントを残す

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