リモートデスクトップ接続でキーボードレイアウトを再初期化するプログラム
わたしは英語キーボードを使っているのですが,Linux からリモートデスクトップ接続したときに,入力ロケールが EN になってしまって困っていました。
-k en-us
のように英語キーマップを指定するとうまくいく……のですが,ログイン時に Windows 側の「入力言語」が「英語」になってしまいます(ログインダイアログに「EN」と表示される)。入力言語は Alt + Shift キーを押すと切り替えることができます。一応,コントロールパネルの「地域と言語のオプション」から「言語」タブの「テキストサービスと入力言語」で「詳細」ボタンを押し,「既定の言語」が「英語(米国) - US」になっているので「Microsoft Natual Input 2002」を選択すればいいはず……ですが,ログインするたびに再び EN になってしまうのが困り者です。
rdesktop の tips - daily dayflower
つまり,-k en-us
のように英語キーマップを指定するとキーボードの表記と入力される文字が一致する一方,入力ロケールが英語になるので IME がそのままでは(Alt + Grave を押しても)使えない,と。Alt + Shift を押して入力ロケールを切り替えることができるけど,いちいちそれをするのがめんどい。
これわたしのとこだけの特殊な状況かなと思ってたら,フランス語と英語の両キーボードを使ってる人も困っていたのでほっと?しました。
んで,いちいちコンパネ立ち上げて直すのも大変なのでちょっとしたコードを書いてみました。
#include <windows.h> #include <shlwapi.h> #pragma comment(lib, "shlwapi.lib") static void ShowErrorMessageBox(DWORD dwError = 0); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { LSTATUS r; HKL hkl; TCHAR layoutId[32]; DWORD dwRegType, cbLayoutId; if (! GetSystemMetrics(SM_REMOTESESSION)) return 0; cbLayoutId = sizeof(layoutId); r = SHRegGetValue(HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Preload"), TEXT("1"), SRRF_RT_REG_SZ, &dwRegType, layoutId, &cbLayoutId); if (r != ERROR_SUCCESS) { ShowErrorMessageBox(); return 1; } #if 0 MessageBox(NULL, layoutId, TEXT("Layout ID"), MB_OK | MB_ICONINFORMATION); #endif hkl = LoadKeyboardLayout(layoutId, KLF_SUBSTITUTE_OK | KLF_ACTIVATE); if (! hkl) { ShowErrorMessageBox(); return 1; } if (! SystemParametersInfo(SPI_SETDEFAULTINPUTLANG, 0, &hkl, SPIF_SENDCHANGE)) { ShowErrorMessageBox(); return 1; } return 0; } static void ShowErrorMessageBox(DWORD dwError /* = 0 */) { /* ほげほげ……ほんとは FormatMessage() して MessageBox() を出してるけど略 */ }
ポイントは,
- レジストリではローカルログオン時の入力ロケールの優先順位が
HKCU\Keyboard Layout\Preload
以下にあるんだけど,リモートデスクトップ接続するとなぜかシステム的にオーバーライドされる。なので,レジストリから入力ロケールを読み取ってSystemParametersInfo(SPI_SETDEFAULTINPUTLANG)
で再設定してる。これでうまくいく。 GetSystemMetrics(SM_REMOTESESSION)
で 0 以外(一応 1)が帰ると,それはリモートデスクトップ接続した環境。0 だとローカルログインした環境。これが一番ポータブルで安全で楽な確認手段。HKL
をそのままSystemParametersInfo()
に渡していてハマりました。実際はHKL
のさらにポインタ。SHRegGetValue()
は deprecated でRegGetValue()
使いなよ,と最近の SDK に書いてあるけど,使えるのは XP 64bit 以降(Vista 含む)。なので,あえて使ってます。EXPAND_SZ とかにも対応できるのでこれ系の関数を使うと楽。
レイアウトコードの長さが(ゆとりはみてるものの)決め打ちなのがいまいちですが,めんどいのでこれで。
注意点としては,これを実行した後に起動するプロセスに対してのみ入力ロケールの変更が効くところです。つまり,スタートアップとかに仕掛けても,たとえばそれ以前に起動したプロセスには効いていないし,エクスプローラ自身にも効いていません。まぁ,エクスプローラ自身は Alt + Shift で対処してください。