概要
- 自動化ツールをインストールせずに、Windowsに標準搭載されている「UI Automation」機能を使って、ブラウザを自動操作するコードです。たとえば、「Office 非搭載PC」でブラウザでの作業を自動化することが可能です。
- この内容は、以下の動画で使われているものです。
実行環境
- Microsoft Windows 11
- PowerShell version 5.1
- Microsoft Edge
開発環境
- Windows PowerShell ISE
(Windowsに標準搭載されているPowerShellの統合開発環境)
※「UI Automation PowerShell Extensions」は不要です。
注意点
- プログラムの実行については、すべて自己責任で行ってください。実行により発生した、いかなる直接的または間接的被害について、作者はその責任を負いません。
- このコードにおいては、読み込み待機処理やエラー処理は加味しきれていません。
- Win32やWPFなど、従来の古いウィンドウシステムを操作する場合は、要素をうまく取得できない可能性があります。
参照
- 今回のコードは、以下の「System.Windows.Automation」を用いています。
PowerShellコード(汎用関数)
- まずは以下のコードを「Windows PowerShell ISE」のスクリプトウィンドウに貼り付けます。このコードを用いると、「Windowの部分名」や「要素の名前」から操作したい要素を特定し、実際に要素をクリックしたり、値を入力することが容易になります。
汎用関数
# UI オートメーション関連のアセンブリを読み込む
Add-Type -AssemblyName "UIAutomationClient" # UIAutomationClientアセンブリを追加
Add-Type -AssemblyName "UIAutomationTypes" # UIAutomationTypesアセンブリを追加
# UI オートメーション関連の型を定義する
$AutomationElement = [System.Windows.Automation.AutomationElement] # AutomationElementクラスを定義
$TreeScope = [System.Windows.Automation.TreeScope] # TreeScope列挙型を定義
$Condition = [System.Windows.Automation.Condition] # Conditionクラスを定義
$InvokePattern = [System.Windows.Automation.InvokePattern] # InvokePatternクラスを定義
$ValuePattern = [System.Windows.Automation.ValuePattern] # ValuePatternクラスを定義
$SelectionItemPattern = [System.Windows.Automation.SelectionItemPattern]
$TogglePattern = [System.Windows.Automation.TogglePattern]
$ExpandCollapsePattern = [System.Windows.Automation.ExpandCollapsePattern]
$TreeWalker=[System.Windows.Automation.TreeWalker]
# ウィンドウの全要素の配列を取得する関数
function GetElementsfromWindow {
Param(
$RootWindowName = $null, # ルートウィンドウの名前(部分一致)
$waitMilliseconds = 300 # ウィンドウが見つからない場合の待機時間(ミリ秒)
)
# ルート要素から全ての子要素を取得
$childrenElements = $AutomationElement::RootElement.FindAll($TreeScope::Children, $Condition::TrueCondition)
# ルートウィンドウが見つかるまで繰り返す
Do {
# 全ての子要素について処理
foreach ($element in $childrenElements) {
# 要素の名前がルートウィンドウの名前に部分一致する場合
if ($element.GetCurrentPropertyValue($AutomationElement::NameProperty) -like "*$RootWindowName*") {
# サブツリー内の全要素を取得して返す
return $element.FindAll($TreeScope::Subtree, $Condition::TrueCondition)
}
}
# 指定された名前の部分が含まれるウィンドウが見つからない場合
Write-Host "部分名 '${RootWindowName}' のウィンドウが見つかりません。"
# 指定されたミリ秒数だけ待機
Start-Sleep -Milliseconds $waitMilliseconds
} While ($true)
}
# 要素を特定する関数
function FindElement {
Param(
$RootWindowName = $null, # ルートウィンドウの名前(部分一致)
$PropertyType, # プロパティの種類
$Identifier, # 識別子
$waitMilliseconds = 300 # ウィンドウが見つからない場合の待機時間(ミリ秒)
)
# ルートウィンドウから要素を取得して繰り返し処理
Do {
foreach ($element in GetElementsfromWindow -RootWindowName $RootWindowName) {
# 要素が指定されたプロパティの識別子と一致する場合
if ($element.GetCurrentPropertyValue($AutomationElement::$PropertyType) -eq $Identifier) {
# 要素を返す
return $element
}
}
# 指定されたプロパティの識別子が見つからない場合
Write-Host " '$Identifier' の '$PropertyType' 要素が見つかりません。"
# 指定されたミリ秒数だけ待機
Start-Sleep -Milliseconds $waitMilliseconds
} While ($true)
}
# クリック操作をする関数
function InvokeElement {
Param(
$RootWindowName, # ルートウィンドウの名前
$PropertyType, # プロパティの種類
$Identifier # 識別子
)
# 識別子を元に要素を検索する
$element = FindElement -RootWindowName $RootWindowName -PropertyType $PropertyType -Identifier $Identifier
# $element の名前などをデバッグする
# Write-Host "Element Name: $($element.Current.Name)"
# Write-Host "Control Type: $($element.Current.ControlType.ProgrammaticName)"
# 要素に対して Invoke パターンを実行する
$element.GetCurrentPattern($InvokePattern::Pattern).Invoke()
Start-Sleep -Milliseconds 300
}
# 値を入力する関数
function SetValueNextElement {
Param(
$RootWindowName, # ルートウィンドウの名前
$PropertyType, # プロパティの種類
$Identifier, # 識別子
$inputValue # 入力する値
)
# 識別子を元に要素を検索する
$element = FindElement -RootWindowName $RootWindowName -PropertyType $PropertyType -Identifier $Identifier
# 次の要素を取得する
$nextSiblingElement = $TreeWalker::ControlViewWalker.GetNextSibling($element)
# 次の要素に対して値を設定する
$nextSiblingElement.GetCurrentPattern($ValuePattern::Pattern).SetValue($inputValue)
Start-Sleep -Milliseconds 300
}
###################################################
# ↓↓↓↓↓↓↓ この行以降に自動操作スクリプトを記述する。 ↓↓↓↓↓↓↓ #
###################################################
PowerShellコード(自動操作スクリプトの例)
- 動画で用いた、自動操作スクリプトの例です。CSVファイルのパスを指定して、CSVファイルを配列に読み込み、それらの値を用いて要素に値を入力したり、クリックしたりします。
# CSVファイルのパスを指定します
$csvFilePath = "C:\Users\TestFolder\input_test01\元データ.csv"
# CSVファイルを配列に読み込みます
$csvArray = Import-Csv -Path $csvFilePath
InvokeElement -RootWindowName "Rpa" -PropertyType "NameProperty" -Identifier "開始"
# 配列の中身を1行ずつ処理します
foreach ($row in $csvArray) {
# ヘッダーが「苗字」の場合の処理
if ($row.苗字) {
Write-Output "苗字: $($row.苗字)"
SetValueNextElement -RootWindowName "Rpa" -PropertyType "NameProperty" -Identifier "苗字" -inputValue $($row.苗字)
}
# ヘッダーが「名前」の場合の処理
if ($row.名前) {
Write-Output "名前: $($row.名前)"
SetValueNextElement -RootWindowName "Rpa" -PropertyType "NameProperty" -Identifier "名前" -inputValue $($row.名前)
}
if ($row.会社名) {
Write-Output "会社名: $($row.会社名)"
SetValueNextElement -RootWindowName "Rpa" -PropertyType "NameProperty" -Identifier "会社名" -inputValue $($row.会社名)
}
if ($row.部署) {
Write-Output "部署: $($row.部署)"
SetValueNextElement -RootWindowName "Rpa" -PropertyType "NameProperty" -Identifier "部署" -inputValue $($row.部署)
}
if ($row.住所) {
Write-Output "住所: $($row.住所)"
SetValueNextElement -RootWindowName "Rpa" -PropertyType "NameProperty" -Identifier "住所" -inputValue $($row.住所)
}
if ($row.メールアドレス) {
Write-Output "メールアドレス: $($row.メールアドレス)"
SetValueNextElement -RootWindowName "Rpa" -PropertyType "NameProperty" -Identifier "メールアドレス" -inputValue $($row.メールアドレス)
}
if ($row.電話番号) {
Write-Output "電話番号: $($row.電話番号)"
SetValueNextElement -RootWindowName "Rpa" -PropertyType "NameProperty" -Identifier "電話番号" -inputValue $($row.電話番号)
}
InvokeElement -RootWindowName "Rpa" -PropertyType "NameProperty" -Identifier "登録"
}
PowerShellコード(機能追加)
- 以下のコードを汎用関数の中に追加することで、操作の種類を増やすことができます。
追加関数
# ラジオボタンの状態を変更する関数
function SelectionItemElement {
Param(
$RootWindowName, # ルートウィンドウの名前
$PropertyType, # プロパティの種類
$Identifier # 識別子
)
# 識別子を元に要素を検索する
$element = FindElement -RootWindowName $RootWindowName -PropertyType $PropertyType -Identifier $Identifier
# 要素の SelectionItem パターンを適用して選択状態にする
$element.GetCurrentPattern($SelectionItemPattern::Pattern).Select()
Start-Sleep -Milliseconds 300
}
# チェックボックスの状態を変更する関数
function ToggleElement {
Param(
$RootWindowName, # ルートウィンドウの名前
$PropertyType, # プロパティの種類
$Identifier # 識別子
)
# 識別子を元に要素を検索する
$element = FindElement -RootWindowName $RootWindowName -PropertyType $PropertyType -Identifier $Identifier
# 要素の Toggle パターンを適用してチェックボックスの状態を変更する
$element.GetCurrentPattern($TogglePattern::Pattern).Toggle()
Start-Sleep -Milliseconds 300
}
# コンボボックスを開く関数
function ExpandNextElement {
Param(
$RootWindowName, # ルートウィンドウの名前
$PropertyType, # プロパティの種類
$Identifier # 識別子
)
# 識別子を元に要素を検索する
$element = FindElement -RootWindowName $RootWindowName -PropertyType $PropertyType -Identifier $Identifier
# 次の要素を取得する
$nextSiblingElement = $TreeWalker::ControlViewWalker.GetNextSibling($element)
# 次の要素に対して ExpandCollapse パターンを適用してコンボボックスを開く
$nextSiblingElement.GetCurrentPattern($ExpandCollapsePattern::Pattern).Expand()
Start-Sleep -Milliseconds 300
}
PowerShellコード(要素の調査)
- 以下のコードを用いることで、ウィンドウのすべての要素の特定のプロパティとパターンをデバッグできます。
要素の調査
# UI オートメーション関連のアセンブリを読み込む
Add-Type -AssemblyName "UIAutomationClient" # UIAutomationClientアセンブリを追加
Add-Type -AssemblyName "UIAutomationTypes" # UIAutomationTypesアセンブリを追加
# UI オートメーション関連の型を定義する
$AutomationElement = [System.Windows.Automation.AutomationElement] # AutomationElementクラスを定義
$TreeScope = [System.Windows.Automation.TreeScope] # TreeScope列挙型を定義
$Condition = [System.Windows.Automation.Condition] # Conditionクラスを定義
# ウィンドウの全要素の配列を取得する関数
function GetElementsfromWindow {
Param(
$RootWindowName = $null, # ルートウィンドウの名前(部分一致)
$waitMilliseconds = 300 # ウィンドウが見つからない場合の待機時間(ミリ秒)
)
# ルート要素から全ての子要素を取得
$childrenElements = $AutomationElement::RootElement.FindAll($TreeScope::Children, $Condition::TrueCondition)
# ルートウィンドウが見つかるまで繰り返す
Do {
# 全ての子要素について処理
foreach ($element in $childrenElements) {
# 要素の名前がルートウィンドウの名前に部分一致する場合
if ($element.GetCurrentPropertyValue($AutomationElement::NameProperty) -like "*$RootWindowName*") {
# サブツリー内の全要素を取得して返す
return $element.FindAll($TreeScope::Subtree, $Condition::TrueCondition)
}
}
# 指定された名前の部分が含まれるウィンドウが見つからない場合
Write-Host "部分名 '${RootWindowName}' のウィンドウが見つかりません。"
# 指定されたミリ秒数だけ待機
Start-Sleep -Milliseconds $waitMilliseconds
} While ($true)
}
# 要素を特定する関数
function SearchElement {
Param(
$RootWindowName = $null, # ルートウィンドウの名前(部分一致)
$PropertyType, # プロパティの種類
$Identifier # 識別子
)
# カウンターの初期化
$counter = 0
# 出力の開始を表示
Write-Host "---------出力開始---------"
# GetElementsfromWindow関数で取得した各要素に対してループを実行
foreach ($element in GetElementsfromWindow -RootWindowName $RootWindowName) {
# カウンターの値を増加
$counter++
Write-Host ""
Write-Host "$($counter)"
Write-Host "Name: $($element.Current.Name)"
Write-Host "LocalizedControlType: $($element.Current.LocalizedControlType)"
# サポートされているパターンの出力
$patterns = $element.GetSupportedPatterns()
# 各サポートされているパターンに対してループを実行
foreach ($pattern in $patterns) {
# パターンの名前を表示
Write-Host "Supported Pattern: $($pattern.ProgrammaticName)"
}
# 指定したプロパティの値が識別子と一致する場合の処理
if ($element.GetCurrentPropertyValue($AutomationElement::$PropertyType) -eq $Identifier) {
Read-Host "Enterで続行"
}
}
# 出力の終了を表示
Write-Host "---------出力終了---------"
}
###################################################
# ↓↓↓↓↓↓↓ この行以降に要素調査スクリプトを記述する。 ↓↓↓↓↓↓↓ #
###################################################
SearchElement -RootWindowName "Rpa" -PropertyType "NameProperty" -Identifier "苗字"
コードの修正履歴
例)yyyy/mm/dd:〇〇について修正
2024/05/24: 各関数において、要素取得に失敗または要素の操作に成功した際に「指定されたミリ秒数だけ待機」するコードを挿入しました。