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?
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?
Respostas:
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).
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 FileStream
implementa IDisposable
, portanto, é fácil agrupar todo o seu código dentro de uma using
instruçã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 File
funçõ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 ).
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 IOException
em 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 FileStream
com as funções de sincronização adequadas (porque não é seguro para threads ). Veja este e este post para um exemplo.
2) use a FileShare
enumeraçã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 stream
thread 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 .
File.Create(path)
, deve adicionar .Close()
no final antes de escrever nele. Existem armadilhas como essa, além de using
instruçõ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.
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?
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))
{
}
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
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.
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:
Nas FileStream
operações, coloque-o em um using
bloco com um FileShare.ReadWrite
modo de acesso.
Por exemplo:
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}
Observe que FileAccess.ReadWrite
não é possível se você usar FileMode.Append
.
Corri esse problema quando estava usando um fluxo de entrada para fazer isso File.SaveAs
quando 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 using
declaração com FileAccess.ReadWrite
, assim como o código acima.
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.
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 txtUserName
está na forma de DOMAIN\user
.
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 file
deve ser o caminho UNC: \\computer\share\yourdoc.docx
para que a Process
descobrir o computador é ligado e p.MachineName
seja 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;
}
}
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
}
}
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.
Eu tive o seguinte cenário que estava causando o mesmo erro:
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();
}
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;
}
}
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 goto
aqui. 6) Pegar Exception
é geralmente uma má idéia, neste caso também porque ... 6) Se algo mais acontecer, você estará engolindo o erro.
GC.Collect()
foi ao lidar com alguns objetos COM.