123

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 :(

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
Pure.Krome
  • 84,693
  • 113
  • 396
  • 647
  • 1
    I got this comment from @HansPassant in another [question](https://stackoverflow.com/questions/33331370/a-generic-error-occurred-in-gdi-exception-while-uploading-an-image-file). 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. – Juan Carlos Oropeza Sep 11 '17 at 16:01
  • 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. – taynguyen Nov 26 '18 at 13:50

17 Answers17

191

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.

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 4
    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 :) – Pure.Krome Dec 03 '08 at 11:25
  • Any comments of how to go about this if we want to obey rule CA2000? (msdn.microsoft.com/en-us/library/ms182289.aspx) – Patrick Szalapski Jan 13 '11 at 00:41
  • 1
    @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... – Jon Skeet Jan 13 '11 at 06:28
  • 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. – Oxon Oct 01 '13 at 16:33
108

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.

displayName
  • 13,888
  • 8
  • 60
  • 75
Houman
  • 1,381
  • 1
  • 8
  • 10
  • 5
    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. – mason Feb 17 '15 at 21:00
  • 5
    Incorrect also means that a folder you want to save the image to does not exist. – Roemer May 09 '16 at 20:02
14

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.

Rojzik
  • 882
  • 8
  • 8
4

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.

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");
    }
Community
  • 1
  • 1
Brian Low
  • 11,605
  • 4
  • 58
  • 63
  • 1
    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 Jan 13 '11 at 00:39
  • 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 Mar 16 '18 at 12:41
3

You can try to create another copy of bitmap:

using (var memoryStream = new MemoryStream())
{
    // write to memory stream here

    memoryStream.Position = 0;
    using (var bitmap = new Bitmap(memoryStream))
    {
        var bitmap2 = new Bitmap(bitmap);
        return bitmap2;
    }
}
2

This error occurred to me when I was trying from Citrix. The image folder was set to C:\ in the server, for which I do not have privilege. Once the image folder was moved to a shared drive, the error was gone.

Jay K
  • 21
  • 1
1

A generic error occurred in GDI+. It can occur because of image storing paths issues,I got this error because my storing path is too long, I fixed this by first storing the image in a shortest path and move it to the correct location with long path handling techniques.

S.Roshanth
  • 1,499
  • 3
  • 25
  • 36
1

I was getting this error, because the automated test I was executing, was trying to store snapshots into a folder that didn't exist. After I created the folder, the error resolved

P.Lisa
  • 11
  • 1
0

One strange solution which made my code to work. Open the image in paint and save it as a new file with same format(.jpg). Now try with this new file and it works. It clearly explains you that the file might be corrupted in someway. This can help only if your code has every other bugs fixed

Vinothkumar
  • 235
  • 1
  • 7
0

It has also appeared with me when I was trying to save an image into path

C:\Program Files (x86)\some_directory

and the .exe wasn't executed to run as administrator, I hope this may help someone who has same issue too.

Ali Ezzat Odeh
  • 2,093
  • 1
  • 17
  • 17
0

For me the code below crashed with A generic error occurred in GDI+on the line which Saves to a MemoryStream. The code was running on a web server and I resolved it by stopping and starting the Application Pool that was running the site.

Must have been some internal error in GDI+

    private static string GetThumbnailImageAsBase64String(string path)
    {
        if (path == null || !File.Exists(path))
        {
            var log = ContainerResolver.Container.GetInstance<ILog>();
            log.Info($"No file was found at path: {path}");
            return null;
        }

        var width = LibraryItemFileSettings.Instance.ThumbnailImageWidth;

        using (var image = Image.FromFile(path))
        {
            using (var thumbnail = image.GetThumbnailImage(width, width * image.Height / image.Width, null, IntPtr.Zero))
            {
                using (var memoryStream = new MemoryStream())
                {
                    thumbnail.Save(memoryStream, ImageFormat.Png); // <= crash here 
                    var bytes = new byte[memoryStream.Length];
                    memoryStream.Position = 0;
                    memoryStream.Read(bytes, 0, bytes.Length);
                    return Convert.ToBase64String(bytes, 0, bytes.Length);
                }
            }
        }
    }
mortb
  • 9,361
  • 3
  • 26
  • 44
0

I came across this error when I was trying a simple image editing in a WPF app.

Setting an Image element's Source to the bitmap prevents file saving. Even setting Source=null doesn't seem to release the file.

Now I just never use the image as the Source of Image element, so I can overwrite after editing!

EDIT

After hearing about the CacheOption property(Thanks to @Nyerguds) I found the solution: So instead of using the Bitmap constructor I must set the Uri after setting CacheOption BitmapCacheOption.OnLoad.(Image1 below is the Wpf Image element)

Instead of

Image1.Source = new BitmapImage(new Uri(filepath));

Use:

var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filepath);
image.EndInit();
Image1.Source = image;

See this: WPF Image Caching

mkb
  • 1,106
  • 1
  • 18
  • 21
  • 1
    WPF images have a specific parameter `BitmapCacheOption.OnLoad` to disconnect them from the loading source. – Nyerguds Mar 16 '18 at 12:44
  • Thank you @Nyerguds, until your comment I failed to ask right questions – mkb Mar 18 '18 at 11:53
0

Try this code:

static void Main(string[] args)
{
    byte[] data = null;
    string fullPath = @"c:\testimage.jpg";

    using (MemoryStream ms = new MemoryStream())
    using (Bitmap tmp = (Bitmap)Bitmap.FromFile(fullPath))
    using (Bitmap bm = new Bitmap(tmp))
    {
        bm.SetResolution(96, 96);
        using (EncoderParameters eps = new EncoderParameters(1))
        {   
            eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
            bm.Save(ms, GetEncoderInfo("image/jpeg"), eps);
        }

        data = ms.ToArray();
    }

    File.WriteAllBytes(fullPath, data);
}

private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
        ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();

        for (int j = 0; j < encoders.Length; ++j)
        {
            if (String.Equals(encoders[j].MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase))
                return encoders[j];
        }
    return null;
}
BogdanRB
  • 158
  • 1
  • 5
0

I used imageprocessor to resize images and one day I got "A generic error occurred in GDI+" exception.

After looked up a while I tried to recycle the application pool and bingo it works. So I note it here, hope it help ;)

Cheers

Nick Hoàng
  • 417
  • 2
  • 6
0

I was getting this error today on a server when the same code worked fine locally and on our DEV server but not on PRODUCTION. Rebooting the server resolved it.

Kris
  • 514
  • 3
  • 6
  • 19
0
public static byte[] SetImageToByte(Image img)
    {
        ImageConverter converter = new ImageConverter();
        return (byte[])converter.ConvertTo(img, typeof(byte[]));
    }
public static Bitmap SetByteToImage(byte[] blob)
    {
        MemoryStream mStream = new MemoryStream();
        byte[] pData = blob;
        mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
        Bitmap bm = new Bitmap(mStream, false);
        mStream.Dispose();
        return bm;
    }
  • 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? – Ivan Rubinson Mar 22 '21 at 04:42