Search code examples
c#imagedisposeusing

Image PropertyItems and disposed MemoryStream


I'm loading an Image from a byte[] using MemoryStream and getting information about the image by inspecting it's ProperyItems. In the process of doing this though, I noticed some odd behaviour where some of the image's PropertyItems were disappearing. After much debugging I finally figured out that this was being caused by the MemoryStream being disposed.

MemoryStream ms0 = new MemoryStream(imageBytes);
Image img0 = Image.FromStream(ms0);
Console.Out.WriteLine("Without using, Image propertyIDs: ");
foreach (int itemId in img0.PropertyIdList)
    Console.Out.Write(itemId + ", ");
Console.Out.Write("\n");

Image img1 = null;
using (MemoryStream ms1 = new MemoryStream(imageBytes))
{
    img1 = Image.FromStream(ms1);
}
Console.Out.WriteLine("Outside using, Image propertyIDs: ");
foreach (int itemId in img1.PropertyIdList)
    Console.Out.Write(itemId + ", ");
Console.Out.Write("\n");

Output:

Without using, Image propertyIDs: 
254, 256, 257, 258, 259, 262, 269, 273, 274, 277, 278, 279, 282, 283, 284, 296, 
Outside using, Image propertyIDs: 
254, 256, 257, 258, 259, 262, 274, 277, 278, 284, 296, 

So it appears that at least some of the PropertyItems are directly backed by the contents of the MemoryStream and the solution is not to dispose it, or am I wrong?

In the process of debugging this issue though I've noticed something else odd, if I access the PropertyIdList (or anything related to the images PropertyItems) inside the using block, the PropertyItems won't disappear after the MemoryStream is disposed.

Image img2 = null;
using (MemoryStream ms2 = new MemoryStream(imageBytes))
{
    img2 = Image.FromStream(ms2);
    int[] tmp = img2.PropertyIdList;
}
Console.Out.WriteLine("Outside using with PropertyIdList access, Image propertyIDs: ");
foreach (int itemId in img2.PropertyIdList)
    Console.Out.Write(itemId + ", ");
Console.Out.Write("\n");

Output:

Outside using with PropertyIdList access, Image propertyIDs: 
254, 256, 257, 258, 259, 262, 269, 273, 274, 277, 278, 279, 282, 283, 284, 296,

I looked at the source for the Image class and the PropertyIdList property doesn't appear to be retaining a local copy of the PropertyItems data, so why are the PropertyItems retained after the MemoryStream is disposed in this situation?


Solution

  • Disposing a MemoryStream is in general a fairly useless thing to do. It doesn't have any disposable resources itself, it is just memory and that's already managed by the garbage collector. It only matters if you've used the BeginRead/Write() methods and they are not yet completed, something you just never do.

    It does however set the CanRead() property to false. And that's quite lethal to the Bitmap object you loaded from the MemoryStream.

    What happens next, when you keep using the Bitmap, is fairly unpredictable. GDI+ requires that the stream stays readable, it may use it later, reading the bitmap data in a lazy fashion. Most typically when the bitmap gets painted and that tends to crash your program fairly reliably with a "generic error".

    You found another corner case, seems it just thinks there are no more properties. This is not otherwise that mysterious, you really did close the stream so there are no more properties it could possibly read. That it doesn't generate an exception for that is sloppy but not uncommon for GDI+.

    Just get rid of the using statement, it doesn't do anything useful. If you fret about disposing the stream anyway then you must do so after you won't use the Bitmap object anymore.