UIA(UI Automation)で他アプリ・Webフォームへ自動入力 ― Selenium不要のRPA代替

💡 本編(VBAなし)との位置づけ

VBA禁止・マクロ禁止の完全閉域環境での代替手法は → VBAなしでWebシステム入力を自動化するQRリーダー方式 およびQRシリーズ全編を参照してください。VBAが使えるなら、QRのような物理的なエミュレーションではなく、Windows標準のAPIで他アプリやWebフォームを直接制御できます。

1. なぜ「UI Automation」なのか?

Internet Explorerの廃止に伴い、VBAから直接DOMを操作する「IE制御」は過去のものとなりました。現在、Webスクレイピングや自動操作といえば「SeleniumBasic」やWebDriver系のツールが有名ですが、これには「ブラウザドライバのバージョン管理」「環境構築」「社内PCへの導入許可」といった実務上のハードルがあります。

そこで選択肢になるのが、Windowsに標準搭載されているアクセシビリティAPI「UI Automation(UIA)」です。UIAは、画面上のボタン、入力欄、一覧、チェックボックスなどを「UI要素」として取得し、VBAから入力・クリックなどの操作を行う仕組みです。

UIAは、EdgeやChromeなどのモダンブラウザだけでなく、ERP、会計ソフト、受注システム、社内向けのWindowsアプリなどにも応用できます。

この記事の方針: 電卓のボタン操作ではなく、実務に近い「HTMLフォームへExcel VBAから自動入力する例」で説明します。さらに、通常のUIA操作は一瞬で終わってしまうため、この記事では3名分のデータをあえてゆっくり入力し、読者が動きを目で追えるデモにしています。

2. このサンプルで体感できること

この記事のサンプルコードを実行すると、以下の処理がすべて自動で行われます。

  1. VBAが一時フォルダにテスト用HTMLフォームを自動生成する
  2. EdgeでそのHTMLフォームを開く
  3. UI Automationでブラウザ画面を探す
  4. 社員番号、氏名、金額、備考の入力欄を探す
  5. 3名分のデータを1項目ずつ、ゆっくり自動入力する
  6. 1名分ごとに登録ボタンをクリックする
  7. 画面下部に登録履歴を追記表示する

つまり、読者はHTMLファイルを別途作る必要がありません。Excel VBAの標準モジュールにコードを貼り付け、マクロを実行するだけで、UIAによる自動入力を体感できます。

このコードの特徴
  • UIAutomationClient の参照設定を使います。
  • CreateObject("UIAutomationClient.CUIAutomation") は使いません。
  • ADODB.Stream も使いません。
  • HTMLファイルはVBA側で自動生成します。
  • 入力が速すぎて見えない問題を避けるため、入力ごとに短い待機を入れています。

3. 事前準備

このサンプルでは、VBAからUI Automationを使うために、最初に1回だけ参照設定を行います。

  1. Excelを開く
  2. Alt + F11 でVBE(Visual Basic Editor)を開く
  3. ツール > 参照設定 を開く
  4. UIAutomationClient にチェックを入れる
  5. 挿入 > 標準モジュール を選択する
  6. この記事のコードをそのまま貼り付ける
  7. UIA_体感デモ_3名分_ゆっくり入力 を実行する
補足: UIAオブジェクトは Set uia = New CUIAutomation で作成します。環境によっては CreateObject("UIAutomationClient.CUIAutomation") が429エラーになるため、この記事では参照設定を前提にした安定寄りのコードにしています。

4. 貼り付けるだけで動く全部入りコード

以下のコードを、Excel VBAの標準モジュールにそのまま貼り付けてください。貼り付け後、UIA_体感デモ_3名分_ゆっくり入力 を実行します。

Option Explicit

' ============================================================
' UI Automation 体感デモ 3名分 ゆっくり入力版
'
' 前提:
'   VBEの「ツール」→「参照設定」で
'   「UIAutomationClient」にチェックを入れてください。
'
' このコードの目的:
'   ・VBAがHTMLフォームを自動生成する
'   ・EdgeでHTMLを開く
'   ・UI Automationで入力欄を探す
'   ・3名分のデータをゆっくり入力する
'   ・登録ボタンを押して履歴を表示する
'
' 注意:
'   ・CreateObject("UIAutomationClient.CUIAutomation") は使いません。
'   ・ADODB.Stream も使いません。
' ============================================================

Private Const SCOPE_CHILDREN As Long = 2
Private Const SCOPE_SUBTREE As Long = 7

Private Const UIA_NAME_PROPERTY_ID As Long = 30005
Private Const UIA_CONTROL_TYPE_PROPERTY_ID As Long = 30003

Private Const UIA_INVOKE_PATTERN_ID As Long = 10000
Private Const UIA_VALUE_PATTERN_ID As Long = 10002

Private Const UIA_BUTTON_CONTROL_TYPE_ID As Long = 50000
Private Const UIA_EDIT_CONTROL_TYPE_ID As Long = 50004


Public Sub UIA_体感デモ_3名分_ゆっくり入力()

    Dim uia As CUIAutomation
    Dim root As IUIAutomationElement
    Dim browserWindow As IUIAutomationElement
    Dim htmlPath As String
    Dim url As String

    Dim data(1 To 3, 1 To 4) As String
    Dim i As Long

    On Error GoTo ErrHandler

    ' 1. デモ用データ 3名分
    data(1, 1) = "A001"
    data(1, 2) = "山田太郎"
    data(1, 3) = "12500"
    data(1, 4) = "1人目:UIAで自動入力しています。"

    data(2, 1) = "A002"
    data(2, 2) = "佐藤花子"
    data(2, 3) = "23800"
    data(2, 4) = "2人目:同じフォームに連続入力しています。"

    data(3, 1) = "A003"
    data(3, 2) = "鈴木一郎"
    data(3, 3) = "9800"
    data(3, 4) = "3人目:登録ボタンまで自動で押します。"

    ' 2. UI Automation オブジェクトを作成
    Set uia = New CUIAutomation

    ' 3. 一時フォルダにHTMLファイルを自動作成
    htmlPath = Environ$("TEMP") & "\uia_test_form_from_vba.html"
    CreateSampleHtmlFile htmlPath

    ' 4. file URLに変換
    url = "file:///" & Replace(htmlPath, "\", "/")

    ' 5. Edgeで開く
    OpenUrlWithEdge url

    ' 6. ブラウザ起動待ち
    Application.Wait Now + TimeValue("00:00:03")

    ' 7. デスクトップ全体を取得
    Set root = uia.GetRootElement

    ' 8. ブラウザウィンドウを探す
    Set browserWindow = WaitWindowByName(uia, root, "UIA入力練習フォーム", 15)

    If browserWindow Is Nothing Then
        MsgBox "ブラウザ画面が見つかりませんでした。" & vbCrLf & _
               "HTMLは作成されています。" & vbCrLf & vbCrLf & _
               htmlPath, vbExclamation
        Exit Sub
    End If

    ' 9. 3名分をゆっくり入力して登録
    For i = 1 To 3

        Application.StatusBar = i & "人目を入力中..."

        ' 前回入力値を消す
        SetValueByName uia, browserWindow, "社員番号", ""
        SetValueByName uia, browserWindow, "氏名", ""
        SetValueByName uia, browserWindow, "金額", ""
        SetValueByName uia, browserWindow, "備考", ""
        DemoPause 0.5

        ' 社員番号
        SetValueByName uia, browserWindow, "社員番号", data(i, 1)
        DemoPause 0.8

        ' 氏名
        SetValueByName uia, browserWindow, "氏名", data(i, 2)
        DemoPause 0.8

        ' 金額
        SetValueByName uia, browserWindow, "金額", data(i, 3)
        DemoPause 0.8

        ' 備考
        SetValueByName uia, browserWindow, "備考", data(i, 4)
        DemoPause 0.8

        ' 登録ボタン
        InvokeButtonByName uia, browserWindow, "登録"
        DemoPause 1.5

    Next i

    Application.StatusBar = False

    MsgBox "3名分のデモ入力が完了しました。" & vbCrLf & vbCrLf & _
           "VBAでHTMLを作成し、Edgeを開き、UIAで自動入力しました。" & vbCrLf & vbCrLf & _
           "作成されたHTML:" & vbCrLf & htmlPath, vbInformation

    Exit Sub

ErrHandler:
    Application.StatusBar = False

    MsgBox "エラーが発生しました。" & vbCrLf & _
           "番号: " & Err.Number & vbCrLf & _
           "内容: " & Err.Description, vbCritical

End Sub


' ============================================================
' テスト用HTMLを作成
' ADODB.Streamは使わず、通常のファイル出力で保存
' ============================================================
Private Sub CreateSampleHtmlFile(ByVal filePath As String)

    Dim html As String

    html = ""
    html = html & "<!DOCTYPE html>" & vbCrLf
    html = html & "<html lang=""ja"">" & vbCrLf
    html = html & "<head>" & vbCrLf
    html = html & "<meta charset=""shift_jis"">" & vbCrLf
    html = html & "<title>UIA入力練習フォーム</title>" & vbCrLf

    html = html & "<style>" & vbCrLf
    html = html & "body {" & vbCrLf
    html = html & "    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;" & vbCrLf
    html = html & "    max-width: 760px;" & vbCrLf
    html = html & "    margin: 40px auto;" & vbCrLf
    html = html & "    line-height: 1.8;" & vbCrLf
    html = html & "    background: #f6f8fb;" & vbCrLf
    html = html & "    color: #222;" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & ".card {" & vbCrLf
    html = html & "    background: #fff;" & vbCrLf
    html = html & "    border-radius: 14px;" & vbCrLf
    html = html & "    padding: 28px;" & vbCrLf
    html = html & "    box-shadow: 0 8px 24px rgba(0,0,0,0.08);" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & "h1 {" & vbCrLf
    html = html & "    margin-top: 0;" & vbCrLf
    html = html & "    font-size: 26px;" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & ".desc {" & vbCrLf
    html = html & "    background: #eef5ff;" & vbCrLf
    html = html & "    border-left: 5px solid #4285f4;" & vbCrLf
    html = html & "    padding: 12px 16px;" & vbCrLf
    html = html & "    margin-bottom: 22px;" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & "label {" & vbCrLf
    html = html & "    display: block;" & vbCrLf
    html = html & "    margin-top: 16px;" & vbCrLf
    html = html & "    font-weight: bold;" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & "input, textarea, button {" & vbCrLf
    html = html & "    width: 100%;" & vbCrLf
    html = html & "    padding: 11px 12px;" & vbCrLf
    html = html & "    font-size: 16px;" & vbCrLf
    html = html & "    box-sizing: border-box;" & vbCrLf
    html = html & "    border-radius: 8px;" & vbCrLf
    html = html & "    border: 1px solid #ccd3dd;" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & "textarea {" & vbCrLf
    html = html & "    min-height: 88px;" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & "button {" & vbCrLf
    html = html & "    margin-top: 24px;" & vbCrLf
    html = html & "    cursor: pointer;" & vbCrLf
    html = html & "    background: #2563eb;" & vbCrLf
    html = html & "    color: #fff;" & vbCrLf
    html = html & "    border: none;" & vbCrLf
    html = html & "    font-weight: bold;" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & "button:hover {" & vbCrLf
    html = html & "    background: #1d4ed8;" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & "#result {" & vbCrLf
    html = html & "    margin-top: 24px;" & vbCrLf
    html = html & "    padding: 14px;" & vbCrLf
    html = html & "    background: #f3f6fa;" & vbCrLf
    html = html & "    border-left: 5px solid #999;" & vbCrLf
    html = html & "    white-space: pre-wrap;" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & "#result.done {" & vbCrLf
    html = html & "    background: #ecfdf3;" & vbCrLf
    html = html & "    border-left-color: #22c55e;" & vbCrLf
    html = html & "}" & vbCrLf

    html = html & "</style>" & vbCrLf
    html = html & "</head>" & vbCrLf

    html = html & "<body>" & vbCrLf
    html = html & "<div class=""card"">" & vbCrLf
    html = html & "<h1>UIA入力練習フォーム</h1>" & vbCrLf

    html = html & "<div class=""desc"">" & vbCrLf
    html = html & "この画面はVBAが自動生成したHTMLです。<br>" & vbCrLf
    html = html & "Excel VBAからUI Automationで、3名分のデータがゆっくり入力されます。" & vbCrLf
    html = html & "</div>" & vbCrLf

    html = html & "<form>" & vbCrLf

    html = html & "    <label for=""empCode"">社員番号</label>" & vbCrLf
    html = html & "    <input id=""empCode"" type=""text"" aria-label=""社員番号"" placeholder=""ここに社員番号が入ります"">" & vbCrLf

    html = html & "    <label for=""empName"">氏名</label>" & vbCrLf
    html = html & "    <input id=""empName"" type=""text"" aria-label=""氏名"" placeholder=""ここに氏名が入ります"">" & vbCrLf

    html = html & "    <label for=""amount"">金額</label>" & vbCrLf
    html = html & "    <input id=""amount"" type=""text"" aria-label=""金額"" placeholder=""ここに金額が入ります"">" & vbCrLf

    html = html & "    <label for=""memo"">備考</label>" & vbCrLf
    html = html & "    <textarea id=""memo"" aria-label=""備考"" placeholder=""ここに備考が入ります""></textarea>" & vbCrLf

    html = html & "    <button id=""registerButton"" type=""button"" aria-label=""登録"" onclick=""registerData();"">登録</button>" & vbCrLf

    html = html & "</form>" & vbCrLf

    html = html & "<div id=""result"">未登録</div>" & vbCrLf
    html = html & "</div>" & vbCrLf

    html = html & "<script>" & vbCrLf
    html = html & "var registerCount = 0;" & vbCrLf
    html = html & "" & vbCrLf
    html = html & "function registerData() {" & vbCrLf
    html = html & "    registerCount++;" & vbCrLf
    html = html & "    var empCode = document.getElementById('empCode').value;" & vbCrLf
    html = html & "    var empName = document.getElementById('empName').value;" & vbCrLf
    html = html & "    var amount = document.getElementById('amount').value;" & vbCrLf
    html = html & "    var memo = document.getElementById('memo').value;" & vbCrLf
    html = html & "    var result = document.getElementById('result');" & vbCrLf
    html = html & "    result.className = 'done';" & vbCrLf
    html = html & "" & vbCrLf
    html = html & "    var line =" & vbCrLf
    html = html & "        '【' + registerCount + '人目 登録完了】' + '\n' +" & vbCrLf
    html = html & "        '社員番号: ' + empCode + '\n' +" & vbCrLf
    html = html & "        '氏名: ' + empName + '\n' +" & vbCrLf
    html = html & "        '金額: ' + amount + '\n' +" & vbCrLf
    html = html & "        '備考: ' + memo + '\n' +" & vbCrLf
    html = html & "        '-----------------------------\n';" & vbCrLf
    html = html & "" & vbCrLf
    html = html & "    if (result.textContent === '未登録') {" & vbCrLf
    html = html & "        result.textContent = line;" & vbCrLf
    html = html & "    } else {" & vbCrLf
    html = html & "        result.textContent = result.textContent + '\n' + line;" & vbCrLf
    html = html & "    }" & vbCrLf
    html = html & "}" & vbCrLf
    html = html & "</script>" & vbCrLf

    html = html & "</body>" & vbCrLf
    html = html & "</html>" & vbCrLf

    WriteTextFile filePath, html

End Sub


' ============================================================
' テキストファイルを書き出す
' ============================================================
Private Sub WriteTextFile(ByVal filePath As String, ByVal text As String)

    Dim ff As Integer

    ff = FreeFile

    Open filePath For Output As #ff
    Print #ff, text
    Close #ff

End Sub


' ============================================================
' EdgeでURLを開く
' ============================================================
Private Sub OpenUrlWithEdge(ByVal url As String)

    On Error GoTo Fallback

    Shell "msedge.exe --new-window """ & url & """", vbNormalFocus
    Exit Sub

Fallback:
    Err.Clear

    ' Edgeが見つからない場合は既定ブラウザで開く
    Shell "cmd /c start """" """ & url & """", vbNormalFocus

End Sub


' ============================================================
' タイトルに指定文字を含むウィンドウを待って取得
' ============================================================
Private Function WaitWindowByName( _
    ByVal uia As CUIAutomation, _
    ByVal root As IUIAutomationElement, _
    ByVal keyword As String, _
    ByVal timeoutSeconds As Long) As IUIAutomationElement

    Dim startTime As Single
    Dim windows As IUIAutomationElementArray
    Dim cond As IUIAutomationCondition
    Dim elm As IUIAutomationElement
    Dim i As Long
    Dim nameValue As String

    startTime = Timer

    Do While Timer - startTime < timeoutSeconds

        Set cond = uia.CreateTrueCondition
        Set windows = root.FindAll(SCOPE_CHILDREN, cond)

        For i = 0 To windows.Length - 1

            Set elm = windows.GetElement(i)

            On Error Resume Next
            nameValue = elm.CurrentName
            On Error GoTo 0

            If InStr(1, nameValue, keyword, vbTextCompare) > 0 Then
                Set WaitWindowByName = elm
                Exit Function
            End If

        Next i

        DoEvents
        Application.Wait Now + TimeValue("00:00:01")

    Loop

End Function


' ============================================================
' Name + ControlType で要素を探す
' ============================================================
Private Function FindElementByNameAndControlType( _
    ByVal uia As CUIAutomation, _
    ByVal parent As IUIAutomationElement, _
    ByVal elementName As String, _
    ByVal controlTypeId As Long) As IUIAutomationElement

    Dim condName As IUIAutomationCondition
    Dim condType As IUIAutomationCondition
    Dim condAnd As IUIAutomationCondition

    Set condName = uia.CreatePropertyCondition(UIA_NAME_PROPERTY_ID, elementName)
    Set condType = uia.CreatePropertyCondition(UIA_CONTROL_TYPE_PROPERTY_ID, controlTypeId)
    Set condAnd = uia.CreateAndCondition(condName, condType)

    Set FindElementByNameAndControlType = parent.FindFirst(SCOPE_SUBTREE, condAnd)

End Function


' ============================================================
' 入力欄へ値を入れる
' ============================================================
Private Sub SetValueByName( _
    ByVal uia As CUIAutomation, _
    ByVal parent As IUIAutomationElement, _
    ByVal elementName As String, _
    ByVal inputValue As String)

    Dim elm As IUIAutomationElement
    Dim valuePattern As IUIAutomationValuePattern

    Set elm = FindElementByNameAndControlType( _
                uia, _
                parent, _
                elementName, _
                UIA_EDIT_CONTROL_TYPE_ID)

    If elm Is Nothing Then
        MsgBox "入力欄が見つかりません: " & elementName, vbExclamation
        Exit Sub
    End If

    On Error Resume Next
    Set valuePattern = elm.GetCurrentPattern(UIA_VALUE_PATTERN_ID)

    If Not valuePattern Is Nothing Then
        valuePattern.SetValue inputValue
    End If

    If Err.Number <> 0 Or valuePattern Is Nothing Then
        Err.Clear

        ' ValuePatternが使えない場合の予備動作
        elm.SetFocus
        Application.Wait Now + TimeValue("00:00:01")
        SendKeys "^a", True
        SendKeys inputValue, True
    End If

    On Error GoTo 0

End Sub


' ============================================================
' ボタンをクリックする
' ============================================================
Private Sub InvokeButtonByName( _
    ByVal uia As CUIAutomation, _
    ByVal parent As IUIAutomationElement, _
    ByVal elementName As String)

    Dim elm As IUIAutomationElement
    Dim invokePattern As IUIAutomationInvokePattern

    Set elm = FindElementByNameAndControlType( _
                uia, _
                parent, _
                elementName, _
                UIA_BUTTON_CONTROL_TYPE_ID)

    If elm Is Nothing Then
        MsgBox "ボタンが見つかりません: " & elementName, vbExclamation
        Exit Sub
    End If

    On Error Resume Next
    Set invokePattern = elm.GetCurrentPattern(UIA_INVOKE_PATTERN_ID)

    If Not invokePattern Is Nothing Then
        invokePattern.Invoke
    End If

    If Err.Number <> 0 Or invokePattern Is Nothing Then
        Err.Clear

        ' InvokePatternが使えない場合の予備動作
        elm.SetFocus
        Application.Wait Now + TimeValue("00:00:01")
        SendKeys "{ENTER}", True
    End If

    On Error GoTo 0

End Sub


' ============================================================
' デモ用の短い待機
'
' Application.Waitは1秒単位になりやすいため、
' Timer + DoEvents で小数秒の待機にする
' ============================================================
Private Sub DemoPause(Optional ByVal seconds As Double = 0.7)

    Dim startTime As Double

    startTime = Timer

    Do While Timer - startTime < seconds
        DoEvents
    Loop

End Sub

5. 実行すると何が起きるのか?

このマクロを実行すると、VBAが一時フォルダにHTMLフォームを作成し、Edgeで開きます。その後、UI Automationを使って画面上の入力欄を探し、3名分のデータを順番に入力します。

今回のデモでは、あえて入力ごとに短い待機を入れています。通常のUIA操作は非常に高速なため、待機を入れないと一瞬で完了してしまい、読者が何が起きたのか分かりにくくなるためです。

中心となる処理は以下です。

For i = 1 To 3

    SetValueByName uia, browserWindow, "社員番号", data(i, 1)
    DemoPause 0.8

    SetValueByName uia, browserWindow, "氏名", data(i, 2)
    DemoPause 0.8

    SetValueByName uia, browserWindow, "金額", data(i, 3)
    DemoPause 0.8

    SetValueByName uia, browserWindow, "備考", data(i, 4)
    DemoPause 0.8

    InvokeButtonByName uia, browserWindow, "登録"
    DemoPause 1.5

Next i

この部分を見ると、VBA側のデータをWebフォームの各項目に入れ、登録ボタンを押す流れが分かります。

VBAコード内の名前 画面上の項目 実行内容
"社員番号" 社員番号入力欄 A001A002A003 を順番に入力
"氏名" 氏名入力欄 山田太郎佐藤花子鈴木一郎 を入力
"金額" 金額入力欄 12500238009800 を入力
"備考" 備考欄 各人ごとの説明文を入力
"登録" 登録ボタン 1名分ごとにクリック

電卓の num1ButtonplusButton のような内部IDではなく、画面に見える項目名とVBAコードが対応しているため、UIAの動きが理解しやすくなります。

6. HTML側とVBA側の対応関係

このサンプルでは、VBAが生成するHTMLの入力欄に aria-label を付けています。

例えば、社員番号欄は内部的に次のようなHTMLとして作成されています。

<input id="empCode" type="text" aria-label="社員番号">

この aria-label="社員番号" が、EdgeやChromeのUIAツリー上では Name として取得されます。そのため、VBA側では次の条件で探せます。

Set condName = uia.CreatePropertyCondition(UIA_NAME_PROPERTY_ID, "社員番号")

ただし、同じ名前のラベルや要素が画面上に存在する可能性があるため、このコードでは Name だけでなく ControlType も組み合わせています。

Set condName = uia.CreatePropertyCondition(UIA_NAME_PROPERTY_ID, elementName)
Set condType = uia.CreatePropertyCondition(UIA_CONTROL_TYPE_PROPERTY_ID, controlTypeId)
Set condAnd = uia.CreateAndCondition(condName, condType)

これにより、例えば「社員番号という名前を持つ入力欄」だけを探すことができます。

7. UIAで使っている2つの操作パターン

UI Automationでは、画面上の要素を操作するために「パターン」と呼ばれる仕組みを使います。このサンプルで使っている主なパターンは2つです。

7-1. ValuePattern

ValuePattern は、テキストボックスなど「値を持つ要素」に対して使います。

Set valuePattern = elm.GetCurrentPattern(UIA_VALUE_PATTERN_ID)
valuePattern.SetValue inputValue

この処理によって、社員番号欄や氏名欄へ文字列を直接セットしています。

7-2. InvokePattern

InvokePattern は、ボタンなど「実行できる要素」に対して使います。

Set invokePattern = elm.GetCurrentPattern(UIA_INVOKE_PATTERN_ID)
invokePattern.Invoke

この処理によって、登録ボタンをクリックしています。

予備動作も入れている理由: ブラウザや対象アプリによっては、ValuePatternInvokePattern が期待どおり取得できない場合があります。そのため、このサンプルでは予備動作として SetFocusSendKeys も入れています。実務では対象アプリごとに最も安定する方法を選びます。

8. 待機時間を調整する

デモの速度は、コード内の DemoPause の秒数で調整できます。

DemoPause 0.8

もっとゆっくり見せたい場合は、例えば以下のようにします。

DemoPause 1.2

逆に、実務処理として高速に動かしたい場合は、DemoPause の行を削除するか、待機時間を短くします。

実務では高速化、説明では低速化: 実務処理では速い方が便利ですが、記事や教育用デモでは速すぎると理解しづらくなります。そのため、この記事ではあえて低速化しています。

9. 実務Webシステムへの応用

このサンプルはローカルHTMLを自動生成して試していますが、考え方は社内Webシステムでも同じです。

  1. 対象画面をEdgeまたはChromeで開く
  2. Inspect.exe や Accessibility Insights で入力欄やボタンの情報を調べる
  3. NameAutomationIdControlType などで要素を探す
  4. 入力欄には ValuePattern.SetValue を使う
  5. ボタンには InvokePattern.Invoke を使う
  6. 複数件のデータをループで入力する
  7. 画面遷移後は、要素が出るまで再試行する

実務Webシステムでは、HTML側に分かりやすい aria-labelid が付いていない場合もあります。その場合は、NameAutomationIdControlTypeClassName などを組み合わせて対象を絞り込みます。

9-1. AutomationIdで探す場合

対象要素に安定した AutomationId がある場合は、Name ではなく AutomationId で探した方が安定することがあります。

Set cond = uia.CreatePropertyCondition(UIA_AutomationIdPropertyId, "empCode")

ただし、Webアプリでは AutomationId が取得できない場合や、画面更新のたびに変わる場合もあります。その場合は NameControlType と組み合わせます。

9-2. 複数条件で探す場合

同じ名前の要素が複数ある場合は、条件を組み合わせます。この記事のサンプルでも、以下のように NameControlType を組み合わせています。

Set condName = uia.CreatePropertyCondition(UIA_NAME_PROPERTY_ID, "社員番号")
Set condType = uia.CreatePropertyCondition(UIA_CONTROL_TYPE_PROPERTY_ID, UIA_EDIT_CONTROL_TYPE_ID)

Set condAnd = uia.CreateAndCondition(condName, condType)

Set elm = parent.FindFirst(SCOPE_SUBTREE, condAnd)

10. UIAの実務での落とし穴と対策

UIAは強力ですが、実務では以下の点に注意が必要です。

  • タイミング問題:画面遷移後にすぐ要素を取得しようとすると Nothing が返る場合があります。Application.Wait だけに頼らず、要素が見つかるまで再試行する処理を入れると安定します。
  • 要素の特定が困難:Webアプリの場合、AutomationId がなく、Name も動的に変わるケースがあります。その場合は CreateAndCondition で複数条件を組み合わせます。
  • 同じNameの要素が複数ある:例えば「検索」というボタンが複数ある画面では、親要素を絞ってからボタンを探す、または ControlType を併用します。
  • スクロール外の要素:UIAは画面に描画されている要素を取得するAPIです。スクロール外や仮想化された一覧の中にある要素は、取得できない場合があります。
  • 管理者権限の壁:管理者権限で起動しているアプリケーションの要素は、通常権限のExcel VBAからアクセスできない場合があります。Excelと操作対象アプリを同じ権限レベルで実行する必要があります。
  • 仮想デスクトップ:CitrixやVDI環境では、UIAが期待通りに動作しない場合があります。その場合はQR Automation Labのような、USB-HID型QRリーダーを使った物理入力方式が代替手段になります。
  • セキュリティ:パスワードや個人情報を自動入力する場合は、社内規程、ログ、誤送信対策、承認フローを必ず確認してください。

11. QR Automation Lab vs UIA ― 環境で選ぶ自動入力手法

比較項目 QR Automation Lab(本編) UIA VBA(この記事)
VBA 完全不要 ✅ 必須
ソフトウェア制約 全てのソフトが禁止でもOK VBA実行が許可されている必要あり
対象 入力欄にカーソルを置ける画面全般 UIAで要素取得できるアプリ・Web画面
速度 QR読取1件=1〜3秒程度 プログラム実行のため高速
安定性 画面構造に依存しにくい Name / AutomationId / 画面構造に依存
仮想デスクトップ ハードウェア入力なので比較的強い ✅ Citrix等で制限される場合あり
推奨環境 完全閉域・ソフト全面禁止 VBA許可・ローカルPC・UIA取得可能な画面

両方の手法を理解しておけば、環境に応じて最適な自動入力手法を選べます。

12. 公式リファレンス

UIAを実務で利用する場合は、以下のMicrosoft公式情報も確認しておくと理解が深まります。

13. 関連記事