Excel VBA UIAutomation開発支援ツール UIA_Explorer

## ファイルダウンロード
OneDrive直リンクです。
https://1drv.ms/x/s!AoqQVc8cDlxVg7E1nULo_AMJGYwa5A?e=y2w7AY
※5/26更新(R1)

PCのブラウザからリンクを開いたらWeb版のExcelが起動しますので、ファイル→名前を付けて保存→コピーのダウンロード　で保存してください。

何となく触ってみれば使い方は分かるかもしれません。解説記事か動画を作ろうと思います。機会があれば説明会とかも。

## 動作環境
* Windows10以降
* 64bit版のMicrosoft Excel（デスクトップアプリ）
※Word,PowerPoint,Accessにも移植できますが、参照設定やらが若干面倒です
* UIAutomationに頼らざるを得ない環境（他ので済むならそれに越したことは無いです）

## 機能
### ツリー構築による構造調査
* ``Do Loop``で``ElementFromPoint``を自動実行しツリーを構築 
* ドラッグ＆ドロップで``ElementFromPoint``を実行しツリーを構築 
* ``Do Loop``で``GetFocucedElement``を自動実行しツリーを構築
* ノードをダブルクリックして子要素を表示

### エレメントの詳細情報を取得
* エレメントの位置を枠線によって強調表示
* エレメントが持つプロパティの一覧表示
* エレメントが持つコントロールパターンの一覧表示
### エレメントに対する処理テスト
* ``SetFocus``メソッド実行
* ``ShowContextMenu``メソッド実行
* クリック処理（左、右、中央）
* 各種コントロールパターンの動作テスト
* ``IAccessiblePattern``から``Acc``ツリー展開
* ``TextPattern``などから``TextRange``ビューワー展開
* ``Internet Explorer_Server``クラスから``IHTMLDocument``ビューワー展開（試作）

### コーディング支援
* 最初だけ``New CUIAutomation``宣言コードを挿入
* ``FindFirst``/``FindAll``でのエレメントの取得
* ``FindFirst``/``FindAll``に使用する検索条件を``ListView``から設定
* 検索条件を``CreateAndCondition``/``CreateOrCondition``のどちらで合成するかの選択
* ``TreeScope``の設定（``Treescope_Children``/``TreeScope_Subtree``）
* ``FindAll``で取得した後、``GetElement(xx)``で更に要素を取得
* ``FindAll``で取得した後、``For Each``で要素を巡回
* ``TreeWalker.GetParentElement``でのエレメントの取得
* ``TreeWalker.GetPreviousSiblingElement``でのエレメントの取得
* ``TreeWalker.GetNextSiblingElement``でのエレメントの取得
* ``TreeWalker.GetFirstChildElement``でのエレメントの取得
* ``TreeWalker.GetLastChildElement``でのエレメントの取得
* ***これらを１処理として、シナリオリストに登録***
* ***登録した一連の処理のコードをクリップボードにコピー***
* 選択したエレメントをトップウィンドウから``TreeWalker``で取得するインスタントコードの出力

## 操作方法
:::note info
書きかけです。時間がある時に少しずつ書いていきます。
:::
### エレメントツリー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/f2c0b0e6-d8fc-f292-218a-1e74359e55b4.png)
デスクトップ上のUIautomation要素をツリー表示します。ツール起動直後はトップウィンドウが一覧表示されています。ツリーのノードをダブルクリックすることで、対象要素の子要素を展開できます。子要素の取得を繰り返すことで、処理対象の要素の取得経路などを調査することができます。また、各要素の持つプロパティやコントロールパターンについても確認が可能です。

### プロパティリスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/fd25d3c1-5c99-119a-d030-3320246e28b4.png)
エレメントツリー内で選択した要素が持つ各種プロパティ値を一覧表示しています。これは``CUIAutomation.PollForPotentialSupportedProperties``メソッドによって取得したプロパティIDとプロパティ名を取得 →　それを``IUIAutomationElement.GetCurrentPropertyValue``メソッドの引数に使ってプロパティ値を取得 → リストにそれぞれの値を表示 という流れで実現しています。プロパティの値が``IUIautomationElementArray``なんかだと表現がむずしいので、「ｘ個のエレメント」などと表示させる程度に留めています。

### コントロールパターンリスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/cf168037-e76c-9f8f-f5c2-b3de55caeb5d.png)
プロパティリストと同様、対象のエレメントがサポートしているコントロールパターンの一覧を表示しています。こちらは``CUIAutomation.PollForPotentialSupportedPatterns``メソッドによって取得しています。

### 要素の位置の表示／非表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/f12a62ab-1a9c-ee5c-6f58-f08c9463c97a.png)
この「枠」ボタンがオンの時は、エレメントツリー内で選択中の要素が画面上のどこにあるのかを色のついた枠で表示します。これは``CurrentBoundingRectangle``プロパティでの座標取得 → user32の関数によって座標位置に透明なウィンドウを作成 → gdi32の関数による枠線描画 という手法で実現しています。

### 要素位置表示枠のカラー変更
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/87eddec0-7bb6-2d45-d953-cb326124475c.png)
このボタンを押すと「色の設定」ダイアログが呼び出され、そこで選択した色で枠の色を変更することができます。

### エレメントツリーのリセット
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/d09fd713-ff98-8db8-e3af-410fcde89e9d.png)
ツリービューの内容を一旦消去し、デスクトップの現在のトップウィンドウを再取得します。

### TreeWalkerの変更
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/6c1079fb-41cb-e391-71c1-edaa4a7b54fe.png)
エレメントツリーの構築に使用する``IUIautomationTreeWalker``の種類を``ControlViewWalker``、``ContetntViewWalker``、``RawViewWalker``から選択します。

### エレメントサーチ（カーソル起点、自動実行）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/c58b71de-b9b5-f5f4-7477-4e2d52e90860.png)
このトグルスイッチをONにしておくと、ループ処理で``GetCursorPos``でカーソル座標を連続して取得 → 一定時間カーソル座標に変換がなければ``ElementFromPoint``でカーソル座標上の要素を取得 → 取得した要素を起点にエレメントツリーを構築 という処理が行われます。Inspectと同じような感じで画面上のエレメントの構成を調査することができます。

### エレメントサーチ（フォーカス起点、自動実行）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/3dba6f6f-db08-e564-bad5-5820bd2d596b.png)
エレメントサーチのトグルスイッチに合わせてこちらのトグルスイッチもONにしておくと、``ElementFromPoint``ではなく``GetFocucedElement``によって取得した要素を起点にしてエレメントツリーが構築されるようになります。

### エレメントサーチ（カーソル起点、ドラッグ＆ドロップ単発）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/ab9a28c8-0bf2-940b-9829-b4ea46c34cf8.png)
このアイコンを取得したい要素までドラッグ＆ドロップすることで、カーソル位置で``ElementFromPoint``を実行して要素を取得し、エレメントツリーを構築します。

### SetFocusメソッド実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/86afebbf-4b99-73f2-b0e9-436741430f88.png)
エレメントツリーで選択中の要素に対して``SetFocus``メソッドを実行します。実行後にフォーカスを外さないままツリー探索を行うために、エレメントサーチが有効になります。

### ShowContextMenuメソッド実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/226bd1db-c41b-dbe0-dc97-7673cc0f3fb9.png)
エレメントツリーで選択中の要素に対して``ShowContextMenu``メソッドを実行します。実行後にポップアップしたメニューを消さないままツリー探索を行うために、エレメントサーチが有効になります。

### マウスクリック実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/d5e1d2d1-281a-3b58-260d-1dd38a30b861.png)
エレメントツリーで選択中の要素に対してマウスのクリック操作を実行します。``GetClickablePoint``または``CurrentBoundingRectangle``で座標を取得 → ``SetCursorPos``でカーソルを移動 → ``mouse_event``でクリックを実現しています。実行後はそのまま探索を行うために、エレメントサーチが有効になります。

### 各種コントロールパターンの実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/aa0cb9f4-4c62-4e98-c25a-8fa8ad584362.png)
このリストに表示されているパターンをダブルクリックすることで、コントロールパターン操作用のフォームを表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/4815f965-a406-61e6-9f53-5dc47a656d5f.png)
このフォームは、各種コントロールパターンのメソッドの実行や、プロパティ値の確認ができるようになっています。様々なパターンに対応するようにクラスを準備していますが、どう頑張っても実際の場面ではサポートしている要素が無いパターンもありました。

### IAccessibleのツリーを探索するためのACC_Explorer
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/2348c4c3-b888-5b05-4153-c3b09af00561.png)
コントロールパターンの１つである``LegacyIAccessiblePattern``のメソッドに``GetIAccessible``があります。言葉の通りですが、これは``IUIautomationElement``を``IAccessible``オブジェクトに変換する機能で、ここから``IAcessible``としてツリー探索を続行できるようにしてみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769916/d5a4329a-79e7-20d3-f217-2d9001c6f23e.png)
ダブルクリックで子要素を取得したり、選択中の要素の基本的なアクションが行えるだけでコーディング支援機能などもありませんので、オマケ程度の機能と思ってください。

:::note info
続きは時間がある時に追記します。
:::


## その他
オマケのつもりで最後に追加したコーディング支援機能でしたが、「そういえばこんな処理もよく書くよね」と思いながら機能を拡張させた結果、想定外に強烈なものになりました。<br>
私と同じく、システムの改修や導入に一切口を出せない立場で苦労している人に届けばと思います。

## 更新履歴
※5/26更新(R1)
* 選択中のエレメントをトップウィンドウからのツリー移動のみで全自動で取得するコード生成機能を追加しました。
* 選択中のエレメントがInternet Explorer_Serverクラスだった場合、IHTMLDocumentを取得してツリーを展開するサンプル機能を追加しました。

※5/25更新(R1)
* シナリオ登録時にプロパティコンディションも保存し、後から呼び出せるようにしました。
* コントロールパターンのテスト領域を別ウィンドウで展開するように変更しました。
* これに伴い、フォームサイズを小さめにしました。


※5/25更新(R2)
* ドラッグ＆ドロップでも座標を取得できるようにしました。試作段階で実装していた機能でエレメントサーチの自動実行が作れたのでそちらに移行したのですが、実際に使ってみたら単発での取得の方が便利な場面もあったので復活させました。