3

我用 C# 制作了一个应用程序,它将连续执行屏幕捕获并使用计时器将其显示在 PictureBox 中。运行几秒钟后,出现 ArgumentException。

下面是代码和具有 ArgumentException 的行

private void timer1_Tick(object sender, EventArgs e)
    {
        Rectangle bounds = Screen.GetBounds(Point.Empty);
        Graphics graphics;
        Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
        using (graphics = Graphics.FromImage(bitmap))
        {

            graphics.CopyFromScreen(0, 0, 0, 0, new Size(bounds.Width , bounds.Height )); // ArgumentException
            pictureBox1.Image = bitmap;
            pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;

        }
    }

除此之外,我注意到在运行应用程序几秒钟后,Windows 会出现一条提示内存不足的警报。

有关解决此问题的任何提示?

4

3 回答 3

4

您不断为图片框设置一个新的位图,而以前的位图永远不会被释放。过了一会儿,系统耗尽了 GDI 句柄和/或内存(运行您的代码,我在 15 秒内消耗了 1 gig 的内存)。

您可以简单地重用现有位图:

Rectangle bounds = Screen.GetBounds(Point.Empty);

Image bitmap = pictureBox1.Image ?? new Bitmap(bounds.Width, bounds.Height);

using (Graphics graphics = Graphics.FromImage(bitmap))
{
    graphics.CopyFromScreen(0, 0, 0, 0, new Size(bounds.Width, bounds.Height));

    if (pictureBox1.Image == null)
    {
        pictureBox1.Image = bitmap;
    }
    else
    {
        pictureBox1.Refresh();
    }

}

您也不必pictureBox1.SizeMode在每次迭代时重新设置。


或者,您可以手动处理以前的位图:

Rectangle bounds = Screen.GetBounds(Point.Empty);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
    graphics.CopyFromScreen(0, 0, 0, 0, new Size(bounds.Width, bounds.Height));

    using (Image prev_bitmap = pictureBox1.Image)
    {
        pictureBox1.Image = bitmap;
    }
}
于 2013-05-29T12:43:05.547 回答
1
   pictureBox1.Image = bitmap;

Yes, your program won't last long when you frequently update the picture box. The Bitmap class is the singular .NET class where IDisposable can't easily be ignored. It is like an iceberg, bitmaps can use massive amounts of unmanaged memory but very little managed memory. You must dispose bitmaps when you no longer use them to prevent them from consuming all available unmanaged memory for their pixel data. The garbage collector tends to hide that problem but it can't do so when it doesn't run frequently enough. And the managed portion of Bitmap is too small to trigger a collection often enough. Fix:

   if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
   pictureBox1.Image = bitmap;
于 2013-05-29T12:53:51.923 回答
-1

The following KB can help understanding this issue: Bitmap and Image constructor dependencies

GDI+, and therefore the System.Drawing namespace, may defer the decoding of raw image bits until the bits are required by the image. Additionally, even after the image has been decoded, GDI+ may determine that it is more efficient to discard the memory for a large Bitmap and to re-decode later. Therefore, GDI+ must have access to the source bits for the image for the life of the Bitmap or the Image object.

To retain access to the source bits, GDI+ locks any source file, and forces the application to maintain the life of any source stream, for the life of the Bitmap or the Image object.

One has to figure out when is the object ready for disposal.

于 2016-03-23T21:29:37.433 回答