【VBAでUIAutomation】アプリケーションをマクロで動かす

2021年9月22日

RPA-UIAutomation

社内システムを自動化したいって思ったことありませんか?

あるけど、それってRPAとか使わないとできないんだよね

そんなことはありません。VBAでも自動化可能です!

社内システムって操作するのが非常に面倒ですよね。技術負債の塊だったり。。。

ブラウザ操作のものはスクレイピングで操作できるけど、専用アプリケーションは操作できないと思っていませんか?

大丈夫です。それもVBAでできます。

UIAutomationというライブラリを活用すれば、RPAと同じことがVBAでも実現できます。

UIAutomationを活用して面倒な社内システムも自動化してしまいましょう!

UIAutomationとは

UIAutomationはマイクロソフトの提供しているライブラリです。

このライブラリは、他のアプリケーションのユーザインターフェイス (UI) 要素を識別して操作することができます。

詳細は後述しますが、アプリケーション要素の名前やクラス名を指定することでボタンクリックやテキスト入力などができます。

UIAutomationの使い方を紹介

ここからはWindowsの電卓アプリを題材に、UIAutomationの使い方を紹介していきます。

①アプリケーション要素の名前やクラス名を調べる

プログラムを書く前に、アプリケーション要素の名前やクラス名を調べていきましょう。

調べるアプリは色々とあるのですが、Windows SDK付属のinspect.exeを紹介します。

inspect.exeは既にインストール済の場合があるので、まずは下記のフォルダを調べてinspect.exeが存在していないか調べてみてください。

C:\Program Files (x86)\Windows Kits(メジャーバージョン)\bin(バージョン/ビルド番号)\x86
例:C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86

https://qiita.com/RPAbot/items/f1d6438c8f2500db65ae

inspect.exeが無ければ下記のリンク先からWindows SDKをダウンロードして下さい。
https://developer.microsoft.com/ja-jp/windows/downloads/sdk-archive/

inspect.exe

inspect.exeを開くと、左側にアプリケーション要素のツリー構造、右側に要素のプロパティが表示されます。

inspect.exe

試しに電卓アプリを開いて「1」ボタンを押してみてください。
上の画像のように電卓ウィンドウのツリーが開いて、"1″buttonという要素がアクティブになります。

inspect.exe

要素がアクティブになったら、右側のプロパティを見ます。

UIAutomationで使用するのはNameとClassNameの2つのプロパティです。
このいずれかのプロパティを用いて要素(エレメント)を特定します。

基本的にはClassNameを使用して、上の画像のようにClassNameが他の要素と同じ場合にはNameを使用して要素を特定しましょう。

ウィンドウの取得はウィンドウハンドルを利用

ツリーの一番上にあるウィンドウは、後述するウィンドウハンドルを用いて取得します。
(Nameプロパティが分かれば取得できます)

この一番上のウィンドウから1つずつ下の要素を取得して、目的のボタン要素などを取得していきます。

補足

アプリケーションによってはinspect.exeで要素を調べられないものがあります。

その場合はUIAutomationでは操作できないため、下記の記事を参考にしてみて下さい。
(C#では実装したことがあるのですが、VBAで実装可能かは不明です)

https://qiita.com/mima_ita/items/702fdbdee30346b5738e

②参照設定の有効化

UIAutomationClient

VBEの参照設定でUIAutomationClientの参照を有効化して下さい。

③UIAutomationによる自動化コードの書き方

ここからは電卓アプリを題材に、UIAutomationで自動化するコードの書き方を紹介します。

このコードでは電卓で86-1=の計算をさせます。(やろまいだね)

Private Declare Function FindWindowA Lib "user32" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Sub Calc()
'電卓でやろまい(86-1)を計算
    Dim Hwnd As Long
    Hwnd = FindWindowA(vbNullString, "電卓")
    Call CalcClick(Hwnd, "8")
    Call CalcClick(Hwnd, "6")
    Call CalcClick(Hwnd, "-")
    Call CalcClick(Hwnd, "1")
    Call CalcClick(Hwnd, "=")
End Sub
Private Sub CalcClick(ByVal Hwnd As Long, strBtn As String)
    Dim uiAuto As CUIAutomation: Set uiAuto = New CUIAutomation
    Dim uiElm As IUIAutomationElement
    Dim uiCnd As IUIAutomationCondition
    Dim uiInvoke As IUIAutomationInvokePattern
    
    'ウィンドウハンドルからエレメントを取得
    Set uiElm = uiAuto.ElementFromHandle(ByVal Hwnd)
    
    '電卓ウィンドウのエレメントを取得
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "Windows.UI.Core.CoreWindow")
    Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)
    
    'groupのエレメントを取得
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "LandmarkTarget")
    Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)
    
    Dim strBtnName As String '0~9(+-×÷=)エレメントのName
    
    'strBtnに応じてstrBtnNameとuiCndを設定
    Select Case strBtn
        Case "+"
            strBtnName = "プラス"
            Set uiCnd = uiAuto.CreatePropertyCondition(UIA_NamePropertyId, "標準演算子")
        Case "-"
            strBtnName = "マイナス"
            Set uiCnd = uiAuto.CreatePropertyCondition(UIA_NamePropertyId, "標準演算子")
        Case "/"
            strBtnName = "除算"
            Set uiCnd = uiAuto.CreatePropertyCondition(UIA_NamePropertyId, "標準演算子")
        Case "*"
            strBtnName = "乗算"
            Set uiCnd = uiAuto.CreatePropertyCondition(UIA_NamePropertyId, "標準演算子")
        Case "="
            strBtnName = "等号"
            Set uiCnd = uiAuto.CreatePropertyCondition(UIA_NamePropertyId, "標準演算子")
        Case Else
            strBtnName = strBtn
            Set uiCnd = uiAuto.CreatePropertyCondition(UIA_NamePropertyId, "数字パッド")
    End Select
    
    '標準演算子(数字パッド)のエレメントを取得
    Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)
    
    '0~9(+-×÷=)エレメントを取得
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_NamePropertyId, strBtnName)
    Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)
    
    '取得したエレメントをクリック
    Set uiInvoke = uiElm.GetCurrentPattern(UIA_InvokePatternId)
    uiInvoke.Invoke
    
End Sub

Calcプロシージャが全体を制御するプログラムで、CalcClickプロシージャが電卓アプリを実際に操作しています。

ここからはUIAutomationで自動化するプログラムのポイントを紹介していきます。

ウィンドウの取得はウィンドウハンドルを利用

Private Declare Function FindWindowA Lib "user32" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    Hwnd = FindWindowA(vbNullString, "電卓")
    'ウィンドウハンドルからエレメントを取得
    Set uiElm = uiAuto.ElementFromHandle(ByVal Hwnd)

ツリーの一番上にある電卓ウィンドウはウィンドウハンドルを用いて取得します。
( ウィンドウハンドル:コンピュータが各ウィンドウに割り振る管理番号)

user32ライブラリのFindWindowA関数を使用するので、モジュールの先頭にPrivate Declare~の部分を記述して下さい。

この関数でウィンドウハンドルを取得し、得られたウィンドウハンドルからアプリ上の要素(エレメント)を取得します。

1つ下のツリー要素を取得

    '電卓ウィンドウのエレメントを取得
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "Windows.UI.Core.CoreWindow")
    Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)

この後は1つ下のツリー要素を取得する操作を4回行います。

ツリー要素の取得はFindFirstメソッドを使います。
この関数の引数は2つで、1つはツリー上の検索する位置を、もう1つはコンディションを指定する必要があります。

ツリー上の検索する位置は、1つ下の要素ならばTreeScope_Childrenを使います。
(他のツリー上の位置指定方法は下記リンク参照)
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.automation.treescope?view=net-5.0

もう1つのコンディションはCreatePropertyConditionメソッドを用いて作成します。
ここで先ほど調べた要素の名前やクラス名を使用します。

uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "クラス名")
uiAuto.CreatePropertyCondition(UIA_NamePropertyId, "名前") 

クラス名を使う場合と名前を使う場合で、第1引数を変更して下さい。

このようにCreatePropertyCondition+FindFirstの組合せで1つ下のツリー要素を取得していきます。

Select Caseの部分は少し複雑に見えるかもしれませんが、入力したいボタンに応じてコンディションと名前を分岐させているだけです。

ステップ実行させながら何をしているか確認してみて下さい。

クリック、入力、取得

    Dim uiInvoke As IUIAutomationInvokePattern
    '取得したエレメントをクリック
    Set uiInvoke = uiElm.GetCurrentPattern(UIA_InvokePatternId)
    uiInvoke.Invoke

このプログラムでは最後に電卓アプリのボタンをクリックしています。

取得した要素をクリックするためにはuiInvokeに一度セットし、Invokeメソッドを実行します。
これでボタンクリックができます。

Dim uiVal As IUIAutomationValuePattern
Set uiVal = エレメント.GetCurrentPattern(UIA_ValuePatternId)
'文字列の入力
uiVal.SetValue 文字列
'値の取得
Debug.Print uiVal.CurrentValue

今回のプログラムでは使わなかったですが、文字列を入力したり、値を取得するにはSetValue/CurrentValueを使用して下さい。

筆者おすすめのVBA本はこちらです。

created by Rinker
技術評論社
¥2,178 (2021/10/18 10:25:13時点 Amazon調べ-詳細)
created by Rinker
¥2,948 (2021/10/18 21:50:31時点 Amazon調べ-詳細)

《VBA上級者を目指す人へのおすすめ》

created by Rinker
技術評論社
¥3,608 (2021/10/18 10:25:15時点 Amazon調べ-詳細)

他の「VBA」の記事はこちらからどうぞ

VBA

Posted by やろまい