We have started facing an issue in the past months where the method System.Drawing.Bitmap.Save(string filename)
stops working after the server has been running for several hours. The hours after which it starts failing are proportional to the server load.
This code involved with that call has been working perfectly well for several years, it gets and image from the user, resizes it and saves it to disk. The application is an ASP.Net application developed .Net Framework 3.5.
When the call to the Bitmap.Save
fails, it raises the infamous exception:
ERROR MESSAGE: Error genérico en GDI+.
STACK TRACE: en System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
en System.Drawing.Image.Save(String filename, ImageFormat format)
en System.Drawing.Image.Save(String filename)
I've been reading a lot of posts about that exception, and I am fully aware that this exception is raised in many different situations. It is a generic exception that is raised for many different reasons (i.e when the application has no permisison to write to a folder, when the code is trying to save the image in the same file it is in reading from, etc) but none of those scenarios is our case.
When we realied the method was failing, we had just released a new version of our application, so we initially thought the error could be in our code (despite that part had not been changed, we thought we had changed something else that was interfering and producing the error), but the more we have investigated the less we understand when the error happens.
It is also important to mention that at the same time the error appeared, we also updated our servers with the latest windows updates, where I see also two suspicious ones:
KB2929755 http://support.microsoft.com/kb/2929755/en-us
Out of memory when you load some image resources in a Windows application
KB2957503 http://support.microsoft.com/kb/2957503/en-us
MS14-036: Description of the security update for Windows 7, Windows Server 2008 R2, Windows Server 2008, Windows Vista, and Windows Server 2003: June 10, 2014
This ones updates the GDIplus.dll on the server to avoid some security issues.
My feeling is that something on the server is not being released, like if some piece of code were leaking handlers or memory, and at some point the server runs out of them and can no longer save the Bitmap. When the error appears, any call to Bitmap.Save from any part of the application raises that exception. Rebooting the IIS solves the problem until the next time, however, that's not the case if we only restart the application pool.
If it can help in any way, we are using the HiQPdf library (version 6.8.0.0) to convert some HTML documents to PDF. This tool creates a new process for every conversion and killing it when finished, and supposedly releasing all the resources used in a proper way.
Is someone exeperiencing any similar issues?
Has someone had any issues with the Windows updates described?
I forgot to mention also that we tried resizing the images using WPF APIs, instead of GDI+ but we were facing the same problem, after several hours the server started raising an error (although this time the message was Excepción de HRESULT: 0x88982F8A), so we reverted it back to our original code.
Any help would be appreciated!
EDIT:
Actually, the code that peforms the image resizing and saving is the following. All variables are within using blocks, but for the casted image to a Bitmap, which I assume gets disposed anyway:
using (Image originalLogo = Image.FromStream(fuLogo.PostedFile.InputStream))
{
using (Image resizedLogo = ImageUtil.ResizeImage(originalLogo, maxWidth, maxHeight))
{
((System.Drawing.Bitmap)resizedLogo).Save(newFilePath);
}
}
Could the casting to Bitmap require creating an intermediate variable to force its dispose too, or that is not needed?
using (Image originalLogo = Image.FromStream(fuLogo.PostedFile.InputStream))
{
using (Image resizedLogo = ImageUtil.ResizeImage(originalLogo, maxWidth, maxHeight))
{
using (Bitmap bitmap=((System.Drawing.Bitmap)resizedLogo))
{
bitmap.Save(newFilePath);
}
}
}
Thanks, Enric
EDIT:
I'm pasting below the code for the ResizeImage method:
public static System.Drawing.Image ResizeImage(System.Drawing.Image originalImage, int maxWidth, int maxHeight)
{
int imgWidth = originalImage.Width;
int imgHeight = originalImage.Height;
if (originalImage.Height<maxHeight && originalImage.Width<maxWidth) return originalImage;
double widhtRatio = ((double)maxWidth) / ((double)imgWidth);
double heightRatio = ((double)maxHeight) / ((double)imgHeight);
int targetWidth, targetHeight;
if (widhtRatio < heightRatio)
{
targetWidth = (int)(imgWidth * widhtRatio);
targetHeight = (int)(imgHeight * widhtRatio);
}
else
{
targetWidth = (int)(imgWidth * heightRatio);
targetHeight = (int)(imgHeight * heightRatio);
}
//Not all pixel formats are supported, therefore, using the
//originalImage pixel format raises an exception if the pixel format is not supported.
//By not specifying it, the Bitmap constructor will use one compatible with the FromImage method.
//For more info see: http://forums.asp.net/p/1195630/2066153.aspx
// Image resizedImage = new Bitmap(targetWidth, targetHeight, originalImage.PixelFormat);
System.Drawing.Image resizedImage = new Bitmap(targetWidth, targetHeight);
using (Graphics riGraphics = Graphics.FromImage(resizedImage))
{
riGraphics.CompositingQuality = CompositingQuality.HighQuality;
riGraphics.SmoothingMode = SmoothingMode.HighQuality;
Rectangle rectangle = new Rectangle(0, 0, targetWidth, targetHeight);
riGraphics.DrawImage(originalImage, rectangle);
riGraphics.Flush();
}
return resizedImage;
}
I've got an absolute stab in the dark for you:
In your application code, is the bitmap object wrapped in a using
or explicitly disposed of after the Save() is called? I've seen hard to trace exceptions thrown from memory leaks in the past when dealing with IDisposible objects when they're used in repetition and don't get properly disposed of. These circumstances always seemed to be intermittent like what you're describing and took tons of digging before realizing the solution was as simple as calling Bitmap.Dispose()
(or putting it back after having it accidentally deleted during a re-factor or code update).
However unlikely, because it sounds like your code worked before having windows updates applied...I thought I'd throw that out there.
User contributions licensed under CC BY-SA 3.0