Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is PictureBox.Load locking image on some systems?

(Please see the edit on the bottom of the question, if you do not want to read the whole story.)

Hi,

I am new to stackoverflow. Don’t get me wrong, I use it quite often. But up until now I never actually posted something. This is because I did not have something new/useful to say and my English is not that good. The first thing (might have) changed, the latter did not.

I ran into a problem at a customer's Windows 7 system quite recently. I was shipping a C# .Net 4.0 Windows Forms application via ClickOnce. Basically, it is an application that creates a bitmap file and shows it to the user. If the bitmap exists prior to the creation, the existing file gets deleted first. After that the new file is created and loaded by a PictureBox.

The following thing occurred at the customer’s system: After starting the application the first creation succeeds – the second and all following ones do not. The file cannot be deleted, because some process is blocking it. This process is the application itself.

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

Well, of course that is nothing unusual. The thing is I tested the application on several systems. None showed this exception. And until now I am unable to see an code error.

So I looked a little bit closer on the customer’s system: The only difference I could find is, that they changed the users folder so that they are not located on the windows partition, but on a different one (C:\Users --> D:\Users). I searched for an instruction on the internet and did the same thing on one of my test systems. To my surprise I got the same exception when I ran my application on it.

With that I could change my code so that the exception does not occur anymore. But I do not understand why that is. So maybe there is something wrong with my code and the error just reveals itself under that special circumstances. Or maybe the code is okay and the reason lies somewhere else. I just hoped that you might be able to help me.

So here is some code I put together, that shows the same behavior. I used 3 buttons, an OpenFileDialog and a PictureBox on a Form. First thing to do is to choose an image file. By pressing one of the two remaining buttons it gets copied into the main folder of the application. After being copied it is shown by the PictureBox. By the way, it does not seem to matter if it is a ClickOnce-application or a “normal” one.

String m_FileName;

private void btnChooseFile_Click(object sender, EventArgs e) {
    if(openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) { // If file was chosen, set file name for later use and activate buttons.
        m_FileName = "Test" + Path.GetExtension(openFileDialog1.FileName);
    }
}

private void button1_Click(object sender, EventArgs e) {

    // This is not working.
    if(this.pictureBox1.Image != null) {
        //Image img = this.pictureBox1.Image; // I was not sure, if maybe the pictureBox somehow prevents the disposing of the image, as long as it's assigned to it.
        //this.pictureBox1.ImageLocation = null; // So I set them null, both the image and the image location.
        //this.pictureBox1.Image = null; 
        //img.Dispose(); // Then I disposed the image.

        this.pictureBox1.Image.Dispose(); // The short version. It is not working either way.
        this.pictureBox1.Image = null;
    }
    (new FileInfo(openFileDialog1.FileName)).CopyTo(m_FileName, true); // But still this is where the Exception occurs.
    this.pictureBox1.Load(m_FileName);
}

private void button2_Click(object sender, EventArgs e) {

    //This is working.
    if(this.pictureBox1.Image != null) {
        //Image img = this.pictureBox1.Image;
        //this.pictureBox1.Image = null;
        //img.Dispose();

        this.pictureBox1.Image.Dispose();
        this.pictureBox1.Image = null;
    }
    (new FileInfo(openFileDialog1.FileName)).CopyTo(m_FileName, true);
    pictureBox1.Image = Image.FromFile(m_FileName);
}

What happens now is the following: If I start the application and click button1 twice, I will get the exception (on the second click). If I start it and click button2 twice, I will not. If I start the application and click buttons1 first and after that button2, I will get the exception. So, the Picture.Load-Function somehow blocks the file, even if I dispose it.

When I searched on the internet, I found an article from microsoft: http://support.microsoft.com/kb/309482/en-us. But it does not hit the bull's eye.

Take into account, that both versions are working on all my test machines. I just get the exception when I change the users folder to a non-windows-partition.

Why is that? And where is the difference in the presented versions?


Edit

Okay, because of the first and only reaction so far, it seems to me, that it is still not clear, what really happens: If I take the above code, put it in a Windows Forms application, compile it and run it on different computers (at work, at home, does not matter) it works - both button1 and button2 (with the Click-functions linked to them) can be used as often as I like - no exception thrown. If I run the application on a computer, where I changed the users folder, and click button1 the second time - bam - IOException, file locked by process. Button2 works as long as I do not press button1.

The first answer implies, that I should get the locking on every system. But I DO NOT (as long as I do not change the users folder)! I tested it on every single computer I could get my hands on - no IOException. I set up a new system, just to rule out some special changes to the systems in my company - both buttonx_Click functions worked - no exception either. I even compiled the program on another computer - same behavior. The only three systems to throw that exception were the ones with the changed users folder.

So far I have no clue, why this difference in behavior occurs. Can somebody help me?

Anybody?

like image 346
user3379589 Avatar asked Mar 04 '14 16:03

user3379589


1 Answers

Yes, this is normal. Happens on any operating system, doesn't have anything to do with the Users folder location. The PictureBox.Load() method was intented to be used to load images from locations other than the file system. Like a web site. Which is slow, it avoids freezing the UI while the download is taking place.

It internally uses a FileStream when it discovers that the url you pass is actually a file and not a website name. This FileStream does not get disposed until the PictureBox itself is disposed or you call the Load() method again. A requirement because Image.FromStream() requires the stream to remain readable until the image is no longer used. It is this FileStream that keeps a lock on the file. Disposing the PictureBox.Image is not enough to also dispose the FileStream, the Image object doesn't know that it is being displayed inside a picture box.

There are several ways to solve this problem:

  • Use the Image property instead of Load(), assign it from Image.FromFile(). Disposing the Image now also releases the lock on the file. As you found out
  • Keep a dummy image around, one that perhaps displays a "Loading..." bitmap. Load() it first to release the lock on the file
  • Dispose the PictureBox and recreate it.
like image 82
Hans Passant Avatar answered Nov 14 '22 22:11

Hans Passant