4

How can I resolve a path with drive letter from a device path?

For example, convert

\Device\HarddiskVolume4\Windows\System32\RuntimeBroker.exe

into

C:\Windows\System32\RuntimeBroker.exe

Assuming HarddiskVolume4 maps to C: on this computer.

I have found this question, but I want to use it in C#.

Bruno Zell
  • 7,761
  • 5
  • 38
  • 46
  • really all solutions under your link not the best. clean and efficient solution for task - use `IOCTL_MOUNTMGR_QUERY_POINTS` . but i can paste it only on *c++* but not on *c#* – RbMm Jan 18 '18 at 12:16
  • 2
    In your case there's a logical-drive "C:" mountpoint, but the assumption that you can iterate through drive-letter mountpoints with `QueryDosDevice` is wrong in general. A volume may be mounted in a folder or only have a "Volume{GUID}" name. I think the simplest solution is to prepend "\\?\GLOBALROOT" to the path; open the file with minimal access; and call `GetFinalPathNameByHandle` to try to get its `VOLUME_NAME_DOS` or `VOLUME_NAME_GUID`. Otherwise you need to query "\\.\MountPointManager" `IOCTL_MOUNTMGR_QUERY_POINTS` via `DeviceIoControl` as mentioned above. – Eryk Sun Jan 18 '18 at 12:22
  • @eryksun This seems very interresting. I'll look into it! – Bruno Zell Jan 18 '18 at 12:26
  • depend for what you need drive letter name form. if you need for call `CreateFileW` - you can simply add "\\\\?\\GLOBALROOT" prefix. or use `NtOpenFile`. however if you need use it in shell api - correct way got `MOUNTMGR_MOUNT_POINT` array via `IOCTL_MOUNTMGR_QUERY_POINTS` and found where `DeviceName` was exactly prefix for your path. and replace this prefix to `SymbolicLinkName` prefix when it in form `MOUNTMGR_IS_DRIVE_LETTER` – RbMm Jan 18 '18 at 12:42

1 Answers1

2
string devicePath = @"\Device\HarddiskVolume4\Windows\System32\RuntimeBroker.exe";
string driveLetterPath = DevicePathMapper.FromDevicePath(devicePath);

// driveLetterPath == C:\Windows\System32\RuntimeBroker.exe

Since we want a path with a drive letter we need to replace \Device\HarddiskVolume4 with the correct drive letter, e.g. C:. For that use QueryDosDevice and map a dos device to each drive letter. Then we can search and replace for the dos device path.

This is a possible implementation. It uses extension methods internal:

public static class DevicePathMapper {
    [DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
    private static extern uint QueryDosDevice([In] string lpDeviceName, [Out] StringBuilder lpTargetPath, [In] int ucchMax);

    public static string FromDevicePath(string devicePath) {
        var drive = Array.Find(DriveInfo.GetDrives(), d => devicePath.StartsWith(d.GetDevicePath(), StringComparison.InvariantCultureIgnoreCase));
        return drive != null ?
            devicePath.ReplaceFirst(drive.GetDevicePath(), drive.GetDriveLetter()) :
            null;
    }

    private static string GetDevicePath(this DriveInfo driveInfo) {
        var devicePathBuilder = new StringBuilder(128);
        return QueryDosDevice(driveInfo.GetDriveLetter(), devicePathBuilder, devicePathBuilder.Capacity + 1) != 0 ?
            devicePathBuilder.ToString() :
            null;
    }

    private static string GetDriveLetter(this DriveInfo driveInfo) {
        return driveInfo.Name.Substring(0, 2);
    }

    private static string ReplaceFirst(this string text, string search, string replace) {
        int pos = text.IndexOf(search);
        if (pos < 0) {
            return text;
        }
        return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
    }
}
Bruno Zell
  • 7,761
  • 5
  • 38
  • 46