When I list virtual disks within diskpart:
DISKPART> list vdisk
VDisk ### Disk ### State Type File
--------- -------- -----
P/Invoking the GetStorageDependencyInformation will provide a strictly VHD API solution. While this function does not take a drive number as an input parameter, a wrapper method will. The wrapper method converts a drive number to a string of the form "\\\\.\\PhysicalDriveN" which is passed to CreateFile
and the resultant handle is passed to GetStorageDependencyInformation
. A similar wrapper method will take an input of a single char
drive letter.
The following code was translated to C# from an unmanaged example:
using DWORD = System.UInt32;
using ULONG = System.UInt32;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.IO;
namespace VhdTest
{
class Program
{
static void Main(string[] args)
{
String[] arr;
arr = VirtualDisk.GetDependentVolumePaths('e');
arr = VirtualDisk.GetDependentVolumePaths(1);
}
}
class VirtualDisk
{
#region [ Native ]
#region [ Constants ]
const DWORD ERROR_INSUFFICIENT_BUFFER = 122;
const DWORD ERROR_SUCCESS = 0;
const DWORD GENERIC_READ = 0x80000000;
const DWORD FILE_SHARE_READ = 1;
const DWORD FILE_SHARE_WRITE = 2;
const DWORD OPEN_EXISTING = 3;
const DWORD FILE_ATTRIBUTE_NORMAL = 0x00000080;
const DWORD FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
#endregion
#region [ Enums ]
[Flags]
enum DEPENDENT_DISK_FLAG
{
DEPENDENT_DISK_FLAG_NONE = 0x00000000,
//
// Multiple files backing the virtual storage device
//
DEPENDENT_DISK_FLAG_MULT_BACKING_FILES = 0x00000001,
DEPENDENT_DISK_FLAG_FULLY_ALLOCATED = 0x00000002,
DEPENDENT_DISK_FLAG_READ_ONLY = 0x00000004,
//
//Backing file of the virtual storage device is not local to the machine
//
DEPENDENT_DISK_FLAG_REMOTE = 0x00000008,
//
// Volume is the system volume
//
DEPENDENT_DISK_FLAG_SYSTEM_VOLUME = 0x00000010,
//
// Volume backing the virtual storage device file is the system volume
//
DEPENDENT_DISK_FLAG_SYSTEM_VOLUME_PARENT = 0x00000020,
DEPENDENT_DISK_FLAG_REMOVABLE = 0x00000040,
//
// Drive letters are not assigned to the volumes
// on the virtual disk automatically.
//
DEPENDENT_DISK_FLAG_NO_DRIVE_LETTER = 0x00000080,
DEPENDENT_DISK_FLAG_PARENT = 0x00000100,
//
// Virtual disk is not attached on the local host
// (instead attached on a guest VM for instance)
//
DEPENDENT_DISK_FLAG_NO_HOST_DISK = 0x00000200,
//
// Indicates the lifetime of the disk is not tied
// to any system handles
//
DEPENDENT_DISK_FLAG_PERMANENT_LIFETIME = 0x00000400
}
[Flags]
enum GET_STORAGE_DEPENDENCY_FLAG
{
GET_STORAGE_DEPENDENCY_FLAG_NONE = 0x00000000,
// Return information for volumes or disks hosting the volume specified
// If not set, returns info about volumes or disks being hosted by
// the volume or disk specified
GET_STORAGE_DEPENDENCY_FLAG_HOST_VOLUMES = 0x00000001,
GET_STORAGE_DEPENDENCY_FLAG_PARENTS = GET_STORAGE_DEPENDENCY_FLAG_HOST_VOLUMES,
// The handle provided is to a disk, not volume or file
GET_STORAGE_DEPENDENCY_FLAG_DISK_HANDLE = 0x00000002,
}
enum STORAGE_DEPENDENCY_INFO_VERSION
{
STORAGE_DEPENDENCY_INFO_VERSION_UNSPECIFIED = 0,
STORAGE_DEPENDENCY_INFO_VERSION_1 = 1,
STORAGE_DEPENDENCY_INFO_VERSION_2 = 2,
}
#endregion
#region [ Structures ]
[StructLayout(LayoutKind.Sequential)]
struct STORAGE_DEPENDENCY_INFO_TYPE_1
{
DEPENDENT_DISK_FLAG DependencyTypeFlags;
ULONG ProviderSpecificFlags;
VIRTUAL_STORAGE_TYPE VirtualStorageType;
}
[StructLayout(LayoutKind.Sequential)]
struct STORAGE_DEPENDENCY_INFO_TYPE_2
{
public DEPENDENT_DISK_FLAG DependencyTypeFlags;
public ULONG ProviderSpecificFlags;
public VIRTUAL_STORAGE_TYPE VirtualStorageType;
public ULONG AncestorLevel;
public IntPtr DependencyDeviceName;
public IntPtr HostVolumeName;
public IntPtr DependentVolumeName;
public IntPtr DependentVolumeRelativePath;
}
[StructLayout(LayoutKind.Explicit)]
struct STORAGE_DEPENDENCY_INFO_Union
{
[FieldOffset(0)]
STORAGE_DEPENDENCY_INFO_TYPE_1 Version1Entries;
[FieldOffset(0)]
STORAGE_DEPENDENCY_INFO_TYPE_2 Version2Entries;
}
[StructLayout(LayoutKind.Sequential)]
struct STORAGE_DEPENDENCY_INFO
{
public STORAGE_DEPENDENCY_INFO_VERSION Version;
public ULONG NumberEntries;
public STORAGE_DEPENDENCY_INFO_Union Union;
}
[StructLayout(LayoutKind.Sequential)]
struct VIRTUAL_STORAGE_TYPE
{
public ULONG DeviceId;
public Guid VendorId;
}
#endregion
#region [ PInvokes ]
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern SafeFileHandle CreateFile(string lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
IntPtr lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("virtdisk.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern DWORD GetStorageDependencyInformation(SafeHandle ObjectHandle,
GET_STORAGE_DEPENDENCY_FLAG Flags,
ULONG StorageDependencyInfoSize,
IntPtr StorageDependencyInfo,
ref ULONG SizeUsed);
#endregion
#endregion
#region [ Managed Methods ]
public static String[] GetDependentVolumePaths(char driveLetter)
{
driveLetter = Char.ToUpper(driveLetter);
if (driveLetter < 'A' || driveLetter > 'Z')
{
String paramName = "driveLetter";
String message = "Drive letter must fall in range [a-zA-Z]";
throw new ArgumentOutOfRangeException(paramName, message);
}
String fileName = String.Format(@"\\.\{0}:\", driveLetter);
return getDependentVolumePaths(fileName);
}
public static String[] GetDependentVolumePaths(UInt32 driveNumber)
{
// TODO: Per SO, isn't max drive 15?
// http://stackoverflow.com/questions/327718/how-to-list-physical-disks
if (driveNumber > 9)
{
String paramName = "driveNumber";
String message = "Drive number must be <= 9";
throw new ArgumentOutOfRangeException(paramName, message);
}
String fileName = String.Format(@"\\.\PhysicalDrive{0}", driveNumber);
return getDependentVolumePaths(fileName);
}
static unsafe String[] getDependentVolumePaths(String fileName)
{
DWORD dwDesiredAccess = GENERIC_READ;
DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
DWORD dwCreationDisposition = OPEN_EXISTING;
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS;
SafeHandle driveHandle = null;
STORAGE_DEPENDENCY_INFO info = new STORAGE_DEPENDENCY_INFO();
info.Version = STORAGE_DEPENDENCY_INFO_VERSION.STORAGE_DEPENDENCY_INFO_VERSION_2;
IntPtr ptr;
try
{
driveHandle = CreateFile(fileName,
dwDesiredAccess, //GENERIC_READ,
dwShareMode,
IntPtr.Zero,
dwCreationDisposition,
dwFlagsAndAttributes,
IntPtr.Zero);
if (driveHandle.IsInvalid)
{
return null;
}
GET_STORAGE_DEPENDENCY_FLAG flags = GET_STORAGE_DEPENDENCY_FLAG.GET_STORAGE_DEPENDENCY_FLAG_NONE;
flags |= GET_STORAGE_DEPENDENCY_FLAG.GET_STORAGE_DEPENDENCY_FLAG_PARENTS;
if (fileName.ToUpper().Contains("PHYSICAL"))
flags |= GET_STORAGE_DEPENDENCY_FLAG.GET_STORAGE_DEPENDENCY_FLAG_DISK_HANDLE;
DWORD infoSize = (DWORD)Marshal.SizeOf(info);
byte[] infoByteArray;
DWORD cbSize = 0;
DWORD opStatus;
#region [ Pull STORAGE_DEPENDENCY_INFO into byte array ]
infoByteArray = new byte[infoSize];
fixed (byte* p1 = infoByteArray)
{
ptr = (IntPtr)p1;
Marshal.StructureToPtr(info, ptr, true);
opStatus = GetStorageDependencyInformation(driveHandle, flags, infoSize, ptr, ref cbSize);
if (opStatus == ERROR_INSUFFICIENT_BUFFER)
{
infoSize = cbSize;
cbSize = 0;
infoByteArray = new byte[infoSize];
fixed (byte* p2 = infoByteArray)
{
ptr = (IntPtr)p2;
Marshal.StructureToPtr(info, ptr, true);
opStatus = GetStorageDependencyInformation(driveHandle, flags, infoSize, ptr, ref cbSize);
}
}
}
#endregion
if (opStatus != ERROR_SUCCESS)
{
//
// This is most likely due to the disk not being a mounted VHD.
//
return null;
}
}
finally
{
if (driveHandle != null && !driveHandle.IsInvalid && !driveHandle.IsClosed)
{
driveHandle.Close();
}
}
List pathList = new List();
info = (STORAGE_DEPENDENCY_INFO)Marshal.PtrToStructure(ptr, typeof(STORAGE_DEPENDENCY_INFO));
//STORAGE_DEPENDENCY_INFO_TYPE_2 sdi2 = new STORAGE_DEPENDENCY_INFO_TYPE_2();
STORAGE_DEPENDENCY_INFO_TYPE_2* p = (STORAGE_DEPENDENCY_INFO_TYPE_2*)&info.Union;
for (DWORD i = 0; i < info.NumberEntries; i++, p++)
{
ptr = (IntPtr)p;
STORAGE_DEPENDENCY_INFO_TYPE_2 sdi2 = (STORAGE_DEPENDENCY_INFO_TYPE_2)Marshal.PtrToStructure(ptr, typeof(STORAGE_DEPENDENCY_INFO_TYPE_2));
String str1 = Marshal.PtrToStringUni(sdi2.DependencyDeviceName);
String str2 = Marshal.PtrToStringUni(sdi2.HostVolumeName);
String str3 = Marshal.PtrToStringUni(sdi2.DependentVolumeName);
String relativePath = Marshal.PtrToStringUni(sdi2.DependentVolumeRelativePath);
String fullPath = Path.GetFullPath(relativePath);
pathList.Add(fullPath);
}
return pathList.ToArray();
}
#endregion
}
}