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