Existe uma maneira de verificar se um arquivo está em uso?

846

Estou escrevendo um programa em c # que precisa acessar repetidamente 1 arquivo de imagem. Na maioria das vezes, ele funciona, mas se meu computador estiver executando rapidamente, ele tentará acessar o arquivo antes de ser salvo no sistema de arquivos e lançará um erro: "Arquivo em uso por outro processo" .

Gostaria de encontrar uma maneira de contornar isso, mas todo o meu Google apenas produziu verificações usando o tratamento de exceções. Isso é contra a minha religião, então eu queria saber se alguém tem uma maneira melhor de fazê-lo.

Dawsy
fonte
30
Tudo bem, você pode testá-lo examinando todas as alças abertas no sistema. No entanto, como o Windows é um sistema operacional multitarefa, existe a chance de, logo após a execução do código, determinar se o arquivo está aberto e você considerar que não é, um código de processo começa a usar esse arquivo e, quando você tentar use-o, você recebe um erro. Mas não há nada errado em verificar primeiro; apenas não assuma que não está em uso quando você realmente precisa.
BobbyShaftoe
3
Mas apenas para esta questão específica; Eu recomendo não examinar os identificadores de arquivo e apenas tentar um número predefinido de vezes, digamos 3-5 antes de falhar.
BobbyShaftoe
Como esse arquivo de imagem é gerado? Você pode parar / dormir / pausar seu programa até que a geração seja concluída? Essa é de longe uma maneira superior de lidar com a situação. Caso contrário, acho que você não pode evitar o tratamento de exceções.
Catchwa 18/05/09
Todo o uso de exceções não é uma verificação de alguma suposição, fazendo algo potencialmente perigoso, enquanto deliberadamente não descartando a possibilidade de falha?
GTC
26
Sua filosofia tem um mau entendimento das exceções. A maioria das pessoas pensa que as exceções significam algo que está errado, algo está errado, morre, morre. Quando exceção significa .... exceção. Isso significa que ocorreu algo excepcional que você precisa "manipular" (ou considerar). Talvez você queira continuar tentando novamente para acessar dados, talvez o usuário precise saber que você não pode obter uma conexão. O que você faz? Você lida com o ConnectionFailedException e notifica o usuário; talvez, eles parem de tentar após uma hora e notem que o cabo está desconectado.
21813 Lee Louviere

Respostas:

543

NOTA atualizada sobre esta solução : A verificação com FileAccess.ReadWritefalha de arquivos somente leitura, portanto a solução foi modificada para verificação FileAccess.Read. Embora esta solução funcione porque tentar verificar com FileAccess.Readele falhará se o arquivo tiver um bloqueio de gravação ou leitura, no entanto, esta solução não funcionará se o arquivo não tiver um bloqueio de gravação ou leitura, ou seja, foi aberto (para leitura ou gravação) com o acesso FileShare.Read ou FileShare.Write.

ORIGINAL: usei esse código nos últimos anos e não tive problemas com ele.

Entenda sua hesitação em usar exceções, mas você não pode evitá-las o tempo todo:

protected virtual bool IsFileLocked(FileInfo file)
{
    try
    {
        using(FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
        {
            stream.Close();
        }
    }
    catch (IOException)
    {
        //the file is unavailable because it is:
        //still being written to
        //or being processed by another thread
        //or does not exist (has already been processed)
        return true;
    }

    //file is not locked
    return false;
}
ChrisW
fonte
60
Esta é uma ótima solução, mas tenho um comentário - você pode não querer abrir o arquivo com o modo de acesso FileAccess.Read, pois o ReadWrite sempre falhará se o arquivo for somente leitura.
precisa saber é o seguinte
220
-1. Essa é uma resposta ruim, porque o arquivo pode ficar bloqueado por outro encadeamento / processo após ser fechado no IsFileLocked e antes que o encadeamento tenha a chance de abri-lo.
Polyfun
16
Eu acho que essa é uma ótima resposta. Estou usando isso como um método de extensão à la public static bool IsLocked(this FileInfo file) {/*...*/}.
Manuzor 04/07/2012
54
@ ChrisW: você pode estar se perguntando o que está acontecendo. Não se assuste. Você só está sendo sujeitos à ira da comunidade diário WTF: thedailywtf.com/Comments/...
Pierre Lebeaupin
16
@ ChrisW Por que isso é uma coisa ruim. Esta comunidade está aqui para apontar respostas boas e ruins. Se um monte de profissionais perceber que isso é uma coisa ruim e se inscrever para votar, então o site é WAI. E antes que você seja negativo, se você ler esse artigo, eles dizem "votam a resposta certa" e não votam a resposta errada. Deseja que eles também expliquem seus votos positivos nos comentários. Obrigado por me apresentar outro bom site!
9788 LeeVersion:
569

Você pode sofrer com uma condição de corrida de encadeamento, onde existem exemplos documentados disso sendo usado como uma vulnerabilidade de segurança. Se você verificar se o arquivo está disponível, mas tentar usá-lo, poderá lançar nesse ponto, que um usuário mal-intencionado pode usar para forçar e explorar seu código.

Sua melhor aposta é uma tentativa de captura / finalmente, que tenta entender o arquivo.

try
{
   using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open))
   {
        // File/Stream manipulating code here
   }
} catch {
  //check here why it failed and ask user to retry if the file is in use.
}
Spence
fonte
124
+1. Não há uma maneira 100% segura de "saber se um arquivo está em uso", porque milissegundos após a verificação, o arquivo pode não estar mais em uso ou vice-versa. Em vez disso, basta abrir o arquivo e usá-lo se não houver exceções.
Sedat Kapanoglu
8
Pena que o .NET não suporta CAS. Algo como, TryOpenFile (Ref FileHandle) que retorna sucesso / falha. Sempre deve haver uma solução alternativa para não depender apenas do tratamento de exceções. Gostaria de saber como o Microsoft Office faz isso.
TamusJRoyce
2
O principal a entender aqui é que essa API está simplesmente usando a API do Windows para obter um identificador de arquivo. Como tal, eles precisam traduzir o código de erro recebido da API C e envolvê-lo em uma exceção a ser lançada. Temos o tratamento de exceções no .Net, por que não usá-lo. Dessa forma, você pode escrever um caminho de encaminhamento limpo no seu código e deixar o tratamento de erros em um caminho de código separado.
Spence
36
A instrução using é para garantir que o fluxo seja fechado depois que eu terminar. Eu acho que você descobrirá que o using () {} tem menos caracteres do que tente {} finalmente {obj.Dispose ()}. Você também descobrirá que agora precisa declarar sua referência de objeto fora da instrução using, que é mais digitada. Se você tem uma interface explícita, também precisa transmitir. Finalmente, você deseja descartar o mais rápido possível, e a lógica finalmente pode ter uma interface do usuário ou qualquer outra ação de longa duração que tem pouco a ver com a chamada de IDispose. </rant>
Spence
2
isso não nega o fato de que você deve declarar seu objeto fora da tentativa e precisa chamar explicitamente descarte, o que usar faz por você e significa a mesma coisa.
Spence
92

Use isto para verificar se um arquivo está bloqueado:

using System.IO;
using System.Runtime.InteropServices;
internal static class Helper
{
const int ERROR_SHARING_VIOLATION = 32;
const int ERROR_LOCK_VIOLATION = 33;

private static bool IsFileLocked(Exception exception)
{
    int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
    return errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION;
}

internal static bool CanReadFile(string filePath)
{
    //Try-Catch so we dont crash the program and can check the exception
    try {
        //The "using" is important because FileStream implements IDisposable and
        //"using" will avoid a heap exhaustion situation when too many handles  
        //are left undisposed.
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
            if (fileStream != null) fileStream.Close();  //This line is me being overly cautious, fileStream will never be null unless an exception occurs... and I know the "using" does it but its helpful to be explicit - especially when we encounter errors - at least for me anyway!
        }
    }
    catch (IOException ex) {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex)) {
            // do something, eg File.Copy or present the user with a MsgBox - I do not recommend Killing the process that is locking the file
            return false;
        }
    }
    finally
    { }
    return true;
}
}

Por motivos de desempenho, recomendo que você leia o conteúdo do arquivo na mesma operação. aqui estão alguns exemplos:

public static byte[] ReadFileBytes(string filePath)
{
    byte[] buffer = null;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
                sum += count;  // sum is a buffer offset for next reading

            fileStream.Close(); //This is not needed, just me being paranoid and explicitly releasing resources ASAP
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
    }
    return buffer;
}

public static string ReadFileTextWithEncoding(string filePath)
{
    string fileContents = string.Empty;
    byte[] buffer;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
            {
                sum += count;  // sum is a buffer offset for next reading
            }

            fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP

            //Depending on the encoding you wish to use - I'll leave that up to you
            fileContents = System.Text.Encoding.Default.GetString(buffer);
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    { }     
    return fileContents;
}

public static string ReadFileTextNoEncoding(string filePath)
{
    string fileContents = string.Empty;
    byte[] buffer;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) 
            {
                sum += count;  // sum is a buffer offset for next reading
            }

            fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP

            char[] chars = new char[buffer.Length / sizeof(char) + 1];
            System.Buffer.BlockCopy(buffer, 0, chars, 0, buffer.Length);
            fileContents = new string(chars);
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
    }

    return fileContents;
}

Experimente você mesmo:

byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");
Jeremy Thompson
fonte
8
Eu upvote se não houvesse tantos "números mágicos" lá en.wikipedia.org/wiki/Magic_number_(programming)
Kris
3
Eu estava me referindo às comparações errorCode, não o bit muda. ainda que agora você mencionou ...
Kris
1
Sua captura deve estar ativada IOException, em vez de geral, Exceptione depois um teste no tipo.
Askolein
3
@ JeremyThompson, infelizmente, você colocou o específico IOExceptionapós o geral. O geral vai pegar tudo que passa e o específico IOExceptionsempre será solitário. Apenas troque os dois.
Askolein
Eu gosto desta solução. Uma outra sugestão: dentro da captura como uma outra coisa para o if (IsFileLocked (ex)) eu jogaria ex. Isso resolverá o caso em que o arquivo não existe (ou qualquer outra IOException) lançando a exceção.
shindigo
7

Basta usar a exceção como pretendido. Aceite que o arquivo esteja em uso e tente novamente várias vezes até que sua ação seja concluída. Isso também é o mais eficiente, porque você não perde nenhum ciclo verificando o estado antes de agir.

Use a função abaixo, por exemplo

TimeoutFileAction(() => { System.IO.File.etc...; return null; } );

Método reutilizável que atinge o tempo limite após 2 segundos

private T TimeoutFileAction<T>(Func<T> func)
{
    var started = DateTime.UtcNow;
    while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
    {
        try
        {
            return func();                    
        }
        catch (System.IO.IOException exception)
        {
            //ignore, or log somewhere if you want to
        }
    }
    return default(T);
}
kernowcode
fonte
6

Talvez você possa usar um FileSystemWatcher e assistir ao evento Changed.

Eu não usei isso sozinho, mas pode valer a pena tentar. Se o file systemwatcher for um pouco pesado para este caso, eu usaria o loop try / catch / sleep.

Karl Johan
fonte
1
O uso de um FileSystemWatcher não ajuda, porque os eventos Created e Changed aumentam no início de uma criação / alteração de arquivo. Até arquivos pequenos precisam de mais tempo para serem gravados e fechados pelo sistema operacional do que o aplicativo .NET precisa para executar o retorno de chamada FileSystemEventHandler. Isso é tão triste, mas não há outra opção além de estimar o tempo de espera antes de acessar o arquivo ou correr em loops de exceção ...
O FileSystemWatcher não lida muito com muitas alterações ao mesmo tempo, portanto, tenha cuidado com isso.
Ben F
1
BTW, vocês notaram enquanto depuravam e observavam threads que a MS chama de FSW "FileSystemWather"? O que é uma wather, afinal?
devlord
Precisamente recebi esse problema porque um serviço do Windows com o FileSystemWatcher tenta ler o arquivo antes que o processo o feche.
Freedeveloper 08/08/19
6

Você pode retornar uma tarefa que fornece um fluxo assim que estiver disponível. É uma solução simplificada, mas é um bom ponto de partida. É thread-safe.

private async Task<Stream> GetStreamAsync()
{
    try
    {
        return new FileStream("sample.mp3", FileMode.Open, FileAccess.Write);
    }
    catch (IOException)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        return await GetStreamAsync();
    }
}

Você pode usar esse fluxo como de costume:

using (var stream = await FileStreamGetter.GetStreamAsync())
{
    Console.WriteLine(stream.Length);
}
Ivan Branets
fonte
3
Quantos segundos até uma pilha exceder a partir da recursão GetStreamAsync()?
CAD bloke
@CADbloke, você levantou um ponto muito bom. Na verdade, minha amostra pode ter uma exceção de estouro de pilha, caso um arquivo não esteja disponível por um longo tempo. Relacionado a esta resposta stackoverflow.com/questions/4513438/… , pode gerar a exceção em 5 horas.
277 Ivan Branets
Em relação aos seus casos de uso, é preferível lançar uma exceção de E / S se, digamos, 10 tentativas de leitura do arquivo falharem. A outra estratégia pode estar aumentando o tempo de espera por um segundo depois que 10 tentativas falharam. Você também pode usar uma mistura de ambos.
277 Ivan Branets
Eu (e faço) simplesmente alertaria o usuário que o arquivo está bloqueado. Eles geralmente travam eles mesmos e provavelmente farão algo a respeito. Ou não.
CAD cara
Em alguns casos, você precisa usar a política de repetição, pois um arquivo pode ainda não estar pronto. Imagine um aplicativo da área de trabalho para baixar imagens em alguma pasta temporária. O aplicativo inicia o download e, ao mesmo tempo, você abre esta pasta no explorador de arquivos. O Windows deseja criar uma miniatura imediatamente e bloquear o arquivo. Ao mesmo tempo, seu aplicativo tenta substituir a imagem bloqueada para outro lugar. Você receberá uma exceção se não usar a política de repetição.
Ivan Branets 31/07
4

a única maneira que conheço é usar a API de bloqueio exclusivo do Win32, que não é muito rápida, mas existem exemplos.

A maioria das pessoas, para uma solução simples para isso, simplesmente tenta / pega / dorme os loops.

Luke Schafer
fonte
1
Você não pode usar esta API sem abrir o arquivo primeiro, quando não precisa mais.
Harry Johnston
1
Arquivo de Schrodinger.
TheLastGIS
4
static bool FileInUse(string path)
    {
        try
        {
            using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
            {
                fs.CanWrite
            }
            return false;
        }
        catch (IOException ex)
        {
            return true;
        }
    }

string filePath = "C:\\Documents And Settings\\yourfilename";
bool isFileInUse;

isFileInUse = FileInUse(filePath);

// Then you can do some checking
if (isFileInUse)
   Console.WriteLine("File is in use");
else
   Console.WriteLine("File is not in use");

Espero que isto ajude!

Julian
fonte
10
A verificação real que você executa é boa; colocá-lo dentro de uma função é enganoso. Você NÃO deseja usar uma função como esta antes de abrir um arquivo. Dentro da função, o arquivo é aberto, verificado e fechado. Em seguida, o programador ASSUME que o arquivo AINDA está ok para usar e tenta abri-lo para uso. Isso é ruim porque pode ser usado e bloqueado por outro processo que foi colocado na fila para abrir esse arquivo. Entre a 1ª vez que foi aberta (para verificação) e a 2ª vez em que foi aberta (para uso), o sistema operacional poderia ter programado seu processo e poderia estar executando outro processo.
27413 Lakey
3

As respostas aceitas acima sofrem um problema em que, se o arquivo tiver sido aberto para gravação no modo FileShare.Read ou se o arquivo tiver um atributo Somente Leitura, o código não funcionará. Essa solução modificada funciona de maneira mais confiável, com duas coisas a serem lembradas (como também é verdade para a solução aceita):

  1. Não funcionará para arquivos que foram abertos com um modo de compartilhamento de gravação
  2. Isso não leva em consideração os problemas de encadeamento, portanto, você precisará bloqueá-lo ou lidar com problemas de encadeamento separadamente.

Tendo em mente o acima exposto, isso verifica se o arquivo está bloqueado para gravação ou bloqueado para impedir a leitura :

public static bool FileLocked(string FileName)
{
    FileStream fs = null;

    try
    {
        // NOTE: This doesn't handle situations where file is opened for writing by another process but put into write shared mode, it will not throw an exception and won't show it as write locked
        fs = File.Open(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); // If we can't open file for reading and writing then it's locked by another process for writing
    }
    catch (UnauthorizedAccessException) // https://msdn.microsoft.com/en-us/library/y973b725(v=vs.110).aspx
    {
        // This is because the file is Read-Only and we tried to open in ReadWrite mode, now try to open in Read only mode
        try
        {
            fs = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None);
        }
        catch (Exception)
        {
            return true; // This file has been locked, we can't even open it to read
        }
    }
    catch (Exception)
    {
        return true; // This file has been locked
    }
    finally
    {
        if (fs != null)
            fs.Close();
    }
    return false;
}
rboy
fonte
Ainda tem o mesmo problema que a resposta aceita - apenas informa se o arquivo foi bloqueado por outro processo em um momento específico , o que não é uma informação útil. Quando a função retornar, o resultado já estará desatualizado!
Harry Johnston
1
isso é verdade, só é possível verificar a qualquer momento (ou assinar eventos), a vantagem dessa abordagem sobre a solução aceita é que ela pode verificar se há um atributo somente leitura e um bloqueio de gravação e não retornar um falso positivo.
rboy
3

Além de trabalhar com três linhas e apenas para referência: se você quiser informações completas - há um pequeno projeto no Microsoft Dev Center:

https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4

Da introdução:

O código de exemplo C # desenvolvido no .NET Framework 4.0 ajudaria a descobrir qual é o processo que está bloqueando um arquivo. A função RmStartSession incluída no rstrtmgr.dll foi usada para criar uma sessão do gerenciador de reinicialização e, de acordo com o resultado do retorno, uma nova instância do objeto Win32Exception é criada. Após registrar os recursos em uma sessão do Restart Manager por meio da função RmRegisterRescources , a função RmGetList é chamada para verificar o que os aplicativos estão usando em um arquivo específico, enumerando a matriz RM_PROCESS_INFO .

Funciona conectando-se à "Reiniciar sessão do gerenciador".

O Restart Manager usa a lista de recursos registrados com a sessão para determinar quais aplicativos e serviços devem ser desligados e reiniciados. Os recursos podem ser identificados por nomes de arquivos, nomes abreviados de serviço ou estruturas RM_UNIQUE_PROCESS que descrevem aplicativos em execução.

Pode ser um pouco complicado para suas necessidades específicas ... Mas se é isso que você deseja, vá em frente e pegue o vs-project.

Bernhard
fonte
2

Na minha experiência, você geralmente quer fazer isso, depois 'protege' seus arquivos para fazer algo sofisticado e depois usa os arquivos 'protegidos'. Se você tiver apenas um arquivo que queira usar assim, poderá usar o truque explicado na resposta de Jeremy Thompson. No entanto, se você tentar fazer isso em vários arquivos (por exemplo, quando estiver escrevendo um instalador), sofrerá bastante.

Uma maneira muito elegante de resolver isso é usar o fato de que seu sistema de arquivos não permitirá que você altere o nome de uma pasta se um dos arquivos lá estiver sendo usado. Mantenha a pasta no mesmo sistema de arquivos e funcionará como um encanto.

Observe que você deve estar ciente das maneiras óbvias de que isso possa ser explorado. Afinal, os arquivos não serão bloqueados. Além disso, esteja ciente de que existem outros motivos que podem resultar na Movefalha da sua operação. Obviamente, o tratamento adequado de erros (MSDN) pode ajudar aqui.

var originalFolder = @"c:\myHugeCollectionOfFiles"; // your folder name here
var someFolder = Path.Combine(originalFolder, "..", Guid.NewGuid().ToString("N"));

try
{
    Directory.Move(originalFolder, someFolder);

    // Use files
}
catch // TODO: proper exception handling
{
    // Inform user, take action
}
finally
{
    Directory.Move(someFolder, originalFolder);
}

Para arquivos individuais, eu continuaria com a sugestão de bloqueio postada por Jeremy Thompson.

atlaste
fonte
Olá, Como a ordem das respostas muda, você pode esclarecer qual postagem acima você quer dizer para os leitores desse controle de qualidade popular. Obrigado.
21414 Jeremy Thompson
1
@ JeremyThompson Você está certo, obrigado, eu vou editar o post. Eu usaria a solução sua, principalmente por causa do uso correto FileSharee da verificação de um bloqueio.
Atlaste
2

Aqui está um código que, tanto quanto eu posso dizer, faz a mesma coisa que a resposta aceita, mas com menos código:

    public static bool IsFileLocked(string file)
    {
        try
        {
            using (var stream = File.OpenRead(file))
                return false;
        }
        catch (IOException)
        {
            return true;
        }        
    }

No entanto, acho que é mais robusto fazê-lo da seguinte maneira:

    public static void TryToDoWithFileStream(string file, Action<FileStream> action, 
        int count, int msecTimeOut)
    {
        FileStream stream = null;
        for (var i = 0; i < count; ++i)
        {
            try
            {
                stream = File.OpenRead(file);
                break;
            }
            catch (IOException)
            {
                Thread.Sleep(msecTimeOut);
            }
        }
        action(stream);
    }
cdiggins
fonte
2

Você pode usar minha biblioteca para acessar arquivos de vários aplicativos.

Você pode instalá-lo a partir do nuget: Install-Package Xabe.FileLock

Se você quiser obter mais informações, consulte https://github.com/tomaszzmuda/Xabe.FileLock

ILock fileLock = new FileLock(file);
if(fileLock.Acquire(TimeSpan.FromSeconds(15), true))
{
    using(fileLock)
    {
        // file operations here
    }
}

O método fileLock.Acquire retornará true somente se puder bloquear o arquivo exclusivo para esse objeto. Mas o aplicativo que carrega o arquivo também deve fazê-lo no bloqueio de arquivo. Se o objeto estiver inacessível, o método retornará false.

Tomasz udamuda
fonte
1
Não basta postar alguma ferramenta ou biblioteca como resposta. Pelo menos demonstre como ele resolve o problema na própria resposta.
precisa saber é o seguinte
Adicionada demonstração :) Sorry @ paper1111
Tomasz Żmuda
1
Requer todos os processos que estão usando o arquivo para cooperar. É improvável que seja aplicável ao problema original dos POs.
Harry Johnston
2

Uma vez eu precisei enviar PDFs para um arquivo de backup online. Mas o backup falharia se o usuário tivesse o arquivo aberto em outro programa (como o leitor de PDF). Na pressa, tentei algumas das principais respostas deste tópico, mas não consegui fazê-las funcionar. O que funcionou para mim foi tentar mover o arquivo PDF para seu próprio diretório . Descobri que isso falharia se o arquivo fosse aberto em outro programa e se a movimentação fosse bem-sucedida, não haveria operação de restauração necessária, como aconteceria se fosse movido para um diretório separado. Quero postar minha solução básica, caso seja útil para casos de uso específicos de outras pessoas.

string str_path_and_name = str_path + '\\' + str_filename;
FileInfo fInfo = new FileInfo(str_path_and_name);
bool open_elsewhere = false;
try
{
    fInfo.MoveTo(str_path_and_name);
}
catch (Exception ex)
{
    open_elsewhere = true;
}

if (open_elsewhere)
{
    //handle case
}
Benjamin Curtis Drake
fonte
0

Estou interessado em ver se isso desencadeia algum reflexo WTF. Eu tenho um processo que cria e, posteriormente, inicia um documento PDF a partir de um aplicativo de console. No entanto, eu estava lidando com uma fragilidade em que, se o usuário executasse o processo várias vezes, gerando o mesmo arquivo sem primeiro fechar o arquivo gerado anteriormente, o aplicativo lançaria uma exceção e morreria. Essa foi uma ocorrência bastante frequente, porque os nomes dos arquivos são baseados nos números das cotações de vendas.

Em vez de falhar de uma maneira tão desagradável, decidi confiar no versionamento de arquivo incrementado automaticamente:

private static string WriteFileToDisk(byte[] data, string fileName, int version = 0)
{
    try
    {
        var versionExtension = version > 0 ? $"_{version:000}" : string.Empty;
        var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{fileName}{versionExtension}.pdf");
        using (var writer = new FileStream(filePath, FileMode.Create))
        {
            writer.Write(data, 0, data.Length);
        }
        return filePath;
    }
    catch (IOException)
    {
        return WriteFileToDisk(data, fileName, ++version);
    }
}

Provavelmente, um pouco mais de cuidado pode ser dado ao catchbloco para garantir que eu esteja capturando as IOException (s) corretas. Provavelmente também limparei o armazenamento do aplicativo na inicialização, pois esses arquivos devem ser temporários de qualquer maneira.

Sei que isso vai além do escopo da questão do OP de simplesmente verificar se o arquivo está em uso, mas esse era realmente o problema que eu estava procurando resolver quando cheguei aqui; talvez seja útil para outra pessoa.

Vinney Kelly
fonte
0

Algo assim ajudaria?

var fileWasWrittenSuccessfully = false;
while (fileWasWrittenSuccessfully == false)
{
    try
    {
        lock (new Object())
        {
            using (StreamWriter streamWriter = new StreamWriter(filepath.txt"), true))
            {
                streamWriter.WriteLine("text");
            }
        }

        fileWasWrittenSuccessfully = true;
    }
    catch (Exception)
    {

    }
}
Tadej
fonte
-2

Tente mover / copiar o arquivo para um diretório temporário. Se você puder, ele não tem bloqueio e você pode trabalhar com segurança no diretório temp sem obter bloqueios. Caso contrário, tente movê-lo novamente em x segundos.

Carra
fonte
@jcolebrand bloqueia o quê? o que você copiou? Ou o que você colocou no diretório temp?
Cullub
5
se você copiar o arquivo, esperando que mais ninguém trabalhe nele, e você usará o arquivo temporário, e alguém o bloqueie logo após a cópia, você poderá perder dados.
precisa saber é o seguinte
-3

Eu uso essa solução alternativa, mas tenho um intervalo de tempo entre quando eu verifico o bloqueio do arquivo com a função IsFileLocked e quando abro o arquivo. Nesse período, algum outro thread pode abrir o arquivo, então eu receberei IOException.

Então, eu adicionei um código extra para isso. No meu caso, quero carregar o XDocument:

        XDocument xDoc = null;

        while (xDoc == null)
        {
            while (IsFileBeingUsed(_interactionXMLPath))
            {
                Logger.WriteMessage(Logger.LogPrioritet.Warning, "Deserialize can not open XML file. is being used by another process. wait...");
                Thread.Sleep(100);
            }
            try
            {
                xDoc = XDocument.Load(_interactionXMLPath);
            }
            catch
            {
                Logger.WriteMessage(Logger.LogPrioritet.Error, "Load working!!!!!");
            }
        }

O que você acha? Posso mudar alguma coisa? Talvez eu não precise usar a função IsFileBeingUsed?

obrigado

zzfima
fonte
4
o que é IsFileBeingUsed? código fonte sobre IsFileBeingUsed?
Kiquenet