C# Dispose function giving an error

2019-08-17 08:28发布

问题:

I am trying to take screenshot for every 10 miliseconds and set them as a Picturebox.image with Timer. For few seconds program runs perfectly but after few seconds program is crashing. I tried to use Dispose() Function in the end of the code to clear the memory but Dispose Function also gives an error. (increasing interval of timer doesn't worked)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace gameBot
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        public Bitmap screenshot;
        Graphics GFX;
        private void button1_Click(object sender, EventArgs e)
        {
            timer1.enabled = true;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            takescreenshot();
        }
        private void takescreenshot()
        {

            screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, 
            Screen.PrimaryScreen.Bounds.Height);
            GFX = Graphics.FromImage(screenshot);
            GFX.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);
            pictureBox1.Image = screenshot;
            screenshot.Dispose();              
        }           
    }
}

The error is

"An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll

Additional information: Parameter is not valid."

Also program is using too much RAM before crashing (Maybe it's crashing because of out of memory exception?) As you can see in here

回答1:

See Graphics.FromImage():

You should always call the Dispose method to release the Graphics and related resources created by the FromImage method.

Also, there's no need to keep that Graphics at Class level.

With that in mind, all you need is:

public Bitmap screenshot;

private void takescreenshot()
{
    if (screenshot != null)
    {
        screenshot.Dispose();
    }

    screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
    using (Graphics GFX = Graphics.FromImage(screenshot))
    {
        GFX.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);
    }
    pictureBox1.Image = screenshot;
}


回答2:

I would suggest changing:

pictureBox1.Image = screenshot;
screenshot.Dispose();

to:

var oldScreenshot = pictureBox1.Image;
pictureBox1.Image = screenshot;
GFX.Dispose();
if (oldScreenshot != null)
    oldScreenshot.Dispose;

to ensure that the old screenshot is disposed whenever you assign a new one.



回答3:

1) Actually your first problem "Parameter is not valid." is beacause youre disposing screenshot object. If you will try to run your takescreenshot() method only once - you will get this error. I assume that happens because you set object "screenshot" to PictureBox1.Image and than immediately dispose it. Thats logical! PictureBox can`t render disposed object.

2) Try to modify your code on button handler like that:

    private Object thisLock = new Object();
    private void button1_Click(object sender, EventArgs e)
    {
        Thread thr = new Thread(() =>
        {
            while (true)
            {pictureBox1.Invoke((Action)(() =>
                    {
                        screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
                            Screen.PrimaryScreen.Bounds.Height);
                        GFX = Graphics.FromImage(screenshot);
                        GFX.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0,
                            Screen.PrimaryScreen.Bounds.Size);
                        pictureBox1.Image = screenshot;
                    }));
                }
                Thread.Sleep(10);
        });
        thr.Start();
    }

Works fine! The best way is to get event when picturebox finished rendering, But I didn`t find anything about this.