Jeffnl
Jeffnl

Reputation: 261

GetRoundedRegion exception

I made a custom button with rounded edges, i use CreateRoundRectRgn for this, in the paint event this is called to round all the edges, when i run my program everything works fine until after about one minute, then i get the following exception(the value of p = 0):

enter image description here

one thing to add is that the button is undergoing about 50 paint events per second because of a color fade (blinking)

this is my paint event:

 protected override void OnPaint(PaintEventArgs e)
    {
        this.SuspendLayout();

        this.Region = GetRoundedRegion(this.Width, this.Height);

        base.OnPaint(e);

        if (!this.Enabled)
            e.Graphics.Clear(this.DisabledColor);
        else if (Blinking)
            e.Graphics.Clear(Color.FromArgb(blinkingIntensity, this.BlinkColor));
        else if (this.Pressed)
            e.Graphics.Clear(this.PressedColor);
        else if (this.Selected)
            e.Graphics.Clear(this.SelectedColor);
        else
            e.Graphics.Clear(this.Color);


        using (Pen blackPen = new Pen(Color.FromArgb(150, Color.Black), 2))
        using (Pen whitePen = new Pen(Color.FromArgb(150, Color.Black), 2))
        {
            if (Pressed)
            {
                e.Graphics.DrawLines(blackPen, new[] { new Point(0, this.Height), new Point(0, 0), new Point(this.Width, 0) });
                e.Graphics.DrawArc(blackPen, new Rectangle(0, 0, _Radius, _Radius), 180, 90);

                e.Graphics.DrawLines(whitePen, new[] { new Point(0, this.Height - 2), new Point(this.Width - 2, this.Height - 2), new Point(this.Width - 2, 0) });
                e.Graphics.DrawArc(whitePen, new Rectangle(this.Width - 2 - _Radius, this.Height - 2 - _Radius, _Radius, _Radius), 0, 90);

                using (LinearGradientBrush lb = new LinearGradientBrush(new Point(0, 0), new Point(0, this.Height), Color.FromArgb(150, Color.Black), Color.FromArgb(100, Color.White)))
                {
                    e.Graphics.FillRectangle(lb, 0, 0, this.Width, this.Height);
                }
            }
            else
            {
                e.Graphics.DrawLines(whitePen, new[] { new Point(0, this.Height), new Point(0, 0), new Point(this.Width, 0) });
                e.Graphics.DrawArc(whitePen, new Rectangle(0, 0, _Radius, _Radius), 180, 90);

                e.Graphics.DrawLines(blackPen, new[] { new Point(0, this.Height - 2), new Point(this.Width - 2, this.Height - 2), new Point(this.Width - 2, 0) });
                e.Graphics.DrawArc(blackPen, new Rectangle(this.Width - 2 - _Radius, this.Height - 2 - _Radius, _Radius, _Radius), 0, 90);

                using (LinearGradientBrush lb = new LinearGradientBrush(new Point(0, 0), new Point(0, this.Height), Color.FromArgb(100, Color.White), Color.FromArgb(150, Color.Black)))
                {
                    e.Graphics.FillRectangle(lb, 0, 0, this.Width, this.Height);
                }
            }
        }

        int pressedoffset = 0;
        if (Pressed)
            pressedoffset = 2;

        int maxWidth = this.Width - 20;

        // Set up string.
        string measureString = Text;

        Font stringFont = Font;

        // Set maximum width of string.
        int stringWidth = this.Width - 20;

        SizeF stringSize = new SizeF();

        // Set string format.
        using (StringFormat newStringFormat = new StringFormat())
        {
            newStringFormat.FormatFlags = StringFormatFlags.DisplayFormatControl;

            stringSize = e.Graphics.MeasureString(measureString, stringFont, stringWidth, newStringFormat);
        }
        // Draw string to screen.
        if (CenterText)
            e.Graphics.DrawString(measureString, stringFont, Brushes.White, new PointF(((this.Width / 2) - (stringSize.Width / 2)) + pressedoffset, ((this.Height / 2) - (stringSize.Height / 2)) + pressedoffset));
        else
            e.Graphics.DrawString(measureString, stringFont, Brushes.White, new PointF(10 + pressedoffset, ((this.Height / 2) - (stringSize.Height / 2)) + pressedoffset));

        this.ResumeLayout();
    }

Upvotes: 1

Views: 676

Answers (2)

Hans Passant
Hans Passant

Reputation: 942020

    this.Region = GetRoundedRegion(this.Width, this.Height);

You get this kind of exception when your program leaks handles. Windows lets you use up to 10,000 of them, then it gets sulky and assumes there's something drastically wrong with your program. Something you can see with Taskmgr.exe, Processes tab. View + Select Columns and tick GDI Objects. Might as well include Handles and USER Objects. If that number keeps going up while you use your program then you have a handle leak. Kaboom when it reaches 10,000.

Your use of the Region property is an excellent candidate. It should not be assigned in OnPaint(), that can create a storm of paint events. It needs to be done only once, you should only ever assign it in OnHandleCreated() so it is done just once.

That in itself will fix the quick exception you'll get. But make sure you don't still have a handle leak, you also have the responsibility of releasing the native handle. You must pinvoke DeleteObject() after the window is disposed. Test that by writing a test program that repeatedly creates and disposes the control. And ensure the GDI Objects count is remains stable. Favor using the .NET Region class so this is all automatic, minus the convenience of getting a rounded rectangle created for you.

Upvotes: 1

RoadieRich
RoadieRich

Reputation: 6576

Before I continue, I should state that I am not familiar with the gdi library, so this is a guess at best. I will not be held responsible for any damage to software, hardware, data or sanity that results in using any of the information provided below..

The value 0 for p indicates that the call to CreateRoundedRectRgn has failed. From the function name, it sounds like it creates a region, and returns a handle to its location in memory, as the documentation says you should use DeleteObject to delete a region you are no longer using.

That would suggest to me (and again, this is only a guess) that whatever resources the gdi library is using to create regions is being used up (memory or address space), for instance, hence the CreateRegion call fails.

You have two options from here: either don't create regions so often (i.e. once when you create the control, and then if it moves), or use DeleteObject to delete the region at the end of your Paint method. This might be a good use for an IDisposable, in a using block.

Upvotes: 0

Related Questions