我正在尝试使用C#中的VHD API创建一些vhd / vhdx文件。
有一个看起来像这样的C ++联盟:
typedef struct _CREATE_VIRTUAL_DISK_PARAMETERS
{
CREATE_VIRTUAL_DISK_VERSION Version;
union
{
struct
{
GUID UniqueId;
ULONGLONG MaximumSize;
ULONG BlockSizeInBytes;
ULONG SectorSizeInBytes;
PCWSTR ParentPath;
PCWSTR SourcePath;
} Version1;
struct
{
GUID UniqueId;
ULONGLONG MaximumSize;
ULONG BlockSizeInBytes;
ULONG SectorSizeInBytes;
ULONG PhysicalSectorSizeInBytes;
PCWSTR ParentPath;
PCWSTR SourcePath;
OPEN_VIRTUAL_DISK_FLAG OpenFlags;
VIRTUAL_STORAGE_TYPE ParentVirtualStorageType;
VIRTUAL_STORAGE_TYPE SourceVirtualStorageType;
GUID ResiliencyGuid;
} Version2;
struct
{
GUID UniqueId;
ULONGLONG MaximumSize;
ULONG BlockSizeInBytes;
ULONG SectorSizeInBytes;
ULONG PhysicalSectorSizeInBytes;
PCWSTR ParentPath;
PCWSTR SourcePath;
OPEN_VIRTUAL_DISK_FLAG OpenFlags;
VIRTUAL_STORAGE_TYPE ParentVirtualStorageType;
VIRTUAL_STORAGE_TYPE SourceVirtualStorageType;
GUID ResiliencyGuid;
PCWSTR SourceLimitPath;
VIRTUAL_STORAGE_TYPE BackingStorageType;
} Version3;
};
} CREATE_VIRTUAL_DISK_PARAMETERS, *PCREATE_VIRTUAL_DISK_PARAMETERS;
我正在尝试将其转换为C#,但没有太多运气。我根本不对Version3感兴趣,所以我就把它排除在外。
我已经尝试了很多东西,我能做的最好的就是让Version2工作(通过做一些非常奇怪的事情),但我从来没有设法让Version1和Version2同时工作。
到目前为止已经取得了最好结果的解决方案就是这样,但是由于Version1根本不起作用,而Version1中的SectorSizeInBytes
是ulong
而不是uint
(如果我将它更改为uint
,我会破坏版本2,而版本1仍无效!)
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParameters
{
[FieldOffset(0)] public CreateVirtualDiskParametersVersion1 Version1;
[FieldOffset(0)] public CreateVirtualDiskParametersVersion2 Version2;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParametersVersion1
{
public CreateVirtualDiskVersion Version;
public Guid UniqueId;
public ulong MaximumSize;
public uint BlockSizeInBytes;
public ulong SectorSizeInBytes;
public string ParentPath;
public string SourcePath;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParametersVersion2
{
public CreateVirtualDiskVersion Version;
public Guid UniqueId;
public ulong MaximumSize;
public uint BlockSizeInBytes;
public uint SectorSizeInBytes;
public uint PhysicalSectorSizeInBytes;
public string ParentPath;
public string SourcePath;
public OpenVirtualDiskFlags OpenFlags;
public VirtualStorageType ParentVirtualStorageType;
public VirtualStorageType SourceVirtualStorageType;
public Guid ResiliencyGuid;
}
我从理论上知道Version
字段应该在Version结构之外设置,我也试过了,但它只是让事情更加有趣......
那么,有人可以建议如何正确地将上述内容翻译成C#,而不需要那样的Version3结构吗?
答案 0 :(得分:1)
使用Pack = 1
到StructLayout
属性可消除struct
成员之间的任何填充。
在TCP连接中,结构通常在没有填充的情况下传递,因此使用该结构的所有程序都可以在内存中对其布局达成一致。
然而,正如@David Heffernan指出的那样,将结构传递给Windows DLL时可能并非如此。我没有测试到CreateVirtualDisk
的实际通话,因为它看起来有点冒险,因为之前我还没有使用过这个电话,如果我做了一个,我也不想破坏我的磁盘错误。根据以下引用,看起来默认包装为8个字节(默认为Pack = 0
或Pack = 8
)可能是正确的设置。
请参阅64-bit Windows API struct alignment caused Access Denied error on named pipe
Windows SDK期望打包为8个字节。来自Using the Windows Headers
项目应编译为使用默认结构打包,当前为8个字节,因为最大的整数类型是8个字节。这样做可确保头文件中的所有结构类型都编译到应用程序中,并具有Windows API期望的相同对齐。它还确保具有8字节值的结构正确对齐,并且不会在强制数据对齐的处理器上引起对齐错误。
Version
移至CreateVirtualDiskParameters
的顶部。
然后两个工会跟随。两者都具有相同的偏移量sizeof(CREATE_VIRTUAL_DISK_VERSION)
。
同样SectorSizeInBytes
为uint
而不是ulong
。
您可以让编组人员使用该属性完成填充string
成员的工作,例如
[MarshalAs(UnmanagedType.LPWStr)] public string ParentPath;
或者,您可以在内存中显示它,它是指向Unicode字符串的指针:
public IntPtr ParentPath;
然后用
自己提取字符串Marshal.PtrToStringAuto(vdp.Version1.ParentPath)
如果您将C#结构传递给外部DLL,请使用非托管字符串填充
vdp.Version1.ParentPath = (IntPtr)Marshal.StringToHGlobalAuto("I am a managed string");
然后在你完成它时释放非托管字符串
Marshal.FreeHGlobal(vdp.Version1.ParentPath);
试试这个。
public enum CREATE_VIRTUAL_DISK_VERSION
{
CREATE_VIRTUAL_DISK_VERSION_UNSPECIFIED = 0,
CREATE_VIRTUAL_DISK_VERSION_1 = 1,
CREATE_VIRTUAL_DISK_VERSION_2 = 2
};
public enum OPEN_VIRTUAL_DISK_FLAG
{
OPEN_VIRTUAL_DISK_FLAG_NONE = 0x00000000,
OPEN_VIRTUAL_DISK_FLAG_NO_PARENTS = 0x00000001,
OPEN_VIRTUAL_DISK_FLAG_BLANK_FILE = 0x00000002,
OPEN_VIRTUAL_DISK_FLAG_BOOT_DRIVE = 0x00000004,
OPEN_VIRTUAL_DISK_FLAG_CACHED_IO = 0x00000008,
OPEN_VIRTUAL_DISK_FLAG_CUSTOM_DIFF_CHAIN = 0x00000010
};
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)]
public struct VIRTUAL_STORAGE_TYPE
{
uint DeviceId;
Guid VendorId;
};
[StructLayout(LayoutKind.Explicit, Pack = 8, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParameters
{
[FieldOffset(0)]
public CREATE_VIRTUAL_DISK_VERSION Version;
[FieldOffset(8))]
public CreateVirtualDiskParametersVersion1 Version1;
[FieldOffset(8))]
public CreateVirtualDiskParametersVersion2 Version2;
}
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParametersVersion1
{
public Guid UniqueId;
public ulong MaximumSize;
public uint BlockSizeInBytes;
public uint SectorSizeInBytes;
//public IntPtr ParentPath; // PCWSTR in C++ which is a pointer to a Unicode string
//public IntPtr SourcePath; //string
[MarshalAs(UnmanagedType.LPWStr)] public string ParentPath;
[MarshalAs(UnmanagedType.LPWStr)] public string SourcePath;
}
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParametersVersion2
{
public Guid UniqueId;
public ulong MaximumSize;
public uint BlockSizeInBytes;
public uint SectorSizeInBytes;
public uint PhysicalSectorSizeInBytes;
//public IntPtr ParentPath; //string
//public IntPtr SourcePath; //string
[MarshalAs(UnmanagedType.LPWStr)] public string ParentPath;
[MarshalAs(UnmanagedType.LPWStr)] public string SourcePath;
public OPEN_VIRTUAL_DISK_FLAG OpenFlags;
public VIRTUAL_STORAGE_TYPE ParentVirtualStorageType;
public VIRTUAL_STORAGE_TYPE SourceVirtualStorageType;
public Guid ResiliencyGuid;
}