0

I want to make a screen capturer that can capture many times whenever the user clicked it (I want to create my own, so don't just tell me that there's a software for that, and for my own practice too) :)

Bitmap.Save "Generic Error"

I tried searching all of the possible solutions from StackOverflow (Above link is one of the many that I've found), but I can't any that can help with my problem.

This is my Code

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

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

        private void btnCaptureScreen_Click(object sender, EventArgs e)
        {
            String screenName = AppDomain.CurrentDomain.BaseDirectory + "ScreenImages/screen.jpg";
            CaptureScreen(screenName);
            screen1.Image = Image.FromFile(screenName);
        }

        private void CaptureScreen(String screenName)
        {
            Rectangle bounds = Screen.PrimaryScreen.Bounds;
            using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
            {
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
                }
                bitmap.Save(screenName, ImageFormat.Jpeg);
            }
        }
    }
}

The problem occurs when I tried to save my Picture Box twice (It always happened the second time)

Error Message: System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'

This is my Form

Form Image

Don't mind ScreenCapture.cs, that's just one of the other solution I tried to try Screen Capturing.

EDIT-FOR-DUPLICATE I've tried Overwrite Existing Image just now, and it's still the same error as before.

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

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

        private void btnCaptureScreen_Click(object sender, EventArgs e)
        {
            String screenName = AppDomain.CurrentDomain.BaseDirectory + "ScreenImages/screen.jpg";
            if (File.Exists(screenName)) File.Delete(screenName);
            CaptureScreen(screenName);
            screen1.Image = Image.FromFile(screenName);
        }

        private Image GetCopyImage(string path)
        {
            using (Image im = Image.FromFile(path))
            {
                Bitmap bm = new Bitmap(im);
                return bm;
            }
        }

        private void CaptureScreen(String screenName)
        {
            Rectangle bounds = Screen.PrimaryScreen.Bounds;
            using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
            {
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
                }
                bitmap.Save(screenName, ImageFormat.Jpeg);
                bitmap.Dispose();
            }
        }
    }
}

Now this is where I got the error (Again, after the second click)

if (File.Exists(screenName)) File.Delete(screenName);

Error Message: System.IO.IOException: 'The process cannot access the file 'C:\Users\Michael HT\Documents\2. Work\Project\Screen Monitoring\Screen Monitoring\bin\Debug\ScreenImages\screen.jpg' because it is being used by another process.'

EDIT-ADDING-NULL-BEFORE-DELETING

private void btnCaptureScreen_Click(object sender, EventArgs e)
        {
            String screenName = AppDomain.CurrentDomain.BaseDirectory + "ScreenImages/screen.jpg";
            screen1.Image = null;
            if (File.Exists(screenName)) File.Delete(screenName);
            CaptureScreen(screenName);
            screen1.Image = Image.FromFile(screenName);
        }

I tried adding null before deleting, still got the same error at

if (File.Exists(screenName)) File.Delete(screenName);

Error Message: System.IO.IOException: 'The process cannot access the file 'C:\Users\Michael HT\Documents\2. Work\Project\Screen Monitoring\Screen Monitoring\bin\Debug\ScreenImages\screen.jpg' because it is being used by another process.'

EDIT-FOR-THE-SOLUTION

I tried this and it worked!!

private void btnCaptureScreen_Click(object sender, EventArgs e)
        {
            String screenName = AppDomain.CurrentDomain.BaseDirectory + "ScreenImages/screen.jpg";
            if(screen1.Image != null) screen1.Image.Dispose();
            screen1.Image = null;
            CaptureScreen(screenName);
            screen1.Image = Image.FromFile(screenName);
        }

Here's the correct code :)

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

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

        private void btnCaptureScreen_Click(object sender, EventArgs e)
        {
            String screenName = AppDomain.CurrentDomain.BaseDirectory + "ScreenImages/screen.jpg";
            if(screen1.Image != null) screen1.Image.Dispose();
            screen1.Image = null;
            CaptureScreen(screenName);
            screen1.Image = Image.FromFile(screenName);
        }

        private Image GetCopyImage(string path)
        {
            using (Image im = Image.FromFile(path))
            {
                Bitmap bm = new Bitmap(im);
                return bm;
            }
        }

        private void CaptureScreen(String screenName)
        {
            Rectangle bounds = Screen.PrimaryScreen.Bounds;
            using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
            {
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
                }
                bitmap.Save(screenName, ImageFormat.Jpeg);
            }
        }
    }
}

Thanks for the help!!

Michael Harley
  • 831
  • 1
  • 9
  • 20
  • You need to release all references to the file before overwriting it. Not as simple as one might think..! – TaW Jul 13 '18 at 08:15
  • Since it "always happens the second time".. are you sure it doesn't happen once it tries to overwrite the file created "the first time"? – Mihai Ovidiu Drăgoi Jul 13 '18 at 08:16
  • @TaW How do I do that? Sorry I'm new in GDI :( – Michael Harley Jul 13 '18 at 08:18
  • @MihaiOvidiuDrăgoi Yes I'm sure, I've got the folder for ScreenImages, so that's not the problem for my issue – Michael Harley Jul 13 '18 at 08:18
  • @TaW I've updated my question, as per your Mark as Duplicate. Like I said before, how do I release all references to the file before overwritting it? I know what you mean, but I don't know how, every possible "Dispose" function that I've found, I tried it already and got the same error. I've tried that File Exist then Delete too even before I asked here. – Michael Harley Jul 13 '18 at 08:30
  • Hm, is that actually the same error?? – TaW Jul 13 '18 at 08:30
  • Is screen1 a PictureBox? Try to set its Image to null before deleting the file! – TaW Jul 13 '18 at 08:32
  • @TaW Oh yeah, I just notice it that one of the error was done and now I'm up for the next error :( I can't delete an opened file, so how do I close the file? And i tried adding `screen1.Image = null`, not working too, still the same error. "Being used for another process" – Michael Harley Jul 13 '18 at 08:34
  • @TaW Thanks for the help, I already updated my question with an answer there :) Apparently, I need to dispose of my picturebox.Image first. – Michael Harley Jul 13 '18 at 08:42
  • Correct. This works here: `private void btnCaptureScreen_Click(object sender, EventArgs e) { String screenName =...; Image img = screen1.Image; if (img != null) img.Dispose(); screen1.Image = null; if (File.Exists(screenName)) File.Delete(screenName); CaptureScreen(screenName); screen1.Image = Image.FromFile(screenName); }` – TaW Jul 13 '18 at 08:43
  • 1
    @TaW Thanks a lot!! :) – Michael Harley Jul 13 '18 at 08:44
  • Careful... calling `screen1.Image.Dispose()` directly can give an `ObjectDisposedException` if the UI tries to repaint it. It's much safer to put it in a new variable, then set `screen1.Image` to null, and then dispose the image from the other variable. – Nyerguds Jul 15 '18 at 19:04

0 Answers0