首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >AutomationElement.FindAll()性能问题

AutomationElement.FindAll()性能问题
EN

Stack Overflow用户
提问于 2015-10-06 12:41:25
回答 4查看 6.9K关注 0票数 6

我们一直在使用MS框架,并注意到在Windows10/ .NET 4.6中查找对象集合时似乎出现了明显的减速。

在Windows10/AutomationElement.FindAll 4.6框上测试.NET ()时,我们发现元素集合的时间平均是在Windows8.1/ .NET 4.5.1框中找到完全相同的元素时的3到5倍。我的测试是针对具有虚拟化的WPF DataGrid (使用回收)和获取DataGrid每一行中的所有单元格。

在我们的Win10框中,获得每行单元格的每个FindAll调用大约需要30-50 30,甚至更长。在Win8.1盒上,它大约需要5-10毫秒。我不知道为什么,但我不认为这个问题仅限于DataGrid,因为我们的FindAll()调用没有什么特别之处。

代码语言:javascript
运行
复制
        //get grid
    AutomationElement gridElement = AutomationElement.RootElement.FindFirst(TreeScope.Descendants,
           new PropertyCondition(AutomationElement.AutomationIdProperty, "dataGridAutomationId"));

    //get all visible rows
    AutomationElementCollection dataItems = gridElement.FindAll(TreeScope.Descendants,
                       new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.DataItem));

 foreach (AutomationElement dataItem in dataItems)
 {
    if (!string.IsNullOrEmpty(dataItem.Current.Name))
    {
        //call under test
        AutomationElementCollection cells = dataItem.FindAll(TreeScope.Children,
             new PropertyCondition(AutomationElement.ClassNameProperty, "DataGridCell"));
    }
 }

AutomationElement.RootElement的使用仅用于测试。“dataItem.FindAll()”调用是我正在测试的。

Win 8.1和Win10机器规格:

  • Xeon W3670 3.20GHzCPU
  • 12 ram内存
  • 64位操作系统

我们尝试通过com包装使用非托管MS,但是在Win10上没有明显的性能改进。

如有任何建议,将不胜感激。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2015-10-14 12:27:01

这似乎已在最新一轮的Windows10更新(kb/3093266)中得到修正。据与我交谈的一名会员国支助代表说:

"UIA经常调用NtQuerySystemInformation,经常调用该API的性能并不令人满意。他们对特定的代码路径进行了更改,不再调用该API,从而提高了总体性能。“

不幸的是,这是他们所掌握的所有信息,所以我无法确切地确定是什么导致了这个问题。

经过更新和测试,两台机器的性能是相同的。

票数 4
EN

Stack Overflow用户

发布于 2015-10-09 12:25:44

我建议尝试一种与FindAll()不同的方法,例如使用TreeWalker。UIAutomation实现倾向于在不同的OS版本之间进行更改(至少我觉得是这样的)(如果您想检查版本,则将它们编译为UIAutomationCore.dll )。请注意,您可以检查最新.NET框架的托管代码。TreeWalker:http://referencesource.microsoft.com/#UIAutomationClient/System/Windows/Automation/TreeWalker.cs的示例链接

例如,要检查TreeWalker的直接子级,可以使用:

代码语言:javascript
运行
复制
var walker = TreeWalker.RawViewWalker;
var current = walker.GetFirstChild(/* a parent automation element here*/);
while (current != null)
{
  // use current (an automationelement) here, than go to next:
  current = walker.GetNextSibling(current);
}
票数 1
EN

Stack Overflow用户

发布于 2015-10-13 03:46:23

这可能与您的特定情况无关,但有时托管.NET UIA与本地Windows在性能特性上可能存在差异。因此,如果实际可行的话,您可能需要考虑通过使用Windows来查看您正在与之交互的UI是否获得了更好的结果。

作为一个测试,我刚刚创建了一个应用程序,它提供了一个DataGrid,其中包含25行,每行有10个单元格。然后,我编写了下面的UIA客户端代码,以访问通过UIA公开的每个单元的名称。(关于如何调用本地Windows的C#代码的一些说明是在http://blogs.msdn.com/b/winuiautomation/archive/2015/09/30/so-how-will-you-help-people-work-with-text-part-2-the-uia-client.aspx。)

我认为测试代码真正有趣的一点是,一旦我拥有了DataItems的父元素,我就可以使用一个跨进程调用来访问我所需要的所有数据。考虑到跨进程调用的速度很慢,我希望尽可能少调用。

谢谢,

盖伊

代码语言:javascript
运行
复制
IUIAutomationElement rootElement = uiAutomation.GetRootElement();

// The first few steps below find a DataGridRowsPresenter for the 
// DataGrid we're interested in.
IUIAutomationElement dataGridRowsPresenter = null;

// We'll be setting up various UIA conditions and cache requests below.
int propertyIdControlType = 30003; // UIA_ControlTypePropertyId
int propertyIdName = 30005; // UIA_NamePropertyId
int propertyIdAutomationId = 30011; // UIA_AutomationIdPropertyId
int propertyIdClassName = 30012; // UIA_ClassNamePropertyId
int controlTypeIdDataItem = 50029; // UIA_DataItemControlTypeId

// Look for the test app presenting the DataGrid. For this test, assume there's
// only one such UIA element that'll be found, and the current language doesn't
// effect any of the searches below.
string testAppName = "Window1";

IUIAutomationCondition conditionTestAppName =
    uiAutomation.CreatePropertyCondition(
        propertyIdName, testAppName);

IUIAutomationElement testAppElement =
    rootElement.FindFirst(
        TreeScope.TreeScope_Children,
        conditionTestAppName);

// Did we find the test app?
if (testAppElement != null)
{
    // Next find the DataGrid. By looking at the UI with the Inspect SDK tool first,
    // we can know exactly how the UIA hierarchy and properties are being exposed.
    string dataGridAutomationId = "DataGrid_Standard";

    IUIAutomationCondition conditionDataGridClassName =
        uiAutomation.CreatePropertyCondition(
            propertyIdAutomationId, dataGridAutomationId);

    IUIAutomationElement dataGridElement =
        testAppElement.FindFirst(
            TreeScope.TreeScope_Children,
            conditionDataGridClassName);

    // Did we find the DataGrid?
    if (dataGridElement != null)
    {
        // We could simply look for all DataItems that are descendents of the DataGrid.
        // But we know exactly where the DataItems are, so get the element that's the 
        // parent of the DataItems. This means we can then get that element's children,
        // and not ask UIA to search the whole descendent tree.
        string dataGridRowsPresenterAutomationId = "PART_RowsPresenter";

        IUIAutomationCondition conditionDataGridRowsPresenter =
            uiAutomation.CreatePropertyCondition(
                propertyIdAutomationId, dataGridRowsPresenterAutomationId);

        dataGridRowsPresenter =
            dataGridElement.FindFirst(
                TreeScope.TreeScope_Children,
                conditionDataGridRowsPresenter);
    }
}

// Ok, did we find the element that's the parent of the DataItems?
if (dataGridRowsPresenter != null)
{
    // Making cross-proc calls is slow, so try to reduce the number of cross-proc calls we 
    // make. In this test, we can find all the data we need in a single cross-proc call below.

    // Create a condition to find elements whose control type is DataItem.
    IUIAutomationCondition conditionRowsControlType =
        uiAutomation.CreatePropertyCondition(
            propertyIdControlType, controlTypeIdDataItem);

    // Now say that all elements returned from the search should have their Names and
    // ClassNames cached with them. This means that when we access the Name and ClassName
    // properties later, we won't be making any cross-proc call at that time.
    IUIAutomationCacheRequest cacheRequestDataItemName = uiAutomation.CreateCacheRequest();
    cacheRequestDataItemName.AddProperty(propertyIdName);
    cacheRequestDataItemName.AddProperty(propertyIdClassName);

    // Say that we also want data from the children of the elements found to be cached 
    // beneath the call to find the DataItem elements. This means we can access the Names 
    // and ClassNames of all the DataItems' children, without making more cross-proc calls.
    cacheRequestDataItemName.TreeScope =
        TreeScope.TreeScope_Element | TreeScope.TreeScope_Children;

    // For this test, say that we don't need a live reference to the DataItems after we've 
    // done the search. This is ok here, because the cached data is all we need. It means
    // that we can't later get current data (ie not cached) from the DataItems returned.
    cacheRequestDataItemName.AutomationElementMode =
        AutomationElementMode.AutomationElementMode_None;

    // Now get all the data we need, in a single cross-proc call.
    IUIAutomationElementArray dataItems = dataGridRowsPresenter.FindAllBuildCache(
        TreeScope.TreeScope_Children,
        conditionRowsControlType,
        cacheRequestDataItemName);

    if (dataItems != null)
    {
        // For each DataItem found...
        for (int idxDataItem = 0; idxDataItem < dataItems.Length; idxDataItem++)
        {
            IUIAutomationElement dataItem = dataItems.GetElement(idxDataItem);

            // This test is only interested in DataItems with a Name.
            string dataItemName = dataItem.CachedName;
            if (!string.IsNullOrEmpty(dataItemName))
            {
                // Get all the direct children of the DataItem, that were cached 
                // during the search.
                IUIAutomationElementArray elementArrayChildren = 
                    dataItem.GetCachedChildren();
                if (elementArrayChildren != null)
                {
                    int cChildren = elementArrayChildren.Length;

                    // For each child of the DataItem...
                    for (int idxChild = 0; idxChild < cChildren; ++idxChild)
                    {
                        IUIAutomationElement elementChild =
                            elementArrayChildren.GetElement(idxChild);
                        if (elementChild != null)
                        {
                            // This test is only interested in the cells.
                            if (elementChild.CachedClassName == "DataGridCell")
                            {
                                string cellName = elementChild.CachedName;

                                // Do something useful with the cell name now...
                            }
                        }
                    }
                }
            }
        }
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32970247

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档