Sunday, March 15, 2009

Image file handle adventures.

Today I had an issue with not being able to delete a file programmatically.
While looking for the reason the file was 'locked' (the actual error message was "The process cannot access the file 'file.jpg' because it is being used by another process") I discovered a tool which can help finding out the process which is locking the file.

Process Explorer

Anyway, here is how the image was processed by the application:

Bitmap b = (Bitmap)Image.FromFile(_Filepath);
pictureBox1.Image = b;

// later in the code

System.IO.MemoryStream ms = new System.IO.MemoryStream();
pictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);

// even later in the code

File.Delete(_Filepath);

At the point where File.Delete() was called, the handle to the file existed (under some circumstances).

Here is how I implemented the fix initially:

public Bitmap getBitmapFromFile(string filename)
{
Image i = null;
using (Stream s = new FileStream(filename, FileMode.Open))
{
i = Image.FromStream(s);
s.Close();
}
return (Bitmap)i;
}

pictureBox1.Image = getBitmapFromFile(_Filepath);

// later in the code

System.IO.MemoryStream ms = new System.IO.MemoryStream();
pictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);

// even later in the code

File.Delete(_Filepath);

The approach did not work, however, this time returning me the error "A generic error occurred in GDI+" at the line where I tried to save the image.

Here is the reason why this happened:

Bitmap and Image constructor dependencies

In my case, obviously, "Additionally, if the stream was destroyed during the life of the Bitmap object, you cannot successfully access an image that was based on a stream" - see how I tried to close a stream, trying to release a handle to the file this way?

The possible solutions can be found here:

Understanding "A generic error occurred in GDI+." Error

That's how I fixed my problem eventually:

public Bitmap getBitmapFromFile(string filePath)
{
Image img = Image.FromFile(filePath);
Bitmap bmp = img as Bitmap;
Graphics g = Graphics.FromImage(bmp);
Bitmap bmpNew = new Bitmap(bmp);
g.DrawImage(bmpNew, new Point(0, 0));
g.Dispose();
bmp.Dispose();
img.Dispose();
return bmpNew;
}

pictureBox1.Image = Syco.Common.Util.getBitmapFromFile(_Filepath);

// later in the code

System.IO.MemoryStream ms = new System.IO.MemoryStream();
pictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);

// even later in the code

File.Delete(_Filepath);

Now the image is saved properly and no handle is held against the file, so it is deleted properly too.

by . Also posted on my website

No comments: