首页 > 解决方案 > File.Exists 在 File.Delete 之后返回 true

问题描述

我有以下方法来删除具有提供路径的文件

private void DestroyFile(string path)
{
    try
    {
        if (File.Exists(path))
        {
            File.Delete(path);
        }
        if (File.Exists(path))
        {
            throw new IOException(string.Format("Failed to delete file: '{0}'.", path));
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

如果文件在 File.Delete 方法之后存在,我将收到抛出的 IOException。具体来说

System.IO.IOException):无法删除文件:'C:\Windows\TEMP\[FILE NAME]'。

我还确认执行完成后路径变量中的位置不存在该文件。我想知道我是否在 File.Delete 之后更新文件系统和使用 File.Exists 再次检查它之间遇到竞争条件。有没有更好的方法可以顺利删除?我知道如果文件不存在, File.Delete 不会返回错误,所以这些检查可能有点多余。我应该检查文件是否正在使用而不是它是否存在?

一些重要的附加信息:该程序可以并且确实经常成功运行,但最近经常看到此特定错误。

标签: c#file-io

解决方案


File.Delete将文件标记为删除。只有当文件的所有句柄都关闭时,文件才会真正被删除(如果没有这样的句柄 - 在 File.Delete 返回后它总是会被删除)。正如DeleteFile winapi 函数(由 C# 使用File.Delete)所记录的:

DeleteFile 函数将文件标记为在关闭时删除。因此,文件删除直到文件的最后一个句柄关闭后才会发生

通常,您删除的文件没有打开的句柄。或者,如果有打开的句柄——它们通常没有“删除”共享(这个共享允许另一个进程将文件标记为删除),所以当你尝试删除这样的文件时——它要么被删除(没有打开的句柄)要么访问被拒绝或类似的异常被抛出(一些句柄,但没有删除共享)。

但是,有时某些软件(例如防病毒软件或搜索索引器)可能会通过“删除”共享打开任意文件并将它们保留一段时间。如果您尝试删除此类文件 - 它不会出错,并且当该软件关闭其句柄时文件确实会被删除。但是,File.Exists对于此类“待删除”文件将返回 true。

您可以使用这个简单的程序重现此问题:

public class Program {
    public static void Main() {
        string path = @"G:\tmp\so\tmp.file";
        // create file with delete share and don't close handle
        var file = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete);
        DestroyFile(path);
        GC.KeepAlive(file);
    }

    private static void DestroyFile(string path) {
        try {

            if (File.Exists(path)) {
                // no error
                File.Delete(path);
            }
            // but still exists
            if (File.Exists(path)) {
                throw new IOException(string.Format("Failed to delete file: '{0}'.", path));
            }
        }
        catch (Exception ex) {
            throw ex;
        }
    }
}

您可以File.Exists在上面的程序中永远重试检查 - 文件将一直存在,直到您关闭句柄。

所以这就是你的情况 - 一些程序已经打开了这个文件的句柄FileShare.Delete

你应该预料到这种情况。例如 - 只需删除该File.Exists检查,因为您将文件标记为删除,无论如何它都会被删除。


推荐阅读