1

好吧,事情已经到了这个地步。我在许多其他网站中搜索了这个网站,似乎没有人能给我一个直接的答案,所以我将尝试直接询问。在这个问题上呆了大约 3 天,我不能再浪费时间了。

目标:我正在构建的应用程序在 WPF 中,并将用作我的设计团队和我将很快开展的项目的错误跟踪器。由于我们将使用 C++ 构建游戏,因此发生的大多数错误都会对它们具有视觉元素,因此我包含了在用户将错误添加到列表时提供相关错误图像的功能。然后我拍摄该图像并将其保存到本地目录(用于测试)。现在,Error 对象中的图像路径指向了通向本地目录的路径。此功能已经过测试并且工作正常。当我想从列表中删除一个错误时,我的问题就出现了。我收到了那个非常臭名昭著的“IO Exception”,说我要删除的图像正在被另一个进程使用。

到目前为止:起初我尝试了非常优雅的解决方案,但是对于所有事情,您都只是想看看您是否可以让事情甚至工作。所以我现在使用的大部分代码都是实验性的和激进的。因此,请在查看时注意所使用的代码是出于绝望,因此可能已经尝试过任何“简单”的解决方案(我确实对此进行了很多研究,因为我讨厌这样做)。我能想到的事情是荒谬的处置和强制垃圾收集被调用,所以请不要评论这种做法的负面性质,我很清楚:)。

编码

将图像保存到本地目录

public void OnBrowseClick()
    {
        Microsoft.Win32.OpenFileDialog openBox = new Microsoft.Win32.OpenFileDialog();

        // Show dialog box to user and grab output
        Nullable<bool> result = openBox.ShowDialog();

        if (result == true)
        {
            // Create temp variable to hold local path string
            string localPath = Directory.GetCurrentDirectory();      

            // Grab the extension of the specified file path
            string extension = openBox.FileName.Substring(openBox.FileName.LastIndexOf("\\"));

            // Add extension to local path
            localPath += extension;

            // Create local copy of image at given file path (being ridiculous at this point)
            using (Stream stream = new FileStream(openBox.FileName, FileMode.Open, FileAccess.ReadWrite))
            {
                using (Bitmap bmp = LoadImage(stream))
                {
                    using (Bitmap temp = (Bitmap)bmp.Clone())
                    {
                        temp.Save(localPath);
                        temp.Dispose();
                    }

                    bmp.Dispose();
                }

                stream.Dispose();
            }

            // Set the URL in the image text box (UI stuff)
            LocalError.ImagePath = localPath;   
        }
    }

下面是上面函数中用到的LoadImage函数

private Bitmap LoadImage(Stream stream)
    {
        Bitmap retval = null;

        using (Bitmap bitmap = new Bitmap(stream))
        {
            retval = new Bitmap(bitmap.Width, bitmap.Height, bitmap.PixelFormat);

            using (Graphics gdi = Graphics.FromImage(retval))
            {
                gdi.DrawImageUnscaled(bitmap, 0, 0); 
                gdi.Flush();
                gdi.Dispose();
                bitmap.Dispose();
            }
        }

        // Garbage collection here to be safe
        GC.WaitForPendingFinalizers();
        GC.Collect();

        return retval;
    } 

最后我们来到我尝试删除图像的地方

public void OnDeleteClick()
    {
        // Ask user to make sure they want to delete selected item(s)
        MessageBoxResult result = MessageBox.Show("Are you sure you want to delete selected item(s) from the list?",
                                "Delete", MessageBoxButton.YesNo);

        if (result == MessageBoxResult.Yes)
        {
            for( int i = 0; i < Parent.ErrorListControl.ErrorDataGrid.SelectedItems.Count; ++i)
            {
                // Get path to image
                string path = (Parent.ErrorListControl.ErrorDataGrid.SelectedItems[i] as Error).ImagePath;

                // Even tried calling garbage collection here!!!!!
                System.GC.WaitForPendingFinalizers();
                System.GC.Collect();
                File.Delete(path);

                // Remove the error from the error list
                Parent.ErrorListVM.ErrorList.Remove((Error)Parent.ErrorListControl.ErrorDataGrid.SelectedItems[i]);

                // Decrement counter because we altered the list while in a loop
                i--;
            }
        }
    }

注意:如果有人希望我进一步解释任何事情,或者如果您需要知道我遗漏的内容,请询问我会尽快回复您!在这一点上,任何建议都是有帮助的,我绝对不知道我做错了什么。我通常只在 C++ 环境中编程,所以我倾向于管理我自己的内存,这整个“垃圾收集”的事情真的给我们的项目带来了麻烦!(题外话:我不知道为什么我没有得到任何颜色突出显示,所以我向任何花时间阅读本文的人道歉)。

4

2 回答 2

0

既然您的LoadImage方法只是简单地复制图像,为什么不使用File.Copy(source, dest)并避免使用所有的位图、绘图等呢?您的目标可能是在创建本地位图后对其进行修改,但在复制后仍然可以完成。

此外,在使用using块时,.Dispose()不需要显式,因为using块会为您执行此操作:

using (var obj = new SomeDisposableObject())
{
    // code here
    // obj.Dispose(); <-- not needed, since...
} // ...at this point obj.Dispose is called automatically.
于 2012-09-14T15:40:00.500 回答
0

Here's a simple way to do what you want. In this example, I'm using Path.GetTempFileName() to generate a random file name in the local user's temp directory. If you don't need to persist the files then it's a good place to store them temporarily. Also, the user could theoretically import two files with the same name. So you want to use some kind of random filename generation or other mechanism to avoid conflicts.

private void browseButton_Click(object sender, RoutedEventArgs e)
{
    var openFileDialog = new Microsoft.Win32.OpenFileDialog();

    if (openFileDialog.ShowDialog(this) == true)
    {
        using (Bitmap originalImage = new Bitmap(openFileDialog.FileName))
        {
            string tempFileName = System.IO.Path.GetTempFileName();

            originalImage.Save(tempFileName);

            // LocalError.LocalPath
            LocalPath = tempFileName;
        }
    }
}

private void deleteButton_Click(object sender, RoutedEventArgs e)
{
    if (File.Exists(LocalPath))
    {
        File.Delete(LocalPath);
    }
}

Although a simple File.Copy should suffice as long as you have the right paths, I was just providing a solution that matched your question.

EDIT: Actually the current directory does not seem to be changed by the OpenFileDialog. I could swear that it did at some point. So I don't think this is your problem. Regardless, this code still works for me and you shouldn't require anything more complicated than this.

EDIT #2: It seems the lock is actually caused by the image being databound to the view and presumably locked by the BitmapSource. You should be able to create it without locking the file. Generally, this is slower so don't do it this way unless you need to be able to modify or delete the file.

bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.CacheOption = BitmapCacheOption.OnLoad;
bitmapSource.CreateOption = BitmapCreateOptions.IgnoreImageCache;
bitmapSource.UriSource = new Uri(ImagePath, UriKind.Absolute);
bitmapSource.EndInit();
于 2012-09-14T15:44:57.973 回答