Общая ошибка GDI : устранена при вызове GC.Collect()

#c# #garbage-collection

#c# #сборка мусора

Вопрос:

У меня есть этот фрагмент кода («ruta1» и «ruta2» — это строки, содержащие путь к разным изображениям):

    Bitmap pic;
   Bitmap b = new Bitmap(SomeWidth, SomeHeight);
   g = Graphics.FromImage(b);
   g.FillRectangle(new SolidBrush(Color.White), 0, 0, b.Width, b.Height);

   b.Save(ruta2, ImageFormat.Jpeg);
   g.Dispose();
   b.Dispose();

   b = new Bitmap(OtherWidth, OtherHeight);
   pic = new Bitmap(ruta2);
   g = Graphics.FromImage(b);
   g.FillRectangle(new SolidBrush(Color.White), 0, 0, b.Width, b.Height);
   g.DrawImage(pic, 0, 0, pic.Height, pic.Width); 

   pic.Dispose();
   b.Save(ruta2, ImageFormat.Jpeg);
   g.Dispose();
   b.Dispose();

   pic = new Bitmap(ruta1);
   b = new Bitmap(AnotherWidth, AnotherHeight);
   g = Graphics.FromImage(b);
   g.FillRectangle(new SolidBrush(Color.White), 0, 0, b.Width, b.Height);

   int k = 1;

   // I just draw the "pic" in a pattern on "b"
   for (h = 0; h <= b.Height; h  = pic.Height)
       for (w = 0; w <= b.Width; w  = pic.Width)
            g.DrawImage(pic, w, h, k * pic.Width, k * pic.Height);

   pic.Dispose();            
   GC.Collect();  // If I comment this line, I get a "generic GDI  Error" Exception
   b.Save(ruta1, ImageFormat.Jpeg);
   g.Dispose();
   b.Dispose();
  

Не имеет значения, установил ли я pic = null после удаления, если я не вызываю сборщик мусора, я получаю исключение «Общая ошибка GDI «. Только когда я вызываю сборщик мусора, моя программа всегда работает нормально.

Кто-нибудь может объяснить такое поведение? Зависит ли это от .Версия Net framework? Я использую Visual C # Express 2008, с .Net framework 3.5

Ответ №1:

Во-первых, было бы неплохо, если бы вы использовали ключевое слово « using » для расширения области использования ваших одноразовых объектов (таких как Bitmap и Graphics ), вместо того, чтобы вызывать Dispose() вручную для каждого. Использование « using » лучше по многим причинам, таким как очистка содержимого при возникновении исключения, но это также значительно улучшает читаемость кода.

Во-вторых, кисти GDI также являются IDisposable объектами, поэтому вам не следует создавать их и забывать. Сделайте это вместо:

 using (var brush = new SolidBrush(Color.White))
{
    g.FillRectangle(brush, 0, 0, width, height)
}
  

… или, что еще лучше, создайте свои кисти в начале вашего приложения и сохраняйте их до конца (но не забудьте также избавиться от них). IIRC, создание / удаление кистей сильно влияет на производительность, если выполняется часто.

В-третьих, я полагаю, что ваша ошибка находится во 2-м разделе:

  1. Вы открываете изображение «ruta2»
  2. Вы создаете новый образ
  3. Вы рисуете содержимое «ruta2» внутри этого нового изображения
  4. Вы сохраняете эту новую вещь поверх файла «ruta2», но исходное изображение с шага 1 все еще загружено и, вероятно, имеет какой-то дескриптор в файле «ruta2», поэтому вам нужно избавиться от него перед перезаписью файла.

Если вы реорганизуете этот 2-й раздел подобным образом, он должен работать:

 using (var b = new Bitmap(OtherWidth, OtherHeight))
using (var g = Graphics.FromImage(b))
{
     using (var brush = new SolidBrush(Color.Red))
     {
         g.FillRectangle(brush, 0, 0, b.Width, b.Height);
     }
     using (var pic = new Bitmap(ruta2))
     {
         g.DrawImage(pic, 0, 0, pic.Height, pic.Width);
     }
     b.Save(ruta2, ImageFormat.Jpeg);
 }
  

Комментарии:

1. Я забыл использовать слово «using» при использовании кода, который я использую для использования: D : D : D