We use AutomationUIClient console application to test our WPF application.
We set up in our WPF application a custom class TreeItemAutomationPeer with ISelectionProvider ad IExpandCollapseProvider
If this object is use by automation application console, the Owner (TreeItem in my case) is kept so it leak...
We add the last method GetChildrenCore() to prevent memory leaks from 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;
}
}
The TreeItem class implement 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
Here is the leak from DotMemory :
When I took the snapshot, console application is still attached to the WPF application.
How can I release the TreeItemAutomationPeer's Owner from ExpandCollapseProviderWrapper to prevent the leak ?
In the console application, we get AutomationElement object. Is there a list of all the AutomationElement we used and a way to release them ?
Thanks a lot :)
Whiletrue
EDIT :
According to the dotmemory documentation ( https://www.jetbrains.com/help/dotmemory/Analyzing_GC_Roots.html ) RefCounted handle is :
The root prevents garbage collection if the reference count of the object is a certain value. If an object is passed to a COM library using COM Interop, CLR creates a RefCounted handle to this object. This root is needed as COM is unable to perform garbage collection. Instead, it uses reference counting. If the object is no longer needed, COM sets the count to 0. This means that RefCounted handle is no longer a root and the object can be collected. Thus, if you see RefCounted handle, then, probably, the object is passed as an argument to unmanaged code.
I found a workaround ! :)
In the console application, i use System.Diagnostics.Process.Start() to start my application.
As the TreeItemAutomationPeer was held by COM, i tried to "detatch" from process, call the GC and reattach to the process.
After this no more Leak in my dotMemory snapshot \o/