Why am I getting an access violation when I try to convert an array of bytes to an instance of a class using Marshal.PtrToStructure?

In C ++, I have this:

struct BasePacketProto { unsigned short PACKET_OPCODE; unsigned short PACKET_MAGIC_NUMBER; unsigned short PACKET_REMAIN_DATA_LENGTH; }; struct CM_Request_AcceptAccount : BasePacketProto { unsigned char ACCOUNT_LOGIN[16]; unsigned char ACCOUNT_PASSWORD[16]; }; static void SendPacket() { CM_Request_AcceptAccount packet; packet.PACKET_OPCODE = opCM_Request_AcceptAccount; packet.PACKET_MAGIC_NUMBER = 123; packet.PACKET_REMAIN_DATA_LENGTH = sizeof(CM_Request_AcceptAccount) - sizeof(BasePacketProto); memcpy(packet.ACCOUNT_LOGIN, "asd", sizeof("asd") * sizeof(char)); memcpy(packet.ACCOUNT_PASSWORD, "asd_pass", sizeof("asd_pass") * sizeof(char)); //Send the packet to the server. int lLength = send(lhSocket, (const char*)&packet, sizeof(CM_Request_AcceptAccount), 0); } 

In C # , this is:

 [StructLayout(LayoutKind.Sequential)] class BasePacketProto { public System.UInt16 PACKET_OPCODE; public System.UInt16 PACKET_MAGIC_NUMBER; public System.UInt16 PACKET_REMAIN_DATA_LENGTH; } [StructLayout(LayoutKind.Sequential)] class CM_Request_AcceptAccount : BasePacketProto { public byte[] ACCOUNT_LOGIN = new byte[16]; public byte[] ACCOUNT_PASSWORD = new byte[16]; } 

A class that breaks packages:

 public class PacketProcessor { static List<byte> raw_packet = new List<byte>(); static int PACKET_HEADER_SIZE = Marshal.SizeOf(typeof(BasePacketProto)); static public void ProcessPacketBytes(byte[] bytes, int size) { for (int i = 0; i < size; i++) raw_packet.Add(bytes[i]); //Adding bytes to own storage if (raw_packet.Count < PACKET_HEADER_SIZE) //If we don't have enough bytes //to build base packet, we will //return and wait for more. return; //This packet building works fine! BasePacketProto bpp = ConvertBytesTo<BasePacketProto>(raw_packet.GetRange( 0, PACKET_HEADER_SIZE).ToArray()); if (raw_packet.Count >= (PACKET_HEADER_SIZE + bpp.PACKET_REMAIN_DATA_LENGTH)) //If we have enough bytes in storage to restore child packet. { switch ((ClientPacketOpcodes)bpp.PACKET_OPCODE) { case ClientPacketOpcodes.opCM_Request_AcceptAccount: //But this one fails bpp = ConvertBytesTo<CM_Request_AcceptAccount>(raw_packet.GetRange( 0, PACKET_HEADER_SIZE + bpp.PACKET_REMAIN_DATA_LENGTH).ToArray()); PacketHandler.Handle_opCM_Request_AcceptAccount((CM_Request_AcceptAccount)bpp); break; default: break; } raw_packet.RemoveRange(0, PACKET_HEADER_SIZE + bpp.PACKET_REMAIN_DATA_LENGTH); } } static T ConvertBytesTo<T>(byte[] data) { unsafe { fixed(byte *ptr = data) { //I am getting an access violation here when trying to //build child packet :( return (T)Marshal.PtrToStructure(new IntPtr(ptr), typeof(T)); } } } } 

Somewhere in the receive stream:

 while (clientStream.CanRead) { byte[] temp_buff = new byte[1024]; int received = 0; while ((received = clientStream.Read(temp_buff, 0, 1024)) > 0) //Here we receive 38 bytes { //Passing it to the packet splitter. PacketProcessor.ProcessPacketBytes(temp_buff, received); } } 

Result when a client sends a packet to the server:

Failed to handle System.AccessViolationException
Try reading or writing to protected memory. Most likely, this indicates damage to another memory.

Why can't it convert 38 bytes to CM_Request_AcceptAccount? What to do to make it work?

+4
source share
1 answer

When you declare a structure like

 struct CM_Request_AcceptAccount : BasePacketProto { unsigned char ACCOUNT_LOGIN[16]; unsigned char ACCOUNT_PASSWORD[16]; }; 

in C ++, an array is "inline" and "fixed" in length or, in other words, contributes 16 bytes each to the "size" of the structure.

But in C #, you re-declare the same structure as:

 [StructLayout(LayoutKind.Sequential)] class CM_Request_AcceptAccount : BasePacketProto { public byte[] ACCOUNT_LOGIN = new byte[16]; public byte[] ACCOUNT_PASSWORD = new byte[16]; } 

Here you do not indicate that the first 16 bytes belong to the ACCOUNT_LOGIN array, and the next 16 bytes belong to ACCOUNT_PASSWORD .

Line byte [] ACCOUNT_LOGIN = new byte [16]

says nothing to the marshaller. This causes the CLR to allocate a 16-byte array on the heap when an instance of CM_Request_AcceptAccount is created in the code.

To properly structure the structure, change the C # declaration to:

 [StructLayout(LayoutKind.Sequential)] class CM_Request_AcceptAccount : BasePacketProto { [MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] public byte[] ACCOUNT_LOGIN; [MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] public byte[] ACCOUNT_PASSWORD; } 

Additional info: Since your char arrays are for storing a C style string, you can also use

 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=16)] public string ACCOUNT_LOGIN; 

It should be remembered that the marshaller expects a null terminating character in 16 characters that you specified in your C ++ code.

+2
source

All Articles