【UiPath】アンカーを使ったコントロールの指定についてもう一度解説!

本投稿は[UiPath (ja) Advent Calendar 2019](https://qiita.com/advent-calendar/2019/uipath_ja) 8日目となります。WindowsアプリケーションやHTMLフォームでコントロール (UI要素) を指定するときに、変わりやすいUIに対して応用編で使う「アンカー」「アンカーベース」という仕組みですが、既存でいくつか解説記事が出ていますが、まったく初めての人にはどれもちょっと難しいのではないか！？と思い書き始めたのがこの投稿になります。(自分なりにかみ砕いて書いたつもりですが、わかりにくかったらごめんなさい!)

# はじめに: そもそもなぜコントロールはセレクターでつかめるのか
Windowsでは、昔から障碍者向けの機能を実装するためのアクセシビリティ機能が充実していました。たとえば色覚障碍者向けに画面の色調を変える「ハイコントラスト」が一番有名ですが、それに加えて聴覚障碍者向けに文字を音声にする「テキスト読み上げ」、マウスやキーボードが使えない障碍者のために声やその他の入力からマウスやキーボードをエミュレートする機能などです。

後に挙げた2つの機能を実装するには、Windowsのユーザーインターフェイス (UI)内に現れる各コントロールをシステムが認識できるようにして、障碍者のアシストを行うアプリケーションに情報を渡せるようにする必要があります。そのために生まれたのが「**UI Automation**」と呼ばれる標準APIです。Windowsのすべての画面要素は一番広い「デスクトップ」の子コントロールという形で階層的 (親子兄弟の関係) につながっています。

一方、ウェブページ/アプリケーションはブラウザーで表示する文書の構造解析/操作を標準化して扱いやすくしたいという別のニーズから「**Document Object Model (DOM)**」という仕様が標準化されました。

RPAの画面操作技術はこれらの仕組みに乗っかっているのです。より詳しい解説は @RPAbot さんの「[RPAを支える技術についての解説](https://qiita.com/RPAbot/items/9d87e9292ac2deded338)」に譲ります。UiPathで使われる、UIの「**セレクター**」は、これらのAPIを使って実現しています。

# コントロール (UI要素) がつかみにくい場合がある
しかし、世の中にはこのルールに従ってくれるアプリケーションばかりではありません。WindowsやVisual Studioが提供する「標準コントロール」およびコントロールをその単位で見た目を変える処理をしている限りはUI Automationはきちんと働くのですが、**ひとつのコントロールを巨大なキャンパスとして、その中にいろいろなコントロールを自分で描画してしまう場合 (カスタムドローイング)**は、Windowsシステムからはこのキャンバスの中の各コントロールは認識できなくなってしまいます。

### ケース1: アクセシビリティが考慮されていないアプリケーション
有名なWindowsアプリケーションで実例を挙げると、たとえば「Adobe Acrobat Reader DC」です。起動してすぐの画面は以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/9f198734-f398-60fd-772d-807e10e184d9.png)

UI Explorerで調べてみると、緑色で囲ったコントロールはつかむことが可能ですが、赤枠のペインはひとつのコントロールで認識され、この中は「**カスタムドローイング**」が行われています。そのため、赤枠の中の "コントロール" のように見えるひとつひとつのオブジェクトをUI Explorerでつかむことはできません。同様に、赤く丸を付けたコントロールもUI Explorerには反応しません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/d95bd05b-a86a-8c87-f83a-cb315d6e3301.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/8738c2b5-a7db-96a5-5702-175af246044e.png)


<font color="Red">※ 2020年12月現在のバージョン (2020.013.20074)では、上記でつかめなかった場所のコントロールも掴めるようになっています。(ただ、試す回によっては相変わらずつかめない場合があり、動作が不安定である可能性があります。)</font>

### ケース2: 動的に変わるHTMLフォーム
ウェブアプリケーションの場合、各コントロールの構造は明確なのですが、**コントロールにID、名前や目印がついていない場合**に、コントロールの識別が難しくなります。ウェブアプリケーションはWindowsアプリケーションよりも**レイアウトが動的**であり、座標や画像認識での識別が難しくなる場合があります。また、場合によってはウェブページ内に画像が貼り付けられ、**画像の中に文字が埋め込まれる場合**も考えられます。

動的に変わるウェブアプリケーションはUiPathがRPA Challenge ( http://www.rpachallenge.com/?lang=JA )というデモアプリケーションで環境を提供しています。個人情報登録フォームが、登録するたびにレイアウトやコントロールのIDが変わる仕組みになっています。現実にもJavaScriptで動的に生成されるフォームはこのようなつくりになっていることがあるようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/d9bbcc86-ac64-407c-28c8-b9058ac62147.png)


### コントロールが識別できない場合に起こること
ロボットの実行時に「コントロールが見つからない」というエラーが表示されたり、エラーは出なくても異なるコントロールに対して操作が行われる可能性があります。どちらもその後のシーケンスがきちんと動作しなくなります。

# アンカーって何?
UiPathでこれらのようなつかめないコントロールをつかむための足掛かりになるのが「**アンカー**」の機能です。アンカーとは日本語で「**いかり (錨⚓)**」のことで、まわりのコントロールに下ろしておくことで、近くのコントロールを認識しやすくする仕組みです。「**[アンカーベース](https://docs.uipath.com/activities/lang-ja/docs/anchor-base)**」は、「錨を下ろす処理をするステップ」と考えてください。

>似たような仕組みはMicrosoft WordやPowerPointのようなドキュメント作成アプリケーションでもあります。Wordではフローティングオブジェクト (画像)の位置を測る基準となる場所のことをアンカーポイントと呼んでおり、これと同じ概念です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/586c59d4-9a90-8083-46ee-83ee0b52554d.png)


# アンカーダイアログボックスが出る場合
UiPathでUI操作のレコーディングを行っていると、コントロールを選択した後に以下のようなダイアログボックスが出ることがあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/ff3772ee-d060-119e-d956-c76e99f05b1c.png)
>**アンカーを使いますか?**
選択されたコントロールは信頼性のあるセレクターを持ちません。固定属性を持つ相対要素 (アンカー)を指定することにより、より確実に対象を特定できます。

これは、指定したUI要素に十分な情報がないとUiPathが判断した場合に表示されます。この場合は、アンカーとなる周りのコントロールを指定しておくことで、よりコントロールの識別精度が上がる場合があります。ただし、実際に精度が上げられるかどうかは試行錯誤をする必要があります。


### どのような場合にアンカーダイアログボックスが出るか試してみる
では、どのようなケースでアンカーダイアログボックスが出るのか試してみました。リアルアプリケーションだと条件が判定しづらいので、Visual Studioでアプリケーションを作って、様々な画面レイアウトのものを作りUiPathのレコーディング機能で操作してみることにしました。

**1. Windows Formの入力画面**
まず、Windows FormのC#アプリケーションをビルドし、以下のようにラベル付きの入力フィールド (TextBox) が3つ並んだフォームで試してみました。これはダイアログボックスが表示されずに普通に識別できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/6c1109f1-d68b-7f47-1a5e-f9f920a1c51f.png)

UI Explorerで見た時のセレクターのXMLは以下のような形です。コントロールにtextBox1という名前もついているので、周りにラベルがあってもなくてもコントロール名で識別が可能です。

```XML:
<wnd app='windowsformsapp1.exe' ctrlname='Form1' />
<wnd ctrlname='textBox1' />
```

**2. WPFの入力画面**
次にWPF (Windows Presentation Foundation)のC#アプリケーションを、以下のようにラベル付きの入力フィールド (TextBox) が3つ並んだフォームで試してみました。レコーディング中に真ん中のテキストボックスを選択して入力しようとすると、今度はアンカーダイアログボックスが表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/6dbb113b-56da-d321-a625-49fb7420453c.png)

UI Explorerで見た時のセレクターのXMLは以下のような形です。**コントロールには名前がついておらず**、idx属性がついているものの、これだけでは信頼できないということなのでしょう。(間に新しいコントロールが入るとidxは自動で変更になる可能性がある)

```XML:
<wnd app='wpfapp2.exe' cls='HwndWrapper*' title='MainWindow' />
<ctrl idx='2' role='editable text' />
```
アンカーを左のラベル (Label3)に設定してレコーディングを終了すると、以下のようなシーケンスが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/04869a77-cae7-fb2c-691f-bafcdf796e3f.png)

作成されたアクティビティのメニューからセレクターを見てみると、idx属性は収録されていないことがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/5a11b2d8-b809-0668-6698-f9ce7e52b4f2.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/b45c3ed8-2357-9a40-ab4f-bf4672c45daa.png)

アンカーを設定したLabelコントロールと操作のターゲットとなるTextBoxコントロールはUI要素の階層構造でいうと以下のように兄弟関係となります。識別しやすい兄弟のコントロールを起点 (アンカー) にして、近くのコントロールを識別することになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/4c5ff81a-a051-6a0a-0627-a1dc49143cf2.png)

Labelコントロールはname属性が表示名になっており、一意の識別が可能です。

```XML:
<wnd app='wpfapp2.exe' cls='HwndWrapper*' title='MainWindow' />
<ctrl name='Label3' role='text' />
```

このようにウェブに近い技術のWPFではアンカーを使う必要が出てくることがあります。また、ウェブで作成するフォームも、入力フィールドのコントロールに名前を付けることが必須でないので、同様にアンカーでの対応が必要になってくるケースがあります。

### 自分でアンカーアクティビティの挿入もできる
レコーディングでアンカーが作成されない場合でも、アクティビティの一覧からアンカーベースをシーケンスに挿入することが可能です。「UI Automation」-「要素」-「検出」に「**アンカーベース**」と「**コンテキスト対応のアンカー**」の2つが存在します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/57739c00-428e-0cc0-70fc-54ab9f2f52fe.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/66892910-65e4-7752-f660-137714704a03.png)

「コンテキスト対応のアンカー」は[UiPath v2019-fastTrackの新機能](https://www.uipath.com/ja/products/release-notes/uipath-v2019.4.2)で、コンテキスト対応版は精度があげられるようです。

>以前の [アンカーベース (Anchor Base)] アクティビティ上に構築することで、新しい [コンテキスト対応のアンカー (Context Aware Anchor)] アクティビティを作成しました。精度を向上させるためにアンカーとターゲットのペアが考慮されるようになり、アンカーが一意である必要性がなくなりました。

# まとめ
いかがでしたでしょうか。UI要素の指定としては応用編になりますが、この仕組みを理解して操作方法をマスターしておくと、通常のレコーディングで識別しにくいコントロールが出てきたときに、まわりのコントロールを駆使してターゲットしたいコントロールにたどり着くことができる場合があります。

# 参考記事

* [コアワカ - UiPathのアンカーベースを使って1ランク上の自動化処理を作成する方法](https://koawaka.com/rpa_anchor-base/)
* [【できるUiPath】アンカーベースと相対要素の使い方。近くの要素を手がかりに特定要素を操作する](https://dekiru.net/article/17788/)
* [キャスレー - 【UiPath】アンカーを使用した相対的なセレクター作成について](https://www.casleyconsulting.co.jp/blog/engineer/4007/)
* [RPA Navi - 【UiPath】Anchor Baseの使い方](http://rpa-navi.com/archives/255)