Странное исключение с доступом к файлам и окнами проводника

Рассмотрим эту простую программу:

private static void Main(string[] args)
{
        var directoryName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Directory");

        if (Directory.Exists(directoryName))
            Directory.Delete(directoryName, true);

        Directory.CreateDirectory(directoryName);

        var stream = File.Create(Path.Combine(directoryName, "File")); //throws
        stream.Close();
}

Это прекрасно работает, пока вы просто выполняете эту программу. Странная вещь происходит, если вы просматриваете этот Directory в проводнике Windows, а затем запускаете. В этом случае я получаю UnautorizedAccessException "Access to the path 'C:\Users\rfurman\AppData\Roaming\Directory\File' is denied."

Если это странно, выполните это с теми же условиями:

private static void Main(string[] args)
{
        var directoryName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Directory");

        if (Directory.Exists(directoryName))
            Directory.Delete(directoryName, true);

        var value = Directory.Exists(directoryName);

        Console.WriteLine(value);
        Console.ReadKey();
 }

Эта программа печатает True, если Directory открыто в проводнике.

Я хотел бы знать, почему это происходит и как защититься от такой ситуации.

Я использую окна 7 и .net 4.


person Rafal    schedule 09.10.2012    source источник
comment
если вы просматриваете этот каталог, что это значит?   -  person Saurabh R S    schedule 09.10.2012
comment
@Luftwaffe: откройте эту папку в проводнике Windows.   -  person Rafal    schedule 09.10.2012
comment
так вы имеете в виду .. папка остается открытой в проводнике .. верно?   -  person Saurabh R S    schedule 09.10.2012
comment
Он открыт во время выполнения программы.   -  person Rafal    schedule 09.10.2012
comment
А папка действительно удаляется?   -  person Milind Thakkar    schedule 12.10.2012
comment
Папка удаляется. Попробуй сам.   -  person Rafal    schedule 12.10.2012
comment
Не удалось воспроизвести [ xkcd.com/583 ] Действительно, я не получаю исключение, каталог получает удалено в первом случае, а во втором - false, независимо от доступа к папке в проводнике. Да, папка остается открытой, даже несмотря на то, что она удаляется и создается снова, просто Explorer слишком быстро ее заметит (вы можете добавить Thread.Sleep(10000) после удаления перед созданием, и Explroer должен покинуть папку). Я использую Windows 7 и .net 4.5. Исправлено в .NET 4.5?   -  person Theraot    schedule 12.10.2012
comment
Эксплорер не проблема. Странность в .net (4 в моем случае) в том, что после заказа удаления папки возвращается, что эта папка существует. И это странное исключение доступа. И упомянутая история просто предполагает плохих тестировщиков.   -  person Rafal    schedule 12.10.2012


Ответы (4)


Directory.Delete внутри использует RemoveDirectory win API в Kernel32. Что делает RemoveDirectory, так это «пометить каталог для удаления». Каталог удаляется, когда закрывается последний дескриптор этого каталога. Я считаю, что это означает «после того, как проводник покинул эту папку»

На моем компьютере такой ситуации не происходит, поэтому я не могу проверить, но я подозреваю, что у вас может быть способ. Системы на базе NT иногда позволяют переименовывать файлы и каталоги, даже если они открыты. Я не знаю точных случаев, когда это разрешено, но я использовал это для переименования загруженных файлов dll и записи новых, например:

File.Rename(@"C:\App\test.dll", @"C:\App\test.dll");
File.Copy(@"C:\App\Update\test.dll-v1.1", @"C:\App\test.dll");

Таким образом, ваш код может выглядеть так после изменения

var directoryName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Directory");

if (Directory.Exists(directoryName)) {
    var randomExt = ".random"; // generate randomly
    Directory.Move(directoryName, directoryName + randomExt)
    Directory.Delete(directoryName + randomExt, true);
}
Directory.CreateDirectory(directoryName);

var stream = File.Create(Path.Combine(directoryName, "File")); //throws
stream.Close();
person Erdogan Kurtur    schedule 12.10.2012
comment
ОК, ваше решение работает, но выглядит как взлом. В документации метода Directory.Delete четко указано, что метод удаляет папку и ее содержимое. Нет никакой информации о том, что это может быть асинхронно. Я просто не могу принять, что это асинхронно. Можете ли вы сослаться на какую-нибудь статью, которая доказывает то, что вы подразумеваете? - person Rafal; 12.10.2012
comment
Извините за разочарование. Чтобы доказать, что Directory.Delete использует RemoveDirectory, вы должны использовать ILSpy. Доступ к документации по RemoveDirectory можно получить по адресу msdn. .microsoft.com/en-us/library/windows/desktop/. Просто посмотрите раздел комментариев. - person Erdogan Kurtur; 12.10.2012

Это своего рода дубликат: Странное поведение при удалении каталога на SSD-накопителе

Проводник просто вызывает немного большую задержку при удалении папки. Удаление каталога не является «точно» синхронной операцией. Каталог помечен для удаления, но фактическое удаление может немного запаздывать.

Насколько я знаю, это существует столько же, сколько и NTFS (win2k/Xp).

person csharptest.net    schedule 12.10.2012

Эта проблема меня тоже удивила. Моя альтернатива, которая представляет собой другой кладж:

    if (Directory.Exists(directoryName))
    {
        Directory.Delete(directoryName, true);
        while (Directory.Exists(directoryName))
            Thread.Sleep(100);
    }

    Directory.CreateDirectory(directoryName);
person JNay    schedule 28.02.2013

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

Ссылка: Directory.Delete Method

person Saurabh R S    schedule 09.10.2012
comment
удалить удалось, но результаты видны с задержкой - person Rafal; 09.10.2012
comment
В справочнике MSDN есть список условий, при которых Directory.Delete вызовет IOException. И даже, к моему удивлению, это условие (папка открыта в проводнике) им не обрабатывается. Но в разделе Remarks они упомянули, что в некоторых случаях, если указанный каталог открыт в проводнике, метод Delete не сможет его удалить. - person Saurabh R S; 09.10.2012
comment
Это не вариант. Каталог удаляется. - person Rafal; 09.10.2012