为什么PictureBox.Load在某些系统上锁定图像?

时间:2014-03-04 16:26:55

标签: c# winforms bitmap picturebox file-locking

(如果您不想阅读整个故事,请参阅问题底部的编辑。)

嗨,

我是stackoverflow的新手。不要误会我的意思,我经常使用它。但到目前为止,我从未真正发布过一些东西。这是因为我没有新的/有用的东西说,我的英语不是那么好。第一件事(可能有)改变了,后者没有改变。

我最近在客户的Windows 7系统上遇到了问题。我通过ClickOnce发布了一个C#.Net 4.0 Windows Forms应用程序。基本上,它是一个创建位图文件并将其显示给用户的应用程序。如果在创建之前存在位图,则首先删除现有文件。之后,新文件由PictureBox创建和加载。

客户系统发生了以下情况:启动应用程序后,第一次创建成功 - 第二次创建成功,后续所有创建都没有。该文件无法删除,因为某些进程阻止了该文件。这个过程就是应用程序本身。

  

System.IO.IOException:进程无法访问文件“filename”,因为它正由另一个进程使用。

嗯,当然这没什么不寻常的。问题是我在几个系统上测试了应用程序。没有显示此例外。到目前为止,我无法看到代码错误。

所以我在客户的系统上看得更近一点:我唯一能找到的区别是,他们更改了用户文件夹,以便它们不在Windows分区上,而是在另一个分区上(C :\ Users - > D:\ Users)。我在互联网上搜索了一条指令,并在我的一个测试系统上做了同样的事情。令我惊讶的是,当我在其上运行我的应用程序时,我遇到了同样的异常。

有了这个,我可以改变我的代码,以便不再发生异常。但我不明白为什么会这样。所以也许我的代码有问题,而错误只是在这种特殊情况下揭示出来。或者代码可以正常,原因在于其他地方。我只是希望你能够帮助我。

所以这里是我放在一起的一些代码,显示了相同的行为。我在Form上使用了3个按钮,一个OpenFileDialog和一个PictureBox。首先要做的是选择一个图像文件。通过按其余两个按钮之一,它将被复制到应用程序的主文件夹中。复制后,它由PictureBox显示。顺便说一下,如果它是ClickOnce应用程序或“普通”应用程序似乎并不重要。

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);
}

现在发生的情况如下:如果我启动应用程序并单击button1两次,我将获得异常(在第二次单击时)。如果我启动它并单击button2两次,我就不会。如果我启动应用程序并首先单击buttons1,然后单击button2,我将获得异常。所以,Picture.Load-Function以某种方式阻止文件,即使我处理它。

当我在互联网上搜索时,我发现了一篇来自微软的文章:http://support.microsoft.com/kb/309482/en-us。但它并没有引起牛市的注意。

请注意,两个版本都在我的所有测试机器上运行。当我将users文件夹更改为非Windows分区时,我只是得到了异常。

为什么?这些版本的差异在哪里?


修改

好的,由于到目前为止的第一个也是唯一的反应,在我看来,它仍然不清楚,究竟发生了什么:如果我采用上面的代码,将它放在Windows窗体应用程序中,编译并运行它在不同的计算机上(在工作中,在家里,无关紧要)它的工作原理 - button1和button2(链接到它们的Click函数)可以随时使用 - 没有异常抛出。如果我在计算机上运行应用程序,我更改了用户文件夹,然后第二次单击button1 - bam - IOException,文件被进程锁定。只要我没有按下button1,Button2就会工作。

第一个答案意味着,我应该锁定每个系统。但我不(只要我不更改用户文件夹)!我在每台可以接触到的计算机上测试过它 - 没有IOException。我设置了一个新系统,只是为了排除我公司系统的一些特殊变化 - 都是buttonx_Click函数工作 - 也没有例外。我甚至在另一台计算机上编译了程序 - 同样的行为。抛出该异常的唯一三个系统是具有已更改用户文件夹的系统。

到目前为止,我不知道为什么会出现这种行为上的差异。有人能帮助我吗?

任何人

2 个答案:

答案 0 :(得分:7)

是的,这很正常。发生在任何操作系统上,与Users文件夹位置无关。 PictureBox.Load()方法旨在用于从文件系统 other 的位置加载图像。就像一个网站。这很慢,它避免了在下载过程中冻结UI。

当它发现您传递的URL实际上是文件而不是网站名称时,它在内部使用FileStream。在放置PictureBox本身或再次调用Load()方法之前,此FileStream不会被释放。一个要求,因为Image.FromStream()要求流在不再使用图像之前保持可读性。正是这个FileStream保持对文件的锁定。处置PictureBox.Image还不足以配置FileStream,Image对象不知道它正在图片框中显示。

有几种方法可以解决这个问题:

  • 使用Image属性而不是Load(),从Image.FromFile()中分配它。现在处理图像也会释放文件上的锁定。正如你发现的那样
  • 保留虚拟图像,可能会显示“正在加载...”位图。首先加载()以释放文件上的锁
  • 处理PictureBox并重新创建它。

答案 1 :(得分:0)

这有效并解锁文件

Image img= Image.FromFile(mypath);
                
Graphics g = pictureBox1.CreateGraphics();
g.DrawImage(img,0,0);
            
img.Dispose();