Image.Save(..) throws a GDI+ exception because the memory stream is closed

107,328

Solution 1

As it's a MemoryStream, you really don't need to close the stream - nothing bad will happen if you don't, although obviously it's good practice to dispose anything that's disposable anyway. (See this question for more on this.)

However, you should be disposing the Bitmap - and that will close the stream for you. Basically once you give the Bitmap constructor a stream, it "owns" the stream and you shouldn't close it. As the docs for that constructor say:

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

I can't find any docs promising to close the stream when you dispose the bitmap, but you should be able to verify that fairly easily.

Solution 2

A generic error occurred in GDI+. May also result from incorrect save path! Took me half a day to notice that. So make sure that you have double checked the path to save the image as well.

Solution 3

Perhaps it is worth mentioning that if the C:\Temp directory does not exist, it will also throw this exception even if your stream is still existent.

Solution 4

Copy the Bitmap. You have to keep the stream open for the lifetime of the bitmap.

When drawing an image: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI

    public static Image ToImage(this byte[] bytes)
    {
        using (var stream = new MemoryStream(bytes))
        using (var image = Image.FromStream(stream, false, true))
        {
            return new Bitmap(image);
        }
    }

    [Test]
    public void ShouldCreateImageThatCanBeSavedWithoutOpenStream()
    {
        var imageBytes = File.ReadAllBytes("bitmap.bmp");

        var image = imageBytes.ToImage();

        image.Save("output.bmp");
    }

Solution 5

I had the same problem but actually the cause was that the application didn't have permission to save files on C. When I changed to "D:\.." the picture has been saved.

Share:
107,328

Related videos on Youtube

Pure.Krome
Author by

Pure.Krome

Just another djork trying to ply his art in this mad mad world. Tech stack I prefer to use: Laguage: C# / .NET Core / ASP.NET Core Editors: Visual Studio / VS Code Persistence: RavenDB, SqlServer (MSSql or Postgres) Source control: Github Containers: Docker & trying to learn K&'s Cloud Platform: Azure Caching/CDN: Cloudflare Finally: A Tauntaun sleeping bag is what i've always wanted spaces > tabs

Updated on July 08, 2022

Comments

  • Pure.Krome
    Pure.Krome almost 2 years

    i've got some binary data which i want to save as an image. When i try to save the image, it throws an exception if the memory stream used to create the image, was closed before the save. The reason i do this is because i'm dynamically creating images and as such .. i need to use a memory stream.

    this is the code:

    [TestMethod]
    public void TestMethod1()
    {
        // Grab the binary data.
        byte[] data = File.ReadAllBytes("Chick.jpg");
    
        // Read in the data but do not close, before using the stream.
        Stream originalBinaryDataStream = new MemoryStream(data);
        Bitmap image = new Bitmap(originalBinaryDataStream);
        image.Save(@"c:\test.jpg");
        originalBinaryDataStream.Dispose();
    
        // Now lets use a nice dispose, etc...
        Bitmap2 image2;
        using (Stream originalBinaryDataStream2 = new MemoryStream(data))
        {
            image2 = new Bitmap(originalBinaryDataStream2);
        }
    
        image2.Save(@"C:\temp\pewpew.jpg"); // This throws the GDI+ exception.
    }
    

    Does anyone have any suggestions to how i could save an image with the stream closed? I cannot rely on the developers to remember to close the stream after the image is saved. In fact, the developer would have NO IDEA that the image was generated using a memory stream (because it happens in some other code, elsewhere).

    I'm really confused :(

    • Juan Carlos Oropeza
      Juan Carlos Oropeza almost 7 years
      I got this comment from @HansPassant in another question. You'll get this exception whenever the codec has trouble writing the file. A good debugging statement to add is System.IO.File.WriteAllText(path, "test") before the Save() call, it verifies the basic ability to create the file. You'll now get a good exception that tells you what you did wrong.
    • taynguyen
      taynguyen over 5 years
      You should image2.Save inside using block. I think the originalBinaryDataStream2 was automatically disposed at the end of the using. And that would throw the exception.
  • Pure.Krome
    Pure.Krome over 15 years
    awesome! that's a great reply Jon. Makes perfect sence (and I missed the bit about the stream in the docs). Two Thumbs Up! I'll report back when i've given it a go :)
  • Patrick Szalapski
    Patrick Szalapski over 13 years
    This doesn't work exactly; in your code in ToImage(), local "image" will correctly have a .RawFormat of whatever the original file was (jpeg or png, etc), whereas the return value of ToImage() will unexpectedly have .RawFormat MemoryBmp.
  • Patrick Szalapski
    Patrick Szalapski over 13 years
    Any comments of how to go about this if we want to obey rule CA2000? (msdn.microsoft.com/en-us/library/ms182289.aspx)
  • Jon Skeet
    Jon Skeet over 13 years
    @Patrick: It's simply not applicable - you've transferred ownership of the resource, basically. The closest you could come would be to create a "NonClosingStream" wrapper which ignores the Dispose call. I think I may have one in MiscUtil - not sure...
  • Kirk Broadhurst
    Kirk Broadhurst over 12 years
    +1 This exception seems to occur in a variety of scenarios. Invalid path is one I've encountered today.
  • Oxon
    Oxon over 10 years
    Thanks for info @Jon. For me, for some strange reason it was working even with the dispose() in local dev environment but didnt work in production.
  • mason
    mason over 9 years
    I'm glad I saw this, my path was C\Users\mason\Desktop\pic.png. Missing colon! I would have spent forever before I noticed that.
  • Roemer
    Roemer about 8 years
    Incorrect also means that a folder you want to save the image to does not exist.
  • Nyerguds
    Nyerguds over 6 years
    Not sure how the RawFormat matters much, though. If you want to use that, retrieve it from the object somewhere along the way, but in general, save as whatever type you actually want to have.
  • Nyerguds
    Nyerguds over 6 years
    WPF images have a specific parameter BitmapCacheOption.OnLoad to disconnect them from the loading source.
  • mkb
    mkb over 6 years
    Thank you @Nyerguds, until your comment I failed to ask right questions
  • Ivan Rubinson
    Ivan Rubinson over 3 years
    Welcome to StackOverflow! Please consider adding some explaination text to go with the code. A good place to start: why do it this way and not another?