С# Pen выбрасывает OutOfMemoryException

Форма с панелью и коробками, нарисованными, пользователь нажимает на ящик, и на мгновение появляется надпись "box hit".

У меня есть рабочая версия кода, но я попытался переставить его, чтобы поэкспериментировать с его эффективностью, и я обнаружил, что в настоящее время я получаю это исключение.

Я не понимаю, почему это исключение происходит, и я хотел бы иметь возможность устранить его, но я не знаю, как это сделать. Я сузил его до какой линии. Но помимо этого я не знаю.

Ошибка возникает при щелчке по ящику. Таким образом, выполняется вырезка панели.

Все элементы управления, например. панель, метка, программно сгенерированы, так что, по всей видимости, возможно, что кто-нибудь сможет скопировать/вставить код, изменив имя класса и воспроизвести исключение, которое я получаю.

Я читал онлайн, что перо может генерировать исключение outofmemory, даже если оно действительно не из памяти, но оно просто бросает это исключение, хотя это все еще не говорит мне, почему и как его можно избежать, чтобы я мог понять, как писать код, где перо не выбрасывает это исключение.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace WindowsFormsApplication17
{
    public partial class Form1 : Form
    {
        Label lbl1;
        Panel panel;
        Timer timer1;

        Point[][] points = new Point[9][];

        GraphicsPath[] gps = new GraphicsPath[9];

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            panel = new Panel();


            lbl1 = new Label();
            timer1 = new Timer();

            this.Controls.Add(panel);

            Label alias = lbl1;
            alias.Text = "box clicked";
            alias.Visible = false;

            panel.BackgroundImageLayout = ImageLayout.Stretch;
            this.Height = 500; this.Width = 500;


            panel.Controls.Add(lbl1);

            panel.MouseDown += p_MouseDown;
            timer1.Tick += timer1_Tick;

            panel.BackColor = Color.White;
            panel.Height = 400;
            panel.Width = 400;


            for (int i = 0; i < 9; i++) points[i] = new Point[4];

            points[0][0] = new Point(19, 262);
            points[0][1] = new Point(28, 257);
            points[0][2] = new Point(27, 284);
            points[0][3] = new Point(16, 285);


            points[1][0] = new Point(52, 253);
            points[1][1] = new Point(62, 250);
            points[1][2] = new Point(61, 277);
            points[1][3] = new Point(49, 278);


            points[2][0] = new Point(87, 249);
            points[2][1] = new Point(100, 248);
            points[2][2] = new Point(99, 275);
            points[2][3] = new Point(86, 274);

            points[3][0] = new Point(126, 250);
            points[3][1] = new Point(140, 252);
            points[3][2] = new Point(139, 279);
            points[3][3] = new Point(126, 277);


            points[4][0] = new Point(164, 257);
            points[4][1] = new Point(175, 260);
            points[4][2] = new Point(175, 287);
            points[4][3] = new Point(164, 284);

            points[5][0] = new Point(197, 265);
            points[5][1] = new Point(209, 269);
            points[5][2] = new Point(209, 295);
            points[5][3] = new Point(198, 292);


            points[6][0] = new Point(228, 273);
            points[6][1] = new Point(241, 275);
            points[6][2] = new Point(240, 300);
            points[6][3] = new Point(229, 300);

            points[7][0] = new Point(262, 274);
            points[7][1] = new Point(274, 273);
            points[7][2] = new Point(275, 300);
            points[7][3] = new Point(262, 301);

            points[8][0] = new Point(297, 272);
            points[8][1] = new Point(308, 268);
            points[8][2] = new Point(311, 295);
            points[8][3] = new Point(298, 296);


            panel.Paint += thepanel_Paint;


            // I can see that I can remove all the braces, but anyhow.
            for (int i = 0; i < 9; i++)
            {
                using (gps[i] = new GraphicsPath())
                using (Pen pen = new Pen(Color.Black, 1))  //this pen is not for drawing. but for the logic re graphicspath
                {
                    gps[i].AddPolygon(points[i]);                  
                }
            }


        }




        void p_MouseDown(object sender, EventArgs e)
        {

            MouseEventArgs mea = (MouseEventArgs)e;

            //GraphicsPath[] gps = new GraphicsPath[9];
            bool boxhit = false;


            for (int i = 0; i < 9; i++)
            {
                using (Pen pen = new Pen(Color.Black, 1))  //this pen is not for drawing. but for the logic re graphicspath
                {

                    bool cond1=false; 
                    bool cond2=false;  


                    cond1 = gps[i].IsOutlineVisible(new Point(mea.X, mea.Y), pen);
                    cond2 = gps[i].IsVisible(new Point(mea.X, mea.Y));



                    if (cond1 == true ||
                        cond2 == true)
                    {
                        boxhit = true;
                        lbl1.Visible = true;
                        timer1.Enabled = true;
                    }


                }


            } //for

            // if (boxhit == false) MessageBox.Show("no box hit");


        }


        private void timer1_Tick(object sender, EventArgs e)
        {
            lbl1.Visible = false; timer1.Enabled = false;

        }



        private void thepanel_Paint(object sender, PaintEventArgs e)
        {
            GraphicsPath[] gps = new GraphicsPath[9];

            Graphics gg = panel.CreateGraphics();

            for (int i = 0; i < 9; i++)
                using (gps[i] = new GraphicsPath())
                {
                    gps[i].AddPolygon(points[i]);
                    gg.DrawPath(new Pen(Color.Blue, 1), gps[i]);
                }

        }
    }
}

В качестве побочного примечания, вот код до перегруппировки, где он работает, он не генерирует исключение (хотя он, возможно, неэффективен в том, что каждый кусочек он добавляет многоугольники, чтобы определить, на что нажимается, когда на самом деле это только нужно один раз).

введите описание изображения здесь

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace WindowsFormsApplication15
{
    public partial class Form1 : Form
    {
        Label lbl1;
        Panel panel;
        Timer timer1;

        Point[][] points = new Point[9][];


        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            panel = new Panel();


            lbl1 = new Label();
            timer1 = new Timer();

            this.Controls.Add(panel);

            Label alias = lbl1;
            alias.Text = "box clicked";
            alias.Visible = false;

            panel.BackgroundImageLayout = ImageLayout.Stretch;

            this.Height = 500; this.Width = 500;


            panel.Controls.Add(lbl1);

            panel.MouseDown += p_MouseDown;
            timer1.Tick += timer1_Tick;

            panel.BackColor = Color.White;
            panel.Height = 400;
            panel.Width = 400;

            for (int i = 0; i < 9; i++) points[i] = new Point[4];

            points[0][0] = new Point(19, 262);  //left top
            points[0][1] = new Point(28, 257); //right top
            points[0][2] = new Point(27, 284);   //right bottom
            points[0][3] = new Point(16, 285);  //left bottom


            points[1][0] = new Point(52, 253);
            points[1][1] = new Point(62, 250);
            points[1][3] = new Point(49, 278);
            points[1][2] = new Point(61, 277);

            points[2][0] = new Point(87, 249);
            points[2][1] = new Point(100, 248);
            points[2][3] = new Point(86, 274);
            points[2][2] = new Point(99, 275);

            points[3][0] = new Point(126, 250);
            points[3][1] = new Point(140, 252);
            points[3][3] = new Point(126, 277);
            points[3][2] = new Point(139, 279);

            points[4][0] = new Point(164, 257);
            points[4][1] = new Point(175, 260);
            points[4][3] = new Point(164, 284);
            points[4][2] = new Point(175, 287);

            points[5][0] = new Point(197, 265);
            points[5][1] = new Point(209, 269);
            points[5][3] = new Point(198, 292);
            points[5][2] = new Point(209, 295);

            points[6][0] = new Point(228, 273);
            points[6][1] = new Point(241, 275);
            points[6][3] = new Point(229, 300);
            points[6][2] = new Point(240, 300);

            points[7][0] = new Point(262, 274);
            points[7][1] = new Point(274, 273);
            points[7][3] = new Point(262, 301);
            points[7][2] = new Point(275, 300);

            points[8][0] = new Point(297, 272);
            points[8][1] = new Point(308, 268);
            points[8][3] = new Point(298, 296);
            points[8][2] = new Point(311, 295);


            panel.Paint += thepanel_Paint;

        }


        void p_MouseDown(object sender, EventArgs e)
        {
            MouseEventArgs mea = (MouseEventArgs)e;

            GraphicsPath[] gps = new GraphicsPath[9];
            bool boxhit = false;


            for (int i = 0; i < 9; i++)
            {
                using (gps[i] = new GraphicsPath())
                using (Pen pen = new Pen(Color.Black,1))  //this pen is not for drawing. but for the logic re graphicspath
                {

                    gps[i].AddPolygon(points[i]);


                    if (gps[i].IsOutlineVisible(new Point(mea.X, mea.Y), pen) == true || gps[i].IsVisible(new Point(mea.X, mea.Y))) 
                    {
                        boxhit = true;
                        lbl1.Visible = true;
                        timer1.Enabled = true;
                    }


                }

            } //for

           // if (boxhit == false) MessageBox.Show("no box hit");


        }


        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {



        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {

        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            lbl1.Visible = false; timer1.Enabled = false;

        }



        private void thepanel_Paint(object sender, PaintEventArgs e)
        {
            GraphicsPath[] gps = new GraphicsPath[9];

            Graphics gg = panel.CreateGraphics();

            for (int i = 0; i < 9; i++)
                using (gps[i] = new GraphicsPath())
                {
                    gps[i].AddPolygon(points[i]);
                    gg.DrawPath(new Pen(Color.Blue, 1), gps[i]);
                }

        }
    }
}

Добавлен

В ответ на комментарий Ханса.

Я вижу, что я пропустил using для этого графического объекта в процедуре рисования панели. Но если я добавлю его, значит, с этим кодом.

 using(Graphics gg = panel.CreateGraphics())
            for (int i = 0; i < 9; i++)
                using (gps[i] = new GraphicsPath())
                {
                    gps[i].AddPolygon(points[i]);
                    gg.DrawPath(new Pen(Color.Black, 1), gps[i]);
                }

Я все еще получаю то же исключение в строке cond1 = gps[i].IsOutlineVisible(new Point(mea.X, mea.Y), pen); в процедуре p_MouseDown.

Добавлен дополнительный

ответ Q, но еще одна странная вещь. Я попытался создать простую программу, которая дает то же самое исключение, но программа не рушилась или выполнялась правильно, поэтому, возможно, возникла ли ошибка времени выполнения или нет что явно предикативно. Здесь я ожидал бы outofmemoryexception или, по крайней мере, два ящика сообщений, а не только один, и я не получаю исключения и только одно сообщение.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace blah
{
    public partial class Form1 : Form
    {
        Pen p;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            MessageBox.Show("sadf"); //displays
            MessageBox.Show(p.Color.ToString()); // doesn't display a messagebox at all!
        }

    }
}

Ответ 1

Помимо инициализации Pen и некоторых других объектов, которые вы не используете, вы используете ключевое слово using для создания GraphicsPath s. Итак, что происходит, когда вы выходите из инструкции using... да, объект расположен.

Вместо этого попробуйте создать свои пути, например.

for (int i = 0; i < 9; i++)
{
    gps[i] = new GraphicsPath();
    gps[i].AddPolygon(points[i]);
}

Итак, это действительно не имеет никакого отношения к приложению, получающему OutOfMemory... но да, исключение, по крайней мере, вводит в заблуждение.

Использование выше вашего приложения работает, но, как сказал @Hans Passant, вы все еще теряете память.

Edit:

Здесь переписывается ваш код, просто "потому что":). Надеюсь, что это поможет и/или имеет смысл.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    // It preferred to use established naming conventions,
    // such as prefixing private fields with "_".

    // Declaring access modifiers explicitly, even default modifier
    // "private", is advised because it makes reading thousands lines of code
    // that much easier (one can expect first keyword to be access modifier)

    public partial class Form1 : Form
    {
        // For convenience and readability, 
        // I'd prefer Lists and List-of-Lists over arrays
        private readonly List<List<Point>> _points = new List<List<Point>>();
        private readonly List<GraphicsPath> _graphicsPaths = new List<GraphicsPath>();

        public Form1()
        {
            InitializeComponent();

            // Points to create paths from    
            _points.AddRange(new[] 
            {
                new List<Point>
                {
                    new Point(19, 62),
                    new Point(28, 57),
                    new Point(27, 84),
                    new Point(16, 85)
                },
                new List<Point>
                {
                    new Point(52, 53),
                    new Point(62, 50),
                    new Point(61, 77),
                    new Point(49, 78)
                },
                new List<Point>
                {
                    new Point(87, 49),
                    new Point(100, 48),
                    new Point(99, 75),
                    new Point(86, 74)
                }
            });

            // Create GDI graphics paths    
            foreach (List<Point> points in _points)
            {
                GraphicsPath path = new GraphicsPath();
                path.AddPolygon(points.ToArray());
                _graphicsPaths.Add(path);
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Adjust form
            Height = 200;
            Width = 100;

            // Create label and panel
            // No need for these to be private fields
            Label label = new Label
            {
                Text = @"Border clicked",
                Visible = false
            };

            // Create panel
            Panel panel = new Panel
            {
                Height = 400,
                Width = 400,
                BackColor = Color.White,
                BackgroundImageLayout = ImageLayout.Stretch
            };

            // Paint event handler.
            // Personally I prefer inline anonymous methods
            // over named methods when logic is simple 
            // and it not being reused    
            panel.Paint += (o, args) =>
            {
                // 'using' because we want to get rid of Graphics
                // and Pen when we are done drawing paths
                using (Graphics graphics = panel.CreateGraphics())
                {
                    using (Pen pen = new Pen(Color.Blue, 3))
                    {
                        foreach (GraphicsPath path in _graphicsPaths)
                            graphics.DrawPath(pen, path);
                    }
                }
            };

            // Mouse (down) event handler.     
            panel.MouseDown += (o, args) =>
            {
                // Get mouse point
                Point mousePoint = new Point(args.X, args.Y);

                // Again, we want to dispose Pen
                using (Pen pen = new Pen(Color.Transparent, 0F))
                {
                    // Get first path under mouse pointer  
                    GraphicsPath path = _graphicsPaths.FirstOrDefault(p =>
                        p.IsOutlineVisible(mousePoint, pen));

                    if (path == null)
                        return;

                    // If found, "flash" our informative label
                    // in non-blocking way  
                    Task.Run(() =>
                    {
                        label.Invoke((Action)(() => label.Visible = true));
                        Thread.Sleep(500);
                        label.Invoke((Action)(() => label.Visible = false));
                    });
                }
            };

            // Add controls to containers 
            panel.Controls.Add(label);                
            Controls.Add(panel);
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // This could be more reasonable place to dispose
            // GDI Graphics path created earlier? 
            foreach (GraphicsPath path in _graphicsPaths)
                path.Dispose();
            _graphicsPaths.Clear();
        }
    }
}