0

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

Neuron
  • 5,141
  • 5
  • 38
  • 59
  • 1
    which line throws the exception? maybe the interval is too small for the garbage collector to keep up. wrap disposables in `using`, for example GFX should also be disposed. do a memory profile, and see if the framework can handle with a lower rate. [some existing posts](https://stackoverflow.com/q/397754/1132334) – Cee McSharpface Jul 16 '17 at 13:04
  • A bitmap screenshot of your whole screen, every 10 milliseconds - that is, 100 times per second! - is going to eat a lot of memory for sure. If you want to do this you will have to make sure that any big objects are _very_ short-lived and even then one wonders if your garbage collector can keep up with it. – oerkelens Jul 16 '17 at 13:18

3 Answers3

1

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.

mjwills
  • 23,389
  • 6
  • 40
  • 63
0

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;
}
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
0

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.