PowerShell

PowerShellでSendKeysを使ってみた

経緯

仕事で使うメッセンジャーツールで毎回同じメンバーのグループを作る必要があるのですが、
そのツールが提供する設定やらコマンドやらでは、その操作を自動化できそうになかったので、
しょうがなくSendKeys操作でやることにしました。
せっかくの機会なので、こういう場面で自動化しやすくするためにPowerShellの関数を作り、
その過程で得た知識の備忘録として記載。

SendKeysとは

・キーストローク(打鍵、キーを押すこと)によってキーに対応した情報をアプリに送ること。
 また、キーストローク単体でSendKeysと同じ意味で使われることもある。
・自動化するにあたっての最終手段です。(妥協策)

メリット

・Windowsの標準ツールで実装、および、利用することができる。
(.NETやWSHの環境で動作できるため。他はよく知りません...)
・他のアプリのCUIコマンドやAPIを覚える必要がないので、学習コストが少ない。

デメリット

・SendKeysによる処理が実行中の間は他の操作ができない。
(フォーカスが変更されるので意図しない結果になってしまう)

キーの表記方法

キー コードのサンプル ※()内は補足説明
英数字記号 "a", "123", "!", " "(Space)
アクション "{BS}", "{DEL}", "~"(Enter), "{LEFT}"(左矢印), "{F4}"
制御 "^"(Ctrlキー), "+"(Shiftキー), "%"(Altキー) ※1
エスケープ "{^}"(^), "{{}{}}"({})
複数指定 "^(ac)"(Ctrl+Aで全選択したのちCtrl+Cでコピー)
ループ "{a 5}"(aaaaa) ※2

※1 日本特有のキー(半角/全角、変換)やWindowsロゴキー、アプリケーションキーは利用できない。
※2 1つのキーに対して指定できる。
コードの一覧についてはこちらのドキュメントを参照してください。
SendKeys メソッド

実装における注意点

・フォーカス(SendKeys操作の対象)
 普段、キーで操作しているときと同様、前面に操作したいアプリを配置すること。
 →自動化処理のはじめにアプリを起動するか、もしくは、すでに起動中のアプリを前面にする。
・Wait
 →アプリがキーストロークして反応できる状態まで待つ必要がある。
 →ウィンドウのフォーカスだけではなく、ウィンドウ内のどこにフォーカスがくるのかも気にすること。
 →マシンの性能によっては、Wait時間を調整する必要が出てくる。
・その他
 →ショートカットキーについては事前に知っておくこと。

PowerShellでの実装

動作環境
・Windows10
・PowerShell 5.1

Add-Type -AssemblyName Microsoft.VisualBasic
Add-Type -AssemblyName System.Windows.Forms

<#
.Synopsis
   実行中の任意のプロセスにキーストロークを送る操作をします。
.DESCRIPTION
   パラメータのキーストローク、プロセス名がそれぞれ未指定の場合、何も実行されません。
   キーストロークのみが指定された場合は実行時のフォーカスへキーストロークを送り、
   プロセス名のみが指定された場合はフォーカスのみが指定されたプロセスに変更します。
.EXAMPLE
   Send-Keys -KeyStroke "test.%~" -ProcessName "LINE"

   このコマンドは既に起動中のLINEアプリに対して"test."と入力し、
   Altキーを押しながらEnterキーを押下する操作をしています。
#>
function Send-Keys
{
    [CmdletBinding()]
    [Alias("sdky")]
    Param
    (
        # キーストローク
        # アプリケーションに送りたいキーストローク内容を指定します。
        # キーストロークの記述方法は下記のWebページを参照。
        # https://msdn.microsoft.com/ja-jp/library/cc364423.aspx
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [string]
        $KeyStroke,

        # プロセス名
        # キーストロークを送りたいアプリケーションのプロセス名を指定します。
        # 複数ある場合は、PIDが一番低いプロセスを対象とする。
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true)]
        [string]
        $ProcessName,

        # 待機時間
        # コマンドを実行する前の待機時間をミリ秒単位で指定します。
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true)]
        [int]
        $Wait = 0
    )

    Process
    {
        $Process = ps | ? {$_.Name -eq $ProcessName} | sort -Property CPU -Descending | select -First 1
        Write-Verbose $Process", KeyStroke = "$KeyStroke", Wait = "$Wait" ms."
        sleep -Milliseconds $Wait
        if ($Process -ne $null)
        {
            [Microsoft.VisualBasic.Interaction]::AppActivate($Process.ID)
        }
        [System.Windows.Forms.SendKeys]::SendWait($KeyStroke)
    }
}

備考

Tabキーや方向キーを何回も連続で押す必要が出てきたら、以下のようなコードでスッキリと書けます。

# Before
Send-Keys "{DOWN}{DOWN}{DOWN}{DOWN}{DOWN}test." -ProcessName "LINE"
# After
Send-Keys "$("{DOWN}" * 5)test." -ProcessName "LINE"

参考

Microsoft Developer Network|Windows Script Host
SendKeys メソッド
Windows管理者のためのWindows Script Host入門
第5回 WshShellオブジェクトの詳細(1) (4/4)