При рисовании изображения: System.Runtime.InteropServices.ExternalException: Общая ошибка в GDI

У меня есть глобальный объект Graphics, созданный с панели. Через определенные промежутки времени изображение извлекается с диска и вставляется в панель с помощью Graphics.DrawImage(). Он отлично работает для нескольких итераций, а затем я получаю следующее полезное исключение:

System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.
at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
at System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y)
at System.Drawing.Graphics.DrawImage(Image image, Point point)

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

Я столкнулся с той же проблемой при использовании PictureBox, но на этот раз, по крайней мере, я получил ошибку вместо ничего.

Я проверил объекты GDI и объекты USER в Диспетчере задач, но они всегда вокруг 65 пользовательских объектов и 165 объектов GDI, когда приложение работает, а когда нет.

Мне нужно разобраться с этим, как только это не так, как будто я могу использовать точки останова в библиотеках .NET System и видеть, где именно выполнение не выполняется.

Спасибо заранее.

EDIT: это код отображения:

private void DrawImage(Image image)
{
  Point leftCorner = new Point((this.Bounds.Width / 2) - (image.Width / 2), (this.Bounds.Height / 2) - (image.Height / 2));
  _graphics.DrawImage(image, leftCorner);
}

код загрузки изображения:

private void LoadImage(string filename, ref Image image)
{
  MemoryStream memoryStream = DecryptImageBinary(Settings.Default.ImagePath + filename, _cryptPassword);

  image = Image.FromStream(memoryStream);

  memoryStream.Close();
  memoryStream.Dispose();
  memoryStream = null;
}

_image глобально, и его ссылка обновляется в LoadImage. Они передаются как параметры, так как я хочу изменить глобальные ссылки из максимально возможного количества мест и сохранить остальные методы самостоятельно. _graphics также глобальна.

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

webBrowser.Visible = false;
panel.Visible = true;
DrawImage(_image)
_image.Dispose();
_image = null;

_image ссылается на предварительно загруженное изображение.

Надеюсь, что это поможет.

Ответ 1

Ваша проблема похожа на то, что я думал, но не совсем. Когда вы загружаете изображение, вы загружаете его из MemoryStream. Вы должны сохранить поток открытым для жизни изображения, см. MSDN Image.FromStream.

Вы должны оставить поток открытым для срока службы изображения.

Решение состоит в том, чтобы сделать копию вашего изображения в функции FromImage:

private void LoadImage(string filename, ref Image image)
{
  using (MemoryStream memoryStream = DecryptImageBinary(Settings.Default.ImagePath + filename, _cryptPassword))
  {
      using (tmpImage = Image.FromStream(memoryStream))
      { 
         image = new Bitmap(tmpImage);
      }
  }

}

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

Ответ 2

Без небольшого количества кода недостаточно для правильной диагностики здесь, однако, одна вещь, на которую нужно обратить внимание, заключается в том, что вы, возможно, располагали на изображении свой рисунок с какой-то точкой раньше, и это происходит только после того, как сборщик мусора запускает что ваш код не работает. Вы используете клонированные изображения где угодно? Одна вещь, которую я был удивлен, чтобы узнать, что если вы делаете прямое изображение clone, вы не клонируете базовое растровое изображение, которое image полагается только на структуру изображения, чтобы создать правильную копию изображения, которое вы должны создать для нового изображения:

var newImage = new Bitmap(img)

а

var newImage = oldImg.Clone();
oldImg.Dispose();
...
gr.DrawImage(newImage, new Rectangle(0,0,newImage.Width,newImage.Height);

будет работать некоторое время, но затем сбой в некоторой случайной точке...