【发布时间】:2016-06-09 22:07:58
【问题描述】:
完整的极简(非工作)代码:http://pastebin.com/GPdSxyrt
我正在尝试 PInvoke GetVirtualDiskInformation (https://msdn.microsoft.com/en-us/library/windows/desktop/dd323670(v=vs.85).aspx) 并拥有我的代码:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfo
{
public GetVirtualDiskInfoVersion Version; //GET_VIRTUAL_DISK_INFO_VERSION
public GetVirtualDiskInfoUnion Union;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoUnion
{
[FieldOffset(0)] public GetVirtualDiskInfoSize Size;
[FieldOffset(0)] public Guid Identifier; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoParentLocation ParentLocation;
[FieldOffset(0)] public Guid ParentIdentifier; //GUID
[FieldOffset(0)] public uint ParentTimestamp; //ULONG
[FieldOffset(0)] public VirtualStorageType VirtualStorageType; //VIRTUAL_STORAGE_TYPE
[FieldOffset(0)] public uint ProviderSubtype; //ULONG
[FieldOffset(0)] public bool Is4kAligned; //BOOL
[FieldOffset(0)] public bool IsLoaded; //BOOL
[FieldOffset(0)] public GetVirtualDiskInfoPhysicalDisk PhysicalDisk;
[FieldOffset(0)] public uint VhdPhysicalSectorSize; //ULONG
[FieldOffset(0)] public ulong SmallestSafeVirtualSize; //ULONGLONG
[FieldOffset(0)] public uint FragmentationPercentage; //ULONG
[FieldOffset(0)] public Guid VirtualDiskId; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoChangeTrackingState ChangeTrackingState;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoSize
{
public ulong VirtualSize; //ULONGLONG
public ulong PhysicalSize; //ULONGLONG
public uint BlockSize; //ULONG
public uint SectorSize; //ULONG
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoParentLocation
{
public bool ParentResolved; //BOOL
public char ParentLocationBuffer; //WCHAR[1] //TODO
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoPhysicalDisk
{
public uint LogicalSectorSize; //ULONG
public uint PhysicalSectorSize; //ULONG
public bool IsRemote; //BOOL
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoChangeTrackingState
{
public bool Enabled; //BOOL
public bool NewerChanges; //BOOL
public char MostRecentId; //WCHAR[1] //TODO
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct VirtualStorageType
{
public VirtualStorageDeviceType DeviceId; //ULONG
public Guid VendorId; //GUID
}
public enum GetVirtualDiskInfoVersion
{
Unspecified = 0,
Size = 1,
Identifier = 2,
ParentLocation = 3,
ParentIdentifier = 4,
ParentTimestamp = 5,
VirtualStorageType = 6,
ProviderSubtype = 7,
Is4KAligned = 8,
PhysicalDisk = 9,
VhdPhysicalSectorSize = 10,
SmallestSafeVirtualSize = 11,
Fragmentation = 12,
IsLoaded = 13,
VirtualDiskId = 14,
ChangeTrackingState = 15
}
public enum VirtualStorageDeviceType
{
Unknown = 0,
Iso = 1,
Vhd = 2,
Vhdx = 3,
Vhdset = 4
}
[DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
public static extern uint GetVirtualDiskInformation
(
[In] VirtualDiskSafeHandle virtualDiskHandle,
[In, Out] ref uint virtualDiskInfoSize,
[In, Out] ref GetVirtualDiskInfo virtualDiskInfo,
[In, Out] ref uint sizeUsed
);
我这样调用 GetVirtualDiskInformation:
var info = new GetVirtualDiskInfo {Version = infoVersion};
infoSize = (uint) Marshal.SizeOf(info);
var result = NativeMethods.GetVirtualDiskInformation(handle, ref infoSize, ref info, ref sizeUsed);
显然handle 在这里确实包含一个有效的VirtualDiskSafeHandle。
问题在于我的输出结构完全搞砸了。数据无处不在。根据它,例如 VHD 的 LogicalSectorSize 是 257,它有一个负数 VirtualSize。
我做错了什么,我怎样才能让它正常工作?
编辑:
出了什么问题的具体例子:
我创建了一个全新的 VHDX 作为差异(无父,无源),将其最大大小设置为 50MB,LogicalSectorSize 设置为 512,PhysicalSectorSize 设置为 4096,BlockSize 设置为 2MB,它是 VendorId(GUID ) 到new Guid("EC984AEC-A0F9-47e9-901F-71415A66345B")。它从未附加过,它在磁盘上的大小正好是 4096KB。
我希望在表演时:
var info = new GetVirtualDiskInfo {Version = GetVirtualDiskInfoVersion.Size};
infoSize = (uint) Marshal.SizeOf(info);
var result = NativeMethods.GetVirtualDiskInformation(handle, ref infoSize, ref info, ref sizeUsed);
info.Union.Size 会返回:
VirtualSize = 52428800
PhysicalSize = 4194304
BlockSize = 2097152
SectorSize = 512
我得到的是“足够接近”。除了VirtualSize,所有值都是正确的,它返回52428801。现在额外的一个字节可能是预期的,但我对此表示怀疑。无论如何,接下来的几个例子会产生更糟糕的结果:
var info = new GetVirtualDiskInfo {Version = GetVirtualDiskInfoVersion.VirtualStorageType};
infoSize = (uint) Marshal.SizeOf(info);
var result = NativeMethods.GetVirtualDiskInformation(handle, ref infoSize, ref info, ref sizeUsed);
info.Union.VirtualStorageType 的预期结果:
DeviceId = 2 //2 Symbolizes VHDX, which is how I created it
VendorId = "EC984AEC-A0F9-47e9-901F-71415A66345B" //As a GUID
实际结果:
DeviceId = 257
VendorId = "EC984AEC-A0F9-47e9-901F-71415A66345B" //As a GUID
换句话说,GUID 很好,DeviceId 不是。 257 甚至都不是有效值。
最后一个例子:
var info = new GetVirtualDiskInfo {Version = GetVirtualDiskInfoVersion.PhysicalDisk};
infoSize = (uint) Marshal.SizeOf(info);
var result = NativeMethods.GetVirtualDiskInformation(handle, ref infoSize, ref info, ref sizeUsed);
预计info.Union.PhysicalDisk 会返回:
LogicalSectorSize = 512
PhysicalSectorSize = 4096
IsRemote = false
实际结果:
LogicalSectorSize = 257
PhysicalSectorSize = 512
IsRemote = false
所以第一个值又是完全错误的。 257 不是一个可接受的值,第二个值也是错误的,我希望是 4096。
EDIT2在初始代码中添加了 VirtualStorageDeviceType。
EDIT3 完整示例:http://pastebin.com/GPdSxyrt
EDIT4
我发现问题出在 C 中为 WCHAR[1] 的字段上。如果我在 GetVirtualDiskInfoUnion 结构中注释掉 ChangeTrackingState,那么一切正常。
不过,我不确定在 C# 中如何设置它们,它们似乎不是 char 也不是 IntPtr 从我尝试过的,那么这到底是什么意思?
【问题讨论】:
-
你的结构
GetVirtualDiskInfoUnion有问题,所有字段都在偏移量0(FieldOffset(0)),我建议你用正确的偏移量替换零,看看它是否有效 -
@bob1024 正确的偏移量是 0。它表示 C 中的联合,请参阅msdn.microsoft.com/en-us/library/windows/desktop/…
-
@DavidHeffernan 这正是我所做的。联合设置为显式!
-
是的。我读错了。错误检查告诉你什么?函数调用返回什么。还有什么是 infoVersion?
-
@DavidHeffernan 没有错误,只是返回的数据不正确。 intoVersion 是您要查询的内容,它只是
GetVirtualDiskInfoVersion类型的枚举。在本机中存在联合的原因是因为您需要选择要查询的内容,它不会同时为您提供所有信息。 infoVersion 被映射到GetVirtualDiskInfo.Version