【问题标题】:C# Graphics.CopyFromScreen "parameter is not valid"C# Graphics.CopyFromScreen“参数无效”
【发布时间】:2013-05-29 12:29:24
【问题描述】:

我用 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 会出现一条提示内存不足的警报。

有解决这个问题的技巧吗?

【问题讨论】:

  • @GSerg 处理后,PictureBox 无法再显示图片了。

标签: c# graphics bitmap


【解决方案1】:

您不断为图片框设置新位图,而以前的位图永远不会被释放。过了一会儿,系统耗尽了 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;
    }
}

【讨论】:

    【解决方案2】:
       pictureBox1.Image = bitmap;
    

    是的,当您频繁更新图片框时,您的程序不会持续很长时间。 Bitmap 类是 IDisposable 不能轻易被忽略的单一 .NET 类。这就像一座冰山,位图可以使用大量的非托管内存但很少使用托管内存。您必须在不再使用位图时对其进行处置,以防止它们为像素数据消耗所有可用的非托管内存。垃圾收集器往往会隐藏这个问题,但当它运行得不够频繁时,它就无法做到这一点。而且 Bitmap 的托管部分太小而无法经常触发集合。修复:

       if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
       pictureBox1.Image = bitmap;
    

    【讨论】:

      【解决方案3】:

      以下知识库有助于理解此问题: Bitmap and Image constructor dependencies

      GDI+,因此 System.Drawing 命名空间,可能会推迟 解码原始图像位,直到图像需要这些位。 此外,即使图像已被解码,GDI+ 也可能 确定丢弃大量内存更有效 位图并稍后重新解码。因此,GDI+ 必须能够访问 位图或图像生命周期中图像的源位 目的。

      为了保留对源位的访问,GDI+ 锁定任何源文件,并且 强制应用程序维持任何源流​​的生命周期,例如 Bitmap 或 Image 对象的生命周期。

      必须弄清楚该对象何时可以处置。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多