结论

前几篇文章在这里:

  1. .NET C# AutomationElement 获取桌面UI元素
  2. .NET C# 手撕线程池 | 县城撕裂者
  3. .NET C# AutomationElement 加速探索
  4. .NET C# UIAutomationElement 获取桌面元素
  5. .NET C# 通过Win32API获取窗口及控件的信息

经过几天的研究初步获得了如下的结论:

  1. Win32API不靠谱
  2. Snipaste开发大佬交流后得知的MSAA方法有局限,对于Text属性可能不能读取(这个功能对我来说至关重要,替代品为UIAutomation

先说测试后的结论:

  1. 竟然是System.Windows.Automation最高效
  2. 手写的剪枝DFS比微软封装的方法高效
  3. 如果测试的时候用户在操作其他UI或有UI变化会导致速度变慢
  4. 多线程优化几乎没有效果

猜测:

  1. 瓶颈就是这个需求本身
  2. 速度取决于Server端,和如何实现无关

测试

测试范围:

  • System.Windows.Automation
    • 手写带有剪枝的DFS
    • 线程池优化的剪枝DFS
    • 使用WalkerDFS
    • 直接使用FindAll(TreeScope.Descendants)
  • UIAutomationClient (COM)
    • 手写带有剪枝的DFS
    • 线程池优化的剪枝DFS
    • 使用WalkerDFS
    • 直接使用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!");
最后修改:2021 年 08 月 02 日 01 : 26 PM
真的不买杯奶茶嘛....qwq