Я использую .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?
Ответы:
Примечание редактора: хотя этот ответ содержит некоторую полезную информацию, он фактически неверен в отношении работы
Directory.Delete
. Пожалуйста, прочитайте комментарии к этому ответу и другие ответы на этот вопрос.Я столкнулся с этой проблемой раньше.
Корень проблемы в том, что эта функция не удаляет файлы, которые находятся в структуре каталогов. Поэтому вам нужно создать функцию, которая удаляет все файлы в структуре каталогов, а затем все каталоги, прежде чем удалять сам каталог. Я знаю, что это идет против второго параметра, но это гораздо более безопасный подход. Кроме того, вы, вероятно, захотите удалить атрибуты доступа READ-ONLY из файлов непосредственно перед их удалением. В противном случае это вызовет исключение.
Просто вставьте этот код в свой проект.
Кроме того, я лично добавляю ограничение на области машины, которые разрешено удалять, потому что вы хотите, чтобы кто-то вызывал эту функцию
C:\WINDOWS (%WinDir%)
илиC:\
.источник
Directory.Delete(path, true)
не удаляет файлы, неверно. См. MSDN msdn.microsoft.com/en-us/library/fxeahc5f.aspxDirectory.Delete(string,bool)
сбой, что-то блокируется или неправильно пропускается, и не существует единого размера, подходящего для решения этой проблемы. Людям нужно решать эту проблему в своем контексте, и мы не должны сильно растерять каждую идею о проблеме (с повторными попытками и глотанием исключений) и надеяться на хороший результат.Если вы пытаетесь рекурсивно удалить каталог,
a
и каталогa\b
открыт в проводнике,b
он будет удален, но вы получите сообщение об ошибке «каталог не пустой»,a
даже если он пуст, когда вы идете и смотрите. Текущий каталог любого приложения (включая Проводник) сохраняет дескриптор каталога . Когда вы звонитеDirectory.Delete(true)
, он удаляет снизу вверх:,b
затемa
. Еслиb
он открыт в Explorer, Explorer обнаружит удалениеb
, поменяет каталог вверхcd ..
и очистит открытые дескрипторы. Поскольку файловая система работает асинхронно,Directory.Delete
операция завершается ошибкой из-за конфликтов с проводником.Неполное решение
Первоначально я опубликовал следующее решение с идеей прерывания текущего потока, чтобы у проводника было время освободить дескриптор каталога.
Но это работает, только если открытый каталог является непосредственным дочерним элементом удаляемого каталога. Если
a\b\c\d
он открыт в Проводнике, и вы используете егоa
, этот метод завершится ошибкой после удаленияd
иc
.Несколько лучшее решение
Этот метод будет обрабатывать удаление глубокой структуры каталогов, даже если в Проводнике открыт один из каталогов нижнего уровня.
Несмотря на дополнительную работу по возврату данных самостоятельно, мы все еще должны справиться с тем,
UnauthorizedAccessException
что может произойти на этом пути. Неясно, прокладывает ли первая попытка удаления вторую, удачную, или это просто временная задержка, вызванная выбросом / перехватом исключения, которое позволяет файловой системе наверстать упущенное.Возможно, вы сможете уменьшить количество исключений, генерируемых и перехваченных в типичных условиях, добавляя
Thread.Sleep(0)
в началеtry
блока. Кроме того, существует риск того, что при большой загрузке системы вы можете выполнить обеDirectory.Delete
попытки и потерпеть неудачу. Считайте это решение отправной точкой для более надежного рекурсивного удаления.Общий ответ
Это решение касается только особенностей взаимодействия с Windows Explorer. Если вам нужна надежная операция удаления, следует помнить, что все (антивирусный сканер и т. Д.) Может иметь открытый дескриптор того, что вы пытаетесь удалить, в любое время. Поэтому вы должны попробовать позже. Сколько позже и сколько раз вы попробуете, зависит от того, насколько важно удалить объект. Как указывает MSDN ,
Это невинное утверждение, снабженное только ссылкой на справочную документацию NTFS, должно заставить ваши волосы встать на ноги.
( Изменить : много. Первоначально у этого ответа было только первое, неполное решение.)
источник
catch
блок (тем временем, Explorer все еще освобождает каталог, поскольку не было отправлено никакой команды, чтобы сказать ему не делать этого). ВызовThread.Sleep(0)
может быть или не быть необходимым, так какcatch
блок уже дал системе немного больше времени, но он обеспечивает некоторую дополнительную безопасность при низкой стоимости. После этогоDelete
вызывается с уже выпущенным каталогом.Прежде чем идти дальше, проверьте следующие причины, которые находятся под вашим контролем:
В противном случае проверьте следующие законные причины вне вашего контроля:
Если что-то из перечисленного является проблемой, вы должны понять, почему это происходит, прежде чем пытаться улучшить свой код удаления. Должно ли ваше приложение удалять файлы только для чтения или недоступные файлы? Кто так их пометил и почему?
Как только вы исключили вышеуказанные причины, все еще существует вероятность ложных сбоев. Удаление не удастся, если кто-то держит дескриптор любого из удаляемых файлов или папок, и существует множество причин, по которым кто-то может перечислять папку или читать ее файлы:
Общий подход к устранению ложных сбоев состоит в том, чтобы делать попытки несколько раз, делая паузу между попытками. Вы, очевидно, не хотите продолжать попытки вечно, поэтому вам следует отказаться после определенного количества попыток и либо выбросить исключение, либо проигнорировать ошибку. Нравится:
На мой взгляд, такой помощник должен использоваться для всех удалений, потому что ложные сбои всегда возможны. Однако ВЫ ДОЛЖНЫ ПРИНЯТЬ ЭТОТ КОД К ВАШЕМУ ИСПОЛЬЗОВАНИЮ, а не просто слепо копировать его.
У меня были ложные сбои для внутренней папки данных, созданной моим приложением, расположенной в папке% LocalAppData%, поэтому мой анализ выглядит следующим образом:
Папка управляется исключительно моим приложением, и у пользователя нет веской причины для того, чтобы отмечать вещи как доступные только для чтения или недоступные в этой папке, поэтому я не пытаюсь разобраться с этим случаем.
Там нет ценного пользовательского материала, поэтому нет риска принудительного удаления чего-либо по ошибке.
Будучи внутренней папкой данных, я не ожидаю, что она будет открыта в проводнике, по крайней мере, я не чувствую необходимости конкретно обрабатывать дело (т.е. я прекрасно справляюсь с этим делом через службу поддержки).
Если все попытки не удаются, я предпочитаю игнорировать ошибку. В худшем случае приложение не может распаковать некоторые новые ресурсы, вылетает и предлагает пользователю обратиться в службу поддержки, что является приемлемым для меня, если это происходит не часто. Или, если приложение не аварийно завершает работу, оно оставляет некоторые старые данные, что опять-таки приемлемо для меня.
Я решил ограничить количество попыток до 500 мс (50 * 10). Это произвольный порог, который работает на практике; Я хотел, чтобы порог был достаточно коротким, чтобы пользователи не убивали приложение, думая, что оно перестало отвечать. С другой стороны, полсекунды - это достаточно времени, чтобы нарушитель мог закончить обработку моей папки. Судя по другим ответам SO, которые иногда даже
Sleep(0)
приемлемы, очень немногие пользователи когда-либо будут испытывать больше, чем одна повторная попытка.Я повторяю каждые 50 мс, что является другим произвольным числом. Я чувствую, что если файл обрабатывается (индексируется, проверяется), когда я пытаюсь удалить его, 50 мс - это подходящее время, чтобы ожидать завершения обработки в моем случае. Кроме того, 50 мс достаточно мал, чтобы не привести к заметному замедлению; опять же,
Sleep(0)
кажется, этого достаточно во многих случаях, поэтому мы не хотим слишком долго откладывать.Код повторяется при любых исключениях ввода-вывода. Обычно я не ожидаю каких-либо исключений при доступе к% LocalAppData%, поэтому я выбрал простоту и принял риск задержки в 500 мс в случае легитимного исключения. Я также не хотел придумывать способ обнаружить точное исключение, к которому я хочу повторить попытку.
источник
Directory.Exists
охранник не решить это.]Современный асинхронный ответ
Принятый ответ просто неверен, он может сработать для некоторых людей, потому что время, необходимое для получения файлов с диска, освобождает все, что блокировало файлы. Дело в том, что это происходит потому, что файлы блокируются каким-либо другим процессом / потоком / действием. Другие ответы используют
Thread.Sleep
(Yuck), чтобы через некоторое время повторить попытку удаления каталога. Этот вопрос нуждается в повторении с более современным ответом.Модульные тесты
Эти тесты показывают пример того, как заблокированный файл может привести
Directory.Delete
к сбою, иTryDeleteDirectory
способ, описанный выше, устраняет проблему.источник
Thread.Sleep
которые вы должны избегать сегодня, и вместо этого используйтеasync
/await
сTask.Delay
. Это понятно, это очень старый вопрос.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);
чтобы ждать, пока каталог действительно исчезнетОдна важная вещь, о которой следует упомянуть (я добавил ее в качестве комментария, но мне не разрешено), это то, что поведение перегрузки изменилось с .NET 3.5 на .NET 4.0.
Начиная с .NET 4.0 он удаляет файлы в самой папке, но НЕ в 3.5. Это можно увидеть и в документации MSDN.
.NET 4.0
.NET 3.5
источник
У меня была та же проблема под Delphi. И в результате мое приложение блокировало каталог, который я хотел удалить. Каким-то образом каталог был заблокирован, когда я писал в него (некоторые временные файлы).
Уловка 22 состояла в том, что я сделал простую замену каталога на его родительский, прежде чем удалить его.
источник
Я удивлен, что никто не думал об этом простом нерекурсивном методе, который может удалять каталоги, содержащие файлы только для чтения, без необходимости изменять атрибут «только чтение» каждого из них.
(Измените немного, чтобы не запускать окно cmd на мгновение, которое доступно по всему интернету)
источник
Вы можете воспроизвести ошибку, запустив:
При попытке удалить каталог 'b' выдает IOException "Каталог не пустой". Это глупо, так как мы только что удалили каталог 'c'.
Насколько я понимаю, объяснение состоит в том, что каталог «c» помечается как удаленный. Но удаление еще не зафиксировано в системе. Система ответила, что работа выполнена, хотя на самом деле она все еще обрабатывается. Система, вероятно, ожидает, что файловый менеджер сосредоточится на родительском каталоге, чтобы зафиксировать удаление.
Если вы посмотрите на исходный код функции Delete ( http://referencesource.microsoft.com/#mscorlib/system/io/directory.cs ), то увидите, что она использует встроенную функцию Win32Native.RemoveDirectory. Это поведение "не ждать" отмечено здесь:
( http://msdn.microsoft.com/en-us/library/windows/desktop/aa365488(v=vs.85).aspx )
Сон и повторите это решение. См. Решение Ряскля.
источник
У меня были странные проблемы с разрешением на удаление каталогов профилей пользователей (в C: \ Documents and Settings), несмотря на то, что я мог это делать в оболочке Explorer.
Мне не имеет смысла, что «файловая» операция делает с каталогом, но я знаю, что она работает, и этого мне достаточно!
источник
Этот ответ основан на: https://stackoverflow.com/a/1703799/184528 . Разница с моим кодом заключается в том, что мы возвращаем много удаленных подкаталогов и файлов только при необходимости, вызов Directory.Delete завершается неудачно с первой попытки (что может произойти из-за того, что проводник Windows просматривает каталог).
источник
UnauthorizedAccessException
? Это было бы просто бросить, снова. И снова. И снова ... Потому что каждый раз он собирается идтиcatch
и снова вызывать функцию. АThread.Sleep(0);
не меняет ваши разрешения. В этот момент он должен просто зарегистрировать ошибку и завершиться неудачей. И этот цикл будет продолжаться до тех пор, пока открыт (под) каталог - он не закрывает его программно. Готовы ли мы просто позволить этому делать это до тех пор, пока эти вещи остаются открытыми? Есть ли способ лучше?UnauthorizedAccessException
он попытается вручную удалить каждый файл. Таким образом, он продолжает добиваться прогресса, обращаясь к структуре каталогов. Да, потенциально каждый файл и каталог будут выдавать одно и то же исключение, но это также может произойти просто потому, что проводник держит дескриптор к нему (см. Stackoverflow.com/a/1703799/184528 ). Я изменю «tryAgain» на «secondTry». чтобы было понятнее.Process.Kill()
для любого процесса, которым файл может быть заблокирован, и удалить файлы. Проблема, с которой я сталкиваюсь, заключается в удалении каталога, в котором один из этих файлов все еще был открыт (см. Stackoverflow.com/questions/41841590/… ). Итак, возвращаясь к этому циклу, независимо от того, что еще он делает, если он делаетDirectory.Delete()
это снова в этой папке, он все равно не будет выполнен, если этот дескриптор не может быть освобожден.UnauthorizedAccessException
поскольку удаление файлов (при условии, что это даже разрешено, потому что получить этот код не удалосьDirectory.Delete()
) волшебным образом не дает вам разрешения на удаление каталога.Ни одно из перечисленных выше решений не помогло мне. В итоге я использовал отредактированную версию решения @ryascl, как показано ниже:
источник
Возможно ли, что у вас есть состояние гонки, когда другой поток или процесс добавляет файлы в каталог:
Последовательность будет:
Процесс удаления А:
Если кто-то еще добавит файл между 1 и 2, тогда, возможно, 2 выдаст исключение из списка?
источник
Я потратил несколько часов, чтобы решить эту проблему и другие исключения с удалением каталога. Это мое решение
Этот код имеет небольшую задержку, которая не важна для моего приложения. Но будьте осторожны, задержка может быть проблемой для вас, если у вас есть много подкаталогов в каталоге, который вы хотите удалить.
источник
Рекурсивное удаление каталогов, которое не удаляет файлы, безусловно, неожиданно. Мое исправление для этого:
Я сталкивался с случаями, когда это помогало, но, как правило, Directory.Delete удаляет файлы внутри каталогов после рекурсивного удаления, как описано в msdn .
Время от времени я сталкиваюсь с этим нерегулярным поведением также как пользователь Windows Explorer: иногда я не могу удалить папку (она думает, что бессмысленное сообщение «отказано в доступе»), но когда я развертываю и удаляю нижние элементы, я могу удалить верхние предметы также. Поэтому я думаю, что приведенный выше код имеет дело с аномалией ОС, а не с проблемой библиотеки базовых классов.
источник
Каталог или файл в нем заблокированы и не могут быть удалены. Найдите виновника, который его запирает, и посмотрите, сможете ли вы его устранить.
источник
Похоже, что выбор пути или подпапки в проводнике Windows достаточен, чтобы заблокировать одиночное выполнение Directory.Delete (path, true), выбрасывая IOException, как описано выше, и умирая вместо загрузки Windows Explorer из родительской папки и действуя как ожидается.
источник
У меня была эта проблема сегодня. Это происходило потому, что у меня был проводник Windows, открытый для каталога, который пытался удалить, вызывая рекурсивный вызов сбой и, следовательно, IOException. Убедитесь, что в каталоге нет ручек.
Кроме того, в MSDN ясно, что вам не нужно писать собственный отзыв: http://msdn.microsoft.com/en-us/library/fxeahc5f.aspx
источник
У меня была такая же проблема с Windows Workflow Foundation на сервере сборки с TFS2012. Внутренне рабочий процесс называется Directory.Delete () с рекурсивным флагом, установленным в true. В нашем случае это связано с сетью.
Мы удаляли двоичную папку для отбрасывания на общем сетевом ресурсе, а затем заново создавали и заново заполняли ее последними двоичными файлами. Любая другая сборка потерпит неудачу. При открытии папки удаления после неудачной сборки папка была пустой, что указывает на то, что все аспекты вызова Directory.Delete () были успешными, за исключением удаления действительного каталога.
Похоже, что проблема вызвана асинхронным характером обмена файлами по сети. Сервер сборки приказал файловому серверу удалить все файлы, а файловый сервер сообщил, что это произошло, хотя он еще не был полностью завершен. Затем сервер сборки запросил удаление каталога, а файловый сервер отклонил запрос, потому что он не завершил удаление файлов.
Два возможных решения в нашем случае:
Последний метод быстрый и грязный, но, кажется, делает свое дело.
источник
Это из-за FileChangesNotifications.
Это происходит начиная с ASP.NET 2.0. Когда вы удаляете какую-либо папку в приложении, она перезапускается . Вы можете увидеть это сами, используя ASP.NET Health Monitoring .
Просто добавьте этот код в ваш web.config / configuration / system.web:
После этого проверьте
Windows Log -> Application
. Что здесь происходит:При удалении папки, если есть
Delete(path, true)
подпапка, сначала удаляется подпапка. FileChangesMonitor достаточно знать об удалении и закрыть ваше приложение. Между тем ваш основной каталог еще не удален. Это событие из журнала:Delete()
не закончил свою работу, и, поскольку приложение закрывается, возникает исключение:Если у вас нет подпапок в папке, которую вы удаляете, Delete () просто удаляет все файлы и эту папку, приложение тоже перезапускается, но вы не получаете никаких исключений , потому что перезапуск приложения ничего не прерывает. Но все равно вы теряете все внутрипроцессные сеансы, приложение не отвечает на запросы при перезапуске и т. Д.
Что теперь?
Существуют некоторые обходные пути и твики для отключения этого поведения, « Соединение каталогов» , « Отключение FCN с реестром» , « Остановка FileChangesMonitor с помощью Reflection» (поскольку нет открытого метода) , но все они, похоже, не правы, поскольку FCN существует для причина. Он следит за структурой вашего приложения , которая не является структурой ваших данных . Краткий ответ: разместите папки, которые вы хотите удалить за пределами вашего приложения. FileChangesMonitor не будет получать уведомлений, и ваше приложение не будет перезапускаться каждый раз. Вы не получите никаких исключений. Чтобы сделать их видимыми из Интернета, есть два способа:
Создайте контроллер, который обрабатывает входящие вызовы, а затем отправляет файлы обратно, читая из папки вне приложения (вне wwwroot).
Если ваш проект большой и производительность важнее всего, создайте отдельный маленький и быстрый веб-сервер для обслуживания статического контента. Таким образом вы оставите IIS свою конкретную работу. Это может быть на той же машине (mongoose для Windows) или на другой машине (nginx для Linux). Хорошей новостью является то, что вам не нужно платить дополнительную лицензию Microsoft для установки статического сервера контента в Linux.
Надеюсь это поможет.
источник
Как упомянуто выше, «принятое» решение терпит неудачу в точках повторного анализа - но люди все еще отмечают его (???) Существует гораздо более короткое решение, которое правильно копирует функциональность:
Я знаю, что не обрабатывает случаи разрешений, упомянутые позже, но для всех целей и задач FAR BETTER обеспечивает ожидаемую функциональность оригинального / стокового Directory.Delete () - и с гораздо меньшим количеством кода тоже .
Вы можете безопасно продолжить обработку, потому что старый каталог будет в стороне … даже если он не исчезнет, потому что «файловая система все еще догоняет» (или любое другое оправдание, которое MS дал для предоставления сломанной функции) .
В качестве преимущества, если вы знаете, что целевой каталог большой / глубокий и не хотите ждать (или беспокоиться об исключениях), последнюю строку можно заменить на:
Вы все еще можете продолжать работать.
источник
\\server\C$\dir
и сделал это\\server\C$asf.yuw
. В результате я получил сообщение об ошибкеDirectory.Move()
-Source and destination path must have identical roots. Move will not work across volumes.
Сработало нормально, когда я использовал код Пита, КРОМЕ которого не обрабатывает ни заблокированные файлы, ни открытые каталоги, поэтому он никогда не попадает вThreadPool
команду.Эта проблема может возникать в Windows, когда в каталоге (или в любом подкаталоге) есть файлы, длина пути которых превышает 260 символов.
В таких случаях вам нужно удалить
\\\\?\C:\mydir
вместоC:\mydir
. О пределе в 260 символов вы можете прочитать здесь .источник
Я решил с этой техникой тысячелетия (вы можете оставить Thread.Sleep самостоятельно в улове)
источник
Если текущий каталог вашего приложения (или любого другого приложения) является тем, который вы пытаетесь удалить, это не будет ошибкой нарушения прав доступа, но каталог не будет пустым. Убедитесь, что это не ваше собственное приложение, изменив текущий каталог; также убедитесь, что каталог не открыт в какой-либо другой программе (например, Word, Excel, Total Commander и т. д.). Большинство программ перейдут в каталог последнего открытого файла, что может привести к этому.
источник
в случае сетевых файлов Directory.DeleteHelper (recursive: = true) может вызвать IOException, что вызвано задержкой удаления файла
источник
Я думаю, что есть файл, открытый каким-то потоком, о котором вы не знаете, у меня была та же проблема, и я решил ее, закрыв все потоки, которые указывали на каталог, который я хотел удалить.
источник
Эта ошибка возникает, если какой-либо файл или каталог считается используемым. Это вводящая в заблуждение ошибка. Проверьте, открыты ли у вас какие-либо окна проводника или окна командной строки для какого-либо каталога в дереве или для программы, которая использует файл в этом дереве.
источник
Я решил один возможный случай указанной проблемы, когда методы были асинхронными и закодированы следующим образом:
С этим:
Вывод? Существует некоторый асинхронный аспект избавления от дескриптора, используемого для проверки существования, с которым Microsoft не смогла поговорить. Это как если бы асинхронный метод внутри оператора if имел оператор if, действующий как оператор using.
источник
Вам не нужно создавать дополнительный метод для рекурсивности или удалять файлы внутри папки extra. Все это происходит автоматически по телефону
Подробности здесь .
Нечто подобное работает довольно хорошо:
Передав true в качестве переменной для удаления метода, удаляются также вложенные файлы и вложенные папки с файлами.
источник
Ни один из приведенных выше ответов не работал для меня. Похоже, что использование моего собственного приложения
DirectoryInfo
в целевом каталоге приводило к тому, что оно оставалось заблокированным.Принудительный сборщик мусора решил проблему, но не сразу. Несколько попыток удалить при необходимости.
Обратите внимание,
Directory.Exists
как оно может исчезнуть после исключения. Я не знаю, почему удаление для меня было отложено (Windows 7 SP1)источник
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); } } }
добавить истину во втором парам.
Это удалит все.
источник