にせねこメモ

はてなダイアリーがUTF-8じゃないので移ってきました。

AutoHotkeyで入力キーボートレイアウトの切替を行う

Windowsでは複数の入力言語やキーボードレイアウト(IMEを含む)を設定できる。日本語IMEは日本語入力のオンオフを切り替えられるのでそれだけを利用している場合には触れることは少ないかもしれないが、Google日本語入力ATOKのような別のIMEを併用したり、中国語、タイ語など他の言語のIMEやキーボードを使う場合、切替が必要となる。

この切替は「Alt+Shift」(入力言語の切替)、「Ctrl+Shift」(同言語でのキーボードレイアウトの切替)、あるいはWindows 8以降では「Windowsロゴキー+Space」(入力言語・キーボードレイアウトの切替)のショートカットキーで行うことができる。また、設定で特定のキーボードレイアウトへ切り替えるショートカットを指定することができるが、設定できるキーの組合せに制限があったり、うまく設定が反映されなかったりすることがあるので、AutoHotkeyでキーボードレイアウトの切替を定義できれば便利だろう。


AutoHotkeyはSendMessage命令を利用してWindows APIのウィンドウメッセージを投げることができる。これで望む入力言語・キーボードに対応するinput locale identifier (入力ロケール識別子)を指定してWM_INPUTLANGCHANGEREQUESTを投げると、入力言語・キーボードを切り替えることができる。
したがって、このinput locale identifierを適切に取得すれば、指定したキーボードレイアウトを切り替えるホットキーが定義できる。これを取得するために、Windows APIのLoadKeyboardLayout関数やGetKeyboardLayout関数を利用することができる。

検証環境

Windows デスクトップ アプリケーションのいくつかで動作を確認したが、動かないアプリケーションもあるかもしれない。

方法1: LoadKeyboardLayout関数を使う場合

LoadKeyboardLayout関数のsyntaxは次のようになっている。

HKL WINAPI LoadKeyboardLayout(
  _In_ LPCTSTR pwszKLID,
  _In_ UINT    Flags
);

LoadKeyboardLayoutの引数のpwszKLIDは「input locale identifierの名前」であり、16進数の数字8文字のからなる文字列で、レジストリ

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Keyboard Layouts

の下にある項目のものである。ここから、利用したいキーボードを表す8桁の16進数字をメモしておく。

なお、既定のキーボードについては次のページからも参照できる。


目的のキーボードを探すのに役立つ情報として、pwszKLIDの下4ケタはlanguage identifierである。language identifierは次のページから参照できる。

また、上4桁については、その言語のデフォルトキーボードは0000となるようである。ある言語のキーボードレイアウトにいくつか種類がある場合、0001, 0002, と上4桁の番号が順番に増えたりするらしい。一方でMSKLCなどでキーボードレイアウトを自作した場合などは上4桁がa000などとなる場合もある。

実装例

ここでは、例として「日本語・Microsoft IME」と「ロシア語 (タイプライター)」に切り替えるホットキーを設定してみる。
まず、pwszKLIDとして与えるべきものは

キーボード pwszKLID
日本語・Microsoft IME 00000411
ロシア語 (タイプライター) 00010419

である。

これらを引数に指定してLoadKeyboardLayout関数を呼び出してinput locale identifierを取得し、取得したinput locale identifierを指定してPostMessageでWM_INPUTLANGCHANGEREQUEST (0x50)メッセージをアクティブウィンドウに投げれば切り替えができる。

F11で日本語・Microsoft IME、F12でロシア語 (タイプライター)に切り替えるAutoHotkeyスクリプトは次のように書ける。

F11::
  ;日本語・MS-IMEへの切替
  ja := DllCall("LoadKeyboardLayout", "Str", "00000411", "Int", 1)
  PostMessage 0x50, 0, ja,, A  ;WM_INPUTLANGCHANGEREQUEST
Return

F12::
  ;ロシア語 (タイプライター)への切替
  ru := DllCall("LoadKeyboardLayout", "Str", "00010419", "Int", 1)
  PostMessage 0x50, 0, ru,, A  ;WM_INPUTLANGCHANGEREQUEST
Return

方法2: GetKeyboardLayout関数を使う場合

いちいちレジストリエディタ開いてinput locale identifierの名前を調べるのは面倒である。どうせ切り替えるキーボードは普段使っているものなので、現在使用しているキーボードから直接input locale identifierを取得してしまえばよい。


GetKeyboardLayout関数は現在選択されているinput locale identifierを返す。
しかし、LoadKeyboardLayoutの場合と異なり、32ビットのinput locale identifierのMSB (最上位ビット)が1の場合でも正整数が返ってくる。要するに32ビット符号なし整数扱いになっているようだ。
一方でPostMessegeでWM_INPUTLANGCHANGEREQUESTを投げる時に指定するinput locale identifierは符号あり整数にする必要があるようで、GetKeyboardLayout関数が返した数値をそのまま用いると、32ビットアプリでは動くが、64ビットアプリでは動作しないようである。
そのため、自分で変換する必要がある。


まず、次のようなスクリプトを使って、使っている入力言語・キーボードレイアウトのinput locale identifierを取得する。
結果はキー入力として出力されるので、テキストエディタなどを開いて調べたいキーボードレイアウトに切り替えた状態でF10を押す。

F10::
  SetFormat, Integer, H
  WinGet, WinID,, A
  ThreadID:=DllCall("GetWindowThreadProcessId", "UInt", WinID, "UInt", 0)
  InputLocaleID:=DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
  Send, %InputLocaleID%
Return


すると、次のようなinput locale identifierが取得できる。

キーボード input locale id
日本語・Microsoft IME 0x4110411
ロシア語 (タイプライター) 0xF0080419

日本語・Microsoft IMEの場合は32ビットのMSBが0であるため問題ないが、ロシア語 (タイプライター)の場合はMSBが1なため負数で表す必要がある。

次のWebサービスのようなものを用いて、32ビット16進数表記を符号あり10進整数に変換する。

すると、0xF0080419は-267910119であることがわかる。これをWM_INPUTLANGCHANGEREQUESTメッセージのlParamに指定する。


F11で日本語・Microsoft IME、F12でロシア語 (タイプライター)に切り替えるAutoHotkeyスクリプトは次の様になる。

F11::
  ;日本語・MS-IMEへの切替
  PostMessage 0x50, 0, 0x4110411,, A  ;WM_INPUTLANGCHANGEREQUEST
Return

F12::
  ;ロシア語 (タイプライター)への切替
  PostMessage 0x50, 0, -267910119,, A  ;WM_INPUTLANGCHANGEREQUEST
Return