Question

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).

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top