No se puede eliminar el directorio con Directory.Delete (path, true)

Estoy usando .NET 3.5, tratando de eliminar recursivamente un directorio usando:

Directory.Delete(myPath, true); 

Según entiendo, esto debería arrojarse si los archivos están en uso o si hay un problema de permisos, pero de lo contrario debería eliminar el directorio y todos sus contenidos.

Sin embargo, ocasionalmente obtengo esto:

 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) ... 

No me sorprende que el método arroje a veces, pero me sorprende recibir este mensaje en particular cuando el recursivo es verdadero. ( Sé que el directorio no está vacío).

¿Hay alguna razón por la que vea esto en lugar de AccessViolationException?

Nota del editor: aunque esta respuesta contiene información útil, es fácticamente incorrecta sobre el funcionamiento de Directory.Delete . Lea los comentarios de esta respuesta y otras respuestas a esta pregunta.


Me encontré con este problema antes.

La raíz del problema es que esta función no elimina los archivos que están dentro de la estructura del directorio. Entonces, lo que tendrá que hacer es crear una función que elimine todos los archivos dentro de la estructura del directorio y luego todos los directorios antes de eliminar el directorio. Sé que esto va en contra del segundo parámetro, pero es un enfoque mucho más seguro. Además, es probable que desee eliminar los atributos de acceso SOLO DE LECTURA de los archivos justo antes de eliminarlos. De lo contrario, se generará una excepción.

Solo inserta este código en tu proyecto.

 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); } 

Además, personalmente añado una restricción en las áreas de la máquina que se pueden eliminar porque desea que alguien llame a esta función en C:\WINDOWS (%WinDir%) o C:\ .

Si está intentando eliminar de manera recursiva el directorio a y el directorio a\b está abierto en el Explorador, b se eliminará pero obtendrá el error “el directorio no está vacío” aunque esté vacío cuando vaya y observe. El directorio actual de cualquier aplicación (incluido Explorer) conserva un identificador para el directorio . Cuando llama a Directory.Delete(true) , lo elimina de abajo arriba: b , luego a . Si b está abierto en Explorer, Explorer detectará la eliminación de b , cambiará el directorio hacia arriba cd .. y limpiará los identificadores abiertos. Como el sistema de archivos funciona de forma asíncrona, la operación Directory.Delete falla debido a conflictos con Explorer.

Solución incompleta

Originalmente publiqué la siguiente solución, con la idea de interrumpir el hilo actual para permitir que el tiempo del Explorer libere el identificador del directorio.

 // incomplete! try { Directory.Delete(path, true); } catch (IOException) { Thread.Sleep(0); Directory.Delete(path, true); } 

Pero esto solo funciona si el directorio abierto es el hijo inmediato del directorio que está eliminando. Si a\b\c\d está abierto en el Explorador y lo usa en a , esta técnica fallará después de eliminar d y c .

Una solución algo mejor

Este método manejará la eliminación de una estructura de directorios profunda incluso si uno de los directorios de nivel inferior está abierto en el Explorador.

 ///  /// Depth-first recursive delete, with handling for descendant /// directories open in Windows Explorer. ///  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); } } 

A pesar del trabajo adicional de recursing por nuestra cuenta, todavía tenemos que manejar la excepción de acceso UnauthorizedAccessException que puede ocurrir en el camino. No está claro si el primer bash de eliminación está allanando el camino para el segundo, exitoso, o si es simplemente el retraso de tiempo introducido por el lanzamiento / captura de una excepción que permite que el sistema de archivos se ponga al día.

Es posible que pueda reducir el número de excepciones lanzadas y atrapadas en condiciones típicas agregando un Thread.Sleep(0) al comienzo del bloque try . Además, existe el riesgo de que, bajo una gran carga del sistema, pueda volar a través de los dos bashs de Directory.Delete y fallar. Considere esta solución como un punto de partida para una eliminación recursiva más robusta.

Respuesta general

Esta solución solo aborda las peculiaridades de interactuar con Windows Explorer. Si desea una operación de eliminación sólida como una roca, una cosa a tener en cuenta es que cualquier cosa (escáner de virus, lo que sea) podría tener un control abierto para lo que está tratando de eliminar, en cualquier momento. Entonces debes intentarlo más tarde. Cuánto más tarde, y cuántas veces lo intentes, depende de cuán importante es eliminar el objeto. Como lo indica MSDN ,

El código de iteración de archivos robusto debe tener en cuenta muchas complejidades del sistema de archivos.

Esta afirmación inocente, suministrada solo con un enlace a la documentación de referencia de NTFS, debería hacer que se ponga de pie.

( Editar : Mucho. Esta respuesta originalmente solo tenía la primera solución incompleta).

Antes de seguir adelante, verifique los siguientes motivos que están bajo su control:

  • ¿La carpeta está configurada como un directorio actual de su proceso? Si es así, cámbialo primero por algo diferente.
  • ¿Has abierto un archivo (o cargado un archivo DLL) desde esa carpeta? (y se olvidó de cerrar / descargarlo)

De lo contrario, verifique las siguientes razones legítimas fuera de su control:

  • Hay archivos marcados como de solo lectura en esa carpeta.
  • No tiene un permiso de eliminación para algunos de esos archivos.
  • El archivo o subcarpeta está abierto en el Explorador u otra aplicación.

Si alguno de los problemas anteriores es el problema, debe comprender por qué sucede antes de tratar de mejorar su código de eliminación. ¿Debería su aplicación eliminar archivos de solo lectura o inaccesibles? ¿Quién los marcó de esa manera y por qué?

Una vez que haya descartado las razones anteriores, aún existe la posibilidad de errores espurios. La eliminación fallará si alguien maneja alguno de los archivos o carpetas que se eliminan, y hay muchas razones por las cuales alguien puede estar enumerando la carpeta o leyendo sus archivos:

  • indexadores de búsqueda
  • anti-virus
  • software de respaldo

El enfoque general para tratar fallas falsas es intentarlo varias veces, pausando entre los bashs. Obviamente no quiere seguir intentándolo para siempre, por lo que debe darse por vencido después de una cierta cantidad de bashs y lanzar una excepción o ignorar el error. Me gusta esto:

 private static void DeleteRecursivelyWithMagicDust(string destinationDir) { const int magicDust = 10; for (var gnomes = 1; gnomes <= magicDust; gnomes++) { try { Directory.Delete(destinationDir, true); } catch (DirectoryNotFoundException) { return; // good! } catch (IOException) { // System.IO.IOException: The directory is not empty System.Diagnostics.Debug.WriteLine("Gnomes prevent deletion of {0}! Applying magic dust, attempt #{1}.", destinationDir, gnomes); // see http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true for more magic Thread.Sleep(50); continue; } return; } // depending on your use case, consider throwing an exception here } 

En mi opinión, un ayudante como ese debe usarse para todas las eliminaciones porque las fallas falsas siempre son posibles. Sin embargo, DEBE ADAPTAR ESTE CÓDIGO A SU CASO DE USO, no solo copiarlo a ciegas.

Tuve errores espurios en una carpeta de datos interna generada por mi aplicación, que se encuentra en% LocalAppData%, por lo que mi análisis es el siguiente:

  1. La carpeta está controlada únicamente por mi aplicación, y el usuario no tiene un motivo válido para ir y marcar cosas como de solo lectura o inaccesibles dentro de esa carpeta, por lo que no trato de manejar ese caso.

  2. No hay elementos valiosos creados por el usuario, por lo que no hay riesgo de borrar algo por error.

  3. Siendo una carpeta de datos interna, no espero que esté abierta en el explorador, al menos no siento la necesidad de manejar específicamente el caso (es decir, estoy manejando bien el caso a través de soporte).

  4. Si todos los bashs fallan, elijo ignorar el error. En el peor de los casos, la aplicación no puede descomprimir algunos recursos más nuevos, falla y solicita al usuario que se ponga en contacto con el soporte, lo cual es aceptable para mí, siempre que no ocurra a menudo. O bien, si la aplicación no falla, dejará algunos datos antiguos, lo que de nuevo es aceptable para mí.

  5. Elijo limitar los bashs a 500ms (50 * 10). Este es un umbral arbitrario que funciona en la práctica; Quería que el umbral fuera lo suficientemente corto para que los usuarios no mataran la aplicación, pensando que había dejado de responder. Por otro lado, medio segundo es suficiente tiempo para que el delincuente termine de procesar mi carpeta. A juzgar por otras respuestas SO que a veces encuentran que incluso Sleep(0) es aceptable, muy pocos usuarios experimentarán más de un solo bash.

  6. Vuelvo a intentar cada 50 ms, que es otro número arbitrario. Siento que si un archivo se está procesando (indexando, verificando) cuando bash eliminarlo, 50 ms es el momento adecuado para esperar que el proceso se complete en mi caso. Además, 50 ms es lo suficientemente pequeño como para no dar lugar a una desaceleración notable; de nuevo, Sleep(0) parece ser suficiente en muchos casos, por lo que no queremos demorar demasiado.

  7. El código reintenta en cualquier excepción IO. Normalmente no espero ninguna excepción que acceda a% LocalAppData%, así que elegí la simplicidad y acepté el riesgo de una demora de 500ms en caso de que ocurriera una excepción legítima. Tampoco quería encontrar una manera de detectar la excepción exacta en la que quiero volver a intentar.

Tuve el mismo problema bajo Delphi. Y el resultado final fue que mi propia aplicación estaba bloqueando el directorio que quería eliminar. De alguna manera el directorio se bloqueó cuando estaba escribiendo en él (algunos archivos temporales).

La captura 22 fue, hice un directorio de cambio simple a su padre antes de eliminarlo.

Me sorprende que nadie haya pensado en este método simple no recursivo, que puede eliminar directorios que contienen archivos de solo lectura, sin necesidad de cambiar el atributo de solo lectura de cada uno de ellos.

 Process.Start("cmd.exe", "/c " + @"rmdir /s/q C:\Test\TestDirectoryContainingReadOnlyFiles"); 

(Cambie un poco para no disparar una ventana de cmd momentáneamente, que está disponible en todo internet)

Puede reproducir el error ejecutando:

 Directory.CreateDirectory(@"C:\Temp\a\b\c\"); Process.Start(@"C:\Temp\a\b\c\"); Thread.Sleep(1000); Directory.Delete(@"C:\Temp\a\b\c"); Directory.Delete(@"C:\Temp\a\b"); Directory.Delete(@"C:\Temp\a"); 

Al intentar eliminar el directorio ‘b’, arroja la excepción IOException “El directorio no está vacío”. Eso es estúpido ya que acabamos de eliminar el directorio ‘c’.

Según entiendo, la explicación es que el directorio ‘c’ está marcado como eliminado. Pero la eliminación aún no está comprometida en el sistema. El sistema tiene respuesta, el trabajo está hecho, mientras que de hecho, todavía está en proceso. El sistema probablemente espere que el explorador de archivos tenga foco en el directorio principal para confirmar la eliminación.

Si observa el código fuente de la función Eliminar ( http://referencesource.microsoft.com/#mscorlib/system/io/directory.cs ), verá que usa la función nativa Win32Native.RemoveDirectory. Este comportamiento de no esperar se observa aquí:

La función RemoveDirectory marca un directorio para su eliminación al cerrar. Por lo tanto, el directorio no se elimina hasta que se cierra el último identificador del directorio.

( http://msdn.microsoft.com/en-us/library/windows/desktop/aa365488(v=vs.85).aspx )

Dormir y volver a intentar es la solución. Cf la solución de ryascl.

Una cosa importante que debería mencionarse (lo había agregado como comentario, pero no estoy autorizado) es que el comportamiento de la sobrecarga cambió de .NET 3.5 a .NET 4.0.

 Directory.Delete(myPath, true); 

A partir de .NET 4.0, elimina los archivos en la carpeta pero NO en 3.5. Esto se puede ver en la documentación de MSDN también.

.NET 4.0

Elimina el directorio especificado y, si está indicado, cualquier subdirectorio y archivo en el directorio.

.NET 3.5

Elimina un directorio vacío y, si está indicado, cualquier subdirectorio y archivo en el directorio.

Tenía esos extraños problemas de permisos para eliminar directorios de Perfil de usuario (en C: \ Documents and Settings) a pesar de poder hacerlo en el shell de Explorer.

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

No tiene sentido para mí lo que hace una operación de “archivo” en un directorio, ¡pero sé que funciona y eso es suficiente para mí!

Respuesta asincrónica moderna

La respuesta aceptada es simplemente incorrecta, podría funcionar para algunas personas porque el tiempo necesario para obtener archivos del disco libera lo que sea que esté bloqueando los archivos. El hecho es que esto sucede porque los archivos se bloquean por algún otro proceso / flujo / acción. Las otras respuestas usan Thread.Sleep (Yuck) para intentar borrar el directorio después de un tiempo. Esta pregunta necesita una nueva respuesta con una respuesta más moderna.

 public static async Task 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; } 

Pruebas unitarias

Estas pruebas muestran un ejemplo de cómo un archivo bloqueado puede hacer que Directory.Delete falle y cómo el método TryDeleteDirectory anterior soluciona el problema.

 [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 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); } } } 

Esta respuesta se basa en: https://stackoverflow.com/a/1703799/184528 . La diferencia con mi código es que solo recursemos muchos subdirectorios y archivos cuando es necesario llamar a Directory.Delete falla en un primer bash (lo que puede suceder debido a que el explorador de Windows está mirando un directorio).

  public static void DeleteDirectory(string dir, bool secondAttempt = false) { // If this is a second try, we are going to manually // delete the files and sub-directories. if (secondAttempt) { // Interrupt the current thread to allow Explorer time to release a directory handle Thread.Sleep(0); // Delete any files in the directory foreach (var f in Directory.GetFiles(dir, "*.*", SearchOption.TopDirectoryOnly)) File.Delete(f); // Try manually recursing and deleting sub-directories foreach (var d in Directory.GetDirectories(dir)) DeleteDirectory(d); // Now we try to delete the current directory Directory.Delete(dir, false); return; } try { // First attempt: use the standard MSDN approach. // This will throw an exception a directory is open in explorer Directory.Delete(dir, true); } catch (IOException) { // Try again to delete the directory manually recursing. DeleteDirectory(dir, true); } catch (UnauthorizedAccessException) { // Try again to delete the directory manually recursing. DeleteDirectory(dir, true); } } 

Pasé algunas horas para resolver este problema y otras excepciones al eliminar el directorio. Esta es mi solución

  public static void DeleteDirectory(string target_dir) { DeleteDirectoryFiles(target_dir); while (Directory.Exists(target_dir)) { lock (_lock) { DeleteDirectoryDirs(target_dir); } } } private static void DeleteDirectoryDirs(string target_dir) { System.Threading.Thread.Sleep(100); if (Directory.Exists(target_dir)) { string[] dirs = Directory.GetDirectories(target_dir); if (dirs.Length == 0) Directory.Delete(target_dir, false); else foreach (string dir in dirs) DeleteDirectoryDirs(dir); } } private static void DeleteDirectoryFiles(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) { DeleteDirectoryFiles(dir); } } 

Este código tiene un pequeño retraso, que no es importante para mi aplicación. Pero tenga cuidado, la demora puede ser un problema para usted si tiene muchos subdirectorios dentro del directorio que desea eliminar.

La eliminación recursiva de directorios que no elimina archivos es ciertamente inesperada. Mi solución para eso:

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

Experimenté casos en los que esto ayudó, pero en general, Directory.Delete elimina los archivos dentro de directorios tras la eliminación recursiva, como se documenta en msdn .

De vez en cuando me encuentro con este comportamiento irregular también como usuario de Windows Explorer: a veces no puedo eliminar una carpeta (creo que el mensaje sin sentido es “acceso denegado”) pero cuando profundizo y elimino elementos inferiores, puedo eliminar la parte superior artículos también. Así que supongo que el código anterior trata sobre una anomalía del sistema operativo, no con un problema de biblioteca de clase base.

¿Es posible que tenga una condición de carrera donde otro hilo o proceso está agregando archivos al directorio?

La secuencia sería:

Proceso de deleción A:

  1. Vaciar el directorio
  2. Eliminar el directorio (ahora vacío).

Si alguien más agrega un archivo entre 1 y 2, ¿entonces quizás 2 arrojaría la excepción?

El directorio o un archivo está bloqueado y no se puede eliminar. Encuentre al culpable que lo bloquea y vea si puede eliminarlo.

Parece que tener la ruta o subcarpeta seleccionada en el Explorador de Windows es suficiente para bloquear una única ejecución de Directory.Delete (path, true), lanzando una IOException como se describe arriba y muriendo en lugar de iniciar Windows Explorer en una carpeta principal y procesarse como esperado.

Tuve este problema hoy. It was happening because I had windows explorer open to the directory that was trying to be deleted, causing the recursive call the fail and thus the IOException. Make sure there are no handles open to the directory.

Also, MSDN is clear that you don’t have to write your own recusion: http://msdn.microsoft.com/en-us/library/fxeahc5f.aspx

I’ve had this same problem with Windows Workflow Foundation on a build server with TFS2012. Internally, the workflow called Directory.Delete() with the recursive flag set to true. It appears to be network related in our case.

We were deleting a binary drop folder on a network share before re-creating and re-populating it with the latest binaries. Every other build would fail. When opening the drop folder after a failed build, the folder was empty, which indicates that every aspect of the Directory.Delete() call was successful except for deleting the actually directory.

The problem appears to be caused by the asynchronous nature of network file communications. The build server told the file server to delete all of the files and the file server reported that it had, even though it wasn’t completely finished. Then the build server requested that the directory be deleted and the file server rejected the request because it hadn’t completely finished deleting the files.

Two possible solutions in our case:

  • Build up the recursive deletion in our own code with delays and verifications between each step
  • Retry up to X times after an IOException, giving a delay before trying again

The latter method is quick and dirty but seems to do the trick.

This is because of FileChangesNotifications.

It happens since ASP.NET 2.0. When you delete some folder within an app, it gets restarted . You can see it yourself, using ASP.NET Health Monitoring .

Just add this code to your web.config/configuration/system.web:

      

After that check out Windows Log -> Application . What is going on:

When you delete folder, if there is any sub-folder, Delete(path, true) deletes sub-folder first. It is enough for FileChangesMonitor to know about removal and shut down your app. Meanwhile your main directory is not deleted yet. This is the event from Log:

enter image description here

Delete() didn’t finish its work and because app is shutting down, it raises an exception:

enter image description here

When you do not have any subfolders in a folder that you are deleting, Delete() just deletes all files and that folder, app is getting restarted too, but you don’t get any exceptions , because app restart doesn’t interrupt anything. But still, you lose all in-process sessions, app doesn’t response to requests when restarting, etc.

What now?

There are some workarounds and tweaks to disable this behaviour, Directory Junction , Turning Off FCN with Registry , Stopping FileChangesMonitor using Reflection (since there is no exposed method) , but they all don’t seem to be right, because FCN is there for a reason. It is looking after structure of your app , which is not structure of your data . Short answer is: place folders you want to delete outside of your app. FileChangesMonitor will get no notifications and your app will not be restarted every time. You will get no exceptions. To get them visible from the web there are two ways:

  1. Make a controller that handles incoming calls and then serves files back by reading from folder outside an app (outside wwwroot).

  2. If your project is big and performance is most important, set up separate small and fast webserver for serving static content. Thus you will leave to IIS his specific job. It could be on the same machine (mongoose for Windows) or another machine (nginx for Linux). Good news is you don’t have to pay extra microsoft license to set up static content server on linux.

Espero que esto ayude.

As mentioned above the “accepted” solution fails on reparse points – yet people still mark it up(???). There’s a much shorter solution that properly replicates the functionality:

 public static void rmdir(string target, bool recursive) { string tfilename = Path.GetDirectoryName(target) + (target.Contains(Path.DirectorySeparatorChar.ToString()) ? Path.DirectorySeparatorChar.ToString() : string.Empty) + Path.GetRandomFileName(); Directory.Move(target, tfilename); Directory.Delete(tfilename, recursive); } 

I know, doesn’t handle the permissions cases mentioned later, but for all intents and purposes FAR BETTER provides the expected functionality of the original/stock Directory.Delete() – and with a lot less code too .

You can safely carry on processing because the old dir will be out of the way …even if not gone because the ‘file system is still catching up’ (or whatever excuse MS gave for providing a broken function) .

As a benefit, if you know your target directory is large/deep and don’t want to wait (or bother with exceptions) the last line can be replaced with:

  ThreadPool.QueueUserWorkItem((o) => { Directory.Delete(tfilename, recursive); }); 

You are still safe to carry on working.

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 .

Non of above solutions worked well for me. I ended up by using an edited version of @ryascl solution as below:

  ///  /// Depth-first recursive delete, with handling for descendant /// directories open in Windows Explorer. ///  public static void DeleteDirectory(string path) { foreach (string directory in Directory.GetDirectories(path)) { Thread.Sleep(1); DeleteDir(directory); } DeleteDir(path); } private static void DeleteDir(string dir) { try { Thread.Sleep(1); Directory.Delete(dir, true); } catch (IOException) { DeleteDir(dir); } catch (UnauthorizedAccessException) { DeleteDir(dir); } } 

If your application’s (or any other application’s) current directory is the one you’re trying to delete, it will not be an access violation error but a directory is not empty. Make sure it’s not your own application by changing the current directory; also, make sure the directory is not open in some other program (eg Word, excel, Total Commander, etc.). Most programs will cd to the directory of the last file opened, which would cause that.

in case of network files, Directory.DeleteHelper(recursive:=true) might cause IOException which caused by the delay of deleting file

I think that there is a file open by some stream you are not aware of I had the same problem and solved it by closing all the streams that where pointing to the directory I wanted to delete.

This error occurs if any file or directory is considered in-use. It is a misleading error. Check to see if you have any explorer windows or command-line windows open to any directory in the tree, or a program that is using a file in that tree.

I resolved one possible instance of the stated problem when methods were async and coded like this:

 // delete any existing update content folder for this update if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath)) await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath); 

Con este:

 bool exists = false; if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath)) exists = true; // delete any existing update content folder for this update if (exists) await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath); 

¿Conclusión? There is some asynchronous aspect of getting rid of the handle used to check existence that Microsoft has not been able to speak to. It’s as if the asynchronous method inside an if statement has the if statement acting like a using statement.

None of the above answers worked for me. It appears that my own app’s usage of DirectoryInfo on the target directory was causing it to remain locked.

Forcing garbage collection appeared to resolve the issue, but not right away. A few attempts to delete where required.

Note the Directory.Exists as it can disappear after an exception. I don’t know why the delete for me was delayed (Windows 7 SP1)

  for (int attempts = 0; attempts < 10; attempts++) { try { if (Directory.Exists(folder)) { Directory.Delete(folder, true); } return; } catch (IOException e) { GC.Collect(); Thread.Sleep(1000); } } throw new Exception("Failed to remove folder."); 

add true in the second param.

 Directory.Delete(path, true); 

It will remove all.