Hey guys, I have written a "bot" program that is scriptable to do certain automated tasks, e.g. type, move mouse, interact with windows etc.

I have a little window on this bot program (picture box) which captures the screen and places a crosshair on the window which shows the user where the mouse is on the screen:

        public void CaptureThread()
        {
            try
            {
                Bitmap scrnimg = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format24bppRgb);
                while (this.Visible)
                {
                    while (this.WindowState == FormWindowState.Minimized)
                        Thread.Sleep(750);

                    try
                    {
                        Display.CaptureScreenRegion(Screen.PrimaryScreen.Bounds, scrnimg);

                        Pen Pen;
                        switch (CrosshairColour)
                        {
                            default:
                            case "Red":
                                Pen = new Pen(Color.Red, 5);
                                break;
                            case "Blue":
                                Pen = new Pen(Color.Blue, 5);
                                break;
                            case "Green":
                                Pen = new Pen(Color.Green, 5);
                                break;
                            case "Lime":
                                Pen = new Pen(Color.LimeGreen, 5);
                                break;
                            case "Magenta":
                                Pen = new Pen(Color.Magenta, 5);
                                break;
                            case "Black":
                                Pen = new Pen(Color.Black, 5);
                                break;
                            case "White":
                                Pen = new Pen(Color.White, 5);
                                break;
                        }
                        Graphics g = Graphics.FromImage(scrnimg);
                        g.CompositingMode = CompositingMode.SourceCopy;

                        float formLeft = Screen.PrimaryScreen.Bounds.Left;
                        float formTop = Screen.PrimaryScreen.Bounds.Top;
                        float formRight = Screen.PrimaryScreen.Bounds.Right;
                        float formBottom = Screen.PrimaryScreen.Bounds.Bottom;

                        // Place a black box over the location of the main window
                        Rectangle r = new Rectangle(this.Location.X + splitContainer1.Location.X, this.Location.Y + splitContainer1.Location.Y, pictureBox1.Width + 24, pictureBox1.Height + 71);
                        Pen Box = new System.Drawing.Pen(Color.Black, 1);
                        g.FillRectangle(Box.Brush, r);
                        g.DrawRectangle(Pen, r);

                        if (DrawCrosshair)
                        {
                            g.DrawLine(Pen, 0, Cursor.Position.Y, Cursor.Position.X, Cursor.Position.Y);
                            g.DrawLine(Pen, Cursor.Position.X, 0, Cursor.Position.X, Cursor.Position.Y);
                            g.DrawLine(Pen, formRight, Cursor.Position.Y, Cursor.Position.X, Cursor.Position.Y);
                            g.DrawLine(Pen, Cursor.Position.X, formBottom, Cursor.Position.X, Cursor.Position.Y);
                        }

                        g.Dispose();

                        SetMirror(scrnimg);
                        //pictureBox1.Image = scrnimg;

                        // Set the title of the main window
                        WindowTitle("(" + Cursor.Position.X + ", " + Cursor.Position.Y + ")");
                    }
                    catch { }

                    // Range from 10 - 1000ms
                    Thread.Sleep(MirrorFreq);
                }
            }
            catch { }
        }

        delegate void SetMirrorCallback(Bitmap bmp);
        private void SetMirror(Bitmap bmp)
        {
            if (pictureBox1.InvokeRequired)
            {
                SetMirrorCallback d = new SetMirrorCallback(SetMirror);
                this.Invoke(d, new object[] { bmp });
            }
            else
            {
                pictureBox1.Image = bmp;
            }
        }

As you can probably guess, this uses up anywhere from 8% to 17% of my processor time while the window is up, and this also is causing a memory leak.

Does anybody have a more efficient way of doing this that will not cause memory leaks and also lower the cpu usage?

Thanks.

Recommended Answers

All 6 Replies

Can we see more of the code? It might help. Also this wouldn't have happen to be a bot for a game? Like a macro would it?

Well it could be used for a game, but its primary purpose is to navigate around windows, for example, making videos using scripted mouse movement and typing etc. To be honest, it would probably be pretty useless for game bots as it doesnt use injection or anything that would really help the bot navigate around a game.

Not sure what other code will be helpful as that's the only part which renders the picturebox, all I can think of is this:

        // This is in Display class
        public static Bitmap CaptureScreenRegion(Rectangle rect, Bitmap bmp)
        {
            Graphics GFX = System.Drawing.Graphics.FromImage(bmp);
            GFX.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size, CopyPixelOperation.SourceCopy);
            GFX.Dispose();
            return bmp;
        }

For the memory issue try something like this before assigning the PB.Image

if (PB.Image != null) PB.Image.Dispose();

I've tried using Dispose() but it creates problems where the image doesn't update.

Are you running the capture on a secondary thread? Based on your class name, this looks like a high probability. If you are, your should not be directly accessing the UI controls from that thread. I suggest you follow a pattern similar to what is used below.

namespace ScreenCapture
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            UpdateImageDelegate = new RefreshPB(pictureBox1.Refresh);
            SetImage += new SetImageEventHandler(UpdateImage);
            backgroundWorker1.WorkerSupportsCancellation = true;
        }

        delegate void RefreshPB();

        private RefreshPB UpdateImageDelegate;

        private event SetImageEventHandler SetImage;
        private delegate void SetImageEventHandler(Image bmp);

        private void UpdateImage(Image bmp)
        {
            if (pictureBox1.Image != null)
            {
                pictureBox1.Image.Dispose();
            }

            pictureBox1.Image = bmp;
            if (pictureBox1.InvokeRequired)
            {
                pictureBox1.Invoke(UpdateImageDelegate);
            }
            else
            {
                pictureBox1.Refresh();
            }

        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            do
            {
                if (backgroundWorker1.CancellationPending)
                {
                    e.Cancel = true;
                    break; // TODO: might not be correct. Was : Exit Do
                }
                if (SetImage != null)
                {
                    SetImage(ScreenShot.ScreenCapture.CaptureScreen());
                }
                System.Threading.Thread.Sleep(100);
            } while (true);

        }

        private void button2_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync(pictureBox1);
        }
    }
}

Replace my CatureScreen in this line: SetImage(ScreenShot.ScreenCapture.CaptureScreen()); with your code that returns a bitmap.

You're right, it is running on a seperate thread, however I thought that using InvokeRequired would be sufficient.

Thanks for your example TnTinMN, it works great.

In regards to the CPU usage, I think it's just the frequency and the size of the bitmap that is the problem. (The primary screen is 1920x1080, which is being refreshed every 10-250ms), perhaps compression would help, as the picturebox SizeMode is set to Zoom?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.