编组结构导致 C# 中的 OutOfBoundsException

Marshalled struct results in OutOfBoundsException in C#

所以我制作了一个结构,我想使用简单的 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;
    }

和 return 它到 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;
    }

但是我得到了:

System.ArgumentOutOfRangeException: 'Requested range extends past the end of the array.'

尝试在线转换时:

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

我现在打算使用简单的序列化 instad,但我想知道为什么它不能按预期工作?

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

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

您的 Marshal.SizeOf(...) 电话反映了这一点。它returns 16,总是。怎么可能?即使字符串比 16 长得多,如何将您的字符串编组为这 16 个字节内的内容?

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

16 字节是 8 字节的 int(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 字节用于 int + 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
{