LINQed IN

Blog and resources on C# and .NET related software development stuff by Troy Magennis 
Welcome to LINQed IN Sign in | Join | Help
in
Home Blogs Files

LINQed IN - Troy Magennis' View on .NET, C# and Software Development

Tips for WinForms Memory and Painting Debugging

Every now and then I come across a Windows Forms application that has memory leak, or graphic resource leak issues. In managed environments like .NET, some developers overlook that many components wrap un-managed finite resources.  I worked with a fellow developer Mike Zhang today, and because of the difficulties in replicating the issue, we decided that a full pass through the entire source code fixing these issues was our best shot at solving a niggling production issue. After today, here are our top 4 tips and the first steps to ensuring you have a well behaved Windows Forms .NET application.

Begin Update / End Update (Blank, unpainted regions of the screen)

Many Windows controls have a BeginUpdate and EndUpdate methods. These inhibit any Paint events being called when a lot of data is being added to a ListView for example. Big trouble occurs if an EndUpdate isn’t called matching a BeginUpdate! Most general case, is an exception occurs after the begin, but before the EndUpdate gets called.

To Fix: Do a solution wide search for “BeginUpdate”; Go through the result list (yes, EVERY one of them) and ensure that a the EndUpdate is wrapped in a Finally block.

BAD

   1:  this.BeginUpdate();
   2:  // lots of work, including work that may throw an exception!
   3:  // ...
   4:  this.EndUpdate();

GOOD

   1:  this.BeginUpdate();
   2:  try
   3:  {
   4:       // lots of work, including work that may throw an exception!
   5:  }
   6:  finally
   7:  {
   8:      // EndUpdate called even in the case of an Exception in the lots of work above.
   9:      this.EndUpdate();
  10:  }

Pen, Brush and Graphic Resources (Resource leak)

Even in .NET, a lot of managed code uses unmanaged resources. These managed wrappers implement the IDisposible pattern, where the Dispose method nicely releases all unmanaged resources. However – What if Dispose doesn’t get called? Either because Garbage Collection in .NET is happens on its own non-deterministic timetable, some external reference keeps an object alive, or an exception occurs and Dispose is never called. This manifests itself as areas that don’t draw properly or have an enclosed red cross.

To Fix: Do a solution wide search for “Graphic ”, “Pen “ and “Brush “; Go through the result list (yes, EVERY one of them) and ensure that the scope of the Brush, Pen or Graphic is within a using clause, or a try/finally block.

Read: "using" statement reference on MSDN: http://msdn2.microsoft.com/en-us/library/yh598w02.aspx

BAD

   1:      Graphics g = Graphics.FromImage(sourceImage);
   2:      Pen pen = new Pen();
   3:      // lots of work, including work that might fail
   4:      g.DrawRectangle(pen, 10, 10, 100, 100);
   5:      throw new Exception();
   6:      pen.Dispose(); // never called
   7:      g.Dispose(); // never called

GOOD

   1:      using(Graphics g = Graphics.FromImage(sourceImage))
   2:      {
   3:          using(Pen pen = new Pen())
   4:          {
   5:              // lots of work, including work that might fail
   6:              g.DrawRectangle(pen, 10, 10, 100, 100);
   7:              throw new Exception();
   8:           } // calls pen.Dispose() in all cases
   9:      } // calls g.Dispose() in ALL cases

Manual Event Wiring Up and Object Lifetime (memory and resource leak)

This is without a doubt the most common (and some say only common) memory leak on .NET applications. If your component subscribes to an event in ANOTHER component or class, you must –

a) Unhook yourself in your Dispose method for the form or class you subscribed FROM

b) Avoid multiple hookups to an event if your form is opened, or called multiple times

Tips:

a) Hookup events using the Forms designer when possible; This automatically handles events in the proper way

b) To be sure multiple hookups don’t accidently occur, remove yourself from an event before hooking your event up (this is safe, even the first time) –

BAD
ValidationGlobalManager.OnValidate += new My ValidationHandler(MyValidationFunction);


GOOD
  ValidationGlobalManager.OnValidate -= new My ValidationHandler(MyValidationFunction);
  ValidationGlobalManager.OnValidate += new My ValidationHandler(MyValidationFunction);

c) If you hook up an event outside of InitializeComponent (i.e. NOT through the forms designer), then YOU are responsible for unhooking your event in the Dispose() method of your form.

OnPaint and OnDraw Event Handlers - Call the base. implementation in all cases (unpredictable drawing behavior)

Many controls allow you to implement your own OnDraw and OnPaint event handlers. However, the base.OnDraw(); or base.OnPaint(); methods MUST be called under all conditions, even in the case your code fails with an exception.

To Fix: Do a solution wide search for “OnDraw ” and “OnPaint“; Go through the result list (yes, EVERY one of them) and ensure that

a) the base call is first, so it always runs, OR

b) the base call is protected with a try/finally block

Mike and Troy.

Published Wednesday, April 09, 2008 2:04 PM by Troy.Magennis

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

No Comments

What do you think?

(required) 
(optional)
(required) 

This Blog

Post Calendar

<April 2008>
SuMoTuWeThFrSa
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

News

I'm in Seattle, WA at the moment and will be for the next couple of months. Enjoying my holidays and as you can see i'm getting to spends some time on my laptop writing a few blog posts!

Syndication

Powered by Community Server, by Telligent Systems