Как различать разные типы исключения System.UnauthorizedAccessException в приложении UWP?

Этот фрагмент кода пытается прочитать файл за пределами местоположения, разрешенного для приложений UWP:

StorageFile sf2 = await StorageFile.GetFileFromPathAsync(Path.Combine("C:\\Temp", "spam.dat"));

выдает исключение System.UnauthorizedAccessException, которое перехватывается отладчиком Visual Studio 2015 и отображается в красивом всплывающем окне:

Диалог исключения WinRT

Полный текст ниже:

Исключение типа System.UnauthorizedAccessException произошло в mscorlib.ni.dll, но не было обработано в пользовательском коде.

Информация WinRT: нет доступа к указанному файлу или папке (C: \ Temp \ spam.dat). Элемент не находится в месте, к которому приложение имеет доступ (включая папки данных приложения, папки, доступные через возможности, и сохраненные элементы в списках StorageApplicationPermissions). Убедитесь, что файл не помечен системными или скрытыми атрибутами файла.

Исключение типа System.UnauthorizedAccessException произошло в mscorlib.ni.dll, но не было обработано в пользовательском коде.

Теперь еще один фрагмент кода (этот код пытается прочитать файл, который уже открыт для записи):

StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(ApplicationData.Current.LocalFolder.Path);
StorageFile file = await folder.CreateFileAsync("spam.dat", CreationCollisionOption.ReplaceExisting);
Stream fs = await file.OpenStreamForWriteAsync();
StorageFile sf2 = await StorageFile.GetFileFromPathAsync(Path.Combine(ApplicationData.Current.LocalFolder.Path, "spam.dat"));
await sf2.OpenStreamForReadAsync();

Экран исключения в отладчике выглядит совершенно иначе:

Обычное диалоговое окно исключения

Дополнительная информация: Доступ запрещен. (Исключение из HRESULT: 0x80070005 (E_ACCESSDENIED))

Я не понимаю, как отладчик выглядит по-другому для этих исключений. В моем коде мне нужно понять причину получения UnauthorizedAccessException - если это произошло из-за того, что файл заблокирован для чтения, поскольку он занят другим писателем (и я могу повторить попытку немного позже), или по другой причине, например, за пределами LocalFolder (и повторять попытки не стоит). На самом деле я разрабатываю библиотеку, которая будет использоваться разработчиками приложений UWP, поэтому я не знаю, какой файл / путь к файлу мне будет предоставлен, и не могу делать никаких предположений относительно разрешений, структуры каталогов и т. Д. Я просто получаю путь на входе и, если что-то случится, может только использовать обработку исключений, чтобы понять причины.

Итак, отладчик Visual Studio каким-то образом видит разницу, но я не понимаю, как я могу сделать это программно. InnerException имеет значение null в обоих случаях, значение HResult одинаково (0x80070005).


person Alex    schedule 31.01.2017    source источник
comment
Как именно ваша библиотека будет использоваться другими разработчиками? Что он будет делать? Возможно, вам лучше всего не обрабатывать исключение, чтобы потребители вашего кода могли решить, как они хотят справиться с ситуацией.   -  person Newell Clark    schedule 31.01.2017
comment
Ожидается, что библиотека справится с ситуацией, когда несколько потоков или даже экземпляров приложения могут одновременно читать и записывать из / в одни и те же общие файлы. Поведение уже задано, я не могу его изменить. Библиотека уже ведет себя так для других платформ .NET, я просто делаю порт UWP.   -  person Alex    schedule 31.01.2017
comment
И мне любопытно, почему отладчик Visual Studio так по-разному отображает эти исключения ... Что между ними такого разного?   -  person Alex    schedule 31.01.2017


Ответы (2)


Если вы переносите библиотеку .NET на UWP, предположительно, она вообще не имеет зависимостей от StorageFile (как в реализации, так и в общедоступном интерфейсе), поэтому я бы продолжал использовать типы .NET, которые имеют явные ошибки (несанкционированный доступ или ввод-вывод) :

try
{
  var f = File.OpenRead(@"d:\temp\foo.txt");
}
catch (UnauthorizedAccessException ex)
{
  Debug.WriteLine(ex.Message);
}

try
{
  var localPath = ApplicationData.Current.LocalFolder.Path;
  var filename = Path.Combine(localPath, "foo.txt");
  var f1 = File.OpenWrite(filename);
  var f2 = File.OpenRead(filename);
}
catch (IOException ex)
{
  Debug.WriteLine(ex.Message);
}

Если я запустил ваш пример кода, я не получу никаких исключений для примера записи / чтения с использованием StorageFile (хотя я использую сборки для предварительной оценки для Windows 10 Creator's Update). Я могу получить исключение, если дважды попытаюсь открыть один и тот же файл для записи, но тогда он также отображается как IOException:

try
{
  var f = await StorageFile.GetFileFromPathAsync(@"d:\temp\foo.txt");
}
catch (UnauthorizedAccessException ex)
{
  Debug.WriteLine(ex.Message);
}

try
{
  var localFolder = ApplicationData.Current.LocalFolder;
  var f1 = await localFolder.CreateFileAsync("foo.txt", CreationCollisionOption.ReplaceExisting);
  var f2 = await localFolder.GetFileAsync("foo.txt");
  var s1 = await f1.OpenAsync(FileAccessMode.ReadWrite, StorageOpenOptions.AllowOnlyReaders);
  var s2 = await f2.OpenAsync(FileAccessMode.ReadWrite, StorageOpenOptions.AllowOnlyReaders);
}
catch (IOException ex)
{
  Debug.WriteLine(ex.Message);
}
person Peter Torr - MSFT    schedule 31.01.2017
comment
Это интересно. Несмотря на то, что он не дает прямого ответа на мой вопрос, он показывает, что конфликт доступа к файлам может привести не только к UnauthorizedAccessException, но и к IOException. Исходная библиотека, не относящаяся к UWP, действительно обрабатывала IOException в этом случае, и во время работы над миграцией я обнаружил, что это условие не возникает (в моем случае вместо этого возникло исключение UnauthorizedAccessException). Но ваш случай снова показывает IOException. Кажется, оба моих случая происходят при разных обстоятельствах. - person Alex; 31.01.2017
comment
Я скопировал ваш пример кода напрямую и не получил никаких ошибок из-за нарушений совместного доступа. Это может быть что-то, что зависит от конкретной сборки Windows, которую вы используете (я думаю, что недавно мы могли смягчить правила). Можете ли вы попробовать проверить свойство HResult, чтобы узнать, не отличается ли оно от него? - person Peter Torr - MSFT; 01.02.2017
comment
Из исходного сообщения: InnerException имеет значение null в обоих случаях, HResult одинаков (0x80070005). - person Alex; 01.02.2017
comment
Я также пробовал использовать обычное приложение .NET на том же компьютере: FileStream fs1 = new FileStream("log.txt", FileMode.OpenOrCreate); FileStream fs2 = new FileStream("log.txt", FileMode.Open); Получение исключения IOException (не UnauthorizedAccessException) The process cannot access the file '...\log.txt' because it is being used by another process. Исключение UnauthorizedAccessException происходит только с UWP для меня .. - person Alex; 01.02.2017
comment
Какая у вас сборка Windows? - person Peter Torr - MSFT; 02.02.2017
comment
Microsoft Windows 10 Профессиональная. Версия 10.0.14393, сборка 14393. - person Alex; 02.02.2017

Алекс,

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

StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(ApplicationData.Current.LocalFolder.Path);
        StorageFile file = await folder.CreateFileAsync("spam.dat", CreationCollisionOption.ReplaceExisting);
        Stream fs = await file.OpenStreamForWriteAsync();
        fs.Flush();
        fs.Dispose();
        StorageFile sf2 = await StorageFile.GetFileFromPathAsync(Path.Combine(ApplicationData.Current.LocalFolder.Path, "spam.dat"));
        var x = await sf2.OpenStreamForReadAsync();
person Kiran Paul    schedule 31.01.2017
comment
Вы не поняли всего смысла. Мой код специально создан для создания этого исключения. Я говорю об обработке исключений, и чтобы продемонстрировать это, мне нужно, чтобы это исключение произошло. - person Alex; 31.01.2017
comment
ох извините да вы уже упомянули об этом. Это было у меня плохо. Я думал, что вы тоже получаете такое исключение во втором типе кода .. - person Kiran Paul; 31.01.2017