0

Having spent some time reading up on the Windows.Storage classes and methods, I'm a bit shocked to find that there is no simple way to retrieve a list of the internal drives on a machine using UWP. I've found examples such as this: How to get logical drives names in Windows 10?, but this provides the removable drives only (Flash drives, DVD drive, etc).

I know I can allow the user to select a folder and that'll provide me with the handle to the selected drive, but it seems clunky as I want the user to be able to run my software and scan the internal disks on the PC without having to re-select the drives every time.

I've added the broadFileSystemAccess attribute to the manifest so have access to the disks, I just can't call a method to get the internal drives. As a workaround, I've written the following method.

public List<StorageFolder> GetInternalDrives()
{
    string driveLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int driveLettersLen = driveLetters.Length;
    string removableDriveLetters = "";
    string driveLetter;

    List<StorageFolder> drives = new List<StorageFolder>();
    StorageFolder removableDevices = KnownFolders.RemovableDevices;
    IReadOnlyList<StorageFolder> folders = Task.Run<IReadOnlyList<StorageFolder>>(async () => await removableDevices.GetFoldersAsync()).Result;

    foreach (StorageFolder removableDevice in folders)
    {
        if (string.IsNullOrEmpty(removableDevice.Path)) continue;
        driveLetter = removableDevice.Path.Substring(0, 1).ToUpper();
        if (driveLetters.IndexOf(driveLetter) > -1) removableDriveLetters += driveLetter;
    }

    for (int curDrive = 0; curDrive < driveLettersLen; curDrive++)
    {
        driveLetter = driveLetters.Substring(curDrive, 1);
        if (removableDriveLetters.IndexOf(driveLetter) > -1) continue;

        try
        {
            StorageFolder drive = Task.Run<StorageFolder>(async () => await StorageFolder.GetFolderFromPathAsync(driveLetter + ":")).Result;
            drives.Add(drive);
        }
        catch (System.AggregateException) { }

    }

    return drives;
}

And here's an example of GetInternalDrives being called:

List<StorageFolder> drives = GetInternalDrives();
panScanParams.Children.Clear();
foreach (StorageFolder drive in drives)
{
    CheckBox cb = new CheckBox();
    cb.Content = drive.DisplayName;
    cb.IsChecked = true;
    panScanParams.Children.Add(cb);
}

Whilst the code is only invoked once for an instance of the app, I'm not at all happy with the code. As it calls StorageFolder.GetFolderFromPathAsync for every letter of the alphabet, it crashes over 20 times on my machine. Writing code which you expect to fail and handling the exception just seems bad practice. But with the lack of a suitable alternative, I don't know what other options I have.

Has anyone tackled the same problem, and if so, what did you do to resolve it?

  • 1
    In UWP, there is no method to list all drivers directly, probably for security reasons. But when I used your code to test, it worked well, so when you called **StorageFolder.GetFolderFromPathAsync()** for every letter, what exception it throwed? And whether you configued access to allow your app to access the file system in Settings > Privacy > File system or not? – Faywang - MSFT Oct 22 '19 at 05:44
  • On calling StorageFolder.GetFolderFromPathAsync() for a drive letter which doesn't exist a System.AggregateException is thrown. The Microsoft documentation (https://learn.microsoft.com/en-us/uwp/api/windows.storage.storagefolder.getfolderfrompathasync) doesn't list AggregateException as an exception which might be raised. I'd have expected a FileNotFoundException exception, but this might well be happening as I'm executing the code using Task.Run; I could easily change my method to async to test this theory. – Mathew Rimmington Oct 22 '19 at 13:24
  • PS. I did have to add my application to the list which allows file system access in settings / privacy. I've read that there is a way to prompt users with the settings / privacy window on launch of the app for the first time. – Mathew Rimmington Oct 22 '19 at 13:28
  • Yes, when you combine Task.Run with GetFolderFromPathAsync(), it will trigger multiple exceptions, so it throws AggregateException. And for now, if this code can work well to achieve the effect you want? – Faywang - MSFT Oct 23 '19 at 03:13
  • I've adjusted the method to work asynchronous with the calling code to update the UI using Task.Run. Bizarrely the exception raised now is a plain System.Exception with the message "Error HRESULT E_FAIL has been returned from a call to a COM component." – Mathew Rimmington Oct 23 '19 at 16:16
  • I get the desired result with the code, it just seems madness to have to call a method 26 times which I'm fairly sure will crash about 22 of those times on a regular user's PC. Hopefully Microsoft will find a devise a solution sooner or later. I'll leave the question open for now for anyone else to reflect on it. – Mathew Rimmington Oct 23 '19 at 16:39

1 Answers1

2

Or you can try this code var driveLetters = DriveInfo.GetDrives().Select(x => x.RootDirectory.Root).ToList().OrderBy(x => x.Root.FullName).ToList(); to get drives instead of using StorageFolder method to exclude one by one. After that, traversing the resulting list and convert to StorageFolder. Below code I haven't removed removable drives.

var driveLetters = DriveInfo.GetDrives().Select(x => x.RootDirectory.Root).ToList().OrderBy(x => x.Root.FullName).ToList();
List<StorageFolder> drives = new List<StorageFolder>();
foreach (var driveInfo in driveLetters)
{
    String a = driveInfo.ToString();
    StorageFolder drive = Task.Run<StorageFolder>(async () => await StorageFolder.GetFolderFromPathAsync(a)).Result;
    drives.Add(drive);
}
Faywang - MSFT
  • 5,798
  • 1
  • 5
  • 8