IOException: o processo não pode acessar o arquivo 'caminho do arquivo' porque está sendo usado por outro processo

172

Eu tenho algum código e quando ele executa, lança um IOException, dizendo que

O processo não pode acessar o arquivo 'filename' porque está sendo usado por outro processo

O que isso significa e o que posso fazer sobre isso?

Adriano Repetti
fonte
1
Veja esta pergunta para saber como encontrar o outro processo que está usando o arquivo.
stomy

Respostas:

274

Qual é a causa?

A mensagem de erro é bastante clara: você está tentando acessar um arquivo e não está acessível porque outro processo (ou mesmo o mesmo) está fazendo algo com ele (e não permitiu nenhum compartilhamento).

Depuração

Pode ser bem fácil de resolver (ou muito difícil de entender), dependendo do seu cenário específico. Vamos ver alguns.

Seu processo é o único a acessar esse arquivo.
Você tem certeza de que o outro processo é o seu próprio processo. Se você sabe que abre esse arquivo em outra parte do seu programa, primeiro precisa verificar se fecha corretamente o identificador de arquivo após cada uso. Aqui está um exemplo de código com esse bug:

var stream = new FileStream(path, FileAccess.Read);
var reader = new StreamReader(stream);
// Read data from this file, when I'm done I don't need it any more
File.Delete(path); // IOException: file is in use

Felizmente FileStreamimplementa IDisposable, portanto, é fácil agrupar todo o seu código dentro de uma usinginstrução:

using (var stream = File.Open("myfile.txt", FileMode.Open)) {
    // Use stream
}

// Here stream is not accessible and it has been closed (also if
// an exception is thrown and stack unrolled

Esse padrão também garantirá que o arquivo não seja deixado aberto em caso de exceções (pode ser o motivo pelo qual o arquivo está sendo usado: algo deu errado e ninguém o fechou; veja esta postagem como exemplo).

Se tudo parecer correto (você sempre fecha todos os arquivos abertos, mesmo em exceções) e possui vários threads de trabalho, há duas opções: refazer seu código para serializar o acesso a arquivos (nem sempre é factível e nem sempre desejado) ou aplique um padrão de repetição . É um padrão bastante comum para operações de E / S: você tenta fazer algo e, em caso de erro, espera e tenta novamente (você se perguntou por que, por exemplo, o Windows Shell leva algum tempo para informar que um arquivo está em uso e não pode ser excluído?). Em C #, é muito fácil de implementar (consulte também melhores exemplos sobre E / S de disco , rede e acesso a banco de dados ).

private const int NumberOfRetries = 3;
private const int DelayOnRetry = 1000;

for (int i=1; i <= NumberOfRetries; ++i) {
    try {
        // Do stuff with file
        break; // When done we can break loop
    }
    catch (IOException e) when (i <= NumberOfRetries) {
        // You may check error code to filter some exceptions, not every error
        // can be recovered.
        Thread.Sleep(DelayOnRetry);
    }
}

Observe um erro comum que vemos frequentemente no StackOverflow:

var stream = File.Open(path, FileOpen.Read);
var content = File.ReadAllText(path);

Nesse caso ReadAllText(), falhará porque o arquivo está em uso ( File.Open()na linha anterior). Abrir o arquivo antecipadamente não é apenas desnecessário, mas também errado. O mesmo se aplica a todas as Filefunções que não retornam um identificador para o arquivo que você está trabalhando com: File.ReadAllText(), File.WriteAllText(), File.ReadAllLines(), File.WriteAllLines()e outros (como File.AppendAllXyz()funções) tudo vai abrir e fechar o arquivo por si mesmos.

Seu processo não é o único a acessar esse arquivo
Se o seu processo não é o único a acessar esse arquivo, a interação pode ser mais difícil. Um padrão de nova tentativa ajudará (se o arquivo não puder ser aberto por mais ninguém, mas sim, você precisará de um utilitário como o Process Explorer para verificar quem está fazendo o que ).

Maneiras de evitar

Quando for o caso, use sempre usando declarações aos arquivos abertos. Como dito no parágrafo anterior, ele ajudará ativamente a evitar muitos erros comuns (consulte esta postagem para um exemplo de como não usá-lo ).

Se possível, tente decidir quem possui o acesso a um arquivo específico e centralize o acesso por meio de alguns métodos conhecidos. Se, por exemplo, você tiver um arquivo de dados em que seu programa leia e grave, você deve colocar todo o código de E / S dentro de uma única classe. Isso tornará a depuração mais fácil (porque você sempre pode colocar um ponto de interrupção lá e ver quem está fazendo o que) e também será um ponto de sincronização (se necessário) para acesso múltiplo.

Não se esqueça que as operações de E / S sempre podem falhar, um exemplo comum é este:

if (File.Exists(path))
    File.Delete(path);

Se alguém excluir o arquivo depois, File.Exists()mas antes File.Delete(), ele lançará um arquivo IOExceptionem um local onde você se sentirá errado.

Sempre que possível, aplique um padrão de nova tentativa e, se estiver usando FileSystemWatcher, adie a ação (porque você será notificado, mas um aplicativo ainda poderá estar trabalhando exclusivamente com esse arquivo).

Cenários avançados
Nem sempre é tão fácil, então você pode precisar compartilhar o acesso com outra pessoa. Se, por exemplo, você está lendo do começo e escrevendo até o fim, tem pelo menos duas opções.

1) compartilhe o mesmo FileStreamcom as funções de sincronização adequadas (porque não é seguro para threads ). Veja este e este post para um exemplo.

2) use a FileShareenumeração para instruir o SO a permitir que outros processos (ou outras partes do seu próprio processo) acessem o mesmo arquivo simultaneamente.

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}

Neste exemplo, mostrei como abrir um arquivo para escrever e compartilhar para leitura; observe que, ao ler e escrever sobreposições, isso resulta em dados indefinidos ou inválidos. É uma situação que deve ser tratada ao ler. Observe também que isso não torna o acesso ao streamthread seguro, portanto, esse objeto não pode ser compartilhado com vários threads, a menos que o acesso seja sincronizado de alguma forma (consulte os links anteriores). Outras opções de compartilhamento estão disponíveis e abrem cenários mais complexos. Por favor, consulte o MSDN para mais detalhes.

Em geral, os processos N podem ler o mesmo arquivo todos juntos, mas apenas um deve escrever; em um cenário controlado, você pode até ativar gravações simultâneas, mas isso não pode ser generalizado em poucos parágrafos de texto nesta resposta.

É possível desbloquear um arquivo usado por outro processo? Nem sempre é seguro e nem tão fácil, mas sim, é possível .

Adriano Repetti
fonte
Não sei o que há de errado com meu código, uso blocos, mas ainda há um erro ao dizer que o arquivo está em uso quando tento excluí-lo.
Jamshaid Kamran
Não, na verdade eu tenho um controle de timer fazendo isso, ao lado de timer, se eu chamar a função novamente, ele lançará uma exceção. Basicamente, eu estou descriptografando um arquivo em outro arquivo de texto, depois disso excluindo o arquivo recém-criado, que lança a exceção na exclusão!
Jamshaid Kamran
3
@ جمشیدکامران Por que criar um arquivo com o conteúdo que você tem no seu código e excluí-lo? Parece estranho criar o arquivo em primeiro lugar, então. Como você não publicou o código, não sabemos o que você está fazendo. Mas quando você cria seu arquivo, se o fizer File.Create(path), deve adicionar .Close()no final antes de escrever nele. Existem armadilhas como essa, além de usinginstruções para gravar os arquivos e depois excluí-los. Você deve postar o código na sua pergunta sobre como você está criando e excluindo seu arquivo. Mas provavelmente se alinha com o mencionado acima.
precisa saber é o seguinte
Meu código usa, Directory.SetCreationTimeUTC()mas falha quando o File Explorer está aberto, alegando que o diretório está sendo acessado por outro processo. Como devo lidar com essa situação?
Kyle Delaney
1
@KyleDelaney Eu diria que você precisa esperar até que a pasta seja fechada, se não demorar alguns segundos, tudo se tornará rapidamente complexo (mantenha uma fila de segundo plano com operações pendentes? Observador do sistema de arquivos? Sondagem?) Você pode querer postar uma pergunta com mais detalhes por uma resposta ad-hoc
Adriano Repetti
29

O uso do FileShare corrigiu meu problema de abrir o arquivo, mesmo que ele fosse aberto por outro processo.

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}
Muhammad Umar
fonte
9

Ocorreu um problema ao fazer upload de uma imagem e não pôde excluí-la e encontrou uma solução. gl hf

//C# .NET
var image = Image.FromFile(filePath);

image.Dispose(); // this removes all resources

//later...

File.Delete(filePath); //now works
Hudson
fonte
2
Se você estiver excluindo um arquivo, precisará descartar o objeto de imagem primeiro.
shazia 14/05
1
Bom, procurei esta solução. Eu também tive um problema com a função Image.FromFile.
the scion
1
@thescion :) np
Hudson
1
Isso funcionou brilhantemente para mim e resolveu meu problema exato. Eu queria processar uma pasta de arquivos Tiff, convertê-los em fluxos de bytes [] e enviar para um serviço e, em seguida, mover a pasta de arquivos Tiff para uma pasta de arquivo "Processado". O Image.FromFile coloca um bloqueio no arquivo Tiff e não o libera oportunamente. Portanto, quando eu movia os arquivos Tiff e a pasta que continha, recebia o erro "sendo usado por outro processo", pois os bloqueios ainda estavam no lugar. Executar o .Release logo após obter os bytes do arquivo Tiff corrigiu totalmente esse problema de arquivo bloqueado.
Desenvolvedor63
4

Eu recebi esse erro porque estava executando o File.Move para um caminho de arquivo sem um nome de arquivo, preciso especificar o caminho completo no destino.

amor ao vivo
fonte
E não apenas sem um nome de arquivo. Um nome de arquivo ilegal (de destino) - no meu caso "... \ file". - dará o mesmo erro estúpido e o indicará na direção errada por meio dia!
Nick Westgate
3

O erro indica que outro processo está tentando acessar o arquivo. Talvez você ou alguém o tenha aberto enquanto você tenta escrever nele. "Ler" ou "Copiar" geralmente não causa isso, mas escrever nele ou chamar excluir nele causaria.

Existem algumas coisas básicas para evitar isso, como outras respostas mencionaram:

  1. Nas FileStreamoperações, coloque-o em um usingbloco com um FileShare.ReadWritemodo de acesso.

    Por exemplo:

    using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
    {
    }

    Observe que FileAccess.ReadWritenão é possível se você usar FileMode.Append.

  2. Corri esse problema quando estava usando um fluxo de entrada para fazer isso File.SaveAsquando o arquivo estava em uso. No meu caso, descobri que na verdade não precisava salvá-lo no sistema de arquivos, então acabei removendo isso, mas provavelmente poderia ter tentado criar um FileStream em uma usingdeclaração com FileAccess.ReadWrite, assim como o código acima.

  3. Salvar seus dados como um arquivo diferente e voltar para excluir o antigo quando ele não estiver mais em uso, em seguida, renomear aquele que foi salvo com sucesso no nome do original é uma opção. Como você testa o arquivo em uso é realizado através do

    List<Process> lstProcs = ProcessHandler.WhoIsLocking(file);

    linha no meu código abaixo e pode ser feito em um serviço do Windows, em loop, se você tiver um arquivo específico que deseja assistir e excluir regularmente quando quiser substituí-lo. Se você nem sempre tiver o mesmo arquivo, um arquivo de texto ou tabela de banco de dados poderá ser atualizado para que o serviço sempre verifique nomes de arquivos e execute essa verificação de processos e, posteriormente, execute o processo de eliminação e eliminação, conforme descrevo na próxima opção. Observe que você precisará de um nome de usuário e senha de conta com privilégios de administrador no computador especificado, é claro, para executar a exclusão e o término dos processos.

  4. Quando você não souber se um arquivo estará em uso quando estiver tentando salvá-lo, poderá fechar todos os processos que poderiam estar em uso, como o Word, se for um documento do Word, antes do salvamento.

    Se for local, você pode fazer isso:

    ProcessHandler.localProcessKill("winword.exe");

    Se for remoto, você pode fazer o seguinte:

    ProcessHandler.remoteProcessKill(computerName, txtUserName, txtPassword, "winword.exe");

    Onde txtUserNameestá na forma de DOMAIN\user.

  5. Digamos que você não saiba o nome do processo que está bloqueando o arquivo. Então, você pode fazer isso:

    List<Process> lstProcs = new List<Process>();
    lstProcs = ProcessHandler.WhoIsLocking(file);
    
    foreach (Process p in lstProcs)
    {
        if (p.MachineName == ".")
            ProcessHandler.localProcessKill(p.ProcessName);
        else
            ProcessHandler.remoteProcessKill(p.MachineName, txtUserName, txtPassword, p.ProcessName);
    }

    Note-se que filedeve ser o caminho UNC: \\computer\share\yourdoc.docxpara que a Processdescobrir o computador é ligado e p.MachineNameseja válido.

    Abaixo está a classe que essas funções usam, que requer a adição de uma referência System.Management. O código foi originalmente escrito por Eric J .:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using System.Management;
    
    namespace MyProject
    {
        public static class ProcessHandler
        {
            [StructLayout(LayoutKind.Sequential)]
            struct RM_UNIQUE_PROCESS
            {
                public int dwProcessId;
                public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
            }
    
            const int RmRebootReasonNone = 0;
            const int CCH_RM_MAX_APP_NAME = 255;
            const int CCH_RM_MAX_SVC_NAME = 63;
    
            enum RM_APP_TYPE
            {
                RmUnknownApp = 0,
                RmMainWindow = 1,
                RmOtherWindow = 2,
                RmService = 3,
                RmExplorer = 4,
                RmConsole = 5,
                RmCritical = 1000
            }
    
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            struct RM_PROCESS_INFO
            {
                public RM_UNIQUE_PROCESS Process;
    
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
                public string strAppName;
    
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
                public string strServiceShortName;
    
                public RM_APP_TYPE ApplicationType;
                public uint AppStatus;
                public uint TSSessionId;
                [MarshalAs(UnmanagedType.Bool)]
                public bool bRestartable;
            }
    
            [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
            static extern int RmRegisterResources(uint pSessionHandle,
                                                UInt32 nFiles,
                                                string[] rgsFilenames,
                                                UInt32 nApplications,
                                                [In] RM_UNIQUE_PROCESS[] rgApplications,
                                                UInt32 nServices,
                                                string[] rgsServiceNames);
    
            [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
            static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
    
            [DllImport("rstrtmgr.dll")]
            static extern int RmEndSession(uint pSessionHandle);
    
            [DllImport("rstrtmgr.dll")]
            static extern int RmGetList(uint dwSessionHandle,
                                        out uint pnProcInfoNeeded,
                                        ref uint pnProcInfo,
                                        [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
                                        ref uint lpdwRebootReasons);
    
            /// <summary>
            /// Find out what process(es) have a lock on the specified file.
            /// </summary>
            /// <param name="path">Path of the file.</param>
            /// <returns>Processes locking the file</returns>
            /// <remarks>See also:
            /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
            /// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
            /// 
            /// </remarks>
            static public List<Process> WhoIsLocking(string path)
            {
                uint handle;
                string key = Guid.NewGuid().ToString();
                List<Process> processes = new List<Process>();
    
                int res = RmStartSession(out handle, 0, key);
                if (res != 0) throw new Exception("Could not begin restart session.  Unable to determine file locker.");
    
                try
                {
                    const int ERROR_MORE_DATA = 234;
                    uint pnProcInfoNeeded = 0,
                        pnProcInfo = 0,
                        lpdwRebootReasons = RmRebootReasonNone;
    
                    string[] resources = new string[] { path }; // Just checking on one resource.
    
                    res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
    
                    if (res != 0) throw new Exception("Could not register resource.");
    
                    //Note: there's a race condition here -- the first call to RmGetList() returns
                    //      the total number of process. However, when we call RmGetList() again to get
                    //      the actual processes this number may have increased.
                    res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
    
                    if (res == ERROR_MORE_DATA)
                    {
                        // Create an array to store the process results
                        RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
                        pnProcInfo = pnProcInfoNeeded;
    
                        // Get the list
                        res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
                        if (res == 0)
                        {
                            processes = new List<Process>((int)pnProcInfo);
    
                            // Enumerate all of the results and add them to the 
                            // list to be returned
                            for (int i = 0; i < pnProcInfo; i++)
                            {
                                try
                                {
                                    processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
                                }
                                // catch the error -- in case the process is no longer running
                                catch (ArgumentException) { }
                            }
                        }
                        else throw new Exception("Could not list processes locking resource.");
                    }
                    else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
                }
                finally
                {
                    RmEndSession(handle);
                }
    
                return processes;
            }
    
            public static void remoteProcessKill(string computerName, string userName, string pword, string processName)
            {
                var connectoptions = new ConnectionOptions();
                connectoptions.Username = userName;
                connectoptions.Password = pword;
    
                ManagementScope scope = new ManagementScope(@"\\" + computerName + @"\root\cimv2", connectoptions);
    
                // WMI query
                var query = new SelectQuery("select * from Win32_process where name = '" + processName + "'");
    
                using (var searcher = new ManagementObjectSearcher(scope, query))
                {
                    foreach (ManagementObject process in searcher.Get()) 
                    {
                        process.InvokeMethod("Terminate", null);
                        process.Dispose();
                    }
                }            
            }
    
            public static void localProcessKill(string processName)
            {
                foreach (Process p in Process.GetProcessesByName(processName))
                {
                    p.Kill();
                }
            }
    
            [DllImport("kernel32.dll")]
            public static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, int dwFlags);
    
            public const int MOVEFILE_DELAY_UNTIL_REBOOT = 0x4;
    
        }
    }
vapcguy
fonte
2

Como outras respostas neste tópico apontaram, para resolver esse erro, é necessário inspecionar cuidadosamente o código, para entender onde o arquivo está sendo bloqueado.

No meu caso, eu estava enviando o arquivo como um anexo de email antes de executar a operação de movimentação.

Portanto, o arquivo ficou bloqueado por alguns segundos até o cliente SMTP terminar de enviar o email.

A solução que eu adotei foi mover o arquivo primeiro e depois enviar o email. Isso resolveu o problema para mim.

Outra solução possível, como apontado anteriormente por Hudson, seria descartar o objeto após o uso.

public static SendEmail()
{
           MailMessage mMailMessage = new MailMessage();
           //setup other email stuff

            if (File.Exists(attachmentPath))
            {
                Attachment attachment = new Attachment(attachmentPath);
                mMailMessage.Attachments.Add(attachment);
                attachment.Dispose(); //disposing the Attachment object
            }
} 
Abhishek Poojary
fonte
Se um arquivo estiver em uso File.Move(), não funcionará e causará o mesmo erro. Se apenas adicionar um arquivo a um email, não acho que ocorra algum erro durante o uso durante a Attachments.Add()operação, porque esta é apenas uma operação de cópia. Se, por algum motivo, você puder copiá-lo para um diretório Temp, anexe a cópia e exclua o arquivo copiado posteriormente. Mas acho que, se o OP quiser modificar um arquivo e usá-lo, esse tipo de solução (da qual você não mostrou o código, apenas a parte anexa) funcionaria. .Dispose()é sempre uma boa ideia, mas não é relevante aqui, a menos que o arquivo seja aberto em uma operação anterior.
Vapcguy
1

Eu tive o seguinte cenário que estava causando o mesmo erro:

  • Carregar arquivos para o servidor
  • Em seguida, livre-se dos arquivos antigos depois que eles foram enviados

A maioria dos arquivos era pequena, no entanto, alguns eram grandes e, portanto, a tentativa de excluir os arquivos resultou no erro de acesso ao arquivo .

Não foi fácil encontrar, no entanto, a solução era tão simples quanto Aguardar "a tarefa concluir a execução":

using (var wc = new WebClient())
{
   var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
   tskResult.Wait(); 
}
usefulBee
fonte
-1

Meu código abaixo resolve esse problema, mas sugiro Antes de tudo, você precisa entender o que está causando esse problema e tentar a solução que você pode encontrar alterando o código

Posso dar outra maneira de resolver esse problema, mas a melhor solução é verificar sua estrutura de codificação e tentar analisar o que faz isso acontecer, se você não encontrar nenhuma solução, poderá seguir este código abaixo

try{
Start:
///Put your file access code here


}catch (Exception ex)
 {
//by anyway you need to handle this error with below code
   if (ex.Message.StartsWith("The process cannot access the file"))
    {
         //Wait for 5 seconds to free that file and then start execution again
         Thread.Sleep(5000);
         goto Start;
    }
 }
Cinza
fonte
1
Alguns problemas com este código: 1) se você precisar ligar GC.*(), provavelmente terá outros problemas com seu código. 2) A mensagem é localizada e frágil , use o HRESULT. 3) Você pode dormir com Task.Delay()(e dez segundos é excessivo em muitos casos). 4) Você não tem uma condição de saída: esse código pode travar o forver. 5) Você definitivamente não precisa gotoaqui. 6) Pegar Exceptioné geralmente uma má idéia, neste caso também porque ... 6) Se algo mais acontecer, você estará engolindo o erro.
Adriano Repetti
@AdrianoRepetti meu primeiro comentário disse que não use este código tente encontrar outra solução, esta será a última opção e, de qualquer forma, se esse erro acontecer, o código será interrompido, mas se você estiver lidando com esse sistema de erros, o sistema aguardará para liberar o arquivo e, depois disso, iniciará trabalhando e não enfrento outros problemas com o GC. * enquanto estiver usando esse código. eu já tenho um sistema funcionando com esse código, então eu publiquei pode ser útil para alguém.
Ash
Eu diria para não usar este código sempre (por causa dos pontos que eu mencionei acima) e eu não iria considerar este trabalho em um sistema de prod mas isso é apenas a minha POV. Sinta-se livre para discordar!
Adriano Repetti
1) Eu usei o serviço Windows, onde eu consumi isso e usei o GC. * Até esse momento, não enfrento nenhum problema com isso2) Sim, o HRESULT pode ser a melhor opção em termos de padrão de codificação. para 5 segundos 4) de qualquer maneira, é melhor o sistema aguardar algum tempo para liberar recursos em vez de parar o sistema com erro 5) sim, eu concordo com você O tratamento de exceções nem sempre é uma boa ideia, mas se o seu trecho de código for pequeno e você sabe que aqui eu posso lidar com exceção, então esta solução é uma opção para você.
Ash
Consulte também: docs.microsoft.com/en-us/archive/blogs/ricom/… . O caso ONLY (e enfatizo ONLY) que eu precisava usar GC.Collect()foi ao lidar com alguns objetos COM.
Adriano Repetti