UIAutomation insepectの二段目

タグの編集
投稿者 でふぁいあんと  (社会人) 投稿日時 2022/12/26 15:07:03
連投すいません。
直下の質問につなげればよかったのですが、
内容が変わるので、新規に質問させていただきます。


UIAutomationで、エレメントを探す場合に
'クラス名
Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "Windows.UI.Core.CoreWindow")
Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)
'テキストボックス'(複数)
Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
Set aryuiElm = elmBFC.FindAll(TreeScope_Subtree, uiCnd)
'ボタン(複数)
Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
Set aryuiElm = elmBFC.FindAll(TreeScope_Subtree, uiCnd)

など、ある条件下のエレメントを探すことはわかりましたが、
全ての配下のエレメントを探す方法はわかりますか?

例えば,inspect.exeを起動したときに、
トップは[デスクトップ1 ウインドウ]となっていて、その次の段(二段目)にすべてのエレメントを網羅しています。この2段目のエレメントを取得する方法です。

いろいろ検索したのですが、よくわかっていないこともあり
質問させて頂きました。よろしくお願いします
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/12/26 15:32:18
> 全ての配下のエレメントを探す方法はわかりますか?

現在のデスクトップだけが対象ということで良いでしょうか。
https://atmarkit.itmedia.co.jp/ait/articles/1608/05/news047.html

とりあえず、GetRootElement メソッドを呼んで、
デスクトップ (ルート要素)を取得するところまではできていますか?

ルート要素から辿る場合は、TreeScope_Descendants を使うと要素数が多すぎて
スタックが不足してしまうので、TreeScope_Children を指定して検索するようにしましょう。


> その次の段(二段目)にすべてのエレメントを網羅しています。
「その次の段(二段目)」というのが何を意味しているか分かりませんでしが:

最初の子要素という意味ならば FindFirst TreeScope_Children
全ての子要素という意味ならば FindAll TreeScope_Children で
無条件探索( CreateTrueCondition )してみるとか。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/12/26 16:14:09
あるいは CreateTreeWalker メソッドを呼び出して
IUIAutomationTreeWalker インターフェイスの
GetFirstChildElement / GetNextSiblingElement メソッドで
ツリーを辿るとか。

下記は C++ 版での探索例ですが、VBA からの COM 呼び出しでも手順的には同じです。
https://learn.microsoft.com/ja-jp/windows/win32/winauto/uiauto-howto-walk-uiautomation-tree

プロパティをキャッシュしておきたい場合は、
CreateCacheRequest で IUIAutomationCacheRequest を取得し、 
それを各種 Cache 系メソッドに渡してアクセスします。
投稿者 でふぁいあんと  (社会人) 投稿日時 2022/12/26 16:52:04
魔界の仮面弁士様、引き続きのお返事ありがとうございます。

デスクトップ配下のelementを探すというのは一例として上げてみました
Set uiAuto = New UIAutomationClient.CUIAutomation
Set uiDsk = uiAuto.GetRootElement

例えば、edge配下の要素などを下っていくときに、
途中で階層がこんがらかったときに全要素、または全要素数を確認して、
inspectの階層と同じかチェックしたいときがあります。

>無条件探索( CreateTrueCondition )してみるとか。
たぶん、これを探していました。
この構文はuiAuto.CreatePropertyConditionと関係するのでしょうか?

TreeViewに関しては、自身でもやってみました。
'Set uiTree = uia.ContentViewWalker
'Set uiElm = uiTree.GetFirstChildElement(uiDsk)
Set uiElm = uiTree.GetNextSiblingElement(uiElm)
こちらだと、全部の数を確認するときにループで回さないといけないので
効率悪いかなと思いました。
投稿者 (削除されました)  () 投稿日時 2022/12/26 17:10:57
(削除されました)
投稿者 でふぁいあんと  (社会人) 投稿日時 2022/12/26 17:12:44
魔界の仮面弁士様ヒントありがとうございます。

すみません。先に自分で調べればよかったです。
''CreateTrueConditionは、CreatePropertyConditionは不要だったのですねですね。

Set uiAuto = New UIAutomationClient.CUIAutomation
Set elmDsk = uiAuto.GetRootElement
Set aryElm = elmDsk.FindAll(TreeScope_Children, uiAuto.CreateTrueCondition)

  For I = 0 To aryElm.Length - 1
  Debug.Print aryElm.GetElement(I).CurrentName, aryElm.GetElement(I).CurrentClassName
  Next

これで、一応全数取得ができました
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/12/26 17:56:29
> こちらだと、全部の数を確認するときにループで回さないといけないので
> 効率悪いかなと思いました。
通信回数的にも、FindAll / FindFirst による検索の方が効率的ですね。
https://learn.microsoft.com/ja-jp/windows/win32/api/uiautomationclient/nn-uiautomationclient-iuiautomationtreewalker


> この構文はuiAuto.CreatePropertyConditionと関係するのでしょうか?
はい、関係します。
検索条件はすべて「IUIAutmationCondition インターフェイス」を通じて行われます。


IUIAutomationCondition 自体は、プロパティやメソッドを一切保有していませんが、
継承先のインターフェイスによって、いくつかのメンバーが追加されます。

継承関係は下記の通り。

IUIAutmationCondition
├IUIAutomationBoolCondition
├IUIAutomationNotCondition
├IUIAutomationAndCondition
├IUIAutomationOrCondition
└IUIAutomationPropertyCondition



CreatePropertyCondition メソッドは既にご存じのはずなので、
まずは、IUIAutomationBoolCondition からみていきましょう。
これは ReadOnly な「Property BooleanValue As Long」だけを持ちます。


Dim ac As IUIAutomationCondition '検索条件を表すインターフェイス 

Dim ua As CUIAutomation
Set ua = New CUIAutomation

Dim bc0 As IUIAutomationBoolCondition
Set bc0 = ua.CreateFalseCondition()  'FALSE 条件を生成 
'Set ac = bc0 
Debug.Print bc0.BooleanValue  '「0」 

Dim bc1 As IUIAutomationBoolCondition
Set bc1 = ua.CreateTrueCondition()  'TRUE 条件を生成 
'Set ac = bc1 
Debug.Print bc1.BooleanValue  '「1」 



予想がついていると思いますが、これらは
「If False Then」や「If True Then」な条件に相当します。


次に、IUIAutomationNotCondition 。
その名が示すように、反転のための Not 処理であり、
こちらは「Function GetChild() As IUIAutomationCondition」だけを持ちます。

このインターフェイスは、CreateNotCondition メソッドによって生成されます。

Dim nc0 As IUIAutomationNotCondition
Set nc0 = ua.CreateNotCondition(bc0)
Set nc0 = ua.CreateNotCondition(bc1)
'Set ac = nc0 
'Set ac = nc0.GetChild() 



ここまでくれば、IUIAutomationOrCondition と IUIAutomationAndCondition の働きも分かりますね。
複数の条件を AND/OR 演算で繋ぐためのものです。

Dim ac0 As IUIAutomationAndCondition
Dim oc0 As IUIAutomationOrCondition
Set ac0 = ua.CreateAndCondition(bc0, bc1)
Set oc0 = ua.CreateOrCondition(bc0, bc1)


Create{And|Or}Condition メソッドは 2 つの条件を繋ぎます。
これを繰り返せばより多くの条件を繋げることができますが、代わりに
Create{And|Or}ConditionFromArray メソッドを使うこともできます。
Create{And|Or}ConditionFromNativeArray については、おそらく使うことはないでしょう。


最後は本題の IUIAutomationPropertyCondition インターフェイス。
こちらはよくご存じでしょうから、説明するまでも無いでしょう。

Dim pc0 As IUIAutomationPropertyCondition
Set pc0 = ua.CreatePropertyCondition(Id, Value)
'Set pc0 = ua.CreatePropertyConditionEx(Id, Value, Flags) 
'Set ac = pc0 
Debug.Print pc0.PropertyId
Debug.Print pc0.PropertyValue
Debug.Print pc0.PropertyConditionFlags



CreatePropertyCondition メソッドもしくは
CreatePropertyConditionEx メソッドで生成します。Ex 版を使うと、
部分文字列での検索や、大文字小文字を無視した検索ができます。

これを先の NOT / AND / OR と組み合わせれば、より複雑な問い合わせにも対応できますね。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/12/26 18:19:04
> ''CreateTrueConditionは、CreatePropertyConditionは不要だったのですねですね。

はい。条件指定のために必要なのは
IUIAutomationPropertyCondition ではなく
IUIAutomationCondition ですので、無条件検索時には
CreatePropertyCondition は不要です。


なお、COM 版では IUIAutomationBoolCondition インターフェイスが公開されていますが、
.NET Managed 版では、 NotCondition は非公開(private)なクラスになっています。
https://referencesource.microsoft.com/#UIAutomationClient/System/Windows/Automation/Condition.cs,179

さらに蛇足ついでに言えば、COM 版の CreateTrueCondition メソッド / CreateFalseCondition メソッドは
.NET Managed 版における Condition クラスの静的フィールドに相当します。

Dim tc = Condition.TrueCondition
Dim fc = Condition.FalseCondition
投稿者 でふぁいあんと  (社会人) 投稿日時 2022/12/28 16:24:52
「結果がわかった」で解決しましたが、
まさかこのように丁寧なご説明いただけるとは
誠に魔界の仮面弁士様には感謝いたします。

IUIAutmationについては断片的な情報しかないようで
(私の検索下手かもしれませんが、、)
このように1つの命令でもまとめてご説明頂けると
非常に助かります。

重ね重ねありがとうございました
投稿者 (削除されました)  () 投稿日時 2023/1/2 13:56:20
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/1/2 13:59:05
> Create{And|Or}Condition メソッドは 2 つの条件を繋ぎます。
> これを繰り返せばより多くの条件を繋げることができますが、代わりに
> Create{And|Or}ConditionFromArray メソッドを使うこともできます。
> Create{And|Or}ConditionFromNativeArray については、おそらく使うことはないでしょう。

間違っていました。m(_ _)m

CreateAndConditionFromArray や
CreateOrConditionFromArray は、 VBA から利用できないようです。

CreateAndConditionFromNativeArray や
CreateOrConditionFromNativeArray であれば利用できます。

Dim uiAuto As CUIAutomation8
Set uiAuto = New CUIAutomation8

Dim cond(0 To 2) As IUIAutomationCondition
Set cond(0) = uiAuto.CreateAndCondition( _
    uiAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId), _
    uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "Chrome_WidgetWin_1") _
)   'Chromium(Google Chrome, Microsoft Edge) 
Set cond(1) = uiAuto.CreateAndCondition( _
    uiAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId), _
    uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "IEFrame") _
)   'MSIE(Microsoft Internet Explorer) 
Set cond(2) = uiAuto.CreateAndCondition( _
    uiAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_PaneControlTypeId), _
    uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "MozillaWindowClass") _
)   'Firefox(Mozilla Firefox) 

'『Visual Basic でサポートされていないオートメーション タイプが関数で使用されています。』 
'Set condBrowser = uiAuto.CreateOrConditionFromArray(cond) 

'第一引数に「配列の先頭要素」を渡し、第二引数に「配列の要素数」を渡す 
Set condBrowser = uiAuto.CreateOrConditionFromNativeArray(cond(0), 3)

Dim uiBrowsers As IUIAutomationElementArray
Set uiBrowsers = uiAuto.GetRootElement().FindAll(TreeScope_Children, condBrowser)