4

So I was writing a quick application to sort my wallpapers neatly into folders according to aspect ratio. Everything is going smoothly until I try to actually move the files (using FileInfo.MoveTo()). The application throws an exception:

System.IO.IOException The process cannot access the file because it is being used by another process.

The only problem is, there is no other process running on my computer that has that particular file open. I thought perhaps that because of the way I was using the file, perhaps some internal system subroutine on a different thread or something has the file open when I try to move it. Sure enough, a few lines above that, I set a property that calls an event that opens the file for reading. I'm assuming at least some of that happens asynchronously. Is there anyway to make it run synchronously? I must change that property or rewrite much of the code.

Here are some relevant bits of code, please forgive the crappy Visual C# default names for things, this isn't really a release quality piece of software yet:

private void button1_Click(object sender, EventArgs e)
{
    for (uint i = 0; i < filebox.Items.Count; i++)
    {
        if (!filebox.GetItemChecked((int)i)) continue;

        //This calls the selectedIndexChanged event to change the 'selectedImg' variable
        filebox.SelectedIndex = (int)i;

        if (selectedImg == null) continue;

        Size imgAspect = getImgAspect(selectedImg);

        //This is gonna be hella hardcoded for now
        //In the future this should be changed to be generic
        //and use some kind of setting schema to determine
        //the sort/filter results

        FileInfo file = ((FileInfo)filebox.SelectedItem);

        if (imgAspect.Width == 8 && imgAspect.Height == 5)
        {
            finalOut = outPath + "\\8x5\\" + file.Name;
        }
        else if (imgAspect.Width == 5 && imgAspect.Height == 4)
        {
            finalOut = outPath + "\\5x4\\" + file.Name;
        }
        else
        {
            finalOut = outPath + "\\Other\\" + file.Name;
        }

        //Me trying to tell C# to close the file
        selectedImg.Dispose();
        previewer.Image = null;

        //This is where the exception is thrown
        file.MoveTo(finalOut);
    }
}

//The suspected event handler
private void filebox_SelectedIndexChanged(object sender, EventArgs e)
{
    FileInfo selected;
    if (filebox.SelectedIndex >= filebox.Items.Count || filebox.SelectedIndex < 0) return;
    selected = (FileInfo)filebox.Items[filebox.SelectedIndex];

    try
    {
        //The suspected line of code
        selectedImg = new Bitmap((Stream)selected.OpenRead());
    }
    catch (Exception) { selectedImg = null;  }

    if (selectedImg != null)
        previewer.Image = ResizeImage(selectedImg, previewer.Size);
    else
        previewer.Image = null;
}

I have a long-fix in mind (that's probably more efficient anyway) but it presents more problems still :/

Any help would be greatly appreciated.

Evan C
  • 41
  • 1
  • 1
  • 2

3 Answers3

1

Since you are using your selectedImg as a Class scoped variable it is keeping a lock on the File while the Bitmap is open. I would use an using statement and then Clone the Bitmap into the variable you are using this will release the lock that Bitmap is keeping on the file.

Something like this.

using ( Bitmap img  = new Bitmap((Stream)selected.OpenRead()))
{
    selectedImg = (Bitmap)img.Clone();
}
Mark Hall
  • 53,938
  • 9
  • 94
  • 111
0

New answer:

I looked at the line where you do an OpenRead(). Clearly, this locks your file. It would be better to provide the file path instead of an stream, because you can't dispose your stream since bitmap would become erroneous.

Another thing I'm looking in your code which could be a bad practice is binding to FileInfo. Better create a data-transfer object/value object and bind to a collection of this type - some object which has the properties you need to show in your control -. That would help in order to avoid file locks.

In the other hand, you can do some trick: why don't you show streched to screen resolution images compressing them so image size would be extremly lower than actual ones and you provide a button called "Show in HQ"? That should solve the problem of preloading HD images. When the user clicks "Show in HQ" button, loads that image in memory, and when this is closed, it gets disposed.

It's ok for you?

If I'm not wrong, FileInfo doesn't block any file. You're not opening it but reading its metadata.

In the other hand, if you application shows images, you should move to memory visible ones and load them to your form from a memory stream.

That's reasonable because you can open a file stream, read its bytes and move them to a memory stream, leaving the lock against that file.

NOTE: This solution is fine for not so large images... Let me know if you're working with HD images.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • Unfortunately, I am working with many HD Images, so preloading every image isn't an option. My test directory has something like 120 HD images, some larger than 2000x4000 – Evan C Jan 17 '11 at 20:44
0
using(selectedImg = new Bitmap((Stream)selected))

Will that do it?

NiDeep
  • 43
  • 5