结论
前几篇文章在这里:
- .NET C# AutomationElement 获取桌面UI元素
- .NET C# 手撕线程池 | 县城撕裂者
- .NET C# AutomationElement 加速探索
- .NET C# UIAutomationElement 获取桌面元素
- .NET C# 通过Win32API获取窗口及控件的信息
经过几天的研究初步获得了如下的结论:
Win32API
不靠谱- 和
Snipaste
开发大佬交流后得知的MSAA方法有局限,对于Text
属性可能不能读取(这个功能对我来说至关重要,替代品为UIAutomation
)
先说测试后的结论:
- 竟然是
System.Windows.Automation
最高效 - 手写的剪枝
DFS
比微软封装的方法高效 - 如果测试的时候用户在操作其他UI或有UI变化会导致速度变慢
- 多线程优化几乎没有效果
猜测:
- 瓶颈就是这个需求本身
- 速度取决于Server端,和如何实现无关
测试
测试范围:
System.Windows.Automation
- 手写带有剪枝的
DFS
- 线程池优化的剪枝
DFS
- 使用
Walker
写DFS
- 直接使用
FindAll(TreeScope.Descendants)
- 手写带有剪枝的
UIAutomationClient (COM)
- 手写带有剪枝的
DFS
- 线程池优化的剪枝
DFS
- 使用
Walker
写DFS
- 直接使用
FindAll(TreeScope.Descendants)
- 手写带有剪枝的
测试代码:
// IUIAutomation
IUIAutomationElement element = GIUIAutomationManager.GetWindowByProcessID(15204);
{
StreamWriter file = new StreamWriter("iui.out", append: true);
for (int j = 0; j < 100; j++)
{
DateTime ts_start = DateTime.UtcNow;
List<IUIAutomationElement> res = GIUIAutomationManager.GetLeafElementsDFS(element);
DateTime ts_end = DateTime.UtcNow;
Debug.WriteLine("iui, epoch: " + (j + 1));
Debug.WriteLine((ts_end - ts_start).TotalSeconds.ToString() + " " + res.Count);
await file.WriteLineAsync((ts_end - ts_start).TotalSeconds.ToString() + " " + res.Count);
}
}
for (int i = 1; i <= 24; i ++)
{
StreamWriter file = new StreamWriter("iui_multi_" + i + ".out", append: true);
GThreadPool<ThreadStart> threadPool = new GThreadPool<ThreadStart>(i, new Action<ThreadStart>((threadStart) =>
{
threadStart.Invoke();
}));
for (int j = 0; j < 100; j++)
{
DateTime ts_start = DateTime.UtcNow;
ConcurrentQueue<IUIAutomationElement> res = GIUIAutomationManager.GetLeafElementsByParallel(element, threadPool);
DateTime ts_end = DateTime.UtcNow;
Debug.WriteLine("iui, thread: " + i + ", epoch: " + (j + 1));
Debug.WriteLine((ts_end - ts_start).TotalSeconds.ToString() + " " + res.Count);
await file.WriteLineAsync((ts_end - ts_start).TotalSeconds.ToString() + " " + res.Count);
}
}
// AutomationElement
AutomationElement a_element = GAutomationManager.GetWindowByProcessID(15204);
{
StreamWriter file = new StreamWriter("ae.out", append: true);
for (int j = 0; j < 100; j++)
{
DateTime ts_start = DateTime.UtcNow;
List<AutomationElement> res = GAutomationManager.GetLeafElements(a_element);
DateTime ts_end = DateTime.UtcNow;
Debug.WriteLine("ae, epoch: " + (j + 1));
Debug.WriteLine((ts_end - ts_start).TotalSeconds.ToString() + " " + res.Count);
await file.WriteLineAsync((ts_end - ts_start).TotalSeconds.ToString() + " " + res.Count);
}
}
for (int i = 1; i <= 24; i++)
{
StreamWriter file = new StreamWriter("ae_multi_" + i + ".out", append: true);
GThreadPool<ThreadStart> threadPool = new GThreadPool<ThreadStart>(i, new Action<ThreadStart>((threadStart) =>
{
threadStart.Invoke();
}));
for (int j = 0; j < 100; j++)
{
DateTime ts_start = DateTime.UtcNow;
ConcurrentQueue<AutomationElement> res = GAutomationManager.GetLeafElementsByParallel(a_element, threadPool);
DateTime ts_end = DateTime.UtcNow;
Debug.WriteLine("ae, thread: " + i + ", epoch: " + (j + 1));
Debug.WriteLine((ts_end - ts_start).TotalSeconds.ToString() + " " + res.Count);
await file.WriteLineAsync((ts_end - ts_start).TotalSeconds.ToString() + " " + res.Count);
}
}
MessageBox.Show("Done!");