Winforms: ContextMenuStrip中的内存泄露

一、问题描述

在一个Form里添加一个ContextMenuStrip。如果在运行的时候反复打开然后关闭该Form,我们发现程序中出现内存泄露。

二、问题重现

1.       Visual Studio中创建一个Winforms项目;

2.       在项目中添加一个FormForm2);

3.       Form2中添加一个ContextMenuStrip

4.       Form2添加一个MouseClick事件响应器(Event Handler);

5.       Form2_MouseClick添加如下代码:

private void Form2_MouseClick(object sender, MouseEventArgs e)

{

    if (e.Button == System.Windows.Forms.MouseButtons.Right)

    {

        contextMenuStrip1.Show(this, new Point(10, 10));

    }

}

6.       Form1中添加一个Button

7.       Form1中为button1添加Click事件响应器;

8.       button1_C lick添加如下代码:

private void button1_Click(object sender, EventArgs e)

{

    Form2 form = new Form2();

    form.ShowDialog();

 

    GC.Collect();

    GC.WaitForPendingFinalizers();

    GC.Collect();

}

9.       编译运行;

10.   Form1上点击button1,弹出Form2

11.   Form2上单击右键,弹出菜单,在关闭Form2

12.   WinDbgAttach该程序,用如下命令统计UserPreferenceChangedEventHandler的数量:

!dumpheap -stat -type Microsoft.Win32.UserPreferenceChangedEventHandler

13.   重复第1112步,我们会注意到没多重复一次,UserPreferenceChangedEventHandler会多一个实例。内存泄露发生了。

三、问题分析

Winforms为一个控件(Control)创建句柄的时候,会为UserPreferenceChanged注册一个事件响应器。当我们销毁该控件的句柄时,我们会删除这个事件响应器。通常一个控件的句柄在两种情况下会被销毁:一是当该控件收到WM_C LOSE消息时,二是当控件被Dispose的时候。

通常当我们关掉一个Form的时候,Form本身以及它的子控件都能收到WM_CLOSE消息,因此控件的句柄都会被销毁,同时UserPreferenceChanged的事件响应器也会被删除。但当一个ContextMenuStrip为不可见的时候,是收不到WM_CLOSE消息的,因此它的UserPreferenceChanged 事件响应器没被删除,从而导致内存泄露。

为了能销毁它的句柄并删除对应的UserPreferenceChanged事件响应器,我们应该调用它的Dispose函数。

四、解决方案

在上面的例子中,我们需要如下两步来Dispose一个ContextMenuStrip

1.       Form2.Dispose调用 ContextMenuStripMenu.Dispose

protected override void Dispose(bool disposing)

{

    if (disposing)

    {

        if(components != null)

            components.Dispose();

 

        contextMenuStrip1.Dispose();

    }

 

    base.Dispose(disposing);

}

2.       当关闭Form2的时候,调用Form2.Dispose。我们可以通过using关键字来自动Dispose Form2

private void button1_Click(object sender, EventArgs e)

{

              using (Form2 form = new Form2())

                  form.ShowDialog();

  

    GC.Collect();

    GC.WaitForPendingFinalizers();

    GC.Collect();

          }

你可能感兴趣的:(c,object,null,button,Components,WinForms)