【问题标题】:Memory leak with custom FrameWorkElementAutomationPeer with IExpandCollapseProvider in C#C# 中带有 IExpandCollapseProvider 的自定义 FrameWorkElementAutomationPeer 的内存泄漏
【发布时间】:2018-06-01 10:27:10
【问题描述】:

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

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

如果这个对象被自动化应用程序控制台使用,所有者(在我的例子中是 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 的泄漏:

当我拍摄快照时,控制台应用程序仍然附加到 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 句柄,则很可能该对象作为参数传递给非托管代码。

【问题讨论】:

    标签: c# memory-leaks ui-automation automationelement automationpeer


    【解决方案1】:

    我找到了解决方法! :)

    在控制台应用程序中,我使用 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/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-24
      • 2018-07-15
      • 1970-01-01
      • 2016-05-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多