Estoy usando .NET 3.5, tratando de eliminar recursivamente un directorio usando:
Directory.Delete(myPath, true);
Tengo entendido que 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 todo su contenido.
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 a veces arroje, pero me sorprende recibir este mensaje en particular cuando lo recursivo es cierto. ( Sé que el directorio no está vacío).
¿Hay alguna razón por la que vería esto en lugar de AccessViolationException?
Respuestas:
Nota del editor: aunque esta respuesta contiene información útil, en realidad es incorrecta sobre el funcionamiento de
Directory.Delete
. Lea los comentarios para 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 archivos que están dentro de la estructura de directorios. 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 en sí. 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 READ-ONLY de los archivos justo antes de eliminarlos. De lo contrario, eso generará una excepción.
Simplemente inserte este código en su proyecto.
Además, para mí personalmente agrego una restricción en las áreas de la máquina que se pueden eliminar porque desea que alguien llame a esta función
C:\WINDOWS (%WinDir%)
oC:\
.fuente
Directory.Delete(path, true)
no elimina archivos es incorrecta. Vea MSDN msdn.microsoft.com/en-us/library/fxeahc5f.aspxDirectory.Delete(string,bool)
falla, algo está bloqueado o mal puesto y no hay una solución única para tal problema. La gente necesita abordar ese problema en su contexto y no deberíamos estar haciendo un gran esfuerzo para cada idea (con reintentos y deglución de excepciones) y esperando un buen resultado.Si está intentando eliminar el directorio de forma recursiva
a
y el directorioa\b
está abierto en el Explorador,b
se eliminará, pero obtendrá el error 'el directorio no está vacío'a
, aunque esté vacío cuando vaya y mire. El directorio actual de cualquier aplicación (incluido Explorer) conserva un identificador para el directorio . Cuando llamasDirectory.Delete(true)
, se elimina de abajo hacia arriba:,b
entoncesa
. Sib
está abierto en Explorer, Explorer detectará la eliminación deb
, cambiará el directorio hacia arribacd ..
y limpiará los controladores abiertos. Dado que el sistema de archivos funciona de forma asíncrona, laDirectory.Delete
operación 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 Explorer tenga tiempo de liberar el identificador de directorio.
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 usaa
, esta técnica fallará después de eliminard
yc
.Una solución algo mejor
Este método manejará la eliminación de una estructura de directorio profunda incluso si uno de los directorios de nivel inferior está abierto en el Explorador.
A pesar del trabajo adicional de recurrir por nuestra cuenta, todavía tenemos que manejar lo
UnauthorizedAccessException
que puede ocurrir en el camino. No está claro si el primer intento de eliminación está allanando el camino para el segundo, exitoso, o si es simplemente el retraso de tiempo introducido por lanzar / atrapar 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 deltry
bloque. Además, existe el riesgo de que, bajo una gran carga del sistema, pueda volar a través de ambosDirectory.Delete
intentos y fallar. Considere esta solución como un punto de partida para una eliminación recursiva más sólida.Respuesta general
Esta solución solo aborda las peculiaridades de interactuar con el Explorador de Windows. Si desea una operación de eliminación sólida, una cosa a tener en cuenta es que cualquier cosa (escáner de virus, lo que sea) podría tener un identificador abierto para lo que está tratando de eliminar, en cualquier momento. Entonces tienes que intentarlo más tarde. Cuánto más tarde y cuántas veces lo intente dependerá de lo importante que sea que se elimine el objeto. Como indica MSDN ,
Esta declaración inocente, suministrada solo con un enlace a la documentación de referencia de NTFS, debería poner los pelos de punta.
( Editar : mucho. Esta respuesta originalmente solo tenía la primera solución incompleta).
fuente
catch
se ejecuta el bloque (mientras tanto, Explorer todavía está liberando el directorio, ya que no se ha enviado ningún comando para decirle que no lo haga). La llamada aThread.Sleep(0)
puede o no ser necesaria, ya que elcatch
bloque ya le ha dado al sistema un poco más de tiempo, pero proporciona un poco de seguridad adicional a bajo costo. Después de eso,Delete
se llama al, con el directorio ya publicado.Antes de continuar, verifique las siguientes razones que están bajo su control:
De lo contrario, verifique las siguientes razones legítimas fuera de su control:
Si alguno de los problemas anteriores es el problema, debe comprender por qué sucede antes de intentar 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, todavía existe la posibilidad de fallas espurias. La eliminación fallará si alguien maneja cualquiera de los archivos o carpetas que se eliminan, y hay muchas razones por las cuales alguien puede enumerar la carpeta o leer sus archivos:
El enfoque general para lidiar con fallas espurias es intentar varias veces, haciendo una pausa entre los intentos. Obviamente, no desea seguir intentándolo para siempre, por lo que debe rendirse después de un cierto número de intentos y lanzar una excepción o ignorar el error. Me gusta esto:
En mi opinión, un ayudante como ese debería usarse para todas las eliminaciones porque siempre son posibles fallas espurias. Sin embargo, DEBE ADAPTAR ESTE CÓDIGO A SU CASO DE USO, no solo copiarlo a ciegas.
Tuve fallas espurias para una carpeta de datos interna generada por mi aplicación, ubicada bajo% LocalAppData%, por lo que mi análisis es el siguiente:
La carpeta está controlada únicamente por mi aplicación, y el usuario no tiene una razón válida para marcar cosas como de solo lectura o inaccesibles dentro de esa carpeta, por lo que no trato de manejar ese caso.
No hay cosas valiosas creadas por el usuario allí, por lo que no hay riesgo de eliminar algo por la fuerza por error.
Al ser 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 ese caso a través del soporte).
Si todos los intentos fallan, elijo ignorar el error. En el peor de los casos, la aplicación no puede descomprimir algunos recursos más nuevos, se bloquea y solicita al usuario que se ponga en contacto con el soporte, lo cual es aceptable para mí siempre que no suceda con frecuencia. O, si la aplicación no se bloquea, dejará algunos datos antiguos, lo que nuevamente es aceptable para mí.
Elijo limitar los reintentos a 500 ms (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 ha dejado de responder. Por otro lado, medio segundo es tiempo de sobra para que el delincuente termine de procesar mi carpeta. A juzgar por otras respuestas SO que a veces se consideran
Sleep(0)
aceptables, muy pocos usuarios experimentarán más de un reintento.Vuelvo a intentar cada 50 ms, que es otro número arbitrario. Siento que si un archivo se está procesando (indexado, verificado) cuando intento eliminarlo, 50 ms es el momento adecuado para esperar que el procesamiento se complete en mi caso. Además, 50 ms es lo suficientemente pequeño como para no provocar una desaceleración notable; nuevamente,
Sleep(0)
parece ser suficiente en muchos casos, por lo que no queremos demorar demasiado.El código vuelve a intentar cualquier excepción de E / S. Normalmente no espero ninguna excepción para acceder a% LocalAppData%, así que elegí la simplicidad y acepté el riesgo de un retraso de 500 ms en caso de que ocurra una excepción legítima. Tampoco quería encontrar una manera de detectar la excepción exacta en la que quiero volver a intentar.
fuente
Directory.Exists
guardia niave lo haría no resolver eso.]Respuesta asincrónica moderna
La respuesta aceptada es simplemente incorrecta, podría funcionar para algunas personas porque el tiempo necesario para obtener los archivos del disco libera lo que sea que bloqueaba 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 volver a intentar eliminar el directorio después de un tiempo. Esta pregunta debe revisarse con una respuesta más moderna.Pruebas unitarias
Estas pruebas muestran un ejemplo de cómo un archivo bloqueado puede hacer
Directory.Delete
que falle y cómo elTryDeleteDirectory
método anterior soluciona el problema.fuente
Thread.Sleep
que debes evitar hoy y usarasync
/await
with en suTask.Delay
lugar. Eso es comprensible, esta es una pregunta muy antigua.BC36943 'Await' cannot be used inside a 'Catch' statement, a 'Finally' statement, or a 'SyncLock' statement.
if (!Directory.Exists(directoryPath)) { return true; } await Task.Delay(millisecondsDelay);
para esperar hasta que el directorio se haya ido realmenteUna cosa importante que debería mencionarse (lo agregué como comentario pero no se me permite) es que el comportamiento de la sobrecarga cambió de .NET 3.5 a .NET 4.0.
A partir de .NET 4.0, elimina archivos en la carpeta en sí, pero NO en 3.5. Esto también se puede ver en la documentación de MSDN.
.NET 4.0
.NET 3.5
fuente
Tuve el mismo problema con 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).
El problema fue que hice un simple cambio de directorio a su padre antes de eliminarlo.
fuente
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.
(Cambie un poco para no disparar una ventana de cmd momentáneamente, que está disponible en todo Internet)
fuente
Puede reproducir el error ejecutando:
Al intentar eliminar el directorio 'b', arroja la IOException "El directorio no está vacío". Eso es estúpido ya que acabamos de eliminar el directorio 'c'.
Según tengo entendido, la explicación es que el directorio 'c' está marcado como eliminado. Pero la eliminación aún no se ha confirmado en el sistema. El sistema ha respondido que el trabajo está hecho, mientras que, de hecho, todavía se está procesando. El sistema probablemente espera que el explorador de archivos se centre 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 utiliza la función nativa Win32Native.RemoveDirectory. Este comportamiento de no esperar se observa aquí:
( http://msdn.microsoft.com/en-us/library/windows/desktop/aa365488(v=vs.85).aspx )
Dormir y reintentar es la solución. Cf la solución del ryascl.
fuente
Tuve esos problemas de permisos extraños al eliminar directorios de perfil de usuario (en C: \ Documents and Settings) a pesar de poder hacerlo en el shell de Explorer.
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í.
fuente
Esta respuesta se basa en: https://stackoverflow.com/a/1703799/184528 . La diferencia con mi código es que solo repetimos muchos subdirectorios y archivos de eliminación cuando es necesario una llamada a Directory.Delete falla en un primer intento (lo que puede suceder debido a que el explorador de Windows mira un directorio).
fuente
UnauthorizedAccessException
? Simplemente lanzaría, de nuevo. Y otra vez. Y de nuevo ... Porque cada vez irá alcatch
y llamará a la función nuevamente. AThread.Sleep(0);
no cambia sus permisos. Simplemente debe registrar el error y fallar con gracia, en ese punto. Y este bucle continuará siempre que el (sub) directorio esté abierto; no lo cierra mediante programación. ¿Estamos preparados para dejar que haga esto mientras esas cosas se dejen abiertas? ¿Hay una mejor manera?UnauthorizedAccessException
, intentará eliminar cada archivo manualmente. Por lo tanto, continúa progresando atravesando la estructura del directorio. Sí, potencialmente todos los archivos y directorios arrojarán la misma excepción, pero esto también puede ocurrir simplemente porque el explorador tiene un identificador (consulte stackoverflow.com/a/1703799/184528 ) Cambiaré el "tryAgain" a "second Try" para que quede más claro.Process.Kill()
cualquier proceso por el que un archivo pudiera estar bloqueado y borrar los archivos. El problema con el que me encuentro es al eliminar un directorio donde uno de esos archivos todavía estaba abierto (consulte stackoverflow.com/questions/41841590/… ). Entonces, volviendo a este bucle, no importa qué más esté haciendo, si lo haceDirectory.Delete()
en esa carpeta nuevamente, todavía fallará si ese identificador no se puede liberar.UnauthorizedAccessException
borraran los archivos (suponiendo que esto estuviera permitido, porque para acceder a ese código, fallóDirectory.Delete()
) no mágicamente le da permiso para eliminar el directorio.Ninguna de las soluciones anteriores funcionó bien para mí. Terminé usando una versión editada de la solución @ryascl como se muestra a continuación:
fuente
¿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 eliminación A:
Si alguien más agrega un archivo entre 1 y 2, ¿tal vez 2 arrojaría la excepción enumerada?
fuente
He pasado algunas horas para resolver este problema y otras excepciones al eliminar el directorio. Esta es mi solucion
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.
fuente
La eliminación recursiva de directorios que no elimina archivos es ciertamente inesperada. Mi solución para eso:
Experimenté casos en los que esto ayudó, pero en general, Directory.Delete elimina los archivos dentro de los 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 del Explorador de Windows: a veces no puedo eliminar una carpeta (creo que el mensaje sin sentido es "acceso denegado"), pero cuando profundizo y elimino los elementos inferiores, puedo eliminar el superior artículos también. Entonces, supongo que el código anterior trata con una anomalía del sistema operativo, no con un problema de biblioteca de clase base.
fuente
El directorio o un archivo en él está bloqueado y no se puede eliminar. Encuentre al culpable que lo bloquea y vea si puede eliminarlo.
fuente
Parece que tener la ruta o subcarpeta seleccionada en el Explorador de Windows es suficiente para bloquear una sola ejecución de Directory.Delete (ruta, verdadero), arrojando una IOException como se describió anteriormente y muriendo en lugar de arrancar el Explorador de Windows en una carpeta principal y proceder como esperado.
fuente
Tuve este problema hoy. Estaba sucediendo porque tenía el explorador de Windows abierto en el directorio que intentaba eliminarse, lo que provocó que la llamada recursiva fallara y, por lo tanto, la IOException. Asegúrese de que no haya identificadores abiertos en el directorio.
Además, MSDN tiene claro que no tiene que escribir su propia recusación: http://msdn.microsoft.com/en-us/library/fxeahc5f.aspx
fuente
He tenido este mismo problema con Windows Workflow Foundation en un servidor de compilación con TFS2012. Internamente, el flujo de trabajo llamado Directory.Delete () con el indicador recursivo establecido en verdadero. Parece estar relacionado con la red en nuestro caso.
Estábamos eliminando una carpeta de colocación binaria en un recurso compartido de red antes de volver a crearla y volver a llenarla con los últimos archivos binarios. Cualquier otra construcción fallaría. Al abrir la carpeta de colocación después de una compilación fallida, la carpeta estaba vacía, lo que indica que todos los aspectos de la llamada Directory.Delete () fueron exitosos, excepto para eliminar el directorio actual.
El problema parece ser causado por la naturaleza asincrónica de las comunicaciones de archivos de red. El servidor de compilación le dijo al servidor de archivos que eliminara todos los archivos y el servidor de archivos informó que lo había hecho, a pesar de que no estaba completamente terminado. Luego, el servidor de compilación solicitó que se eliminara el directorio y el servidor de archivos rechazó la solicitud porque no había terminado de eliminar por completo los archivos.
Dos posibles soluciones en nuestro caso:
El último método es rápido y sucio, pero parece hacer el truco.
fuente
Esto se debe a FileChangesNotifications.
Sucede desde ASP.NET 2.0. Cuando elimina alguna carpeta dentro de una aplicación, se reinicia . Puede verlo usted mismo, utilizando ASP.NET Health Monitoring .
Simplemente agregue este código a su web.config / configuration / system.web:
Después de eso echa un vistazo
Windows Log -> Application
. Que esta pasando:Cuando elimina la carpeta, si hay alguna subcarpeta,
Delete(path, true)
primero se elimina la subcarpeta. Es suficiente para FileChangesMonitor saber acerca de la eliminación y cerrar su aplicación. Mientras tanto, su directorio principal aún no se ha eliminado. Este es el evento de Log:Delete()
no terminó su trabajo y debido a que la aplicación se está cerrando, genera una excepción:Cuando no tiene ninguna subcarpeta en una carpeta que está eliminando, Delete () simplemente elimina todos los archivos y esa carpeta, la aplicación también se reinicia, pero no obtiene ninguna excepción , porque el reinicio de la aplicación no interrumpe nada. Pero aún así, pierde todas las sesiones en proceso, la aplicación no responde a las solicitudes al reiniciar, etc.
¿Ahora que?
Hay algunas soluciones y ajustes para deshabilitar este comportamiento, Directory Junction , Desactivar FCN con Registry , Detener FileChangesMonitor usando Reflection (ya que no hay un método expuesto) , pero no todos parecen estar en lo correcto, porque FCN está ahí para un razón. Se ocupa de la estructura de su aplicación , que no es la estructura de sus datos . La respuesta corta es: coloque las carpetas que desea eliminar fuera de su aplicación. FileChangesMonitor no recibirá notificaciones y su aplicación no se reiniciará cada vez. No obtendrás excepciones. Para que sean visibles desde la web, hay dos formas:
Haga un controlador que maneje las llamadas entrantes y luego devuelva los archivos al leer desde la carpeta fuera de una aplicación (fuera de wwwroot).
Si su proyecto es grande y el rendimiento es lo más importante, configure un servidor web pequeño y rápido para servir contenido estático. Por lo tanto, dejará a IIS su trabajo específico. Podría estar en la misma máquina (mangosta para Windows) u otra máquina (nginx para Linux). La buena noticia es que no tiene que pagar una licencia adicional de Microsoft para configurar el servidor de contenido estático en Linux.
Espero que esto ayude.
fuente
Como se mencionó anteriormente, la solución "aceptada" falla en los puntos de análisis, pero la gente todavía la marca (???). Hay una solución mucho más corta que replica adecuadamente la funcionalidad:
Lo sé, no maneja los casos de permisos mencionados más adelante, pero para todos los efectos, FAR BETTER proporciona la funcionalidad esperada del directorio original / stock.Delete () - y con mucho menos código también .
Puede continuar el procesamiento de forma segura porque el antiguo directorio estará fuera del camino ... incluso si no se ha ido porque el 'sistema de archivos todavía se está poniendo al día' (o cualquier excusa que MS haya dado para proporcionar una función rota) .
Como beneficio, si sabe que su directorio de destino es grande / profundo y no quiere esperar (o molestarse con excepciones) la última línea se puede reemplazar con:
Aún es seguro seguir trabajando.
fuente
\\server\C$\dir
y lo hizo\\server\C$asf.yuw
. Como resultado, recibí un error en elDirectory.Move()
-Source and destination path must have identical roots. Move will not work across volumes.
Funcionó bien una vez que usé el código de Pete, EXCEPTO ninguno de los controladores para cuando hay archivos bloqueados o directorios abiertos, por lo que nunca llega alThreadPool
comando.Este problema puede aparecer en Windows cuando hay archivos en un directorio (o en cualquier subdirectorio) cuya longitud de ruta es mayor que 260 símbolos.
En tales casos, debe eliminar en
\\\\?\C:\mydir
lugar deC:\mydir
. Sobre el límite de 260 símbolos puede leer aquí .fuente
He resuelto con esta técnica milenaria (puedes dejar el Hilo. Dormir solo en la captura)
fuente
Si el directorio actual de su aplicación (o de cualquier otra aplicación) es el que está tratando de eliminar, no será un error de infracción de acceso, pero el directorio no está vacío. Asegúrese de que no sea su propia aplicación cambiando el directorio actual; Además, asegúrese de que el directorio no esté abierto en algún otro programa (por ejemplo, Word, Excel, Total Commander, etc.). La mayoría de los programas se cd en el directorio del último archivo abierto, lo que causaría eso.
fuente
en el caso de archivos de red, Directory.DeleteHelper (recursive: = true) puede causar IOException que se debe a la demora de eliminar el archivo
fuente
Creo que hay un archivo abierto por alguna secuencia que desconoce. Tuve el mismo problema y lo resolví cerrando todas las secuencias que apuntaban al directorio que quería eliminar.
fuente
Este error ocurre si algún archivo o directorio se considera en uso. Es un error engañoso. Verifique si tiene ventanas de explorador o de línea de comandos abiertas en cualquier directorio del árbol o en un programa que esté usando un archivo en ese árbol.
fuente
Resolví una posible instancia del problema declarado cuando los métodos eran asíncronos y se codificaban así:
Con este:
¿Conclusión? Hay un aspecto asincrónico de deshacerse del identificador utilizado para verificar la existencia con el que Microsoft no ha podido hablar. Es como si el método asincrónico dentro de una instrucción if tuviera la instrucción if actuando como una instrucción using.
fuente
No tiene que crear un método adicional para la recursividad o eliminar archivos dentro de la carpeta adicional. Todo esto haciendo automáticamente llamando
Los detalles están aquí .
Algo como esto funciona bastante bien:
pasando true como variable para eliminar el método, eliminará los subarchivos y la subcarpeta con archivos también.
fuente
Ninguna de las respuestas anteriores funcionó para mí. Parece que el uso de mi propia aplicación
DirectoryInfo
en el directorio de destino estaba causando que permaneciera bloqueado.Forzar la recolección de basura pareció resolver el problema, pero no de inmediato. Unos pocos intentos de eliminar cuando sea necesario.
Tenga en cuenta
Directory.Exists
que puede desaparecer después de una excepción. No sé por qué se retrasó la eliminación para mí (Windows 7 SP1)fuente
GC.Collect
es a) solo un mal consejo yb) no es una causa general suficientemente común de directorios bloqueados para merecer inclusión aquí. Solo elige uno de los otros y no siembres más confusión en la mente de lectores inocentesusing
declaración, entonces:using (DirectoryInfo di = new DirectoryInfo(@"c:\MyDir")) { for (int attempts = 0; attempts < 10; attempts++) { try { if (di.Exists(folder)) { Directory.Delete(folder, true); } return; } catch (IOException e) { Thread.Sleep(1000); } } }
agregue verdadero en el segundo parámetro.
Eliminará todo.
fuente