ウインドウを管理する方法

AutoHotkeyの最も簡単で便利な機能の1つは、ウィンドウを操作するキーボードショートカット(ホットキー)を作成することができることです。スクリプトは、ほとんどすべてのウィンドウをアクティブ化、クローズ、最小化、最大化、復元、非表示、表示、移動することができます。これは、適切なWin関数を呼び出して、タイトルやその他の基準でウィンドウを指定することで行われます。

Run "notepad.exe"
WinWait "Untitled - Notepad"
WinActivate "Untitled - Notepad"
WinMove 0, 0, A_ScreenWidth/4, A_ScreenHeight/2, "Untitled - Notepad"

この例では、新しいメモ帳ウィンドウを開き、それを主画面の一部(幅の1/4、高さの1/2)を埋めるように移動させます。試す方法は、「サンプルコードを実行する方法」を参照してください。

ウィンドウを操作するための関数の多くは、それほど多くないので詳しく説明しません。例えば、ウィンドウをアクティブにせずに最小化するには、WinActivateWinMinimizeに置き換えます。ウィンドウを操作したり、情報を取得したりできる関数の一覧は、Win関数を参照してください。

このチュートリアルの大部分は、操作するウィンドウを特定する方法に関するものです(これが最も面倒な部分だからです)。例えば、上記の例には様々な問題があります。

基本を押さえた上で、ひとつずつ解決していきます。

ヒント:AutoHotkeyには、ウィンドウのタイトル、クラス、プロセス名を確認するために使用できるWindow Spyというスクリプトが付属しています。クラス名やプロセス名は、タイトルだけでウィンドウを特定することができない場合によく使われます。Window Spyは、スクリプトのトレイメニューまたはAutoHotkey Dashで見つけることができます。

タイトルマッチング

ウィンドウをタイトルで指定する場合、いくつかの注意点があります。

詳しくは「マッチング動作」をご覧ください。

アクティブウインドウ

アクティブなウィンドウを参照するには、ウィンドウのタイトルの代わりに文字「A」を使用します。例えば、アクティブなウィンドウを最小化することができます。

WinMinimize "A"

ラストファウンドウィンドウ

WinWaitWinExistWinActiveWinWaitActiveWinWaitNotActiveは、一致するウィンドウを見つけると、そのウィンドウを ラストファウンドウインドウに設定します。ほとんどのウィンドウ関数では、ウィンドウのタイトル(および関連するパラメータ)を省略することができ、その場合、最後に見つかったウィンドウをデフォルトとして使用します。事例:

Run "notepad.exe"
WinWait "Untitled - Notepad"
WinActivate
WinMove 0, 0, A_ScreenWidth/4, A_ScreenHeight/2

これにより、ウィンドウのタイトルを繰り返す必要がなくなり、時間の節約になり、ウィンドウのタイトルを変更する必要がある場合にスクリプトを更新しやすくなり、コードを読みやすくすることができます。一致するウィンドウが複数ある場合や、ウィンドウが「見つかった」後にウィンドウのタイトルが変更された場合でも、毎回同じウィンドウで動作するようにすることで、スクリプトの信頼性を高めています。また、スクリプトをより効率的に実行できるようになりますが、それほど大きな差はありません。

ウィンドウズクラス

ウィンドウクラスは、ウィンドウを作成するためのテンプレートとして使用される属性のセットです。ウィンドウのクラス名は、アプリやウィンドウの目的に関連していることがよくあります。ウィンドウのクラスは、ウィンドウが存在する間は変化しないので、タイトルで識別することが現実的でない場合や不可能な場合に、ウィンドウを識別するためにこのクラスを使用することがよくあります。

例えば、ウィンドウのタイトル "Untitled - Notepad" の代わりに、ウィンドウのクラス(この場合、システムの言語に関係なく"Notepad"である)を使用することができます。

Run "notepad.exe"
WinWait "ahk_class Notepad"
WinActivate
WinMove 0, 0, A_ScreenWidth/4, A_ScreenHeight/2

ウィンドウクラスは、上図のように "ahk_class"という単語を使ってタイトルと区別します。複数の条件を組み合わせる場合は、ウィンドウのタイトルを最初に記載します。事例:"Untitled ahk_class Notepad".

Related: ahk_class

Process Name/Path

Windowsは、"ahk_exe"という単語の後にプロセスの名前またはパスを付けることで、それを作成したプロセスによって識別することができます。事例:

Run "notepad.exe"
WinWait "ahk_exe notepad.exe"
WinActivate
WinMove 0, 0, A_ScreenWidth/4, A_ScreenHeight/2

Related: ahk_exe

プロセスID(PID)

各プロセスにはID番号があり、プロセスが終了するまで一意であります。これを利用して、新しいプロセスによって作成される以外のメモ帳ウィンドウを無視するようにすることで、メモ帳の例をより信頼性の高いものにすることができます。

Run "notepad.exe",,, &notepad_pid
WinWait "ahk_pid " notepad_pid
WinActivate
WinMove 0, 0, A_ScreenWidth/4, A_ScreenHeight/2

そのうちの2つは、Run関数の未使用のWorkingDirOptionsパラメータをスキップするためで、必要なもの(OutputVarPID)は、第4パラメータだからです。

アンパサンド(&)は参照演算子です。Run関数にnotepad_pid変数を参照渡し(つまり、値ではなく変数そのものを渡す)、関数が変数に新しい値を代入できるようにするために使用されます。すると、notepad_pidは実際のプロセスIDのプレースホルダーになります。

文字列"ahk_pid"は、変数notepad_pidが含むプロセスIDと、空白で区切って順番に書くだけで連結されます。結果は "ahk_pid 1040"のような文字列になりますが、数字は予測できません。

新しいプロセスで複数のウィンドウを作成する可能性がある場合、ウィンドウのタイトルとその他の条件をスペースで区切ることで組み合わせることができます。ウィンドウのタイトルは必ず最初に表示される必要があります。事例:"Untitled ahk_class Notepad ahk_pid " notepad_pid.

関連する ahk_pid

ウインドウID (HWND)

各ウィンドウにはID番号があり、ウィンドウが破壊されるまで一意であることが確認されています。プログラミングの用語では、これを「ウィンドウハンドル」またはHWNDと呼びます。最後に見つかったウィンドウのように便利ではありませんが、ウィンドウのIDを変数に格納することで、タイトルが変わってもスクリプトが任意の名前で参照することができます。最後に見つけたウィンドウは一度に1つだけですが、ウィンドウIDは変数名を作れるだけ使えます(配列を使うこともできます)。

ウィンドウIDは、WinWaitWinExistWinActiveによって返され、他のソースから来ることもあります。メモ帳の例は、これを利用するために書き換えることができます。

Run "notepad.exe"
notepad_id := WinWait("Untitled - Notepad")
WinActivate notepad_id
WinMove 0, 0, A_ScreenWidth/4, A_ScreenHeight/2, notepad_id

これは、WinWaitの戻り値を変数 "notepad_id"に代入しています。つまり、WinWaitがウィンドウを見つけると、その結果としてウィンドウのIDが生成され、スクリプトはこの結果を変数に格納するのです。"notepad_id"は、この例のために私が作った名前です。

ウィンドウのタイトルを括弧で囲み、関数名の直後に括弧を付けていることに注目してください。関数呼び出し文(つまり行頭の関数呼び出し)では括弧を省略することができますが、その場合、関数の戻り値を得ることはできません。

また、スクリプトは、ウィンドウを閉じたり、再アクティブ化したり、別の場所に移動させるなど、後で使用するために変数 notepad_idを保持することができます。

関連: ahk_id

タイムアウト

デフォルトでは、WinWaitは一致するウィンドウが表示されるまで無期限に待機します。トレイアイコンからスクリプトのメインウィンドウを開くことで、この現象が起きているかどうかを判断できます(ただし、トレイアイコンを無効にしている場合は除く)。デフォルトでは、通常ListLinesビューでウィンドウが開きます。WinWaitがまだ待機している場合は、行のリストの一番下に表示され、その後に待機を始めてからの秒数が表示されます。表示メニューから「更新」を選択しない限り、数値は更新されません。

この例を実行し、上記のようにメインウィンドウを開いてみてください。

WinWait "Untitled - Notpad"  ; (意図的な誤字)

スクリプトがウィンドウ待ちでスタックしている場合は、通常、スクリプトを終了するか再読み込みしてスタックを解除する必要があります。このような事態を未然に防ぐ(あるいは再発を防ぐ)には、WinWaitのTimeoutパラメータを使用します。例えば、この場合、ウィンドウが表示されるまで最大で5秒待ちます。

Run "notepad.exe",,, &notepad_pid
if WinWait("ahk_pid " notepad_pid,, 5)
{
    WinActivate
    WinMove 0, 0, A_ScreenWidth/4, A_ScreenHeight/2
}

if文の下のブロックは、WinWaitが一致するウィンドウを見つけた場合のみ実行されます。タイムアウトした場合、そのブロックはスキップされ、閉じ波括弧の後(その後にコードがある場合)に実行が継続されます。

なお、"WinWait "の後の括弧は、関数の結果をif文の条件など)で使用したい場合に必要です。関数の呼び出しそのものが、関数の結果の代用品と考えることができます。例えば、WinWaitがタイムアウト前にマッチを見つけた場合、結果は非ゼロとなる。if 1はif文の下のブロックを実行し、if 0はそれをスキップします。

別の書き方としては、待ち時間がタイムアウトしたら早めにリターンします(言い換えれば、アボートします)。事例:

Run "notepad.exe",,, &notepad_pid
if !WinWait("ahk_pid " notepad_pid,, 5)
    return
WinActivate
WinMove 0, 0, A_ScreenWidth/4, A_ScreenHeight/2

結果は、論理not演算子(!またはnot)を適用して反転させる。WinWaitがタイムアウトした場合、その結果は0となる。!0の結果は1なので、WinWaitがタイムアウトしたときにif文でreturnを実行します。

WinWaitの結果は、実際にはウィンドウのID(上記の通り)か、タイムアウトした場合はゼロになります。また、IDでウィンドウを参照したい場合は、if文の中で直接使用するのではなく、結果を変数に代入することができます。

Run "notepad.exe",,, &notepad_pid
notepad_id := WinWait("ahk_pid " notepad_pid,, 5)
if notepad_id
{
    WinActivate notepad_id
    WinMove 0, 0, A_ScreenWidth/4, A_ScreenHeight/2, notepad_id
}

変数名の繰り返しを避けるために、結果を変数に代入し、それがゼロでないことを確認する両方が可能です()を同時に使用することができます。

Run "notepad.exe",,, &notepad_pid
if notepad_id := WinWait("ahk_pid " notepad_pid,, 2)
{
    WinActivate notepad_id
    WinMove 0, 0, A_ScreenWidth/4, A_ScreenHeight/2, notepad_id
}

その場合、:=(代入)と===(比較)を混同しないように注意してください。例えば、myvar := 0の場合は新しい値を割り当て、毎回同じ結果(偽)を出すのに対し、if myvar = 0は、以前に割り当てられた値を0と比較します。

Expressions (Math etc.)

ウィンドウを移動させる場合、WinGetPos関数で取得できる以前の位置やサイズからの相対移動が便利なことが多いです。例えば、RCtrlを押しながら矢印キーを押すことで、アクティブなウィンドウを各方向に10ピクセルずつ移動させることができるホットキーのセットは次のとおりです。

>^Left::MoveActiveWindowBy(-10,   0)
>^Right::MoveActiveWindowBy(+10,   0)
>^Up::MoveActiveWindowBy(  0, -10)
>^Down::MoveActiveWindowBy(  0, +10)

MoveActiveWindowBy(x, y) {
    WinExist "A"  ; アクティブウィンドウをLast Found Windowにします。
    WinGetPos &current_x, &current_y
    WinMove current_x + x, current_y + y
}

この例では、何度もコードを繰り返すことを避けるために、関数を定義しています。xyは、各ホットキーで指定された2つの数値のプレースホルダーとなります。WinGetPosは現在の位置をcurrent_xcurrent_yに格納し、それをxyに加算しています。

このようなシンプルな式は、かなり見慣れたものになるはずです。詳しくは「」をご覧ください。ただし、すぐに覚える必要のない内容も多いのでご注意ください。