Seems that DesignSurface has a memory leak.
This can be observed by running the program bellow and push several times
the <ShowDialog> and <CallGC> buttons and observe that the heap memory
increase each time <ShowDialog> button is pushed.
The leak can be also observed by running the CLR Profiler for Whidbey.
Details from CLR Profiler are at the end of the message.
CLRProfiler for Whidbey final can be downloaded from
http://www.microsoft.com/downloads/details.aspx familyid=a362781c-3870-43be-8926-862b40aa0cd0
I would like to know the cause and also get an workaround. (please)
Here the source-code
//---------------------- BEGIN -----------------
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel.Design;
namespace TestMemLeakDesignSurface
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
class MainForm : Form
{
public MainForm()
{
this.Text = "MainForm TestMemLeakDesignSurface";
Button btnShowDialog = new Button();
this.Width = 500;
btnShowDialog.Text = "Show Dialog";
btnShowDialog.Location= new Point(20, 20);
Button btnCallGC = new Button();
btnCallGC.Text = "Call GC";
btnCallGC.Location= new Point(200, 20);
btnShowDialog.Click += new EventHandler(btnShowDialog_Click);
btnCallGC.Click += new EventHandler(btnCallGC_Click);
this.Controls.Add(btnCallGC);
this.Controls.Add(btnShowDialog);
}
void btnShowDialog_Click(object sender, EventArgs e)
{
using (DesignSufaceHostForm form = new DesignSufaceHostForm())
{
form.ShowDialog(this);
}
}
void btnCallGC_Click(object sender, EventArgs e)
{
// do whatever is possible to force GC to collect
// nobody refer the DesignSufaceHostForm or Designed Form
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
long totalMemeory = System.GC.GetTotalMemory(true);
MessageBox.Show(string.Format("totalMemory: {0:n}", totalMemeory));
}
}
public class DesignedForm : Form
{
public DesignedForm()
{
this.Size = new Size(300, 300);
this.Text = "Designed Form";
}
}
class DesignSufaceHostForm : Form
{
DesignSurface designSurface;
public DesignSufaceHostForm()
{
this.Size = new Size(500, 500);
this.Text = "Host of DesignSuface";
designSurface = new DesignSurface();
designSurface.BeginLoad(typeof(DesignedForm));
Control view = designSurface.View as Control;
if (view != null)
{
view.Dock = DockStyle.Fill;
this.Controls.Add(view);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
designSurface.Dispose();
}
base.Dispose(disposing);
}
}
}
//---------------------- END -----------------
This is extracted from CLR Profiler after aplying filter:
"TestMemLeakDesignSurface.DesignedForm" and enable only
Show Callers/Referencing Objects
<root> : 4.8 kB (100.00%)
Handle, Pinning <root>->Handle, Pinning->(System.Object []): 4.8 kB
(100.00%)
Handle <root>->Handle->(System.Threading.ThreadAbortException): 0.0 bytes
(0.00%)
Handle <root>->Handle->(System.ExecutionEngineException): 0.0 bytes
(0.00%)
Handle <root>->Handle->(TestMemLeakDesignSurface.MainForm): 0.0 bytes
(0.00%)
Handle <root>->Handle->(System.SharedStatics): 0.0 bytes (0.00%)
Handle <root>->Handle->(System.StackOverflowException): 0.0 bytes (0.00%)
Handle <root>->Handle->(System.AppDomain): 0.0 bytes (0.00%)
Handle, Pinning <root>->Handle, Pinning->(System.Object): 0.0 bytes
(0.00%)
Handle <root>->Handle->(System.OutOfMemoryException): 0.0 bytes (0.00%)
Handle <root>->Handle->(System.Threading.Thread): 0.0 bytes (0.00%)
Stack <root>->Stack->(System.Windows.Forms.ApplicationContext): 0.0 bytes
(0.00%)
Finalizer <root>->Finalizer->(System.Windows.Forms.Internal.DeviceContext):
0.0 bytes (0.00%)
Stack <root>->Stack->(System.Windows.Forms.NativeMethods.MSG []): 0.0
bytes (0.00%)
Stack <root>->Stack->(System.Windows.Forms.Application.ComponentManager):
0.0 bytes (0.00%)
Stack <root>->Stack->(System.Windows.Forms.Application.ThreadContext): 0.0
bytes (0.00%)
Handle <root>->Handle->(System.Reflection.Assembly): 0.0 bytes (0.00%)
Handle <root>->Handle->(System.Security.PermissionSet): 0.0 bytes (0.00%)
Handle <root>->Handle->(System.Reflection.Module): 0.0 bytes (0.00%)
Finalizer <root>->Finalizer->(System.Drawing.BufferedGraphics): 0.0 bytes
(0.00%)
Finalizer <root>->Finalizer->(System.Windows.Forms.Internal.WindowsFont):
0.0 bytes (0.00%)
System.Object [] Handle, Pinning->System.Object
[]->(System.Diagnostics.TraceSwitch,System.Internal.HandleCollector.HandleType
[],System.Runtime.InteropServices.HandleRef,...): 4.8 kB (100.00%) (1
object, 4.0 kB (84.14%))
System.Collections.Hashtable System.Object
[]->System.Collections.Hashtable->(System.Collections.Hashtable.bucket []):
772 bytes (15.86%) (1 object, 56 bytes (1.15%))
System.Collections.Hashtable.bucket []
System.Collections.Hashtable->System.Collections.Hashtable.bucket
[]->(System.EventHandler): 716 bytes (14.71%) (1 object, 288 bytes
(5.92%))
System.EventHandler System.Collections.Hashtable.bucket
[]->System.EventHandler->(System.Windows.Forms.Design.ControlCommandSet):
428 bytes (8.79%) (1 object, 32 bytes (0.66%))
System.Windows.Forms.Design.ControlCommandSet
System.EventHandler->System.Windows.Forms.Design.ControlCommandSet->(System.ComponentModel.Design.DesignerHost.Site,System.Windows.Forms.Design.EventHandlerService,TestMemLeakDesignSurface.DesignedForm,...):
396 bytes (8.13%) (1 object, 76 bytes (1.56%))
TestMemLeakDesignSurface.DesignedForm
System.Windows.Forms.Design.ControlCommandSet->TestMemLeakDesignSurface.DesignedForm->(System.ComponentModel.Design.DesignerHost.Site,System.String,System.Windows.Forms.Control.ControlNativeWindow,...):
320 bytes (6.57%) (1 object, 320 bytes (6.57%))
<bottom> : 0.0 bytes (0.00%)

DesignSurface MemoryLeak
Liam Johnstone
Divyang
See the new class in brown & bold bellow
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel.Design;
using System.Windows.Forms.Design;
using System.Reflection;
using System.Collections;
namespace TestMemLeakDesignSurface
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
class MainForm : Form
{
public MainForm()
{
this.Text = "MainForm TestMemLeakDesignSurface";
Button btnShowDialog = new Button();
this.Width = 500;
btnShowDialog.Text = "Show Dialog";
btnShowDialog.Location= new Point(20, 20);
Button btnCallGC = new Button();
btnCallGC.Text = "Call GC";
btnCallGC.Location= new Point(200, 20);
btnShowDialog.Click += new EventHandler(btnShowDialog_Click);
btnCallGC.Click += new EventHandler(btnCallGC_Click);
this.Controls.Add(btnCallGC);
this.Controls.Add(btnShowDialog);
}
void btnShowDialog_Click(object sender, EventArgs e)
{
using (DesignSufaceHostForm form = new DesignSufaceHostForm())
{
form.ShowDialog(this);
}
}
void btnCallGC_Click(object sender, EventArgs e)
{
// do whatever is possible to force GC to collect
// nobody refer the DesignSufaceHostForm or Designed Form
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
long totalMemeory = System.GC.GetTotalMemory(true);
MessageBox.Show(string.Format("totalMemory: {0:n}", totalMemeory));
}
}
public class DesignedForm : Form
{
public DesignedForm()
{
this.Size = new Size(300, 300);
this.Text = "Designed Form";
}
}
class DesignSufaceHostForm : Form
{
DesignSurface designSurface;
public DesignSufaceHostForm()
{
this.Size = new Size(500, 500);
this.Text = "Host of DesignSuface";
designSurface = new DesignSurface();
designSurface.BeginLoad(typeof(DesignedForm));
Control view = designSurface.View as Control;
if (view != null)
{
view.Dock = DockStyle.Fill;
this.Controls.Add(view);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
ClearControlCommandSetWorkaround clearHelper = new ClearControlCommandSetWorkaround(designSurface);
designSurface.Dispose();
clearHelper.DoClear();
base.Dispose(disposing);
}
}
}
/// <summary>
/// This class is present as workaround for a bug
/// http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx feedbackid=8131ef87-3227-49a0-925c-67a2ade8ef2b
///
/// Because Microsoft chose to hide a lot of classes we are forced to heavely use the
/// reflection to accomplish this.
/// </summary>
internal class ClearControlCommandSetWorkaround
{
object commandSet;
internal ClearControlCommandSetWorkaround(DesignSurface designSurface)
{
IDesignerHost host = designSurface.GetService(typeof(IDesignerHost)) as IDesignerHost;
IDesigner designer = host.GetDesigner(host.RootComponent);
DocumentDesigner docDesigner = designer as DocumentDesigner;
commandSet = GetFieldValue(docDesigner, "commandSet");
}
internal void DoClear()
{
// I think that commandStatusHash should not be static
// now we have to clear the hash for Event handler references
// that have as target the current commandSet
Type commandSetItemType = commandSet.GetType().Assembly.GetType("System.Windows.Forms.Design.CommandSet+CommandSetItem");
Hashtable commandStatusHash = GetFieldValue(null, "commandStatusHash", commandSetItemType) as Hashtable;
List<EventHandler> ehToRemove = new List<EventHandler>();
foreach (EventHandler eh in commandStatusHash.Keys)
{
if(eh.Target == commandSet)
{
ehToRemove.Add(eh);
}
}
foreach (EventHandler eh in ehToRemove)
{
commandStatusHash.Remove(eh);
}
/*
// the source-code comented was a previous workaround
// seems that it is not needed anymore.
SetFieldValue(commandSet, "baseControl", null);
Type[] cmdSetTypes = new Type[] {
commandSet.GetType(),
commandSet.GetType().BaseType };
foreach (Type cmdSetType in cmdSetTypes)
{
Array cmdSetItemsArray = GetFieldValue(commandSet, "commandSet", cmdSetType) as Array;
foreach (object cmdSetItem in cmdSetItemsArray)
{
SetFieldValue(cmdSetItem, "statusHandler", null);
}
SetFieldValue(commandSet, "commandSet", null, cmdSetType);
}
*/
}
#region Reflection related helpers
static FieldInfo GetFieldInfo(object obj, string fieldName, Type objType)
{
FieldInfo fieldInfo = objType.GetField(fieldName, BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
if (fieldInfo != null)
return fieldInfo;
if (objType == typeof(object))
return null;
return GetFieldInfo(obj, fieldName, objType.BaseType);
}
static FieldInfo GetFieldInfo(object obj, string fieldName)
{
return GetFieldInfo(obj, fieldName, obj.GetType());
}
static object GetFieldValue(object obj, string fieldName)
{
return GetFieldValue(obj, fieldName, obj.GetType());
}
static object GetFieldValue(object obj, string fieldName, Type type)
{
FieldInfo fieldInfo = GetFieldInfo(obj, fieldName, type);
if (fieldInfo == null)
{
return null;
}
return fieldInfo.GetValue(obj);
}
static void SetFieldValue(object obj, string fieldName, object fieldValue, Type type)
{
FieldInfo fieldInfo = GetFieldInfo(obj, fieldName, type);
if (fieldInfo == null)
{
return;
}
fieldInfo.SetValue(obj, fieldValue);
}
static void SetFieldValue(object obj, string fieldName, object fieldValue)
{
SetFieldValue(obj, fieldName, fieldValue, obj.GetType());
}
#endregion
}
}
Tolle
However if they don’t put in a reproduced state, I other users can vote for the bug. (i.e. I would like to vote for the “Bug Details: Disabling a Control that contains a DesignSurface "view" causes a StackOverflowException” but because its status I can’t. And I’m assuring you that I reproduced it in less than 5 minutes.
- Cristi
RogerLainson
If you are confident that this is a bug (which it seems indeed), please report it to the product support pages:
http://lab.msdn.microsoft.com/productfeedback/default.aspx
Skivan
For example see the bug:
http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx feedbackid=9a051bf5-bdbf-40c6-a9d2-c8408486b966
on that am also interested. This bug was reported more than a month ago (Opened Date: 2005-11-07 09:12:35) and it doesn't have at least "Reproduced" or Validated status.
Do you have an ideea if I'll ever have a feedback
Link to the bug I reported:
http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx feedbackid=8131ef87-3227-49a0-925c-67a2ade8ef2b
Do you have any ideea if is a web page with all known bugs that are in Whidbey
olenka
http://lab.msdn.microsoft.com/productfeedback/SearchResults.aspx text=&stype=1&fields=1&type=0&category=11&os=0&oslang=0&status=0&msstatus=0&resolution=0&chgdays=&validation=0&votes=&voterating=0&workarounds=False&attachments=False&product=0&version=0
p.s. If you can reproduce a problem, you could of course also submit a new case with a link to the previous case.
manojamin
How soon will Microsoft respond to my issue
An issue must have two votes before Microsoft will respond. If the volume of incoming issues is high, reply may be delayed.
This is not entirely true, they already responded to one of the bugs I submitted that had only one vote :). But you might try to get other people to vote on your bug or to validate it, this may help get you higher on the priority list.
Still I can imagine that we have to be patient, there are hundreds of bugs that are being filed every week. My experience is that they will always respond, even though it may take some time.