Search code examples
c++hyper-vwindows-server-2016change-trackingvirtual-disk

VirtDisk.h QueryChangesVirtualDisk() function return RangeCount as 0


https://learn.microsoft.com/ko-kr/windows/win32/api/virtdisk/nf-virtdisk-querychangesvirtualdisk?redirectedfrom=MSDN

DWORD QueryChangesVirtualDisk(
  HANDLE                            VirtualDiskHandle,
  PCWSTR                            ChangeTrackingId,
  ULONG64                           ByteOffset,
  ULONG64                           ByteLength,
  QUERY_CHANGES_VIRTUAL_DISK_FLAG   Flags,
  PQUERY_CHANGES_VIRTUAL_DISK_RANGE Ranges,
  PULONG                            RangeCount,
  PULONG64                          ProcessedLength
);

I am trying to get difference between two virtual disk or two Resillent Change Tracking ID (RCT)

but RangeCount always return as 0

I opened virtual disk file (vhdx) with two enum parameter VIRTUAL_DISK_ACCESS_GET_INFO, OPEN_VIRTUAL_DISK_FLAG_NONE

opStatus = OpenVirtualDisk(
        &storageType,
        VirtualDiskPath,
        VIRTUAL_DISK_ACCESS_GET_INFO,
        OPEN_VIRTUAL_DISK_FLAG_NONE,
        NULL,
        &vhdHandle ); // output handle

then use QueryChangesVirtualDisk()

opStatus = QueryChangesVirtualDisk(
    vhdHandle,
    ChangeTrackingId,
    0,
    32212254720,
    QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE,
    pRctRanges,
    &rctRangeCnt,
    &processedLength
    );

OpenVirtualDisk() QueryChangesVirtualDisk()

Both function did not return error but in every case RangeCount always has a value of 0

There is definitely a difference between virtual disk file and RCT ID

please give me some advice

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <initguid.h>
#include <strsafe.h>
#include <virtdisk.h>

#include "Storage.h"

DWORD QueryChangesVirtualDisk(_In_    LPCWSTR     VirtualDiskPath, _In_    LPCWSTR     ChangeTrackingId)
{
    VIRTUAL_STORAGE_TYPE storageType;
    PGET_VIRTUAL_DISK_INFO diskInfo;
    ULONG diskInfoSize;
    DWORD opStatus;

    HANDLE vhdHandle;

    QUERY_CHANGES_VIRTUAL_DISK_RANGE *pRctRanges;
    ULONG                            rctRangeCnt;
    ULONG64                          processedLength;
    UINT                             i;

    vhdHandle = INVALID_HANDLE_VALUE;
    diskInfo = NULL;
    diskInfoSize = sizeof(GET_VIRTUAL_DISK_INFO);

    rctRangeCnt = 0L;
    processedLength = 0L;
    pRctRanges = NULL;

    i = 0;

    diskInfo = (PGET_VIRTUAL_DISK_INFO)malloc(diskInfoSize);
    if (diskInfo == NULL)
    {
        opStatus = ERROR_NOT_ENOUGH_MEMORY;
        goto Cleanup;
    }

    storageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;
    storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_UNKNOWN;

    //////////////////////////////////////////////////////////////////

    opStatus = OpenVirtualDisk(
        &storageType,
        VirtualDiskPath,
        VIRTUAL_DISK_ACCESS_GET_INFO,
        OPEN_VIRTUAL_DISK_FLAG_NONE,
        NULL,
        &vhdHandle);

    if (opStatus != ERROR_SUCCESS)
    {
        wprintf(L"OpenVirtualDisk fail\n");
        goto Cleanup;
    }

    opStatus = QueryChangesVirtualDisk(
        vhdHandle,
        ChangeTrackingId,
        0,
        32212254720,
        QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE,
        pRctRanges,
        &rctRangeCnt,
        &processedLength
        );

    wprintf(L"rctRangeCnt : %lu\n", rctRangeCnt);
    wprintf(L"processedLength : %llu\n", processedLength);

    for (i = 0; i < rctRangeCnt; i++)
    {
        wprintf(L"ByteOffset : %lld   ByteLength : %lld\n", pRctRanges[i].ByteOffset, pRctRanges[i].ByteLength);
    }

    if (opStatus != ERROR_SUCCESS)
    {
        wprintf(L"QueryChangesVirtualDisk fail\n");
        goto Cleanup;
    }

Cleanup:

    if (opStatus == ERROR_SUCCESS)
    {
        wprintf(L"success\n");
    }
    else
    {
        wprintf(L"error = %u\n", opStatus);
    }

    if (vhdHandle != INVALID_HANDLE_VALUE)
    {
        CloseHandle(vhdHandle);
    }

    if (diskInfo != NULL)
    {
        free(diskInfo);
    }

    if (pRctRanges != NULL)
    {
        for (i = 0; i < rctRangeCnt; i++)
        {
            free(&pRctRanges[i]);
        }
    }

    return opStatus;
}

Solution

  • The problem is that you pass a null-pointer for the Ranges argument and zero for RangeCount.

    For Ranges you're supposed to pass a pointer to the first element or an array of QUERY_CHANGES_VIRTUAL_DISK_RANGE elements, and the RangeCount should be initialized to the number of elements in that array.

    When the QueryChangesVirtualDisk function returns it will have modified RangeCount to be the number of initialized elements in the Ranges array.

    From the documentation you linked to, about the RangeCount argument:

    On input, the value indicates the number of QUERY_CHANGES_VIRTUAL_DISK_RANGE structures that the array that the Ranges parameter points to can hold. On output, the value contains the number of QUERY_CHANGES_VIRTUAL_DISK_RANGE structures that the method placed in the array.

    In short: The function doesn't create this array for you, you must do it before you call the function.