C# – When drawing an image: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI

cgdi+winforms

I've got a global Graphics object created from a Panel. At regular intervals an image is picked up from the disk and drawn into the panel using Graphics.DrawImage(). It works fine for a few iterations and then I'm getting the following helpful exception:

System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.
at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
at System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y)
at System.Drawing.Graphics.DrawImage(Image image, Point point)

I ruled out memory leaks as I dispose of the image object when I'm done with it. I know that the images are not corrupted and can be read fine as the program executes fine for a while before the panel stops showing.

I ran into the same problem when using a PictureBox but this time at least I got an error instead of nothing.

I checked the GDI objects and USER objects in the Task Manager but they're always around 65 user objects and 165 GDI objects when the app works and when it doesn't.

I do need to get to the bottom of this as soon as and it's not like I can stick breakpoints in .NET System libraries and see where exactly execution fails.

Thanks in advance.

EDIT: This is the display code:

private void DrawImage(Image image)
{
  Point leftCorner = new Point((this.Bounds.Width / 2) - (image.Width / 2), (this.Bounds.Height / 2) - (image.Height / 2));
  _graphics.DrawImage(image, leftCorner);
}

the image load code:

private void LoadImage(string filename, ref Image image)
{
  MemoryStream memoryStream = DecryptImageBinary(Settings.Default.ImagePath + filename, _cryptPassword);

  image = Image.FromStream(memoryStream);

  memoryStream.Close();
  memoryStream.Dispose();
  memoryStream = null;
}

_image is global and its reference is updated in LoadImage. They are passed as parameters as I want to change the global references from as few places as possible only and keep the other methods self contained. _graphics is also global.

I've also got a webBrowser control for web sites and I either show an image or a website at one time. when there's time to display an image, the following code executes:

webBrowser.Visible = false;
panel.Visible = true;
DrawImage(_image)
_image.Dispose();
_image = null;

_image is referencing a pre-loaded image.

Hope this helps.

Best Answer

Your problem is similar to what I thought, but not quite. When you are loading the image, you are loading it from a MemoryStream. You have to keep the stream open for the lifetime of the image, see MSDN Image.FromStream.

You must keep the stream open for the lifetime of the Image.

The solution is to make a copy of your image in the FromImage function:

private void LoadImage(string filename, ref Image image)
{
  using (MemoryStream memoryStream = DecryptImageBinary(Settings.Default.ImagePath + filename, _cryptPassword))
  {
      using (tmpImage = Image.FromStream(memoryStream))
      { 
         image = new Bitmap(tmpImage);
      }
  }

}

Similar to the dispose problem I mentioned, the image will seem to work and then randomly fail when the underlying stream is garbage collected.

Related Topic