You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

调用GetVirtualDiskMetadata遇错误1168:无法读取VHDX磁盘标识符

问题:读取VHDX磁盘标识符时出现"Element not found"错误

我尝试编写基于Windows API的C程序,读取Hyper-V VHDX的磁盘标识符并输出到控制台,计划使用virtdisk.lib和uuid.lib,运行环境为Windows 11。后续项目需要在无PowerShell VHD-* cmdlet的情况下随机化VHDX标识符,若有更易脚本化的实现方式欢迎补充。

测试用VHDX通过以下PowerShell命令生成:

New-VHD -Dynamic -SizeBytes 30GB -BlockSizeBytes 1MB -Path "$(pwd)\test.vhdx"

期望程序输出与以下命令获取的DiskIdentifier一致:

Get-VHD "$(pwd)\test.vhdx" | Select-Object -Property DiskIdentifier

简化版程序read_disk_identifier.c如下(暂不考虑错误检查):

#define UNICODE
#define _UNICODE

#include <initguid.h>
#include <cguid.h>
#include <windows.h>
#include <virtdisk.h>
#include <objbase.h>
#include <rpc.h>
#include <stdio.h>
#include <wchar.h>

static const GUID VHDX_METADATA_VIRTUAL_DISK_IDENTIFIER = { 0xBECA12AB, 0xB2E6, 0x4523, { 0x93, 0xEF, 0xC3, 0x09, 0xE0, 0x00, 0xC7, 0x46 } };

static void print_last_error_w(const wchar_t* context, DWORD error) {
  wchar_t* message = NULL;
  DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
  DWORD len = FormatMessageW(
    flags,
    NULL,
    error,
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    (LPWSTR)&message,
    0,
    NULL
  );

  if (len == 0 || message == NULL) {
    fwprintf(stderr, L"%ls failed with error %lu (and FormatMessageW failed)\n", context, (unsigned long)error);
    return;
  }

  fwprintf(stderr, L"%ls failed with error %lu: %ls", context, (unsigned long)error, message);
  LocalFree(message);
}

static void print_guid_w(const wchar_t* label, const GUID* guid) {
  wchar_t buf[64];
  int chars = StringFromGUID2(guid, buf, (int)(sizeof(buf) / sizeof(buf[0])));
  if (chars <= 0) {
    fwprintf(stdout, L"%ls <formatting failed>\n", label);
    return;
  }
  fwprintf(stdout, L"%ls %ls\n", label, buf);
}

int wmain(int argc, wchar_t** argv) {
  const wchar_t* path = argv[1];

  VIRTUAL_STORAGE_TYPE storage_type;
  storage_type.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_VHDX;
  storage_type.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT;
  OPEN_VIRTUAL_DISK_PARAMETERS open_params;
  ZeroMemory(&open_params, sizeof(open_params));
  open_params.Version = OPEN_VIRTUAL_DISK_VERSION_2;
  open_params.Version2.GetInfoOnly = 0;
  open_params.Version2.ReadOnly = 0;
  open_params.Version2.ResiliencyGuid = GUID_NULL;

  HANDLE vhd_handle = INVALID_HANDLE_VALUE;
  DWORD open_status = OpenVirtualDisk(
    &storage_type,
    path,
    VIRTUAL_DISK_ACCESS_NONE,
    OPEN_VIRTUAL_DISK_FLAG_NONE,
    &open_params,
    &vhd_handle
  );
  if (open_status != ERROR_SUCCESS) {
    print_last_error_w(L"Error opening virtual disk: ", open_status);
    CloseHandle(vhd_handle);
    return 1;
  }

  GUID old_guid;
  DWORD old_guid_size = sizeof(old_guid);
  DWORD get_status = GetVirtualDiskMetadata(
    vhd_handle,
    &VHDX_METADATA_VIRTUAL_DISK_IDENTIFIER,
    &old_guid_size,
    &old_guid
  );

  if (get_status == ERROR_SUCCESS) {
    print_guid_w(L"Old virtual hard disk GUID is: ", &old_guid);
  } else {
    print_last_error_w(L"Error reading virtual disk GUID: ", get_status);
  }

  CloseHandle(vhd_handle);
  return 0;
}

编译运行后出现错误:

Error reading virtual disk GUID:  failed with error 1168: Element not found.

问题原因与修复步骤

1. 使用错误的元数据GUID

你手动定义的VHDX_METADATA_VIRTUAL_DISK_IDENTIFIERGUID不正确。正确的VHDX磁盘标识符元数据GUID是系统预定义的VIRTUAL_DISK_IDENTIFIER,值为{7B7166EC-2179-4020-94E8-B724DFD72085},已包含在virtdisk.h中。

修复:删除手动定义的GUID,直接使用系统定义的VIRTUAL_DISK_IDENTIFIER

// 移除原来的static const GUID定义
// 直接使用virtdisk.h中的VIRTUAL_DISK_IDENTIFIER

2. 磁盘打开权限不足

使用VIRTUAL_DISK_ACCESS_NONE权限打开磁盘,无法读取元数据。需要至少VIRTUAL_DISK_ACCESS_READ权限。

修复:修改OpenVirtualDisk的权限参数:

DWORD open_status = OpenVirtualDisk(
    &storage_type,
    path,
    VIRTUAL_DISK_ACCESS_READ, // 替换为READ权限
    OPEN_VIRTUAL_DISK_FLAG_NONE,
    &open_params,
    &vhd_handle
);

3. 更可靠的替代方案:使用GetVirtualDiskInformation

直接读取元数据并非最优方式,GetVirtualDiskInformation提供了更直接的磁盘标识符获取途径:

修改后代码片段

// 打开磁盘成功后,替换原GetVirtualDiskMetadata代码
VIRTUAL_DISK_INFO disk_info = {0};
disk_info.Version = VIRTUAL_DISK_INFO_IDENTIFIER;
DWORD info_size = sizeof(VIRTUAL_DISK_INFO);
DWORD get_status = GetVirtualDiskInformation(
    vhd_handle,
    &info_size,
    &disk_info,
    NULL,
    NULL,
    NULL
);

if (get_status == ERROR_SUCCESS) {
    print_guid_w(L"Disk Identifier: ", &disk_info.Identifier);
} else {
    print_last_error_w(L"Error getting disk info: ", get_status);
}

4. 编译链接注意事项

确保编译时链接virtdisk.libuuid.lib,例如MSVC编译命令:

cl read_disk_identifier.c virtdisk.lib uuid.lib

脚本化实现替代方案

若需要无需PowerShell的脚本化方式,可使用diskpart命令行工具:

  1. 创建脚本文件vdisk_info.txt
select vdisk file="C:\full\path\to\test.vhdx"
detail vdisk
  1. 运行命令:
diskpart /s vdisk_info.txt
  1. 使用findstr提取标识符:
diskpart /s vdisk_info.txt | findstr "Disk Identifier"

内容的提问来源于stack exchange,提问作者Joshua Schroijen

火山引擎 最新活动