June 2009 Archives
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+."
Source="System.Drawing"
ErrorCode=-2147467259
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:
StackTrace:
at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder,
EncoderParameters encoderParams)
at System.Drawing.Image.Save(String filename, ImageFormat format)
at System.Drawing.Image.Save(String filename)
at TestOpenXMLSDK2.Form1.btnSaveImages_Click(
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.
The new Sun boss put the OpenOffice and JavaFX groups on notice during the JavaOne opening keynote today: Produce some JavaFX libraries for the OpenOffice suite and do it quickly. Larry Ellison, head of the soon-to-be Oracle/Sun Java giant, said: "I've been meeting with different groups inside of Sun, and one of the things we're looking forward to is seeing libraries come out of the OpenOffice group that are JavaFX-based."
He offered spreadsheet and word-processing programs as the types of JavaFX application he expects. (Never mind that OpenOffice already has a spreadsheet program called Calc and a word processor called Writer.)
The two-hour keynote closed with a symbolic passing of the torch from the old “Chairman of JavaOne,” former Sun CEO Scott McNealy, to the new one, Oracle CEO Ellison. With so much speculation about how the acquisition will shake out for Java, both men had to address the topic for the largely developer audience. But their exchange was carefully worded because the acquisition has not yet been finalized.
As expected, Ellison reiterated Oracle's commitment to Java (Oracle’s entire middleware stack is 100-percent Java) and twice pledged to expand its investment in Java, which drew applause both times. But his most pointed remarks were the challenge to the OpenOffice and JavaFX groups and later wondering out loud why Sun/Oracle couldn’t produce mobile devices like netbooks or Google and T-Mobile's G1 phone, all based on the JavaFX platform.
In a setting where Ellison had to be particularly mindful of his words, choosing to declare these challenges publicly provided some insight into where Oracle will place its focus for Sun Java.
For you JavaFX devotees, hearing Ellison put the platform front and center has to be encouraging. At one point, he said: "We're very committed to seeing JavaFX exploited throughout Oracle and throughout Sun."
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |










