Tuesday, June 19, 2012

A generic error occurred in GDI+.

This week I've been focusing again on a utility I'm building to convert data from our old system to our new one - once that new one is in fact ready.

Anyway, this week I've been looking at some memory issues and the wonderful "generic error in GDI+" error I've been seeing crop up occasionally during the conversion process.

One part of the process, load l lot of small images out of a database, for writing straight back into a new database. The image data is read in as a byte array, from the byte array into a MemoryStream, which is in turn used to build an Image object, (Image being a property of one of the business entities) which is then saved. (The conversion process makes use of the business components, entities and data access classes already built to do this.)

So in trying to hunt down some out of memory error messages during this portion of the conversion, I"ve worked back through the code that manipulates the images to ensure objects are disposed of when they should be. I wrapped some portions of the code in using statements and noticed some small improvements. Until, that is, I wrapped a MemoryStream object in one particular innocuous looking private function in a using statement in order to ensure that stream object was disposed of quickly as it was called repetitively.

The job of that function was to take a byte array and give me back an Image.

Originally:


        protected System.Drawing.Image getImageFromByte(Byte[] img)
        {
            System.Drawing.Image returnValue = null;
 
            if (img == null)
                return null;

                System.IO.MemoryStream stream = new System.IO.MemoryStream(img);
                returnValue = System.Drawing.Image.FromStream(stream);
 
            return returnValue;
        }

Became this;

        protected System.Drawing.Image getImageFromByte(Byte[] img)
        {
            System.Drawing.Image returnValue = null;
 
            if (img == null)
                return null;
 
            using (System.IO.MemoryStream stream = new System.IO.MemoryStream(img))
            {
                returnValue = System.Drawing.Image.FromStream(stream);
            }
 
            return returnValue;
        }


After that small change, every call to access the image that was returned by this function, failed with a "Generic GDI+ error" exception.

Once again, Stackoverflow to the rescue:
http://stackoverflow.com/questions/3845456/loading-an-image-from-a-stream-without-keeping-the-stream-open


Turns out, the Image object keeps a reference to it's core Stream object for optimisation purposes, so that when it is disposed, before the image is, you're in a world of trouble.


This article helps explain why: http://support.microsoft.com/Default.aspx?id=814675

The answer? I chose the non-indexed method - it just seemed easier...

        protected System.Drawing.Image getImageFromByte(Byte[] img)
        {
            System.Drawing.Image returnValue = null;
 
            if (img == null)
                return null;
 
            using (System.IO.MemoryStream stream = new System.IO.MemoryStream(img))
            using (System.Drawing.Image imgFromStream = System.Drawing.Image.FromStream(stream))
            {
                // Image object requires the MemoryStream be kept open - work around, create a new non-indexed image
                
                // Set the newImage being created to the same size as the original image
                System.Drawing.Bitmap newImg = new System.Drawing.Bitmap(imgFromStream.Width, imgFromStream.Height);
 
                using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(newImg))
                {
                    // Draw the imgFromStream onto the newImg graphics object
                    g.DrawImage(imgFromStream, 0, 0);
                }
 
                returnValue = newImg;
            }
            
            return returnValue;
        }