System.Windows.Automation在枚举表行与UIAutomationCore时非常慢

时间:2015-02-05 20:46:00

标签: c# winforms ui-automation white microsoft-ui-automation

我正在尝试通过UI Automation自动测试我的应用程序(主要使用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自动化的性能?我看过白色的源代码,我没有看到任何明显的东西。我无法分析System.Windows.Automation本身,因为我找不到任何PDB。我对UI自动化不是很熟悉所以也许对其他人来说很明显。白色是:0.13.0.0我正在64位Windows 7上进行测试。

2 个答案:

答案 0 :(得分:3)

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

对于那些来自Google并且在缓慢的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 Automation框架中不可用的模式和属性。

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

  • IUIAutomationLegacyIAccessiblePattern
  • IUIAutomationObjectModelPattern
  • IUIAutomationAnnotationPattern
  • IUIAutomationTextPattern2
  • IUIAutomationStylesPattern
  • IUIAutomationSpreadsheetPattern
  • IUIAutomationSpreadsheetItemPattern
  • IUIAutomationTransformPattern2
  • IUIAutomationTextChildPattern
  • IUIAutomationDragPattern
  • IUIAutomationDropTargetPattern
  • IUIAutomationTextEditPattern
  • IUIAutomationCustomNavigationPattern

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

  • AppBar
  • SemanticZoom

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

  • IUIAutomationElement2
  • IUIAutomationElement3
  • IUIAutomationElement4

答案 1 :(得分:0)

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