private void ResolveDevicePathNames(string pathName) { string volumePath; string volumeDevicePath; string volumeDevicePathSlash = null; string volumeDrive = null; string volumeDosDevice = null; if (pathName.StartsWith(@"\??\")) { pathName = pathName.Substring(4); } int loop = 26; while (loop > 0) { loop--; if (string.IsNullOrEmpty(pathName)) { break; } // Finds the volume (win32 device path, or drive letter) with a trailing slash, for the path given. volumePath = m_OS.GetVolumePathName(pathName); if (volumePath == null) { // GetVolumePath fails on drives that don't exist, or may fail on SUBST'd drives (e.g. Win10). If it // is a SUBST'd drive, get the new path and loop again. if (ParseDosDevice(pathName, ref volumeDosDevice, ref volumeDrive, ref pathName)) { continue; } if (volumeDosDevice != null) { // A drive letter that doesn't support GetVolumeNameForVolumeMountPoint, isn't subst'd. volumePath = string.Format("\\\\.\\GLOBALROOT{0}\\", volumeDosDevice); } else { // A device that isn't a volume, i.e. a physical drive like '\\.\PhysicalDrive0'. volumePath = string.Format("{0}\\", pathName.TrimEnd(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar)); } } // Check if the resultant path is a SUBST'd drive. Windows XP can get here. Win10 doesn't. If it is a // SUBST'd drive, then get the new path and loop again. if (ParseDosDevice(volumePath, ref volumeDosDevice, ref volumeDrive, ref pathName)) { continue; } // Converts the volume path to the Win32 device path, that we can query it with an IOCTL later. The // Win32 function GetVolumeNameForVolumeMountPoint adds a trailing slash, which needs to be removed for // some API, like the IOCTL. string volumeDevice = m_OS.GetVolumeNameForVolumeMountPoint(volumePath); if (volumeDevice == null) { // There is no mount point for the drive given. It could be a SUBST'd drive with a path, in which // case we take just the drive letter, get the new path from the SUBST'd drive and loop again. if (ParseDosDevice(volumePath.Substring(0, 3), ref volumeDosDevice, ref volumeDrive, ref pathName)) { continue; } // We got here, because the path can't be mapped to a volume, and the test above shows it's not // SUBST'd. It could be a network drive, a physical drive, or a badly implemented driver (like the // ImDisk driver that doesn't support GetVolumeNameForVolumeMountPoint()). if (IsWin32Device(volumePath)) { volumeDevice = volumePath; } else { // Probably a network drive, then we really don't have a volume device. break; } } if (volumeDevice[volumeDevice.Length - 1] == System.IO.Path.DirectorySeparatorChar) { volumeDevicePath = volumeDevice.Remove(volumeDevice.Length - 1, 1); volumeDevicePathSlash = volumeDevice; } else { volumeDevicePath = volumeDevice; volumeDevicePathSlash = string.Format("{0}{1}", volumeDevice, System.IO.Path.DirectorySeparatorChar); } m_VolumeData.VolumePath = volumePath; m_VolumeData.VolumeDevicePath = volumeDevicePath; m_VolumeData.VolumeDevicePathSlash = volumeDevicePathSlash; if (volumeDosDevice == null && IsDriveLetter(volumePath)) { volumeDrive = volumePath.Substring(0, 2); volumeDosDevice = m_OS.QueryDosDevice(volumePath.Substring(0, 2)); } break; } // For some reason, the level of recursion when parsing the API is too high. This might be invalid data from // the OS, or a bug in the program. Obviously, we should never get here, but it's better than an infinite // loop. if (loop == 0) { throw new InvalidOperationException("Operation took too long to complete"); } if (volumeDosDevice == null && volumeDevicePathSlash == null) { // We couldn't map the drive letter to a DOS device, and didn't find a mount. The drive probably doesn't // exist. throw new System.IO.FileNotFoundException("Path can't be resolved to a volume"); } // In case a Win32 device was given, we now do a reverse lookup. if (volumeDrive == null) { ResolveDriveLetter(volumeDevicePathSlash, ref volumeDrive, ref volumeDosDevice); } m_VolumeData.VolumeDrive = volumeDrive ?? string.Empty; m_VolumeData.VolumeDosDevicePath = volumeDosDevice ?? string.Empty; }