Edited at

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)