Reputation: 3009
Developers know that WinForms ToolStrip control usage may cause managed memory leaks if we do not force it to release some stuff manually. I mean the internal event handler of the system static Microsoft.Win32.SystemEvents.UserPreferenceChanged
event. To release the resources properly, we need an explicit call of the ToolStrip Dispose method as it is described, for instance, in this or this SO posts.
However, this does not help if we use a ToolStrip from a descendant of System.ComponentModel.Component - at least, in my case. Here is the corresponding part of code:
Private Class DropDownFilterBox
Inherits System.ComponentModel.Component
Private WithEvents fToolStripMain As New AutoFilterToolStrip
Private WithEvents fToolStripOKCancel As New AutoFilterToolStrip
Private WithEvents fContextMenuStripCustomFilterOperators As New ContextMenuStrip
Private WithEvents fToolStripDropDownCustomFilterDatePicker As New ToolStripDropDown
Private WithEvents fToolStripControlHostCustomFilterDatePicker As New AutoFilterToolStripControlHostDatePicker
.......................
Public Overloads Sub Dispose()
fToolStripMain.Dispose()
fToolStripOKCancel.Dispose()
fContextMenuStripCustomFilterOperators.Dispose()
fToolStripDropDownCustomFilterDatePicker.Dispose()
fToolStripControlHostCustomFilterDatePicker.Dispose()
MyBase.Dispose()
End Sub
End Class
The AutoFilterToolStrip is defined like this:
Private Class AutoFilterToolStrip
Inherits ToolStrip
......................
End Class
, but this should not matter in our context.
I even call DropDownFilterBox.Dispose manually to clean up the used resources when it is needed, but it seems this does not have any effect.
Some developers recommend hiding ToolStrips (set the Visible property to False) as the UserPreferenceChanged event handler should be removed by ToolStrip automatically in this case. Yes, the internal HookStaticEvents method which should do the work is called at that. but this also does not help.
I even tried to detach the problem event handler manually using reflection:
RemoveHandler Microsoft.Win32.SystemEvents.UserPreferenceChanged, _
DirectCast( _
System.Delegate.CreateDelegate(GetType(Microsoft.Win32.UserPreferenceChangedEventHandler), fToolStripMain, "OnUserPreferenceChanged"), _
Microsoft.Win32.UserPreferenceChangedEventHandler _
)
, but this also does not have any effect.
Any ideas on how to overcome this memory leak problem in our case and why the explicit call of ToolStrip.Dispose may not work in our case?
We develop in VB.NET 2010 for .NET Framework 4+ (client profile).
Upvotes: 0
Views: 1052
Reputation: 3009
The missing part of my code was the following:
Dim myOverflowButton As ToolStripOverflow
myOverflowButton = DirectCast(fToolStripMain.OverflowButton.DropDown, ToolStripOverflow)
If (myOverflowButton IsNot Nothing) Then
myOverflowButton.Dispose()
End If
myOverflowButton = DirectCast(fToolStripOKCancel.OverflowButton.DropDown, ToolStripOverflow)
If (myOverflowButton IsNot Nothing) Then
myOverflowButton.Dispose()
End If
ToolStrip creates the so called overflow button automatically, and it also subscribes to the UserPreferenceChanged event which may cause memory leaks! Some more info about this can be found on SO here: ToolStrip memory leak.
Now the full listing of my component's Dispose method looks like this:
Public Overloads Sub Dispose()
With fContainer.Controls
.Remove(fToolStripMain)
.Remove(fToolStripOKCancel)
End With
fToolStripMain.Visible = False
fToolStripOKCancel.Visible = False
fContextMenuStripCustomFilterOperators.Visible = False
fToolStripDropDownCustomFilterDatePicker.Visible = False
fToolStripControlHostCustomFilterDatePicker.Visible = False
fToolStripMain.Dispose()
fToolStripOKCancel.Dispose()
fContextMenuStripCustomFilterOperators.Dispose()
fToolStripDropDownCustomFilterDatePicker.Dispose()
fToolStripControlHostCustomFilterDatePicker.Dispose()
Dim myOverflowButton As ToolStripOverflow
myOverflowButton = DirectCast(fToolStripMain.OverflowButton.DropDown, ToolStripOverflow)
If (myOverflowButton IsNot Nothing) Then
myOverflowButton.Dispose()
End If
myOverflowButton = DirectCast(fToolStripOKCancel.OverflowButton.DropDown, ToolStripOverflow)
If (myOverflowButton IsNot Nothing) Then
myOverflowButton.Dispose()
End If
' Dispose calls for other used components
MyBase.Dispose()
End Sub
I also should admit that the technique of finding memory leaks in managed code, described in the CodeProject article Memory Leak Detection in .NET, helped me a lot in finding the solution - BTW, spending no penny for buying commercial memory profilers.
Upvotes: 1