tags:

views:

31

answers:

3

I'm tracking down a pesky problem and I've narrowed down the problem down and realized it only happens when I'm dealing with an Image instance returned by Image.FromStream(). I have a utility method that returns an Image instance from a file using a Stream so I don't have the file handle left open. Here is that utility method (nothing special):

public static Image ImageFromFileReleaseHandle(string filename)
{
    using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
    {
        return Image.FromStream(fs);
    }
}

When I try to save an Image loaded from the above method I'm getting an InteropServices.ExternalException "A generic error occurred in GDI+.". The following code example will demonstrate this:

private void button6_Click(object sender, EventArgs e)
{
    var filename = @"D:\My Documents\My Pictures\2010-03-27 hangover hike.jpg";

    //  Get an Image instance
    Image image;
    using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
    {
        image = Image.FromStream(fs);
    }

    //  Save to a temp file - this is the code that throws the exception
    image.Save(Path.GetTempFileName());
}

If I load the image using Image.FromFile() I can save no problem:

private void button6_Click(object sender, EventArgs e)
{
    var filename = @"D:\My Documents\My Pictures\2010-03-27 hangover hike.jpg";

    //  Get an Image instance
    Image image = Image.FromFile(filename);

    //  Save to a temp file - this is the code that throws the exception
    image.Save(Path.GetTempFileName());
}

I can't think of any additional information that would be helpful. Hopefully my code examples are simple enough that you can clearly see the problem.

Thanks, Steve

+1  A: 

According to the documentation:

You must keep the stream open for the lifetime of the Image.

Try calling Save inside the using block to verify that no exception is thrown if the stream is still open.

adrift
@adrift: saving before the stream is disposed does work. Thanks for the suggestion.
Steve K
A: 

try this instead

//  Get an Image instance
    Image image;
    using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
    {
        image = Image.FromStream(fs);
        //  Save to a temp file - this is the code that throws the exception
        image.Save(Path.GetTempFileName());
    }

I think the using block is giving you issues

msarchet
Thanks for the suggestion and you are correct that leaving the stream open will allow me to save. However this won't work in my application because the code that creates the Image instance and the code the saves it aren't related. I don't want to leave a FileStream open because there is no guarantee it will be closed. What I need is a pure memory Image instance with NO dependencies to stream or files.
Steve K
@user47: Then copy the image from your FileStream into a MemoryStream.
Allon Guralnek
@Allon: I thought the same thing after I left my last comment and just did an experiment and it does seem to work. I now need to research the implications of leaving a MemoryStream open... Thanks for the suggestion
Steve K
@SteveK: MemoryStream is a completely managed object, so there are no implications. See [Jon Skeet's answer](http://stackoverflow.com/questions/234059/is-a-memory-leak-created-if-a-memorystream-in-net-is-not-closed/234257#234257) about it. Also, you could always create a class `MemoryImage` which could contain both the `Image` and the `MemoryStream` and its `Dispose` method would dispose of both, cleaning up everything.
Allon Guralnek
A: 

Based on suggestions and comments so far and my own additional reading I think I have a working solution. Basically the problem is that I can't close the Stream. Fine, but I also can't leave a FileStream open with no guarantee it will be closed properly.

Instead I'm reading the file into a buffer then creating a MemoryStream and passing that to Image.FromStream().

var filename = @"D:\My Documents\My Pictures\2010-03-27 hangover hike.jpg";

byte[] imageData = FileUtils.ReadWholeFileBytes(filename);
Image image = Image.FromStream(new MemoryStream(imageData));

//  dump the buffer to test if this upsets the Memory Stream.  I need to read more
//  about MemoryStream and how it may keep the GC from cleaning up it's underlying data
imageData = null;        

//  Save to a temp file
image.Save(Path.GetTempFileName());

Thanks all for your suggestions and questions.

Steve K
UPDATE: My solution is only a half-solution. It has the unfortunate consequence of resulting in an Image with an ImageFormat set to MemoryBmp. This means that you will need to explicitly set the ImageFormat when saving or else it will be saved with the PNG encoder.
Steve K