我有一个由我自己编写的服务器客户端程序。我使用TCP协议的套接字。我正在尝试从客户端向服务器发送缓冲区。该缓冲区是struct,我将其转换为字节数组发送它,在服务器上我将它从字节数组转换回struct:
public static byte[] StructToByteArray(loginStruct obj)
{
int len = Marshal.SizeOf(typeof(loginStruct));
byte[] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}
public static void ByteArrayToStruct(byte[] buffer, ref loginStruct obj)
{
int len = Marshal.SizeOf(typeof(loginStruct));
IntPtr i = Marshal.AllocHGlobal(len);
Marshal.Copy(buffer, 0, i, len);
obj = (loginStruct)Marshal.PtrToStructure(i, obj.GetType());
Marshal.FreeHGlobal(i);
}
我的结构:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct loginStruct
{
public string userName;
public string password;
public loginStruct(string userName, string password)
{
this.userName = userName;
this.password = password;
}
}
我在线上遇到了运行时错误:
obj = (loginStruct)Marshal.PtrToStructure(i, obj.GetType());
错误是:
尝试读取或写入受保护的内存。这通常表明其他内存已损坏。
在ByteArrayToStruct函数的服务器上,len等于缓冲区大小为16。
答案 0 :(得分:1)
如果您尝试发送固定大小的char *
缓冲区,则字符串的默认编组可能不适用于您。您应该使用MarshalAs
属性标记字符串并指定实际的缓冲区大小,以便.NET知道在非托管版本的结构中分配和复制的内存量。它看起来像这样:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct loginStruct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)]
public string userName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)]
public string password;
public loginStruct(string userName, string password)
{
this.userName = userName;
this.password = password;
}
}
有关如何跨托管/非托管边界编组字符串的详细信息,请参阅:Default Marshaling for Strings,尤其是“结构中使用的字符串”部分。
顺便说一下,我认为您不需要指定Pack
,因为您的数组每个都是8个字节,所以没有填充空间。您需要非常确定需要覆盖结构上的打包,尤其是如果您的代码需要在32位和64位系统上工作。如果C定义不包含#pragma pack
或类似内容,那么您可能也不应该这样做。