将DrawToBitmap转换为位图 - System.ArgumentException:参数无效

6

我正在创建一个 Label,有时我会使用 .DrawToBitmap()。我不知道为什么,但在运行程序一段时间后(经常调用 .DrawToBitmap()),我会遇到异常:

System.ArgumentException: Parameter is not valid.
   at System.Drawing.Bitmap..ctor(Int32 width, Int32 height, PixelFormat format)
   at System.Drawing.Bitmap..ctor(Int32 width, Int32 height)

我无法频繁调用此函数。如果我尝试采取彻底的方法:

while(true)
{

  System.Windows.Forms.Label label = new Label();

  label.Font = new Font("Arial", 20);
  label.Text = "test";

  try
  {
    Bitmap image = new Bitmap(300, 500);
    label.DrawToBitmap(image, label.ClientRectangle);
  }
  catch (Exception e)
  {
    Console.WriteLine(e);
  }
}

我在5-6秒钟(1000-2000次调用)后收到了异常。问题在哪里?如何避免出现这种情况?

编辑:感谢大家提出的关于Dispose()的想法 - 如果我在label上使用,一切都可以完美地运作。 即使我不在Bitmap上使用它也没问题。两个答案都很好,我只能接受其中一个:(

2个回答

7

所以,那个错误信息来自GDI+深处,可能有很多原因。然而,我看到你的代码中存在一个显著的问题:

a lot 意思是“很多”的意思。

 label.Font = new Font("Arial", 20);

Font 对象实现了 IDisposable 接口,你正在一个紧密的循环中创建大量的这些对象,并且从未调用 Dispose() 方法。同样的情况也适用于位图本身。我敢打赌,GDI 已经运行到了资源枯竭的边缘。

以现有代码为例,很难理解它的意义。它实际上什么都没有做,只是创建了大量的 FontBitmap 对象,所以我甚至不能建议将每个声明封装在一个 using 语句中。不管怎样,当您快速连续创建大量 GDI 对象而不处理它们时,您最终会遇到这个问题。

如果您需要这些对象在一段时间内保持有效,则需要确保稍后调用 Dispose() 以尽可能及时地释放本机资源(最终器将为您执行此操作,但最好不要等待)。如果它们是局部变量,则将它们封装在一个 using 语句中,这样当块退出时就会调用 Dispose()

using(var b = new Bitmap(w, h))
{
    // use 'b' for whatever
} // b.Dispose() is called for you

嗨。起初我尝试使用(Bitmap...) => 没有任何变化。但是当我在每次调用结束时使用label.Dispose()时,一切都很好 :) 谢谢 - 我已经测试了30,000个调用,我认为现在没问题了。如果没有label.Dispose(),我会在1000-2000个调用后出现异常。从未想过我必须dispose()标签。 - miri
@miri:就像@HansPassant所说的那样,我认为释放标签会导致异常。我相信应该将字体和位图包装在“using”块中。我不知道你是如何在没有看到标签的使用方式的情况下释放标签的。 - IAbstract

4
GDI+异常通常并不准确描述实际问题。在这种情况下,它实际上意味着“位图太大”。但这仍然不能很好地描述,实际上是由于非托管内存不足而导致的。该位图过大以至于无法适应当前可用的内存量。
因为您没有调用位图的Dispose()方法。您经常可以省略该方法而不会注意到问题。但对于Bitmap类而言,它占用极少数量的垃圾收集内存,但却需要大量的非托管内存。它不能快速触发垃圾回收器来释放非托管内存。
代码片段本身并不合理,但您可以这样编写它以避免出现异常:
using (Bitmap image = new Bitmap(300, 500)) {
    label.DrawToBitmap(image, label.ClientRectangle);
}

1
我非常、非常讨厌GDI错误信息......这需要经验才能诊断问题,这使得信息本身毫无用处。 - Ed S.
谢谢你的想法!Dispose()很棒,但是我需要在标签上使用它。 - miri
不,你肯定不想处理 Label,那会使它消失并崩溃你的代码。处理位图。 - Hans Passant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接