I got a nice little Yellow Screen of Death (YSOD) error on some code running under IIS that worked fine when running on the VS Developer Web Server. The error was "A generic error occurred in GDI+" and you know that if an error is generic, it's sure in the heck not specific.
My little application takes an overhead map that's stored in a local file, does some calculations from user input and draws an X on the map, then returns the resulting dynamically generated image.
There's basically three ways to do images on the server side. Use Native APIs and Interop, which only works in full trust, use System.Drawing, which "isn't supported" or use WPF on the server side, which also, ahem, isn't officially supported. I'm still trying to figure out why, but just to be clear, I used System.Drawing in extremely high traffic sites with no problems. As long as paid close attention to my unmanaged resources, I have never had a problem. I've heard anecdotally of people having trouble with GDI+ (System.Drawing) and switching over to WPF and having no problem with that. As with all things, test what you're doing. There's even some ASP.NET Controls on CodePlex that might help.
Now this post can't answer ALL reasons you're getting "a generic error occurred in GDI+" but it can answer mine. In my particular case (and I think this is the most common mistake) I was saving the composited image as a PNG.
First, I'll show you a little chunk of a code from 5 years ago that took two images and built a single image from them.
public class SomeCheckImageHandler : IHttpHandler{ //some stuff snipped public SomeCheckImageHandler(){} public void ProcessRequest(HttpContext context) { context.Response.ContentType = "image/jpg"; //some stuff snipped GetCheckImageRequest req = new GetCheckImageRequest(); //some stuff snipped, get the params from the QueryString GetCheckImageResponse res = banking.GetCheckImage(req); //some stuff snipped if (res.ImageBack != null) { //merge them into one image using(MemoryStream m = new MemoryStream(res.BackImageBytes)) using(Image backImage = System.Drawing.Image.FromStream(m)) using(MemoryStream m2 = new MemoryStream(res.BrontImageBytes)) using(Image frontImage = System.Drawing.Image.FromStream(m2)) using(Bitmap compositeImage = new Bitmap(frontImage.Width,frontImage.Height+backImage.Height)) using(Graphics compositeGraphics = Graphics.FromImage(compositeImage)) { compositeGraphics.CompositingMode = CompositingMode.SourceCopy; compositeGraphics.DrawImageUnscaled(frontImage,0,0); compositeGraphics.DrawImageUnscaled(backImage,0,frontImage.Height); compositeImage.Save(context.Response.OutputStream, ImageFormat.Jpeg); } } else //just show the front, we've got no back { using(MemoryStream m = new MemoryStream(frontImageBytes)) using(Image image = System.Drawing.Image.FromStream(m)) { image.Save(context.Response.OutputStream, ImageFormat.Jpeg); } } }}
This code generated a JPEG. No problems, runs fine even today. Now, the code I was working on created a PNG and when you create a PNG you need a seekable stream. This little sample uses the BaseHttpHandler Phil and I made.
Note the highlighted lines in this sample. (You'll see the highlights if you view this post on my blog directly, rather than from RSS.)
The Trick for making PNGs: Without the extra intermediate MemoryStream to save to, you won't be able to make PNGs. You can't image.Save() a PNG directly to Response.OutputStream.
namespace FooFoo{ public class MapHandler : BaseHttpHandler public override void HandleRequest(HttpContext context) { string filename = context.Server.MapPath(".") + @"\images\newmap2000.jpg"; using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) { using (System.Drawing.Image image = System.Drawing.Image.FromStream(fs)) { using (Graphics g = Graphics.FromImage(image)) { BrickFinderDrawing drawer = new BrickFinderDrawing(...somedata...); drawer.DrawBrick(g); drawer.DrawName(g); using (MemoryStream stream = new MemoryStream()) { image.Save(stream, ImageFormat.Png); stream.WriteTo(context.Response.OutputStream); } } } } } public override bool RequiresAuthentication { get { return false; } } public override string ContentMimeType { get { return "image/png"; } } public override bool ValidateParameters(HttpContext context) { return true; } }}
Just a little something I wish I remembered when I hit the YSOD. Sure glad I have a blog to put this kind of stuff on! ;)
Related Links
Scott at DevReach in Bulgaria in October
Developer Stand up Comedy - Coding 4 Fun
TechDays/DevDays Netherlands and Belgium:
Posts by Category Posts by Month
Greatest Hits Dev Tools List