Есть ли способ проверить, используется ли файл?

Я пишу программу на C #, которая должна повторно получить доступ к 1 файлу изображения. В большинстве случаев это работает, но если мой компьютер работает быстро, он попытается получить доступ к файлу до его сохранения в файловой системе и выдаст ошибку: «Файл используется другим процессом» .

Я хотел бы найти способ обойти это, но все мои поиски в Google привели только к созданию проверок с использованием обработки исключений. Это против моей религии, поэтому мне было интересно, есть ли у кого-нибудь лучший способ сделать это?

18.05.2009 06:37:40
Хорошо, вы можете проверить это, изучив все открытые маркеры в системе. Однако, поскольку Windows является многозадачной операционной системой, есть вероятность, что сразу после запуска кода, чтобы определить, открыт ли файл, и вы считаете, что это не так, код процесса начинает использовать этот файл, а затем, когда вы попытаетесь используйте его, вы получите ошибку. Но в проверке нет ничего плохого; только не думайте, что он не используется, когда он вам действительно нужен.
BobbyShaftoe 18.05.2009 06:45:08
Но только для этой конкретной проблемы; Я бы порекомендовал не проверять файловые дескрипторы, а просто попробовать какое-то заданное количество раз, скажем 3-5, прежде чем потерпеть неудачу.
BobbyShaftoe 18.05.2009 06:46:14
Как создается этот файл изображения? Можете ли вы остановить / спать / приостановить свою программу до завершения генерации? Это, безусловно, лучший способ справиться с ситуацией. Если нет, то я не думаю, что вы можете избежать использования обработки исключений.
Catchwa 18.05.2009 06:48:22
Не является ли все использование исключений проверкой некоторых предположений, совершая что-то потенциально опасное, в то же время преднамеренно не исключая возможность отказа?
jwg 30.01.2013 16:02:07
Ваша философия плохо понимает исключения. Большинство людей думают, что исключения означают «святое дерьмо из-за-судьбы-что-то-неправильно-умри-умри-умри». Когда исключение означает .... исключение. Это означает, что произошло нечто исключительное, что вам нужно «обработать» (или учесть). Возможно, вы хотите продолжать повторную попытку доступа к данным, возможно, пользователь должен знать, что вы не можете установить соединение. Чем ты занимаешься? Вы обрабатываете исключение ConnectionFailedException и уведомляете пользователя, поэтому, возможно, они прекратят попытки через час и заметят, что кабель отключен.
Lee Louviere 11.03.2013 15:28:06
18 ОТВЕТОВ
РЕШЕНИЕ

Обновлено ПРИМЕЧАНИЕ об этом решении : проверка с использованием FileAccess.ReadWriteфайлов, доступных только для чтения, завершится неудачно, поэтому решение было изменено для проверки FileAccess.Read. Хотя это решение работает, потому что попытка проверки FileAccess.Readне удастся, если файл имеет блокировку записи или чтения, однако, это решение не будет работать, если у файла нет блокировки записи или чтения, т.е. он был открыт (для чтения или записи) с доступом FileShare.Read или FileShare.Write.

ОРИГИНАЛ: Я использовал этот код в течение последних нескольких лет, и у меня не было никаких проблем с ним.

Поймите, что вы колеблетесь с использованием исключений, но вы не можете избежать их все время:

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;
}
534
7.11.2019 17:45:47
Это отличное решение, но у меня есть один комментарий - вы, возможно, не захотите открывать файл с режимом доступа FileAccess.Read, поскольку ReadWrite всегда завершится ошибкой, если файл будет доступен только для чтения.
adeel825 29.01.2010 01:28:30
-1. Это плохой ответ, потому что файл может быть заблокирован другим потоком / процессом после его закрытия в IsFileLocked и до того, как ваш поток получит возможность открыть его.
Polyfun 26.05.2010 15:58:41
Я думаю, что это отличный ответ. Я использую это как метод расширения а-ля public static bool IsLocked(this FileInfo file) {/*...*/}.
Manuzor 4.07.2012 11:06:47
@ChrisW: вам может быть интересно, что происходит. Не пугайтесь. Вы просто подвержены гневу сообщества Daily WTF: thedailywtf.com/Comments/…
Pierre Lebeaupin 11.03.2013 13:15:48
@ChrisW Почему это плохо. Это сообщество здесь, чтобы указать на хорошие и плохие ответы. Если группа профессионалов заметит, что это плохо, и присоединиться к downvote, то сайт WAI. И прежде чем вы получите отрицательный результат, если вы прочитаете эту статью, они скажут: «Подчеркни правильный ответ», а не понизь неправильный. Вы хотите, чтобы они также объясняли свои комментарии в комментариях. Спасибо за то, что познакомили меня с другим хорошим сайтом!
Lee Louviere 11.03.2013 15:12:40

единственный известный мне способ - использовать API эксклюзивной блокировки Win32, который не слишком быстр, но примеры существуют.

Большинство людей, для простого решения этой проблемы, просто пробуют / ловят / спят петлями.

4
18.05.2009 06:42:31
Вы не можете использовать этот API без предварительного открытия файла, после чего вам больше не нужно.
Harry Johnston 7.09.2017 21:15:55
Файл Шредингера.
TheLastGIS 18.02.2020 08:45:21

Возможно, вы могли бы использовать FileSystemWatcher и наблюдать за событием Changed.

Я сам этим не пользовался, но, возможно, стоит попробовать. Если файловый системный наблюдатель окажется немного тяжелым для этого случая, я бы пошел на цикл try / catch / sleep.

7
18.05.2009 06:52:09
Использование FileSystemWatcher не помогает, потому что события Created и Changed возникают в начале создания / изменения файла. Даже небольшие файлы требуют больше времени для записи и закрытия операционной системой, чем приложение .NET для запуска через обратный вызов FileSystemEventHandler. Это так грустно, но нет другого выбора, кроме как оценить время ожидания перед доступом к файлу или запуститься в циклах исключений ...
user219337 8.07.2011 12:29:25
FileSystemWatcher не очень хорошо обрабатывает множество изменений одновременно, поэтому будьте осторожны с этим.
Ben F 20.06.2012 07:59:51
Кстати, вы, ребята, заметили во время отладки и просмотра потоков, что MS называет свою собственную FSW "FileSystemWather"? Что везет в любом случае?
devlord 22.04.2013 00:29:23
Я получил именно эту проблему, потому что служба Windows с FileSystemWatcher пытается прочитать файл, прежде чем процесс закрыл его.
freedeveloper 8.08.2018 15:09:06

Вы можете страдать от состояния гонки потоков, в котором есть документированные примеры использования в качестве уязвимости безопасности. Если вы проверите, что файл доступен, но затем попытаетесь использовать его, вы можете выбросить его в этот момент, который злоумышленник может использовать для принудительного использования и использования вашего кода.

Ваша лучшая ставка - это попытка catch / finally, которая пытается получить дескриптор файла.

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.
}
565
12.03.2013 16:58:55
+1. Не существует 100% безопасного способа «узнать, используется ли файл», потому что миллисекунды после того, как вы выполните проверку, файл может больше не использоваться, или наоборот. Вместо этого вы просто открываете файл и используете его, если нет исключений.
Sedat Kapanoglu 4.01.2011 07:16:05
Жаль, что .NET не поддерживает CAS. Что-то вроде TryOpenFile (Ref FileHandle), который возвращает успех / неудачу. Всегда должен быть обходной путь, а не полагаться только на обработку исключений. Интересно, как это делает Microsoft Office.
TamusJRoyce 31.07.2011 22:37:20
Здесь важно понять, что этот API просто использует Windows API для получения дескриптора файла. Как таковые, они должны преобразовать код ошибки, полученный от C API, и обернуть его в исключение, которое нужно выбросить. У нас есть обработка исключений в .Net, так почему бы не использовать ее. Таким образом, вы можете написать чистый прямой путь в своем коде и оставить обработку ошибок в отдельном пути кода.
Spence 1.08.2011 23:52:55
Оператор using должен гарантировать, что поток будет закрыт после того, как я закончу. Я думаю, вы обнаружите, что using () {} содержит меньше символов, чем try {} finally {obj.Dispose ()}. Вы также обнаружите, что теперь вам нужно объявить ссылку на ваш объект вне оператора using, который является более типичным. Если у вас есть явный интерфейс, вам также придется использовать приведение. Наконец, вы хотите избавиться как можно скорее, и логика finally может иметь пользовательский интерфейс или любые другие длительные действия, которые имеют мало общего с вызовом IDispose. </ rant>
Spence 27.11.2012 20:01:51
это не отменяет того факта, что вы должны объявить свой объект вне попытки и должны явно вызвать dispose, что с помощью делает для вас и означает то же самое.
Spence 31.01.2013 00:27:45

Попробуйте переместить / скопировать файл во временную папку. Если вы можете, у него нет блокировки, и вы можете безопасно работать в временном каталоге, не получая блокировки. В противном случае просто попробуйте переместить его снова через x секунд.

-2
18.05.2009 09:11:04
@jcolebrand что блокирует? тот, который вы скопировали? Или тот, который вы положили в временный каталог?
Cullub 7.08.2014 17:23:24
если вы копируете файл, ожидая, что никто другой не будет над ним работать, и вы собираетесь использовать временный файл, а затем кто-то заблокирует его сразу после того, как вы скопируете его, то вы потенциально потеряете данные.
jcolebrand 7.08.2014 21:07:46
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");

Надеюсь это поможет!

4
22.08.2011 17:30:16
Фактическая проверка, которую вы выполняете, в порядке; помещение его внутрь функции вводит в заблуждение. Вы НЕ хотите использовать такую ​​функцию перед открытием файла. Внутри функции файл открывается, проверяется и закрывается. Затем программист предполагает, что файл все еще в порядке, и пытается открыть его для использования. Это плохо, потому что он может быть использован и заблокирован другим процессом, который был поставлен в очередь для открытия этого файла. Между 1-м разом, когда он был открыт (для проверки), и 2-м разом, когда он был открыт (для использования), ОС могла отложить планирование вашего процесса и запустить другой процесс.
Lakey 25.03.2013 19:50:23

Я использую этот обходной путь, но у меня есть промежуток времени между проверкой блокировки файла с помощью функции IsFileLocked и открытием файла. В этот промежуток времени файл может открыть какой-то другой поток, поэтому я получу IOException.

Итак, я добавил дополнительный код для этого. В моем случае я хочу загрузить 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!!!!!");
            }
        }

Что вы думаете? Могу ли я что-то изменить? Может быть, мне не нужно было использовать функцию IsFileBeingUsed вообще?

Спасибо

-3
17.01.2012 15:45:31
Что такое IsFileBeingUsed? Исходный код о IsFileBeingUsed?
Kiquenet 30.07.2013 06:01:09

Используйте это, чтобы проверить, заблокирован ли файл:

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;
}
}

Из соображений производительности я рекомендую прочитать содержимое файла в той же операции. Вот некоторые примеры:

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;
}

Попробуйте сами:

byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");
91
20.04.2015 07:06:55
Я бы поддержал, если бы там не было так много «магических чисел» en.wikipedia.org/wiki/Magic_number_(programming)
Kris 11.03.2013 21:27:45
Я имел в виду сравнения кодов ошибок, а не сдвиги битов. хотя теперь вы упоминаете об этом ...
Kris 15.03.2013 09:35:17
Ваш Catch должен быть включен IOException, а не на общем, Exceptionа затем тест на тип.
Askolein 22.04.2013 14:31:53
@JeremyThompson к сожалению, вы ставите конкретное IOExceptionпосле общего. Общий поймает все, что проходит, а конкретный IOExceptionвсегда будет одинок. Просто поменяйте местами.
Askolein 18.06.2013 13:48:17
Мне нравится это решение. Еще одно предложение: внутри catch как else для if (IsFileLocked (ex)) я бы бросил ex. Это тогда обработает случай, где файл не существует (или любое другое IOException), бросая исключение.
shindigo 18.04.2014 20:33:34

По моему опыту, вы обычно хотите сделать это, затем «защитить» свои файлы, чтобы сделать что-то причудливое, а затем использовать «защищенные» файлы. Если у вас есть только один файл, который вы хотите использовать подобным образом, вы можете воспользоваться трюком, объясненным в ответе Джереми Томпсона. Однако, если вы попытаетесь сделать это с большим количеством файлов (скажем, например, когда пишете установщик), вам будет очень больно.

Очень элегантный способ решить эту проблему - использовать тот факт, что ваша файловая система не позволит вам изменить имя папки, если один из файлов там используется. Держите папку в той же файловой системе, и она будет работать как шарм.

Обратите внимание, что вы должны знать об очевидных способах его использования. В конце концов, файлы не будут заблокированы. Также имейте в виду, что есть и другие причины, которые могут привести Moveк сбою в работе. Очевидно, что правильная обработка ошибок (MSDN) может помочь здесь.

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);
}

Что касается отдельных файлов, я бы придерживался предложения по блокировке от Джереми Томпсона.

2
21.11.2014 11:10:11
Привет, Так как порядок ответов меняется, вы можете уточнить, какой пост выше вы имеете в виду для читателей этого популярного QA. Спасибо.
Jeremy Thompson 21.11.2014 11:02:32
@ JeremyThompson Вы правы, спасибо, я буду редактировать пост. Я бы использовал решение от вас, в основном из-за вашего правильного использования FileShareи проверки блокировки.
atlaste 21.11.2014 11:09:25

Просто используйте исключение по назначению. Подтвердите, что файл используется, и повторите попытку несколько раз, пока ваше действие не будет завершено. Это также наиболее эффективно, потому что вы не тратите время на проверку состояния, прежде чем действовать.

Используйте функцию ниже, например

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

Повторно используемый метод, который истекает через 2 секунды

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);
}
7
26.05.2015 15:29:09

Принятые ответы, приведенные выше, имеют проблему, когда файл, открытый для записи в режиме FileShare.Read, или файл с атрибутом «Только чтение», код не будет работать. Это модифицированное решение работает наиболее надежно, имея в виду две вещи (как и для принятого решения):

  1. Он не будет работать для файлов, которые были открыты в режиме записи с общим доступом
  2. Это не учитывает проблемы с многопоточностью, поэтому вам придется заблокировать их или обрабатывать проблемы с потоками отдельно.

Помня вышеупомянутое, это проверяет, заблокирован ли файл для записи или заблокирован, чтобы предотвратить чтение :

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;
}
3
15.10.2015 14:31:30
Все еще имеет ту же проблему, что и принятый ответ - он только сообщает вам, был ли файл заблокирован другим процессом в определенный момент времени , что не является полезной информацией. К тому времени, когда функция вернула результат, он может быть уже устаревшим!
Harry Johnston 16.10.2015 19:57:42
это правда, можно проверить только в любой данный момент времени (или подписаться на события), преимущество этого подхода перед принятым решением состоит в том, что он может проверять атрибут только для чтения и блокировку записи и не возвращать ложное срабатывание.
rboy 17.10.2015 03:03:43

Вы можете вернуть задачу, которая даст вам поток, как только он станет доступным. Это упрощенное решение, но это хорошая отправная точка. Это потокобезопасно.

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();
    }
}

Вы можете использовать этот поток как обычно:

using (var stream = await FileStreamGetter.GetStreamAsync())
{
    Console.WriteLine(stream.Length);
}
6
20.10.2015 12:33:01
Сколько секунд до переполнения стека от рекурсии GetStreamAsync()?
CAD bloke 24.07.2017 11:14:05
@CADbloke, вы подняли очень хороший вопрос. Действительно, мой образец может иметь исключение переполнения стека, если файл не доступен в течение длительного времени. В связи с этим ответом stackoverflow.com/questions/4513438/… может вызвать исключение через 5 часов.
Ivan Branets 26.07.2017 08:51:07
Что касается ваших вариантов использования, то предпочтительнее выдать исключение ввода-вывода, если, скажем, 10 попыток чтения файла не увенчались успехом. Другой стратегией может быть увеличение времени ожидания на секунду после 10 неудачных попыток. Вы также можете использовать смесь обоих.
Ivan Branets 26.07.2017 08:59:46
Я бы (и сделал) просто предупредить пользователя, что файл заблокирован. Обычно они сами запираются, поэтому, вероятно, что-то с этим сделают. Или нет.
CAD bloke 26.07.2017 12:25:17
В некоторых случаях вам нужно использовать политику повторных попыток, поскольку файл может быть еще не готов. Представьте себе настольное приложение для загрузки изображений во временную папку. Приложение начинает загрузку, и в то же время вы открываете эту папку в проводнике. Windows хочет немедленно создать миниатюру и заблокировать файл. В то же время ваше приложение пытается заменить заблокированное изображение на другое место. Вы получите исключение, если не используете политику повторных попыток.
Ivan Branets 31.07.2017 13:16:47

Вот код, который, насколько я могу судить, делает то же самое, что и принятый ответ, но с меньшим количеством кода:

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

Однако я думаю, что это более надежно сделать следующим образом:

    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);
    }
2
22.08.2016 20:46:36

Вы можете использовать мою библиотеку для доступа к файлам из нескольких приложений.

Вы можете установить его из nuget: Install-Package Xabe.FileLock

Если вы хотите больше информации об этом, проверьте https://github.com/tomaszzmuda/Xabe.FileLock

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

Метод fileLock.Acquire вернет true, только если может заблокировать файл исключительно для этого объекта. Но приложение, которое загружает файл, должно делать это и при блокировке файла. Если объект недоступен, метод возвращает false.

2
7.09.2017 10:49:34
Пожалуйста, не размещайте какой-либо инструмент или библиотеку в качестве ответа. По крайней мере, продемонстрируйте, как это решает проблему в самом ответе.
paper1111 7.09.2017 10:45:25
Добавлена ​​демоверсия :) Извините @ paper1111
Tomasz Żmuda 7.09.2017 10:50:04
Требуются все процессы, которые используют файл для совместной работы. Маловероятно, чтобы быть применимы к оригинальной проблемы ОП.
Harry Johnston 7.09.2017 21:17:32

Мне интересно посмотреть, вызывает ли это какие-либо рефлексы WTF. У меня есть процесс, который создает и впоследствии запускает документ PDF из консольного приложения. Однако я имел дело с уязвимостью, когда, если бы пользователь запускал процесс несколько раз, генерируя один и тот же файл, не закрывая предварительно сгенерированный файл, приложение выдает исключение и умирает. Это было довольно частым явлением, потому что имена файлов основаны на номерах коммерческих предложений.

Вместо того, чтобы терпеть неудачу в такой неблагодарной манере, я решил положиться на автоматическое добавление версий файлов:

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);
    }
}

Возможно, catchблоку нужно уделить больше внимания, чтобы убедиться, что я ловлю правильные исключения IOException. Я, вероятно, также очистлю хранилище приложения при запуске, так как эти файлы в любом случае должны быть временными.

Я понимаю, что это выходит за рамки вопроса ОП о простой проверке, используется ли файл, но это действительно была проблема, которую я искал, когда приехал сюда, так что, возможно, это будет полезно для кого-то еще.

0
5.04.2018 12:07:23

Помимо работы с 3-строчками и просто для справки: если вы хотите получить полную информацию - в Microsoft Dev Center есть небольшой проект:

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

Из введения:

Пример кода C #, разработанный в .NET Framework 4.0, поможет выяснить, какой процесс блокирует файл. Функция RmStartSession , включенная в rstrtmgr.dll, используется для создания сеанса менеджера перезапуска, и в соответствии с возвращаемым результатом создается новый экземпляр объекта Win32Exception. После регистрации ресурсов на сессию Restart Manager через RmRegisterRescources функции, RmGetList функция вызывается , чтобы проверить , какие приложения используют конкретный файл, перечисляя RM_PROCESS_INFO массив.

Он работает, подключившись к «Перезапустить сеанс менеджера».

Диспетчер перезапуска использует список ресурсов, зарегистрированных в сеансе, чтобы определить, какие приложения и службы должны быть закрыты и перезапущены. Ресурсы можно идентифицировать по именам файлов, коротким именам служб или структурам RM_UNIQUE_PROCESS, которые описывают работающие приложения.

Это может быть немного слишком сильно для ваших конкретных нужд ... Но если это то, что вы хотите, продолжайте и возьмите vs-проект.

3
9.10.2018 06:56:20

Поможет ли что-нибудь подобное?

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)
    {

    }
}
0
9.04.2019 12:48:56

Мне когда-то нужно было загружать PDF-файлы в онлайн-архив. Но резервное копирование не удастся, если пользователь откроет файл в другой программе (например, в программе чтения PDF). В спешке я попытался ответить на несколько лучших вопросов в этой теме, но не смог заставить их работать. Что мне помогло, так это попытка переместить файл PDF в его собственный каталог . Я обнаружил, что это не получится, если файл будет открыт в другой программе, и если перемещение будет успешным, операция восстановления не потребуется, как если бы он был перемещен в отдельный каталог. Я хочу опубликовать свое базовое решение на случай, если оно может быть полезно для других конкретных случаев использования.

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
}
2
16.08.2019 01:40:20