C#中的C++并集——奇怪的行为
C++ union in C# — weird behaviour
我正在尝试使用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
,我会破坏Version2,Version1仍然不起作用!)
[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 structs之外,我也尝试过,但它只会让事情变得更加有趣。。。
那么,有人能建议如何正确地将上面的内容翻译成C#,而不需要Version3结构吗?
使用Pack = 1
到StructLayout
属性可以消除struct
成员之间的任何填充。在TCP连接中,结构通常在没有填充的情况下传递,这样所有使用该结构的程序都可以就其在内存中的布局达成一致。
然而,正如@David Heffernan所指出的,当将结构传递到Windows DLL时,情况可能并非如此。我没有测试对CreateVirtualDisk
的实际调用,因为这似乎有点冒险,因为我以前没有使用过这个调用,而且如果我犯了错误,也不想破坏我的磁盘。根据以下引用,8字节的默认封装(默认为Pack = 0
或Pack = 8
)似乎是正确的设置。
请参阅64位Windows API结构对齐导致命名管道上的访问被拒绝错误
Windows SDK预计打包为8字节。从使用Windows标头
项目应该编译为使用默认的结构封装,目前为8字节,因为最大的整数类型为8字节。这样做可以确保头文件中的所有结构类型都以与Windows API期望的相同对齐方式编译到应用程序中。它还确保具有8字节值的结构正确对齐,并且不会在执行数据对齐的处理器上导致对齐错误。
Version
移动到CreateVirtualDiskParameters
的顶部。随后,两个工会紧随其后。两者具有相同的偏移CCD_ 14。
此外,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;
}
- 布局兼容类型的并集
- 用C#中的并集模拟C++嵌套结构
- 在模拟器中使用并集来模拟CPU寄存器有多合适
- 提升几何体:C++并集多个多边形
- C++ 在可变参数模板的参数包中并集参数?
- 与位字段的并集为位字段成员提供了意外值
- pybind11:用可选数据表示并集
- 使用并集作为std::unordereded_map的密钥
- 使用嵌套结构初始化并集
- 给定一组正整数 <=k 及其 n 个子集,找到哪些子集对给出原始集合的并集
- 指向并集的指针的向量.如何正确访问联合的元素?
- C++:传递未知并集作为参数
- 如何在模板与并集之间进行继承?
- 并集 (C++) 中出现意外结果
- boost::icl::interval_map重叠并集
- 与特征数据类型并集
- 使用并集将双精度转换为十六进制再转换为双精度
- 使用并集的简单结构中的成员变量别名
- C#中的C++并集——奇怪的行为
- 如何使用嵌套在没有名称的结构中的c并集