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