2

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);
     }
   }
  }
}
Jamshaid K.
  • 3,555
  • 1
  • 27
  • 42
  • How are you *loading* the images into the grid? – Glorin Oakenfoot Aug 19 '16 at 12:55
  • Let me include my `LoadImages();` method in the post also! – Jamshaid K. Aug 19 '16 at 12:57
  • Neither dgvImages.Rows.Clear() nor dgvImages.Dispose() is going to call the Dispose() method of any of the images you use. DGV cannot possibly know if the image might be in use somewhere else so it cannot safely do this. You have to take care of this yourself. GC.Collect() by itself isn't good enough either, you have to also call GC.WaitForPendingFinalizers(). If that still doesn't release the lock then you are using the image elsewhere. – Hans Passant Aug 19 '16 at 12:59
  • Check if the file is locked using the code: http://stackoverflow.com/a/11060322/3142139 – M.Hassan Aug 19 '16 at 13:01
  • I am not using the image anywhere else @HansPassant. I am sure about it. – Jamshaid K. Aug 19 '16 at 13:03
  • Well, we're pretty sure you are not calling the Dispose() method of the images you use and that you are not calling GC.WaitForPendingFinalizers() either. Write correct code to get ahead. – Hans Passant Aug 19 '16 at 13:06
  • @M.Hassan I Know the file is not being used elsewhere but in the two of these forms. so i think i don't need to know whether if file is being used or not. – Jamshaid K. Aug 19 '16 at 13:07
  • @HansPassant I am confused. I think I am calling the `GC.WaitForPendingFinalizers();` in my code above. but still it is not releasing the image. – Jamshaid K. Aug 19 '16 at 13:11
  • Another problem is that you do this in the CellContentClick event. Deleting the rows does not guarantee that the DGV isn't still holding on to the cell reference while it raises the event. Which prevents the cell from getting garbage collected, its Value property keeps a reference to the Image so it won't be collected either. You really *do* have to dispose the images yourself, some odds that setting the Value property of the cell to null might work. – Hans Passant Aug 19 '16 at 13:25

2 Answers2

1

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.

Community
  • 1
  • 1
Justin Shield
  • 2,390
  • 16
  • 12
  • I really appreciate the answer, but it is causing an argument exception in Program.cs class. it says the parameter is not valid. – Jamshaid K. Aug 19 '16 at 13:37
  • Try new Bitmap("file_location", true); That will get the bitmap to use icm as per the documentation here. https://msdn.microsoft.com/en-us/library/3135s427(v=vs.110).aspx According to the remarks, Use this constructor to open images with the following file formats: BMP, GIF, EXIF, JPG, PNG and TIFF. – Justin Shield Aug 19 '16 at 13:40
  • This was the best answer, I never knew `Image.FromFile` puts a lock on the Image. Thanks a lot!! – Jamshaid K. Aug 19 '16 at 13:47
1

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;
            }
}
M.Hassan
  • 10,282
  • 5
  • 65
  • 84