I came across a somewhat obscure issue that, it seems, really should not be so obscure. I am working on an application that will be used to display rotating videos, a 'ticker' and rotating graphics in lobbies/waiting areas, on the client's hardware. I am using Windows Forms 2.0, DirectX 10 to display the videos, and a simple WinForms PictureBox control to display the graphic images. My intent was that the 1.0 version of the program would no longer use the PictureBox in order to support animated GIFs to be among the graphic items. My prior experience was that simply using a PictureBox.Image property would not properly display an animated GIF, but only show the first frame.
So, I read somewhere that with WinForms 2.0, that was no longer the case. So, I figured I'd try one out before going too far. I put an AniGif in the rotation, and it loads up. I wait the 5-seconds I know this particular GIF has until frame 2 comes up, and, lo and behold, an uncatchable exception:
A generic error occurred in GDI+
Taking a peek at the stack trace from the exception, I find that there are, in fact, WinForms methods being called on my behalf at the appropriate time, trying to display the next frame of the image. But they are not working. "Generic Error" and all. How helpful...
Google to the rescue! Only; not so much. Lots of talk about the GIF being 'invalid' (mine absolutely is not). Others incorrectly state my previous belief, "You can not display animted GIFs in a picturebox via the Image property!". Still others get totally sidetracked by the uncatchable nature of this exception, since it is triggered by code in the message loop, and not directly by user code.
On a lark, I find myself reading the documentation on the PictureBox on MSDN, seeing if I can find anything related. I'm just clicking around to see what's what, and I find myself reading about the Image class itself, and it's Image.FromStream() method, which is how I am creating the Image object to set the PictureBox.Image property to from the file. So, buried in the documentation of this function, is this little gem of information:
You must keep the stream open for the lifetime of the Image.
Of course, being a good .NET Programmer, I dispose of every IDisposable object when I am done with it. Stream objects count. The Stream is opened, the Image object created and set to the PictureBox, and then the Stream is closed (via 'using'). For images which are not animated, that works perfectly fine. However, when WinForms is automatically attempting to load the next frame of an animated GIF which was loaded from a stream, it tries to re-read the image from the stream.
Because I can not keep the FileStream open, which would lock the image file itself (preventing concurrent management of that file, which is likely to be needed), my solution was to create a MemoryStream object with Form scope, and then simply copy the FileStream's data to it, close the FileStream, then use the MemoryStream to load the Image object. This leaves the file free, and keeps the Image data available. Each time the image rotates to another one, of course I dispose of the existing MemoryStream.
What About Obscurity
I said this was more obscure than it seems it should be. My guess is the reason for that is because (gasp) many people are not properly disposing of things such as the FileStreams explicitly (or implicitly) created when creating Image objects to set to the PictureBox.Image property, so they never see this problem. Eek!