Domanda Montare a livello di codice un Microsoft Virtual Hard Drive (VHD)


Sto provando a montare un Virtual Hard Drive (.VHD) usando la funzione API di Windows 7, ma non riesco a trovare la funzione rilevante, ne esiste una?

Sto programmando in C ++ usando Visual Studio 2010, per informazioni.

Grazie in anticipo ;)


10
2018-06-24 21:29


origine


risposte:


Questa è una vecchia domanda, ma non ha ancora una risposta, quindi ne fornirò una nel caso in cui qualcuno ci inciampi sopra come ho fatto io.

Allegare il VHD

Per il riferimento completo su MSDN [Riferimento 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 collegato correttamente, ora verrà visualizzato come qualsiasi altro disco fisico e una lettera di unità verrà automaticamente assegnata ai volumi contenuti nel VHD. Se desideri scegliere quale lettera di unità viene utilizzata per montarla, continua a leggere.

Assegnare una lettera di unità

Innanzitutto, aggiungi il ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER segnala la chiamata a AttachVirtualDisk in modo che non esegua questa assegnazione automatica delle lettere. Successivamente, dovrai trovare il percorso del volume dei volumi VHD [ha questo formato: \\? \ Volume {GUID}]:

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

Ora avrai il percorso fisico del tuo VHD collegato nell'unità fisica nel seguente formato: \\. \ PhysicalDrive # dove # è il numero di unità che dovrai trovare nei volumi VHD con FindFirstVolume / FindNextVolume. Estrai il numero e convertilo in un numero intero e sarai pronto per il prossimo pezzo di codice:

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);

Non dimenticare questi include e link a questa libreria:

#define WINVER _WIN32_WINNT_WIN7
#include <windows.h>
#include <winioctl.h>
#include <virtdisk.h>

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

Disclaimer: Questo è qualcosa che stavo facendo in un codice C #, ho tradotto il codice in C / C ++ a causa della domanda ma non ho ancora provato a compilarlo. Se trovi errori nel codice, per favore modificalo o fammi sapere così posso farlo.

Modifiche: errori di battitura, include e lib, dimenticato FindVolumeClose, avvisi di elevazione


14
2017-12-22 23:00



Come richiesto, sto postando il codice C #. Per ulteriori informazioni, vedere la mia altra risposta. Il codice utilizza la stessa API da virtdisk.dll, non penso sia possibile farlo con l'API gestita, ma se mi sbaglio, fatemelo sapere.

Va bene prima cosa, dichiarazioni di interoperabilità:

    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);

Allegare il 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]");
}

Assegnare una lettera di unità

Per prima cosa, troviamo il percorso 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
}

Ora possiamo montare il VHD su una lettera di unità di nostra scelta (o qualsiasi altra cartella per quella materia, vedi https://msdn.microsoft.com/en-ca/library/windows/desktop/aa365561(v=vs.85).aspx) mountPoint può essere una lettera di unità in questo formato: X: \ o un percorso di una cartella vuota

      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]");
            }
        }

Questo dovrebbe essere.

Dichiarazione di non responsabilità: ho estratto questi frammenti da una base di codice più grande e ho cercato di rimuovere le dipendenze da altri componenti, tuttavia, poiché non ho Visual Studio su questa macchina, non l'ho compilato. Per favore fatemi sapere se ho commesso un errore in modo da poterlo correggere.


9
2018-03-31 02:16