C# 绘制图像时:System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1772083/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 20:41:26  来源:igfitidea点击:

When drawing an image: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI

c#gdi+winforms

提问by Michali

I've got a global Graphics object created from a Panel. At regular intervals an image is picked up from the disk and drawn into the panel using Graphics.DrawImage(). It works fine for a few iterations and then I'm getting the following helpful exception:

我有一个从 Panel 创建的全局 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)

I ruled out memory leaks as I dispose of the image object when I'm done with it. I know that the images are not corrupted and can be read fine as the program executes fine for a while before the panel stops showing.

当我处理完图像对象时,我排除了内存泄漏。我知道图像没有损坏并且可以正常读取,因为程序在面板停止显示之前可以正常执行一段时间。

I ran into the same problem when using a PictureBox but this time at least I got an error instead of nothing.

我在使用 PictureBox 时遇到了同样的问题,但这次至少我得到了一个错误而不是什么都没有。

I checked the GDI objects and USER objects in the Task Manager but they're always around 65 user objects and 165 GDI objects when the app works and when it doesn't.

我检查了任务管理器中的 GDI 对象和 USER 对象,但是当应用程序工作和不工作时,它们总是大约 65 个用户对象和 165 个 GDI 对象。

I do need to get to the bottom of this as soon as and it's not like I can stick breakpoints in .NET System libraries and see where exactly execution fails.

我确实需要尽快深入了解这一点,而且我不能在 .NET 系统库中设置断点并查看执行失败的确切位置。

Thanks in advance.

提前致谢。

EDIT: This is the display code:

编辑:这是显示代码:

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

the image load code:

图片加载代码:

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 is global and its reference is updated in LoadImage. They are passed as parameters as I want to change the global references from as few places as possible only and keep the other methods self contained. _graphics is also global.

_image 是全局的,它的引用在 LoadImage 中更新。它们作为参数传递,因为我只想从尽可能少的地方更改全局引用并保持其他方法自包含。_graphics 也是全球性的。

I've also got a webBrowser control for web sites and I either show an image or a website at one time. when there's time to display an image, the following code executes:

我还有一个用于网站的 webBrowser 控件,我可以一次显示图像或网站。当有时间显示图像时,将执行以下代码:

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

_image is referencing a pre-loaded image.

_image 正在引用预加载的图像。

Hope this helps.

希望这可以帮助。

采纳答案by Kris Erickson

Your problem is similar to what I thought, but not quite. When you are loading the image, you are loading it from a MemoryStream. You have to keep the stream open for the lifetime of the image, see MSDN Image.FromStream.

您的问题与我的想法相似,但不完全相同。当您加载图像时,您是从 MemoryStream 加载它。您必须在图像的整个生命周期内保持流打开,请参阅MSDN Image.FromStream

You must keep the stream open for the lifetime of the Image.

您必须在图像的整个生命周期内保持流打开。

The solution is to make a copy of your image in the FromImage function:

解决方案是在 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);
      }
  }

}

Similar to the dispose problem I mentioned, the image will seem to work and then randomly fail when the underlying stream is garbage collected.

与我提到的处置问题类似,当底层流被垃圾收集时,图像似乎可以工作,然后随机失败。

回答by Kris Erickson

Without a little more code there is not enough to properly diagnose here, however, one thing to look at is that you may have disposed on the image your a drawing with at some point earlier and it is only after the garbage collector runs that your code is failing. Are you using cloned images anywhere? One thing I was suprised to learn is that if you do a straight cloneof an image, you are not cloning the underlying bitmap that the image rely's upon, only the image structure, to create a proper copy of an image you have to create a new image:

如果没有更多的代码,这里还不足以进行正确的诊断,但是,要注意的一件事是您可能已经在某个时候将您的绘图放在图像上,并且只有在垃圾收集器运行之后您的代码正在失败。您是否在任何地方使用克隆图像?我惊讶地了解到的一件事是,如果您直接克隆图像,则不会克隆图像所依赖的底层位图,而只是图像结构,以创建图像的正确副本,您必须创建一个新图片:

var newImage = new Bitmap(img)

as

作为

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

will work for a while, but then fail at some random point...

将工作一段时间,但随后在某个随机点失败......