Утечка памяти с помощью пользовательского FrameWorkElementAutomationPeer с IExpandCollapseProvider в C # - PullRequest
0 голосов
/ 01 июня 2018

Мы используем консольное приложение AutomationUIClient для тестирования нашего приложения WPF.

Мы установили в нашем приложении WPF пользовательский класс TreeItemAutomationPeer с ISelectionProvider ad IExpandCollapseProvider

Если этот объект используется консолью приложения автоматизации, владелец (TreeItem в моем случае) сохраняется, поэтому он утечки ...

Мы добавляем последний метод GetChildrenCore (), чтобы предотвратить утечки памяти от детей.

    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:

Leak in DotMemory

Когда я сделал снимок, консольное приложение все еще было подключенов приложение WPF.

Как можно освободить владельца TreeItemAutomationPeer от ExpandCollapseProviderWrapper для предотвращения утечки?

В консольном приложении мы получаем объект AutomationElement.Есть ли список всех элементов AutomationElement, которые мы использовали, и способ их выпуска?

Большое спасибо:)

Whiletrue

EDIT:

Согласнок документации точки памяти (https://www.jetbrains.com/help/dotmemory/Analyzing_GC_Roots.html) Дескриптор RefCounts:

Корень предотвращает сборку мусора, если счетчик ссылок объекта имеет определенное значение.Если объект передается в библиотеку COM с помощью COM Interop, CLR создает дескриптор RefCounting для этого объекта.Этот корень необходим, так как COM не может выполнить сборку мусора.Вместо этого он использует подсчет ссылок.Если объект больше не нужен, COM устанавливает счетчик равным 0. Это означает, что дескриптор RefCounts больше не является корнем, и объект можно собирать.Таким образом, если вы видите дескриптор RefCounting, то, вероятно, объект передается в качестве аргумента неуправляемому коду.

1 Ответ

0 голосов
/ 01 июня 2018

Я нашел обходной путь!:)

В консольном приложении я использую System.Piagnostics.Process.Start () для запуска моего приложения.

Поскольку TreeItemAutomationPeer удерживался COM, я попытался «отсоединиться» отпроцесс, вызовите GC и подключите к процессу.

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

После этого больше не будет утечки в моем снимке dotMemory \ o /

...