Почему после Directory.Delete выбрасывает IOException Каталог не пустой - PullRequest
0 голосов
/ 24 октября 2019

Я создаю каталог и настраиваю FileSystemWatcher на нем. Затем я создаю файл. Все это делается в конструкторе форм. В обработчике события кнопки я удаляю созданный выше каталог. Иногда выдает IOException: каталог не пустой. После этого я даже не могу получить доступ к дочернему каталогу в проводнике. Я продолжаю получать ошибки «Отказано в доступе». Этот каталог удаляется после завершения моего процесса. AFAIK FileSystemWatcher не должен блокировать каталог.


        string alphaBeta = @"Alpha\Beta";
        public Form1()
        {
            InitializeComponent();
            Directory.CreateDirectory(alphaBeta);
            FileSystemWatcher watcher = new FileSystemWatcher()
            {
                Path = alphaBeta,
                Filter = "*.dat",
                NotifyFilter = NotifyFilters.FileName
            };
            watcher.EnableRaisingEvents = true;
            File.WriteAllText(alphaBeta + @"\Gamma.dat", "Delta");
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            Directory.Delete("Alpha", true);//Recursively Delete
        }

Как правильно удалить каталог без застревания каталога?

ОБНОВЛЕНИЕ : Выше приведен минимальный воспроизводимый пример

Мой реальный сценарий включает в себя надстройку, загруженную в explorer.exe, которая отслеживает изменения в каталоге конфигурации. Он подключается к событиям создания и переименования FSW.

Код удаления выполняется в программе удаления. Деинсталлятор не должен убивать проводника. Мы думали, что FileSystemWatcher не заблокирует папку и будет молча останавливать мониторинг изменений после удаления каталога. Но, похоже, это не так.

Ответы [ 2 ]

2 голосов
/ 24 октября 2019

Проблема вызвана тем, что FSW сохраняет дескриптор открытого каталога , пока включены события . Исходный код показывает , что отключение событий также закрывает дескриптор. Чтобы удалить каталог, FSW должен быть отключен.

Вторая проблема, однако, заключается в том, что FileSystemWatcher watcher определяется как переменная внутри конструктора Form1(), что означает, что она является потерянной и сразу же доступна для сборки мусора еще до отображения формы. Однако сборщик мусора запускается не часто, а это значит, что нет гарантии, что он будет работать до нажатия Delete. Поскольку наблюдатель нигде не хранится, его уже нельзя отключить.

По крайней мере, FSW необходимо сохранить в поле, а события перед удалением следует отключить. Мы также должны убедиться, что наблюдатель удаляется при удалении самой формы:

public class Form1
{
    FileSystemWatcher _watcher;

    public Form1()
    {
        ...
        _watcher=CreateDormantWatcher(path,pattern);
        _watcher.EnableRaisingEvents=true ;
    }

    private void btnDelete_Click(object sender, EventArgs e)
    {
        _watcher.EnableRaisingEvents =false;
        Directory.Delete("Alpha", true);//Recursively Delete
    }

    protected override void Dispose (bool disposing)
    {
        base.Dispose(disposing);
        if (disposing)
        {
             _watcher.Dispose();
        }
        _watcher=watcher;
    }

    FileSystemWatcher CreateDormantWatcher(string path,string pattern)
    {
        //Don't store to the field until the FSW is 
        //already configured
        var watcher=new FileSystemWatcher()
        {
            Path = path,
            Filter = "pattern,
            NotifyFilter = NotifyFilters.FileName
        };
        watcher.Changed += OnChanged;
        watcher.Created += OnCreated;
        watcher.Deleted += OnChanged;
        watcher.Renamed += OnRenamed;
        return watcher;
    }

Простое исправление: добавьте его в качестве компонента

Возможно, лучшая идея,было бы добавить FileSystemWatcher на форму в качестве компонента. FileSystemWatcher наследуется от Component, что означает, что при помещении его в форму добавьте код создания и конфигурации в InitializeComponents(). Он также будет утилизирован при утилизации всех других компонентов.

Если мы сделаем это, нам просто нужно будет переключить EnableRaisingEvents, когда это необходимо.

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

    public Form1()
    {
        InitializeComponent();
        Directory.CreateDirectory(FileSystemWatcher1.Path);
        FileSystemWatcher1.EnableRaisingEvents = true;
        File.WriteAllText(alphaBeta + @"\Gamma.dat", "Delta");
    }

    private void btnDelete_Click(object sender, EventArgs e)
    {
        FileSystemWatcher1.EnableRaisingEvents = false;
        Directory.Delete("Alpha", true);//Recursively Delete
    }
0 голосов
/ 24 октября 2019

Похоже, это проблема с FileSystemWatcher. Если код для этого закомментирован, то исключение не происходит.

Может показаться, что FileSystemWatcher не обязательно удаляется до вызова метода Directory.Delete.

может быть целесообразно обработать FileSystemWatcher так, чтобы он был удален перед удалением ресурсов, от которых он мог зависеть, но в вашем примере кода это легко достигается простым добавлением watcher.Dispose();:

string alphaBeta = @"Alpha\Beta";
public Form1()
{
    InitializeComponent();
    Directory.CreateDirectory(alphaBeta);
    FileSystemWatcher watcher = new FileSystemWatcher()
    {
        Path = alphaBeta,
        Filter = "*.dat",
        NotifyFilter = NotifyFilters.FileName
    };
    watcher.EnableRaisingEvents = true;
    watcher.Dispose();
    File.WriteAllText(alphaBeta + @"\Gamma.dat", "Delta");
}

private void button1_Click(object sender, EventArgs e)
{

    Directory.Delete("Alpha", true);//Recursively Delete
}
...