C# 封送结构在C中导致OutOfBoundsException#
所以我制作了一个结构,我想用一个简单的DatagramSocket发送它 结构代码如下所示: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
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
{