G
Greg
I have found what seems to be a leak of GDI resources in the .NET
TreeView. I'm not entirely sure it's the .NET code or the underlying
OS Common control but here's the skinny:
If you use the Checkboxes property and set it to true on a TreeView it
will leak 1 or 2 bitmaps and 2 memDCs every time you create and dispose
of such a treeview.
Our application has a browser-like UI parts of the UI are changing as
controls get removed and destroyed. This leak eventually leaks too
many GDI resources resulting in a crash.
I was using the GDIUsage tool posted at
http://msdn.microsoft.com/msdnmag/issues/03/01/GDILeaks/default.aspx to
verify this and I put together a small app that exhibits the behavior.
Start a new project, create a new form and paste this into your code to
try it out. Click the create a tree, then the middle button to
remove/dispose/GC. You'll notice that the tree with the checkboxes
leaks resources and the one without checkboxes leaks nothing.
We are on VS2k3 using .NET 1.1 SP1.
Do the MS folks know about this and is there a patch?
==================code================
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Panel TreeViewPanel;
private System.Windows.Forms.Button CreateCheckTreeButton;
private System.Windows.Forms.Button CreateNoCheckTreeButton;
private System.Windows.Forms.Button DisposeTreeButton;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.TreeViewPanel = new System.Windows.Forms.Panel();
this.CreateCheckTreeButton = new System.Windows.Forms.Button();
this.CreateNoCheckTreeButton = new System.Windows.Forms.Button();
this.DisposeTreeButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// TreeViewPanel
//
this.TreeViewPanel.Location = new System.Drawing.Point(24, 16);
this.TreeViewPanel.Name = "TreeViewPanel";
this.TreeViewPanel.Size = new System.Drawing.Size(680, 400);
this.TreeViewPanel.TabIndex = 0;
//
// CreateCheckTreeButton
//
this.CreateCheckTreeButton.Location = new System.Drawing.Point(24,
440);
this.CreateCheckTreeButton.Name = "CreateCheckTreeButton";
this.CreateCheckTreeButton.Size = new System.Drawing.Size(152, 40);
this.CreateCheckTreeButton.TabIndex = 1;
this.CreateCheckTreeButton.Text = "Create TreeView with CheckBoxes";
this.CreateCheckTreeButton.Click += new
System.EventHandler(this.CreateCheckTreeButton_Click);
//
// CreateNoCheckTreeButton
//
this.CreateNoCheckTreeButton.Location = new
System.Drawing.Point(552, 440);
this.CreateNoCheckTreeButton.Name = "CreateNoCheckTreeButton";
this.CreateNoCheckTreeButton.Size = new System.Drawing.Size(152,
40);
this.CreateNoCheckTreeButton.TabIndex = 2;
this.CreateNoCheckTreeButton.Text = "Create TreeView without
CheckBoxes";
this.CreateNoCheckTreeButton.Click += new
System.EventHandler(this.CreateNoCheckTreeButton_Click);
//
// DisposeTreeButton
//
this.DisposeTreeButton.Location = new System.Drawing.Point(288,
440);
this.DisposeTreeButton.Name = "DisposeTreeButton";
this.DisposeTreeButton.Size = new System.Drawing.Size(152, 40);
this.DisposeTreeButton.TabIndex = 3;
this.DisposeTreeButton.Text = "Remove and Dispose of the Tree";
this.DisposeTreeButton.Click += new
System.EventHandler(this.DisposeTreeButton_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(728, 510);
this.Controls.Add(this.DisposeTreeButton);
this.Controls.Add(this.CreateNoCheckTreeButton);
this.Controls.Add(this.CreateCheckTreeButton);
this.Controls.Add(this.TreeViewPanel);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private TreeView CreateTreeControl()
{
TreeView tree = new System.Windows.Forms.TreeView();
tree.BorderStyle = System.Windows.Forms.BorderStyle.None;
tree.Dock = System.Windows.Forms.DockStyle.Fill;
tree.ImageIndex = -1;
tree.Location = new System.Drawing.Point(3, 16);
tree.Name = "TheTree";
tree.ShowLines = false;
TreeViewPanel.Controls.Add(tree);
tree.Nodes.Add("Node1");
tree.Nodes.Add("Node2");
tree.Nodes.Add("Node3");
return tree;
}
private void CreateCheckTreeButton_Click(object sender,
System.EventArgs e)
{
TreeView tree = CreateTreeControl();
// Add checkboxes
tree.CheckBoxes = true;
}
private void CreateNoCheckTreeButton_Click(object sender,
System.EventArgs e)
{
CreateTreeControl();
}
private void DisposeTreeButton_Click(object sender, System.EventArgs
e)
{
while (TreeViewPanel.Controls.Count > 0)
{
Control c = TreeViewPanel.Controls[0];
TreeViewPanel.Controls.Remove(c);
c.Dispose();
}
// Invoke the GC
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
==================endcode-=============
TreeView. I'm not entirely sure it's the .NET code or the underlying
OS Common control but here's the skinny:
If you use the Checkboxes property and set it to true on a TreeView it
will leak 1 or 2 bitmaps and 2 memDCs every time you create and dispose
of such a treeview.
Our application has a browser-like UI parts of the UI are changing as
controls get removed and destroyed. This leak eventually leaks too
many GDI resources resulting in a crash.
I was using the GDIUsage tool posted at
http://msdn.microsoft.com/msdnmag/issues/03/01/GDILeaks/default.aspx to
verify this and I put together a small app that exhibits the behavior.
Start a new project, create a new form and paste this into your code to
try it out. Click the create a tree, then the middle button to
remove/dispose/GC. You'll notice that the tree with the checkboxes
leaks resources and the one without checkboxes leaks nothing.
We are on VS2k3 using .NET 1.1 SP1.
Do the MS folks know about this and is there a patch?
==================code================
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Panel TreeViewPanel;
private System.Windows.Forms.Button CreateCheckTreeButton;
private System.Windows.Forms.Button CreateNoCheckTreeButton;
private System.Windows.Forms.Button DisposeTreeButton;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.TreeViewPanel = new System.Windows.Forms.Panel();
this.CreateCheckTreeButton = new System.Windows.Forms.Button();
this.CreateNoCheckTreeButton = new System.Windows.Forms.Button();
this.DisposeTreeButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// TreeViewPanel
//
this.TreeViewPanel.Location = new System.Drawing.Point(24, 16);
this.TreeViewPanel.Name = "TreeViewPanel";
this.TreeViewPanel.Size = new System.Drawing.Size(680, 400);
this.TreeViewPanel.TabIndex = 0;
//
// CreateCheckTreeButton
//
this.CreateCheckTreeButton.Location = new System.Drawing.Point(24,
440);
this.CreateCheckTreeButton.Name = "CreateCheckTreeButton";
this.CreateCheckTreeButton.Size = new System.Drawing.Size(152, 40);
this.CreateCheckTreeButton.TabIndex = 1;
this.CreateCheckTreeButton.Text = "Create TreeView with CheckBoxes";
this.CreateCheckTreeButton.Click += new
System.EventHandler(this.CreateCheckTreeButton_Click);
//
// CreateNoCheckTreeButton
//
this.CreateNoCheckTreeButton.Location = new
System.Drawing.Point(552, 440);
this.CreateNoCheckTreeButton.Name = "CreateNoCheckTreeButton";
this.CreateNoCheckTreeButton.Size = new System.Drawing.Size(152,
40);
this.CreateNoCheckTreeButton.TabIndex = 2;
this.CreateNoCheckTreeButton.Text = "Create TreeView without
CheckBoxes";
this.CreateNoCheckTreeButton.Click += new
System.EventHandler(this.CreateNoCheckTreeButton_Click);
//
// DisposeTreeButton
//
this.DisposeTreeButton.Location = new System.Drawing.Point(288,
440);
this.DisposeTreeButton.Name = "DisposeTreeButton";
this.DisposeTreeButton.Size = new System.Drawing.Size(152, 40);
this.DisposeTreeButton.TabIndex = 3;
this.DisposeTreeButton.Text = "Remove and Dispose of the Tree";
this.DisposeTreeButton.Click += new
System.EventHandler(this.DisposeTreeButton_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(728, 510);
this.Controls.Add(this.DisposeTreeButton);
this.Controls.Add(this.CreateNoCheckTreeButton);
this.Controls.Add(this.CreateCheckTreeButton);
this.Controls.Add(this.TreeViewPanel);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private TreeView CreateTreeControl()
{
TreeView tree = new System.Windows.Forms.TreeView();
tree.BorderStyle = System.Windows.Forms.BorderStyle.None;
tree.Dock = System.Windows.Forms.DockStyle.Fill;
tree.ImageIndex = -1;
tree.Location = new System.Drawing.Point(3, 16);
tree.Name = "TheTree";
tree.ShowLines = false;
TreeViewPanel.Controls.Add(tree);
tree.Nodes.Add("Node1");
tree.Nodes.Add("Node2");
tree.Nodes.Add("Node3");
return tree;
}
private void CreateCheckTreeButton_Click(object sender,
System.EventArgs e)
{
TreeView tree = CreateTreeControl();
// Add checkboxes
tree.CheckBoxes = true;
}
private void CreateNoCheckTreeButton_Click(object sender,
System.EventArgs e)
{
CreateTreeControl();
}
private void DisposeTreeButton_Click(object sender, System.EventArgs
e)
{
while (TreeViewPanel.Controls.Count > 0)
{
Control c = TreeViewPanel.Controls[0];
TreeViewPanel.Controls.Remove(c);
c.Dispose();
}
// Invoke the GC
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
==================endcode-=============