10

I can see that an element with specific Automation ID has children in the Inspect tool:

Inspect screenshot

But when I try to retrieve them like this:

AutomationElement aPane = mainWindow.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.AutomationIdProperty, "8264"));
AutomationElementCollection theChildren = aPane.FindAll(TreeScope.Subtree, Condition.TrueCondition);

The aPane element is retrieved correctly, but theChildren element is empty. Any ideas what went wrong?

August
  • 490
  • 3
  • 5
  • 18
  • Something is wrong here. A `Subtree` scoped query includes the searched element and all descendants so `theChildren` should always contains at least 1 element, `aPane`. – Mike Zboray Jan 06 '13 at 22:13
  • well yes, it does retrieve the aPane, but nothing more – August Jan 06 '13 at 22:15
  • OK. Is it possible there is another element with the id "8264"? Do a `FindAll` on the `mainWindow` instead of `FindFirst` to see if there are any others. – Mike Zboray Jan 06 '13 at 22:18
  • @mikez `AutomationElementCollection aPane = mainWindow.FindAll(TreeScope.Subtree, new PropertyCondition(AutomationElement.AutomationIdProperty, "8264"));` returns collection with 1 element. So, sadly, there's only one element with that ID. I supposed it should be unique. – August Jan 06 '13 at 22:23
  • I could ask whether `mainWindow` is the instance you think it is, but assuming it is, I have another idea. I'm not sure it will solve your issue, but I will post it as an answer anyway. – Mike Zboray Jan 06 '13 at 22:33
  • 1
    mikez: You are on the wrong way. This is not a problem in the code of August. What he found is one of the multiple SEVERE BUGS in the Automation framework. Microsoft implemented that very sloppy and it is full of bugs - The same problem as with it's predecessor (the Active Accessiblity framework) that also was useless because it was full of bugs. – Elmue Nov 17 '16 at 13:20

6 Answers6

15

On rare occasions I've found that the Find* calls don't find all automation objects. The only consistent case I've seen with this that WPF TextBlock controls, when in a data template, won't be found by those calls. In these cases, you can try the RawViewWalker which is probably closer to what Inspect is doing internally.

public static IEnumerable<AutomationElement> FindInRawView(this AutomationElement root)
{
    TreeWalker rawViewWalker = TreeWalker.RawViewWalker;
    Queue<AutomationElement> queue = new Queue<AutomationElement>();
    queue.Enqueue(root);
    while (queue.Count > 0)
    {
       var element = queue.Dequeue();
       yield return element;

       var sibling = rawViewWalker.GetNextSibling(element);
       if (sibling != null)
       {
          queue.Enqueue(sibling);
       }

       var child = rawViewWalker.GetFirstChild(element);
       if (child != null)
       {
          queue.Enqueue(child);
       }
    }
}
Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
  • 6
    This answer is wrong. The RawViewWalker does not solve anything. I have the same problem and tried it and the problem stays the same. The cause of children not beeing found is that there are SEVERE BUGS in Microsoft's implementation on the server side of the automation. If you test your same code with a Win32 application, a WPF application and a .NET Forms application you will find that the bugs are varying. While in a WinForms application children are sometimes not returned the same code works perfectly in a Win32 application. Microsoft did a VERY SLOPPY work with the Automation framework. – Elmue Nov 17 '16 at 13:16
  • 1
    I was able to retrieve my missing elements using the `TreeWalker.RawViewWalker` – Uentee Sep 27 '19 at 06:51
2

Actually the problem is that Inspect.exe is written in unmanaged code while I was trying to achieve the same results in managed code. Unmanaged code returns slightly different results than the managed version (e. g. manged code would return control type document where the unmanaged code would return edit in my application).

While it took me some time to understand it, unmanaged code is much faster, more accurate and therefore more reliable.

Some examples of unmanaged UI automation code for C# can be found in the Microsoft Windows UI Automation Blog e. g. here,

August
  • 490
  • 3
  • 5
  • 18
  • This answer is nonsense. The managed code is just a wrapper for the unmanaged COM objects. When you execute managed code the Automation framework calls the same unmanaged COM objects under the hood as when you execute managed code. Managed and unmanaged code use the same UIAutomationCore.dll. There is only one UIAutomationCore.dll in the Windows\System32 directory. – Elmue Nov 17 '16 at 12:48
  • 2
    Managed and Unmanaged really have different implementation. There is even better managed implementation: https://www.nuget.org/packages/UIAComWrapper/ – NN_ Oct 18 '17 at 19:08
2

A bit of a late answer, but I wanted to correct the answer chosen here. Yes, it's true that the VS provided COM wrapper may use a different UIAutomationClient.dll, and that using native code will be different to managed code while calling UIAutomation methods, but nonetheless the question asked here is a different issue. (By the way, you can use a COM wrapper from managed code to call the correct version of the UIAutomation dll's, which will solve issues like "inspect.exe finds it but my managed code cannot").

I also ran into the problem asked here (mine was: FindAll(TreeScope.Children, TrueCondition) not returning anything although FindFirst() was successfully returning children on the same control).

I tried mike-z's approach using RawViewWalker to find children and it worked fine for this case. I'm writing this separate answer to say that it wasn't Find* methods being the problem, but a difference between FindAll & FindFirst methods that caused August's problem.

Update

Inconsistent behavior seems to be the norm when it comes to MS tools. The reason for this update is, I've bumped into a similar issue with my tlbimp.exe'd RCW for uia using C#, and this time I wrote a direct equivalent C code and to my surprise it was working perfectly while the C# code refused working in any way while trying to find a simple OpenFileDialog's controls, then another control on the main form. The only difference between the two worlds is the mysterious MS RCW magic. I'm not sure if it's the way the marshaling is handled with the automatically created COM wrappers (by tlbimp) or something else. And the [ComConversionLoss] attribute that appears for the created interface doesn't sound right to me. Anyways I'm now considering manually crafting the COM interface or converting my whole project to native environment.

AlbusMPiroglu
  • 645
  • 3
  • 13
  • This answer is wrong. There is no "different UIAutomationCore.dll" Search your disk for "UIAutomationCore.dll". On Win 7 I find one in the Windows\System32 (or SysWOW64) directory and one in the WinSXS folder but they have the same version and are binary identical. – Elmue Nov 17 '16 at 13:28
  • The correct answer is that the UI Automation framework is FULL of SEVERE bugs. One of these bugs is that children are returned sometimes and sometimes not. It seems to me that the Automation cache is the cause of the bugs. If I search for children the first time they are found, when I search a second time only the first child is returned. – Elmue Nov 17 '16 at 13:30
  • @Elmue Even i am facing the same issue in my system i am able to get all elements but when at client system i am not able to get the child elements. whats the approach to solve this issue – Ravi Kanth Feb 14 '17 at 05:10
  • @Elmue, you're right, I think I wanted to say UIAutomationClient.dll (I edited my post). Double checking shows an Interop.UIAutomationClient.dll under VS folder assemblies, and another UIAutomationClient.dll under system. – AlbusMPiroglu Feb 14 '17 at 05:21
  • @Ravi, can you try by creating your interop.uiautomationclient.dll on each environment you use? An example from Guy Barker: https://blogs.msdn.microsoft.com/winuiautomation/2016/07/12/building-and-running-a-uia-sample-app-on-windows-10/ – AlbusMPiroglu Feb 14 '17 at 05:24
  • You find the solution to the problem in my post here: http://stackoverflow.com/questions/41768046/system-windows-automation-is-extremely-slow – Elmue Feb 16 '17 at 16:25
1

The difference between managed and unmanaged UI Automation is because the managed use old implementation but Inspect uses COM directly and this is newer version 3.0

Vizor
  • 396
  • 3
  • 14
1

My original example is simplified. I tried to access children using 3 techniques:

  1. The RawViewWalker in .Net managed code.
  2. The equivalent walker in COM, that is, the COM wrappers available in .Net managed code.
  3. Non-.Net code (i.e. unmanaged code) in a completely separate VB6 application I wrote.

Only the VB6 (unmanaged) code gave the same results as Microsoft's Inspect tool. I believe this confirms what others have said above: There are severe problems with Microsoft's UI Automation implementation in .Net. It may be that the only solution to this is to write a custom UI Automation client in .Net, but this assumes that the UI Automation servers in the target applications behave correctly. And those are beyond my control because the target applications are written by other companies, not mine.

Chris W
  • 11
  • 4
  • Yes, you're right. And even there is ready COM implementation for .NET: https://uiacomwrapper.codeplex.com/ – Vizor Jan 13 '18 at 16:09
0

For FindFirst to find a descendant the target element must have IsControlElement set.

In the screenshot from the original poster, IsControlElement is being reported as [Not supported].

In other words, FindFirst searches the control tree, not the raw tree.

Brad Robinson
  • 44,114
  • 19
  • 59
  • 88