StructureToPtr没有复制本机结构中的原始类型字段以正确地构造

时间:2016-03-20 15:09:45

标签: c++-cli

我有一个原生结构,(它非常大,所以我必须使用新的关键字来实例化,下面只是为了制作一个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 struct来镜像它,这也使用了两个枚举,如下所示,

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将本机对象复制到托管对象,并使用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没有被成功复制?我一直在与这场斗争一周,欢迎任何帮助。非常感谢。

1 个答案:

答案 0 :(得分:2)

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

Course.find(id).update_attributes...

KABOOM。本机结构需要96个字节,托管结构需要104个。结果是可怕的,你破坏了内存,并且比LimitPrice成员值被复制到错误的偏移量有更多令人不快的副作用。

解决这个问题的两种基本方法。您只需使用唯一值填充所有托管结构成员,并检查具有错误值的本机结构的第一个成员。之前的成员是错的。继续前进,直到你不再获得kaboom。或者,您可以编写在本机struct成员上使用offsetof()的代码,并将它们与Marshal :: OffsetOf()进行比较。

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

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

  public enum struct EnumOrderPriceTypeType : Byte

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