1

我们使用 AutomationUIClient 控制台应用程序来测试我们的 WPF 应用程序。

我们在 WPF 应用程序中设置了一个自定义类 TreeItemAutomationPeer 与 ISelectionProvider 和 IExpandCollapseProvider

如果此对象由自动化应用程序控制台使用,则所有者(在我的情况下为 TreeItem)被保留,因此它会泄漏......

我们添加最后一个方法 GetChildrenCore() 以防止来自 Children 的内存泄漏。

    public class TreeItemAutomationPeer : FrameworkElementAutomationPeer, ISelectionItemProvider, IExpandCollapseProvider
{
    private readonly TreeItem _treeItem;

    public TreeItemAutomationPeer(TreeItem treeItem)
        : base(treeItem)
    {
        _treeItem = treeItem;
    }

    public override object GetPattern(PatternInterface patternInterface)
    {
        if (patternInterface == PatternInterface.SelectionItem ||
            patternInterface == PatternInterface.ExpandCollapse)
            return this;
        return base.GetPattern(patternInterface);
    }

    protected override string GetClassNameCore()
    {
        return "TreeItem";
    }

    protected override AutomationControlType GetAutomationControlTypeCore()
    {
        //return AutomationControlType.Tree;
        return AutomationControlType.Custom;
    }

    #region ISelectionItemProvider
    public IRawElementProviderSimple SelectionContainer
    {
        get { return _treeItem.SelectionContainer; }
    }
    public bool IsSelected { get { return _treeItem.Item.IsSelected; } }
    public void AddToSelection()
    {
        _treeItem.Item.IsSelected = true;
        ItemHelper.SelectItem(_treeItem, _treeItem.Item);

    }
    public void RemoveFromSelection()
    {
        _treeItem.Item.IsSelected = false;
    }
    public void Select()
    {
        if (_treeItem.Item.IsSelected)
            RemoveFromSelection();
        else AddToSelection();
    }
    #endregion

    #region IExpandCollapseProvider

    public ExpandCollapseState ExpandCollapseState
    {
        get
        {
            return _treeItem.Item.IsExpanded
                ? ExpandCollapseState.Expanded
                : ExpandCollapseState.Collapsed;
        }
    }

    public void Expand()
    {
        _treeItem.Item.IsExpanded = true;
    }

    public void Collapse()
    {
        _treeItem.Item.IsExpanded = false;
    }

    #endregion
    protected override List<AutomationPeer> GetChildrenCore()
    {
        return null;
    }

}

TreeItem 类实现 IRawElementProviderSimple

        #region Automation

    private TreeItemAutomationPeer _itemAutomationPeer;
    protected override AutomationPeer OnCreateAutomationPeer()
    {
        _itemAutomationPeer = new TreeItemAutomationPeer(this);
        return _itemAutomationPeer;
    }

    public IRawElementProviderSimple SelectionContainer
    {
        get { return _container; }
    }

    #endregion

    #region IRawElementProviderSimple

    protected IntPtr GetWindowHandle() { return IntPtr.Zero; }
    protected string GetName() { return Name; }
    protected void AddAutomationProperty(int propertyId, object value) { }
    public object GetPatternProvider(int patternId) { return null; }
    public object GetPropertyValue(int propertyId)
    {
        return propertyId == AutomationElementIdentifiers.NameProperty.Id ? GetName() : null;
    }
    public IRawElementProviderSimple HostRawElementProvider { get { return null; } }
    public ProviderOptions ProviderOptions
    {
        get { return ProviderOptions.ServerSideProvider; }
    }

    #endregion

这是 DotMemory 的泄漏:

DotMemory 泄漏

当我拍摄快照时,控制台应用程序仍附加到 WPF 应用程序。

如何从 ExpandCollapseProviderWrapper 释放 TreeItemAutomationPeer 的所有者以防止泄漏?

在控制台应用程序中,我们获得了 AutomationElement 对象。是否有我们使用的所有 AutomationElement 的列表以及释放它们的方法?

非常感谢 :)

虽然真实

编辑 :

根据 dotmemory 文档 ( https://www.jetbrains.com/help/dotmemory/Analyzing_GC_Roots.html ) RefCounted 句柄是:

如果对象的引用计数是某个值,则根会阻止垃圾收集。如果使用 COM Interop 将对象传递给 COM 库,CLR 会为此对象创建一个 RefCounted 句柄。由于 COM 无法执行垃圾回收,因此需要此根。相反,它使用引用计数。如果不再需要该对象,COM 将计数设置为 0。这意味着 RefCounted 句柄不再是根,可以收集该对象。因此,如果您看到 RefCounted 句柄,那么很可能该对象作为参数传递给非托管代码。

4

1 回答 1

0

我找到了解决方法!:)

在控制台应用程序中,我使用 System.Diagnostics.Process.Start() 来启动我的应用程序。

由于 COM 持有 TreeItemAutomationPeer,我试图从进程中“分离”,调用 GC 并重新附加到进程。

 var processId = process.Id;
        process = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        process = System.Diagnostics.Process.GetProcessById(processId);

在此之后,我的 dotMemory 快照中不再有泄漏 \o/

于 2018-06-01T13:23:58.700 回答