Cannot delete directory with Directory.Delete(path, true)
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?
Solution 1:
Editor's note: Although this answer contains some useful information, it is factually incorrect about the workings of Directory.Delete
. Please read the comments for this answer, and other answers to this question.
I ran into this problem before.
The root of the problem is that this function does not delete files that are within the directory structure. So what you'll need to do is create a function that deletes all the files within the directory structure then all the directories before removing the directory itself. I know this goes against the second parameter but it's a much safer approach. In addition, you will probably want to remove READ-ONLY access attributes from the files right before you delete them. Otherwise that will raise an exception.
Just slap this code into your project.
public static void DeleteDirectory(string target_dir)
{
string[] files = Directory.GetFiles(target_dir);
string[] dirs = Directory.GetDirectories(target_dir);
foreach (string file in files)
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
foreach (string dir in dirs)
{
DeleteDirectory(dir);
}
Directory.Delete(target_dir, false);
}
Also, for me I personally add a restriction on areas of the machine that are allowed to be deleted because do you want someone to call this function on C:\WINDOWS (%WinDir%)
or C:\
.
Solution 2:
If you are trying to recursively delete directory a
and directory a\b
is open in Explorer, b
will be deleted but you will get the error 'directory is not empty' for a
even though it is empty when you go and look. The current directory of any application (including Explorer) retains a handle to the directory. When you call Directory.Delete(true)
, it deletes from bottom up: b
, then a
. If b
is open in Explorer, Explorer will detect the deletion of b
, change directory upwards cd ..
and clean up open handles. Since the file system operates asynchronously, the Directory.Delete
operation fails due to conflicts with Explorer.
Incomplete solution
I originally posted the following solution, with the idea of interrupting the current thread to allow Explorer time to release the directory handle.
// incomplete!
try
{
Directory.Delete(path, true);
}
catch (IOException)
{
Thread.Sleep(0);
Directory.Delete(path, true);
}
But this only works if the open directory is the immediate child of the directory you are deleting. If a\b\c\d
is open in Explorer and you use this on a
, this technique will fail after deleting d
and c
.
A somewhat better solution
This method will handle deletion of a deep directory structure even if one of the lower-level directories is open in Explorer.
/// <summary>
/// Depth-first recursive delete, with handling for descendant
/// directories open in Windows Explorer.
/// </summary>
public static void DeleteDirectory(string path)
{
foreach (string directory in Directory.GetDirectories(path))
{
DeleteDirectory(directory);
}
try
{
Directory.Delete(path, true);
}
catch (IOException)
{
Directory.Delete(path, true);
}
catch (UnauthorizedAccessException)
{
Directory.Delete(path, true);
}
}
Despite the extra work of recursing on our own, we still have to handle the UnauthorizedAccessException
that can occur along the way. It's not clear whether the first deletion attempt is paving the way for the second, successful one, or if it's merely the timing delay introduced by the throwing/catching an exception that allows the file system to catch up.
You might be able to reduce the number of exceptions thrown and caught under typical conditions by adding a Thread.Sleep(0)
at the beginning of the try
block. Additionally, there is a risk that under heavy system load, you could fly through both of the Directory.Delete
attempts and fail. Consider this solution a starting point for more robust recursive deletion.
General answer
This solution only addresses the peculiarities of interacting with Windows Explorer. If you want a rock-solid delete operation, one thing to keep in mind is that anything (virus scanner, whatever) could have an open handle to what you are trying to delete, at any time. So you have to try again later. How much later, and how many times you try, depends on how important it is that the object be deleted. As MSDN indicates,
Robust file iteration code must take into account many complexities of the file system.
This innocent statement, supplied with only a link to the NTFS reference documentation, ought to make your hairs stand up.
(Edit: A lot. This answer originally only had the first, incomplete solution.)