【问题标题】:Why is PictureBox.Load locking image on some systems?为什么 PictureBox.Load 在某些系统上会锁定图像?
【发布时间】:2014-03-04 16:26:54
【问题描述】:

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

你好,

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

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

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

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

嗯,这当然没什么不寻常的。问题是我在几个系统上测试了该应用程序。没有人显示此异常。直到现在我还看不到代码错误。

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

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

所以这是我放在一起的一些代码,它们显示了相同的行为。我在窗体上使用了 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 两次,我不会。如果我启动应用程序并先单击按钮 1,然后单击按钮 2,我将得到异常。所以,Picture.Load-Function 会以某种方式阻止文件,即使我将其丢弃。

在网上搜索的时候,发现了一篇来自微软的文章:http://support.microsoft.com/kb/309482/en-us。但它并没有击中靶心。

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

这是为什么呢?所呈现的版本有何不同?


编辑

好的,因为到目前为止的第一个也是唯一一个反应,在我看来,仍然不清楚,真正发生了什么:如果我采用上面的代码,把它放在一个 Windows 窗体应用程序中,编译它并运行它在不同的计算机上(在工作中,在家里,没关系)它可以工作 - 按钮 1 和按钮 2(与它们链接的 Click 功能)可以随心所欲地使用 - 不会抛出异常。如果我在更改用户文件夹的计算机上运行该应用程序,然后第二次单击 button1 - bam - IOException,文件被进程锁定。只要我不按 button1,Button2 就可以工作。

第一个答案暗示,我应该锁定每个系统。但我不会(只要我不更改用户文件夹)!我在每一台我能拿到手的计算机上测试了它——没有 IOException。我建立了一个新系统,只是为了排除对我公司系统的一些特殊更改——两个 buttonx_Click 功能都有效——也不例外。我什至在另一台计算机上编译了程序 - 相同的行为。唯一抛出该异常的三个系统是用户文件夹已更改的系统。

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

有人吗?

【问题讨论】:

    标签: c# winforms bitmap picturebox file-locking


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 虽然我理解答案,但它只占观察到的行为的一半。如果实际上是文件流锁定了图像,那么所有系统上的行为不应该都相同吗?为什么如果我“削弱”系统,我只能获得锁定行为?如果我自己执行 FileStream-Version 并且不处理 FileStream,我的所有测试系统都会出现异常(如预期的那样!)。所以必须有区别。
    • 我完全不相信这是特定于某些机器的。你刚从一个客户那里得到了一个错误报告,发现它并去寻找一个解释来解释为什么只有这个客户看到它。他们都这样做,但很容易避免它,因为两次加载完全相同的图像并不常见。只需在您自己的机器上复制即可。
    • 所说的应用程序(不是上面的结构)只处理一个位图文件,它自己创建并显示给用户。没有人可以改变它的名字,它是固定的。所以用户对结果没有任何影响。用更少的代码重现这两种效果的最简单方法是上面的示例。只要文件格式相同,您就可以使用不同的文件名。结果是同名“Test.fileformat”。但这不是重点。当我使用上面的示例时,两个版本都可以在我公司的每个系统上运行,但如果你说的适用,版本 1 不应该。
    • 嗯,它适用于所有系统,但我更改了用户文件夹的系统除外。
    【解决方案2】:

    这可以工作并解锁文件

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多