【问题标题】:How to PInvoke GetVirtualDiskInformation in C#如何在 C# 中 PInvoke GetVirtualDiskInformation
【发布时间】: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

标签: c# .net winapi pinvoke


【解决方案1】:

我没有解决方案,但注意到如果您在结构中添加任何 uint 字段作为最后一个字段,它也可以正常工作。我的定义是:


公共结构 GetVirtualDiskInfoUnion
{
[FieldOffset(0)] public GetVirtualDiskInfoSize Size;
[FieldOffset(0)] 公共 Guid 标识符; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoParentLocation ParentLocation;
[FieldOffset(0)] public Guid ParentIdentifier; //GUID
[FieldOffset(0)] public uint ParentTimestamp; //超长
[FieldOffset(0)] public VirtualStorageType VirtualStorageType; //VIRTUAL_STORAGE_TYPE
[FieldOffset(0)] public uint ProviderSubtype; //超长
[FieldOffset(0)] public bool Is4kAligned; //布尔值
[FieldOffset(0)] public bool IsLoaded; //布尔值
[FieldOffset(0)] public GetVirtualDiskInfoPhysicalDisk PhysicalDisk;
[FieldOffset(0)] public uint VhdPhysicalSectorSize; //超长
[FieldOffset(0)] public ulong SmallestSafeVirtualSize; //乌龙龙
[FieldOffset(0)] public uint FragmentationPercentage; //超长
[FieldOffset(0)] public Guid VirtualDiskId; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoChangeTrackingState ChangeTrackingState;
[FieldOffset(0)] public uint 保留; //超长
}

【讨论】:

    猜你喜欢
    • 2015-09-19
    • 1970-01-01
    • 2010-10-26
    • 1970-01-01
    • 2010-12-16
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多