编组结构导致 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
{
所以我制作了一个结构,我想使用简单的 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
{