StructureToPtr 未将本机结构中的原始类型字段正确复制到引用结构

StructureToPtr not copying primitive type fields in native struct to ref struct correctly

我有一个本地结构,(它非常大所以我必须使用新关键字来实例化,下面只是为了制作一个 MCVE 我不能更改结构,因为它作为外部依赖项提供),

struct NativeStruct
{
    char    BrokerID[11];
    char    InvestorID[13];
    char    InstrumentID[31];
    char    OrderRef[13];
    char    UserID[16];
    char    OrderPriceType;
    char    Direction;
    double  LimitPrice;
}

我想将 NativeStruct 转换为托管对象,所以我定义了一个 ref 结构来镜像它,这也使用了如下两个枚举,

public enum struct EnumOrderPriceTypeType
{
    AnyPrice = (Byte)'1',
    LimitPrice = (Byte)'2',
    BestPrice = (Byte)'3',
    LastPrice = (Byte)'4',
    LastPricePlusOneTicks = (Byte)'5',
    LastPricePlusTwoTicks = (Byte)'6',
    LastPricePlusThreeTicks = (Byte)'7',
    AskPrice1 = (Byte)'8',
    AskPrice1PlusOneTicks = (Byte)'9',
    AskPrice1PlusTwoTicks = (Byte)'A',
    AskPrice1PlusThreeTicks = (Byte)'B',
    BidPrice1 = (Byte)'C',
    BidPrice1PlusOneTicks = (Byte)'D',
    BidPrice1PlusTwoTicks = (Byte)'E',
    BidPrice1PlusThreeTicks = (Byte)'F'
};

public enum struct EnumDirectionType
{
    Buy = (Byte)'0',
    Sell = (Byte)'1'
};

[StructLayout(LayoutKind::Sequential)]
public ref struct ManagedStruct
{
    [MarshalAs(UnmanagedType::ByValTStr, SizeConst = 11)]
    String^ BrokerID;
    [MarshalAs(UnmanagedType::ByValTStr, SizeConst = 13)]
    String^ InvestorID;
    [MarshalAs(UnmanagedType::ByValTStr, SizeConst = 31)]
    String^ InstrumentID;
    [MarshalAs(UnmanagedType::ByValTStr, SizeConst = 13)]
    String^ OrderRef;
    [MarshalAs(UnmanagedType::ByValTStr, SizeConst = 16)]
    String^ UserID;
    EnumOrderPriceTypeType OrderPriceType;
    EnumDirectionType Direction;
    double LimitPrice;
};

然后我用StructureToPtr将native object复制到managed object,用WriteLine测试是否复制成功,

NativeStruct *native = new NativeStruct();
ManagedStruct^ managed = gcnew ManagedStruct();
managed->LimitPrice = 95.5;
managed->BrokerID = "666666";
Marshal::StructureToPtr(managed, IntPtr(native), false);
int i;

for (i = 0; i < 11; i++)
    Console::Write(native->BrokerID[i]);
Console::WriteLine();
Console::WriteLine(native->LimitPrice);
Console::WriteLine(L"Hello ");
Console::ReadLine();

我的问题是为什么LimitPrice没有复制成功?我已经为此奋斗了一个星期,欢迎任何帮助。非常感谢。

Marshal::StructureToPtr() 只有在托管结构和本机结构 完全 匹配时才能正常工作。到目前为止,验证这一点的最简单方法是检查结构的大小,它们必须相同。所以将此代码添加到您的程序中:

auto nlen = sizeof(NativeStruct);
auto mlen = Marshal::SizeOf(ManagedStruct::typeid);
System::Diagnostics::Debug::Assert(nlen == mlen);

轰隆隆。本机结构占用 96 个字节,托管结构占用 104 个字节。后果很严重,你破坏了内存,这比 LimitPrice 成员值被复制到错误的偏移量有更多不愉快的副作用。

解决此问题的两种基本方法。您可以简单地用唯一值填充 all 托管结构成员,并检查具有错误值的本机结构的第一个成员。前面那个成员错了。继续前进,直到你不再得到 kaboom。或者您可以编写在本机结构成员上使用 offsetof() 的代码,并将它们与 Marshal::OffsetOf().

进行比较

只是为了省去您的麻烦,问题是 enum 声明。它们在本机结构中的大小为 1 个字节,但托管版本需要 4 个字节。修复:

  public enum struct EnumOrderPriceTypeType : Byte

  public enum struct EnumDirectionType : Byte

请注意添加的 : Byte 以强制枚举占用 1 个字节的存储空间。应该注意的是,一个一个地复制成员而不是使用 Marshal::StructureToPtr() 更快,并且可以为您节省一周的麻烦。