2013-10-08 1 views
0

Les développeurs savent que l'utilisation du contrôle WinForms ToolStrip peut entraîner des fuites de mémoire gérée si nous ne le forçons pas à libérer des éléments manuellement. Je veux dire le gestionnaire d'événement interne de l'événement Microsoft.Win32.SystemEvents.UserPreferenceChanged statique du système. Pour libérer les ressources correctement, nous avons besoin d'un appel explicite de la méthode ToolStrip Dispose tel qu'il est décrit, par exemple, dans les publications SO this ou this. Cependant, cela n'aide pas si nous utilisons un ToolStrip d'un descendant de System.ComponentModel.Component - au moins, dans mon cas. Voici la partie correspondante du code:ToolStrip.Dispose() ne permet pas d'éviter les fuites de mémoire dans System.ComponentModel.Component

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 

Le AutoFilterToolStrip est défini comme ceci:

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

, mais cela ne devrait pas d'importance dans notre contexte.

J'appelle même DropDownFilterBox.Dispose manuellement pour nettoyer les ressources utilisées quand c'est nécessaire, mais il semble que cela n'a aucun effet.

Certains développeurs recommandent de masquer ToolStrips (définissez la propriété Visible sur False) car le gestionnaire d'événements UserPreferenceChanged doit être supprimé automatiquement par ToolStrip dans ce cas. Oui, la méthode interne HookStaticEvents qui devrait faire le travail est appelée à cela. mais cela n'aide pas non plus.

J'ai même essayé de détacher le gestionnaire d'événements problème manuellement à l'aide de réflexion:

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

, mais cela ne aussi aucun effet.

Des idées sur la façon de surmonter ce problème de fuite de mémoire dans notre cas et pourquoi l'appel explicite de ToolStrip.Dispose peut ne pas fonctionner dans notre cas?

Nous développons dans VB.NET 2010 pour .NET Framework 4+ (profil client).

Répondre

1

La partie manquante de mon code est le suivant:

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 crée le soi-disant bouton de débordement automatiquement, et il est également abonnée à l'événement UserPreferenceChanged qui peut provoquer des fuites de mémoire! Quelques informations supplémentaires à ce sujet peuvent être trouvées sur SO ici: ToolStrip memory leak.

Maintenant, la liste complète de la méthode Dispose de mon composant ressemble à ceci:

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 

Je dois aussi admettre que la technique de trouver les fuites de mémoire dans le code managé, décrit dans l'article CodeProject Memory Leak Detection in .NET, m'a beaucoup aidé dans la recherche de la solution - BTW, ne dépensant pas un centime pour l'achat de profileurs de mémoire commerciale.