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.
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:
The native method resides in gdiplus.dll. So long story short, get your production server patched, .NET framework repaired. More details:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With