作業中のメモ

よく「計算機」を使って作業をする.知らなかったことを中心にまとめるつもり.

PowerShell による GUI の自動化

どうも,筆者です.

今回は,Windows に搭載されている PowerShell を利用したスクリプトについて説明する.

経緯

あるソフトウェアで,データを呼び出し,スクショを撮って画像として保存する必要があった.2~3 枚なら手でやろうと思うけれども,200 枚,300 枚もあると流石に自動化したくなる.また,ソフトウェアは,Windows 版の GUI のものしかない.そのため,WindowsGUI を自動化する方法を探すこととなった.

UI Automation

調べたところ,WindowsPowerShell に,「UI Automation」を導入することで GUI の自動化ができるらしい.

早速,http://uiautomation.codeplex.com/ からダウンロードしてきた.保存場所は,「C:\winPowerShell\UIAutomation」というディレクトリを作成し,その中に展開した. そのままでは,PowerShellスクリプトを書いても実行できないらしい.そのため,管理者権限で「Windows PowerShell ISE」を起動させ,以下のコマンドで実行権限を変える.

Set-ExecutionPolicy RemoteSigned

ちなみに,はてなブログでは,ps1 でコードが PowerShell と認識されるらしい.便利になったものだ.

やりたいこと

さて,実際にコードを書いていくことになる.しかし,普段 GUI で行っている操作をいきなりスクリプトに落とそうとしても無理がある.そのため,色々調べて回った. 調べたところ,先程ダウンロードした UI Automation には,UIAutomationSpy.exe というスクリプトを書く際の補助ツールなるものがあるらしい. このツールを借りて,作業を行う.ここでは,以下のような流れで処理を行う.

  1. データがあるディレクトリまでの絶対パスと参照するデータ名を設定する.
  2. 起動させたいソフトウェアを絶対パスで指定し,起動する.
  3. ソフトウェアから,上記で示したデータを参照し,開く.
  4. Alt + PrintScreen により,画面をクリップボードに保存する.
  5. mspaint を実行し,ペイントを起動し,Ctrl + V でクリップボードの画像を貼り付ける.
  6. Ctrl + S で保存する.この時,新規保存となるので,保存先のパスと保存するファイル名を指定する.
  7. これをデータの数だけ繰り返す.

スクリプトを書く

実際に調べながらコードを書いてみた.ます,最初に記述すべき項目として,以下のものがある.

Import-Module C:\winPowerShell\UIAutomation\UIAutomation.dll

これは,UI Automation を利用することを PowerShell 側に知らせるコードである.後は自由に書くだけだが,筆者のように何も知らない人は調べるところから始まる.今回は,キー操作がメインとなるので,キーの一覧を探した.使いそうなものを以下に示しておく.

<#
   Left mouse button        LBUTTON = 0x01,
   Right mouse button       RBUTTON = 0x02,
   Control-break processing CANCEL = 0x03,
   BACKSPACE key            BACK = 0x08,
   TAB key                  TAB = 0x09,
   CLEAR key                CLEAR = 0x0C,
   ENTER key                RETURN = 0x0D,
   SHIFT key                SHIFT = 0x10,
   CTRL key                 CONTROL = 0x11,
   ALT key                  MENU = 0x12,
   PAUSE key                PAUSE = 0x13,
   CAPS LOCK key            CAPITAL = 0x14,
   ESC key                  ESCAPE = 0x1B,
   SPACEBAR                 SPACE = 0x20,
   PAGE UP key              PRIOR = 0x21,
   PAGE DOWN key            NEXT = 0x22,
   END key                  END = 0x23,
   HOME key                 HOME = 0x24,
   LEFT ARROW key           LEFT = 0x25,
   UP ARROW key             UP = 0x26,
   RIGHT ARROW key          RIGHT = 0x27,
   DOWN ARROW key           DOWN = 0x28,
   SELECT key               SELECT = 0x29,
   PRINT key                PRINT = 0x2A,
   EXECUTE key              EXECUTE = 0x2B,
   PRINT SCREEN key         SNAPSHOT = 0x2C,
   INS key                  INSERT = 0x2D,
   DEL key                  DELETE = 0x2E,
   HELP key                 HELP = 0x2F,
   0 key                    VK_0 = 0x30,
   1 key                    VK_1 = 0x31,
   2 key                    VK_2 = 0x32,
   3 key                    VK_3 = 0x33,
   4 key                    VK_4 = 0x34,
   5 key                    VK_5 = 0x35,
   6 key                    VK_6 = 0x36,
   7 key                    VK_7 = 0x37,
   8 key                    VK_8 = 0x38,
   9 key                    VK_9 = 0x39,
   A key                    VK_A = 0x41,
   B key                    VK_B = 0x42,
   C key                    VK_C = 0x43,
   D key                    VK_D = 0x44,
   E key                    VK_E = 0x45,
   F key                    VK_F = 0x46,
   G key                    VK_G = 0x47,
   H key                    VK_H = 0x48,
   I key                    VK_I = 0x49,
   J key                    VK_J = 0x4A,
   K key                    VK_K = 0x4B,
   L key                    VK_L = 0x4C,
   M key                    VK_M = 0x4D,
   N key                    VK_N = 0x4E,
   O key                    VK_O = 0x4F,
   P key                    VK_P = 0x50,
   Q key                    VK_Q = 0x51,
   R key                    VK_R = 0x52,
   S key                    VK_S = 0x53,
   T key                    VK_T = 0x54,
   U key                    VK_U = 0x55,
   V key                    VK_V = 0x56,
   W key                    VK_W = 0x57,
   X key                    VK_X = 0x58,
   Y key                    VK_Y = 0x59,
   Z key                    VK_Z = 0x5A,
   Left Windows key         LWIN = 0x5B,
   Right Windows key        RWIN = 0x5C,
   Applications key         APPS = 0x5D,
   Numeric keypad 0 key     NUMPAD0 = 0x60,
   Numeric keypad 1 key     NUMPAD1 = 0x61,
   Numeric keypad 2 key     NUMPAD2 = 0x62,
   Numeric keypad 3 key     NUMPAD3 = 0x63,
   Numeric keypad 4 key     NUMPAD4 = 0x64,
   Numeric keypad 5 key     NUMPAD5 = 0x65,
   Numeric keypad 6 key     NUMPAD6 = 0x66,
   Numeric keypad 7 key     NUMPAD7 = 0x67,
   Numeric keypad 8 key     NUMPAD8 = 0x68,
   Numeric keypad 9 key     NUMPAD9 = 0x69,
   Multiply key             MULTIPLY = 0x6A,
   Add key                  ADD = 0x6B,
   Separator key            SEPARATOR = 0x6C,
   Subtract key             SUBTRACT = 0x6D,
   Decimal key              DECIMAL = 0x6E,
   Divide key               DIVIDE = 0x6F,
   F1 key                   F1 = 0x70,
   F2 key                   F2 = 0x71,
   F3 key                   F3 = 0x72,
   F4 key                   F4 = 0x73,
   F5 key                   F5 = 0x74,
   F6 key                   F6 = 0x75,
   F7 key                   F7 = 0x76,
   F8 key                   F8 = 0x77,
   F9 key                   F9 = 0x78,
   F10 key                  F10 = 0x79,
   F11 key                  F11 = 0x7A,
   F12 key                  F12 = 0x7B,
   NUM LOCK key             NUMLOCK = 0x90,
   SCROLL LOCK key          SCROLL = 0x91,
#>

これは,実際に実装されているものらしい.これを利用しつつコードを書く.

パラメータの設定

まずは,パラメータの設定からはじめる.ここでは,ファイル名の末尾に連番の数字を振ってデータを識別する.

# データファイルがあるディレクトリパス
$dataPath = "C:\Users\...\dataDir"
# 画像を保存するディレクトリパス
$savePath = "C:\Users\...\imgDir"

# 起動するソフトウェア
$exePath = "C:\Users\...\hoge.exe";
# ファイル名の開始と終了
$startFileNum = 1;
$endFileNum = 500;

ソフトウェアの起動

次に,ソフトウェアを起動するコードを書く.ここでは,最大化して表示する.

########################################################
# 指定したパスにあるプログラムを起動し、最大化して表示 #
########################################################
$process = Start-Process $exePath -PassThru -windowstyle Maximized | Get-UiaWindow;

この「Start-Process」でプロセスを起動する.Get-UiaWindow は,起動したソフトウェアをアクティブにするものだという認識だ(細かいことは知らない).

対象とするデータを開く

そして,ソフトウェアから対象とするデータを開く.ここでは,「hoge001.dat」,「hoge002.dat」という風に名前がついているものとする.連番を $idx で指定する.また,「Alt → r → u」で読み込み画面が開くものとする(通常のソフトは Ctrl + O のはず,これが特殊なだけ).

# 対象のファイルを設定
$idx = 2; # 番号
$padNum = $idx.ToString("000");              # 0 詰め 3 桁の数値とする(2 なら 002, 10 なら 010)
$targetFile = "hogehoge" + $padNum + ".dat"; # 開くファイル
$imgFile = "foo" + $padNum + ".png";         # 保存するファイル

# ここでは,Alt を押した後,r → u の順でキーを押すと,開く画面が出る
$process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null; # Alt キーを押す
$process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_R) | Out-Null; # r キーを押す
$process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_U) | Out-Null; # u キーを押す

おなじみの「開く」という画面が出る.ここでは,$dataPath で指定したパスを入力し,対象とするファイルを開く. まず,「開く」という画面をアクティブにする.ここで,「UIAutomationSpy.exe」を使用した.アクティブにするには,

$openWnd = Get-UiaWindow -Name '開く';

とすればよいらしい.代入する変数名は何でもよい.次に,パスを設定する.パスは,F4 キーを押して入力モードにし,Ctrl + A で全選択,Delete キーで削除,その後,$dataPath で指定したパスを書き込み,Enter を押すという流れになる.これを,コードで書くと以下のようになる.

$openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::F4) | Out-Null;     # F4 キーを押す
$openWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; # Ctrl キーを押し続ける(KeyDown に注目)
$openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A) | Out-Null;   # A キーを押す(全選択状態となる)
$openWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;   # Ctrl キーを離す(KeyUp)
$openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::DELETE) | Out-Null; # Delete キーを押す(削除)
$openWnd.Keyboard.TypeText($dataPath) | Out-Null;                                    # テキストを書き込む
$openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null; # Enter を押す(RETURN として定義)

最後に,$targetFile で指定したファイル名を「ファイル名(N):」の欄に記入する必要がある.これが面倒であった.UIAutomationSpy.exe には,available patterns という項目があり,ここに,「ValuePattern」や「TextPattern」など,設定するための項目が記されている.今回は, http://uiautomation.codeplex.com/wikipage?title=Object%20model&referringTitle=Documentation で使い方を調べた.英語だが,何とかなる.その結果,以下のようなコードが書ける.

# リスト中にあるアイテムを選択して開く
$selectItem = $openWnd | Get-UiaEdit -Name 'ファイル名(N):';
$selectItem.Value = $targetFile;
$selectItem.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null;
# 0.5 秒停止
Start-Sleep -m 500

画面をクリップボードに保存

開いたデータの画面を PrintScreen でクリップボードにコピーする.

# 表示された データファイルを画像として保存
$process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null;      # Alt キーを押す
$process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::SNAPSHOT) | Out-Null; # PrintScreen キーを押す
$process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null;        # Alt キーを離す

ペイントに貼り付け

ペイントは「mspaint」で起動できるので,絶対パスの指定はいらない.

# ペイントを起動
$paintWnd = Start-Process mspaint -PassThru -windowstyle Maximized | Get-UiaWindow;
# クリップボードにある画像を貼り付ける
$paintWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;
$paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_V) | Out-Null;
$paintWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;

名前を付けて保存

# 保存
$paintWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;
$paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_S) | Out-Null;
$paintWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;

$openWnd = Get-UiaWindow -Name '名前を付けて保存';
$openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::F4) | Out-Null;
$openWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;
$openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A) | Out-Null;
$openWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;
$openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::DELETE) | Out-Null;
$openWnd.Keyboard.TypeText($savePath) | Out-Null;
$openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null;

$saveProcess = $openWnd | Get-UiaEdit -Name 'ファイル名:'
$saveProcess.Value = $imgFile;
$saveProcess.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null;

# ペイントを閉じる
$paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null;
$paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_F) | Out-Null;
$paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_X) | Out-Null;
# 0.5 秒停止
Start-Sleep -m 500

スクリプト全体

以上のスクリプトを for 文を用いてループ処理にかければよい.

Import-Module C:\winPowerShell\UIAutomation\UIAutomation.dll

# データファイルがあるディレクトリパス
$dataPath = "C:\Users\...\dataDir"
# 画像を保存するディレクトリパス
$savePath = "C:\Users\...\imgDir"

# 起動するソフトウェア
$exePath = "C:\Users\...\hoge.exe";
# ファイル名の開始と終了
$startFileNum = 1;
$endFileNum = 500;

########################################################
# 指定したパスにあるプログラムを起動し、最大化して表示 #
########################################################
$process = Start-Process $exePath -PassThru -windowstyle Maximized | Get-UiaWindow;

# 対象のファイルを設定
for ($idx = $startFileNum ; $idx -le $endFileNum ; $idx++)
{
    $padNum = $idx.ToString("000");              # 0 詰め 3 桁の数値とする(2 なら 002, 10 なら 010)
    $targetFile = "hogehoge" + $padNum + ".dat"; # 開くファイル
    $imgFile = "foo" + $padNum + ".png";         # 保存するファイル

    # ここでは,Alt を押した後,r → u の順でキーを押すと,開く画面が出る
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null; # Alt キーを押す
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_R) | Out-Null; # r キーを押す
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_U) | Out-Null; # u キーを押す

    $openWnd = Get-UiaWindow -Name '開く';
    $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::F4) | Out-Null;     # F4 キーを押す
    $openWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; # Ctrl キーを押し続ける(KeyDown に注目)
    $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A) | Out-Null;   # A キーを押す(全選択状態となる)
    $openWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;   # Ctrl キーを離す(KeyUp)
    $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::DELETE) | Out-Null; # Delete キーを押す(削除)
    $openWnd.Keyboard.TypeText($dataPath) | Out-Null;                                    # テキストを書き込む
    $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null; # Enter を押す(RETURN として定義)

    # リスト中にあるアイテムを選択して開く
    $selectItem = $openWnd | Get-UiaEdit -Name 'ファイル名(N):';
    $selectItem.Value = $targetFile;
    $selectItem.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null;
    # 0.5 秒停止
    Start-Sleep -m 500



    # 表示された データファイルを画像として保存
    $process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null;      # Alt キーを押す
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::SNAPSHOT) | Out-Null; # PrintScreen キーを押す
    $process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null;        # Alt キーを離す



    # ペイントを起動
    $paintWnd = Start-Process mspaint -PassThru -windowstyle Maximized | Get-UiaWindow;
    # クリップボードにある画像を貼り付ける
    $paintWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;
    $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_V) | Out-Null;
    $paintWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;



    # 保存
    $paintWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;
    $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_S) | Out-Null;
    $paintWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;

    $openWnd = Get-UiaWindow -Name '名前を付けて保存';
    $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::F4) | Out-Null;
    $openWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;
    $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A) | Out-Null;
    $openWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;
    $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::DELETE) | Out-Null;
    $openWnd.Keyboard.TypeText($savePath) | Out-Null;
    $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null;

    $saveProcess = $openWnd | Get-UiaEdit -Name 'ファイル名:'
    $saveProcess.Value = $imgFile;
    $saveProcess.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null;

    # ペイントを閉じる
    $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null;
    $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_F) | Out-Null;
    $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_X) | Out-Null;
    # 0.5 秒停止
    Start-Sleep -m 500
}

これで,スクリプトが完成した.これを,guiAuto.sp1 とでも命名し,適当なディレクトリに保存する.その後,Windows PowerShell ISE を起動し,「(ファイルがあるパス)\guiAuto.sp1」とするとスクリプトが走る.

今後

今回は,調べつつただ書いただけのものができた.関数も使えるらしいので,次やるときは,関数を利用して,機能ごとに分けたものを作成したい.