Получение имени файла виртуального диска по номеру диска

Когда я перечисляю виртуальные диски в diskpart:

DISKPART> list vdisk

  VDisk ###  Disk ###  State                 Type       File
  ---------  --------  --------------------  ---------  ----
  VDisk 0    Disk 2    Attached not open     Fixed       C:\Disk.vhd

Интересная часть для меня здесь имя файла. Я попытался найти эквивалент функции, которая дала бы мне имя файла (в столбце «Файл»), если я знаю номер диска.

В идеале я бы дал "\\?\PhysicalDrive2" и в результате получил бы "C:\Disk.vhd".

Я уже пробовал:

  1. Использование diskpart и вывод синтаксического анализа - поскольку это недокументированный формат, он может измениться в любое время. Это не то, на что я бы полагался.
  2. Общий VHD API – функция не выполняется номер диска в качестве параметра.
  3. Microsoft.Storage.Vds.dll — есть перечисления, которые проходят через каждый диск (например, Service.Providers), но нет свойства/функции, которая даст мне имя исходного файла. Хотя теперь я могу быть уверен, что, например. диск D: является виртуальным диском, я до сих пор не могу узнать, какой файл .vhd был прикреплен.

Есть идеи, что это может быть за функция?


person Josip Medved    schedule 02.05.2010    source источник
comment
Я столкнулся с этим вопросом, исследуя свой собственный проект. Я знаю, что вопрос старый, и ответ на него был удовлетворен, но я подумал, что решение VHD API будет полезно.   -  person LJ VanKuiken    schedule 06.01.2013


Ответы (4)


Вот два решения для извлечения виртуальных дисков на локальном компьютере и печати их информации. Два решения демонстрируют, как использовать COM-объекты VDS для доступа к этим данным как собственным, так и управляемым способом.

Управляемое решение

Я создал частичное COM-взаимодействие из документации MSDN и из Windows 7 SDK (в основном vds.h). Обратите внимание, что оболочки COM являются частичными, что означает, что некоторые методы еще предстоит портировать.

Ниже приведено управляемое приложение, которое использует взаимодействие .NET COM для:

  • Загрузить сервис VDS
  • Запрос поставщиков виртуальных дисков
  • List all the Virtual Disk handled by each provider:
    • The virtual disk's properties gives its GUID, its full driver path, its volume size and its disk file (i.e. C:\Disk.vhd).
    • Виртуальный диск также может быть запрошен как универсальный диск и дает свое имя (например, \\?\PhysicalDrive1), понятное имя и другие свойства.

using System;
using System.Runtime.InteropServices;

namespace VDiskDumper
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create the service loader
            VdsServiceLoader loaderClass = new VdsServiceLoader();
            IVdsServiceLoader loader = (IVdsServiceLoader)loaderClass;
            Console.WriteLine("Got Loader");

            // Load the service
            IVdsService service;
            loader.LoadService(null, out service);
            Console.WriteLine("Got Service");

            // Wait for readyness
            service.WaitForServiceReady();
            Console.WriteLine("Service is ready");

            // Query for vdisk providers
            IEnumVdsObject providerEnum;
            service.QueryProviders(VDS_QUERY_PROVIDER_FLAG.VDS_QUERY_VIRTUALDISK_PROVIDERS, out providerEnum);
            Console.WriteLine("Got Providers");

            // Iterate
            while (true)
            {
                uint fetched;
                object unknown;
                providerEnum.Next(1, out unknown, out fetched);

                if (fetched == 0) break;

                // Cast to the required type
                IVdsVdProvider provider = (IVdsVdProvider)unknown;
                Console.WriteLine("Got VD Provider");

                Dump(provider);
            }

            Console.ReadKey();
        }

        private static void Dump(IVdsVdProvider provider)
        {
            // Query for the vdisks
            IEnumVdsObject diskEnum;
            provider.QueryVDisks(out diskEnum);
            Console.WriteLine("Got VDisks");

            // Iterate
            while (true)
            {
                uint fetched;
                object unknown;
                diskEnum.Next(1, out unknown, out fetched);

                if (fetched == 0) break;

                // Cast to the required type
                IVdsVDisk vDisk = (IVdsVDisk)unknown;

                // Get the vdisk properties
                VDS_VDISK_PROPERTIES vdiskProperties;
                vDisk.GetProperties(out vdiskProperties);

                Console.WriteLine("-> VDisk Id=" + vdiskProperties.Id);
                Console.WriteLine("-> VDisk Device Name=" + vdiskProperties.pDeviceName);
                Console.WriteLine("-> VDisk Path=" + vdiskProperties.pPath);

                // Get the associated disk
                IVdsDisk disk;
                provider.GetDiskFromVDisk(vDisk, out disk);

                // Get the disk properties
                VDS_DISK_PROP diskProperties;
                disk.GetProperties(out diskProperties);

                Console.WriteLine("-> Disk Name=" + diskProperties.pwszName);
                Console.WriteLine("-> Disk Friendly=" + diskProperties.pwszFriendlyName);
            }
        }
    }

    [ComImport, Guid("118610b7-8d94-4030-b5b8-500889788e4e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IEnumVdsObject
    {
        void Next(uint numberOfObjects, [MarshalAs(UnmanagedType.IUnknown)] out object objectUnk, out uint numberFetched);
        void Skip(uint NumberOfObjects);
        void Reset();
        void Clone(out IEnumVdsObject Enum);
    }

    [ComImport, Guid("07e5c822-f00c-47a1-8fce-b244da56fd06"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IVdsDisk
    {
        void GetProperties(out VDS_DISK_PROP diskProperties);
        void GetPack(); // Unported method
        void GetIdentificationData(IntPtr lunInfo);
        void QueryExtents(); // Unported method
        void slot4();
        void SetFlags(); // Unported method
        void ClearFlags(); // Unported method
    }

    [ComImport, Guid("0818a8ef-9ba9-40d8-a6f9-e22833cc771e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IVdsService
    {
        [PreserveSig]
        int IsServiceReady();
        [PreserveSig]
        int WaitForServiceReady();
        void GetProperties(); // Unported method
        void QueryProviders(VDS_QUERY_PROVIDER_FLAG mask, out IEnumVdsObject providers);
        void QueryMaskedDisks(out IEnumVdsObject disks);
        void QueryUnallocatedDisks(out IEnumVdsObject disks);
        void GetObject(); // Unported method
        void QueryDriveLetters(); // Unported method
        void QueryFileSystemTypes(out IntPtr fileSystemTypeProps, out uint numberOfFileSystems);
        void Reenumerate();
        void Refresh();
        void CleanupObsoleteMountPoints();
        void Advise(); // Unported method
        void Unadvise(); // Unported method
        void Reboot();
        void SetFlags(); // Unported method
        void ClearFlags(); // Unported method
    }

    [ComImport, Guid("e0393303-90d4-4a97-ab71-e9b671ee2729"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IVdsServiceLoader
    {
        void LoadService([In, MarshalAs(UnmanagedType.LPWStr)] string machineName, out IVdsService vdsService);
    }

    [ComImport, Guid("1e062b84-e5e6-4b4b-8a25-67b81e8f13e8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IVdsVDisk
    {
        void Open(); // Unported method
        void GetProperties(out VDS_VDISK_PROPERTIES pDiskProperties);
        void GetHostVolume(); // Unported method
        void GetDeviceName(); // Unported method
    }

    [ComImport, Guid("b481498c-8354-45f9-84a0-0bdd2832a91f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IVdsVdProvider
    {
        void QueryVDisks(out IEnumVdsObject ppEnum);
        void CreateVDisk(); // Unported method
        void AddVDisk(); // Unported method
        void GetDiskFromVDisk(IVdsVDisk pVDisk, out IVdsDisk ppDisk);
        void GetVDiskFromDisk(IVdsDisk pDisk, out IVdsVDisk ppVDisk);
    }

    [ComImport, Guid("9c38ed61-d565-4728-aeee-c80952f0ecde")]
    public class VdsServiceLoader
    {
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct Signature
    {
        [FieldOffset(0)]
        public uint dwSignature;
        [FieldOffset(0)]
        public Guid DiskGuid;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct VDS_DISK_PROP
    {
        public Guid Id;
        public VDS_DISK_STATUS Status;
        public VDS_LUN_RESERVE_MODE ReserveMode;
        public VDS_HEALTH health;
        public uint dwDeviceType;
        public uint dwMediaType;
        public ulong ullSize;
        public uint ulBytesPerSector;
        public uint ulSectorsPerTrack;
        public uint ulTracksPerCylinder;
        public uint ulFlags;
        public VDS_STORAGE_BUS_TYPE BusType;
        public VDS_PARTITION_STYLE PartitionStyle;
        public Signature dwSignature;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string pwszDiskAddress;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string pwszName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string pwszFriendlyName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string pwszAdaptorName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string pwszDevicePath;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct VIRTUAL_STORAGE_TYPE
    {
        public uint DeviceId;
        public Guid VendorId;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct VDS_VDISK_PROPERTIES
    {
        public Guid Id;
        public VDS_VDISK_STATE State;
        public VIRTUAL_STORAGE_TYPE VirtualDeviceType;
        public ulong VirtualSize;
        public ulong PhysicalSize;
        [MarshalAs(UnmanagedType.LPWStr)]
        public String pPath;
        [MarshalAs(UnmanagedType.LPWStr)]
        public String pDeviceName;
        public DEPENDENT_DISK_FLAG DiskFlag;
        public bool bIsChild;
        [MarshalAs(UnmanagedType.LPWStr)]
        public String pParentPath;
    }

    public enum DEPENDENT_DISK_FLAG
    {
        DEPENDENT_DISK_FLAG_NONE = 0x00000000,
        DEPENDENT_DISK_FLAG_MULT_BACKING_FILES = 0x00000001,
        DEPENDENT_DISK_FLAG_FULLY_ALLOCATED = 0x00000002,
        DEPENDENT_DISK_FLAG_READ_ONLY = 0x00000004,
        DEPENDENT_DISK_FLAG_REMOTE = 0x00000008,
        DEPENDENT_DISK_FLAG_SYSTEM_VOLUME = 0x00000010,
        DEPENDENT_DISK_FLAG_SYSTEM_VOLUME_PARENT = 0x00000020,
        DEPENDENT_DISK_FLAG_REMOVABLE = 0x00000040,
        DEPENDENT_DISK_FLAG_NO_DRIVE_LETTER = 0x00000080,
        DEPENDENT_DISK_FLAG_PARENT = 0x00000100,
        DEPENDENT_DISK_FLAG_NO_HOST_DISK = 0x00000200,
        DEPENDENT_DISK_FLAG_PERMANENT_LIFETIME = 0x00000400,
    }

    public enum VDS_DISK_STATUS
    {
        VDS_DS_UNKNOWN = 0,
        VDS_DS_ONLINE = 1,
        VDS_DS_NOT_READY = 2,
        VDS_DS_NO_MEDIA = 3,
        VDS_DS_FAILED = 5,
        VDS_DS_MISSING = 6,
        VDS_DS_OFFLINE = 4
    }

    public enum VDS_HEALTH
    {
        VDS_H_UNKNOWN = 0,
        VDS_H_HEALTHY = 1,
        VDS_H_REBUILDING = 2,
        VDS_H_STALE = 3,
        VDS_H_FAILING = 4,
        VDS_H_FAILING_REDUNDANCY = 5,
        VDS_H_FAILED_REDUNDANCY = 6,
        VDS_H_FAILED_REDUNDANCY_FAILING = 7,
        VDS_H_FAILED = 8,
        VDS_H_REPLACED = 9,
        VDS_H_PENDING_FAILURE = 10,
        VDS_H_DEGRADED = 11
    }

    public enum VDS_LUN_RESERVE_MODE
    {
        VDS_LRM_NONE = 0,
        VDS_LRM_EXCLUSIVE_RW = 1,
        VDS_LRM_EXCLUSIVE_RO = 2,
        VDS_LRM_SHARED_RO = 3,
        VDS_LRM_SHARED_RW = 4
    }

    public enum VDS_PARTITION_STYLE
    {
        VDS_PST_UNKNOWN = 0,
        VDS_PST_MBR = 1,
        VDS_PST_GPT = 2
    }

    public enum VDS_QUERY_PROVIDER_FLAG
    {
        VDS_QUERY_SOFTWARE_PROVIDERS = 0x1,
        VDS_QUERY_HARDWARE_PROVIDERS = 0x2,
        VDS_QUERY_VIRTUALDISK_PROVIDERS = 0x4
    }

    public enum VDS_STORAGE_BUS_TYPE
    {
        VDSBusTypeUnknown = 0,
        VDSBusTypeScsi = 0x1,
        VDSBusTypeAtapi = 0x2,
        VDSBusTypeAta = 0x3,
        VDSBusType1394 = 0x4,
        VDSBusTypeSsa = 0x5,
        VDSBusTypeFibre = 0x6,
        VDSBusTypeUsb = 0x7,
        VDSBusTypeRAID = 0x8,
        VDSBusTypeiScsi = 0x9,
        VDSBusTypeSas = 0xa,
        VDSBusTypeSata = 0xb,
        VDSBusTypeSd = 0xc,
        VDSBusTypeMmc = 0xd,
        VDSBusTypeMax = 0xe,
        VDSBusTypeFileBackedVirtual = 0xf,
        VDSBusTypeMaxReserved = 0x7f
    }

    public enum VDS_VDISK_STATE
    {
        VDS_VST_UNKNOWN = 0,
        VDS_VST_ADDED,
        VDS_VST_OPEN,
        VDS_VST_ATTACH_PENDING,
        VDS_VST_ATTACHED_NOT_OPEN,
        VDS_VST_ATTACHED,
        VDS_VST_DETACH_PENDING,
        VDS_VST_COMPACTING,
        VDS_VST_MERGING,
        VDS_VST_EXPANDING,
        VDS_VST_DELETED,
        VDS_VST_MAX
    }
}

Собственное решение

Ниже представлено родное приложение, использующее COM-интерфейсы VDS для:

  • Загрузить сервис VDS
  • Запрос поставщиков виртуальных дисков
  • List all the Virtual Disk handled by each provider:
    • The virtual disk's properties gives its GUID, its full driver path, its volume size and its disk file (i.e. C:\Disk.vhd).
    • Виртуальный диск также может быть запрошен как универсальный диск и дает свое имя (например, \\?\PhysicalDrive1), понятное имя и другие свойства.

#include "initguid.h"
#include "vds.h"
#include <stdio.h>

#pragma comment( lib, "ole32.lib" )
#pragma comment( lib, "rpcrt4.lib" )

// Simple macro to release non-null interfaces.
#define _SafeRelease(x) {if (NULL != x) { x->Release(); x = NULL; } }

void exploreVDiskProvider(IVdsVdProvider *pVdProvider);

int __cdecl main(void) 
{
    HRESULT hResult;
    ULONG ulFetched = 0;
    BOOL bDone = FALSE;

    IVdsServiceLoader *pLoader = NULL;
    IVdsService *pService = NULL;
    IEnumVdsObject *pProviderEnum = NULL;
    IUnknown *pUnknown = NULL;
    IVdsVdProvider *pVdProvider = NULL;

    // Initialize COM
    hResult = CoInitialize(NULL);
    if (FAILED(hResult)) goto bail;

    // For this, get a pointer to the VDS Loader
    hResult = CoCreateInstance(CLSID_VdsLoader,
        NULL,
        CLSCTX_LOCAL_SERVER,
        IID_IVdsServiceLoader,
        (void **) &pLoader);
    if (FAILED(hResult)) goto bail;

    printf("Loading VDS Service...\n");

    // Launch the VDS service. 
    hResult = pLoader->LoadService(NULL, &pService);

    // We're done with the Loader interface at this point.
    _SafeRelease(pLoader); 

    if (FAILED(hResult)) goto bail;

    // Wait for service to be ready
    hResult = pService->WaitForServiceReady();
    if (FAILED(hResult)) goto bail;

    printf("VDS Service Loaded\n");

    // Query for virtual disk providers
    hResult = pService->QueryProviders(VDS_QUERY_VIRTUALDISK_PROVIDERS, &pProviderEnum);
    if (FAILED(hResult)) goto bail;

    printf("Querying providers...\n");

    // Iterate over virtual disk providers
    while(1) 
    {
        ulFetched = 0;
        hResult = pProviderEnum->Next(1, &pUnknown, &ulFetched);
        if (FAILED(hResult)) {
            break;
        }
        if (hResult == S_FALSE) {
            break;
        }

        // Cast the current value to a virtual disk provider
        hResult = pUnknown->QueryInterface(IID_IVdsVdProvider, (void **) &pVdProvider); 
        if (FAILED(hResult)) goto bail;

        printf("VDS Virtual Disk Provider Found\n");

        exploreVDiskProvider(pVdProvider);

        _SafeRelease(pVdProvider);

        _SafeRelease(pUnknown);
    }

    getchar();
    return 0;

bail:
    printf("Failed hr=%x\n", hResult);
    return 1;
}

void exploreVDiskProvider(IVdsVdProvider *pVdProvider) {
    HRESULT hResult;
    ULONG ulFetched = 0;

    IEnumVdsObject *pVDiskEnum = NULL;
    IVdsVDisk *pVDisk = NULL;
    IUnknown *pUnknown = NULL;
    IVdsVolume *pVolume = NULL;
    VDS_VDISK_PROPERTIES vdiskProperties = { 0 };
    TCHAR *uuid = NULL;
    IVdsDisk *pDisk = NULL;
    VDS_DISK_PROP diskProperties = { 0 };

    // Query the disks handled by the provider
    hResult = pVdProvider->QueryVDisks(&pVDiskEnum);
    if (FAILED(hResult)) goto bail;

    printf("Querying virtual disks...\n");

    // Iterate over virtual disks
    while(1) 
    {
        ulFetched = 0;
        hResult = pVDiskEnum->Next(1, &pUnknown, &ulFetched);
        if (hResult == S_FALSE) {
            break;
        }

        if (FAILED(hResult)) goto bail;

        // Cast the current value to a disk
        hResult = pUnknown->QueryInterface(IID_IVdsVDisk, (void **) &pVDisk);
        if (FAILED(hResult)) goto bail;

        printf("Virtual disk Found\n");

        // Get the disk's properties and display some of them
        hResult = pVDisk->GetProperties(&vdiskProperties);
        if (FAILED(hResult)) goto bail;

        // Convert the GUID to a string
        UuidToString(&vdiskProperties.Id, (RPC_WSTR *) &uuid);

        // Dump some properties
        printf("-> Disk Id=%ws\n", uuid);
        printf("-> Disk Device Name=%ws\n", vdiskProperties.pDeviceName);
        printf("-> Disk Path=%ws\n", vdiskProperties.pPath);

        // Get the disk instance from the virtual disk
        hResult = pVdProvider->GetDiskFromVDisk(pVDisk, &pDisk);
        if (FAILED(hResult)) goto bail;

        _SafeRelease(pVDisk);

        _SafeRelease(pUnknown);

        // Get the disk's properties and display some of them
        hResult = pDisk->GetProperties(&diskProperties);
        if (FAILED(hResult)) goto bail;

        printf("-> Disk Name=%ws\n", diskProperties.pwszName);
        printf("-> Disk Friendly Name=%ws\n", diskProperties.pwszFriendlyName);
    }

    return;

bail:
    printf("Failed hr=%x\n", hResult);
}
person Laurent Etiemble    schedule 23.05.2010
comment
Я проверю это. Мне потребуется некоторое время, чтобы переписать его на C#. - person Josip Medved; 23.05.2010
comment
В Microsoft.Storage.Vds.dll отсутствует IVdsVDisk. Я предполагаю, что это была проблема с моими чеками. - person Josip Medved; 24.05.2010
comment
@Josip: я добавляю управляемое решение, основанное на частичном COM-взаимодействии. - person Laurent Etiemble; 25.05.2010
comment
@LaurentEtiemble - Может быть, мне не следует комментировать ответ старше 6 лет, но, думаю, я все равно ... Вы пропустили вызов CoUninitialize(); Согласно msdn.microsoft.com /en-us/library/windows/desktop/ — для корректного закрытия библиотеки COM каждый успешный вызов CoInitialize или CoInitializeEx, включая те, которые возвращают S_FALSE, должен быть сбалансирован соответствующим вызовом CoUninitialize. Кроме этого, хороший ответ. +1. - person J.H.; 19.10.2016
comment
Да, вы правы. Если вы владеете вещами, вы чистите их. Хорошо поймал. - person Laurent Etiemble; 20.10.2016
comment
@LaurentEtiemble А почему я не получаю VDisk в методе Dump? - person Master DJon; 02.03.2017

P/Вызов GetStorageDependencyInformation предоставит строго VHD API. Хотя эта функция не принимает номер диска в качестве входного параметра, это сделает метод-оболочка. Метод оболочки преобразует номер диска в строку вида "\\\\.\\PhysicalDriveN", которая передается в CreateFile, а результирующий дескриптор передается в GetStorageDependencyInformation. Аналогичный метод-оболочка примет ввод одной буквы диска char.
Следующий код был переведен на C# из неуправляемый пример:

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<String> pathList = new List<String>();
            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
    }

}
person LJ VanKuiken    schedule 05.01.2013
comment
это вернуло обещанное, расположение VHD с заданной буквы диска, отличное решение. - person maytham-ɯɐɥʇʎɐɯ; 08.11.2019

Официальной управляемой оболочки .NET для API виртуальных дисков не существует. Итак, на данный момент у вас есть три варианта:

  1. Запустите команду dos и очистите ответ консоли, чего делать не следует, поскольку это нестабильный API.

  2. UseMicrosoft.Storage.Vds.dll, который был добавлен в Server 2008. Вы можете использовать рефлектор .NET для проверки API. Однако это также неофициально, поскольку оно недокументировано и, следовательно, может быть изменено без предупреждения в пакетах обновления и т. Д.

  3. Используйте официальный C API. Это то, что я бы рекомендовал, пока не будет выпущен и задокументирован официальный управляемый класс-оболочка. Как отмечалось выше, полная документация по API есть все, что вам нужно. Я бы порекомендовал написать упрощенную dll-оболочку C вокруг этого API, которая делает то, что вам нужно, и не более того. Затем PInvoke ваша библиотека-оболочка.

person fmark    schedule 21.05.2010
comment
1. Уже делал, не нравится. 2. Это выглядит очень многообещающе. Я это попробую. 3. К сожалению, все эти функции принимают на вход имя файла. Для меня ввод должен быть номером диска. - person Josip Medved; 21.05.2010
comment
Как вы изначально получаете номер диска? - person NetMage; 21.05.2010
comment
Нет успеха с 2. Есть перечисления, которые проходят через каждый диск (например, Service.Providers), но нет свойства/функции, которая даст мне имя исходного файла. Хотя теперь я могу быть уверен, что, например. диск D: является виртуальным диском, я до сих пор не могу узнать, какой файл .vhd был прикреплен. - person Josip Medved; 23.05.2010
comment
@NetMage В настоящее время я получаю номер диска через WMI (все диски), а затем сравниваю его с проанализированным выводом diskpart, чтобы определить, какой из них является виртуальным диском и каково его реальное местоположение. - person Josip Medved; 23.05.2010

Не удалось проверить vdisk на моей машине. Но вы можете попробовать запросы WMI.

например. Я могу получить информацию о разделе, используя следующий код

using System;
using System.Management;
using System.Windows.Forms;

namespace WMISample
{
    public class MyWMIQuery
    {
        public static void Main()
        {
            try
            {
                ManagementObjectSearcher searcher = 
                    new ManagementObjectSearcher("root\\CIMV2", 
                    "SELECT * FROM Win32_DiskPartition"); 

                foreach (ManagementObject queryObj in searcher.Get())
                {
                    Console.WriteLine("-----------------------------------");
                    Console.WriteLine("Win32_DiskPartition instance");
                    Console.WriteLine("-----------------------------------");
                    Console.WriteLine("Access: {0}", queryObj["Access"]);
                    Console.WriteLine("Availability: {0}", queryObj["Availability"]);
                    Console.WriteLine("BlockSize: {0}", queryObj["BlockSize"]);
                    Console.WriteLine("Bootable: {0}", queryObj["Bootable"]);
                    Console.WriteLine("BootPartition: {0}", queryObj["BootPartition"]);
                    Console.WriteLine("Caption: {0}", queryObj["Caption"]);
                    Console.WriteLine("ConfigManagerErrorCode: {0}", queryObj["ConfigManagerErrorCode"]);
                    Console.WriteLine("ConfigManagerUserConfig: {0}", queryObj["ConfigManagerUserConfig"]);
                    Console.WriteLine("CreationClassName: {0}", queryObj["CreationClassName"]);
                    Console.WriteLine("Description: {0}", queryObj["Description"]);
                    Console.WriteLine("DeviceID: {0}", queryObj["DeviceID"]);
                    Console.WriteLine("DiskIndex: {0}", queryObj["DiskIndex"]);
                    Console.WriteLine("ErrorCleared: {0}", queryObj["ErrorCleared"]);
                    Console.WriteLine("ErrorDescription: {0}", queryObj["ErrorDescription"]);
                    Console.WriteLine("ErrorMethodology: {0}", queryObj["ErrorMethodology"]);
                    Console.WriteLine("HiddenSectors: {0}", queryObj["HiddenSectors"]);
                    Console.WriteLine("Index: {0}", queryObj["Index"]);
                    Console.WriteLine("InstallDate: {0}", queryObj["InstallDate"]);
                    Console.WriteLine("LastErrorCode: {0}", queryObj["LastErrorCode"]);
                    Console.WriteLine("Name: {0}", queryObj["Name"]);
                    Console.WriteLine("NumberOfBlocks: {0}", queryObj["NumberOfBlocks"]);
                    Console.WriteLine("PNPDeviceID: {0}", queryObj["PNPDeviceID"]);

                    if(queryObj["PowerManagementCapabilities"] == null)
                        Console.WriteLine("PowerManagementCapabilities: {0}", queryObj["PowerManagementCapabilities"]);
                    else
                    {
                        UInt16[] arrPowerManagementCapabilities = (UInt16[])(queryObj["PowerManagementCapabilities"]);
                        foreach (UInt16 arrValue in arrPowerManagementCapabilities)
                        {
                            Console.WriteLine("PowerManagementCapabilities: {0}", arrValue);
                        }
                    }
                    Console.WriteLine("PowerManagementSupported: {0}", queryObj["PowerManagementSupported"]);
                    Console.WriteLine("PrimaryPartition: {0}", queryObj["PrimaryPartition"]);
                    Console.WriteLine("Purpose: {0}", queryObj["Purpose"]);
                    Console.WriteLine("RewritePartition: {0}", queryObj["RewritePartition"]);
                    Console.WriteLine("Size: {0}", queryObj["Size"]);
                    Console.WriteLine("StartingOffset: {0}", queryObj["StartingOffset"]);
                    Console.WriteLine("Status: {0}", queryObj["Status"]);
                    Console.WriteLine("StatusInfo: {0}", queryObj["StatusInfo"]);
                    Console.WriteLine("SystemCreationClassName: {0}", queryObj["SystemCreationClassName"]);
                    Console.WriteLine("SystemName: {0}", queryObj["SystemName"]);
                    Console.WriteLine("Type: {0}", queryObj["Type"]);
                }
            }
            catch (ManagementException e)
            {
                MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
            }
        }
    }
}

Это дает почти все, что связано с вашей машиной. Вы можете попробовать инструмент, чтобы получить пространство имен и классы, доступные на вашем компьютере: rel="nofollow noreferrer">http://www.microsoft.com/downloads/details.aspx?familyid=2cc30a64-ea15-4661-8da4-55bbc145c30e&displaylang=en

person Sachin Chavan    schedule 24.05.2010