I recently downloaded a copy of a new tool from red-gage called Ant Memory Profiler. Ant Memory Profiler is an application that allows you to do memory usage analysis on dot net apps to uncover how your app consumes memory.
During the course of trying out this tool we uncovered an interesting memory leak in the dot net framework ToolStrip control. Our symptom of this was that one of our application forms was being held in memory long after it should have been garbage collected. After a quick tour through google I found a some references on why this is occurring and how to ensure things get cleaned up properly.
Here’s how we add the cleanup code to the close event of the form…
Protected Overrides Sub OnClosed(ByVal e As System.EventArgs)
MyBase.OnClosed(e)
'note, this call is still required as it cleans up one set of extra handlers
reportViewer.Toolbar.Visible = False
'this is the really ugly stuff though, let our cleanup class take care of things
Dim gc2 As New ToolStripGarbageCollector
gc2.RemoveHandlers(reportViewer)
End Sub
And heres the meat behind the cleanup class. Don’t ask what this does, it’s pretty much a copy and paste I found from some newsgroups.
Public Class ToolStripGarbageCollector
Private Const EVENTHANDLER_ON_USER_PREFERENCE_CHANGED As String = "OnUserPreferenceChanged"
Private Const LIST_HANDLERS As String = "_handlers"
Private Const ON_USER_PREFERENCE_CHANGED_EVENT As String = "OnUserPreferenceChangedEvent"
Private Const SYSTEM_EVENT_INVOKE_INFO As String = "SystemEventInvokeInfo"
Private Const TARGET_DELEGATE As String = "_delegate"
Public Sub RemoveHandlers(ByVal ctrl As Control)
NavigateControls(ctrl)
Public Sub NavigateControls(ByVal ctrl As Control)
Console.WriteLine(ctrl.Name & "-" & ctrl.GetType.FullName)
If ctrl.GetType.FullName = "System.Windows.Forms.ToolStripTextBox+ToolStripTextBoxControl" Then
RemoveUpcHandler(ctrl)
ElseIf ctrl.GetType.FullName = "System.Windows.Forms.ToolStrip" Then
End If
For Each c As Control In ctrl.Controls
NavigateControls(c)
Next
Private Sub RemoveUpcHandler(ByVal ctrl As Control)
'create the delegate to OnUserPreferenceChanged
Dim d As [Delegate] = [Delegate].CreateDelegate(GetType(Microsoft.Win32.UserPreferenceChangedEventHandler), ctrl, EVENTHANDLER_ON_USER_PREFERENCE_CHANGED)
Dim handlers As Object = GetType(Microsoft.Win32.SystemEvents).GetField("_handlers", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static).GetValue(Nothing)
Dim upcHandler As Object = GetType(Microsoft.Win32.SystemEvents).GetField(ON_USER_PREFERENCE_CHANGED_EVENT, Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static).GetValue(Nothing)
'get a SystemEventInvokeInfo type
Dim systemEventInvokeInfo As Object = GetType(Microsoft.Win32.SystemEvents).GetNestedType(SYSTEM_EVENT_INVOKE_INFO, Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
'get the SystemEventInvokeInfo list for the UserPreferenceChangedEvent
Dim upcHandlerList As IList = CType(CType(handlers, IDictionary).Item(upcHandler), IList)
'initialize a target count
Dim targetCount As Integer = 0
Dim i As Integer = 0
'loop
While i < upcHandlerList.Count
systemEventInvokeInfo = upcHandlerList(i)
'get the SystemEventInvokeInfo._delegate field
Dim target As [Delegate] = CType(systemEventInvokeInfo.GetType().GetField(TARGET_DELEGATE, Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance).GetValue(systemEventInvokeInfo), [Delegate])
'eval
If target.Target Is d.Target Then
'increment on positive ID
targetCount += 1
i += 1
End While
'remove the handlers
For i = 1 To targetCount
RemoveHandler Microsoft.Win32.SystemEvents.UserPreferenceChanged, CType(d, Microsoft.Win32.UserPreferenceChangedEventHandler)
End Class
dasBlog theme by Mads Kristensen
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.