Если вы пытаетесь рекурсивно удалить каталог, a
и каталог a\b
открыт в проводнике, b
он будет удален, но вы получите сообщение об ошибке «каталог не пустой», a
даже если он пуст, когда вы идете и смотрите. Текущий каталог любого приложения (включая Проводник) сохраняет дескриптор каталога . Когда вы звоните Directory.Delete(true)
, он удаляет снизу вверх:, b
затем a
. Если b
он открыт в Explorer, Explorer обнаружит удаление 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 Explorer. Если вам нужна надежная операция удаления, следует помнить, что все (антивирусный сканер и т. Д.) Может иметь открытый дескриптор того, что вы пытаетесь удалить, в любое время. Поэтому вы должны попробовать позже. Сколько позже и сколько раз вы попробуете, зависит от того, насколько важно удалить объект. Как указывает MSDN ,
Надежный файловый итерационный код должен учитывать многие сложности файловой системы.
Это невинное утверждение, снабженное только ссылкой на справочную документацию NTFS, должно заставить ваши волосы встать на ноги.
( Изменить : много. Первоначально у этого ответа было только первое, неполное решение.)