■ UI自動化について考える
- ウィンドウハンドルについて
- アプリケーションのUIを分析する(Inspectツール)
- UIAutomationでUI操作してみる ←今ここ
- UIAutomationをWinActorに組み込む
前回なんとなくUI検査ツール使ってみたらAutomationIDってのを発見して、これはもしかしてUI自動化できるのか?という気配を感じた。

Inspectツールの上のプルダウンから、「MSAA」、「UI Automation」が選択できる。
どうもすこし調べた感じだとMSAAの方が昔っからある規格らしい。
「AutomationID」は、「UI Automation」の方を選択した時に取得できる。
と、いう訳でUI Automationというライブラリについていろいろ調べて実験した。
1..NETフレームワークから使えるようだ。
.NETフレームワークから、System.Windows.Automationというライブラリから使えるようだ。
.NETフレームワーク開発といえば、Visual Studioさん。
まず、参照設定を追加してUI Automationを使えるようにする。

- UIAutomationClient
- UIAutomationTypes
この2つが参照設定で追加されてればいいみたいね。
2.とにかくコード書いて試しまくった。
コンソールアプリケーションでプロジェクト作ってMicrosoft Docsとにらめっこ。
動くまであきらめずサンプルコード試しまくった。
なんとなく動く感じになってきたコードがこれ。
Imports System.Windows.Automation
Module Module1
Sub Main(args As String())
Dim argVal As String
If args.Length < 3 Then
Console.WriteLine("コマンドラインオプションが不足しています。")
Exit Sub
ElseIf args.Length = 4 Then
argVal = args(3)
End If
' コマンドライン引数を取得
Dim func As String = args(0)
Dim hwnd As String = args(1)
Dim TargetVals As String() = Split(args(2), ":")
Try
' vbs単体起動用に「GetHWND:」で始まるキーワードが指定されていた場合には独自にhandle取得する。
If (hwnd.StartsWith("GetHWND:")) Then
hwnd = GetWindowHandle(Mid(hwnd, 9))
End If
' AutomationElementを取得する。
Dim elm As AutomationElement = AutomationElement.FromHandle(hwnd)
Dim TargetElm As AutomationElement
Dim AutoProp As AutomationProperty
Dim PropCond As PropertyCondition
Select Case TargetVals.Length
Case 1
' 初版との互換対応、単一指定の場合はAutomationIDで要素検索
AutoProp = AutomationElement.AutomationIdProperty
PropCond = New PropertyCondition(AutoProp, TargetVals(0), PropertyConditionFlags.IgnoreCase)
TargetElm = elm.FindFirst(TreeScope.Descendants, PropCond)
Case 2
' 2条件指定の場合は、検索プロパティの指定ができる
Select Case TargetVals(0)
Case "Name"
AutoProp = AutomationElement.NameProperty
Case "AutomationID"
AutoProp = AutomationElement.AutomationIdProperty
Case Else
Console.WriteLine("操作対象の指定方法が不正です。")
Exit Sub
End Select
PropCond = New PropertyCondition(AutoProp, TargetVals(1), PropertyConditionFlags.IgnoreCase)
TargetElm = elm.FindFirst(TreeScope.Descendants, PropCond)
Case 3
' 3条件指定の場合は、Previous または Next 、FirstChild、LastChildによる相対位置指定
Select Case TargetVals(0)
Case "Name"
AutoProp = AutomationElement.NameProperty
Case "AutomationID"
AutoProp = AutomationElement.AutomationIdProperty
Case Else
Console.WriteLine("操作対象の指定方法が不正です。")
Exit Sub
End Select
PropCond = New PropertyCondition(AutoProp, TargetVals(2), PropertyConditionFlags.IgnoreCase)
TargetElm = elm.FindFirst(TreeScope.Descendants, PropCond)
Select Case TargetVals(1)
Case "Previous"
TargetElm = TreeWalker.ControlViewWalker.GetPreviousSibling(TargetElm)
Case "Next"
TargetElm = TreeWalker.ControlViewWalker.GetNextSibling(TargetElm)
Case "FirstChild"
TargetElm = TreeWalker.RawViewWalker.GetFirstChild(TargetElm)
Case "LastChild"
TargetElm = TreeWalker.RawViewWalker.GetLastChild(TargetElm)
Case Else
Console.WriteLine("操作対象の指定方法が不正です。")
Exit Sub
End Select
End Select
If IsNothing(TargetElm) Then
Console.WriteLine("操作対象を取得できませんでした。")
Exit Sub
End If
' 第一引数によって処理を変える。
Select Case StrConv(func, VbStrConv.Lowercase)
Case "click"
Dim ptnInvk As InvokePattern = TargetElm.GetCurrentPattern(InvokePattern.Pattern)
ptnInvk.Invoke()
Case "focus"
TargetElm.SetFocus()
Case "getvalue"
Dim ptnVal As ValuePattern = TargetElm.GetCurrentPattern(ValuePattern.Pattern)
Console.WriteLine(ptnVal.Current.Value)
Case "setvalue"
Dim ptnVal As ValuePattern = TargetElm.GetCurrentPattern(ValuePattern.Pattern)
ptnVal.SetValue(argVal)
Case "getselection"
Dim SlctPtn As SelectionPattern = TargetElm.GetCurrentPattern(SelectionPattern.Pattern)
Dim SelectLabel As String = ""
For Each SlctItm As AutomationElement In SlctPtn.Current.GetSelection()
If (Len(SelectLabel) > 0) Then SelectLabel = SelectLabel & ","
SelectLabel = SelectLabel & SlctItm.GetCurrentPropertyValue(AutomationElement.NameProperty).ToString()
Next
Console.WriteLine(SelectLabel)
Case "getselectionitemisselected"
Dim ptnVal As SelectionItemPattern = TargetElm.GetCurrentPattern(SelectionItemPattern.Pattern)
Console.WriteLine(ptnVal.Current.IsSelected)
Case "setselectionitemvalue"
If (TargetElm.GetCurrentPropertyValue(AutomationElement.IsSelectionPatternAvailableProperty)) Then
Dim itemElm As AutomationElement = TargetElm.FindFirst(TreeScope.Subtree, New PropertyCondition(AutomationElement.NameProperty, argVal))
Dim ptnVal As SelectionItemPattern = itemElm.GetCurrentPattern(SelectionItemPattern.Pattern)
ptnVal.Select()
ElseIf (TargetElm.GetCurrentPropertyValue(AutomationElement.IsSelectionItemPatternAvailableProperty)) Then
Dim ptnVal As SelectionItemPattern = TargetElm.GetCurrentPattern(SelectionItemPattern.Pattern)
ptnVal.Select()
End If
Case "gettoggle"
Dim TglPtn As TogglePattern = TargetElm.GetCurrentPattern(TogglePattern.Pattern)
Console.WriteLine(TglPtn.Current.ToggleState = ToggleState.On)
Case "settoggle"
Dim TglPtn As TogglePattern = TargetElm.GetCurrentPattern(TogglePattern.Pattern)
Select Case StrConv(argVal, VbStrConv.Lowercase)
Case "0", "false"
If (TglPtn.Current.ToggleState = ToggleState.On) Then
TglPtn.Toggle()
End If
Case "1", "true"
If (TglPtn.Current.ToggleState <> ToggleState.On) Then
TglPtn.Toggle()
End If
End Select
Case "getname"
Console.WriteLine(TargetElm.GetCurrentPropertyValue(AutomationElement.NameProperty).ToString())
Case Else
Console.WriteLine("[" & func & "]に相当する処理はありませんでした。")
End Select
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Private Function GetWindowHandle(part_key As String) As String
Dim hwnd As String = part_key
For Each p As Process In System.Diagnostics.Process.GetProcesses()
If (p.ProcessName Like "*" & part_key & "*") Or
(p.MainWindowTitle Like "*" & part_key & "*") Then
hwnd = p.MainWindowHandle
Exit For
End If
Next
Return hwnd
End Function
End Module
3.使い方
コマンドプロンプトから3~4個のオプションを指定して起動する。
- 第一引数
- Click - クリックイベントを発火する(Invoke)
- Focus - 要素にフォーカスを移動する
- GetValue,SetValue - テキスト編集欄の取得設定(TextBox,直接編集可能なCombobox)
- GetSelection - Combobox, ListViewの選択項目が取得
- GetSelectionItemIsSelected - RadioButton, Combobox, ListViewの選択項目がIsSelectedかどうかを取得
- SetSelectionItemValue - RadioButton, Combobox, ListViewの選択項目を設定
- GetToggle,SetToggle ⇒ Checkboxの取得設定
- GetName - 要素のNameプロパティを標準出力に表示する
- 第二引数
- ウィンドウハンドルを指定(GetHWND:ウィンドウタイトルorプロセス名でも可)
- 第三引数
- Inspectツールで取得したName or AutomationIDを指定
(:で区切ってオプション指定することでNext・Previousにも対応)
- 第四引数(第一引数:Set~の場合のみ)
- 要素に設定した文字を指定
電卓の計算結果が「Text」ってなっててGetValueだと値とれなかったからGetNameを追加した。
でも、このまま結果取得すると「表示は●●です」って出てしまう。。。
でも、なんとなくこれをWinActorのスクリプト実行ステージに仕込めばイベントモードのWIN32では操作が座標指定になってしまう要素も操作できるんじゃないかという期待を込めて。
コンパイル済みファイルはこちら。