我们一直在使用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()调用没有什么特别之处。
//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机器规格:
我们尝试通过com包装使用非托管MS,但是在Win10上没有明显的性能改进。
如有任何建议,将不胜感激。
发布于 2015-10-14 12:27:01
这似乎已在最新一轮的Windows10更新(kb/3093266)中得到修正。据与我交谈的一名会员国支助代表说:
"UIA经常调用NtQuerySystemInformation,经常调用该API的性能并不令人满意。他们对特定的代码路径进行了更改,不再调用该API,从而提高了总体性能。“
不幸的是,这是他们所掌握的所有信息,所以我无法确切地确定是什么导致了这个问题。
经过更新和测试,两台机器的性能是相同的。
发布于 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的直接子级,可以使用:
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);
}
发布于 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的父元素,我就可以使用一个跨进程调用来访问我所需要的所有数据。考虑到跨进程调用的速度很慢,我希望尽可能少调用。
谢谢,
盖伊
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...
}
}
}
}
}
}
}
}
https://stackoverflow.com/questions/32970247
复制相似问题