0

开发人员知道,如果我们不强制它手动释放一些东西,WinForms ToolStrip 控件的使用可能会导致托管内存泄漏。我的意思是系统静态事件的内部事件处理程序Microsoft.Win32.SystemEvents.UserPreferenceChanged。为了正确释放资源,我们需要显式调用 ToolStrip Dispose 方法,例如在SO 帖子中描述的那样。

但是,如果我们使用 System.ComponentModel.Component 的后代的 ToolStrip,这将无济于事——至少在我的情况下是这样。这是代码的相应部分:

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

AutoFilterToolStrip 的定义如下:

Private Class AutoFilterToolStrip
    Inherits ToolStrip
    ......................
End Class

,但这在我们的上下文中应该无关紧要。

我什至在需要时手动调用 DropDownFilterBox.Dispose 来清理使用过的资源,但这似乎没有任何效果。

一些开发人员建议隐藏 ToolStrips(将 Visible 属性设置为 False),因为在这种情况下 ToolStrip 应自动删除 UserPreferenceChanged 事件处理程序。是的,应该完成工作的内部 HookStaticEvents 方法被调用。但这也无济于事。

我什至尝试使用反射手动分离问题事件处理程序:

RemoveHandler Microsoft.Win32.SystemEvents.UserPreferenceChanged, _
    DirectCast( _
    System.Delegate.CreateDelegate(GetType(Microsoft.Win32.UserPreferenceChangedEventHandler), fToolStripMain, "OnUserPreferenceChanged"),  _
        Microsoft.Win32.UserPreferenceChangedEventHandler _
    )

,但这也没有任何效果。

关于如何在我们的案例中克服这个内存泄漏问题以及为什么 ToolStrip.Dispose 的显式调用在我们的案例中可能不起作用的任何想法?

我们在 VB.NET 2010 中为 .NET Framework 4+(客户端配置文件)进行开发。

4

1 回答 1

1

我的代码中缺少的部分如下:

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 自动创建所谓的溢出按钮,并且它还订阅可能导致内存泄漏的 UserPreferenceChanged 事件!关于此的更多信息可以在这里找到:ToolStrip 内存泄漏

现在我的组件的 Dispose 方法的完整列表如下所示:

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

我还应该承认,在 CodeProject 文章Memory Leak Detection in .NET中描述的在托管代码中查找内存泄漏的技术对我找到解决方案有很大帮助 - 顺便说一句,不花一分钱购买商业内存分析器。

于 2013-10-23T09:16:09.277 回答