親指シフトキーボードをリモートデスクトップ経由で使う方法
以前、ちょっと実験で、PS/2接続の親指シフトキーボードを接続した環境から別のマシンにリモート接続を行い、そちらで動作するアプリケーション内で親指シフト入力を行えるか試したところ、少し弄る程度で上手くいってしまいました。そのための環境構築手順と、内部動作の詳細について記載してみたいと思います。
ここでの記載内容は、Windows 10(1607又は1703) + 親指の友 Mk-2ドライバを使って確認した内容です。富士通謹製のキーボードドライバを使う場合も似たような方法でいけるのでは? と踏んでいますが、レイアウトファイルとして指定されるものとは別のDLLが同封されていたりと、動作が私のドライバと異なる可能性もありますので、上手くいかなかった場合はごめんなさい。また、PS/2タイプの親指シフトキーボードをWindows標準の親指シフトキーボードドライバで使う場合は、ここで書かれた内容相当のものがシステム標準で実装されていますので、改めてこの記事の内容を行う必要はありません(ただ、ホスト側・コンソール側のどちらかがWin8以降の場合、モードずれ対策の制御が効くかどうかは不明です)。
尚、親指の友 Mk-2ドライバは未署名のため、x64環境下で使うのがそれなりに面倒なのですが、ホスト側(サーバ側・リモートでの接続を受け付ける側)をx64版・コンソール側(つまり、操作するキーボードが接続されている側)をx86版のシステムとすると、署名作業を行う必要がなくなります*1。
超ざっくりした手順
- コンソール側に、使用したいキーボードドライバをインストールし、使用可能な状態にします。
- ホスト側に、コンソール側で使用されるものと同じキーボードレイアウトファイル(例えば、親指の友 Mk-2ドライバであれば、kbol_611.dllかkbol_oas.dll)を、ホスト側の%SystemRoot%\System32\以下にコピーします。ホスト側にも同じドライバがインストールされていれば、既にファイルがあるはずですが、そうでない場合(そうしたくない場合)は手動でコピーしてください。
- ホスト側の以下のレジストリ値を、使用したいレイアウトファイルの名前に書き換えます。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\KeyboardType Mapping\JPN\00000002
"00000002"はレイアウトファイルとして親指の友 Mk-2ドライバのものを使う場合です。富士通製ドライバでどの値が来るかは不明ですが、もし左記を試して上手くいかないようであれば、"00020002"、"00000502"、"00020502"辺りを試してみて下さい(値が無い場合は作成)。
これで導入完了です。
リモート接続時に行われていること
リモートデスクトップのプロトコル上、端末間で転送されるキーコードは「コンソール側キーボードドライバから通知されたスキャンコード」です。つまり、スキャンコードを仮想キーコードや対応する文字として解釈する判断は、全てホスト側で行います(一応、コンソール側からUnicodeキャラクタを送ることもできるようなのですが、これ送ったらどういう扱いになるのだろう…。通常はこっちではなく、前者の方が使われているようです)。
このため、ホスト・コンソール間で使用するキーボードレイアウトの同期が必要です。この情報(キーボードレイアウトの識別子)の転送はセッション開始時に行われます(この辺りの詳しい動作は後述します)。ホスト側はこの情報より、必要なキーボードレイアウトを自身のシステム上で探してロードします。これによって、コンソール側のキーボードドライバが受信・処理したスキャンコードを、ホスト側で正しく扱うことができるようになる…という訳です。
これらの動作を、親指の友 Mk-2ドライバを使う場合の機能分担として示すと、親指シフトの同時打鍵処理(レイアウトファイルが理解できるキーシーケンスに変換する)やキー置換、零遅延モードの処理などは全てコンソール側で行われます。一方、スキャンコードから仮想キーコードへの変換(PF20や英字といった特殊なキーを含む)や、仮想キーコードから文字への変換(NICOLA配列のカナ等を含む)などはホスト側で処理されます。つまり、時間制約がシビアな処理については全てコンソール側で行われることになります。
このため、表示上のラグは感じるかもしれませんが、誤打鍵などの問題は特に発生しないはずです(普通にローカルで入力する場合と同程度)。また、ドライバの挙動はコンソール側で設定した内容がそのまま使えますので、コンソール側における普段使いの感覚で自然に使用できるのではないかと思います。
レイアウトファイルが選ばれるまでの挙動
システムが使用するキーボードレイアウトの情報は、全てレジストリ上に登録されています。この辺りの情報は「入力ロケール」として、一緒に使用するIME等と共にまとめて管理されているようです。
システムにインストールされている入力ロケールは以下のレジストリキーに登録されています。
HKEY_CURRENT_USER\Keyboard Layout\Preload\
ここで指定する値に対応する入力ロケールは、以下のレジストリキーに登録されています。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\
但し、新しいI/Fを使うIME(IMM32ではなくTSFを使うもの)の場合は、IMEに関する情報はこの辺りには現れないようです。例えば、Japnist 10とMS-IMEのみがインストールされている環境では、データが"00000411"になっているエントリ1つだけが存在します。"00000411"は前述したKeyboard Layouts\を見て頂くと分かるとおり、日本語入力ロケールに対応しているエントリです(IMEに関する情報を含まない)。
この入力ロケール毎キー配下に"Layout File"なる値があります。ここが、この入力ロケールで使用されるキーボードレイアウトファイル名です。ところが、試しにKeyboard Layouts\00000411\の値を見て頂くと分かると思いますが、ここには"kbdjpn.dll"が指定されています。お使いのキーボード配列がどうなっていようと、恐らく通常はこのような状態になっているはずです(US配列に設定していようが、富士通製ドライバを導入していようが、親指の友ドライバを使っていようが)。
実は、kbdjpn.dllには他のキーボードレイアウトを参照して渡す処理が実装されています(一応、自分自身が持つデフォルトの日本語配列を返す機能も持っているようなのですが)。現在の言語が日本語かつリモート接続ではない場合、以下のレジストリエントリで指定されたレイアウトファイルを参照します。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\Parameters\LayerDriver JPN
これがかの有名な、日本語ロケールにおけるレイアウトファイルを変更する場合に触るレジストリ値の正体です(よくUS配列を使いたい場合とかに弄られている印象があります)。親指の友 Mk-2ドライバをインストールする際にも、ここを書き換えて同封のレイアウトファイルが読み込まれるようにすることで親指シフト配列を実現しています。
一方、リモート接続された場合は少し挙動が変わります。リモートデスクトップのプロトコルでは、セッション開始時にコンソール側で使用しているキーボードレイアウトの識別子をホストへ伝達します(Client MCS Connect Initial PDU with GCC Conference Create Request時に送る、TS_UD_CS_COREのkeyboardType / keyboardSubType)。この識別子は、恐らくコンソール側で使用しているキーボードレイアウトファイル内に記載されているdwType / dwSubTypeの値を使うのですが、通常日本語配列のキーボードレイアウトでは、別途コンソール側のレジストリ設定で上書きしているようです。これは以下で指定します。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\Parameters\OverrideKeyboardType 及び OverrideKeyboardSubtype
通常の106キーボードであれば0x07(日本語) / 0x02(Microsoft / 106配列)になっているはずです。親指の友 Mk-2ドライバでは0x07 / 0x52(富士通 / FMV親指)にしています…が、レイアウトファイル内のNLS設定にて106キーボードを装うようにフラグを立てている為、Overrideの値をさらに上書きして、対外的には0x07 / 0x02として動作します(ややこしい…)。
ホスト側では、コンソールから送信されたこのType / SubTypeを用いて、このセッションで使用するキーボードレイアウトファイルを検索します。この為のテーブルもレジストリ上に登録されていまして、以下のキーが対応します。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\KeyboardType Mapping\JPN (Typeが0x07の場合…日本語なので)
各値の名前は、SubTypeバイトの上位ニブルのみ4ビット左シフトしたもののようなのですが、テーブルを見ていただければ分かる通り、他にも謎のビットが使われていたりして、正直よくわかりません。唯一Windows SDK付属のkbd.hから伺える変則タイプの挙動として、富士通親指シフトキーボードをリモートデスクトップ経由で使う場合はSubTypeを0x00020002として扱うようです(なのでテーブル上でも、"00020002"のエントリはf3ahvoas.dllというWindows標準親指シフトドライバのレイアウトファイルが指定されています)。
尚、前述の通り、親指の友 Mk-2ドライバのレイアウトファイルを使う場合は00000002の値を弄ればOKです。
終わりに
ひとまずこれで、親指シフトキーボード使用時にもリモート接続を活用しやすくなるのではないかと思います。また、レイアウトファイル周りの解析結果については、他のマイナー配列使いの方にとっても応用が効くかもしれません。
尚、記載内容の多くは、関連するDLLの解析(主にinput.dll、kbdjpn.dll)や外部I/F操作による値変化の追跡などから得ていますので、将来に渡って通用するかどうかの保証はありません。例によって、記載内容を使用する場合は自己責任の下でお願いします。
リモートデスクトッププロトコルについてはマトモな資料が存在しますので、詳しく知りたい方はそちらも参照してみてください。