以编程方式挂载Microsoft虚拟硬盘(VHD)

12

我正在尝试使用Windows 7 API函数挂载虚拟硬盘(.VHD),但我找不到相关的函数,是否存在一个?

我正在使用Visual Studio 2010以C++进行编程,供参考。

提前感谢您的帮助 ;)


1
@CaptainObvlious,这似乎是对问题的一个相当全面的回答。也许你可以重新回答一下这个问题,不包括磁盘创建代码? - Anya Shenanigans
有趣,谢谢。请问如何分配驱动器字母? - Swatcat
@Petesh 如果在我完成代码审查之前没有发布合适的答案,我计划着更新它。在此期间,任何想要使用它或将其作为答案基础的人都可以自由地这样做(一如既往)。 - Captain Obvlious
@Captain Oblivious,你的回答已经解决了95%的问题,谢谢。唯一让我不确定的是如何给VHD分配一个驱动器号。 - Swatcat
@Msalters,我该怎么知道它使用了哪个驱动器号码呢? - Swatcat
显示剩余5条评论
3个回答

20

虽然这是一个旧问题,但它仍没有答案,所以我会提供一个答案,以防像我一样的人偶然发现它。

挂载VHD

有关完整的MSDN参考[VHD参考]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd323700(v=vs.85).aspx

OPEN_VIRTUAL_DISK_PARAMETERS openParameters;
openParameters.Version = OPEN_VIRTUAL_DISK_VERSION_1;
openParameters.Version1.RWDepth = OPEN_VIRTUAL_DISK_RW_DEPTH_DEFAULT;

VIRTUAL_STORAGE_TYPE storageType;
storageType.DeviceID = VIRTUAL_STORAGE_TYPE_DEVICE_VHD;
storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT;

ATTACH_VIRTUAL_DISK_PARAMETERS attachParameters;
attachParameters.Version = ATTACH_VIRTUAL_DISK_VERSION_1;

HANDLE vhdHandle;

if (OpenVirtualDisk(&openStorageType, "{VHD PATH GOES HERE}", 
        VIRTUAL_DISK_ACCESS_ALL, OPEN_VIRTUAL_DISK_FLAG_NONE, 
        &openParameters, &vhdHandle) != ERROR_SUCCESS) {
    // If return value of OpenVirtualDisk isn't ERROR_SUCCESS, there was a problem opening the VHD
}

// Warning: AttachVirtualDisk requires elevation
if (AttachVirtualDisk(vhdHandle, 0, ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME,
        0, &attachParameters, 0) != ERROR_SUCCESS) {
    // If return value of AttachVirtualDisk isn't ERROR_SUCCESS, there was a problem attach the disk
}

VHD成功附加,现在它将像任何其他物理磁盘一样显示,并且驱动器号将自动分配给VHD包含的卷。如果您想选择要用于挂载它的驱动器号,请继续阅读。

分配驱动器号

首先,在AttachVirtualDisk调用中添加ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER标志,以便它不会自动分配驱动器号。接下来,您需要找到VHD卷的卷路径[它具有此格式:\\?\ Volume {GUID}]:

wchar_t physicalDrive[MAX_PATH];
ULONG bufferSize = sizeof(physicalDrive);
GetVirtualDiskPhysicalPath(vhdHandle, &bufferSize, physicalDrive);

现在,您将拥有附加的VHD在物理驱动器中的物理路径,格式如下:\\.\PhysicalDrive#,其中#是您需要使用FindFirstVolume/FindNextVolume查找VHD卷的驱动器号。提取数字并将其转换为整数,您就可以准备好下一段代码了。
char volumeName[MAX_PATH];
DWORD bytesReturned;
VOLUME_DISK_EXTENTS diskExtents;    
HANDLE hFVol = FindFirstVolume(volumeName, sizeof(volumeName)); 
bool hadTrailingBackslash = false;

do {
    // I had a problem where CreateFile complained about the trailing \ and
    // SetVolumeMountPoint desperately wanted the backslash there. I ended up 
    // doing this to get it working but I'm not a fan and I'd greatly 
    // appreciate it if someone has any further info on this matter
    int backslashPos = strlen(volumeName) - 1;
    if (hadTrailingBackslash = volumeName[backslashPos] == '\\') {
        volumeName[backslashPos] = 0;
    }

    HANDLE hVol = CreateFile(volumeName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    if (hVol == INVALID_HANDLE_VALUE) {
        return;
    }

    DeviceIoControl(hVol, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL,
        0, &diskExtents, sizeof(diskExtents), &bytesReturned, NULL);

    // If the volume were to span across multiple physical disks, you'd find 
    // more than one Extents here but we don't have to worry about that with VHD
    // Note that 'driveNumber' would be the integer you extracted out of 
    // 'physicalDrive' in the previous snippet
    if (diskExtents.Extents[0].DiskNumber == driveNumber) {
        if (hadTrailingBackslash) {
            volumeName[backslashPos] = '\\';
        }

        // Found volume that's on the VHD, let's mount it with a letter of our choosing.
        // Warning: SetVolumeMountPoint requires elevation
        SetVolumeMountPoint("H:\\", volumeName);
    } 
} while (FindNextVolume(hFVol, volumeName, sizeof(volumeName)));
FindVolumeClose(hFVol);

不要忘记包含这些并链接到该库:
#define WINVER _WIN32_WINNT_WIN7
#include <windows.h>
#include <winioctl.h>
#include <virtdisk.h>

#pragma comment(lib, "virtdisk.lib")

免责声明:这是我在一个C#代码库中正在做的事情,我将代码翻译成了C/C++,因为问题而没有尝试实际编译它。如果您发现代码中有错误,请编辑它或告诉我,以便我可以进行更正。
编辑:错别字、包含和库(文件夹)、忘记了FindVolumeClose函数、权限提升警告。

1
当然,给我一两天时间! - Pixy
抱歉耽搁了,我正在处理。由于代码比较多,我会将其作为独立的答案发布。 - Pixy
如果您在标志参数中指定0,则CreateFile无法打开卷。我想我在某个地方读到过,您必须指定“FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS”。 - Keith Russell

10

作为要求,我将发布C#代码。有关更多信息,请参见我的其他答案。该代码使用与virtdisk.dll相同的API,我认为不可能使用托管API完成,但如果我错了,请告诉我。

首先是Interop声明:

    private const Int32 ERROR_SUCCESS = 0;
    private const int OPEN_VIRTUAL_DISK_RW_DEPTH_DEFAULT = 1;
    private const int VIRTUAL_STORAGE_TYPE_DEVICE_VHD = 2;
    private IntPtr INVALID_HANDLE_VALUE = (IntPtr) (-1);

    private static readonly Guid VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT = new Guid("EC984AEC-A0F9-47e9-901F-71415A66345B");

    enum ComClassContext : uint {
        CLSCTX_INPROC_SERVER           = 0x1,
        CLSCTX_INPROC_HANDLER          = 0x2,
        CLSCTX_LOCAL_SERVER            = 0x4,
        CLSCTX_INPROC_SERVER16         = 0x8,
        CLSCTX_REMOTE_SERVER           = 0x10,
        CLSCTX_INPROC_HANDLER16        = 0x20,
        CLSCTX_RESERVED1               = 0x40,
        CLSCTX_RESERVED2               = 0x80,
        CLSCTX_RESERVED3               = 0x100,
        CLSCTX_RESERVED4               = 0x200,
        CLSCTX_NO_CODE_DOWNLOAD        = 0x400,
        CLSCTX_RESERVED5               = 0x800,
        CLSCTX_NO_CUSTOM_MARSHAL       = 0x1000,
        CLSCTX_ENABLE_CODE_DOWNLOAD    = 0x2000,
        CLSCTX_NO_FAILURE_LOG          = 0x4000,
        CLSCTX_DISABLE_AAA             = 0x8000,
        CLSCTX_ENABLE_AAA              = 0x10000,
        CLSCTX_FROM_DEFAULT_CONTEXT    = 0x20000,
        CLSCTX_ACTIVATE_32_BIT_SERVER  = 0x40000,
        CLSCTX_ACTIVATE_64_BIT_SERVER  = 0x80000,
        CLSCTX_ENABLE_CLOAKING         = 0x100000,
        CLSCTX_APPCONTAINER            = 0x400000,
        CLSCTX_ACTIVATE_AAA_AS_IU      = 0x800000,
        CLSCTX_PS_DLL                  = 0x80000000
    };

    private enum IO_CONTROL_CODE : uint {
        GET_VOLUME_DISK_EXTENTS = 5636096,
        STORAGE_DEVICE_NUMBER = 2953344
    }

    private enum ATTACH_VIRTUAL_DISK_FLAG : int {
        ATTACH_VIRTUAL_DISK_FLAG_NONE = 0x00000000,
        ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY = 0x00000001,
        ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER = 0x00000002,
        ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME = 0x00000004,
        ATTACH_VIRTUAL_DISK_FLAG_NO_LOCAL_HOST = 0x00000008
    }

    private enum ATTACH_VIRTUAL_DISK_VERSION : int {
        ATTACH_VIRTUAL_DISK_VERSION_UNSPECIFIED = 0,
        ATTACH_VIRTUAL_DISK_VERSION_1 = 1
    }

    private enum OPEN_VIRTUAL_DISK_FLAG : int {
        OPEN_VIRTUAL_DISK_FLAG_NONE = 0x00000000,
        OPEN_VIRTUAL_DISK_FLAG_NO_PARENTS = 0x00000001,
        OPEN_VIRTUAL_DISK_FLAG_BLANK_FILE = 0x00000002,
        OPEN_VIRTUAL_DISK_FLAG_BOOT_DRIVE = 0x00000004
    }

    private enum OPEN_VIRTUAL_DISK_VERSION : int {
        OPEN_VIRTUAL_DISK_VERSION_1 = 1
    }

    private enum VIRTUAL_DISK_ACCESS_MASK : int {
        VIRTUAL_DISK_ACCESS_ATTACH_RO = 0x00010000,
        VIRTUAL_DISK_ACCESS_ATTACH_RW = 0x00020000,
        VIRTUAL_DISK_ACCESS_DETACH = 0x00040000,
        VIRTUAL_DISK_ACCESS_GET_INFO = 0x00080000,
        VIRTUAL_DISK_ACCESS_CREATE = 0x00100000,
        VIRTUAL_DISK_ACCESS_METAOPS = 0x00200000,
        VIRTUAL_DISK_ACCESS_READ = 0x000d0000,
        VIRTUAL_DISK_ACCESS_ALL = 0x003f0000,
        VIRTUAL_DISK_ACCESS_WRITABLE = 0x00320000
    }

    private enum DETACH_VIRTUAL_DISK_FLAG : int {
        DETACH_VIRTUAL_DISK_FLAG_NONE = 0x00000000
    }

    private enum GET_VIRTUAL_DISK_INFO_VERSION {
        GET_VIRTUAL_DISK_INFO_UNSPECIFIED = 0,
        GET_VIRTUAL_DISK_INFO_SIZE = 1,
        GET_VIRTUAL_DISK_INFO_IDENTIFIER = 2,
        GET_VIRTUAL_DISK_INFO_PARENT_LOCATION = 3,
        GET_VIRTUAL_DISK_INFO_PARENT_IDENTIFIER = 4,
        GET_VIRTUAL_DISK_INFO_PARENT_TIMESTAMP = 5,
        GET_VIRTUAL_DISK_INFO_VIRTUAL_STORAGE_TYPE = 6,
        GET_VIRTUAL_DISK_INFO_PROVIDER_SUBTYPE = 7,
        GET_VIRTUAL_DISK_INFO_IS_4K_ALIGNED = 8,
        GET_VIRTUAL_DISK_INFO_PHYSICAL_DISK = 9,
        GET_VIRTUAL_DISK_INFO_VHD_PHYSICAL_SECTOR_SIZE = 10, // 0xA
        GET_VIRTUAL_DISK_INFO_SMALLEST_SAFE_VIRTUAL_SIZE = 11,
        GET_VIRTUAL_DISK_INFO_FRAGMENTATION = 12
    }

    private enum GENERIC_ACCESS_RIGHTS_FLAGS : uint {
        GENERIC_READ = 0x80000000,
        GENERIC_WRITE = 0x40000000,
        GENERIC_EXECUTE = 0x20000000,
        GENERIC_ALL = 0x10000000
    }

    private enum FILE_SHARE_MODE_FLAGS : int {
        FILE_SHARE_READ     = 0x00000001,
        FILE_SHARE_WRITE    = 0x00000002
    }

    private enum CREATION_DISPOSITION_FLAGS : int {
        CREATE_NEW          = 1,
        CREATE_ALWAYS       = 2,
        OPEN_EXISTING       = 3,
        OPEN_ALWAYS         = 4,
        TRUNCATE_EXISTING   = 5
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    struct DISK_EXTENT {
        public Int32 diskNumber;
        public Int64 startingOffset;
        public Int64 extentLength;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    private struct VOLUME_DISK_EXTENTS {
        public Int32 numberOfDiskExtents;
        public DISK_EXTENT[] extents;
    }


    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct ATTACH_VIRTUAL_DISK_PARAMETERS {
        public ATTACH_VIRTUAL_DISK_VERSION version;
        public ATTACH_VIRTUAL_DISK_PARAMETERS_Version1 version1;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct ATTACH_VIRTUAL_DISK_PARAMETERS_Version1 {
        public Int32 reserved;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct OPEN_VIRTUAL_DISK_PARAMETERS {
        public OPEN_VIRTUAL_DISK_VERSION version;
        public OPEN_VIRTUAL_DISK_PARAMETERS_Version1 version1;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct OPEN_VIRTUAL_DISK_PARAMETERS_Version1 {
        public Int32 rwDepth;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct VIRTUAL_STORAGE_TYPE {
        public Int32 deviceId;
        public Guid vendorId;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct STORAGE_DEVICE_NUMBER {
        public Int32 deviceType;
        public Int32 deviceNumber;
        public Int32 partitionNumber;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct GET_VIRTUAL_DISK_INFO_SIZE {
        public GET_VIRTUAL_DISK_INFO_VERSION version;
        public UInt64 virtualSize;
        public UInt64 physicalSize;
        public UInt32 blockSize;
        public UInt32 sectorSize;
    }


    [DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
    private static extern Int32 AttachVirtualDisk(IntPtr virtualDiskHandle, IntPtr securityDescriptor, ATTACH_VIRTUAL_DISK_FLAG flags, Int32 providerSpecificFlags, ref ATTACH_VIRTUAL_DISK_PARAMETERS parameters, IntPtr overlapped);

    [DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
    private static extern Int32 DetachVirtualDisk(IntPtr virtualDiskHandle, DETACH_VIRTUAL_DISK_FLAG flags, Int32 providerSpecificFlags);

    [DllImportAttribute("kernel32.dll", SetLastError = true)]
    [return: MarshalAsAttribute(UnmanagedType.Bool)]
    private static extern Boolean CloseHandle(IntPtr hObject);

    [DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
    private static extern Int32 OpenVirtualDisk(ref VIRTUAL_STORAGE_TYPE virtualStorageType, String path, VIRTUAL_DISK_ACCESS_MASK virtualDiskAccessMask, OPEN_VIRTUAL_DISK_FLAG flags, ref OPEN_VIRTUAL_DISK_PARAMETERS parameters, ref IntPtr handle);

    [DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
    private static extern Int32 GetVirtualDiskPhysicalPath(IntPtr virtualDiskHandle, ref Int32 diskPathSizeInBytes, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder diskPath);

    [DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
    private static extern Int32 GetVirtualDiskInformation(IntPtr virtualDiskHandle, ref UInt32 virtualDiskInfoSize, ref GET_VIRTUAL_DISK_INFO_SIZE virtualDiskInfo, IntPtr sizeUsed);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    private static extern IntPtr FindFirstVolume([MarshalAs(UnmanagedType.LPTStr)] StringBuilder volumeName, Int32 bufferLength);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool FindVolumeClose(IntPtr findVolumeHandle);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool FindNextVolume(IntPtr findVolumeHandle, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder volumeName, Int32 bufferLength);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPTStr)] string fileName, GENERIC_ACCESS_RIGHTS_FLAGS desiredAccess, FILE_SHARE_MODE_FLAGS shareMode, IntPtr securityAttribute, CREATION_DISPOSITION_FLAGS creationDisposition, Int32 flagsAndAttributes, IntPtr templateFile);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool DeviceIoControl(IntPtr deviceHandle, IO_CONTROL_CODE controlCode, IntPtr inBuffer, uint inBufferSize, ref STORAGE_DEVICE_NUMBER outBuffer, uint outBufferSize, ref uint bytesReturned, IntPtr overlapped);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetVolumeMountPoint([MarshalAs(UnmanagedType.LPTStr)] string mountPoint, [MarshalAs(UnmanagedType.LPTStr)] string volumeName);

连接VHD文件

var attachParameters = new ATTACH_VIRTUAL_DISK_PARAMETERS() {
  version = ATTACH_VIRTUAL_DISK_VERSION.ATTACH_VIRTUAL_DISK_VERSION_1
};
var openParameters = new OPEN_VIRTUAL_DISK_PARAMETERS() {
  version = OPEN_VIRTUAL_DISK_VERSION.OPEN_VIRTUAL_DISK_VERSION_1,
  version1 = {
    rwDepth = OPEN_VIRTUAL_DISK_RW_DEPTH_DEFAULT
  }
};
var openStorageType = new VIRTUAL_STORAGE_TYPE() {
  deviceId = VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
  vendorId = VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
};
IntPtr vhdHandle;

if (OpenVirtualDisk(ref openStorageType, vhdPath, VIRTUAL_DISK_ACCESS_MASK.VIRTUAL_DISK_ACCESS_ALL,
      OPEN_VIRTUAL_DISK_FLAG.OPEN_VIRTUAL_DISK_FLAG_NONE, ref openParameters,
      ref this._vhdHandle) != ERROR_SUCCESS) {
  throw new CannotMountException("The VHD cannot be accessed [OpenVirtualDisk failed]");
}

if (AttachVirtualDisk(
      this._vhdHandle, IntPtr.Zero,
      ATTACH_VIRTUAL_DISK_FLAG.ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME | ATTACH_VIRTUAL_DISK_FLAG.ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER,
      0, ref attachParameters, IntPtr.Zero
    ) != ERROR_SUCCESS) {
  this._closeVhd();
  throw new CannotMountException("The VHD cannot be accessed to install the drivers [AttachVirtualDisk failed]");
}

分配驱动器字母

首先,让我们找到vhd路径:

private int _findVhdPhysicalDriveNumber(vhdHandle) {
  int driveNumber;
  int bufferSize = 260;
  StringBuilder vhdPhysicalPath = new StringBuilder(bufferSize);

  GetVirtualDiskPhysicalPath(vhdHandle, ref bufferSize, vhdPhysicalPath);
  Int32.TryParse(Regex.Match(vhdPhysicalPath.ToString(), @"\d+").Value, out driveNumber);
  return driveNumber;
}

private string _findVhdVolumePath(vhdHandle) {
  int vhdPhysicalDrive = this._findVhdPhysicalDriveNumber(vhdHandle);
  StringBuilder volumeName = new StringBuilder(260);
  IntPtr findVolumeHandle;
  IntPtr volumeHandle;
  STORAGE_DEVICE_NUMBER deviceNumber = new STORAGE_DEVICE_NUMBER();
  uint bytesReturned = 0;
  bool found = false;

  findVolumeHandle = FindFirstVolume(volumeName, volumeName.Capacity);
  do {
    int backslashPos = volumeName.Length - 1;
    if (volumeName[backslashPos] == '\\') {
      volumeName.Length--;
    }
    volumeHandle = CreateFile(volumeName.ToString(), 0, FILE_SHARE_MODE_FLAGS.FILE_SHARE_READ | FILE_SHARE_MODE_FLAGS.FILE_SHARE_WRITE,
        IntPtr.Zero, CREATION_DISPOSITION_FLAGS.OPEN_EXISTING, 0, IntPtr.Zero);
    if (volumeHandle == INVALID_HANDLE_VALUE) {
      continue;
    }

    DeviceIoControl(volumeHandle, IO_CONTROL_CODE.STORAGE_DEVICE_NUMBER, IntPtr.Zero, 0,
        ref deviceNumber, (uint) Marshal.SizeOf(deviceNumber), ref bytesReturned, IntPtr.Zero);

    if (deviceNumber.deviceNumber == vhdPhysicalDrive) {
      found = true;
      break;
    }
  } while (FindNextVolume(findVolumeHandle, volumeName, volumeName.Capacity));
  FindVolumeClose(findVolumeHandle);
  //************************
  return found ? volumeName.ToString() : ""; //when It returns "" then the error occurs
}

我们现在可以将VHD挂载到我们选择的驱动器号(或者任何文件夹,参见https://msdn.microsoft.com/en-ca/library/windows/desktop/aa365561(v=vs.85).aspx)。

mountPoint可以是此格式的驱动器号:X:\ 或者是指向一个空文件夹的路径。
      private void _mountVhdToDriveLetter(string vhdVolumePath, string mountPoint) {
            //Autoplay cancelAutoplay = new Autoplay();
            this._mountedDriveLetter = mountPoint;

            if (vhdVolumePath[vhdVolumePath.Length - 1] != '\\') { //**
                vhdVolumePath += '\\';
            }

            if (!SetVolumeMountPoint(mountPoint, vhdVolumePath)) {
                throw new CannotMountException("The VHD cannot be accessed to install the drivers [SetVolumeMountPoint failed]");
            }
        }

就是这样了。

免责声明:我从一个较大的代码库中提取了这些片段,并尽力删除了其他组件的依赖关系,但由于我在这台机器上没有安装Visual Studio,所以我没有编译它。如果我犯了错误,请告诉我,以便我进行更正。


1
你是我的英雄/女英雄... k**** - ZEE
不错,但是在 _findVhdPhysicalDriveNumber() 中应该使用 IOCTL_STORAGE_GET_DEVICE_NUMBER 而不是正则表达式字符串解析。 - Axel Rietschin
还要注意,在卷枚举循环的 C 版本中使用 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS(正确),而在上面的 C# 版本中使用不同的 IOCTL。 - Axel Rietschin
AttachVirtualDisk 还需要启用 SE_MANAGE_VOLUME_NAME 特权。否则我们的朋友 1314(客户端没有所需特权)将会弹出。 - Cristian Amarie
如果有人有演示/源代码,请分享链接,以上代码对我无效。 - jishan siddique
显示剩余2条评论

1

这个答案对Pixy的回答进行了轻微修改(被采纳的答案)。其中我不喜欢的一件事是在winapi可用时使用do-while循环。你可以使用"GetVolumeNameForVolumeMountPointW"来达到同样的效果。下面的片段是用于挂载ISO的,但你可以添加类似的代码来处理VHD。VERIFY和Log语句可以分别替换为assert/return和printf/cout。

    VIRTUAL_STORAGE_TYPE openStorageType;
    OPEN_VIRTUAL_DISK_PARAMETERS openParameters;
    ATTACH_VIRTUAL_DISK_PARAMETERS attachParameters;

    openStorageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_ISO;
    openStorageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT;

    openParameters.Version = OPEN_VIRTUAL_DISK_VERSION_1;
    openParameters.Version1.RWDepth = 0;

    attachParameters.Version = ATTACH_VIRTUAL_DISK_VERSION_1;
    
    VERIFY_ARE_EQUAL (OpenVirtualDisk(&openStorageType, isoFile, VIRTUAL_DISK_ACCESS_READ | VIRTUAL_DISK_ACCESS_ATTACH_RO , OPEN_VIRTUAL_DISK_FLAG_NONE, 
        &openParameters, &mountHandle), static_cast<DWORD>(ERROR_SUCCESS) );

    VERIFY_ARE_EQUAL (AttachVirtualDisk(mountHandle, 0, ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY | ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME | ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER,
        0, &attachParameters, 0), static_cast<DWORD>(ERROR_SUCCESS) );

    WCHAR physicalDrive[MAX_PATH];
    ULONG bufferSize = sizeof(physicalDrive);
    VERIFY_ARE_EQUAL (GetVirtualDiskPhysicalPath(mountHandle, &bufferSize, physicalDrive),  static_cast<DWORD>(ERROR_SUCCESS) );
    bufferSize = wcslen(physicalDrive);
    if (physicalDrive[bufferSize - 1] != L'\\')
    {
        physicalDrive[bufferSize] = L'\\';
        physicalDrive[bufferSize+1] = L'\0';
    }
    Log::Comment(physicalDrive, L"Physical drive");

    WCHAR physicalDriveVolumeName[MAX_PATH] = {0};
    if (!GetVolumeNameForVolumeMountPointW (physicalDrive, physicalDriveVolumeName, MAX_PATH))
    {
        VERIFY_ARE_EQUAL(static_cast<DWORD>(ERROR_SUCCESS), GetLastError());
    }
    bufferSize = wcslen(physicalDriveVolumeName);
    if (physicalDriveVolumeName[bufferSize - 1] != L'\\')
    {
        physicalDriveVolumeName[bufferSize] = L'\\';
        physicalDriveVolumeName[bufferSize+1] = L'\0';
    }
    Log::Comment(physicalDriveVolumeName, L"Physical volume name");

    if (!SetVolumeMountPoint(path, physicalDriveVolumeName)) // Here path can be X:\ (Mount point) or X:\mount\ (Reparse point)
    {
        VERIFY_ARE_EQUAL(static_cast<DWORD>(ERROR_SUCCESS), GetLastError());
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接