UIAutomation无法检索元素的子级。

10

我在检查工具中可以看到一个具有特定自动化ID的元素有子元素:

Inspect截图

但是当我尝试像这样检索它们时:

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

成功检索到aPane元素,但theChildren元素为空。有任何想法是出了什么问题?


这里有些问题。Subtree 范围的查询包括搜索元素和所有后代,因此 theChildren 应该始终包含至少一个元素 aPane - Mike Zboray
是的,它确实检索到了aPane,但没有更多的内容。 - August
好的。是否有可能存在另一个ID为“8264”的元素?尝试在mainWindow上执行FindAll而不是FindFirst,以查看是否还有其他元素。 - Mike Zboray
@mikez AutomationElementCollection aPane = mainWindow.FindAll(TreeScope.Subtree, new PropertyCondition(AutomationElement.AutomationIdProperty, "8264")); 返回一个带有1个元素的集合。所以,遗憾的是,只有一个具有该ID的元素。我本来以为它应该是唯一的。 - August
我可以问一下mainWindow是否是您想要的实例,但是假设它是,我有另一个想法。我不确定它是否会解决您的问题,但我无论如何都会将其发布为答案。 - Mike Zboray
1
mikez:你走错路了。这不是八月代码中的问题。他发现的是自动化框架中多个严重错误之一。微软实现得非常粗糙,而且充满了漏洞——与其前身(Active Accessibility框架)相同,也是无用的,因为它充满了漏洞。 - Elmue
6个回答

15

偶尔我发现Find*调用不能找到所有的自动化对象。我唯一看到这种情况的一致情况是,当WPF TextBlock控件在数据模板中时,这些调用无法找到它们。在这些情况下,您可以尝试使用RawViewWalker,这可能更接近于Inspect在内部执行的操作。

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

6
这个答案是错误的。RawViewWalker 并没有解决任何问题。我有同样的问题,并尝试了它,但问题仍然存在。孩子们无法被找到的原因是在自动化的服务器端,微软实现中存在严重的缺陷。如果你在一个 Win32 应用程序、一个 WPF 应用程序以及一个 .NET Forms 应用程序中测试相同的代码,你会发现这些 Bug 是不同的。虽然在 WinForms 应用程序中有时会返回不完整的孩子,但相同的代码在 Win32 应用程序中可以完美地工作。微软在自动化框架方面做了非常草率的工作。 - Elmue
1
我成功地使用 TreeWalker.RawViewWalker 检索到了我的丢失元素。 - Uentee

2
实际上问题在于Inspect.exe是用非托管代码编写的,而我试图在托管代码中实现相同的结果。非托管代码返回的结果与托管版本略有不同(例如,在我的应用程序中,托管代码将返回文档控件类型,而非托管代码将返回编辑)。
虽然我花了一些时间才理解这一点,但非托管代码更快、更准确,因此更可靠。
一些未经管理的C# UI自动化代码示例可以在Microsoft Windows UI Automation Blog中找到,例如这里

这个回答是无意义的。托管代码只是非托管COM对象的包装器。当您执行托管代码时,自动化框架在幕后调用与执行非托管代码相同的非托管COM对象。托管和非托管代码使用相同的UIAutomationCore.dll。在Windows\System32目录中只有一个UIAutomationCore.dll。 - Elmue
2
托管和非托管实现确实有所不同。甚至有更好的托管实现:https://www.nuget.org/packages/UIAComWrapper/ - NN_

2
有点晚了,但我想纠正这里选择的答案。是的,VS提供的COM包装器可能会使用不同的UIAutomationClient.dll,并且在调用UIAutomation方法时使用本机代码与托管代码将有所不同,但尽管如此,这里提出的问题是一个不同的问题。(顺便说一句,您可以使用来自托管代码的COM包装器调用正确版本的UIAutomation dll,这将解决“inspect.exe找到它但我的托管代码无法找到”的问题)。
我也遇到了这里提出的问题(我的问题是:FindAll(TreeScope.Children, TrueCondition)没有返回任何内容,尽管FindFirst()成功返回了相同控件的子项)。
我尝试了mike-z的方法,使用RawViewWalker查找子项,在这种情况下它很好用。我写这个单独的答案是为了说明不是Find*方法成为问题,而是FindAll和FindFirst方法之间的差异导致了August的问题。
更新
当涉及MS工具时,不一致的行为似乎是常态。更新的原因是,我在使用C#的tlbimp.exe'd RCW时遇到了类似的问题,这次我编写了一个直接等效的C代码,令人惊讶的是,它完美地工作,而C#代码却无论如何都无法在尝试查找简单的OpenFileDialog控件时工作,然后是主窗体上的另一个控件。两个世界之间唯一的区别是神秘的MS RCW魔法。我不确定它是由tlbimp自动创建的COM包装器处理封送还是其他原因。而为创建的接口出现的[ComConversionLoss]属性对我来说听起来不对。无论如何,我现在正在考虑手动制作COM接口或将整个项目转换为本机环境。

这个答案是错误的。没有“不同的UIAutomationCore.dll”。在您的磁盘上搜索“UIAutomationCore.dll”。在Win 7中,我发现一个在Windows\System32(或SysWOW64)目录下,另一个在WinSXS文件夹中,但它们具有相同的版本并且是二进制相同的。 - Elmue
正确答案是UI自动化框架充满了严重的错误。其中一个错误是有时返回子项,有时不返回。我认为自动化缓存是错误的原因。如果我第一次搜索子项,它们被找到,当我第二次搜索时,只有第一个子项被返回。 - Elmue
@Elmue,我也遇到了同样的问题。在我的系统中,我能够获取所有元素,但是在客户端系统中,我无法获取子元素。解决这个问题的方法是什么? - Ravi Kanth
@Elmue,你是对的,我想我想说的是UIAutomationClient.dll(我编辑了我的帖子)。仔细检查后,在VS文件夹程序集下有一个Interop.UIAutomationClient.dll,另一个在系统下的UIAutomationClient.dll。 - AlbusMPiroglu
@Ravi,你可以尝试在每个使用的环境中创建你的interop.uiautomationclient.dll吗?这里有一个来自Guy Barker的例子:https://blogs.msdn.microsoft.com/winuiautomation/2016/07/12/building-and-running-a-uia-sample-app-on-windows-10/ - AlbusMPiroglu
你可以在我的帖子中找到解决问题的方法:https://dev59.com/ZZ7ha4cB1Zd3GeqPimbU - Elmue

1
“托管UI自动化”和“非托管UI自动化”的区别在于,托管UI自动化使用旧的实现方式,而Inspect直接使用COM,这是新版本3.0。”

1

我的原始示例已经简化。我尝试使用以下三种技术访问子级:

  1. .Net托管代码中的RawViewWalker。
  2. COM中等效的Walker,即在.Net托管代码中可用的COM包装器。
  3. 我编写的完全独立的VB6应用程序中的非.Net代码(即非托管代码)。

只有VB6(非托管)代码与Microsoft的Inspect工具给出了相同的结果。我认为这证实了其他人所说的:Microsoft在.Net中的UI自动化实现存在严重问题。也许唯一的解决方案是在.Net中编写自定义UI自动化客户端,但这假定目标应用程序中的UI自动化服务器行为正确。而那些超出我的控制范围,因为目标应用程序是由其他公司编写的,而不是我的公司。


是的,你说得对。甚至还有.NET的现成COM实现:https://uiacomwrapper.codeplex.com/ - Vizor

0

要使用FindFirst查找子元素,目标元素必须设置IsControlElement属性。

在原帖的截图中,IsControlElement被报告为[不支持]

换句话说,FindFirst搜索的是控件树,而不是原始树。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接