2015-12-26 4 views
1

Форма с панелью и коробками, нарисованными на экране, пользователь нажимает на ящик, и на мгновение появляется надпись «box hit».C# Pen выбрасывает OutOfMemoryException

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

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

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

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

Я читал онлайн, что перо может генерировать исключение 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]); 
       } 

     } 
    } 
} 

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

enter image description here

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 порядке.

Добавлено далее

добротности ответил, но еще странно .. Я пытался создать простую программу, которая дает такое же исключение, но программа не врезаться или правильно работать так, возможно ли возникает ошибка выполнения или нет, что явно предикативно. Здесь я ожидал бы 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! 
     } 

    } 
} 
+0

Вы пропускаете объект Graphics в обработчике событий Paint. Возможно, еще и некоторые из них, исключения являются шероховатыми, когда ОС решает, что вы слишком сильно просачиваетесь и больше не позволяете вам создавать. Используйте вкладку «Диспетчер задач», «Процессы» и добавьте столбцы для объектов GDI и объектов USER, чтобы вы могли видеть, что утечка складывается. –

+0

@HansPassant thanks .. Я все еще получаю то же исключение в той же строке (в процедуре p_MouseDown), даже когда я добавляю «использование» в графический объект в обработчик события рисования. Я добавил примечание к этому вопросу. – barlop

+0

@HansPassant только что проверил объекты пользователя и объекты GDI, и они оба 32 и 37, когда я запустил его и во время исключения. – barlop

ответ

2

Кроме того, вы инициализации Pen и некоторые другие объекты, которые вы не используете, вы используете using ключевое слово, чтобы создать GraphicsPath с. Итак, что происходит, когда вы выходите из инструкции 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'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(); 
     } 
    } 
} 
+0

, я не должен использовать 'using' - если я удалю' using', как вы предлагаете, тогда объект не находится в распоряжении. Но предполагается, что он должен уничтожить объекты IDisposable. – barlop

+0

также, как в рабочей версии, так и в нерабочей версии, я использую (gps [i] = new GraphicsPath()). Так что это не может быть так просто, как «вы используете ключевое слово для создания своих графических панелей» проблема] " – barlop

+0

ok Я вижу в процедуре загрузки формы, что для цикла, о котором вы упоминаете, работает, так как он не удаляет объект .. поэтому он доступен для процедуры mousedown. Но, возможно, это плохая практика, чтобы не уничтожать объект (ы) – barlop

Смежные вопросы