Memory/Resources not being released - c#

I am using two windows forms, first form will have multiple image files populated in datagridview and on the second form all those images are placed in another datagridview control, the second datagridview control has a button as Delete Image that will delete the file.
after i click the button exception occurs as file is being used. but i alread have disposed the datagridview and also cleared its rows but still it is causing not to release the image. I already have tried to call GC.Collect(); Method but it didn't helped. my sample code is below.
On First form:
// Commenting the code above
if (validuser)
{
dgvImages.Rows.Clear();
dgvImages.Dispose();
GC.Collect();
var form = new NewForm();
form.Show();
this.Hide();
}
and on second form:
private void dgvImages_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex == 1)
{
string selectedpath = dgvImages.Rows[e.RowIndex].Cells[2].Value.ToString(); // this cell contains the full path of the image
if (File.Exists(selectedpath))
{
dgvImages.Rows.Clear();
dgvImages.Dispose();
GC.Collect();
GC.WaitForPendingFinalizers();
File.Delete(selectedpath);
LoadImages();
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message); // File is in use
}
}
EDIT: Here is how i am loading images
private void LoadImages()
{
if (Directory.Exists(path))
{
DirectoryInfo dir = new DirectoryInfo(path);
FileInfo[] files = dir.GetFiles();
foreach (var f in files)
{
if (f.Extension.ToLower() == ".jpg" || f.Extension.ToLower() == ".png" || f.Extension.ToLower() == ".bmp" || f.Extension.ToLower() == ".tiff")
{
dgvImages.Rows.Add((Image.FromFile(f.FullName)), "Delete Image" ,f.FullName);
}
}
}
}

By default when you use Image.FromFile it will lock the file until the image is disposed.
As per the MSDN resources
https://msdn.microsoft.com/en-us/library/4sahykhd(v=vs.110).aspx
What you want to do is load your images as per this answer here.
Open Image from file, then release lock?
Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp, true);
}
According to the new Bitmap documentation you may need to get it to use icm.
https://msdn.microsoft.com/en-us/library/3135s427(v=vs.110).aspx
Use this constructor to open images with the following file formats: BMP, GIF, EXIF, JPG, PNG and TIFF.

Image.FromFile() method lock the file by GDI+
Load image from stream using the following code:
public Image GetmageFromStream(string path)
{
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) {
var img = Image.FromStream(fs);
return img;
}
}

Related

Overwrite/Update the existing image file in the folder

I'm using a webcam to capture an image and place it to picturebox and save the image to my project folder and get the image path then save to sql database. The problem is i do not know how i gonna update/overwrite the existing file
NOTE: My data is bind in datagridview when i click the cell the image will be retrieved on another form, so basically it based on the ID.
Here's what i've tried to achieve that
private void BtnOk_Click(object sender, EventArgs e)
{
if (picCapturedImage.Image != null)
{
using (var bitmap = new Bitmap(picCapturedImage.Image))
{
bitmap.Save(Common.CustomerPath, ImageFormat.Png);
_updCustomer.picCustomerImage.Image = Image.FromFile(Common.CustomerPath);
}
}
Close();
}
When i run this code it replace the image of User.Png not the capture image(Image1, Image2, etc)
User.Png is my default image when user doesn't want to browse or capture an image
Here's the code for saving the image in the project folder
I used loop to avoid overwriting the image in the project folder, so when i saved the image
Image0, Image1, Image2, etc
if (picCapturedImage.Image != null) {
using (var bitmap = new Bitmap(picCapturedImage.Image))
{
for (int i = 0; i < int.MaxValue; i++)
{
var fileName = $#"..\..\Resources\Photos\Image{i}.png";
if (!File.Exists(fileName)) {
bitmap.Save(fileName, ImageFormat.Png);
Common.CustomerPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $#"..\..\Resources\Photos\Image{i}.png"));
_regCustomer.picCustomerImage.Image = Image.FromFile(Common.CustomerPath);
break;
}
}
}
}
Close();
Here's the code where i save the path
public class Common
{
public static string CustomerPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"..\..\Resources", "User.png"));
}
Here's the code for retrieving the image path
_updCustomer.picCustomerImage.Image = Image.FromFile(customer.ImagePath);

Overwrite BMP file, can't delete, because it's used by another process - Dispose, using not working

That's all there is to this simple program. There's a button, you open an image file, the program puts a watersign on it and overwrites it:
private void button1_Click(object sender, EventArgs e)
{
var openDialog = new OpenFileDialog();
var dialogResult = openDialog.ShowDialog();
if (dialogResult == DialogResult.OK)
{
var file = openDialog.FileName;
using (var bmp = new Bitmap((Bitmap)Image.FromFile(file)))
using (var g = Graphics.FromImage(bmp))
{
openDialog.Dispose();
var waterSign = (Bitmap)Properties.Resources.ResourceManager.GetObject("watersign");
var margin = 15;
var x = bmp.Width - waterSign.Width - margin;
var y = bmp.Height - waterSign.Height - margin;
g.DrawImage(waterSign, new Point(x, y));
waterSign.Dispose();
}
try
{
File.Delete(file);
//bmp2.Save("C:\\Temp\\huhu.bmp");
this.Dispose();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
Right now I'm just trying to delete the damned file which for some reason doesn't work. I tried using as you can see, as well as Dispose(), as well as creating another BMP which gets its data from the first one.
Any ideas? Thanks in advance!
The line
using (var bmp = new Bitmap((Bitmap)Image.FromFile(file)))
Loads a bitmap from the file, then creates an independent copy of it using the Bitmap(Image) constructor. Upon exiting the using statement the copy will get disposed -- but not inner bitmap loaded from the file. Until that inner bitmap is eventually finalized by the GC it will maintain a lock on the file as is stated in the docs:
The file remains locked until the Image is disposed.
This prevents you from deleting the file right away.
Assuming you are actually trying to modify the image in the file and save it back to the original location, you could do something like:
Bitmap bmp = null;
try
{
using (var bmpFromFile = (Bitmap)Image.FromFile(file))
{
bmp = new Bitmap(bmpFromFile);
}
using (var g = Graphics.FromImage(bmp))
{
// Make changes to bmp.
}
// Save bmp to a temp file.
// Delete the original file and move the temp file to that name.
}
finally
{
// Dispose bmp
using (bmp) { }
}
Alternatively, load the file into an intermediate MemoryStream then create the bitmap from the memory stream as suggested here.

Deleting File which is displayed in picturebox

I am selecting file from openfiledialoge and displaying it in picturebox and its name in textbox when I click on delete button I am getting exception The process cannot access the file because it is being used by another process.
I searched a lot for this exception to get resolved but i didn't fine any of them working, when i tried closing file with imagename which is in textbox i.e the file i am displaying in picturebox ; using IsFileLocked method,this closes and deletes all files of particular directory path ,but how can I delete the only file shown in picturebox,where I am going wrong
public partial class RemoveAds : Form
{
OpenFileDialog ofd = null;
string path = #"C:\Users\Monika\Documents\Visual Studio 2010\Projects\OnlineExam\OnlineExam\Image\"; // this is the path that you are checking.
public RemoveAds()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (System.IO.Directory.Exists(path))
{
ofd = new OpenFileDialog();
ofd.InitialDirectory = path;
DialogResult dr = new DialogResult();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Image img = new Bitmap(ofd.FileName);
string imgName = ofd.SafeFileName;
txtImageName.Text = imgName;
pictureBox1.Image = img.GetThumbnailImage(350, 350, null, new IntPtr());
ofd.RestoreDirectory = true;
}
}
else
{
return;
}
}
private void button2_Click(object sender, EventArgs e)
{
//Image img = new Bitmap(ofd.FileName);
string imgName = ofd.SafeFileName;
if (Directory.Exists(path))
{
var directory = new DirectoryInfo(path);
foreach (FileInfo file in directory.GetFiles())
{ if(!IsFileLocked(file))
file.Delete();
}
}
}
public static Boolean IsFileLocked(FileInfo path)
{
FileStream stream = null;
try
{ //Don't change FileAccess to ReadWrite,
//because if a file is in readOnly, it fails.
stream = path.Open ( FileMode.Open, FileAccess.Read, FileShare.None );
}
catch (IOException)
{ //the file is unavailable because it is:
//still being written to or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
}
Thanks in advance for any help
The (previously) accepted answer to this question is very poor practice. If you read the documentation on System.Drawing.Bitmap, in particular for the overload that creates a bitmap from a file, you will find :
The file remains locked until the Bitmap is disposed.
in your code you create the bitmap and store it in a local variable but you never dispose of it when you are done. This means your image object has gone out of scope but has not released its lock on the image file you are trying to delete. For all objects that implement IDisposable (like Bitmap) you must dispose of them yourself. See this question for example (or search for others - this is a very important concept!).
To correct the problem properly you simply need to dispose of the image when you are done with it :
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Image img = new Bitmap(ofd.FileName); // create the bitmap
string imgName = ofd.SafeFileName;
txtImageName.Text = imgName;
pictureBox1.Image = img.GetThumbnailImage(350, 350, null, new IntPtr());
ofd.RestoreDirectory = true;
img.Dispose(); // dispose the bitmap object
}
Please do not take the advice in the answer below - you should nearly never need to call GC.Collect and if you need to do it to make things work it should be a very strong signal that you are doing something else wrong.
Also, if you only want to delete the one file (the bitmap you have displayed) your deletion code is wrong and will delete every file in the directory as well (this is just repeating Adel's point). Further, rather than keep a global OpenFileDialog object alive simply to store the file name, I would suggest getting rid of that and saving just the file info :
FileInfo imageFileinfo; //add this
//OpenFileDialog ofd = null; Get rid of this
private void button1_Click(object sender, EventArgs e)
{
if (System.IO.Directory.Exists(path))
{
OpenFileDialog ofd = new OpenFileDialog(); //make ofd local
ofd.InitialDirectory = path;
DialogResult dr = new DialogResult();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Image img = new Bitmap(ofd.FileName);
imageFileinfo = new FileInfo(ofd.FileName); // save the file name
string imgName = ofd.SafeFileName;
txtImageName.Text = imgName;
pictureBox1.Image = img.GetThumbnailImage(350, 350, null, new IntPtr());
ofd.RestoreDirectory = true;
img.Dispose();
}
ofd.Dispose(); //don't forget to dispose it!
}
else
{
return;
}
}
Then in your second button handler you can just delete the one file you are interested in.
private void button2_Click(object sender, EventArgs e)
{
if (!IsFileLocked(imageFileinfo))
{
imageFileinfo.Delete();
}
}
I had the same problem : I loaded a file in a PictureBox and when trying to delete it I got the same exception.
This occurred only when the image was displayed.
I tried them all :
picSelectedPicture.Image.Dispose();
picSelectedPicture.Image = null;
picSelectedPicture.ImageLocation = null;
and still got the same exception.
Then I found this on CodeProject : [c#] delete image which is opened in picturebox.
Instead of using PictureBox.Load() it creates an Image from the file and sets it as PictureBox.Image:
...
// Create image from file and display it in the PictureBox
Image image = GetCopyImage(imagePath);
picSelectedPicture.Image = image;
...
private Image GetCopyImage(string path) {
using (Image image = Image.FromFile(path)) {
Bitmap bitmap = new Bitmap(image);
return bitmap;
}
}
No more exceptions when I delete the file.
IMHO, this is the most suitable solution.
EDIT
I forgot to mention that you can safely delete the file immediately after display :
...
// Create image from file and display it in the PictureBox
Image image = GetCopyImage(imagePath);
picSelectedPicture.Image = image;
System.IO.File.Delete(imagePath);
...
use this code
string imgName = ofd.SafeFileName;
if (Directory.Exists(path))
{
var directory = new DirectoryInfo(path);
foreach (FileInfo file in directory.GetFiles())
{
GC.Collect();
GC.WaitForPendingFinalizers();
file.Delete();
}
}
Your button2_Click event handler is cycling through all the files inside your directory & doing the deletes.
You need to change your code like the following:
public partial class RemoveAds : Form
{
OpenFileDialog ofd = null;
string path = #"C:\Users\Monika\Documents\Visual Studio 2010\Projects\OnlineExam\OnlineExam\Image\"; // this is the path that you are checking.
string fullFilePath;
public RemoveAds()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (System.IO.Directory.Exists(path))
{
ofd = new OpenFileDialog();
ofd.InitialDirectory = path;
DialogResult dr = new DialogResult();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Image img = new Bitmap(ofd.FileName);
string imgName = ofd.SafeFileName;
txtImageName.Text = imgName;
pictureBox1.Image = img.GetThumbnailImage(350, 350, null, new IntPtr());
fullFilePath = ofd.FilePath;
ofd.RestoreDirectory = true;
}
}
else
{
return;
}
}
private void button2_Click(object sender, EventArgs e)
{
FileInfo file = new FileInfo(fullFilePath);
if(!IsFileLocked(file))
file.Delete();
}
}
public static Boolean IsFileLocked(FileInfo path)
{
FileStream stream = null;
try
{ //Don't change FileAccess to ReadWrite,
//because if a file is in readOnly, it fails.
stream = path.Open ( FileMode.Open, FileAccess.Read, FileShare.None );
}
catch (IOException)
{ //the file is unavailable because it is:
//still being written to or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
}
By using GetThumnailImage you have to specify the width and height which is static.
Use the Load method instead.
eg: pictureBox1.Load(Path to the image); by using this u will have no problem in deleting the image or the folder before closing the app. no other methods need to be created.
hope this helps

C# OpenFileDialog not opening file

I'm trying to use openFileDialog to open a Bitmap image and place it on my form. My form construtor...
public Form1()
{
InitializeComponent();
drawing = new Bitmap(drawingPanel.Width, drawingPanel.Height, drawingPanel.CreateGraphics());
Graphics.FromImage(drawing).Clear(Color.White);
// set default value for line thickness
tbThickness.Text = "5";
}
... opens a new form with a blank screen, and I can draw on it using the mouse and various color selector buttons. I then save the file with this method:
private void btnSave_Click(object sender, EventArgs e)
{
// save drawing
if (file == null) // file is a FileInfo object that I want to use
// to check to see if the file already exists
// I haven't worked that out yet
{
drawing.Save("test.bmp");
//SaveBitmap saveForm = new SaveBitmap();
//saveForm.Show();
}
else
{
drawing.Save(fi.FullName);
}
}
The image does save to the debug folder as a .bmp file. Then I use OpenFileDialog to open the file:
private void btnOpen_Click(object sender, EventArgs e)
{
FileStream myStream;
OpenFileDialog openFile = new OpenFileDialog();
openFile.Filter = "bmp files (*.bmp)|*.bmp";
if (openFile.ShowDialog() == DialogResult.OK)
{
try
{
if ((myStream = (FileStream)openFile.OpenFile()) != null)
{
using (myStream)
{
PictureBox picBox = new PictureBox();
picBox.Location = drawingPanel.Location;
picBox.Size = drawingPanel.Size;
picBox.Image = new Bitmap(openFile.FileName);
this.Controls.Add(picBox);
}
}
}
catch (Exception ex)
{
}
}
}
What happes is that OpenFileDialog box comes up. When I select the file test.bmp, the screen goes away and then reappears, when I select it again, the OpenFileDialog window goes away and I'm back to my form with no image. Was hoping for some pointers. No compile or runtime errors.
Why are you calling ShowDialog() twice?
Just call ShowDialog once, so it doesn't open twice, like you indicated.
From MSDN:
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.Filter = "bmp files (*.bmp)|*.bmp";
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
if ((myStream = openFileDialog1.OpenFile()) != null)
{
using (myStream)
{
// Insert code to read the stream here.
PictureBox picBox = new PictureBox();
picBox.Location = drawingPanel.Location;
picBox.Size = drawingPanel.Size;
picBox.Image = new Bitmap (myStream);
this.Controls.Add(picBox);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
You open a dialog panel, then when it closes you check to see if the result was OK; then you open another new dialog in the using block; then you assign the image result of that to the PictureBox, and then throw everything away when the using block disposes.
You're calling ShowDialogue twice which is likely the source of your problem. Just use the following code, remove everything else from the method. Your use of using is also incorrect. it does clean up which is disposing of the results. You need to refactor or remove the using statement.
private void btnOpen_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog()
{
dlg.Title = "Open Image";
dlg.Filter = "bmp files (*.bmp)|*.bmp";
if (dlg.ShowDialog() == DialogResult.OK)
{
PictureBox picBox = new PictureBox();
picBox.Location = drawingPanel.Location;
picBox.Size = drawingPanel.Size;
picBox.Image = new Bitmap (dlg.FileName);
this.Controls.Add(picBox);
}
}
}
The code above works but is without clean up or error handling. I'll leave that to you.

C# - Making a BackgroundImage update in real time while editing it in other programs

first i have made an desktop AIR app with drag and drop functions to view the tiles image, but because of some issues, i've tryed to make the same in C#. what do you think is the better choice ? ( i know for this little programm performance is not the question, but i mean the dufficulty and complexity of both )
I was building a simple TileViewer, so a picture is selected in openFileDialog and set as tiled BackgroundImage of the Form.
My problem is:
I've been using a timer ( interval = 500 ) to reload the image, so if i edit the image in photoshop, the TileViewer will automaticaly reload the updated image ( as i save it in photoshop ).
BUT the problem is that photoshop doesnt have premissions to do so, cuz the image is opened in an other programm ( my TileViewer )
I removed the Timer and made a refresh button instead. but its the same problem.
I thought of making a copy of the bitmap data, but then I get an error that I dont have enought memory for a 32px x 32px img.
My code :
private string FileName;
public Form1() {
InitializeComponent();
FileName = "";
}
// OPEN FILE btn
private void button1_Click( object sender, EventArgs e ) {
openFileDialog1.Filter = "PNG Files (*.png)|*.png|JPG Files (*.jpg)|*.jpg";
if(openFileDialog1.ShowDialog() == DialogResult.OK) {
button2.Enabled = true;
FileName = openFileDialog1.FileName;
setImage();
}
}
// REFRESH btn
private void button2_Click( object sender, EventArgs e ) {
if( FileName != "" ) {
setImage();
}
}
private void setImage() {
Bitmap tempImg = new Bitmap( FileName );
Rectangle rect = new Rectangle(0, 0, 100, 100);
PixelFormat format = tempImg.PixelFormat;
this.BackgroundImage = new Bitmap( FileName ).Clone( rect, format );
}
So guys, if you have any suggestions or solutions, let me know.
Update 2:
Another issue was is in line Rectangle rect = new Rectangle(0, 0, 100, 100);
where in the constructor you should pass the width and heigth of the new image not an arbitary value like 100.
thats why the runtime says that you're out of memory !! (It did for me and I have a 18 GB monster machine)
Update:
your're leaking objects hence the out of memory exception
(Haans Already warned you about disposing objects thats.)
Try the following code
private string FileName;
public Form1()
{
InitializeComponent();
FileName = "";
}
// OPEN FILE btn
private void button1_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "PNG Files (*.png)|*.png|JPG Files (*.jpg)|*.jpg";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
FileName = openFileDialog1.FileName;
setImage();
}
}
// REFRESH btn
private void button2_Click(object sender, EventArgs e)
{
if (FileName != "")
{
setImage();
}
}
private void setImage()
{
Stream str=new FileStream(FileName,FileMode.Open, FileAccess.Read,FileShare.Read);
Bitmap tempImg= new Bitmap(Bitmap.FromStream(str));
str.Close();
using( tempImg)
{
Rectangle rect = new Rectangle(0, 0, tempImg.Width, tempImg.Height);
PixelFormat format = tempImg.PixelFormat;
this.BackgroundImage = new Bitmap(tempImg);
}
}
OLD Answer
Like Jason said
you might need to do somethinglike this in your setimage method
Stream str = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
this.BackgroundImage = Bitmap.FromStream(str);
str.Close();
Note that you're better off closing any stream that you open after you use it.
instead of
private void setImage() {
Bitmap tempImg = new Bitmap( FileName ); <---
Rectangle rect = new Rectangle(0, 0, 100, 100);
PixelFormat format = tempImg.PixelFormat;
this.BackgroundImage = new Bitmap( FileName ).Clone( rect, format );
}
The Bitmap.Clone() method doesn't do what you hope it does. It creates a "shallow" copy. You get a new Bitmap object but it still uses the underlying pixel buffer. Including the memory-mapped file that GDI+ uses to keep the pixel data out of the swap file. Which in turn puts a lock on the file.
You'll need to make a "deep" copy, do so with the Bitmap(Image) constructor. Like this:
private void setImage() {
using (var tempImg = new Bitmap(FileName)) {
this.BackgroundImage = new Bitmap(tempImg);
}
}
The using statement ensures the original image is disposed and the lock gets released.
You will need to open the file manually with ReadOnly flags, and then load the payload of the file into the bitmap by hand.
an alternitive would be to copy the file and then open the copy.
See if you cna run the following code while photoshop has the file open
FileStream s2 = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.Read);

Categories