How to create, attach, format, mount and assign a letter to a VHD/VHDX?

Laila 40 Reputation points
2024-12-12T21:29:30.64+00:00

I'm trying to programmatically create a VHD/VHDX and perform the same "steps" seen on this vbscript.

Using C++, WINAPI without relying on diskpart:

    diskpartScriptPath = fso.GetSpecialFolder(2) & "\diskpart.txt"    
    Set file = fso.CreateTextFile(diskpartScriptPath, True)
    file.WriteLine "select disk=0"
    file.WriteLine "clean"
    file.WriteLine "convert gpt"
    file.WriteLine "create partition efi size=100"
    file.WriteLine "format quick fs=fat32 label=""System""" ' EFI partition drive letter
    file.WriteLine "create partition msr size=16"
    file.WriteLine "create partition primary"
    file.WriteLine "format quick fs=ntfs label=""Windows"""
    file.WriteLine "assign letter C"

I have been able to create the disk, attach, format, but when i try to assign a letter to it using [SetVolumeMountPointW][1] it fails with the error:

0x57 The parameter is incorrect.

Creating the disk using the [CREATE_VIRTUAL_DISK_PARAMETERS][2] Version1 works, but when i try the Version2 I always get:

error: 0x00000057 : The parameter is incorrect.:

        CREATE_VIRTUAL_DISK_PARAMETERS createParams = {};
        createParams.Version = CREATE_VIRTUAL_DISK_VERSION_2;
        createParams.Version2.UniqueId = GUID_NULL;
        createParams.Version2.MaximumSize = sizeInBytes;
        createParams.Version2.BlockSizeInBytes = 0;     // Default block size
        createParams.Version2.SectorSizeInBytes = 512;  // Standard sector size
        createParams.Version2.ParentPath = NULL;
        createParams.Version2.SourcePath = NULL;
        createParams.Version2.PhysicalSectorSizeInBytes = 4096;
        createParams.Version2.OpenFlags = OPEN_VIRTUAL_DISK_FLAG_NONE;
        createParams.Version2.ParentVirtualStorageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;
        createParams.Version2.ParentVirtualStorageType.VendorId = GUID_NULL;
        createParams.Version2.SourceVirtualStorageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;
        createParams.Version2.SourceVirtualStorageType.VendorId = GUID_NULL;
        createParams.Version2.ResiliencyGuid = GUID_NULL;

        HANDLE diskHandle = INVALID_HANDLE_VALUE;
        VIRTUAL_STORAGE_TYPE storageType =
        {
            VIRTUAL_STORAGE_TYPE_DEVICE_VHDX,
            VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
        };

        HRESULT hr = CreateVirtualDisk(
            &storageType,
            filePath.c_str(),
            VIRTUAL_DISK_ACCESS_CREATE,
            NULL,
            CREATE_VIRTUAL_DISK_FLAG_NONE,
            0,
            &createParams,
            NULL,
            &diskHandle
        );

I would like to understand why its failing on the v2.

The code is a work in progress, I'm completely lost on the partitioning part, i mean, how to properly create all the partitions similar to the previous mentioned code using diskpart.

The disk will be use to deploy a customized Windows image using dism /apply-image before applying it to a virtual machine.

Follow my current implementation:

#include <initguid.h>
#include <iostream>
#include <windows.h>
#include <virtdisk.h>
#include <diskguid.h>
#pragma comment(lib, "virtdisk.lib")
#pragma comment(lib, "Ole32.lib")



#define GUID_NULL {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}

class VHDXCreator
{
public:

    static bool CreateVHDX(const std::wstring& filePath, ULONGLONG sizeInBytes)
    {
        CREATE_VIRTUAL_DISK_PARAMETERS createParams = {};
        createParams.Version = CREATE_VIRTUAL_DISK_VERSION_1;
        createParams.Version1.UniqueId = GUID_NULL;
        createParams.Version1.MaximumSize = sizeInBytes;
        createParams.Version1.BlockSizeInBytes = 0;     // Default block size
        createParams.Version1.SectorSizeInBytes = 512;  // Standard sector size
        createParams.Version1.ParentPath = NULL;
        createParams.Version1.SourcePath = NULL;
        //createParams.Version2.PhysicalSectorSizeInBytes = 4096;
        //createParams.Version2.OpenFlags = OPEN_VIRTUAL_DISK_FLAG_NONE;
        //createParams.Version2.ParentVirtualStorageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;
        //createParams.Version2.ParentVirtualStorageType.VendorId = GUID_NULL;
        //createParams.Version2.SourceVirtualStorageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;
        //createParams.Version2.SourceVirtualStorageType.VendorId = GUID_NULL;
        //createParams.Version2.ResiliencyGuid = GUID_NULL;

		// -----        Create the VHDX        ----------

        HANDLE diskHandle = INVALID_HANDLE_VALUE;
        VIRTUAL_STORAGE_TYPE storageType =
        {
            VIRTUAL_STORAGE_TYPE_DEVICE_VHDX,
            VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
        };

        HRESULT hr = CreateVirtualDisk(
            &storageType,
            filePath.c_str(),
            VIRTUAL_DISK_ACCESS_CREATE,
            NULL,
            CREATE_VIRTUAL_DISK_FLAG_NONE,
            0,
            &createParams,
            NULL,
            &diskHandle
        );

        CloseHandle(diskHandle);

        if (hr != S_OK)
        {
            std::wcerr << L"Failed to create VHDX. Error code: " << hr << std::endl;
            return false;
        }

        // -----        Open and attach the VHDX        ----------

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

        hr = OpenVirtualDisk(
            &storageType,
            filePath.c_str(),
            VIRTUAL_DISK_ACCESS_ATTACH_RW | VIRTUAL_DISK_ACCESS_ALL, 
            OPEN_VIRTUAL_DISK_FLAG_NONE,
            &openParams,
            &diskHandle
        );

        if (hr != S_OK)
        {
			std::wcerr << L"Failed to open VHDX. Error code: " << hr << std::endl;
            CloseHandle(diskHandle);
			return false;
        }

        ATTACH_VIRTUAL_DISK_PARAMETERS attachParams;
        attachParams.Version = ATTACH_VIRTUAL_DISK_VERSION_1;

		hr = AttachVirtualDisk(  // Requires elevation
            diskHandle,
            0,
            ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER,
            0,
            &attachParams,
            NULL
        );

		if (hr != S_OK)
		{
			std::wcerr << L"Failed to attach VHDX. Error code: " << hr << std::endl;
			CloseHandle(diskHandle);
			return false;
		}

		// -----        Get the physical path of the VHDX        ----------

        WCHAR physicalPath[MAX_PATH] = { 0 };
        ULONG physicalPathSize = MAX_PATH;

        hr = GetVirtualDiskPhysicalPath(diskHandle, &physicalPathSize, physicalPath);
        if (hr != S_OK)
        {
			std::wcerr << L"Failed to get physical path of VHDX. Error code: " << hr << std::endl;
			CloseHandle(diskHandle);
			return false;
        }

		// -----        Get the device number of the VHDX        ----------

        STORAGE_DEVICE_NUMBER deviceInfo = {};

        HANDLE diskHandle2 = CreateFileW(physicalPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
        if (!diskHandle2 || diskHandle2 == INVALID_HANDLE_VALUE)
        {
			std::wcerr << L"Failed to open VHDX. Error code: " << hr << std::endl;
			CloseHandle(diskHandle);
			return false;
        }

        BOOL success = DeviceIoControl(diskHandle2, IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, &deviceInfo, sizeof(deviceInfo), 0, 0);
		int diskNumber = deviceInfo.DeviceNumber;
        if (!success|| !diskNumber)
        {
            std::wcerr << L"Failed to get device number of VHDX. Error code: " << hr << std::endl;
            CloseHandle(diskHandle);
			CloseHandle(diskHandle2);
            return false;
        }

        WCHAR  volumeName[MAX_PATH];
        HANDLE findVolumeHandle = FindFirstVolumeW(volumeName, MAX_PATH);
        bool   found = false;
        do
        {
            HANDLE handle = CreateFileW(physicalPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
            if (!handle || handle == INVALID_HANDLE_VALUE)
                continue;
        
			deviceInfo = {};
            success = DeviceIoControl(handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, &deviceInfo, sizeof(deviceInfo), 0, 0);
            CloseHandle(handle);
            if (!success)
                continue;

            DWORD volumeSerial;
            DWORD maxComponentLen;
            DWORD fsFlags;
            WCHAR fsName[MAX_PATH];
            
            GetVolumeInformationW(
                physicalPath,
                volumeName,
                MAX_PATH,
                &volumeSerial,
                &maxComponentLen,
                &fsFlags,
                fsName,
                MAX_PATH
            );

            if (deviceInfo.DeviceNumber == diskNumber)
            {
                found = true;
                break;
            }
        } while (FindNextVolumeW(findVolumeHandle, volumeName, MAX_PATH));
        
        FindVolumeClose(findVolumeHandle);
        if (!found)
        {
			std::cerr << "Failed to find the disk volume name." << std::endl;
			CloseHandle(diskHandle);
			CloseHandle(diskHandle2);
            return false;
        }

        // Create partition table directly
        DRIVE_LAYOUT_INFORMATION_EX* layout = (DRIVE_LAYOUT_INFORMATION_EX*)new BYTE[
            sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX)];
        layout->PartitionStyle = PARTITION_STYLE_GPT;
        layout->PartitionCount = 1;

        // Configure the partition
        layout->PartitionEntry[0].PartitionStyle = PARTITION_STYLE_GPT;
        layout->PartitionEntry[0].StartingOffset.QuadPart = 1024 * 1024;
        layout->PartitionEntry[0].PartitionLength.QuadPart = 0;
        layout->PartitionEntry[0].PartitionNumber = 1;
        layout->PartitionEntry[0].RewritePartition = TRUE;
        layout->PartitionEntry[0].Gpt.PartitionType = PARTITION_BASIC_DATA_GUID;
        CoCreateGuid(&layout->PartitionEntry[0].Gpt.PartitionId);

        // Apply the partition layout
        DWORD bytesReturned = 0;
        success = DeviceIoControl(
            diskHandle2,
            IOCTL_DISK_SET_DRIVE_LAYOUT_EX,
            layout,
            sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX),
            NULL,
            0,
            &bytesReturned,
            NULL);

        delete[] layout;

        if (!success)
        {
            CloseHandle(diskHandle);
            CloseHandle(diskHandle2);
            return false;
        }

        // Update disk properties
        success = DeviceIoControl(
            diskHandle2,
            IOCTL_DISK_UPDATE_PROPERTIES,
            NULL,
            0,
            NULL,
            0,
            &bytesReturned,
            NULL
        );

        //CloseHandle(diskHandle);
        //CloseHandle(diskHandle2);

        if (!success)
        {
			std::cerr << "Failed to update disk properties." << std::endl;
            return false;
        }

		// -----        Format the partition        ----------
        WCHAR formatCmd[MAX_PATH];
        swprintf_s(formatCmd, L"cmd.exe /c format %s /fs:NTFS /V:System /q /y", volumeName + 4);

        STARTUPINFO si = { sizeof(STARTUPINFO) };
        PROCESS_INFORMATION pi;
        if (!CreateProcessW(
            NULL,
            formatCmd,
            NULL,
            NULL,
            FALSE,
            CREATE_NO_WINDOW,
            NULL,
            NULL,
            &si,
            &pi))
        {
            DWORD err = GetLastError();
            return false;
        }

        // Wait for formatting to complete
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);


        // Find first available drive letter
        WCHAR driveLetter = 'J';
        WCHAR rootPath[] = L"X:\\";
        while (driveLetter <= 'Z')
        {
            rootPath[0] = driveLetter;
            if (GetDriveTypeW(rootPath) == DRIVE_NO_ROOT_DIR)
            {
                // Mount volume to drive letter
                std::wstring volumeNameStr = volumeName + 4;  // Skip the first 4 characters (\?\)
                if (volumeNameStr.back() != L'\\')
                    volumeNameStr += L'\\';

                WCHAR physicalDriveVolumeName[MAX_PATH] = {0};
                if (physicalPath[physicalPathSize - 1] != L'\\')
                {
                   physicalPath[physicalPathSize]   = L'\\';
                   physicalPath[physicalPathSize+1] = L'\0';
                }

				WCHAR volumePath[MAX_PATH];
				success = GetVolumeNameForVolumeMountPointW(physicalPath, volumePath, MAX_PATH);
                if (!success)
                {
					DWORD err = GetLastError();
					std::cerr << "Failed to get volume name for volume mount point. Error code: " << err << std::endl;
                }

                BOOL success = SetVolumeMountPointW(rootPath, volumeName);
				if (!success)
				{
					DWORD err = GetLastError();
					return true;
				}
                break;

            }
            driveLetter++;
        }

        return false;
    }
};



int main()
{

    HRESULT hrCom = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (FAILED(hrCom))
    {
        std::wcerr << L"Failed to initialize COM library" << std::endl;
        return 0;
    }

    std::wstring vhdxPath = L"C:\\disk.vhd";
    ULONGLONG diskSizeInBytes = 1LL * 1024 * 1024 * 1024; 
    VHDXCreator::CreateVHDX(vhdxPath, diskSizeInBytes);

    std::cin.get();
    return 0;
}

C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,896 questions
{count} votes

Accepted answer
  1. RLWA32 47,861 Reputation points
    2024-12-13T21:00:11.9133333+00:00

    Take a look at this sample. On a Win10 22H2 VM the output from running the code was

    vhdxcreate

    This is what diskpart showed before/after running the code

    diskpart

    The sample

    // Sample Code
    
    #include <Windows.h>
    
    #define INITGUID
    #include <guiddef.h>
    #include <vds.h>
    #include <diskguid.h>
    
    #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // some CString constructors will be explicit
    
    #include <atlbase.h>
    #include <atlstr.h>
    
    #include <stdio.h>
    #include <tchar.h>
    
    //Provider Object ID is {F26614D6-0690-4785-A5D7-978AF36CD7CF}
    //Provier Name is Microsoft Virtual Disk Service Virtual Disk Provider
    DEFINE_GUID(VDS_VIRTUAL_DISK_PROVIDER, 0xF26614D6, 0x0690, 0x4785, 0xA5, 0xD7, 0x97, 0x8A, 0xF3, 0x6C, 0xD7, 0xCF);
    
    // VDS GUID for new disk {E6AF2287-20E1-4399-A0E9-564FFE4226E8}
    DEFINE_GUID(NEW_DISK_GUID, 0xe6af2287, 0x20e1, 0x4399, 0xa0, 0xe9, 0x56, 0x4f, 0xfe, 0x42, 0x26, 0xe8);
    
    
    HRESULT GetVdsVdProvider(IVdsService* pSvc, IVdsVdProvider** ppProvider);
    HRESULT CreateVirtualDisk(IVdsVdProvider* pProvider, LPWSTR pszName, ULONGLONG cBytes, IVdsVDisk** ppVdisk);
    HRESULT AttachVirtualDisk(IVdsVDisk* pVdisk);
    HRESULT InitializeDisk(IVdsVDisk* pVdisk);
    HRESULT CreateGptPartition(IVdsDisk* pDisk, const GUID& type, ULONGLONG offset, ULONGLONG size, BOOL bRequired = FALSE);
    HRESULT FormatPartition(IVdsDisk* pDisk, ULONGLONG offset, VDS_FILE_SYSTEM_TYPE type, LPCWSTR pszLabel, DWORD dwAllocationsize);
    HRESULT AssignDriveLetter(IVdsDisk* pDisk, ULONGLONG offset, WCHAR driveletter);
    BOOL    GetFreeDriveletter(PWCH driveletter);
    
    
    class CCoInitialize {
    public:
        CCoInitialize(DWORD dwCoInit = COINIT_APARTMENTTHREADED) : m_hr(CoInitializeEx(NULL, dwCoInit)) {}
        ~CCoInitialize() { if (SUCCEEDED(m_hr)) CoUninitialize(); }
        operator HRESULT() const { return m_hr; }
        HRESULT m_hr;
    };
    
    int wmain(int argc, WCHAR* argv[])
    {
        CCoInitialize init(COINIT_MULTITHREADED);
        HRESULT hr;
        int rc = 0;
        WCHAR szVhdx[]{L"C:\\Users\\RLWA32\\AppData\\Local\\Temp\\TestDisk.vhdx"};
    
        if (SUCCEEDED(init))
        {
            CComPtr<IVdsService> pSvc;
            CComPtr<IVdsServiceLoader> pLoader;
            CComQIPtr<IVdsVdProvider> pProv;
    
            ULONGLONG MB = 1048576LL;
            ULONGLONG GB = 1024L * MB;
            ULONGLONG ullBytes = GB * 500;  // Create 500 GB vhdx
    
            hr = CoInitializeSecurity(
                NULL,
                -1,                          // COM authentication
                NULL,                        // Authentication services
                NULL,                        // Reserved
                RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
                RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
                NULL,                        // Authentication info
                EOAC_NONE,                   // Additional capabilities 
                NULL                         // Reserved
            );
    
            hr = pLoader.CoCreateInstance(CLSID_VdsLoader, nullptr, CLSCTX_LOCAL_SERVER);
            if (SUCCEEDED(hr))
            {
                hr = pLoader->LoadService(nullptr, &pSvc);
                pLoader.Release();
            }
    
            if (!pSvc)
            {
                wprintf_s(L"Could not connect to VDS, error 0x%X\n", hr);
                return 1;
            }
    
            pSvc->WaitForServiceReady();
    
            hr = GetVdsVdProvider(pSvc, &pProv);
    
            if (SUCCEEDED(hr) && pProv)
            {
                CComQIPtr<IVdsVDisk> pVdisk;
                CComPtr<IVdsDisk> pDisk;
                //CComPtr<IVdsVolume> pVolume;
    
                hr = CreateVirtualDisk(pProv, szVhdx, ullBytes, &pVdisk);
    
                if (SUCCEEDED(hr))
                {
                    wprintf_s(L"Virtual Disk %s created\n", szVhdx);
    
                    hr = AttachVirtualDisk(pVdisk);
                    if (SUCCEEDED(hr))
                    {
                        wprintf(L"Virtual Disk attached\n");
    
                        hr = InitializeDisk(pVdisk);
                        if (SUCCEEDED(hr))
                        {
                            wprintf(L"Virtual Disk initialized\n");
                            hr = pSvc->Refresh(); // Initialize disk happens outside VDS so tell it to refresh
                            hr = pProv->GetDiskFromVDisk(pVdisk, &pDisk);
                            if (SUCCEEDED(hr))
                            {
                                CComQIPtr<IVdsDisk3> p3(pDisk);
                                CComHeapPtr<VDS_DISK_FREE_EXTENT> pExtents;
                                LONG cExtents{};
                                hr = p3->QueryFreeExtents(0, &pExtents, &cExtents);
                                ATLASSERT(SUCCEEDED(hr));
                                ATLASSERT(cExtents == 1);
    
                                hr = CreateGptPartition(pDisk, PARTITION_SYSTEM_GUID, pExtents->ullOffset, 200 * MB, TRUE);
                                if (SUCCEEDED(hr))
                                {
                                    wprintf_s(L"EFI partition created\n");
                                    hr = FormatPartition(pDisk, pExtents->ullOffset, VDS_FST_FAT32, L"System", 1024);
                                    if (SUCCEEDED(hr))
                                        wprintf_s(L"EFI Fat32 partition formatted\n");
                                    else
                                        wprintf_s(L"Failed to format EFI partition, error was 0x%X\n", hr);
                                }
                                else
                                    wprintf_s(L"Failed to create EFI partition, error was 0x%X\n", hr);
    
                                pExtents.Free();
                                cExtents = 0;
                                hr = p3->QueryFreeExtents(0, &pExtents, &cExtents);
                                ATLASSERT(SUCCEEDED(hr));
                                ATLASSERT(cExtents == 1);
    
                                hr = CreateGptPartition(pDisk, PARTITION_MSFT_RESERVED_GUID, pExtents->ullOffset, 16 * MB, TRUE);
                                if (SUCCEEDED(hr))
                                    wprintf_s(L"Microsoft reserved partition created\n");
                                else
                                    wprintf_s(L"Failed to create MSR partition, error was 0x%X\n", hr);
    
                                pExtents.Free();
                                cExtents = 0;
    
                                for (int i = 3, y = 1; i >= 1; i--, y++)
                                {
                                    CComHeapPtr<VDS_DISK_FREE_EXTENT> pExtents;
                                    LONG cExtents{};
                                    hr = p3->QueryFreeExtents(0, &pExtents, &cExtents);
                                    ATLASSERT(SUCCEEDED(hr));
                                    ATLASSERT(cExtents == 1);
    
                                    hr = CreateGptPartition(pDisk, PARTITION_BASIC_DATA_GUID, pExtents->ullOffset, pExtents->ullSize/i);
                                    if (SUCCEEDED(hr))
                                    {
                                        wprintf_s(L"Data partition created\n");
    
                                        CString strLabel;
                                        strLabel.Format(L"Data%d", y);
                                        hr = FormatPartition(pDisk, pExtents->ullOffset, VDS_FST_NTFS, strLabel, 4096);
                                        if (SUCCEEDED(hr))
                                        {
                                            wprintf_s(L"NTFS data partition %s formatted\n", (LPCWSTR) strLabel);
                                            //WCHAR ch;
                                            //if (GetFreeDriveletter(&ch))
                                            //{
                                            //    hr = AssignDriveLetter(pDisk, pExtents->ullOffset, ch);
                                            //    if (SUCCEEDED(hr))
                                            //        wprintf_s(L"Drive letter %c assigned to %s\n", ch, (LPCWSTR)strLabel);
                                            //    else
                                            //        wprintf_s(L"Failed to assign drive letter, error was 0x%X\n", hr);
                                            //}
                                            //else
                                            //    wprintf_s(L"Failed to find free drive letter\n");
                                        }
                                        else
                                            wprintf_s(L"Failed to format partition, error was 0x%X\n", hr);
                                    }
                                    else
                                        wprintf_s(L"Failed to create partition, error was 0x%X\n", hr);
                                }
    
                                CComPtr<IVdsPack> pPack;
                                hr = pDisk->GetPack(&pPack);
                                if (SUCCEEDED(hr))
                                {
                                    CComPtr<IEnumVdsObject> pEnum;
                                    hr = pPack->QueryVolumes(&pEnum);
                                    if (SUCCEEDED(hr))
                                    {
                                        ULONG cFetched{};
                                        for (CComPtr<IUnknown> pUnk; pEnum->Next(1, &pUnk, &cFetched) == S_OK; pUnk.Release())
                                        {
                                            CComQIPtr<IVdsVolumeMF> pVolume(pUnk);
                                            ATLASSERT(pVolume);
                                            WCHAR szLetter[]{L"\0:\\"};
                                            WCHAR ch{};
                                            if (GetFreeDriveletter(&ch))
                                            {
                                                szLetter[0] = ch;
                                                hr = pVolume->AddAccessPath(szLetter);
                                                if (SUCCEEDED(hr))
                                                    wprintf_s(L"Assigned drive letter %c\n", ch);
                                                else
                                                    wprintf_s(L"Failed to assign driveletter %c\n", ch);
                                            }
                                        }
                                    }
                                }
                            }
                            else
                                wprintf(L"Failed to obtain IVdsDisk interface, error was 0x%X", hr);
                        }
                        else
                            wprintf_s(L"Failed to initialize virtual disk, error was 0x%X\n", hr);
                    }
                    else
                        wprintf_s(L"Failed to attach virtual disk, error was 0x%X\n", hr);
                }
                else
                    wprintf_s(L"Failed to create virtual disk, error was 0x%X\n", hr);
            }
            else
                wprintf_s(L"Failed to obtain IVdsVdProvider interface, error was 0x%X\n", hr);
        }
    
        return rc;
    }
    
    HRESULT GetVdsVdProvider(IVdsService* pSvc, IVdsVdProvider** ppProvider)
    {
        HRESULT hr = E_FAIL, hres = E_FAIL;
        CComPtr<IEnumVdsObject> pEnum;
        ULONG cFetched = 0;
    
        if (!pSvc || !ppProvider)
            return E_POINTER;
    
        hres = pSvc->QueryProviders(VDS_QUERY_VIRTUALDISK_PROVIDERS, &pEnum);
    
        if (FAILED(hres))
            return hres;
    
        for (CComPtr<IUnknown> pUnk; pEnum->Next(1, &pUnk, &cFetched) == S_OK; pUnk.Release())
        {
            VDS_PROVIDER_PROP vpp{};
            CComQIPtr<IVdsProvider> provider(pUnk);
    
            hres = provider->GetProperties(&vpp);
            if (SUCCEEDED(hres))
            {
                CoTaskMemFree(vpp.pwszName);
                CoTaskMemFree(vpp.pwszVersion);
                if (IsEqualGUID(VDS_VIRTUAL_DISK_PROVIDER, vpp.id))
                {
                    hr = provider->QueryInterface(IID_PPV_ARGS(ppProvider));
                    break;
                }
            }
        }
    
        return hr;
    }
    
    HRESULT CreateVirtualDisk(IVdsVdProvider* pProvider, LPWSTR pszName, ULONGLONG cBytes, IVdsVDisk** ppVdisk)
    {
        HRESULT hr;
        VDS_CREATE_VDISK_PARAMETERS vcvp{};
        VIRTUAL_STORAGE_TYPE vst = { VIRTUAL_STORAGE_TYPE_DEVICE_VHDX, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT };
        CComPtr<IVdsAsync> pAsync;
    
        vcvp.UniqueId = NEW_DISK_GUID;
        vcvp.BlockSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_BLOCK_SIZE;
        vcvp.MaximumSize = cBytes;
        vcvp.SectorSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_SECTOR_SIZE;
    
        hr = pProvider->CreateVDisk(&vst,
            pszName,
            nullptr,
            CREATE_VIRTUAL_DISK_FLAG_NONE,
            0, 0, &vcvp, &pAsync);
    
        if (SUCCEEDED(hr))
        {
            HRESULT hres;
            VDS_ASYNC_OUTPUT vao{};
            hr = pAsync->Wait(&hres, &vao);
    
            if (SUCCEEDED(hres))
            {
                ATLASSERT(vao.type == VDS_ASYNCOUT_CREATE_VDISK);
                ATLASSERT(vao.cvd.pVDiskUnk != nullptr);
                hr = vao.cvd.pVDiskUnk->QueryInterface(IID_PPV_ARGS(ppVdisk));
                vao.cvd.pVDiskUnk->Release();
            }
            else
                hr = E_FAIL;
        }
    
        return hr;
    }
    
    HRESULT AttachVirtualDisk(IVdsVDisk* pVdisk)
    {
        HRESULT hr;
        CComPtr<IVdsOpenVDisk> pVdsOpenVDisk;
        CComPtr<IVdsAsync> pAsync;
    
        hr = pVdisk->Open(VIRTUAL_DISK_ACCESS_ALL, OPEN_VIRTUAL_DISK_FLAG_NONE, OPEN_VIRTUAL_DISK_RW_DEPTH_DEFAULT, &pVdsOpenVDisk);
        if (SUCCEEDED(hr) && pVdsOpenVDisk)
        {
            hr = pVdsOpenVDisk->Attach(nullptr, ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER, 0, 0, &pAsync);
            if (SUCCEEDED(hr))
            {
                HRESULT hres;
                VDS_ASYNC_OUTPUT vao{};
                hr = pAsync->Wait(&hres, &vao);
    
                if (SUCCEEDED(hres))
                {
                    ATLASSERT(vao.type == VDS_ASYNCOUT_ATTACH_VDISK);
                }
            }
        }
    
        return hr;
    }
    
    HRESULT InitializeDisk(IVdsVDisk* pVdisk)
    {
        HRESULT hr;
        CComHeapPtr<WCHAR> pszDevice;
    
        hr = pVdisk->GetDeviceName(&pszDevice);
        if (SUCCEEDED(hr))
        {
            HANDLE hDisk = CreateFile(pszDevice, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, NULL);
            if (hDisk != INVALID_HANDLE_VALUE)
            {
                CREATE_DISK cd{};
                DWORD dwReturned = 0;
                cd.PartitionStyle = PARTITION_STYLE_GPT;
                cd.Gpt.MaxPartitionCount = 5;
                hr = CoCreateGuid(&cd.Gpt.DiskId);
                if (DeviceIoControl(hDisk, IOCTL_DISK_CREATE_DISK, &cd, sizeof cd, nullptr, 0, &dwReturned, NULL))
                    hr = S_OK;
                else
                    hr = AtlHresultFromLastError();
    
                CloseHandle(hDisk);
            }
            else
                hr = AtlHresultFromLastError();
        }
    
        return hr;
    }
    
    HRESULT CreateGptPartition(IVdsDisk* pDisk, const GUID& type, ULONGLONG offset, ULONGLONG size, BOOL bRequired)
    {
        HRESULT hr{};
        CREATE_PARTITION_PARAMETERS cpp{};
        CComQIPtr<IVdsAdvancedDisk> pAdvdisk(pDisk);
        CComPtr<IVdsAsync> pAsync;
    
        if (!pAdvdisk)
            return E_NOINTERFACE;
    
        cpp.style = VDS_PST_GPT;
        cpp.GptPartInfo.partitionType = type;
        cpp.GptPartInfo.attributes = bRequired ? GPT_ATTRIBUTE_PLATFORM_REQUIRED : 0;
    
        hr = pAdvdisk->CreatePartition(offset, size, &cpp, &pAsync);
        if (SUCCEEDED(hr))
        {
            HRESULT hres;
            VDS_ASYNC_OUTPUT vao{};
            hr = pAsync->Wait(&hres, &vao);
            if (SUCCEEDED(hres))
            {
                ATLASSERT(vao.type == VDS_ASYNCOUT_CREATEPARTITION);
            }
            else
                hr = hres;
        }
    
        return hr;
    }
    
    HRESULT FormatPartition(IVdsDisk* pDisk, ULONGLONG offset, VDS_FILE_SYSTEM_TYPE type, LPCWSTR pszLabel, DWORD dwAllocationsize)
    {
        HRESULT hr{};
        CComQIPtr<IVdsAdvancedDisk> pAdvdisk(pDisk);
        CComPtr<IVdsAsync> pAsync;
    
        if (!pAdvdisk)
            return E_NOINTERFACE;
    
        hr = pAdvdisk->FormatPartition(offset, type , const_cast<LPWSTR>(pszLabel), dwAllocationsize, FALSE, TRUE, FALSE, &pAsync);
    
        if (SUCCEEDED(hr))
        {
            HRESULT hres;
            VDS_ASYNC_OUTPUT vao{};
            hr = pAsync->Wait(&hres, &vao);
    
            if (SUCCEEDED(hres))
            {
                ATLASSERT(vao.type == VDS_ASYNCOUT_FORMAT);
            }
            else
                hr = hres;
        }
    
        return hr;
    }
    
    HRESULT AssignDriveLetter(IVdsDisk* pDisk, ULONGLONG offset, WCHAR driveletter)
    {
        CComQIPtr<IVdsAdvancedDisk> pAdvdisk(pDisk);
        
        if (!pAdvdisk)
            return E_NOINTERFACE;
    
        return pAdvdisk->AssignDriveLetter(offset, driveletter);
    }
    
    BOOL GetFreeDriveletter(PWCH driveletter)
    {
        DWORD drives = GetLogicalDrives();
    
        for (int i = 2; i < 26; i++)  // Skip A & B
        {
            if ((drives & (1 << i)) == 0)
            {
                *driveletter = L'A' + i;
                return TRUE;
            }
        }
    
        return FALSE;
    }
    
    

    I used VS2022 17.12.3 to build the sample.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.