Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reproducible crash using Graphics.DrawArc

Tags:

.net

graphics

Our software renders technical drawings. One particular drawing made the application crash every time with an OutOfMemoryException. Upon investigation, nothing seemed out of the ordinary; the app didn't request a lot of memory, didn't use a lot of handles. I tried catching the exception and the app finished the drawing without throwing another one. In fact, there was always only the one OutOfMemoryException, and it was always the same graphics primitive that caused it.

The following code is the minimum required to cause this particular crash. It seems that the exact combination of image size, pen style and coordinates causes the exception. Rounding the coordinates down to three decimals makes it disappear, as does making the graphics dimensions smaller or using a pen without dashing.

using (Bitmap b = new Bitmap(200, 200))
{
  using (Graphics g = Graphics.FromImage(b))
  {
    using (Pen p = new Pen(Color.Black))
    {
      p.DashPattern = new float[]{10.0f, 2.0f};

      RectangleF r = new RectangleF(
        BitConverter.ToSingle(new byte[]{0xD3, 0x56, 0xB3, 0x42}, 0),
        BitConverter.ToSingle(new byte[]{0x87, 0x2D, 0x17, 0x43}, 0),
        BitConverter.ToSingle(new byte[]{0xE2, 0x81, 0xD1, 0x3F}, 0),
        BitConverter.ToSingle(new byte[]{0xE2, 0x81, 0xD1, 0x3F}, 0));
      float st = BitConverter.ToSingle(new byte[]{0x6B, 0xF6, 0x1A, 0x42}, 0);
      float sw = BitConverter.ToSingle(new byte[]{0x6D, 0x33, 0x4F, 0x40}, 0);

      g.DrawArc(p, r, st, sw);
    }
  }
}

In this case it's not complicated to create a workaround, but I was wondering if someone had an explanation for this.

like image 336
waldrumpus Avatar asked Feb 18 '13 13:02

waldrumpus


1 Answers

System.Drawing is a wrapper for GDI+, an unmanaged library that pre-dated the release of .NET by many years. It suffers from a typical problem with a C api, error reporting is rather poor. GDI+ has only 20 distinct error codes, not much for such a large chunk of code. They are also very opaque, a particular doozy is Status::GenericError. A pretty common error that's returned for many possible error conditions that have nothing in common.

System.Drawing could do very little to make these error codes more descriptive, given that they were not descriptive by design. Status::OutOfMemory fits that pattern as well. Very little it could do with that but generate a managed exception that means the same thing. Sadly that wasn't a great choice either since OutOfMemoryException is so very specific in .NET.

Lots of lossage here with few ways to win. You just need to take the poor error reporting of GDI+ at face value and patch around the problem. With a high likelihood in this case that avoiding using a dashed pen for very small arcs will solve your problem. You can't see the dash pattern anyway.

like image 140
Hans Passant Avatar answered Oct 14 '22 02:10

Hans Passant