環境
Windows7
AutoHotKey 1.1.25.01
日本語環境で使うなら、絶対にAutoHotKey本体をUnicode版にすべき。そうでないと面倒が多すぎるので、以下はその前提で記述する。
概要
AutoHotKeyをTextExpanderのようなスニペット展開アプリとして利用しています。
実装としては、ホットストリングで文字列をペーストする関数をコールするという感じで。
::ahk::
PasteString("AutoHotKey")
Return
んで、このPasteString()
関数の中身の話。
方法
AutoHotKeyでTextExpanderっぽい動きをするには、以下のような方法がある。
- 自動置換型ホットストリング
- スクリプト実行型ホットストリング
-
Send
系コマンド - クリップボードに格納してペーストする
-
Send
/ControlSend
でCtrl+V
を送る -
PostMessage
/SendMessage
でWM_PASTE
メッセージを送る
-
-
入力はホットストリングで、出力をどうするかという話になる。これがまあ、見事にそれぞれ一長一短。
自動置換型
この場合、IMEオンで実行すると、英文字が日本語入力になってしまうのが最大の問題。
例えば、:0:btw::by the way
とすると、byてぇわy
という入力になってしまう。
また、変換未確定状態になるので、いちいち確定するのも面倒。
IMEオフで実行するなら、上記の問題はないし日本語も送れちゃうが、後述のスクリプト実行型と違い、これそのものにIMEオフにする処理を仕込むことはできない。
*
オプションを指定するホットストリングでなければ、終了文字のホットキーにIMEオフにする処理を仕込むという対策はいちおう考えられる。
IME.ahkを使えば、IME_SET(0)
でIMEをオフにできるので、例えば;
を終了文字とするならこんな感じ。
~`;:: ; `;`キーの押し下げ時、そのままキー入力を通しつつIMEの状態を取得、かつオフにする
ime_status := IME_GET()
IME_SET(0)
Return
`; up:: `;`キーを離した時、IMEの状態をもとに戻す
; Sleep, 100 ; 戻るタイミングが早すぎるようなら
IME_SET(ime_status)
Return
もちろん、#If !IME_Get()
として、IMEがオフの場合のみ有効、といった形にもできる。
スクリプト実行型
[共通]ホットストリングの削除が間に合わない場合がある
どうもホットストリングの削除を待たずにスクリプトの実行を開始するようで、削除が間に合わない場合がある。
ホットストリングを主に文字入力以外に用いるならこのほうがよかろうが、他の用途がメインの人はめったにいないと思う。
Send
系コマンド
Send
系コマンドの場合、IMEオン時の問題は自動置換型と同様だが、こちらはホットストリング単独で回避可能。
例えば以下のようにすれば問題ない。
::btw::
ime_status := IME_GET()
IME_SET(0)
Send, by the way
Sleep, 100 ; 送信中に戻らないように待機
IME_SET(ime_status)
送る文字列が長いと、送信中にIMEオンに戻ってしまうことがあるので、Sleep
を入れるなどの対策が必要。
あと、Google日本語入力だとオンに戻った時通知が出るのがちょっとうっとうしかった。これはGoogle日本語入力側でモード表示をオフにすることで対応。
修飾キーを示す記号(^!+#
)や特殊キーのキー名とみなされる文字列({Enter}
等)を送信する場合、文字列そのものをエスケープするか、Rawで送信する必要がある。(
実行速度は、PCのスペック次第とはいえ遅い。概ね、見えない誰かがズバズバ入力しているのが見える程度だと考えるべき。
クリップボードに格納してペーストする
[共通]OnClipboardChangeとの干渉
OnClipboardChangeラベル(関数)を利用してクリップボードの監視を行っている場合、当然これもトリガーになる。
関数版であれば、OnClipboardChange("<関数名>",0)
とすることで、監視を停止することで対策は可能。([AutoHotKey]OnClipboardChange()関数でクリップボード監視 - Qiita)
ラベル版でも、グローバル変数をフラグに使えば同じことはできるが、もうこの際みんな関数版使ったほうが幸せだと思う。
ただし、それもOnClipboardChange
とPasteString()
が同じスクリプト(#Include
関係を含む)でなければ成立し得ない。
Windows環境変数とか、OnMessage()関数によるプロセス間通信を使えば、OnClipboardChange
を含むスクリプトに監視停止させることは可能だが、めんどいし実行速度的にもよろしくなさそう。
Send
/ControlSend
でCtrl+V
を送る
これがまあ一番無難で、AutoHotKey Wikiのサンプルでも採用されている。
ただし、ターミナル作業中など、ペーストのキー操作がCtrl+Vではないとか、パスワード入力などで、文字入力だがペースト不可といったケースでは当然動作しない。
このような場合、#IfWinActive
等で判定してSendRaw
に切り替えるといった対策は可能。
PostMessage
/SendMessage
でWM_PASTE
メッセージを送る
この場合、キー操作ではなく「ペーストしろ」という命令自体を送るので、どのようなキー操作にペーストが割り当てられていようが対応可能。
ただし、そもそも動作しないウィンドウが少なくない。
例えば、コマンドプロンプトは右クリックから貼り付けは可能だが、WM_PASTE
命令は受け付けないようだ。
また、名前が割り当てられていないコントロールの場合も動作しないし、使える状況はかなり限られる。
どちらかといえば、ショートカットはCtrl+Vではないがペーストは可能な、一部のアプリで限定して利用すべきだろう。
PasteString(String)
{
OnClipboardChange("ClipChanged",0)
Backup := ClipboardAll
Clipboard := String
ControlGetFocus, focusedControl, A
SendMessage, 0x0302, , , %focusedControl%, A
Clipboard := Backup
OnClipboardChange("ClipChanged",1)
}
割り切りか、分岐か
使えるところでだけ使うと割り切れば、それこそWikiのサンプルスクリプト程度の処理でも問題はない。
どこでもCtrl+V
、どこでもSendRaw
でも、まあ便利ではある。
Ctrl+V
でペーストできない奴がバカなんだバーカ!!!っつって、コマンドプロンプトにClink入れるとか、そういう工夫の方向性だってある。
分岐いっぱいつけてスクリプトをゴージャスにしても実行速度がアレだし、自分のユースケースに合わせて必要な条件を整理して、あとは割り切って使うのが重要な気がする。