Программное подключение виртуального жесткого диска Microsoft (VHD)
Я пытаюсь установить виртуальный жесткий диск (.VHD) используя функцию API Windows 7, но я не могу найти соответствующую функцию, существует ли она?
я программирую на C++, используя Visual Studio 2010, для информации.
спасибо заранее ;)
2 ответов
Это старый вопрос, но у него все еще нет ответа, поэтому я предоставлю его, если кто-то наткнется на него, как я.
прикрепление VHD
для полной ссылки на MSDN [VHD Reference]: 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. Если вы хотите выбрать, какая буква диска используется для его монтирования, продолжайте читать.
назначение буквы диска
во-первых, добавьте ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER
флаг для вашего вызова AttachVirtualDisk, чтобы он не выполнял это автоматическое назначение букв. Затем вам нужно будет найти путь Тома томов VHD [он имеет следующий формат:\\?\Volume{GUID}]:
wchar_t physicalDrive[MAX_PATH];
ULONG bufferSize = sizeof(physicalDrive);
GetVirtualDiskPhysicalPath(vhdHandle, &bufferSize, physicalDrive);
теперь у вас будет физический путь вашего подключенного VHD на физическом диске в следующем формате: \\.\PhysicalDrive#, где # - номер диска, который вам понадобится для поиска томов VHD с помощью FindFirstVolume / FindNextVolume. Извлеките число и преобразуйте его в целое число, и вы будете готовы к следующему фрагменту кода:
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++ из-за вопроса, но не пытался его скомпилировать. Если вы найдете ошибки в коде, пожалуйста, отредактируйте его или дайте мне знать, чтобы я мог это сделать.
правки: опечатки, включает в себя и lib, забыл FindVolumeClose, предупреждения высоты
по запросу я отправляю код c#. Для получения дополнительной информации, см. Мой другой ответ. Код использует тот же API из virtdisk.dll, я не думаю, что это возможно сделать с управляемым API, но если я ошибаюсь, пожалуйста, дайте мне знать.
хорошо, во-первых, объявления взаимодействия:
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=против 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 на этой машине я его не компилировал. Пожалуйста, дайте мне знать, если я сделал ошибку, чтобы я мог ее исправить.