c# - una - No se puede eliminar el directorio con Directory.Delete(ruta, verdadero)
eliminar todos los archivos de una carpeta c# (29)
Async moderna respuesta
La respuesta aceptada es simplemente errónea, podría funcionar para algunas personas porque el tiempo que lleva obtener los archivos del disco libera todo lo que estaba 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 utilizan Thread.Sleep
(Yuck) para volver a intentar eliminar el directorio después de un tiempo. Esta pregunta necesita ser revisada con una respuesta más moderna.
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;
}
Pruebas unitarias
Estas pruebas muestran un ejemplo de cómo un archivo bloqueado puede hacer que el Directory.Delete
falle y cómo el método TryDeleteDirectory
anterior resuelve 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<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);
}
}
}
Estoy usando .NET 3.5, tratando de eliminar recursivamente un directorio usando:
Directory.Delete(myPath, true);
Tengo entendido que esto debería ocurrir si los archivos están en uso o si hay un problema con los permisos, pero de lo contrario debería eliminar el directorio y todo su contenido.
Sin embargo, de vez en cuando me sale 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 la recursividad es cierta. ( Sé que el directorio no está vacío.)
¿Hay alguna razón para ver esto en lugar de AccessViolationException?
Antes de continuar, verifique las siguientes razones que están bajo su control:
- ¿La carpeta está configurada como un directorio actual de su proceso? Si es así, cámbielo a otra cosa primero.
- ¿Has abierto un archivo (o cargado un DLL) de esa carpeta? (y se olvidó de cerrarlo / 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 Explorer u otra aplicación.
Si cualquiera de los puntos 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 falsas. La eliminación fallará si alguien tiene un identificador de cualquiera de los archivos o carpetas que se eliminan, y hay muchas razones por las que alguien puede enumerar la carpeta o leer sus archivos:
- indexadores de búsqueda
- anti-virus
- software de copia de seguridad
El enfoque general para hacer frente a fallas espurias es intentar varias veces, haciendo una pausa entre los intentos. Obviamente, no desea seguir intentándolo para siempre, por lo que debe darse por vencido después de un cierto número de intentos 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://.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 debería usarse para todas las eliminaciones porque siempre son posibles fallas falsas. Sin embargo, DEBE ADAPTAR ESTE CÓDIGO A SU CASO DE USO, no solo copiarlo a ciegas.
Tuve fallas falsas para una carpeta de datos interna generada por mi aplicación, ubicada en% 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 ir y marcar las cosas como de solo lectura o inaccesibles dentro de esa carpeta, así que no trato de manejar ese caso.
No hay cosas valiosas creadas por el usuario allí, así que no hay riesgo de borrar algo 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 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 desempaquetar algunos recursos más nuevos, se bloquea y solicita al usuario que se ponga en contacto con el servicio de asistencia, lo cual es aceptable para mí siempre y cuando no ocurra a menudo. O, si la aplicación no falla, dejará atrás algunos datos antiguos, lo que de nuevo 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 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 de SO que a veces encuentran que incluso
Sleep(0)
es aceptable, muy pocos usuarios experimentarán más de un solo reintento.Reintento cada 50ms, que es otro número arbitrario. Creo que si un archivo se está procesando (indexando, verificando) cuando intento borrarlo, 50 ms es el momento adecuado para esperar que el procesamiento se complete en mi caso. Además, 50ms es lo suficientemente pequeño como para no resultar en una desaceleración notable; de nuevo, el
Sleep(0)
parece ser suficiente en muchos casos, por lo que no queremos retrasarnos demasiado.El código reintenta en cualquier excepción de IO. Normalmente no espero excepciones al acceder a% LocalAppData%, así que elegí la simplicidad y acepté el riesgo de un retraso de 500 ms en caso de que se produjera una excepción legítima. Tampoco quería encontrar una manera de detectar la excepción exacta a la que quiero volver a intentarlo.
Es posible que tenga una condición de carrera en la que otro subproceso o proceso está agregando archivos al directorio:
La secuencia sería:
Proceso de eliminación A:
- Vaciar el directorio
- Eliminar el directorio (ahora vacío).
Si alguien más agrega un archivo entre 1 y 2, ¿tal vez 2 arrojaría la excepción enumerada?
Esta respuesta se basa en: https://.com/a/1703799/184528 . La diferencia con mi código, es que solo repetimos muchos subdirectorios y archivos eliminados cuando es necesario, una llamada a Directory.Delete falla en un primer intento (lo que puede suceder debido a que Windows Explorer busca 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);
}
}
He pasado algunas horas para resolver este problema y otras excepciones con la eliminación del directorio. Esta es mi solucion
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 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 elementos inferiores, puedo eliminar la parte superior artículos también. Así que supongo que el código anterior trata con una anomalía del sistema operativo, no con un problema de la biblioteca de clases base.
Me sorprende que nadie haya pensado en este simple método 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 momentáneamente una ventana cmd, que está disponible en todo el Internet)
Puedes 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");
Cuando se intenta eliminar el directorio ''b'', se emite la excepción 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 está comprometida en el sistema. El sistema ha respondido que el trabajo está hecho, mientras que, de hecho, todavía se está procesando. Es probable que el sistema espere 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 usa la función Win32Native.RemoveDirectory nativa. Este comportamiento de no esperar se indica 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 cierre el último manejador del directorio.
( 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.
Si está tratando de eliminar recursivamente 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'' para 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 llama a Directory.Delete(true)
, se borra de abajo hacia 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 controladores abiertos. Dado que el sistema de archivos funciona de forma asíncrona, la operación Directory.Delete
falla debido a conflictos con Explorer.
Solucion incompleta
Originalmente publiqué la siguiente solución, con la idea de interrumpir el subproceso actual para dar tiempo al Explorer para liberar el identificador de 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 Explorer 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 directorio profunda incluso si uno de los directorios de nivel inferior está abierto en el Explorador.
/// <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);
}
}
A pesar del trabajo adicional de recurrir por nuestra cuenta, todavía tenemos que manejar la 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 retardo de tiempo introducido por el lanzamiento / captura de una excepción que permite al sistema de archivos ponerse al día.
Es posible que pueda reducir el número de excepciones lanzadas y capturadas en condiciones típicas agregando un Thread.Sleep(0)
al principio del bloque try
. Además, existe el riesgo de que bajo una carga pesada del sistema, pueda volar a través de los dos intentos 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, una cosa que debe tener en cuenta es que cualquier cosa (antivirus, lo que sea) podría tener un control abierto de lo que está intentando eliminar, en cualquier momento. Así que tienes que intentarlo más tarde. Cuánto más tarde y cuántas veces lo intente, depende de lo importante que sea que se elimine el objeto. Como indica MSDN ,
El robusto código de iteración de archivos debe tener en cuenta muchas complejidades del sistema de archivos.
Esta declaración inocente, provista solo con un enlace a la documentación de referencia de NTFS, debe hacer que sus pelos se levanten.
( Edición : mucho. Esta respuesta originalmente solo tenía la primera solución incompleta).
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).
La captura 22 era, hice un simple cambio de directorio a su padre antes de eliminarlo.
Tuve problemas extraños de permisos al eliminar los directorios de perfiles 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í!
Una cosa importante que se debe mencionar (lo he agregado como un comentario pero no puedo) 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 de la carpeta, pero NO en 3.5. Esto también se puede ver en la documentación de MSDN.
.NET 4.0
Elimina el directorio especificado y, si está indicado, cualquier subdirectorio y archivo en el directorio.
.NET 3.5
Borra un directorio vacío y, si se indica, cualquier subdirectorio y archivo en el directorio.
Nota del editor: Aunque esta respuesta contiene alguna información útil, es objetivamente incorrecta sobre el funcionamiento de Directory.Delete
. Por favor 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 los archivos que están dentro de la estructura del directorio. Entonces, lo que deberá 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 de solo lectura de los archivos justo antes de eliminarlos. De lo contrario, se producirá una excepción.
Simplemente coloque este código en su 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, 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 en C:/WINDOWS (%WinDir%)
o C:/
.
Añadir verdadero en el segundo parámetro.
Directory.Delete(path, true);
Se eliminará todo.
Creo que hay un archivo abierto por algún flujo del que no sabe que tuve el mismo problema y lo resolví cerrando todos los flujos que apuntaban al directorio que quería eliminar.
En el caso de archivos de red, Directory.DeleteHelper (recursive: = true) puede causar IOException, lo que se debe a la demora en la eliminación del archivo.
Este problema puede aparecer en Windows cuando hay archivos en un directorio (o en cualquier subdirectorio) cuya longitud de ruta sea mayor a 260 símbolos.
En tales casos, es necesario eliminar en ////?/C:/mydir
lugar de C:/mydir
. Sobre el límite de 260 símbolos puedes leer here .
Lo resolví con esta técnica milenaria (puedes dejar el Hilo. Dormir solo en la trampa)
bool deleted = false;
do
{
try
{
Directory.Delete(rutaFinal, true);
deleted = true;
}
catch (Exception e)
{
string mensaje = e.Message;
if( mensaje == "The directory is not empty.")
Thread.Sleep(50);
}
} while (deleted == false);
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), lanzar una excepción IOException como se describió anteriormente y morir en lugar de iniciar el Explorador de Windows en una carpeta principal y proceder como esperado.
Resolví una posible instancia del problema planteado cuando los métodos eran asíncronos y codificados de esta manera:
// 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? Hay un aspecto asíncrono de deshacerse del identificador utilizado para verificar la existencia con la que Microsoft no ha podido hablar. Es como si el método asíncrono dentro de una sentencia if tuviera la sentencia if actuando como una sentencia using.
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 correctamente la funcionalidad:
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);
}
Sé que no maneja los casos de permisos que se mencionan más adelante, pero para todos los efectos, FAR BETTER proporciona la funcionalidad esperada del Directorio / Eliminación de acciones original / stock - y también con mucho menos código .
Puede continuar con el procesamiento de manera segura porque el antiguo directorio estará fuera del camino ... incluso si no se ha ido porque el ''sistema de archivos aún 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 las excepciones), la última línea se puede reemplazar por:
ThreadPool.QueueUserWorkItem((o) => { Directory.Delete(tfilename, recursive); });
Todavía estás seguro de seguir trabajando.
El directorio o un archivo en él está bloqueado y no se puede eliminar. Encuentra al culpable que lo bloquea y ve si puedes eliminarlo.
Este error se produce si algún archivo o directorio se considera en uso. Es un error engañoso. Verifique si tiene ventanas de explorador o ventanas de línea de comandos abiertas a cualquier directorio en el árbol, o un programa que esté usando un archivo en ese árbol.
Esto es debido 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:
<healthMonitoring enabled="true">
<rules>
<add name="MyAppLogEvents" eventName="Application Lifetime Events" provider="EventLogProvider" profile="Critical"/>
</rules>
</healthMonitoring>
Después de eso echa un vistazo Windows Log -> Application
. Que esta pasando:
Cuando elimine una carpeta, si hay alguna subcarpeta, Delete(path, true)
elimine primero la subcarpeta. Es suficiente para que FileChangesMonitor sepa acerca de la eliminación y cierre su aplicación. Mientras tanto, su directorio principal no se ha eliminado todavía. 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 subcarpetas 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, FileChangesNotifications. , Desactivar FCN con el Registro , Detener FileChangesMonitor usando Reflection (ya que no hay un método expuesto) , pero no todos parecen estar bien, porque FCN está ahí para 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á excepciones. Para que sean visibles desde la web hay dos formas:
Cree un controlador que maneje las llamadas entrantes y luego devuelva los archivos leyendo desde una 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. Así dejarás a IIS su trabajo específico. Puede 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.
He tenido este mismo problema con Windows Workflow Foundation en un servidor de compilación con TFS2012. Internamente, el flujo de trabajo denominado Directory.Delete () con el indicador recursivo establecido en verdadero. Parece estar relacionado con la red en nuestro caso.
Estábamos eliminando una carpeta desplegable binaria en un recurso compartido de red antes de volver a crearla y rellenarla con los últimos binarios. Cualquier otra construcción fallaría. Al abrir la carpeta desplegable después de una compilación fallida, la carpeta estaba vacía, lo que indica que todos los aspectos de la llamada a Directory.Delete () se realizaron correctamente excepto para eliminar el directorio en realidad.
El problema parece deberse a la naturaleza asíncrona 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, aunque 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 completamente los archivos.
Dos posibles soluciones en nuestro caso:
- Construya la eliminación recursiva en nuestro propio código con retrasos y verificaciones entre cada paso
- Vuelva a intentarlo hasta X veces después de una excepción IOException, dando un retraso antes de volver a intentarlo
El último método es rápido y sucio, pero parece hacer el truco.
Ninguna de las respuestas anteriores funcionó para mí. Parece que el uso de mi propia aplicación DirectoryInfo
en el directorio de destino hacía que permaneciera bloqueada.
Forzar la recolección de basura parecía resolver el problema, pero no de inmediato. Algunos intentos de eliminar donde sea necesario.
Tenga en cuenta Directory.Exists
que puede desaparecer después de una excepción. No sé por qué la eliminación para mí se retrasó (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.");
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:
/// <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))
{
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);
}
}
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 violación de acceso, pero el directorio no estará 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 irán al directorio del último archivo abierto, lo que causaría eso.
Tuve este problema hoy. Ocurrió porque tenía Windows Explorer 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 manijas abiertas en el directorio.
Además, MSDN tiene claro que no tiene que escribir su propia recusión: msdn.microsoft.com/en-us/library/fxeahc5f.aspx