Take a look at this sample. On a Win10 22H2 VM the output from running the code was
This is what diskpart showed before/after running the code
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.