Cannot delete directory with Directory.Delete(path

2019-01-01 03:17发布

I'm using .NET 3.5, trying to recursively delete a directory using:

Directory.Delete(myPath, true);

My understanding is that this should throw if files are in use or there is a permissions problem, but otherwise it should delete the directory and all of its contents.

However, I occasionally get this:

System.IO.IOException: The directory is not empty.
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive)
    at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive)
    ...

I'm not surprised that the method sometimes throws, but I'm surprised to get this particular message when recursive is true. (I know the directory is not empty.)

Is there a reason I'd see this instead of AccessViolationException?

29条回答
看风景的人
2楼-- · 2019-01-01 03:27

Modern Async Answer

The accepted answer is just plain wrong, it might work for some people because the time taken to get files from disk frees up whatever was locking the files. The fact is, this happens because files get locked by some other process/stream/action. The other answers use Thread.Sleep (Yuck) to retry deleting the directory after some time. This question needs revisiting with a more modern answer.

public static async Task<bool> TryDeleteDirectory(
   string directoryPath,
   int maxRetries = 10,
   int millisecondsDelay = 30)
{
    if (directoryPath == null)
        throw new ArgumentNullException(directoryPath);
    if (maxRetries < 1)
        throw new ArgumentOutOfRangeException(nameof(maxRetries));
    if (millisecondsDelay < 1)
        throw new ArgumentOutOfRangeException(nameof(millisecondsDelay));

    for (int i = 0; i < maxRetries; ++i)
    {
        try
        {
            if (Directory.Exists(directoryPath))
            {
                Directory.Delete(directoryPath, true);
            }

            return true;
        }
        catch (IOException)
        {
            await Task.Delay(millisecondsDelay);
        }
        catch (UnauthorizedAccessException)
        {
            await Task.Delay(millisecondsDelay);
        }
    }

    return false;
}

Unit Tests

These tests show an example of how a locked file can cause the Directory.Delete to fail and how the TryDeleteDirectory method above fixes the problem.

[Fact]
public async Task TryDeleteDirectory_FileLocked_DirectoryNotDeletedReturnsFalse()
{
    var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
    var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
    var filePath = Path.Combine(directoryPath, "File.txt");

    try
    {
        Directory.CreateDirectory(directoryPath);
        Directory.CreateDirectory(subDirectoryPath);

        using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
        {
            var result = await TryDeleteDirectory(directoryPath, 3, 30);
            Assert.False(result);
            Assert.True(Directory.Exists(directoryPath));
        }
    }
    finally
    {
        if (Directory.Exists(directoryPath))
        {
            Directory.Delete(directoryPath, true);
        }
    }
}

[Fact]
public async Task TryDeleteDirectory_FileLockedThenReleased_DirectoryDeletedReturnsTrue()
{
    var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
    var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
    var filePath = Path.Combine(directoryPath, "File.txt");

    try
    {
        Directory.CreateDirectory(directoryPath);
        Directory.CreateDirectory(subDirectoryPath);

        Task<bool> task;
        using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
        {
            task = TryDeleteDirectory(directoryPath, 3, 30);
            await Task.Delay(30);
            Assert.True(Directory.Exists(directoryPath));
        }

        var result = await task;
        Assert.True(result);
        Assert.False(Directory.Exists(directoryPath));
    }
    finally
    {
        if (Directory.Exists(directoryPath))
        {
            Directory.Delete(directoryPath, true);
        }
    }
}
查看更多
冷夜・残月
3楼-- · 2019-01-01 03:29

This problem can appear on Windows when there are files in a directory (or in any subdirectory) which path length is greater than 260 symbols.

In such cases you need to delete \\\\?\C:\mydir instead of C:\mydir. About the 260 symbols limit you can read here.

查看更多
何处买醉
4楼-- · 2019-01-01 03:29

add true in the second param.

Directory.Delete(path, true);

It will remove all.

查看更多
倾城一夜雪
5楼-- · 2019-01-01 03:30

It appears that having the path or subfolder selected in Windows Explorer is enough to block a single execution of Directory.Delete(path, true), throwing an IOException as described above and dying instead of booting Windows Explorer out to a parent folder and proceding as expected.

查看更多
还给你的自由
6楼-- · 2019-01-01 03:31

I had a those weird permission problems deleting User Profile directories (in C:\Documents and Settings) despite being able to do so in the Explorer shell.

File.SetAttributes(target_dir, FileAttributes.Normal);
Directory.Delete(target_dir, false);

It makes no sense to me what a "file" operation does on a directory, but I know that it works and that's enough for me!

查看更多
只若初见
7楼-- · 2019-01-01 03:32

Recursive directory deletion that does not delete files is certainly unexpected. My fix for that:

public class IOUtils
{
    public static void DeleteDirectory(string directory)
    {
        Directory.GetFiles(directory, "*", SearchOption.AllDirectories).ForEach(File.Delete);
        Directory.Delete(directory, true);
    }
}

I experienced cases where this helped, but generally, Directory.Delete deletes files inside directories upon recursive deletion, as documented in msdn.

From time to time I encounter this irregular behavior also as a user of Windows Explorer: Sometimes I cannot delete a folder (it think the nonsensical message is "access denied") but when I drill down and delete lower items I can then delete the upper items as well. So I guess the code above deals with an OS anomaly - not with a base class library issue.

查看更多
登录 后发表回答