はじめに
私は絵を描く兼ね合いで、iPadを持ち歩いているのですが、PC作業がしたい!ってときはノートPCを追加で持ち歩くのは重たいので、iPad から自宅 WindowsPC へリモートデスクトップ接続をする方式を採用しています。
で、この iPad + リモートデスクトップという組み合わせで作業すると、
日本語入力時、特に漢字に変換したらうまく文字が入ってくれません。
たとえば「変換が入るとおかしくなる」と打ちたいのに、画面には
へんかんがはいるとおか変換入るとおかしくなる
みたいに、読み(ひらがな)の残骸+変換結果が混ざって出てきてしまいます。
この問題は結構前から観測されているんですが、実はだんだんと改善されています。
最近Powershellなどのターミナルや、ブラウザでは正しく入力できるようになったのですが、
一部アプリ(メモ帳など)や一部サイトなどではまだ問題が観測されます。
そこでこの問題を補正する Windows 常駐ツール KanaFix を Rust で作りました。この記事では、その仕組みと使い方を紹介していきます。
まず原因を実測してみた
まずは何が起きているのかをちゃんとキャプチャして確かめました。素の Win32 ウィンドウに届くメッセージを記録するプローブを用意して、iPad から日本語を打ってみました。
わかったことは、大きく3つ。
1. そもそもPC側ではIME変換が起きていない
届く文字は、すべて VK_PACKET(仮想キーコード 0xE7)を伴う WM_CHAR でした。WM_IME_STARTCOMPOSITION のような IME関連のメッセージは一切来ません。VK_PACKET は SendInput + KEYEVENTF_UNICODE が生成するものです。SendInput は、キーボードやマウスの操作をプログラムから擬似的に送り込む Win32 API の関数で、KEYEVENTF_UNICODE は「これは仮想キーじゃなく Unicode 文字そのものだよ」と指定するフラグです。つまり、iPad 側で日本語変換を済ませてから、確定済みの Unicode を Windows へ流し込んでいるわけですね。Windows ホスト側の変換エンジン(TSF / IMM32)は、まるっとバイパスされていました。
まああたりまえっちゃあたりまえで、iPadのキーパッドで変換をしているからですね。
2. 「読みを打つ→Backspaceで消す→打ち直す」をやっている
時系列で並べてみると、入力はこんな手順になっていました。
VK_PACKET き ょ う は し ご と ← 読みをそのまま実文字として注入
Backspace x7 ← 読みを消去
VK_PACKET 今 日 は 仕 事 ← 変換後テキストを注入
iPad はホストアプリの中に変換候補ウィンドウを描けません。なので、いったん読みを確定文字として打ち込んで、それを 合成 Backspace で消してから、変換後テキストを注入し直す、という作業をしています。
3. なぜメモ帳だけ壊れるのか
旧来の Win32 エディットコントロールや大半のアプリは、この「Backspace して打ち直す」をちゃんと処理してくれます。読みはきれいに消えて、変換結果に置き換わります。
ところが メモ帳(TSF / RichEdit ベース) は、ミリ秒単位で飛んでくる消去と注入のバーストを処理し切れず、読みを消し終わる前に変換結果が入ってきて二重化してしまう。これが原因でした。
どう直したか — バーストを「間引く」
原因さえわかれば、対策はシンプルです。問題は「ホスト側の変換」ではなく、注入されてくる VK_PACKET と Backspace のストリームそのもの。
これがメモ帳に届く前に横取りして、エディタが追いつける速さに整えてあげればいいわけです。
KanaFix は グローバルな低レベルキーボードフック(WH_KEYBOARD_LL) を使って、対象アプリが前面にいるときだけイベントを捕まえ、ワーカースレッドから一定間隔(既定6ms)で再注入します。WH_KEYBOARD_LL は SetWindowsHookEx という Win32 API で仕掛けるフックの種類で、キー入力が各アプリに届く 前 に、システム全体の入力を横取りできるしくみです。横取りした入力をそのまま流すか捨てるかは CallNextHookEx を呼ぶかどうかで決められます。確定バーストをほんの少し引き伸ばしてあげることで、メモ帳の TSF が消去と注入の競合に負けなくなる、という仕組みです。
メモ帳のプロセスに DLL を注入したりはしません。再注入したイベントには dwExtraInfo に印を付けておいて、自分が流したものを自分でまた拾ってしまうフィードバックループを防いでいます。
使い方
- GitHub Releases から
kanafix.exeを入手します。 - 実行します(初回に設定ファイル
kanafix.tomlを同じ場所へ自動生成します)。 - 対象アプリ(既定はメモ帳)で日本語を入力します。
- 終了は実行中ウィンドウで Ctrl+C。止めれば補正は完全に無効化されます。
入手後の検証と「不明な発行元」対処
現在 kanafix.exe はまだコード署名されていないため、Windows が SmartScreen / 「不明な発行元」の警告を表示することがあります。次の手順でファイルの完全性を確認し、ブロックを解除できます。
# 1. 配布ハッシュと一致するか検証する
Get-FileHash .\kanafix.exe -Algorithm SHA256
# 出力されたハッシュが Releases の SHA256SUMS.txt と一致することを確認
# 2. ダウンロード由来のブロック(Mark of the Web)を解除する
Unblock-File .\kanafix.exe
SmartScreen の画面が出た場合は「詳細情報」→「実行」で起動できます。
設定ファイル
apps = ["notepad.exe"] # 補正したいアプリのexe名(小文字・複数可)
pace_ms = 6 # 再注入の間隔(ms)。直り切らなければ増やす(例: 12, 20)
apps… フォアグラウンドのプロセス名と大小無視で一致したときだけ補正します。他のアプリや他のキーには一切触れません。pace_ms… 大きいほど安全側(入力の反映がほんのわずか遅くなります)。環境によって直り切らないときは、ここを少しずつ増やしてみてください。
安全性について
入力を扱うツールなので、内部動作の懸念事項をつらつらと。
- グローバルキーボードフックを使いますが、キーストロークの記録・保存はしません。対象アプリが前面のときの
VK_PACKET/Backspaceを再注入するだけです。 - 入力を一度奪って打ち直す方式なので、もし挙動が不安定になったら Ctrl+C で即停止すれば、素の入力に戻ります。
中身が気になる方はソースを読んでみてください。
まとめ
- iPad の Windows App から日本語を打つと、変換が iPad側で完結して、確定済みの Unicode が注入される。
- そのときの「読みを打つ→Backspaceで消す→打ち直す」バーストを、新しいメモ帳が処理し切れずに文字が二重化してしまう。
- KanaFix は、対象アプリが前面のときだけこのバーストを横取りして再ペーシングし、エディタが追従できるようにして直す。
同じ症状で困っている方の助けになれば嬉しいです。要望やバグ報告、質問はX@pan_afrayzへどうぞ。
- リポジトリ: misc1999/KanaFix
- ライセンス: MIT