gpt4 book ai didi

c# - C# 中的 C++ union — 奇怪的行为

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:45:15 27 4
gpt4 key购买 nike

我正在尝试使用 C# 中的 VHD API 创建一些 vhd/vhdx 文件。

有一个看起来像这样的 C++ union :

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 根本不感兴趣,所以我把它排除在外。

我已经尝试了很多事情,我能做到的最好的就是让版本 2 工作(通过做一些非常奇怪的事情),但我从来没有设法让版本 1 和版本 2 同时工作。

到目前为止,取得最好结果的解决方案是这个,但肯定有问题,因为 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 结构之外,我也试过了,但它只是更有趣地打破了事情......

那么,有人可以建议如何将上面的内容正确地转换为 C#,而忽略不需要的 Version3 结构吗?

最佳答案

使用 Pack = 1StructLayout 属性消除了 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

Projects should be compiled to use the default structure packing, which is currently 8 bytes because the largest integral type is 8 bytes. Doing so ensures that all structure types within the header files are compiled into the application with the same alignment the Windows API expects. It also ensures that structures with 8-byte values are properly aligned and will not cause alignment faults on processors that enforce data alignment.

Version 被移动到 CreateVirtualDiskParameters 的顶部。然后两个 union 紧随其后。两者具有相同的偏移量 sizeof(CREATE_VIRTUAL_DISK_VERSION)

此外,SectorSizeInBytesuint 而不是 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# 中的 C++ union — 奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37625070/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com