调用GetVirtualDiskMetadata遇错误1168:无法读取VHDX磁盘标识符
我尝试编写基于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.lib和uuid.lib,例如MSVC编译命令:
cl read_disk_identifier.c virtdisk.lib uuid.lib
脚本化实现替代方案
若需要无需PowerShell的脚本化方式,可使用diskpart命令行工具:
- 创建脚本文件
vdisk_info.txt:
select vdisk file="C:\full\path\to\test.vhdx" detail vdisk
- 运行命令:
diskpart /s vdisk_info.txt
- 使用
findstr提取标识符:
diskpart /s vdisk_info.txt | findstr "Disk Identifier"
内容的提问来源于stack exchange,提问作者Joshua Schroijen




