Estou usando o .NET 3.5, tentando excluir recursivamente um diretório usando:
Directory.Delete(myPath, true);
Meu entendimento é que isso deve ocorrer se os arquivos estiverem em uso ou houver um problema de permissão, mas, caso contrário, ele deverá excluir o diretório e todo o seu conteúdo.
No entanto, ocasionalmente entendo isso:
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)
...
Não estou surpreso que o método às vezes seja lançado, mas fico surpreso ao receber essa mensagem específica quando a recursiva é verdadeira. (Eu sei que o diretório não está vazio.)
Existe um motivo para ver isso em vez de AccessViolationException?
Respostas:
Nota do editor: Embora esta resposta contenha algumas informações úteis, ela é factualmente incorreta sobre o funcionamento de
Directory.Delete
. Por favor, leia os comentários para esta resposta e outras respostas para esta pergunta.Eu já tive esse problema antes.
A raiz do problema é que essa função não exclui arquivos que estão dentro da estrutura de diretórios. Portanto, o que você precisa fazer é criar uma função que exclua todos os arquivos da estrutura de diretórios e todos os diretórios antes de remover o próprio diretório. Eu sei que isso vai contra o segundo parâmetro, mas é uma abordagem muito mais segura. Além disso, você provavelmente desejará remover os atributos de acesso somente leitura dos arquivos antes de excluí-los. Caso contrário, isso gerará uma exceção.
Basta inserir esse código no seu projeto.
Além disso, para mim, eu adiciono pessoalmente uma restrição em áreas da máquina que podem ser excluídas porque você deseja que alguém chame essa função
C:\WINDOWS (%WinDir%)
ouC:\
.fonte
Directory.Delete(path, true)
não exclui arquivos está errada. Veja MSDN msdn.microsoft.com/en-us/library/fxeahc5f.aspxDirectory.Delete(string,bool)
falhar, algo está bloqueado ou não autorizado e não existe um tamanho único para todas as soluções para esse problema. As pessoas precisam abordar essa questão em seu contexto, e nós não devemos crescer muito peludo - lance todas as idéias para o problema (com tentativas e engolir exceções) e esperamos um bom resultado.Se você estiver tentando excluir o diretório recursivamente
a
e o diretórioa\b
estiver aberto no Explorer,b
ele será excluído, mas o erro 'diretório não está vazio' será exibido,a
mesmo que esteja vazio quando você olhar. O diretório atual de qualquer aplicativo (incluindo o Explorer) mantém um identificador no diretório . Quando você ligaDirectory.Delete(true)
, ele exclui de baixo para cima:,b
entãoa
. Seb
estiver aberto no Explorer, o Explorer detectará a exclusãob
, alterará o diretório para cimacd ..
e limpará os identificadores abertos. Como o sistema de arquivos opera de forma assíncrona, aDirectory.Delete
operação falha devido a conflitos com o Explorer.Solução incompleta
Originalmente, publiquei a seguinte solução, com a idéia de interromper o segmento atual para permitir que o tempo do Explorer libere o identificador de diretório.
Mas isso só funciona se o diretório aberto for o filho imediato do diretório que você está excluindo. Se
a\b\c\d
estiver aberto no Explorer e você o usara
, essa técnica falhará após excluird
ec
.Uma solução um pouco melhor
Esse método manipulará a exclusão de uma estrutura de diretórios profunda, mesmo que um dos diretórios de nível inferior esteja aberto no Explorer.
Apesar do trabalho extra de repetir por conta própria, ainda temos que lidar com o
UnauthorizedAccessException
que pode ocorrer ao longo do caminho. Não está claro se a primeira tentativa de exclusão está abrindo caminho para a segunda, bem-sucedida, ou se é apenas o atraso de tempo introduzido ao lançar / capturar uma exceção que permite ao sistema de arquivos recuperar o atraso.Você pode reduzir o número de exceções lançadas e capturadas em condições típicas adicionando uma
Thread.Sleep(0)
no início dotry
bloco. Além disso, existe o risco de que, sob carga pesada do sistema, você possa passar pelasDirectory.Delete
tentativas e falhar. Considere esta solução como um ponto de partida para uma exclusão recursiva mais robusta.Resposta geral
Esta solução aborda apenas as peculiaridades da interação com o Windows Explorer. Se você deseja uma operação de exclusão sólida, lembre-se de que qualquer coisa (antivírus, qualquer que seja) pode ter um identificador aberto para o que você está tentando excluir, a qualquer momento. Então você tem que tentar novamente mais tarde. Quanto mais tarde e quantas vezes você tenta depende de quão importante é que o objeto seja excluído. Como o MSDN indica ,
Essa declaração inocente, fornecida apenas com um link para a documentação de referência do NTFS, deve fazer você se arrepiar.
( Edit : Muita. Esta resposta originalmente tinha apenas a primeira solução incompleta.)
fonte
catch
bloco será executado (enquanto isso, o Explorer ainda está liberando o diretório, pois nenhum comando foi enviado para solicitar que ele não o faça). A chamada paraThread.Sleep(0)
pode ou não ser necessária, pois ocatch
bloco já deu ao sistema um pouco mais de tempo, mas fornece um pouco de segurança extra por um baixo custo. Depois disso,Delete
é chamado, com o diretório já liberado.Antes de prosseguir, verifique os seguintes motivos que estão sob seu controle:
Caso contrário, verifique os seguintes motivos legítimos fora do seu controle:
Se algum dos itens acima for o problema, você deve entender por que isso acontece antes de tentar melhorar seu código de exclusão. Caso a sua aplicação ser a exclusão de arquivos somente leitura ou inacessíveis? Quem os marcou dessa maneira, e por quê?
Depois de descartar as razões acima, ainda existe a possibilidade de falhas espúrias. A exclusão falhará se alguém manipular um dos arquivos ou pastas que estão sendo excluídos, e há muitos motivos pelos quais alguém pode estar enumerando a pasta ou lendo seus arquivos:
A abordagem geral para lidar com falhas espúrias é tentar várias vezes, fazendo uma pausa entre as tentativas. Obviamente, você não quer continuar tentando para sempre, portanto, desista após um certo número de tentativas e lance uma exceção ou ignore o erro. Como isso:
Na minha opinião, um ajudante como esse deve ser usado para todas as exclusões, porque falhas espúrias são sempre possíveis. No entanto, VOCÊ DEVE ADAPTAR ESTE CÓDIGO AO SEU CASO DE USO, e não apenas copiá-lo cegamente.
Eu tive falhas espúrias em uma pasta de dados interna gerada pelo meu aplicativo, localizada em% LocalAppData%, portanto, minha análise é assim:
A pasta é controlada apenas pelo meu aplicativo e o usuário não tem motivos válidos para marcar as coisas como somente leitura ou inacessíveis dentro dessa pasta, portanto, não tento lidar com esse caso.
Não há material valioso criado pelo usuário, por isso não há risco de excluir algo com força por engano.
Sendo uma pasta de dados interna, não espero que seja aberta no explorer, pelo menos não sinto a necessidade de lidar especificamente com o caso (ou seja, estou lidando bem com o caso através do suporte).
Se todas as tentativas falharem, eu escolho ignorar o erro. Na pior das hipóteses, o aplicativo falha ao descompactar alguns recursos mais recentes, trava e solicita que o usuário entre em contato com o suporte, o que é aceitável para mim desde que isso não ocorra com frequência. Ou, se o aplicativo não travar, ele deixará alguns dados antigos para trás, o que novamente é aceitável para mim.
Eu escolho limitar as tentativas a 500ms (50 * 10). Este é um limite arbitrário que funciona na prática; Eu queria que o limite fosse curto o suficiente para que os usuários não matassem o aplicativo, pensando que ele parou de responder. Por outro lado, meio segundo é bastante tempo para o agressor terminar de processar minha pasta. A julgar por outras respostas do SO que às vezes consideram
Sleep(0)
aceitáveis, muito poucos usuários experimentam mais do que uma única tentativa.Repito a cada 50ms, que é outro número arbitrário. Sinto que, se um arquivo está sendo processado (indexado, verificado) quando tento excluí-lo, 50ms é o momento certo para esperar que o processamento seja concluído no meu caso. Além disso, 50ms é pequeno o suficiente para não resultar em uma desaceleração perceptível; novamente,
Sleep(0)
parece ser suficiente em muitos casos, por isso não queremos atrasar demais.O código tenta novamente todas as exceções de E / S. Normalmente, não espero exceções acessando% LocalAppData%, por isso escolhi a simplicidade e aceitei o risco de um atraso de 500ms no caso de uma exceção legítima. Também não queria descobrir uma maneira de detectar a exceção exata em que desejo tentar novamente.
fonte
Directory.Exists
guarda niave não resolver essa].Resposta assíncrona moderna
A resposta aceita está simplesmente errada, pode funcionar para algumas pessoas, porque o tempo necessário para obter arquivos do disco libera o que estava bloqueando os arquivos. O fato é que isso acontece porque os arquivos são bloqueados por algum outro processo / fluxo / ação. As outras respostas usam
Thread.Sleep
(Yuck) para tentar excluir o diretório novamente após algum tempo. Esta questão precisa ser revisada com uma resposta mais moderna.Testes unitários
Esses testes mostram um exemplo de como um arquivo bloqueado pode causar
Directory.Delete
falhas e como oTryDeleteDirectory
método acima corrige o problema.fonte
Thread.Sleep
que você deve evitar hoje e usaasync
/await
withTask.Delay
. Isso é compreensível, essa é uma pergunta muito antiga.BC36943 'Await' cannot be used inside a 'Catch' statement, a 'Finally' statement, or a 'SyncLock' statement.
if (!Directory.Exists(directoryPath)) { return true; } await Task.Delay(millisecondsDelay);
para esperar até que o diretório realmente desapareçaUma coisa importante que deve ser mencionada (eu a adicionei como comentário, mas não tenho permissão) é que o comportamento da sobrecarga mudou do .NET 3.5 para o .NET 4.0.
A partir do .NET 4.0, ele exclui arquivos da própria pasta, mas NÃO da 3.5. Isso também pode ser visto na documentação do MSDN.
.NET 4.0
.NET 3.5
fonte
Eu tive o mesmo problema no Delphi. E o resultado final foi que meu próprio aplicativo estava bloqueando o diretório que eu queria excluir. De alguma forma, o diretório ficou bloqueado quando eu estava escrevendo nele (alguns arquivos temporários).
A captura 22 foi, eu fiz um diretório de mudança simples para seu pai antes de excluí-lo.
fonte
Estou surpreso que ninguém tenha pensado nesse método simples e não recursivo, que pode excluir diretórios contendo arquivos somente leitura, sem precisar alterar o atributo somente leitura de cada um deles.
(Altere um pouco para não disparar uma janela cmd momentaneamente, disponível em toda a Internet)
fonte
Você pode reproduzir o erro executando:
Ao tentar excluir o diretório 'b', ele lança a IOException "O diretório não está vazio". Isso é estúpido, pois acabamos de excluir o diretório 'c'.
Pelo meu entendimento, a explicação é que o diretório 'c' é carimbado como excluído. Mas a exclusão ainda não foi confirmada no sistema. O sistema respondeu que o trabalho foi concluído e, na verdade, ainda está em processamento. O sistema provavelmente espera que o gerenciador de arquivos se concentre no diretório pai para confirmar a exclusão.
Se você procurar o código fonte da função Excluir ( http://referencesource.microsoft.com/#mscorlib/system/io/directory.cs ), verá que ele usa a função nativa Win32Native.RemoveDirectory. Esse comportamento de não esperar é observado aqui:
( http://msdn.microsoft.com/en-us/library/windows/desktop/aa365488(v=vs.85).aspx )
Dormir e tentar novamente é a solução. Cf a solução do ryascl.
fonte
Eu tive um problema estranho de permissão ao excluir diretórios de perfil de usuário (em C: \ Documents and Settings), apesar de poder fazê-lo no shell do Explorer.
Não faz sentido para mim o que uma operação de "arquivo" faz em um diretório, mas sei que funciona e é o suficiente para mim!
fonte
Esta resposta é baseada em: https://stackoverflow.com/a/1703799/184528 . A diferença com o meu código é que apenas recursamos muitos subdiretórios e arquivos de exclusão quando necessário, uma chamada para o Directory.Delete falha em uma primeira tentativa (o que pode acontecer por causa do Windows Explorer procurar um diretório).
fonte
UnauthorizedAccessException
? Seria apenas jogar de novo. E de novo. E novamente ... Porque cada vez que você acessacatch
e chama a função novamente. AThread.Sleep(0);
não altera suas permissões. Ele deve apenas registrar o erro e falhar normalmente, nesse ponto. E esse loop continuará enquanto o diretório (sub-) estiver aberto - ele não será fechado programaticamente. Estamos preparados para deixá-lo fazer isso enquanto essas coisas forem deixadas em aberto? Existe uma maneira melhor?UnauthorizedAccessException
ele tentará excluir manualmente cada arquivo. Portanto, ele continua progredindo atravessando a estrutura de diretórios. Sim, potencialmente todos os arquivos e diretórios lançam a mesma exceção, mas isso também pode ocorrer simplesmente porque o explorer está segurando um identificador (consulte stackoverflow.com/a/1703799/184528 ) Alterarei o "tryAgain" para "secondTry" para deixar mais claro.Process.Kill()
em qualquer processo pelo qual um arquivo pode ser bloqueado e excluí-lo. O problema que encontro é ao excluir um diretório em que um desses arquivos ainda estava aberto (consulte stackoverflow.com/questions/41841590/… ). Então, voltando a esse loop, não importa o que mais ele esteja fazendo, se fizerDirectory.Delete()
novamente nessa pasta, ainda falhará se esse identificador não puder ser liberado.UnauthorizedAccessException
desde que a exclusão de arquivos (supondo que isso fosse permitido, porque, para chegar a esse código, falhouDirectory.Delete()
), magicamente não lhe dá permissão para excluir o diretório.Não das soluções acima funcionou bem para mim. Acabei usando uma versão editada da solução @ryascl como abaixo:
fonte
É possível que você tenha uma condição de corrida em que outro encadeamento ou processo esteja adicionando arquivos ao diretório:
A sequência seria:
Deleter o processo A:
Se outra pessoa adicionar um arquivo entre 1 e 2, talvez 2 jogue a exceção listada?
fonte
Passei algumas horas para resolver esse problema e outras exceções com a exclusão do diretório. Esta é a minha solução
Este código tem um pequeno atraso, o que não é importante para o meu aplicativo. Mas tenha cuidado, o atraso pode ser um problema para você se você tiver muitos subdiretórios dentro do diretório que deseja excluir.
fonte
A exclusão recursiva de diretório que não exclui arquivos é certamente inesperada. Minha correção para isso:
Eu experimentei casos em que isso ajudava, mas geralmente, o Directory.Delete exclui arquivos dentro de diretórios após a exclusão recursiva, conforme documentado no msdn .
De tempos em tempos, também encontro esse comportamento irregular como usuário do Windows Explorer: Às vezes, não consigo excluir uma pasta (ela acha que a mensagem sem sentido é "acesso negado"), mas quando faço uma busca detalhada e excluo itens inferiores, posso excluir a parte superior. itens também. Então, acho que o código acima lida com uma anomalia do sistema operacional - não com um problema da biblioteca de classes base.
fonte
O diretório ou um arquivo nele está bloqueado e não pode ser excluído. Encontre o culpado que o bloqueia e veja se você pode eliminá-lo.
fonte
Parece que ter o caminho ou subpasta selecionado no Windows Explorer é suficiente para bloquear uma única execução do Directory.Delete (caminho, true), lançando uma IOException conforme descrito acima e morrendo em vez de inicializar o Windows Explorer em uma pasta pai e proceder como esperado.
fonte
Eu tive esse problema hoje. Isso estava acontecendo porque eu tinha o Windows Explorer aberto no diretório que estava tentando ser excluído, causando a chamada recursiva de falha e, portanto, a IOException. Verifique se não há identificadores abertos para o diretório.
Além disso, o MSDN está claro que você não precisa escrever sua própria recusão: http://msdn.microsoft.com/en-us/library/fxeahc5f.aspx
fonte
Eu tive esse mesmo problema com o Windows Workflow Foundation em um servidor de compilação com o TFS2012. Internamente, o fluxo de trabalho chamado Directory.Delete () com o sinalizador recursivo definido como true. Parece estar relacionado à rede no nosso caso.
Estávamos excluindo uma pasta suspensa binária em um compartilhamento de rede antes de recriá-la e preenchê-la novamente com os binários mais recentes. Qualquer outra construção falharia. Ao abrir a pasta suspensa após uma falha na compilação, a pasta estava vazia, o que indica que todos os aspectos da chamada Directory.Delete () foram bem-sucedidos, exceto a exclusão do diretório real.
O problema parece ser causado pela natureza assíncrona das comunicações de arquivos de rede. O servidor de compilação disse ao servidor de arquivos para excluir todos os arquivos e o servidor de arquivos informou que tinha, mesmo que não estivesse completamente concluído. Em seguida, o servidor de compilação solicitou que o diretório fosse excluído e o servidor de arquivos rejeitou a solicitação porque ainda não havia concluído a exclusão dos arquivos.
Duas soluções possíveis no nosso caso:
O último método é rápido e sujo, mas parece fazer o truque.
fonte
Isso ocorre por causa de FileChangesNotifications.
Isso acontece desde o ASP.NET 2.0. Quando você exclui alguma pasta em um aplicativo, ela é reiniciada . Você pode vê-lo usando o ASP.NET Health Monitoring .
Basta adicionar este código ao seu web.config / configuration / system.web:
Depois disso, confira
Windows Log -> Application
. O que está acontecendo:Quando você exclui uma pasta, se houver alguma subpasta,
Delete(path, true)
exclui a subpasta primeiro. É suficiente para o FileChangesMonitor saber sobre remoção e desligar o aplicativo. Enquanto isso, seu diretório principal ainda não foi excluído. Este é o evento do Log:Delete()
não terminou seu trabalho e, como o aplicativo está sendo desligado, gera uma exceção:Quando você não possui nenhuma subpasta em uma pasta que está sendo excluída, Delete () exclui todos os arquivos e essa pasta, o aplicativo também é reiniciado, mas você não recebe nenhuma exceção , porque a reinicialização do aplicativo não interrompe nada. Ainda assim, você perde todas as sessões em processo, o aplicativo não responde a solicitações ao reiniciar etc.
E agora?
Existem algumas soluções alternativas e ajustes para desativar esse comportamento, Junção de Diretório , Desativando FCN com Registro , Interrompendo o FileChangesMonitor usando o Reflection (como não há método exposto) , mas nem todos parecem estar corretos, pois a FCN está lá para razão. Ele está cuidando da estrutura do seu aplicativo , que não é a estrutura dos seus dados . A resposta curta é: coloque as pastas que você deseja excluir fora do seu aplicativo. O FileChangesMonitor não receberá notificações e seu aplicativo não será reiniciado todas as vezes. Você não receberá exceções. Para torná-los visíveis na Web, existem duas maneiras:
Crie um controlador que lide com as chamadas recebidas e depois entregue os arquivos lendo da pasta fora de um aplicativo (fora do wwwroot).
Se o seu projeto for grande e o desempenho mais importante, configure um servidor da web pequeno e rápido separado para veicular conteúdo estático. Assim, você deixará para o IIS seu trabalho específico. Pode estar na mesma máquina (mangusto para Windows) ou em outra máquina (nginx para Linux). A boa notícia é que você não precisa pagar uma licença extra da Microsoft para configurar o servidor de conteúdo estático no linux.
Espero que isto ajude.
fonte
Como mencionado acima, a solução "aceita" falha nos pontos de nova análise - mas as pessoas ainda a marcam (???). Há uma solução muito mais curta que replica adequadamente a funcionalidade:
Eu sei, não lida com os casos de permissões mencionados posteriormente, mas para todos os efeitos, o FAR BETTER fornece a funcionalidade esperada do Directory.Delete / stock original / estoque () - e com muito menos código também .
Você pode continuar o processamento com segurança porque o diretório antigo estará fora do caminho ... mesmo que não tenha saído porque o 'sistema de arquivos ainda está atualizando' (ou qualquer outra desculpa que a MS deu para fornecer uma função quebrada) .
Como benefício, se você souber que o diretório de destino é grande / profundo e não deseja esperar (ou se preocupar com exceções), a última linha pode ser substituída por:
Você ainda está seguro para continuar trabalhando.
fonte
\\server\C$\dir
e o fez\\server\C$asf.yuw
. Como resultado, recebi um erro noDirectory.Move()
-Source and destination path must have identical roots. Move will not work across volumes.
Funcionou bem quando usei o código de Pete EXCEPT, nem manipula quando há arquivos bloqueados ou diretórios abertos - para que nunca chegue aoThreadPool
comando.Esse problema pode aparecer no Windows quando houver arquivos em um diretório (ou em qualquer subdiretório) cujo tamanho do caminho seja maior que 260 símbolos.
Nesses casos, você precisa excluir em
\\\\?\C:\mydir
vez deC:\mydir
. Sobre o limite de 260 símbolos, você pode ler aqui .fonte
Eu resolvi com essa técnica milenar (você pode deixar o Thread.Sleep por conta própria na captura)
fonte
Se o diretório atual do seu aplicativo (ou de qualquer outro aplicativo) for o que você está tentando excluir, não será um erro de violação de acesso, mas um diretório não estará vazio. Verifique se não é seu próprio aplicativo alterando o diretório atual; Além disso, verifique se o diretório não está aberto em outro programa (por exemplo, Word, Excel, Total Commander, etc.). A maioria dos programas cdará para o diretório do último arquivo aberto, o que causaria isso.
fonte
no caso de arquivos de rede, Directory.DeleteHelper (recursive: = true) pode causar IOException, causado pelo atraso na exclusão do arquivo
fonte
Eu acho que existe um arquivo aberto por algum fluxo que você não conhece, tive o mesmo problema e o resolvi fechando todos os fluxos que apontavam para o diretório que eu queria excluir.
fonte
Este erro ocorre se qualquer arquivo ou diretório for considerado em uso. É um erro enganoso. Verifique se você possui alguma janela do explorer ou da linha de comando aberta em qualquer diretório da árvore ou em um programa que esteja usando um arquivo nessa árvore.
fonte
Resolvi uma possível instância do problema declarado quando os métodos eram assíncronos e codificados da seguinte maneira:
Com isso:
Conclusão? Há algum aspecto assíncrono de se livrar do identificador usado para verificar a existência com a qual a Microsoft não conseguiu falar. É como se o método assíncrono dentro de uma instrução if tivesse a instrução if agindo como uma instrução using.
fonte
Você não precisa criar um método extra para recursividade ou excluir arquivos dentro da pasta extra. Tudo isso fazendo automaticamente chamando
Detalhes está aqui .
Algo assim funciona muito bem:
passando true como variável para excluir o método, excluirá os sub-arquivos e a sub-pasta com os arquivos também.
fonte
Nenhuma das respostas acima funcionou para mim. Parece que o uso do meu próprio aplicativo
DirectoryInfo
no diretório de destino estava fazendo com que ele permanecesse bloqueado.Forçar a coleta de lixo pareceu resolver o problema, mas não imediatamente. Algumas tentativas para excluir onde necessário.
Observe
Directory.Exists
como ele pode desaparecer após uma exceção. Não sei por que a exclusão foi atrasada (Windows 7 SP1)fonte
GC.Collect
é a) apenas um mau conselho eb) não é uma causa geral suficientemente comum de diretórios bloqueados para merecer inclusão aqui. Basta escolher um dos outros e não semeiam mais confusão na mente dos leitores inocentesusing
declaração: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); } } }
add true no segundo param.
Isso removerá tudo.
fonte