FileInfo.Delete不会立即删除该文件

时间:2016-04-12 16:14:06

标签: c#

我目前遇到了一个严重的问题。我正在扫描目录中的文件,然后我处理它们[用File.ReadAllText(fi.FullName)读取内容],然后删除该文件。线程休眠200毫秒,然后重新开始(扫描,处理,删除)。问题在于,有时候,我可以看到已经删除的文件出现在下一次扫描中,这种情况不会偶尔发生,只会偶尔发生。

List<FileInfo> files = GetFiles();
if (files != null)
{
    foreach (FileInfo fi in files)
    {
       if (ProcessFile(fi))
       {
           fi.Delete();
           log.Info("Report file: " + fi.FullName + " has been deleted");
       }
   }
}

这是GetFiles方法

internal List<FileInfo> GetFiles()
{
    try
    {
        DirectoryInfo info = new DirectoryInfo(scanDir);
        List<FileInfo> files = info.GetFiles().OrderBy(p => p.CreationTime).Take(10).ToList(); //oldest file first
        return files;
    }
    catch (Exception ex)
    {
       log.Error("Getting files from directory: " + scanDir + ". Error: " + ex.ToString());
        return null;
    }
}

我在其他帖子中读过FileInfo.Delete()需要一些时间,但Microsoft文档没有说明这一点。所以我不确定是什么在讨价还价。任何人都可以发现代码有什么问题吗?是否有关于fileInfo.Delete()是阻塞调用的官方文档?或者它是否简单标记要删除的文件?

修改 这里是ProcessFile中唯一对FileInfo的引用

string message = File.ReadAllText(fi.FullName);

相信File.ReadAllText会关闭文件,并且不应该留下任何句柄如果错误请纠正我!...此外,这只会偶尔发生,而不是所有文件(我正在处理10个文件,它只发生在1)

1 个答案:

答案 0 :(得分:5)

来自Microsoft Reference Source

   public override void Delete()
    {
#if FEATURE_CORECLR
        FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, DisplayPath, FullPath);
        state.EnsureState();
#else
        // For security check, path should be resolved to an absolute path.
        new FileIOPermission(FileIOPermissionAccess.Write, new String[] { FullPath }, false, false).Demand();
#endif

        bool r = Win32Native.DeleteFile(FullPath);
        if (!r) {
            int hr = Marshal.GetLastWin32Error();
            if (hr==Win32Native.ERROR_FILE_NOT_FOUND)
                return;
            else
                __Error.WinIOError(hr, DisplayPath);
        }
    }

显示Delete方法在Win32Native.DeleteFile中使用kernel32.dll方法。进一步检查kernel32.dll可以在this超过500页的参考资料中找到。

在第79页,您可以找到对DeleteFile

的引用
1.49 DeleteFile

The DeleteFile function deletes an existing file.

DeleteFile: procedure
(
lpFileName: string
);
stdcall;
returns( "eax" );
external( "__imp__DeleteFileA@4" );

Parameters

lpFileName
[in] Pointer to a null-terminated string that specifies the file to be deleted.

Windows NT/2000: In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to nearly 32,000 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. For more information, see File Name Conventions.

Windows 95/98: This string must not exceed MAX_PATH characters.

Return Values

If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
Remarks
If an application attempts to delete a file that does not exist, the DeleteFile function fails.
To delete or rename a file, you must have either delete permission on the file or delete child permission in the parent directory. 
If you set up a directory with all access except delete and delete child and the ACLs of new files are inherited, then you should be able to create a file without being able to delete it. However, you can then create a file, and you will get all the access you request on the handle returned to you at the time you create the file. 
If you requested delete permission at the time you created the file, you could delete or rename the file with that handle but not with any other.

Windows 95: The DeleteFile function deletes a file even if it is open for normal I/O or as a memory-mapped file. To prevent loss of data, close files before attempting to delete them.

Windows NT/2000: The DeleteFile function fails if an application attempts to delete a file that is open for normal I/O or as a memory-mapped file.

To close an open file, use the CloseHandle function.

MAPI: For more information, see Syntax and Limitations for Win32 Functions Useful in MAPI Development.

由于它使用kernel32.dll,它与我们从Windows UI中删除文件时所执行的操作共享相同的机制。

internal const String KERNEL32 = "kernel32.dll";
...
[DllImport(KERNEL32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)]
[ResourceExposure(ResourceScope.Machine)]
internal static extern bool DeleteFile(String path);

因此,这表明它不是您可能怀疑的“阻塞”功能。只要文件是可删除的(没有访问权限错误或I / O错误),删除就需要时间。

您的案例的一种解决方法是尝试先收集您要删除的每个文件,然后将它们一起删除,例如,使用异步任务或BackgroundWorker之类的内容。