Подождите, пока файл будет освобожден процессом

Как подождать, пока файл станет свободным, чтобы ss.Save() мог перезаписать его новым. Если я запустил это дважды близко (ish), я получаю ошибку generic GDI+.

    ///<summary>
    /// Grabs a screen shot of the App and saves it to the C drive in jpg
    ///</summary>
    private static String GetDesktopImage(DevExpress.XtraEditors.XtraForm whichForm)
    {
        Rectangle bounds = whichForm.Bounds;

        // This solves my problem but creates a clutter issue
        //var timeStamp = DateTime.Now.ToString("ddd-MMM-dd-yyyy-hh-mm-ss");
        //var fileName = "C:\\HelpMe" + timeStamp + ".jpg";

        var fileName = "C:\\HelpMe.jpg";
        File.Create(fileName);
        using (Bitmap ss = new Bitmap(bounds.Width, bounds.Height))
        using (Graphics g = Graphics.FromImage(ss))
        {
            g.CopyFromScreen(whichForm.Location, Point.Empty, bounds.Size);
            ss.Save(fileName, ImageFormat.Jpeg);
        }

        return fileName;
    }
+22
источник поделиться
7 ответов

Функция, подобная этой, сделает это:

    public static bool IsFileReady(String sFilename)
    {
        // If the file can be opened for exclusive access it means that the file
        // is no longer locked by another process.
        try
        {
            using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
            {
                if (inputStream.Length > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }

            }
        }
        catch (Exception)
        {
            return false;
        }
    }

Вставьте его в цикл while, и у вас есть что-то, что будет заблокировано до тех пор, пока файл не будет доступен

+44
источник

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

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

получение потока

private FileStream GetWriteStream(string path, int timeoutMs)
{
    var time = Stopwatch.StartNew();
    while (time.ElapsedMilliseconds < timeoutMs)
    {
        try
        {
            return new FileStream(path, FileMode.Create, FileAccess.Write);
        }
        catch (IOException e)
        {
            // access error
            if (e.HResult != -2147024864)
                throw;
        }
    }

    throw new TimeoutException($"Failed to get a write handle to {path} within {timeoutMs}ms.");
}

то используйте его следующим образом:

using (var stream = GetWriteStream("path"))
{
    using (var writer = new StreamWriter(stream))
        writer.Write("test");
}

область повтора

private void WithRetry(Action action, int timeoutMs = 1000)
{
    var time = Stopwatch.StartNew();
    while(time.ElapsedMilliseconds < timeoutMs)
    {
        try
        {
            action();
            return;
        }
        catch (IOException e)
        {
            // access error
            if (e.HResult != -2147024864)
                throw;
        }
    }
    throw new Exception("Failed perform action within allotted time.");
}

а затем используйте WithRetry (() = > File.WriteAllText(Path.Combine(_directory, name), содержимое));

+8
источник
другие ответы

Связанные вопросы


Похожие вопросы

bool isLocked = true;
while (isLocked)
 try {
  System.IO.File.Move(filename, filename2);
  isLocked = false;
 }
 catch { }
 System.IO.File.Move(filename2, filename);
+3
источник

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

+2
источник

Вы можете позволить системе ждать, пока процесс не будет закрыт.

Так же просто:

Process.Start("the path of your text file or exe").WaitForExit();

+2
источник

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

Пользователь регистрирует файлы, которые они хотели бы просмотреть, вызывая FileAccessWatcher.RegisterWaitForFileAccess(filePath). Если файл еще не просматривается, запускается новая задача, которая повторно проверяет файл, чтобы узнать, можно ли его открыть. Каждый раз, когда он проверяет, он также считывает размер файла. Если размер файла не увеличивается в заданное время (5 минут в моем примере), цикл завершается.

Когда цикл выходит из файла, доступного или из таймаута, запускается событие FileFinishedCopying.

public class FileAccessWatcher
{
    // this list keeps track of files being watched
    private static ConcurrentDictionary<string, FileAccessWatcher> watchedFiles = new ConcurrentDictionary<string, FileAccessWatcher>();

    public static void RegisterWaitForFileAccess(string filePath)
    {
        // if the file is already being watched, don't do anything
        if (watchedFiles.ContainsKey(filePath))
        {
            return;
        }
        // otherwise, start watching it
        FileAccessWatcher accessWatcher = new FileAccessWatcher(filePath);
        watchedFiles[filePath] = accessWatcher;
        accessWatcher.StartWatching();
    }

    /// <summary>
    /// Event triggered when the file is finished copying or when the file size has not increased in the last 5 minutes.
    /// </summary>
    public static event FileSystemEventHandler FileFinishedCopying;

    private static readonly TimeSpan MaximumIdleTime = TimeSpan.FromMinutes(5);

    private readonly FileInfo file;

    private long lastFileSize = 0;

    private DateTime timeOfLastFileSizeIncrease = DateTime.Now;

    private FileAccessWatcher(string filePath)
    {
        this.file = new FileInfo(filePath);
    }

    private Task StartWatching()
    {
        return Task.Factory.StartNew(this.RunLoop);
    }

    private void RunLoop()
    {
        while (this.IsFileLocked())
        {
            long currentFileSize = this.GetFileSize();
            if (currentFileSize > this.lastFileSize)
            {
                this.lastFileSize = currentFileSize;
                this.timeOfLastFileSizeIncrease = DateTime.Now;
            }

            // if the file size has not increased for a pre-defined time limit, cancel
            if (DateTime.Now - this.timeOfLastFileSizeIncrease > MaximumIdleTime)
            {
                break;
            }
        }

        this.RemoveFromWatchedFiles();
        this.RaiseFileFinishedCopyingEvent();
    }

    private void RemoveFromWatchedFiles()
    {
        FileAccessWatcher accessWatcher;
        watchedFiles.TryRemove(this.file.FullName, out accessWatcher);
    }

    private void RaiseFileFinishedCopyingEvent()
    {
        FileFinishedCopying?.Invoke(this,
            new FileSystemEventArgs(WatcherChangeTypes.Changed, this.file.FullName, this.file.Name));
    }

    private long GetFileSize()
    {
        return this.file.Length;
    }

    private bool IsFileLocked()
    {
        try
        {
            using (this.file.Open(FileMode.Open)) { }
        }
        catch (IOException e)
        {
            var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);

            return errorCode == 32 || errorCode == 33;
        }

        return false;
    }
}

Пример использования:

// register the event
FileAccessWatcher.FileFinishedCopying += FileAccessWatcher_FileFinishedCopying;

// start monitoring the file (put this inside the OnChanged event handler of the FileSystemWatcher
FileAccessWatcher.RegisterWaitForFileAccess(fileSystemEventArgs.FullPath);

Обработать файл FileFinishedCopyingEvent:

private void FileAccessWatcher_FileFinishedCopying(object sender, FileSystemEventArgs e)
{
    Console.WriteLine("File finished copying: " + e.FullPath);
}
+2
источник

Вы можете использовать оператор блокировки с переменной Dummy, и, похоже, она отлично работает.

Проверьте здесь.

0
источник

Посмотрите другие вопросы по меткам или Задайте вопрос