8

My application graphics engine throws these exceptions. They are all consumed by an empty catch block. In the early days I found one that was not trapped (associated with pen widening as I recall). I surrounded it with try and an empty catch block. It seems that these exceptions have no effect on the drawing produced. I have done some reading on this without really understanding or getting to the bottom of it.

So to my questions:

  1. Why would these be thrown if they can be consumed safely? and
  2. Is it safe to ignore them? I worry that each one is having some hidden effect. I have memory leaks which I have never found for example.
ScruffyDuck
  • 2,606
  • 3
  • 34
  • 50
  • Are you disposing stuff like Pens, Brushes, Graphics, Images after using them? – ba__friend Jun 28 '11 at 12:08
  • 5
    They get thrown to tell you that your code is broken. Catching them with an empty catch clause does not fix your code, it merely shoots the messenger. – Hans Passant Jun 28 '11 at 12:10
  • 2
    GDI+ sometimes throws an out-of-memory exception when it's actually an invalid argument. Not sure if that's relevant here. – CodesInChaos Jun 28 '11 at 12:11

5 Answers5

27

I've seen System.Drawing throw OutOfMemoryExceptions even when it's not out of memory. Some GDI+ function is apparently just returning a stupid error code.

IIRC, you will get an OutOfMemoryException if you try to use a LinearGradientBrush to fill a rectangle whose width or height is zero. There may be other conditions too, but this is the main one we ran into.

In that case, there's no need for a try/catch. Just add an if statement to your drawing code, and don't fill the rectangle if the width or height is zero.

Update: According to the comments on this answer, it can also occur if you try to load a corrupted image file. For that, you would have no choice but to do try/catch.

You're probably safe catching OutOfMemoryExceptions from GDI+, but keep the try blocks as small as possible. Consider logging the exceptions, so you can analyze the logs and add defensive code where possible. You don't want to mask a real OutOfMemoryException, but you don't want a stupid GDI+ error code to crash your app either.

Community
  • 1
  • 1
Joe White
  • 94,807
  • 60
  • 220
  • 330
  • There is no image file involved it is drawing. Great idea to use logging. The application has a logger so I will add some logging to the catch clauses and see what shows up. If it looks nasty I will add some defensive code. – ScruffyDuck Jun 28 '11 at 12:40
  • Thank you from 2016! You saved my day! – Genius Nov 06 '16 at 17:59
  • What if you need to draw a gradient line that is vertical or horizonal? Then the rectangle would have a zero width or height! – AaronF Apr 03 '17 at 23:22
  • @AaronF How do you figure? You need a width of at least one pixel if you want to draw a line. If you have a width of zero, then by definition, you're modifying zero pixels, i.e., you're not drawing anything. – Joe White Apr 04 '17 at 15:50
  • @Joe White `new LinearGradientBrush(new Point(10, 0), new Point(10, 100), Color.Blue, Color.Red)` The two points are in line with each other and have height 100, width 0. That hasn't caused an issue thus far. – AaronF Apr 07 '17 at 01:58
  • @AaronF You're defining a gradient, not filling a rectangle. The error happens when you fill a zero-width or zero-height rectangle, which would be a no-op anyway. – Joe White Apr 07 '17 at 16:03
  • I've seen this exception when drawing a zero-width bezier with `Graphics.DrawBezier`. – Drew Noakes May 19 '17 at 19:37
  • indeed about the OutOfMemoryException for zero width/height. Give this ticket an upvote: https://visualstudio.uservoice.com/forums/121579-visual-studio-ide/suggestions/31985866-gdi-incorrect-exception-for-lineargradientbrush – juFo Oct 23 '17 at 10:12
3

It's a pretty bad exception: http://msdn.microsoft.com/en-us/library/system.outofmemoryexception.aspx .. not enough memory to continue execution of the program.

You'll often find if you have so much allocated that 'simple' operations/allocations throw this message, the app will crash soon after. If it's one massive allocation that's failing, you may be able to continue.

If the app does anything important, you should try to close things down gracefully.

To explicitly answer your questions:

  1. They're thrown so the app has a chance to react/recover: some memory allocations (10GB worth of objects) might be expected to fail in many situations, perhaps a one-line app crash (int[] x = new int[5368709120]; equivalent) should really throw an exception rather than crash everything

  2. There should be no hidden effect, but if one allocation fails, then perhaps the next time you want a string or other useful object in some small way allocated for general operation of the app: things might become unstable. That said depending on the environment, you might get this exception at any time..

Edit: Anyone reading this should also consider that apparently GDI+ throws this exception for other reasons too.

Kieren Johnstone
  • 41,277
  • 16
  • 94
  • 144
  • Thanks - to be honest we have never had the application fall over. We have around 30,000 users and this has not been reported. However if I look at the Output log in VS I see several dozen 'First Chance Exceptions' of this type that are being consumed by the empty catch clause(s) – ScruffyDuck Jun 28 '11 at 12:11
  • 3
    OOM means something else when GDI+ throws it. GDI+ exceptions are fairly sloppy. – Hans Passant Jun 28 '11 at 12:11
  • Normally that's true, but GDI+ throws OutOfMemoryExceptions even when it's not out of memory. I've seen it. Some function is returning a bad error code or something. – Joe White Jun 28 '11 at 12:12
  • Thanks guys. I am going to add some logging as suggested below and see what comes up. – ScruffyDuck Jun 28 '11 at 12:41
0

I tried the solution 'Joe White' suggested and that was it. It threw a OutOfMemoryException because the rectangle's width and height were 0. In my case the window was minimized when the exception occurred.

Here is an example;

  Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)

    Using e
        ##Add conditions to avoid OutOfMemoryException##
        If (Not ClientRectangle.Width = 0) And (Not ClientRectangle.Height = 0) Then
            Using rect As GraphicsPath = New GraphicsPath()
                rect.AddRectangle(ClientRectangle)

                Using gb As New PathGradientBrush(rect)
                    gb.WrapMode = WrapMode.Tile
                    gb.SurroundColors = New Color() {GradientColors(1), GradientColors(0), GradientColors(2)}
                    gb.CenterColor = GradientColors(0)
                    gb.SetSigmaBellShape(0.5F)
                    e.Graphics.FillPath(gb, rect)
                End Using
            End Using
        End If
    End Using

End Sub
Begi
  • 1
  • 1
0

I had this line of code:

bgpart = Primary.BackGround.Clone(rect, Primary.BackGround.PixelFormat);

That actually copies a part of the existing background. The purpose was to copy the part of the background my starship was drawn on, so the starship can be moved and, each time, the proper background to be drawn. Simply put, 'something' moves on a standard background, and the background remains.

Then this 'Out of memory' error of system.drawing start to manifest sometimes. When i focus on it, i realize that it has nothing to do with use up all memory. It should be called 'Out of memory RANGE', because what i found out is that on error cases, where i manage to emulate exactly, i asked him to read an image part that was actually outside the image itself, so the memory i ask him to read was not found into the memory of the object. Simply, the error raises when i moved the starship that was already out of the screen and into the screen, so ask him to copy a background non existent.

I replace my code with:

    Bitmap bgpart = null;
    try 
    {
        bgpart = Primary.BackGround.Clone(rect, Primary.BackGround.PixelFormat);
    } 
    catch(Exception e)
    {
        bgpart = Primary.BackGround.Clone(new Rectangle(0, 0, b.Width, b.Height), Primary.BackGround.PixelFormat);
    }

So actually i sent him to read a surely existing part, and problem solved. In my case its ok because i try to read an non existent image part to draw it outside the screen - not needed anyways.

So these errors are (also) thrown from system.drawing when it is forced to read memory not supposed to. If it is safe to bypass, that depends on usage of that readings.

In my case it was safe because i was trying to read and write non existent image parts.

-3

I'll tell you how I solved the problem. Use windows os

  1. Open the Print & Scanner window.

  2. Uncheck the box labeled Allow Windows to manage my default printer.

  3. Set the printer you want to use as the default printer.

lsh
  • 1