「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; } }