这个东西其实是去年9月研究的,不过当时没有记录,现在准备重新研究研究,所以特此写文章记录。项目地址:https://github.com/JeffersonQin/Ayase

至于为什么要用这个名字命名...我永远喜欢三司绫濑(零奶)!角色命名,从我做起。

好了,不说题外话了。AutomationElement是微软提供的用于软件自动化测试的接口,这里讲一下怎么用。

用法

获取根元素

AutomationElement.RootElement;

通过一个元素获取其他元素

注意,我们要掌握一个核心思想,那就是所有的UI元素构成一颗,所以有:Children (儿子), Parent (父亲), Descendent (后代), 等图论中常说的东西。

我们可以指定一个范围和条件,搜索其他元素,比如:

return element.FindFirst(TreeScope.Children,
    new PropertyCondition(AutomationElement.ProcessIdProperty, 54376));

这句话的意思就是找到element元素儿子节点ProcessID是54376的元素。

具体的用法可以查看官方文档:https://docs.microsoft.com/en-us/dotnet/api/system.windows.automation?view=net-5.0

里面详细讲述了如何获取各个属性

获取一个元素子树所有的终止节点

实现一个DFS即可,下面是我自己写的实现 (其中要求元素的都在屏幕的可见范围内,所以加了一个JudgeBounding):

/// <summary>
/// Get the elements that don't have children of an element
/// </summary>
/// <param name="element">Input element</param>
/// <returns></returns>
public static List<AutomationElement> GetLeafElements(AutomationElement element)
{
    List<AutomationElement> res = new List<AutomationElement>();
    try
    {
        Rect bounds = element.Current.BoundingRectangle;
        if (!JudgeBounding(bounds)) return res;
        GetLeafElementsDFS(element, res, bounds);
    }
    catch (Exception)
    {
        return res;
    }
    return res;
}


private static bool JudgeBounding(Rect bounds)
{
    try
    {
        if (bounds == null)
            return false;
        if (bounds.Width == 0 || bounds.Height == 0)
            return false;
        if (bounds.IsEmpty)
            return false;
        if (double.IsInfinity(bounds.Top) ||
            double.IsInfinity(bounds.Left) ||
            double.IsInfinity(bounds.Width) ||
            double.IsInfinity(bounds.Height))
            return false;
        return true;
    }
    catch (Exception)
    {
        return false;
    }
}


private static void GetLeafElementsDFS(AutomationElement element, List<AutomationElement> res, Rect bounds)
{
    AutomationElementCollection children = element.FindAll(TreeScope.Children,
        new PropertyCondition(AutomationElement.IsOffscreenProperty, false));
    foreach (AutomationElement e in children)
    {
        try
        {
            Rect rect = e.Current.BoundingRectangle;
            if (!JudgeBounding(rect)) continue;
            if (rect.Bottom <= bounds.Top || rect.Right <= bounds.Left) continue;
            if (rect.Top >= bounds.Bottom || rect.Left >= bounds.Right) continue;
            GetLeafElementsDFS(e, res, rect);
        } catch (Exception)
        {
            continue;
        }
    }
    if (children.Count == 0) res.Add(element);
}

获取元素的名称、边框等

Rect boundings = element.Current.BoundingRectangle;
String name = element.Current.Name;

AutomationElement的弊端

性能太他妈烂了。之后几篇文章会讲讲我对性能优化的探索。

最后修改:2021 年 11 月 30 日 03 : 53 PM
真的不买杯奶茶嘛....qwq