Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A Generic Error occurred in GDI+ when saving bitmap to MemoryStream

Tags:

c#

gdi+

I have some code that is working perfectly on several machines (development, QA, UAT). Unfortunately, on production I'm getting "A Generic Error occurred in GDI+" on the bmp.Save(ms, ImageFormat.Png); As a result, I'm assuming you won't be able to reproduce the problem, but maybe someone can spot my error.

A few notes, I've searched around a lot for common solutions, note that this is saving to a MemoryStream so the file permission issues most people suggest do not apply, nor does the "bmp is locked while open" solution because again, I'm writing somewhere else. Finally, it's not because png requires a seekable stream because MemoryStream is seekable.

Note, if I change it to ImageFormat.Jpeg it works fine. I'm only having a problem with PNGs. I found mention of the registry key HKEY_CLASSES_ROOT\CLSID\{FAE3D380-FEA4-4623-8C75-C6B61110B681} potentially being the problem due to permissions. As a result, I set the key to allow Everyone to have read access to this key, no change.

public static MemoryStream GenerateImage(string text)
{
    MemoryStream ms = new MemoryStream();
    using (Bitmap bmp = new Bitmap(400,400))
    {
        bmp.Save(ms, ImageFormat.Png);
        ms.Position = 0;
    }
    return ms;
}

Here is the full stack trace:

[ExternalException (0x80004005): A generic error occurred in GDI+.]
System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams) +616457
WP.Tools.Img.GenerateImage(String text) +383

Note: my question already enumerates the solutions in the proposed duplicate. None are the issue. If they were it would fail for JPEG as well.

like image 510
dmeglio Avatar asked Dec 06 '15 18:12

dmeglio


1 Answers

The .NET reference source code here, in the save-to-stream case, gets a status value from a call to native method GdipSaveImageToStream:

public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams) {

    ...

    if (!saved)
    {
        status = SafeNativeMethods.Gdip.GdipSaveImageToStream(new HandleRef(this,nativeImage),new UnsafeNativeMethods.ComStreamFromDataStream(stream),ref g,new HandleRef(encoderParams, encoderParamsMemory));
    }

    ...

}

this status value is the only API return value used to throw an exception from that method. When we look further into the StatusException function which decides what kind of exception to throw based on the status code, we find only a single possible status value to result in the ExternalException you got (from Gdiplus.cs, line 3167):

switch (status)
{
    case GenericError:
        return new ExternalException(SR.GetString(SR.GdiplusGenericError), E_FAIL);

    ...
}

0x80004005 is "unspecified error", and the SR.GdiplusGenericError is the text "A generic error occurred in GDI+." you got. This rules out several other possibilities we might suspect (which would result in different exceptions), namely:

  • out of memory
  • object busy
  • insufficient buffer
  • win32error
  • valueoverflow
  • unknownimageformat
  • property not found/not supported
  • unsupportedgdiplusversion

The native method resides in gdiplus.dll. So long story short, get your production server patched, .NET framework repaired. More details:

  1. compare the versions of that dll in %windir%\system32 between known-good machines and the production machine. The dll has hundreds of dependencies, so even if the version of the file itself matches, see to get your OS patched.
  2. the built-in codec for the PNG format is part of the WIC component of windows and resides in WindowsCodecs.dll and WindowsCodecsExt.dll - check the versions of those libraries as well. The registry key you mentioned should also point to WindowsCodecsExt.dll.
  3. not based on recherche, just ideas: do you access the production server through virtualization/remote desktop connection? Try a console session if you can. Try different screen resolutions and color depths. Try debug/release builds. Make sure you actually have cleared the DEBUG check in your release build configuration. Try builds x64 and MSIL. If you use NGEN on production, try without.
like image 76
Cee McSharpface Avatar answered Oct 14 '22 04:10

Cee McSharpface