C# 封送结构在C中导致OutOfBoundsException#

C# 封送结构在C中导致OutOfBoundsException#,c#,exception,struct,marshalling,outofrangeexception,C#,Exception,Struct,Marshalling,Outofrangeexception,所以我制作了一个结构,我想用一个简单的DatagramSocket发送它 结构代码如下所示: public struct MsgData { private readonly int _value; private readonly string _descr; public MsgData(string desc, int value) { _descr = desc; _value = value; } pu

所以我制作了一个结构,我想用一个简单的DatagramSocket发送它

结构代码如下所示:

 public struct MsgData
{
    private readonly int _value;
    private readonly string _descr;
    public MsgData(string desc, int value)
    {
        _descr = desc;
        _value = value;
    }

    public int GetValue()
    {
        return _value;
    }

    public string GetDescr()
    {
        return _descr;
    }
}
我继续转换为字节数组,如下所示:

 public static byte[] GetBytes(MsgData message)
    {
        var size = Marshal.SizeOf(message);
        var data = new byte[size];

        System.IntPtr ptr = Marshal.AllocHGlobal(size);
        Marshal.StructureToPtr(message, ptr, true);
        Marshal.Copy(ptr, data, 0, size);
        Marshal.FreeHGlobal(ptr);

        return data;
    }
然后将其返回到MsgData结构,如下所示:

public static MsgData GetMessage(byte[] bytes)
    {
        var data = new MsgData();

        var size = Marshal.SizeOf(data);
        var ptr = Marshal.AllocHGlobal(size);

        Marshal.Copy(bytes, 0, ptr, size);

        data = Marshal.PtrToStructure<MsgData>(ptr);
        Marshal.FreeHGlobal(ptr);

        return data;
    }
尝试联机转换时:

Marshal.Copy(bytes, 0, ptr, size);

我现在要使用一个简单的序列化instad,但我想知道为什么它不能像预期的那样工作?

您的代码有几个问题,所以让我解释一下您需要做什么来修复它

首先,您的结构具有针对快速内存访问而优化的布局。当将其编组到字节数组中时,默认情况下,您将复制该内存布局

您的
Marshal.SizeOf(…)
调用反映了这一点。它总是返回16。这怎么可能?即使字符串比16长得多,如何将字符串编组到这16个字节内的某些内容

答案是没有。而是将指向字符串对象的指针编组为字节

16字节是int的8字节(4字节是int+4字节是填充,以对齐8字节内存地址边界上的下一个值,我运行的是64位),然后8字节是字符串引用(地址)

那么需要做什么呢?您需要使用一些属性来修饰您的结构,以告诉编组引擎如何处理它:

[StructLayout(LayoutKind.Sequential, Pack=0)]
public struct MsgData
{
    [MarshalAs(UnmanagedType.I4)]
    private readonly int _value;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
    private readonly string _descr;

    ... rest as you have it
完成此操作后,您将从
Marshal.SizeOf(…)
:68获得此大小

68=整数的4字节+字符串的64字节

请注意,在编组时,动态调整大小的结果实际上并不那么容易处理,因此现在应该设置字符串的上限


然而,对于您的问题,有一个更简单的解决方案,使用.NET中内置的二进制序列化

下面是两个新版本的
Get*
方法,它们不需要您进行编组:

public static byte[] GetBytes(MsgData message)
{
    using (var stream = new MemoryStream())
    {
        new BinaryFormatter().Serialize(stream, message);
        return stream.ToArray();
    }
}

public static MsgData GetMessage(byte[] bytes)
{
    using (var stream = new MemoryStream(bytes))
    {
        return (MsgData)new BinaryFormatter().Deserialize(stream);
    }
}
请注意,您需要将
SerializableAttribute
应用于您的结构:

[Serializable]
public struct MsgData
{

发生异常时,
bytes.Length
是什么?您确定
bytes
包含足够的字节吗?如果(bytes.Length!=size)抛出新的ArgumentException(),我将添加
某个地方。不清楚此代码的用途。首先,您没有在结构上指定StructLayout属性,在这种情况下,打包是未指定的(您可以根据x86/x64检测将使用哪个打包,但代码状态看起来很脆弱)。此外,您没有在字段上指定“marshallas”属性,在这种情况下,您的字符串不会真正被封送。最后,您正在将“true”传递给StructureToPtr的fDeleteOld参数,但默认情况下,您分配的内存不应包含结构。请解释您要完成的任务,而不仅仅是“此代码失败,原因”
var size=Marshal.SizeOf(数据)最后一部分就是我在本例中“简单序列化”的意思。无论我如何生活和学习。谢谢你的详尽的教学解释。
[Serializable]
public struct MsgData
{