UIAutomation won't retrieve children of an ele

2019-01-25 16:17发布

问题:

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

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?

回答1:

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);
       }
    }
}


回答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.



回答3:

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,



回答4:

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



回答5:

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.