Miro
Miro

Reputation: 1806

Waiting for system to delete file

I had a problem with refreshing file list after deleting a file. When I gave command to delete file, the exception was thrown because the refresh method tried to access a file that was supposed to be deleted.

After some thought and debuging I came to conclusion that problem was in a fact that system needs some time to delete a file. And I solve it like this:

//Deleting file
System.Threading.Thread.Sleep(2000);
//Refreshing list

and it worked fine.

My question is

Is there a more elegant way to wait for system do delete file and then continue with code...?

Upvotes: 24

Views: 20761

Answers (7)

Vadim
Vadim

Reputation: 137

I always used this:

System.GC.Collect();
System.GC.WaitForPendingFinalizers();

See here and here

Upvotes: 1

Mike G
Mike G

Reputation: 221

This works for me:

public static void DeleteFile(String fileToDelete)
{
    var fi = new System.IO.FileInfo(fileToDelete);
    if (fi.Exists)
    {
        fi.Delete();
        fi.Refresh();
        while (fi.Exists)
        {    System.Threading.Thread.Sleep(100);
             fi.Refresh();
        }
    }
}

I find that most of the time, the while loop will not be entered.

Upvotes: 22

JPK
JPK

Reputation: 793

Lightweight code to use a FileSystemWatcher, subscribe to its Deleted event and wait.

void DeleteFileAndWait(string filepath, int timeout = 30000)
{
    using (var fw = new FileSystemWatcher(Path.GetDirectoryName(filepath), Path.GetFileName(filepath)))
    using (var mre = new ManualResetEventSlim())
    {
        fw.EnableRaisingEvents = true;
        fw.Deleted += (object sender, FileSystemEventArgs e) =>
        {
            mre.Set();
        };
        File.Delete(filepath);
        mre.Wait(timeout);
    }
}

Upvotes: 10

bradgonesurfing
bradgonesurfing

Reputation: 32182

Here is some code using the FileWatcher. What we want to be able to do is

await Utils.DeleteDirectoryAsync("c:\temp\foo", recurse: true);

the below implements it

using System;
using System.IO;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;

namespace Utils
{
    internal class FileWatcher : IDisposable
    {
        readonly FileSystemWatcher _Watcher;

        public Subject<FileSystemEventArgs> Changed = new Subject<FileSystemEventArgs>();

        public FileWatcher( string file )
        {
            // Create a new FileSystemWatcher and set its properties.
            _Watcher = new FileSystemWatcher
                       {
                           Path = Path.GetDirectoryName(file),
                           NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                                          | NotifyFilters.FileName | NotifyFilters.DirectoryName,
                           Filter =Path.GetFileName(file) 
                       };

            // Add event handlers.
            _Watcher.Changed += OnChanged;
            _Watcher.Created += OnChanged;
            _Watcher.Deleted += OnChanged;
            _Watcher.Renamed += OnChanged;

            // Begin watching.
            _Watcher.EnableRaisingEvents = true;
        }

        // Define the event handlers.
        private void OnChanged( object source, FileSystemEventArgs e )
        {
            Changed.OnNext(e);
        }


        public void Dispose()
        {
            _Watcher.Dispose();
        }
    }
}

and some utils that take advantage of the above observable.

public static class FileUtils
{
    public static IObservable<FileSystemEventArgs> ChangedObservable(string path)
    {
        if (path == null)
            return Observable.Never<FileSystemEventArgs>();

        return Observable.Using(() => new FileWatcher(path), watcher => watcher.Changed);
    }

    public static Task DeleteDirectoryAsync(string path, bool recurse)
    {
        var task = new TaskCompletionSource<Unit>();

        if (Directory.Exists(path))
        {
            ChangedObservable(path)
                .Where(f => f.ChangeType == WatcherChangeTypes.Deleted)
                .Take(1)
                .Subscribe(v => task.SetResult(Unit.Default));

            Directory.Delete(path, recurse);
        }
        else
        {
            task.SetResult(Unit.Default);
        }

        return task.Task;
    }
}

Upvotes: 4

Valerie
Valerie

Reputation: 11

Directory.Delete will throw an exception on the first error it encounters. If you want to continue deleting as many files and subdirectories as you can then you shouldn't use Directory.Delete and should write your own recursive delete with try/catch blocks inside the loops. An example when you might want to do that is if you are trying to cleanup temp files and one of the files has been locked.

Upvotes: 1

GETah
GETah

Reputation: 21419

The most elegant way I can think of is using a FileSystemWatcher and subscribe to its Deleted event.

Upvotes: 7

Chris Shain
Chris Shain

Reputation: 51339

Deleting a directory using Directory.Delete, specifically the overload that takes a 'recursive' boolean, on NTFS, should be an atomic operation from your program's perspective. No need to recurse manually yourself.

Upvotes: -1

Related Questions