Excel VBA UIAutomation 意外と知られていない便利機能まとめ

# 概要
あまり知られていない（ドキュメントが見当たらない）けれど、自動化処理を組む際に実用性があると感じている機能を備忘録的に書き連ねていこうと思う。

## GetClickablePoint メソッド
IUIAutomationElementのメソッド。
文字通り、マウスでクリック可能な座標を取得できる。

座標はtagPOINT型で取得できるので、Invokeが使えないエレメントをクリックしたい時にuser32のmouse_event関数に使う引数を簡単に用意できる。

これを知らない時は、CurrentBoundingRectangleからエレメントの中心座標を割り出すような無駄な処理を用意していた。

## ScrollIntoView メソッド
IUIAutomationScrollItemPatternのメソッド。
スクロールパターン自体は例えばWebページ上の文字や画像、Explorerのアイコンなど、スクロールバーが搭載されたコンテナ内のオブジェクトであれば大抵がサポートしているので、マウス操作のエミュレートが必要な場合には、以下のような流れで処理を行えば良い。
```vb:VBA
    '（略）
    Dim ptn As IUIAutomationScrollItemPattern
    set ptn = elem.getCurrentPattern(UIA_ScrollItemPatternId)
    ptn.ScrollIntoView(0)

    Dim tpt as tagPOINT
    elem.GetClickablePoint(tpt)

    '（略）
```
これで対象エレメントを画面内に表示させ、そこからmouse_eventを使うことができる。
スクロール処理の要否は、前もってCurrentIsOffscreenプロパティを使って確認しても良いかもしれない。

## ShowContextMenu メソッド
IUIAutomationElement3で実装された拡張メソッド。
文字通り、右クリックメニューを表示させることができる。

```vb: VBA

    Dim Elem As IUIAutomationElement3
    
    '（略）
    
    Elem.ShowContextMenu
    
    '（略）
```
IUIAutomationElement「3」インターフェースが必要な点に注意。

## PollForPotentialSupportedProperties メソッド
IUIAutomationのメソッド。
あるエレメントがサポートしているプロパティIDとプロパティ名を列挙してくれる。

```vb:VBA
    Dim uia As New CUIAutomation
    Dim elem As IUIAutomationElement

    '中略、何らかの処理でelemを取得

    Dim PropertyIds() As Long, PropertyNames() As String 

    Call uia.PollForPotentialSupportedProperties(elem, PropertyIds, PropertyNames)
    
```
こんな感じで、プロパティIDとプロパティ名を一括で取得できる。
## PollForPotentialSupportedPatterns メソッド
IUIAutomationのメソッド。
PollForPotentialSupportedPropertiesと同じ要領で、対象エレメントがサポートしているパターンの一覧を列挙してくれる。
```vb:VBA
    Dim uia As New CUIAutomation
    Dim elem As IUIAutomationElement

    '中略、何らかの処理でelemを取得

    Dim PatternIds() As Long, PatternNames() As String 

    Call uia.PollForPotentialSupportedPatterns(elem, PatternIds, PatternNames)
    
```
## CreatePropertyConditionEx メソッド
IUIAutomationのメソッド。
平たく言うと、「検索条件を部分一致にしたIUIAutomationConditionを作成できる」というもの。使い道は以下の３通りを覚えておくとよい。
* IUIAutomationElementのFindFirstメソッド
* IUIAutomationElementのFindAllメソッド
* IUIAutomationのCreateTreeWalkerメソッド

```vb:VBA
    Dim uia As New CUIAutomation
    
    Dim cnd As IUIAutomationCondition
    Set cnd = uia.CreatePropertyConditionEx( _
        UIA_NamePropertyId,"Edge", _
        PropertyConditionFlags_MatchSubstring)

    Dim Elem_TopWindows As IUIAutomationElementArray
    Set Elem_TopWindows = uia.GetRootelement.FindAll(TreeScope_Children, cnd)
```
第三引数で指定するPropertyConditionFlags列挙体
| 定数名 | 値 | 説明 |
|:-:|:-:|:-:|
| PropertyConditionFlags_None  |  0 | 指定なし（Exが付かない方と同じ挙動）|
|  PropertyConditionFlags_IgnoreCase | 1  | 大文字、小文字を区別しない  |
|  PropertyConditionFlags_MatchSubstring | 2  |  部分一致で検索 |

## CreateAndCondition, CreateOrCondition メソッド
IUIAutomationのメソッド。
複数のIUIAutomationConditionを合成することができる。
```vb:VBA    
    Dim uia As New CUIAutomation
    Dim elem As IUIAutomationElement
    Dim cnd1 As IUIAutomationCondition
    Dim cnd2 As IUIAutomationCondition
    Dim cnd As IUIAutomationCondition
    
    set cnd1 = uia.CreatePropertyCondition( _
        UIA_ClassNamePropertyId,"Chrome_WidgetWin_1")
    set cnd2 = uia.CreatePropertyConditionEx( _
        UIA_NamePropertyId, "Edge", PropertyConditionFlags_MatchSubstring)

    Set cnd = uia.CreateAndCondition(cnd1, cnd2)

    Dim Elem_TopWindows As IUIAutomationElementArray
    Set Elem_TopWindows = uia.GetRootelement.FindAll(TreeScope_Children, cnd) 
```
こうすれば、CreateAndConditionにより、「クラス名が"Chrome_WidgetWin_1"である」かつ「タイトルに"Edge"を含む」の条件を満たすトップウィンドウを全て取得することができる。TeamsやChromeを最初から除外し、Edgeのみを操作対象にする時なんかに便利だ。また、見ての通り、FinfWindowなどのAPI関数や、それに付随するウィンドウハンドルを一切使わずにウィンドウを取得できて楽だと思っている。

## CreateTreeWalker メソッド
IUIAutomationのメソッド。
UIAutomationのツリー構造を探索する時は、通常であれば`ControlViewWalker`、`ContentViewWalker`、`RawViewWalker`のどれかを使用すると思う。しかし、`CreateTreeWalker`メソッドによって、特定の条件を満たすエレメントのみで構築されたツリー構造を辿ることができる。
```vb:VBA
    Dim uia As New CUIAutomation
    Dim Elem As IUIAutomationElement

    '（中略）

    Dim cnd As IUIAutomationCondition
    Set cnd = uia.CreatePropertyCondition( _
        UIA_ControlTypePropertyId, _
        UIA_EditControlTypeId)       'UIA_EditControlTypeId = 50004 (&HC354)

    Dim tw As IUIAutomationTreeWalker
    Set tw = uia.CreateTreeWalker(cnd)

    'テキストボックス（EditControl）のエレメントを取得
    Dim Elem_TextBox As IUIAutomationElement
    Set Elem_TextBox = tw.GetFirstChildElement(Elem)
    
    Dim ptn As IUIAutomationValuePattern
    Set ptn = Elem_TextBox.GetCurrentPattern(UIA_ValuePatternId)
    ptn.SetValue "aaa"
```
Webページは構成によっては階層がすごく深く、特定の要素にたどり着くまでにTreeWalkerによる移動を何度も繰り返さなければならない事が多い。そんな時、上記のようにすれば、特定の条件を満たすエレメントに素早く到達できる可能性がある。使い捨てマクロとして雑に処理を書く時なんかに重宝するし、`ControlViewWalker`や`FindFirst`と組み合わせてコーディング量の削減を図ることができるかもしれない。

#### （余談）RawViewWalkerについて
RawViewWalkerは、条件を指定しないTreeWalkerである。`uia.CreateTreeWalker(uia.CreateTrueCondition)`みたいなもの。