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 メソッド](https://msdn.microsoft.com/ja-jp/library/cc364423.aspx)
# 実装における注意点
・フォーカス（SendKeys操作の対象）
　普段、キーで操作しているときと同様、前面に操作したいアプリを配置すること。
　→自動化処理のはじめにアプリを起動するか、もしくは、すでに起動中のアプリを前面にする。
・Wait
　→アプリがキーストロークして反応できる状態まで待つ必要がある。
　→ウィンドウのフォーカスだけではなく、ウィンドウ内のどこにフォーカスがくるのかも気にすること。
　→マシンの性能によっては、Wait時間を調整する必要が出てくる。
・その他
　→ショートカットキーについては事前に知っておくこと。
# PowerShellでの実装
動作環境
・Windows10
・PowerShell 5.1

```PowerShell
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キーや方向キーを何回も連続で押す必要が出てきたら、以下のようなコードでスッキリと書けます。

```PowerShell
# 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 メソッド](https://msdn.microsoft.com/ja-jp/library/cc364423.aspx)
Windows管理者のためのWindows Script Host入門
[第5回　WshShellオブジェクトの詳細（1） (4/4)](http://www.atmarkit.co.jp/ait/articles/0407/08/news101_4.html)