Я использую .NET 3.5, пытаясь рекурсивно удалить каталог, используя:
Directory.Delete(myPath, true);
Я понимаю, что это должно вызывать, если файлы используются или есть проблема с разрешениями, но в противном случае он должен удалить каталог и все его содержимое.
Однако иногда я получаю следующее:
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)
...
Я не удивлен, что этот метод иногда вызывает ошибку, но я удивлен, когда получил именно это сообщение, когда рекурсивно истинно. (У меня знать каталог не пустой.)
Есть ли причина, по которой я увидел бы это вместо AccessViolationException?
Кажется, это какая-то проблема ввода-вывода, кроме того, что каталог не пуст, например, дескрипторы открытых файлов или что-то в этом роде. Я бы попробовал использовать опцию рекурсивного удаления, а затем в перехвате IOException найти и закрыть все дескрипторы открытых файлов, а затем повторить попытку. Здесь обсуждают это: stackoverflow.com/questions/177146/…





У меня была такая же проблема под Delphi. В результате мое собственное приложение блокировало каталог, который я хотел удалить. Каким-то образом каталог был заблокирован, когда я писал в него (некоторые временные файлы).
Уловка 22 заключалась в том, что я сделал простой сменить каталог для его родительского элемента перед его удалением.
+1 Вот кое-что, о чем упоминает msdn для Directory.Delete!
какое-либо окончательное решение с полным образцом исходного кода, работающим над этим?
Возможно ли, что у вас есть состояние гонки, когда другой поток или процесс добавляет файлы в каталог:
Последовательность будет такой:
Процесс удаления A:
Если кто-то еще добавит файл между 1 и 2, то, возможно, 2 вызовет указанное исключение?
Каталог или файл в нем заблокированы и не могут быть удалены. Найдите виновника, который его блокирует, и посмотрите, сможете ли вы его устранить.
T1000 пользователю с открытой папкой: «Вы уволены!»
Примечание редактора: Хотя этот ответ содержит некоторую полезную информацию, на самом деле он неверен относительно работы Directory.Delete. Пожалуйста, прочтите комментарии к этому ответу и другие ответы на этот вопрос.
Я сталкивался с этой проблемой раньше.
Корень проблемы в том, что эта функция не удаляет файлы, находящиеся в структуре каталогов. Итак, что вам нужно сделать, это создать функцию, которая удаляет все файлы в структуре каталогов, а затем все каталоги перед удалением самого каталога. Я знаю, что это противоречит второму параметру, но это гораздо более безопасный подход. Кроме того, вы, вероятно, захотите удалить атрибуты доступа ТОЛЬКО ДЛЯ ЧТЕНИЯ из файлов прямо перед их удалением. В противном случае это вызовет исключение.
Просто вставьте этот код в свой проект.
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);
}
Кроме того, я лично добавляю ограничение на области машины, которые разрешено удалять, потому что вы хотите, чтобы кто-то вызвал эту функцию на C:\WINDOWS (%WinDir%) или C:\.
Серьезно? Похоже на простое расширение .NET. Почему разработчики фреймворка решили опустить это?
Кстати, спасибо, это была полезная копия / вставка.
Это нонсенс. Directory.Delete (myPath, true) - это перегрузка, которая удаляет все файлы, находящиеся в структуре каталогов. Если вы хотите ошибиться, ошибитесь с ответом Райана С.
В приведенном выше коде результат всегда ложный, есть ли для этого причина?
У меня было такое же чувство, и мне кажется, что Лиже Райан С. Насвер имеет более прямое отношение к этой проблеме.
+1, потому что, хотя Directory.Delete () действительно удаляет файлы внутри своих подкаталогов (с рекурсивным = true), он выдает исключение «IOException: каталог не пуст», если один из подкаталогов или файлов доступен только для чтения. Так что это решение работает лучше, чем Directory.Delete ()
У нас есть продукт, развернутый во многих различных средах. Недавно у меня было еще одно из этих исключений в другом модуле. Я вижу, что в каталоге нет файлов только для чтения. Я снова указываю на решение Райана С.
Ваше утверждение, что Directory.Delete(path, true) не удаляет файлы, неверно. См. MSDN msdn.microsoft.com/en-us/library/fxeahc5f.aspx
-1 Может кто-нибудь поставить четкий маркер, что справедливость такого подхода очень сильно сомневается. Если Directory.Delete(string,bool) выходит из строя, что-то заблокировано или неправильно разрешено, и не существует универсального решения такой проблемы. Людям необходимо решать эту проблему в их контексте, и мы не должны сильно волноваться за каждую идею (с повторными попытками и проглатыванием исключений) и надеяться на хороший результат.
у меня не сработает. по-прежнему получать исключение «Каталог не пуст», если некоторые подпапки открываются в проводнике.
@mt_serg Вы получите этот результат, если попытаетесь в этом случае выполнить удаление в самом Проводнике. AFAIK вам нужно использовать какой-то кладж уровня ядра, чтобы принудительно закрыть / сломать другой дескриптор файла приложения, чтобы удалить файл.
Остерегайтесь этого подхода, если в вашем каталоге, который вы удаляете, есть ярлыки / символические ссылки на другие папки - вы можете в конечном итоге удалить больше, чем вы ожидали.
Да, @Chanakya прав. Directory.Delete(path, true) не удаляет файлы / папки, на которые указывают символические ссылки. Предлагаемый им метод работает! Чтобы избежать этого, вызовите File.GetAttributes и проверьте наличие бита FileAttributes.ReparsePoint. Если он установлен, обязательно удалите его из атрибутов файла / папки перед удалением. Также в случае папки не вызывайте рекурсивно собственную функцию для символьной ссылки. Вместо этого просто удалите его с помощью Directory.Delete(dir, false), и он должен работать.
Этот ответ помог обойти исключения, доступные только для чтения. Несмотря на то, что у Directory.Delete нет перегрузки, которая это делает, по крайней мере, этот фрагмент кода имеет! Спасибо, Джереми Эдвардс!
Иногда это решение не работает. После использования этого метода некоторые каталоги иногда все еще существуют.
Спасибо! Это было именно то, что мне нужно.
У меня все заработало, за исключением того, что есть ограничения. Не работает, когда: 1) файл из любого каталога / подкаталога открыт или заблокирован, 2) папка или любая подпапка открыта. Вы должны учитывать эти факторы при любом использовании этого кода. # 1: Если файл является приложением (.exe) или запускает приложение (т.е. является текстовым файлом, поэтому запускается Блокнот), вы можете запустить код WMI, чтобы завершить этот процесс на этом целевом ПК, или выполнить Process.Kill(), если локальный ПК и добавить задержку / повторную попытку блокировок с помощью AV, программного обеспечения резервного копирования и т. д. №2: вы можете завершить работу explorer.exe с помощью кода WMI удаленно или Process.Kill() локально.
Четыре из приведенных выше комментариев, все из 2010 года, относятся к ответу "Райана С.". Некоторое время за последние 8 лет Райан С., по-видимому, превратился в @ryascl, он же Райан Смит.
Я получал то же исключение, и я изменил последнюю строку из Directory.Delete (target_dir, false); в Directory.Delete (target_dir, правда); и он отлично работает.
Его текущее имя пользователя - @rpisryan. Ответ: stackoverflow.com/a/1703799/2724588
Если текущий каталог вашего приложения (или любого другого приложения) - это тот, который вы пытаетесь удалить, это не будет ошибкой нарушения прав доступа, но каталог не будет пустым. Убедитесь, что это не ваше собственное приложение, изменив текущий каталог; также убедитесь, что каталог не открыт в какой-либо другой программе (например, Word, excel, Total Commander и т. д.). Большинство программ будут переходить в каталог с последним открытым файлом, что могло бы вызвать это.
У меня были эти странные проблемы с разрешениями при удалении каталогов профиля пользователя (в C: \ Documents and Settings), несмотря на то, что я мог сделать это в оболочке Explorer.
File.SetAttributes(target_dir, FileAttributes.Normal);
Directory.Delete(target_dir, false);
Для меня не имеет смысла, что делает с каталогом «файловая» операция, но я знаю, что она работает, и мне этого достаточно!
По-прежнему нет надежды, когда в каталоге много файлов и проводник открывает папку, содержащую эти файлы.
Если вы пытаетесь рекурсивно удалить каталог a и каталог a\b открыт в проводнике, b будет удален, но вы получите сообщение об ошибке «каталог не пуст» для a, даже если он пуст, когда вы пойдете и посмотрите. Текущий каталог любого приложения (включая проводник) сохраняет дескриптор каталога. Когда вы вызываете Directory.Delete(true), он удаляет снизу вверх: b, затем a. Если b открыт в проводнике, проводник обнаружит удаление b, сменит каталог вверх cd .. и очистит открытые дескрипторы. Поскольку файловая система работает асинхронно, операция Directory.Delete завершается ошибкой из-за конфликтов с проводником.
Первоначально я опубликовал следующее решение с идеей прерывания текущего потока, чтобы дать обозревателю время освободить дескриптор каталога.
// incomplete!
try
{
Directory.Delete(path, true);
}
catch (IOException)
{
Thread.Sleep(0);
Directory.Delete(path, true);
}
Но это работает, только если открытый каталог является дочерним немедленный каталога, который вы удаляете. Если a\b\c\d открыт в проводнике и вы используете его на a, этот метод не сработает после удаления d и c.
Этот метод будет обрабатывать удаление глубокой структуры каталогов, даже если один из каталогов нижнего уровня открыт в проводнике.
/// <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);
}
}
Несмотря на дополнительную работу по рекурсии, нам, по-прежнему, приходится обрабатывать UnauthorizedAccessException, которые могут возникнуть в процессе. Неясно, прокладывает ли первая попытка удаления путь второй, успешной, или это просто временная задержка, вызванная выбросом / перехватом исключения, которое позволяет файловой системе наверстать упущенное.
Вы можете уменьшить количество генерируемых и перехватываемых исключений в типичных условиях, добавив Thread.Sleep(0) в начало блока try. Кроме того, существует риск того, что при большой загрузке системы вы можете выполнить обе попытки Directory.Delete и потерпеть неудачу. Считайте это решение отправной точкой для более надежного рекурсивного удаления.
Это решение затрагивает только особенности взаимодействия с проводником Windows. Если вам нужна надежная операция удаления, помните, что что угодно (сканер вирусов, что угодно) может иметь открытый дескриптор того, что вы пытаетесь удалить, в любое время. Так что вам придется повторить попытку позже. Сколько позже и сколько раз вы будете пытаться, зависит от того, насколько важно удалить объект. Как MSDN указывает,
Robust file iteration code must take into account many complexities of the file system.
Это невинное заявление, снабженное лишь ссылкой на справочную документацию NTFS, должно заставить вас всколыхнуться.
(Редактировать: Много. Изначально у этого ответа было только первое неполное решение.)
Кажется, что вызов Directory.Delete (path, true), когда путь или одна из папок / файлов по пути открыт или выбран в проводнике Windows, вызовет исключение IOException. Закрытие проводника Windows и повторный запуск моего существующего кода без предложенного выше try / catch сработали нормально.
Я думаю, что это может произойти, даже если проводник не открыт в каталоге. Я думаю, это может быть связано с обращением к нему антивируса. В моем случае на машине был Нортон.
Я не могу понять, как и почему это работает, но это сработало для меня, когда я устанавливал атрибуты файлов и писал мою собственную рекурсивную функцию.
@CarlosLiu Потому что это дает "Проводнику возможность освободить дескриптор каталога"
Происходит то, что система просит Explorer «освободить дескриптор каталога», а затем пытается удалить каталог. Если дескриптор каталога не был удален вовремя, возникает исключение и выполняется блок catch (тем временем Explorer все еще освобождает каталог, поскольку не было отправлено никакой команды, запрещающей это делать). Обращение к Thread.Sleep(0) может быть, а может и не быть необходимым, поскольку блок catch уже дал системе немного больше времени, но он обеспечивает небольшую дополнительную безопасность за небольшую плату. После этого вызывается Delete с уже освобожденным каталогом.
Функции, связанные с существованием файлов, всегда полагаются на нестандартные методы, иначе нет.
Стоит ли добавить немного большее время сна (например, Thread.Sleep (100) - просто для смеха - и, возможно, для более быстрых компьютеров? Я не могу воспроизвести проблему, чтобы подтвердить, что эта идея принесет какую-либо пользу, но может ли кто-нибудь подтвердить (или угадайте), разумна ли это ставка?
@PandaWood на самом деле у меня работал только этот Sleep (100). Sleep (0) не работал. Понятия не имею, что происходит и как это правильно решить. Я имею в виду, что если это зависит от загрузки сервера и в будущем должно быть 300 или 400? Откуда это знать. Должен быть другой правильный способ ...
@ Роман спасибо, интересно. Я тоже использовал 100. То, что заблокировано, должно быть в процессе разблокировки, но еще не завершено, и это то, чего мы ждем. Я предполагаю, что это похоже на сбой сетевого подключения, вы можете пытаться только так долго или так много раз, что в какой-то момент ваш код может отказаться.
@Roman - Идея: полностью пройти рекурсию. Верните логическое значение False, если в какой-то момент возникла проблема. Передайте это значение False до метода верхнего уровня. На этом этапе мы удалим все, что может быть легко удалено, и мы прикажем системе ПОПЫТАТЬСЯ удалить все, поэтому мы надеемся, что Explorer начинает освобождать свои дескрипторы. Теперь засыпаем (500), чтобы дать достаточно времени для любого высвобождения. Затем повторите всю рекурсию. Таким образом, мы делаем один длительный сон, а не повторяем его на всех уровнях рекурсии. Если по-прежнему False, мог бы снова спать еще дольше.
Обратите внимание, что вы также можете столкнуться с этой проблемой, если забудете удалить средство отслеживания файлов, просматривая контент в одной из папок / файлов, которые вы пытаетесь удалить - это то, что я забыл сделать ...
Похоже, что наличия пути или подпапки, выбранной в проводнике Windows, достаточно, чтобы заблокировать однократное выполнение Directory.Delete (path, true), выбросить исключение IOException, как описано выше, и умереть вместо того, чтобы загружать проводник Windows в родительскую папку и действовать как ожидал.
Похоже, это была моя проблема. Как только я закрыл Проводник и снова запустил его, не исключение. Даже выбора родителя было недостаточно. Мне пришлось закрыть Explorer.
Да, такое бывает и есть причина. Итак, есть идеи, как с этим справиться программно, или просто всегда нужно убедиться, что у всех 1000 пользователей эта папка закрыта?
в случае сетевых файлов Directory.DeleteHelper (recursive: = true) может вызвать исключение IOException, вызванное задержкой удаления файла.
У меня сегодня была эта проблема. Это произошло потому, что у меня был проводник Windows, открытый для каталога, который пытался удалить, что привело к сбою рекурсивного вызова и, следовательно, к IOException. Убедитесь, что в каталоге нет открытых дескрипторов.
Кроме того, MSDN ясно дает понять, что вам не нужно писать собственное возражение: http://msdn.microsoft.com/en-us/library/fxeahc5f.aspx
Я потратил несколько часов, чтобы решить эту проблему и другие исключения с удалением каталога. Это мое решение
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);
}
}
Этот код имеет небольшую задержку, которая для моего приложения не важна. Но будьте осторожны, задержка может быть проблемой для вас, если у вас много подкаталогов внутри каталога, который вы хотите удалить.
-1 Что за задержка? Никакого программирования случайно, пожалуйста!
@Ruben -1 за необъяснимую задержку? Это настоящее наказание за подобное, ИМО!
4 голоса за комментарий: по причине. Нет никаких оснований полагать, что вы придумали решение, которое не даст случайного сбоя. lock(_lock) также вызывает у меня сомнения. @ Роджер Я тоже не согласен с твоей жалобой. Представьте, что кто-то в вашей организации прикрепил этот код к вашему приложению, и вы обнаружили его в 16:30, потратив 20 минут на размышления, почему он иногда работает, а иногда по необъяснимым причинам. Код - это не ценность, это вера в заботу, вложенную в него. Итак, ПОЧЕМУ задержка? Комментарии!
@Ruben Я не говорил, что ты ошибаешься. Я просто сказал, что отрицать это только за это - суровое наказание. Я согласен с вами, однако, 4 голоса за не привели к 4 голосам против. Я бы поддержал и ваш комментарий, но не стал бы отрицать ответ из-за необъяснимой задержки :)
@ThunderGr Необъяснимая задержка - это просто конкретный пример случайного программирования. Я никого не голосую против наказывать - у меня на планете есть дела поважнее. Я бы не проголосовал против, если бы оценка была 0, но я встретил ответ раньше, чем более полезные ответы. Следствием того, почему нет 4 отрицательных голоса, является ... почему нет никаких положительных голосов (пожалуйста, позвольте нам не начинать!). В любом случае, сейчас я больше не буду говорить об этом XKCD 386.
@RubenBartelink и другие: хотя мне не особо нравится этот код (я опубликовал другое решение с аналогичным подходом), задержка здесь разумная. Проблема, скорее всего, находится вне контроля приложения; возможно, другое приложение периодически выполняет повторное сканирование файловой системы, таким образом блокируя папку на короткие периоды времени. Задержка решает проблему, обеспечивая обратный отсчет отчета об ошибке до нуля. Какая разница, если мы не имеем ни малейшего представления о первопричине?
@RubenBartelink На самом деле, если подумать, нет, использующий подход задержки и повторной попытки во время удаления каталога NTFS, является здесь безответственным решением. Любой вид продолжающегося обхода файла блокирует удаление, поэтому рано или поздно он обязательно завершится неудачей. И вы не можете ожидать, что все сторонние инструменты для поиска, резервного копирования, антивируса и управления файлами останутся подальше от вашей папки.
@AndreyTarantsov Если задержка будет 100 - почему не 50 или 200; Почему тебе 50? Почему здесь бесконечность, а в твоей - 10? Хотели бы вы, чтобы во фреймворке был такой случайный алгоритм блокировки? Хотели бы вы, чтобы тот отказался, не сказав никому (кроме отладчика?)? И этот, и ваш ответ для меня одинаково спорны.
@RubenBartelink Пока я опаздываю на вечеринку, чтобы дать обоснование, не было бы никакого способа узнать, как долго любой 1 предмет (будь то антивирусный сканер, процедура резервного копирования файлов и т. д.) Будет блокировать файл. Я полагаю, что может быть статистика, и можно взять самое большое время блокировки для всего программного обеспечения, добавить небольшой буфер для безопасности и ожидать, что приложение сможет выполнять свою функцию после этой задержки. Но нет никакого способа узнать, какое программное обеспечение находится на целевом ПК, если мы хотим что-то полностью универсальное и универсальное. Может быть, хорошее время с сегодняшним программным обеспечением не наступит завтра.
@RubenBartelink Другой пример, допустим, вы даете задержку в 100 мс, а максимальное время блокировки любого программного обеспечения на целевом ПК - это программное обеспечение AV = 90 мс. Скажем, у него также есть программное обеспечение для резервного копирования, которое блокирует файлы на 70 мс. Теперь AV блокирует файл, ваше приложение ожидает 100 мс, что обычно нормально, но затем обнаруживает другую блокировку, потому что программное обеспечение резервного копирования начинает захват файла на отметке 70 мс сканирования AV, поэтому для освобождения файла потребуется еще 40 мс. Таким образом, хотя программное обеспечение AV занимает больше времени, а ваши 100 мс обычно дольше, чем любое из двух приложений, вам все равно нужно учитывать, когда оно запускается посередине.
Я думаю, что есть файл, открытый каким-то потоком, о котором вы не знаете. У меня была такая же проблема, и я решил ее, закрыв все потоки, которые указывали на каталог, который я хотел удалить.
Я удивлен, что никто не подумал об этом простом нерекурсивном методе, который может удалять каталоги, содержащие файлы только для чтения, без необходимости изменять атрибут только для чтения каждого из них.
Process.Start("cmd.exe", "/c " + @"rmdir /s/q C:\Test\TestDirectoryContainingReadOnlyFiles");
(Немного измените, чтобы не запускать окно cmd на мгновение, которое доступно во всем Интернете)
Приятно поделиться с нами, но не могли бы вы добавить немного изменений, необходимых для предотвращения запуска окна cmd, вместо того, чтобы предлагать нам искать его в сети?
Это не работает. В той же ситуации, когда я могу удалить файл из командной строки или проводника, использование этого кода для вызова rmdir дает код выхода 145, который переводится как «Каталог не пуст». Он оставляет каталог пустым, но все еще остается на месте, точно так же, как Directory.Delete ("", true)
@Kevin Coulombe, Хамм ... Вы уверены, что используете переключатели / s / q?
@Piyush Soni: Содержимое каталога было удалено правильно. Он только оставил пустой каталог на месте и вернул ошибку 145. Из того, что я собрал на форумах MS, он возвращает эту ошибку во многих различных сценариях. Но больше я не исследовал. Я использую сторонние компоненты COM, поэтому, возможно, один из них держит дескрипторы файлов открытыми, когда этого не следует. В любом случае, мне нужно было следить за тем, чтобы со временем не произошло утечки дискового пространства, поэтому я выбрал модуль сбора мусора, чтобы убедиться, что файл будет удален позже, если он не удастся по какой-либо причине. Извините, я не могу предоставить дополнительную информацию.
@KevinCoulombe: Да, это должны быть компоненты COM. Когда я пробую использовать простой старый C#, он работает и удаляет каталог вместе с файлами внутри (только для чтения или не только для чтения).
Звучит как плохая идея для меня, потому что это решение не будет работать на Mono / Linux.
@frenchone: И это почему-то делает его плохим идея? Я уверен, что были бы эквивалентные команды, чтобы сделать то же самое в linux с моно.
Если вы начнете полагаться на внешние компоненты в поисках того, что должно быть во фреймворке, тогда это «неидеальная» идея, потому что она больше не переносима (или сложнее). Что делать, если exe нет? Или параметр / изменился? Если решение Джереми Эдвардса работает, то его следует предпочесть ИМХО.
Еще одна хорошая альтернатива: stackoverflow.com/a/648055/1417104
Прежде чем двигаться дальше, проверьте следующие причины, которые находятся под вашим контролем:
В противном случае проверьте следующие законные причины, не зависящие от вас:
Если какая-либо из вышеперечисленных проблем является проблемой, вы должны понять, почему это происходит, прежде чем пытаться улучшить свой код удаления. Должен ваше приложение удаляет файлы, доступные только для чтения или недоступные? Кто их так пометил и почему?
После того, как вы исключили вышеуказанные причины, вероятность ложных сбоев все еще существует. Удаление не удастся, если кто-то держит дескриптор любого из удаляемых файлов или папок, и есть много причин, по которым кто-то может перечислять папку или читать ее файлы:
Общий подход к устранению ложных отказов состоит в том, чтобы попробовать несколько раз, делая паузы между попытками. Очевидно, вы не хотите продолжать попытки вечно, поэтому вам следует отказаться от определенного количества попыток и либо выбросить исключение, либо проигнорировать ошибку. Нравится:
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
}
На мой взгляд, подобный помощник следует использовать для всех удалений, потому что всегда возможны ложные сбои. Однако ВЫ ДОЛЖНЫ АДАПТИРОВАТЬ ЭТОТ КОД К СВОЕМУ СЛУЧАЮ ИСПОЛЬЗОВАНИЯ, а не просто копировать его вслепую.
У меня были ложные сбои для внутренней папки данных, созданной моим приложением, расположенной в% LocalAppData%, поэтому мой анализ выглядит следующим образом:
Папка контролируется исключительно моим приложением, и у пользователя нет веской причины идти и отмечать вещи как доступные только для чтения или недоступные внутри этой папки, поэтому я не пытаюсь обрабатывать этот случай.
Там нет ценных вещей, созданных пользователями, поэтому нет риска насильственно удалить что-то по ошибке.
Поскольку это внутренняя папка данных, я не ожидаю, что она будет открыта в проводнике, по крайней мере, я не чувствую необходимости специально обрабатывать этот случай (т.е. я прекрасно справляюсь с этим случаем через службу поддержки).
Если все попытки терпят неудачу, я предпочитаю игнорировать ошибку. В худшем случае приложение не может распаковать некоторые новые ресурсы, вылетает и предлагает пользователю обратиться в службу поддержки, что приемлемо для меня, если это случается нечасто. Или, если приложение не выйдет из строя, оно оставит некоторые старые данные, что снова приемлемо для меня.
Я предпочитаю ограничить количество повторных попыток до 500 мс (50 * 10). Это произвольный порог, который работает на практике; Я хотел, чтобы порог был достаточно коротким, чтобы пользователи не убивали приложение, думая, что оно перестало отвечать. С другой стороны, полсекунды - это достаточно времени, чтобы злоумышленник закончил обработку моей папки. Судя по другим ответам SO, которые иногда считают приемлемым даже Sleep(0), очень немногие пользователи когда-либо испытают более одной попытки.
Я повторяю каждые 50 мсек, это еще одно произвольное число. Я чувствую, что если файл обрабатывается (индексируется, проверяется), когда я пытаюсь его удалить, 50 мс - это как раз подходящее время для ожидания завершения обработки в моем случае. Кроме того, 50 мс - это достаточно мало, чтобы не вызвать заметного замедления; опять же, Sleep(0) кажется достаточно во многих случаях, поэтому мы не хотим слишком долго откладывать.
Код повторяет попытку при любых исключениях ввода-вывода. Обычно я не ожидаю каких-либо исключений при доступе к% LocalAppData%, поэтому я выбрал простоту и принял риск задержки 500 мс в случае возникновения законного исключения. Я также не хотел выяснять способ обнаружения точного исключения, которое я хочу повторить.
P.P.S. Несколько месяцев спустя я рад сообщить, что этот (несколько безумный) фрагмент кода полностью решил проблему. Количество запросов в службу поддержки по этой проблеме сокращается до нуля (примерно 1-2 раза в неделю).
+0 Хотя это более надежный и менее «вот он; идеальное решение для вас », чем stackoverflow.com/a/7518831/11635, для меня то же самое - программирование случайно - обращайтесь с осторожностью. Один полезный момент, воплощенный в вашем коде, заключается в том, что если вы собираетесь выполнить повторную попытку, вам действительно нужно учитывать, что вы участвуете в гонке с двусмысленностью того, был ли каталог «ушел» с момента последней попытки [и niave Directory.Exists guard не решит это.]
люблю это ... не знаю, что я делаю, это всегда болит для меня ... но это не потому, что у меня открыт каталог в проводнике ... не так много шума в Интернете по этому поводу больше -или-менее ошибка ... по крайней мере, у нас с Андреем есть способ с этим справиться :)
Это, вероятно, самый краткий, но полный ответ, который все еще касается странных случаев, когда каталоги случайным образом отказываются удаляться. Кроме того, я неоднократно сталкивался с проблемой упрямого каталога на разных машинах. Я нигде не нашел достойной документации о том, почему именно это происходит, но я предполагаю, что это всегда связано с перехватами файлов на уровне ОС, такими как антивирус или поиск Windows / Google (rip) на рабочем столе.
@RubenBartelink Хотя этот код довольно случайный (и задержки в 500 мс вполне может быть недостаточно), я действительно не вижу разумного способа справиться с файловой системой, которая не позволяет удалять открытые элементы. Это не похоже на то, что в Windows есть API для получения монопольного доступа к папке. Любое решение, которое работает на практике, не вызывает проблем с поддержкой и имеет разумное поведение в худшем случае, в моей книге вполне приемлемо.
@RubenBartelink Но, подумав об этом дальше, я расширил ответ, включив в него все рассуждения, которые входят в такой помощник. Надеюсь, тебе теперь нравится больше. :-)
@AndreyTarantsov +1 Да, сейчас намного лучше, даже если 10x и 50ms очень спорны - давайте надеяться, что голоса пойдут правильно, потому что некоторые из других просто шокирующе плохи
@RubenBartelink Просто из любопытства, что бы вы использовали вместо 10x и 50 мс? Я понимаю, что это не сработает для каждого случая, и, возможно, мне следовало опубликовать более подходящую универсальную версию. Я просто ожидаю, что разработчики приложат все усилия при повторном использовании найденного кода.
@AndreyTarantsov Применять мозги на каком основании? Как ваш мозг придумал 10 и 50? Для меня входы следующие: 1) Sleep(0) дает результат - Sleep(1) или Sleep(50) не спит лучше (но если он выпущен через 1 мс, вы потратили 49 2), если это не будет сделано за 50 мс, почему это будет сделано за 500? 3) важно ли его удалить? Я бы управлял циклом по общему затраченному времени (в секундах) с коротким сном (вероятно, экспоненциально). И я бы заключил это в метод, который принимает TimeSpan, и пусть вызывающий будет принужденный думать.
Поскольку люди, читающие этот вопрос, ищут свою магию и воля вставляют ваш код и, они не будут его редактировать. Тот факт, что вы добавляете туда 10 и 50, заставит многих людей подумать (да, по недействительным причинам), что они являются важными числами, имеющими некоторую значимость: а) необходимые или б) достаточные, чтобы заставить их работать. Их нет ни того, ни другого и не должно быть. Не заставляй меня отвечать на этот проклятый вопрос!
@RubenBartelink Хорошо, поэтому я думаю, что мы можем согласиться с этим: размещение фрагмента кода, который работает для одного конкретного приложения (и никогда не предназначался для каждого случая) в качестве ответа SO, будет медвежьей услугой для многих новичков и / или невежественные разработчики. Я дал его как отправную точку для настройки, но да, некоторые люди собираются использовать его как есть, и это плохо.
Мне нравится идея. Он решил мою проблему и теперь является частью вспомогательной библиотеки, которую я регулярно использую. Я установил параметры времени ожидания и счетчика повторов с разумными значениями по умолчанию (20 повторов, 50 мс) на основе экспериментов: для того, чтобы открыть проводник на 6 уровней в глубину каталога для удаления, потребуется 8 попыток удалить каталог на Win7 / quadcore. Мне не понравился метод перечисления из @ryascl, потому что он (вероятно) вводит накладные расходы, когда простое удаление будет работать, обычно в большинстве случаев.
Мне нужно было удалить папки iis ... это работа для меня.
Остерегайтесь, это не сработает с тихим исключением. Используйте if (gnomes == magicDust) throw;. Кстати, я считаю этот код лучшим, так как это наиболее щадящее решение. Это только более терпеливо, чем рекурсивное удаление каталога по умолчанию, но не заставляет ничего подобного другим решениям.
@nopara Сравнение не нужно; если мы не в курсе, мы потерпели неудачу. И да, во многих случаях вы захотите сгенерировать исключение, а затем добавить соответствующий код обработки ошибок в стек, вероятно, с видимым для пользователя сообщением.
Рекурсивное удаление каталога, которое не приводит к удалению файлов, безусловно, является неожиданным. Мое исправление для этого:
public class IOUtils
{
public static void DeleteDirectory(string directory)
{
Directory.GetFiles(directory, "*", SearchOption.AllDirectories).ForEach(File.Delete);
Directory.Delete(directory, true);
}
}
У меня были случаи, когда это помогало, но обычно Directory.Delete удаляет файлы внутри каталогов при рекурсивном удалении, как задокументировано в msdn.
Время от времени я сталкиваюсь с этим нерегулярным поведением, также как пользователь Windows Explorer: иногда я не могу удалить папку (он думает, что бессмысленным сообщением является «доступ запрещен»), но когда я углубляюсь и удаляю нижние элементы, я могу удалить верхние. предметы тоже. Итак, я предполагаю, что приведенный выше код имеет дело с аномалией ОС, а не с проблемой библиотеки базового класса.
У меня была такая же проблема с Windows Workflow Foundation на сервере сборки с TFS2012. Внутренне рабочий процесс называется Directory.Delete () с рекурсивным флагом, установленным в значение true. В нашем случае это похоже на сеть.
Мы удаляли двоичную папку размещения на сетевом ресурсе перед повторным созданием и повторным заполнением ее последними двоичными файлами. Любая другая сборка выйдет из строя. При открытии папки размещения после неудачной сборки папка была пустой, что указывает на то, что все аспекты вызова Directory.Delete () были успешными, за исключением удаления фактического каталога.
Проблема, по-видимому, вызвана асинхронным характером сетевых файловых обменов. Сервер сборки сказал файловому серверу удалить все файлы, и файловый сервер сообщил об этом, даже если он не был полностью завершен. Затем сервер сборки запросил удаление каталога, и файловый сервер отклонил запрос, поскольку он еще не полностью завершил удаление файлов.
В нашем случае два возможных решения:
Последний метод быстр и грязен, но, похоже, помогает.
Это из-за FileChangesNotifications.
Это происходит, начиная с ASP.NET 2.0. Когда вы удаляете какую-либо папку в приложении, это перезапускается. Вы можете убедиться в этом сами, используя Мониторинг работоспособности ASP.NET.
Просто добавьте этот код в свой web.config / configuration / system.web:
<healthMonitoring enabled = "true">
<rules>
<add name = "MyAppLogEvents" eventName = "Application Lifetime Events" provider = "EventLogProvider" profile = "Critical"/>
</rules>
</healthMonitoring>
После этого проверьте Windows Log -> Application.
Что происходит:
Когда вы удаляете папку, если есть какая-либо подпапка, Delete(path, true) сначала удаляет подпапку. FileChangesMonitor достаточно знать об удалении и закрыть ваше приложение. Между тем ваш основной каталог еще не удален. Это событие из журнала:

Delete() не завершил свою работу, и поскольку приложение закрывается, возникает исключение:

Когда вы нет вложенных папок в удаляемой папке, Delete () просто удаляет все файлы и эту папку, приложение тоже перезапускается, но вы не бывает исключений, потому что перезапуск приложения ничего не прерывает. Но все равно вы теряете все внутрипроцессные сеансы, приложение не отвечает на запросы при перезапуске и т. д.
Что теперь?
Есть некоторые обходные пути и настройки для отключения этого поведения, Directory Junction, Отключение FCN с помощью реестра, Остановка FileChangesMonitor с помощью Reflection (поскольку нет открытого метода), но все они кажутся неправильными, потому что FCN существует не просто так. Он ищет структура вашего приложения, который не является структура ваших данных. Короткий ответ: поместите папки, которые вы хотите удалить, за пределами вашего приложения. FileChangesMonitor не будет получать уведомления, и ваше приложение не будет перезапускаться каждый раз. Вы не получите исключений. Чтобы сделать их видимыми из Интернета, есть два способа:
Сделайте контроллер, который обрабатывает входящие вызовы, а затем возвращает файлы, читая их из папки вне приложения (вне wwwroot).
Если ваш проект большой и производительность важнее всего, настройте отдельный небольшой и быстрый веб-сервер для обслуживания статического контента. Таким образом, вы оставите IIS его конкретную работу. Это может быть тот же компьютер (mongoose для Windows) или другой компьютер (nginx для Linux). Хорошая новость в том, что вам не нужно платить дополнительную лицензию Microsoft для установки сервера статического содержимого в Linux.
Надеюсь это поможет.
Ни один из приведенных выше ответов не помог мне. Похоже, что использование DirectoryInfo в целевом каталоге моим собственным приложением заставляло его оставаться заблокированным.
Похоже, принудительная сборка мусора решила проблему, но не сразу. Несколько попыток удалить где необходимо.
Обратите внимание на Directory.Exists, так как он может исчезнуть после исключения. Я не знаю, почему удаление для меня было отложено (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.");
-1 Программирование случайно. Какой объект что делает при GC? Это хороший общий совет? (Я верю вам, когда вы говорите, что у вас есть проблема, что вы использовали этот код и чувствуете, что сейчас у вас нет проблемы, но дело не в этом)
@RubenBartelink Согласен. Это взлом. Код Voodoo, который что-то делает, когда неясно, что он решает и как. Я бы хотел найти подходящее решение.
Моя проблема в том, что все, что он добавляет сверх stackoverflow.com/a/14933880/11635, является весьма спекулятивным. Если бы я мог, я бы дал -1 за дублирование и -1 за предположение / программирование по совпадению. Распространение GC.Collect - это а) просто плохой совет и б) не достаточно распространенная общая причина заблокированных каталогов, чтобы заслуживать включения сюда. Просто выберите одно из других и не сейте путаницу в умах невинных читателей.
Используйте GC.WaitForPendingFinalizers (); после GC.Collect (); это будет работать, как ожидалось.
Не уверен, не тестировал, но, возможно, лучше было бы что-нибудь сделать с помощью оператора using, тогда: 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); } } }
Вы можете воспроизвести ошибку, запустив:
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");
При попытке удалить каталог 'b' выдается исключение IOException «Каталог не пуст». Это глупо, ведь мы только что удалили каталог «c».
Насколько я понимаю, объяснение состоит в том, что каталог «c» помечен как удаленный. Но удаление еще не выполнено в системе. Система получила ответ, что задание выполнено, хотя фактически оно все еще обрабатывается. Система, вероятно, дождется, когда файловый проводник сфокусируется на родительском каталоге, чтобы зафиксировать удаление.
Если вы посмотрите исходный код функции удаления (http://referencesource.microsoft.com/#mscorlib/system/io/directory.cs), вы увидите, что она использует встроенную функцию Win32Native.RemoveDirectory. Это поведение «не ждать» отмечено здесь:
The RemoveDirectory function marks a directory for deletion on close. Therefore, the directory is not removed until the last handle to the directory is closed.
(http://msdn.microsoft.com/en-us/library/windows/desktop/aa365488(v=vs.85).aspx)
Сон и повтор - вот решение. См. Раствор рясла.
Эта ошибка возникает, если какой-либо файл или каталог считается используемым. Это ошибочная ошибка. Проверьте, открыты ли у вас окна проводника или окна командной строки для любого каталога в дереве или программы, использующей файл в этом дереве.
Как упоминалось выше, «принятое» решение не работает в точках повторной обработки - но люди все еще отмечают его (???). Есть гораздо более короткое решение, которое должным образом воспроизводит функциональность:
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);
}
Я знаю, что не обрабатывает случаи разрешений, упомянутые позже, но для всех намерений и целей НАМНОГО ЛУЧШЕ предоставляет ожидаемая функциональность исходного / стандартного Directory.Delete () - и с гораздо меньшим количеством кода.
Вы можете безопасно продолжить обработку, потому что старый каталог не будет мешать ... даже если не исчез, потому что `` файловая система все еще догоняет '' (или любое другое оправдание, которое MS дала для предоставления неработающей функции).
В качестве преимущества, если вы знаете, что ваш целевой каталог большой / глубокий и не хотите ждать (или беспокоиться об исключениях), последнюю строку можно заменить на:
ThreadPool.QueueUserWorkItem((o) => { Directory.Delete(tfilename, recursive); });
Вы по-прежнему можете продолжать работу.
Можно ли упростить ваше назначение с помощью: string tfilename = Path.Combine (Path.GetDirectoryName (target), Path.GetRandomFileName ());
Я должен согласиться с Питом. В написанном коде разделитель не добавляется. Он пошел по моему пути \\server\C$\dir и превратил его в \\server\C$asf.yuw. В результате я получил ошибку на Directory.Move() - Source and destination path must have identical roots. Move will not work across volumes. Работал нормально, как только я использовал код Пита, КРОМЕ ни одной дескриптора, когда есть заблокированные файлы или открытые каталоги, поэтому он никогда не попадает в команду ThreadPool.
ВНИМАНИЕ: этот ответ следует использовать только с рекурсивным = true. Если установлено значение false, каталог будет перемещен, даже если он не пустой. Что было бы ошибкой; правильное поведение в этом случае - выбросить исключение и оставить каталог без изменений.
Этот ответ основан на: https://stackoverflow.com/a/1703799/184528. Разница с моим кодом заключается в том, что мы рекурсивно обрабатываем многие подкаталоги и файлы для удаления только тогда, когда это необходимо, вызов Directory.Delete завершается неудачно с первой попытки (что может произойти из-за того, что проводник Windows просматривает каталог).
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);
}
}
Так как же удалить папку, если там был UnauthorizedAccessException? Он просто бросил бы снова. И опять. И снова ... Потому что каждый раз он будет переходить к catch и снова вызывать функцию. Thread.Sleep(0); не меняет ваши разрешения. Он должен просто зарегистрировать ошибку и корректно выйти из строя в этот момент. И этот цикл будет продолжаться до тех пор, пока (под) каталог открыт - он не закрывает его программно. Готовы ли мы позволить ему делать это до тех пор, пока эти вещи остаются открытыми? Есть ли способ лучше?
Если есть UnauthorizedAccessException, он попытается вручную удалить каждый файл. Таким образом, он продолжает развиваться, переходя в структуру каталогов. Да, потенциально каждый файл и каталог будет генерировать одно и то же исключение, но это также может произойти просто потому, что проводник удерживает его дескриптор (см. stackoverflow.com/a/1703799/184528). Я изменю «tryAgain» на «secondTry», чтобы сделать его более понятным.
Чтобы ответить более кратко, он передает «истина» и выполняет другой путь кода.
Хорошо, видел ваше редактирование, но я имею в виду не удаление файлов, а удаление каталога. Я написал код, в котором я мог бы выполнить Process.Kill() для любого процесса, которым файл может быть заблокирован, и удалить файлы. Проблема, с которой я сталкиваюсь, заключается в удалении каталога, в котором один из этих файлов все еще был открыт (см. stackoverflow.com/questions/41841590/…). Итак, возвращаясь через этот цикл, независимо от того, что еще он делает, если он снова сделает Directory.Delete() в этой папке, он все равно потерпит неудачу, если этот дескриптор не может быть освобожден.
То же самое произойдет и с UnauthorizedAccessException, поскольку удаление файлов (при условии, что это было разрешено, потому что получить этот код на Directory.Delete() не удалось) волшебным образом не дает вам разрешения на удаление каталога.
добавьте true во второй параметр.
Directory.Delete(path, true);
Он удалит все.
Directory.Delete (путь, истина); был первоначальный вопрос
Я решил один из возможных случаев заявленной проблемы, когда методы были асинхронными и закодированы следующим образом:
// delete any existing update content folder for this update
if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath))
await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath);
С этим:
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);
Заключение? Есть некоторый асинхронный аспект избавления от дескриптора, используемого для проверки существования, с которым Microsoft не может связаться. Это как если бы асинхронный метод внутри оператора if имеет оператор if, действующий как оператор using.
Эта проблема может возникнуть в Windows, если в каталоге (или в любом подкаталоге) есть файлы, длина пути которых превышает 260 символов.
В таких случаях вам необходимо удалить \\\\?\C:\mydir вместо C:\mydir.
Об ограничении в 260 символов вы можете прочитать здесь.
Следует упомянуть одну важную вещь (я добавил ее в качестве комментария, но мне это не разрешено): поведение перегрузки изменилось с .NET 3.5 на .NET 4.0.
Directory.Delete(myPath, true);
Начиная с .NET 4.0, он удаляет файлы в самой папке, но НЕ в 3.5. Это также можно увидеть в документации MSDN.
.NET 4.0
Deletes the specified directory and, if indicated, any subdirectories and files in the directory.
.NET 3.5
Deletes an empty directory and, if indicated, any subdirectories and files in the directory.
Я думаю, это всего лишь изменение документации ... если он удаляет только «пустой каталог», что будет означать удаление файлов в каталоге с параметром 2 °? Если пусто - файлов нет ...
Боюсь, вы ошибаетесь. Я разместил это после тестирования кода с обеими версиями фреймворка. Удаление непустой папки в 3.5 вызовет исключение.
Принятый ответ просто неверен, он может сработать для некоторых людей, потому что время, затрачиваемое на получение файлов с диска, освобождает все, что блокировало файлы. Дело в том, что это происходит потому, что файлы блокируются каким-то другим процессом / потоком / действием. В других ответах используется Thread.Sleep (Yuck), чтобы повторить попытку удаления каталога через некоторое время. Этот вопрос требует пересмотра с более современным ответом.
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;
}
Эти тесты показывают пример того, как заблокированный файл может вызвать сбой Directory.Delete и как описанный выше метод TryDeleteDirectory устраняет проблему.
[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);
}
}
}
Не могли бы вы расширить то, что вы подразумеваете под словом «современный»? Каковы преимущества вашего подхода? Почему другие, по вашему мнению, не правы?
Другие не ошибаются. Они просто используют более старые API, такие как Thread.Sleep, которых вам следует избегать сегодня, и вместо этого используют async / await с Task.Delay. Это понятно, это очень старый вопрос.
Этот подход не будет работать в VB.Net (по крайней мере, не с очень буквальным преобразованием строки в строку) из-за BC36943 'Await' cannot be used inside a 'Catch' statement, a 'Finally' statement, or a 'SyncLock' statement..
@amonroejj Вы должны использовать старую версию. Это было исправлено.
Небольшое улучшение вместо возврата истинного if (!Directory.Exists(directoryPath)) { return true; } await Task.Delay(millisecondsDelay);, чтобы дождаться, пока каталог действительно не исчезнет
Ни одно из вышеперечисленных решений не помогло мне. В итоге я использовал отредактированную версию решения @ryascl, как показано ниже:
/// <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);
}
}
Я решил с помощью этой тысячелетней техники (вы можете оставить Нить. Спать самостоятельно в ловушке)
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);
Вам не нужно создавать дополнительный метод для рекурсии или удалять файлы внутри дополнительной папки. Все это происходит автоматически путем вызова
DirectoryInfo.Delete();
Подробности - здесь.
Что-то вроде этого неплохо работает:
var directoryInfo = new DirectoryInfo("My directory path");
// Delete all files from app data directory.
foreach (var subDirectory in directoryInfo.GetDirectories())
{
subDirectory.Delete(true);// true set recursive paramter, when it is true delete sub file and sub folder with files too
}
передача true в качестве переменной для метода удаления, удалит подфайлы и подпапку с файлами.
Вы не увидите AccessViolationException - это для недопустимых операций с указателем, а не для доступа к диску.