UIAutomation insepectの二段目
投稿者 魔界の仮面弁士  (社会人)
投稿日時
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 )してみるとか。
現在のデスクトップだけが対象ということで良いでしょうか。
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 系メソッドに渡してアクセスします。
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)
こちらだと、全部の数を確認するときにループで回さないといけないので
効率悪いかなと思いました。
デスクトップ配下の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
これで、一応全数取得ができました
すみません。先に自分で調べればよかったです。
''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」だけを持ちます。
予想がついていると思いますが、これらは
「If False Then」や「If True Then」な条件に相当します。
次に、IUIAutomationNotCondition 。
その名が示すように、反転のための Not 処理であり、
こちらは「Function GetChild() As IUIAutomationCondition」だけを持ちます。
このインターフェイスは、CreateNotCondition メソッドによって生成されます。
ここまでくれば、IUIAutomationOrCondition と IUIAutomationAndCondition の働きも分かりますね。
複数の条件を AND/OR 演算で繋ぐためのものです。
Create{And|Or}Condition メソッドは 2 つの条件を繋ぎます。
これを繰り返せばより多くの条件を繋げることができますが、代わりに
Create{And|Or}ConditionFromArray メソッドを使うこともできます。
Create{And|Or}ConditionFromNativeArray については、おそらく使うことはないでしょう。
最後は本題の IUIAutomationPropertyCondition インターフェイス。
こちらはよくご存じでしょうから、説明するまでも無いでしょう。
CreatePropertyCondition メソッドもしくは
CreatePropertyConditionEx メソッドで生成します。Ex 版を使うと、
部分文字列での検索や、大文字小文字を無視した検索ができます。
これを先の NOT / AND / OR と組み合わせれば、より複雑な問い合わせにも対応できますね。
> 効率悪いかなと思いました。
通信回数的にも、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 クラスの静的フィールドに相当します。
はい。条件指定のために必要なのは
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つの命令でもまとめてご説明頂けると
非常に助かります。
重ね重ねありがとうございました
まさかこのように丁寧なご説明いただけるとは
誠に魔界の仮面弁士様には感謝いたします。
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 であれば利用できます。
> これを繰り返せばより多くの条件を繋げることができますが、代わりに
> 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)
直下の質問につなげればよかったのですが、
内容が変わるので、新規に質問させていただきます。
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段目のエレメントを取得する方法です。
いろいろ検索したのですが、よくわかっていないこともあり
質問させて頂きました。よろしくお願いします