【问题标题】:c# Pen throws OutOfMemoryExceptionc# Pen 抛出 OutOfMemoryException
【发布时间】:2016-04-01 02:42:14
【问题描述】:

一个带有面板和框的表单,用户单击一个框,然后会出现一个标签,说“框命中”。

我有一个工作版本的代码,但我尝试重新排列它以尝试使其更高效,但我发现我目前遇到了这个异常。

我不明白为什么会发生此异常,我希望能够对其进行故障排除,但我也不知道该怎么做。我已经把它缩小到它是什么线。但除此之外我不知道。

单击框时会发生错误。因此执行面板的mousedown。

所有控件,例如面板、标签都是以编程方式生成的,因此希望任何人都可以复制/粘贴代码更改类名并重现我得到的异常。

我在网上读到,即使不是真的内存不足,笔也可能会产生内存不足异常,但它只是抛出该异常,尽管这仍然没有告诉我为什么或如何避免它,所以我可以了解如何在笔不会引发异常的情况下编写代码。

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]);
                }

我在p_MouseDown 过程中的cond1 = gps[i].IsOutlineVisible(new Point(mea.X, mea.Y), pen); 行上仍然遇到同样的异常。

进一步添加

Q 得到了回答,但还有更奇怪的事情.. 我试图创建一个给出相同异常的简单程序,但该程序没有崩溃或正确运行,所以可能不会发生运行时错误这显然是可以预见的。在这里,我希望出现内存不足异常,或者至少有两个消息框,而不仅仅是一个,而且我没有得到任何异常,只有一个消息框。

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!
        }

    }
}

【问题讨论】:

  • 您在 Paint 事件处理程序中泄漏了一个 Graphics 对象。也许还有更多,一旦操作系统确定您泄漏太多并且不会让您再创建,异常就会变得不稳定。使用任务管理器,进程选项卡并添加 GDI 对象和用户对象的列,以便您可以看到泄漏的累加。
  • @HansPassant 谢谢.. 我仍然在同一行得到相同的异常,(在 p_MouseDown 过程中),即使我将 using 添加到绘图事件处理程序中的图形对象。我已经在我的问题中添加了一条注释。
  • @HansPassant 刚刚检查了用户对象和 GDI 对象,当我运行它时以及发生异常时它们都是 32 和 37。
  • @barlop 也许是因为我在全球范围内声明了一个。我想如果我只有一个图形路径并更改点,那么在内存方面更好的设计会是
  • 一个有趣的稍微相关的问题stackoverflow.com/questions/34373828/…

标签: c# winforms visual-studio memory idisposable


【解决方案1】:

除了初始化Pen 和其他一些你没有使用的对象之外,你还可以使用using 关键字来创建你的GraphicsPaths。那么当您退出 using 语句时会发生什么...是的,对象已被释放。

相反,请尝试创建路径,例如

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

所以这确实与应用程序获取OutOfMemory 无关...但是,是的,异常至少具有误导性。

使用上面的应用程序可以工作,但正如@Hans Passant 所说,你仍然在泄漏内存。

编辑:

这是对您的代码的重写,只是“因为”:)。希望它有所帮助和/或有意义。

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's 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's 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();
        }
    }
}

【讨论】:

  • 我不应该使用using - 如果我按照您的建议删除using,那么该对象就不会被处理掉。但是应该处理 IDisposable 对象。
  • 另外,在工作版本和非工作版本中,我都有` using (gps[i] = new GraphicsPath())` 所以它不能像“你使用 using关键字来创建您的 GraphicsPaths [因此问题]"
  • 好的,我在表单加载过程中看到你提到的 for 循环是有效的,因为它不会处理对象。所以它可用于 mousedown 过程。但也许不处置对象是不好的做法
  • 当您不再需要对象时,您可以处理它们。在这种情况下,处置没有意义。
  • 谢谢,我去看看
猜你喜欢
  • 2015-02-28
  • 2014-09-20
  • 1970-01-01
  • 1970-01-01
  • 2013-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多