7

我正在尝试通过 UI 自动化对我的应用程序进行自动化测试(主要TestStack.White用于提供友好的界面;它System.Windows.Automation用作后端)。我有一个大约 200 行的表,我需要测试它的值(实际上我只想测试第一行和最后几行)。我发现单独使用 COM-interop UIAutomationCore,我可以在几分之一秒内枚举行,但前提是我不使用 White 或System.Windows.Automation. 一旦System.Windows.Automation初始化,未来枚举行的 UI 自动化操作就会很慢:

First COM run: it took 0.04 seconds to get 102 rows!
First System.Windows.Automation run: it took 7.18 seconds to get 102 rows!
Second COM run: it took 7.87 seconds to get 102 rows!

我创建了一个简单的 WinForms 测试应用程序(TableTest.exe以验证它是否System.Windows.Automation与我的应用程序无关:

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    var form = new Form() { Text = "TableTest", WindowState = FormWindowState.Maximized };
    var dgv = new DataGridView() { Name = "DGV", Dock = DockStyle.Fill, AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill };
    dgv.Columns.Add("i", "i");
    dgv.Columns.Add("2i", "2i");
    dgv.Columns.Add("i^2", "i^2");
    dgv.Columns.Add("i^i", "i^i");
    for (int i = 0; i < 100; ++i)
        dgv.Rows.Add(i, i * 2, i * i, Math.Pow(i, i));
    form.Controls.Add(dgv);

    Application.Run(form);
}

然后我创建了另一个测试应用程序来测试第一个。它可以用作控制台应用程序或 WinForms 应用程序。首先我使用 COM 自动化进行测试,然后使用 System.Windows.Automation 进行测试,然后再次使用 COM 自动化进行测试。从我上面引用的输出中可以看出,第一个块执行得非常快,接下来的两个块执行得非常慢。如果我注释掉System.Windows.Automation块代码,那么两个 COM 块都会快速执行。

using UIA = Interop.UIAutomationCore;
static void Main(string[] args)
{
    var process = System.Diagnostics.Process.Start("TableTest.exe");
    System.Threading.Thread.Sleep(500);
    var uia = new UIA.CUIAutomation();
    var rootCom = uia.GetRootElement();
    var windowCom = rootCom.FindFirst(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_NamePropertyId, "TableTest"));
    var dgvCom = windowCom.FindFirst(UIA.TreeScope.TreeScope_Descendants, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_AutomationIdPropertyId, "DGV"));
    var start = DateTime.Now;
    var rowCount = dgvCom.FindAll(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_ControlTypePropertyId, UIA.UIA_ControlTypeIds.UIA_CustomControlTypeId)).Length;
    var elapsed = (DateTime.Now - start).TotalSeconds;
    Console.WriteLine(String.Format("It took {0} seconds to get {1} rows!", elapsed.ToString("f2"), rowCount));
    process.Kill();

    process = System.Diagnostics.Process.Start("TableTest.exe");
    System.Threading.Thread.Sleep(500);
    var root = AutomationElement.RootElement;
    var window = root.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "TableTest"));
    var dgv = window.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, "DGV"));
    start = DateTime.Now;
    rowCount = dgv.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom)).Count;
        elapsed = (DateTime.Now - start).TotalSeconds;
    Console.WriteLine(String.Format("It took {0} seconds to get {1} rows!", elapsed.ToString("f2"), rowCount));
        process.Kill();

    process = System.Diagnostics.Process.Start("TableTest.exe");
    System.Threading.Thread.Sleep(500);
    uia = new UIA.CUIAutomation();
    rootCom = uia.GetRootElement();
    windowCom = rootCom.FindFirst(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_NamePropertyId, "TableTest"));
    dgvCom = windowCom.FindFirst(UIA.TreeScope.TreeScope_Descendants, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_AutomationIdPropertyId, "DGV"));
    start = DateTime.Now;
    rowCount = dgvCom.FindAll(UIA.TreeScope.TreeScope_Children, uia.CreatePropertyCondition(UIA.UIA_PropertyIds.UIA_ControlTypePropertyId, UIA.UIA_ControlTypeIds.UIA_CustomControlTypeId)).Length;
    elapsed = (DateTime.Now - start).TotalSeconds;
    Console.WriteLine(String.Format("It took {0} seconds to get {1} rows!", elapsed.ToString("f2"), rowCount));
    process.Kill();
}

到底是System.Windows.Automation什么在扼杀 UI 自动化的性能?我查看了 White 源代码,但没有看到任何明显的东西。我无法分析 System.Windows.Automation 本身,因为我找不到任何 PDB。我对 UI 自动化不是很熟悉,所以也许对其他人来说很明显。白色是:0.13.0.0我正在 64 位 Windows 7 上进行测试。

4

2 回答 2

4

我无法回答你的问题。但是很多人会从谷歌来这里搜索关键词“uiautomation slow”,谷歌的第一个结果就是你的问题。(你写了一本畅销书)

对于所有来自谷歌并在缓慢的 UIAutomation 中苦苦挣扎的人,我发布了这个答案。

System.Windows.Automation非常慢。在非常快的计算机上获取 30 个子元素可能需要 1000 毫秒!在 QT 应用程序中获取 Tree 的子元素时,我什至看到它永远挂起。

除此之外,实现甚至不是线程安全的。

System.Windows.Automation 已弃用。不要使用它!

MSDN中,您可以找到以下注释:

UI 自动化首先在 Windows XP 中作为 Microsoft .NET Framework 的一部分提供。尽管当时还发布了非托管 C++ API,但由于互操作性问题,客户端函数的用处受到了限制。对于 Windows 7,API 已在组件对象模型 (COM) 中重写。尽管 UI 自动化的早期版本中引入的库函数仍被记录在案,但它们不应在新应用程序中使用。

性能下降的解决方案是使用新的IUIAutomationElement COM 接口而不是旧的 System.Windows.Automation C# 接口。之后代码将快速运行!

除此之外,新界面提供了更多的模式,微软正在不断地扩展它。在 Windows 10 SDK(UIAutomationClient.h 和 UIAutomationCore.h)中,添加了一些在 .NET 自动化框架中不可用的模式和属性。

UIAutomation 的 COM 版本中提供了以下模式,这些模式在 System.Windows.Automation 中不存在:

  • IUIAutomationLegacyIAccessiblePattern
  • IUIAutomationObjectModelPattern
  • IUIAutomationAnnotationPattern
  • IUIAutomationTextPattern2
  • IUIAutomationStyles模式
  • IUI自动化电子表格模式
  • IUIAutomationSpreadsheetItemPattern
  • IUIAutomationTransformPattern2
  • IUIAutomationTextChildPattern
  • IUIAutomationDragPattern
  • IUIAutomationDropTargetPattern
  • IUIAutomationTextEditPattern
  • IUIAutomationCustomNavigationPattern

此外,还添加了以下控件类型:

  • 应用栏
  • 语义缩放

此外,还添加了以下元素:

  • IUIAutomationElement2
  • IUIAutomationElement3
  • IUIAutomationElement4
于 2017-01-20T16:05:08.407 回答
0

您发布的示例不使用 White ... FWIW,White 每次都使用对 Automation.Find 的递归调用来请求孩子。这会返回有效结果,但比从适当的父节点请求子树要慢 - 请注意,根节点永远不是从中请求子树的“适当”节点(请参阅MSDN上的注释)。由于您的示例只要求孩子一次,这不是问题。

于 2015-07-29T16:23:43.553 回答