microsoft / microsoft/WindowsAppSDK

StorageDevice.FromId(id) gives 'Access Denied Exception' in a OOP background task in WinUI3 - Desktop

Open
#2,372 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
4.7k
Forks
471
Avg merge
3d 13h
Merged PRs (30d)
28

Description

### Describe the bug

I am porting a UWP/DesktopBridge app to WinUI - Desktop. The app registers a `DeviceWatcher` task to detect inserts and ejects of USB storage devices.
In the UWP app the task is an in-process task; in the WinUI app it is (must be) a OOP task.
The task runs well but there is a `Access denied` exception when the task is informed about a USB device being inserted and then calls `StorageDevice.FromId(id)`. The call is there in order to get a `StorageFolder` and from there to extract the `Volume Label` and `Drive Letter` of the MSC device.
The app is SyncFolder, which is a folder sync/copy tool for users, and the `Volume Label` and `Drive Letter` information is needed in order to find out whether the app must inform the user that a possible candidate storage device is being inserted for a given sync/backup task. This would be necessary when the user is using a set of rotating backup disks. Volume Label (and to a lesser extend Drive Letter) would then be the common name for all disks in the set.

The exception happens in the call to `Windows.Devices.Portable.StorageDevice.FromId()`. The code is actually quite simple:
```
async Task> GetAvailableMSCStorageDevicesAsync()
{
var devices = new List();
var portableStorageDevices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(Windows.Devices.Enumeration.DeviceClass.PortableStorageDevice);
ConditionalFileLogger.Log($"PortableStorageDevicesMonitor - GetAvailableStorageDevicesAsync: found {portableStorageDevices.Count} portable devices");
foreach (var d in portableStorageDevices)
{
int step = 0;
try
{
step = 1;
var props = d.Properties;
ConditionalFileLogger.Log($"PortableStorageDevicesMonitor - GetAvailableStorageDevicesAsync: get storage folder of {d.Name}, {d.Id}");
step = 2;
var storageFolder = Windows.Devices.Portable.StorageDevice.FromId(d.Id);
if (storageFolder != null &&
!String.IsNullOrEmpty(storageFolder.Path))
{
step = 3;
ConditionalFileLogger.Log($"PortableStorageDevicesMonitor - RefreshPortableStorageDevicesAsync: add {{d.Name}} ({storageFolder.Path}) to list");
string driveLetter;
string volumeLabel = StorageDeviceExt.GetVolumeLabelAndDriveLetter(storageFolder.DisplayName, out driveLetter);
devices.Add(new StorageDeviceExt.StorageDeviceInfo()
{
DeviceId = d.Id,
DriveType = System.IO.DriveType.Removable,
VolumeLabel = volumeLabel,
DriveLetter = driveLetter
});
}
}
catch (Exception e)
{
ConditionalFileLogger.Log($"StorageDeviceHelper - RefreshPortableStorageDevicesAsync exception 1, step {step} for {d.Name}: {e.Message}");
Analytics.TrackEvent("PortableStorageDevicesMonitor-Exception", new Dictionary
{
{ "Exception", $"GetAvailableStorageDevicesAsync: {e.Message}" }
});
}
}
ConditionalFileLogger.Log($"PortableStorageDevicesMonitor - GetAvailableStorageDevicesAsync: found {devices.Count} portable MSC storage devices");
return devices;
}
```
`ConditionalFileLogger` is my own implementation of a log service that outputs log events to a file.
`Analytics.TrackEvent(..)` is a call to ms appcenter to register the exception in appcenter.

The exception is: Access denied. (0x80070005 (E_ACCESSDENIED)) and is generated for every USB device for which the `FromId()` call is made.

The same call to the above `GetAvailableMSCStorageDevicesAsync()` method runs fine when invoked from within the WinUI app itself (i.e. not from the OOP WinRT process).
So, it seems that the app itself is granted all the rights to get a `StorageFolder` for a portable device but not the OOP `DeviceWatcher` task.

Is this intended behavior?
That would be strange/wrong because the same code in the UWP in-process DeviceWatcher runs without issues.

### Steps to reproduce the bug

Create a OOP DeviceWatcher task in a WinUI 3 Desktop app (packaged, using wap project).
Register the task.
Call `Windows.Devices.Portable.StorageDevice.FromId()`

### Expected behavior

_No response_

### Screenshots

_No response_

### NuGet package version

1.0.0

### Packaging type

Packaged (MSIX)

### Windows version

Windows 11 version 21H2 (22000)

### IDE

Visual Studio 2022

### Additional context

_No response_

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the packaged WinUI 3 Desktop OOP DeviceWatcher reproduction described in the issue, focusing on Windows.Devices.Portable.StorageDevice.FromId(). Compare the call from the OOP task with the same GetAvailableMSCStorageDevicesAsync() call from the app process. Done means establishing whether E_ACCESSDENIED is intended for OOP tasks and documenting or identifying the supported way to obtain the StorageFolder.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
desktop
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.