Recently, I learned a valuable lesson in modern programming efficiency: Problem-solving abilities often take a backseat to information retrieval and analysis. I was working on an application that saved images extracted from Word 2007’s OOXML file format, and received an application error:
System.Runtime.InteropServices.ExternalException was unhandled
Message=”A generic error occurred in GDI+.”
In OOXML, various “parts” of the document are stored in DocumentPart containers. The various parts have relationships, each with its own ID. The project code had obtained the appropriate DocumentPart containing the image from the document, using the relationship ID associated with that image. It then retrieved the image from the DocumentPart, using the Image.FromStream method, and stored the resulting Image for later use. The code that caused the error was a simple Image.Save command that attempted to save the image in a specified folder.
The error message was no help: What does “A generic error occurred in GDI+” mean, anyhow? Time to review the stack trace:
at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder,
at System.Drawing.Image.Save(String filename, ImageFormat format)
at System.Drawing.Image.Save(String filename)
Object sender, EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
Nothing jumps out from that. I had a valid image; the filename was valid; the destination folder existed and contained no existing image with the same filename; the folder wasn’t read-only; I had full permissions on the folder; and there was plenty of room on the disk. Even worse, the code had been running for several days with no problems.
In earlier years, I would have been stuck, but modern programming is fundamentally different. Rather than laboriously solving problems, often by trial-and-error, you can usually solve problems by a simple search. The key to solving this one was the line ErrorCode=-2147467259. Pasting that into Google turned up a discussion that referenced this Microsoft Support page. The information there says basically that if you create a Bitmap from a stream (which I had), that stream must remain open for the lifetime of the Bitmap, because:
“GDI+, and therefore the System.Drawing namespace, may defer the decoding of raw image bits until the bits are required by the image. Additionally, even after the image has been decoded, GDI+ may determine that it is more efficient to discard the memory for a large Bitmap and to re-decode later. Therefore, GDI+ must have access to the source bits for the image for the life of the Bitmap or the Image object.”
I didn’t want to recode the project to keep the stream open, but the page also provided a workaround: You can make a copy of the original bitmap, which removes the requirement to access the source. It provided these instructions:
1. Construct the original Bitmap from the stream, from the memory, or from the file.
2. Create a new Bitmap of the same size, with a pixel format of more than 8 bits-per-pixel (BPP).
3. Use the Graphics.FromImage() method to obtain a Graphics object for the second Bitmap.
4. Use Graphics.DrawImage() to draw the first Bitmap onto the second Bitmap.
5. Use Graphics.Dispose() to dispose of the Graphics.
6. Use Bitmap.Dispose() to dispose of the first Bitmap.
It took only a few minutes to add that code, and the project was back on track. But the real point is that the resources available on the web to modern programmers often makes programming much more of an exercise in good search techniques than an exercise in good logic and debugging techniques.
Of course, all this could have been avoided if the Image class had a way to ensure that the source bits would be available even if the originating stream were closed. And it would have been far less irritating if the error message had said “Unable to save because the source stream or file for this image is no longer available.” But that’s another topic.