Поведение удаленных файлов, открытых с помощью FileShare.Delete, изменилось на Windows? - PullRequest
2 голосов
/ 27 февраля 2020

Мы использовали следующий код в течение нескольких лет.

    /// <summary>
    /// Opens a file and returns an exclusive handle. The file is deleted as soon as the handle is released.
    /// </summary>
    /// <param name="path">The name of the file to create</param>
    /// <returns>A FileStream backed by an exclusive handle</returns>
    /// <remarks>If another process attempts to open this file, they will recieve an UnauthorizedAccessException</remarks>
    public static System.IO.FileStream OpenAsLock(string path)
    {
        var stream = TranslateIOExceptions(() => System.IO.File.Open(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.Delete));
        System.IO.File.Delete(path);
        return stream;
    }

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

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

Сможем ли мы когда-нибудь удалить открытый файл в Windows?

Может ли использование FileShare.Delete вызвать исключение UnauthorizedAccessException?

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

class Program
{
    static void Main(string[] args)
    {
        File.Open("test", FileMode.OpenOrCreate, FileAccess.Write, FileShare.Delete);
        File.Delete("test");
        File.WriteAllText("test", "hello world");
        Console.Write(File.ReadAllText("test"));
        Console.ReadLine();
    }
}

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

Было ли это реальным изменением в поведении? Мы знаем, когда это случилось? Было ли это умышленно (задокументировано)?

1 Ответ

0 голосов
/ 03 марта 2020

Большое спасибо Эрику за подсказку.

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

Модульные тесты еще не подняли тревогу, потому что наша тестовая машина работала со старой версией Windows 10, чем моя машина разработки.

  • Сборка 17134.1304 определенно имеет старое поведение.
  • Сборка 18363.657 определенно имеет новое поведение.

Я просмотрел список выпусков сборки и, к сожалению, между этими двумя версиями было более двух десятков выпусков. Однако я очень подозрительно отношусь к этому «Усовершенствованию и исправлению», указанному как часть build 17763.832, доступно 15 октября 2019

Устранена проблема, из-за которой файлы, хранящиеся в общий том кластера (CSV) с альтернативным потоком данных все еще присутствует после того, как вы попытаетесь удалить их. Вы также можете получить сообщение «Отказано в доступе» при следующей попытке доступа или удаления файлов.

Я не уверен, почему изменение c в CSV влияет на мою систему, но описание точно соответствует изменению, которое я вижу.


Что касается указанного кода c, оказалось, что возвращение FileStream никогда не использовалось в нашем коде. Вместо этого мы полагались на интерфейс IDisposable, закрывая поток после завершения «критической секции» и разблокируя общий файл.

С технической точки зрения, я теперь делаю следующее:

  1. Создание файла блокировки с эксклюзивным дескриптором
  2. Возвращение нового объекта, реализующего IDisposable
  3. Подождите, пока удаляется одноразовый объект, затем закройте поток и try удалить файл
// ...
    public static IDisposable OpenAsLock(string path)
    {
        var stream = TranslateIOExceptions(() => System.IO.File.Open(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.None));
        return new FileBasedLock(stream, path);
    }
// ...

internal class FileBasedLock : IDisposable
{
    public FileBasedLock(FileStream stream, string path)
    {
        Stream = stream ?? throw new System.ArgumentNullException(nameof(stream));
        Path = path ?? throw new System.ArgumentNullException(nameof(path));
    }

    public FileStream Stream { get; }
    public string Path { get; }
    public void Dispose()
    {
        Stream.Close();
        try { File.Delete(Path); }
        catch (IOException) { }
    }
}
...