FindFirstWithOptions root argument

asked 2023-01-07T09:00:22.653+00:00
Descolada 1 Reputation point

I am trying get FindFirstWithOptions working with the root argument, but for some reason it's not returning the correct result. I am using the COM interface from a scripting language, so I'd rather provide pseudo-code...

Testing platform: Windows 11, Notepad application
element = Notepad window element (ElementFromHandle)
condition = CurrentControlType=MenuItem and (CurrentName="Edit" or CurrentName="View")
root = "View" MenuItem element

Expected result: FindFirstWithOptions search starts from the root element, which immediately matches the condition and the "View" MenuItem element is returned.
Actual result: "Edit" MenuItem is returned, as if the root argument were ignored

EDIT: I wrote an example in VBA that works as an Excel macro, with References UIA and UIAutomationClient. The resulting MsgBox shows the Edit MenuItem, not the expected View MenuItem.

Public oUIA As New CUIAutomation8  
Private Sub Main()  
    Dim oRoot As IUIAutomationElement9  
    Dim oNp As IUIAutomationElement9  
    Dim oEdit As IUIAutomationElement9  
    Dim oView As IUIAutomationElement9  
    Dim oFound As IUIAutomationElement9  
    Dim oCond As IUIAutomationAndCondition  
    Set oCond = oUIA.CreateAndCondition(oUIA.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuItemControlTypeId), oUIA.CreateOrCondition(oUIA.CreatePropertyCondition(UIA_NamePropertyId, "Edit"), oUIA.CreatePropertyCondition(UIA_NamePropertyId, "View")))  
    Set oRoot = oUIA.GetRootElement  
    Set oNp = oRoot.FindFirst(TreeScope_Children, oUIA.CreatePropertyConditionEx(UIA_NamePropertyId, "Notepad", PropertyConditionFlags_MatchSubstring))  
    Set oView = oNp.FindFirst(TreeScope_Descendants, oUIA.CreateAndCondition(oUIA.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_MenuItemControlTypeId), oUIA.CreatePropertyCondition(UIA_NamePropertyId, "View")))  
    Set oFound = oNp.FindFirstWithOptions(TreeScope_Descendants, oCond, TreeTraversalOptions_Default, oView)  
    MsgBox Dump(oFound)  
End Sub  
Public Function Dump(oEl As IUIAutomationElement) As String  
    If oEl Is Nothing Then  
        Dump = ""  
        Dump = "Type: " & oEl.CurrentControlType & " Localized: " & oEl.CurrentLocalizedControlType & " Name: " & oEl.CurrentName & " Value: " & oEl.GetCurrentPropertyValue(UIA_ValueValuePropertyId) & " ClassName: " & oEl.CurrentClassName & " AutomationId: " & oEl.CurrentAutomationId  
    End If  
End Function  
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
1,888 questions
Windows 11
Windows 11
A Microsoft operating system designed for productivity, creativity, and ease of use.
3,482 questions
{count} votes

2 answers

Sort by: Most helpful
  1. answered 2023-01-10T09:00:02.737+00:00
    Limitless Technology 8,871 Reputation points

    Hello there,

    Feel like you are trying to find a descendant MenuItem element of the Notepad window element, with either the name "Edit" or "View", and starting the search from the root element.

    The FindFirstWithOptions function should work as expected with the root argument. Here are a few things to check:

    1. Make sure that the root element is a descendant of the Notepad window element.
    2. Make sure that the root element is a MenuItem element.
    3. Make sure that the condition argument correctly specifies the criteria for the elements you want to find.
    4. Make sure that the TreeScope argument correctly specifies the scope of the search.

    It's also a good idea to check the return value of the FindFirstWithOptions function to see if it's Nothing, which indicates that no element was found that matched the specified criteria.

    If RLWA32-6355 comment is also your case or similar issue, You are correct that the TreeTraversalOptions argument determines the order in which the search is performed. The TreeTraversalOptions_Default value specifies a default traversal order, which is generally from the root element to its descendants in a depth-first order. The TreeTraversalOptions_LastToFirstOrder value specifies that the search should be performed in reverse order, from the last element to the first.

    If you are using the TreeTraversalOptions_Default value and the search is not respecting the root element, it could be because the root element does not match the specified condition. In this case, the search would continue to the next element in the default traversal order until it finds an element that matches the condition.

    On the other hand, if you are using the TreeTraversalOptions_LastToFirstOrder value and the search is not respecting the root element, it could be because the root element is not the last element in the traversal order. In this case, the search would continue to the previous element in the reverse traversal order until it finds an element that matches the condition.

    It's also important to note that the FindFirstWithOptions function only searches the immediate children of the root element if the TreeScope argument is set to TreeScope_Children. If the TreeScope argument is set to TreeScope_Descendants, the function will search all descendants of the root element, regardless of the TreeTraversalOptions value.

    I hope this helps clarify the behavior of the FindFirstWithOptions function.

    --If the reply is helpful, please Upvote and Accept it as an answer–

  2. answered 2023-01-11T19:01:56.93+00:00
    Descolada 1 Reputation point

    @RLWA32 I'm fairly certain I've figured this one out:

    1. If the "root" argument is omitted, then by default it will be the root element (GetRootElement()). This means that if, for example, calling FindFirstWithOptions on the Notepad window with TreeScope_Children and a condition of ControlType==Window, then it will return the next Window element from Notepad: Notepad doesn't have any children with type Window, but the search will continue for the children of the root element starting from the next element from Notepad.
    2. The element from which FindFirstWithOptions is called from must be a descendant of the root argument element. The search will start from that element and then continue on until a match is found.
    3. TreeTraversalOptions_LastToFirstOrder is self-explanatory, it will move in the tree backwards.
    4. TreeTraversalOptions_PostOrder will start the search from the deepest element (see explanation: [ and then work its way upwards. The TreeScope with this one is a bit tricky: if for example using Children, then it will travel the starting element, then its parent and parent siblings (so children will actually be parent), and then it will search root argument element children as well (but not root argument elements parents I think).
    No comments