Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Struct within Struct, able to change inner Struct type

Tags:

c#

struct

There isn't much explaining to this, this is what I have:

public struct PACKET_HEADER
    {
        public string computerIp;
        public string computerName;
        public string computerCustomName;
    };

    public struct PACKET
    {
        public PACKET_HEADER pktHdr;
        public PACKET_DATA pktData;
    };


    public struct PACKET_DATA
    {
        public Command command;
        public string data;  
    };

    public struct DATA_MESSAGE
    {
        public string message;
    };

    public struct DATA_FILE
    {
        public string fileName;
        public long fileSize;       
    };

Basically I want the data field in PACKET_DATA to be able to be either DATA_FILE or DATA_MESSAGE. I know the type needs to be changed but I don't know what to, is generics an option?

the end result should be so that I can do either:

pktData.data.fileName or pktData.data.message

EDIT

i could do:

public struct PACKET_DATA
{
    public Command command;
    public string data;
    public DATA_MESSAGE data_message;
    public DATA_FILE data_file;
};

and just set the data_message or file to null when ever i don't need them? how would this impact the serialization / byte array and the data being sent. If I used classes would I not have the same problem

EDIT 2

public struct PACKET_MESSAGE
{
    public PACKET_HEADER pktHdr;
    public Command command;
    public DATA_MESSAGE pktData;
};

public struct PACKET_FILE
{
    public PACKET_HEADER pktHdr;
    public Command command;
    public DATA_FILE pktData;
};

Edit 3

I have a sterilizer and de-sterilizer that works with my original example, if there are no hiccups with that then the actual serialization is done.

EDIT 4

everything seems to be working, apart from one things my serializer is getting "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." gunna have a look at it when post my working solution :)

EDIT 5

    public static byte[] Serialize(object anything)
    {
        int rawsize = Marshal.SizeOf(anything);
        byte[] rawdatas = new byte[rawsize];
        GCHandle handle = GCHandle.Alloc(rawdatas, GCHandleType.Pinned);
        IntPtr buffer = handle.AddrOfPinnedObject();
        Marshal.StructureToPtr(anything, buffer, false);
        handle.Free();
        return rawdatas;
    }

    public static object Deserialize(byte[] rawdatas, Type anytype)
    {
        int rawsize = Marshal.SizeOf(anytype);
        if (rawsize > rawdatas.Length)
            return null;
        GCHandle handle = GCHandle.Alloc(rawdatas, GCHandleType.Pinned);
        IntPtr buffer = handle.AddrOfPinnedObject();
        object retobj = Marshal.PtrToStructure(buffer, anytype);
        handle.Free();
        return retobj;
    } 

FINAL

The structs:

public struct PACKET_HEADER
{
    public string computerIp;
    public string computerName;
    public string computerCustomName;
};

public struct PACKET
{
    public PACKET_HEADER pktHdr;
    public PACKET_DATA pktData;
};

public struct PACKET_DATA
{
    public Command command;
    public IDATA data;
    public T GetData<T>() where T : IDATA
    {
        return (T)(data);
    }
}

public interface IDATA { }

public struct DATA_MESSAGE : IDATA
{
    public string message;
}

public struct DATA_FILE : IDATA
{
    public string fileName;
    public long fileSize;
}

How to create a new Packet (probally could combine together tbh):

    public static PACKET CreatePacket(Command command)
    {
        PACKET packet;
        packet.pktHdr.computerIp = Settings.ComputerIP;
        packet.pktHdr.computerName = Settings.ComputerName;
        packet.pktHdr.computerCustomName = Settings.ComputerCustomName;

        packet.pktData.command = command;
        packet.pktData.data = null;

        return packet;
    }

    public static PACKET CreatePacket(Command command, DATA_MESSAGE data_message)
    {
        PACKET packet;
        packet.pktHdr.computerIp = Settings.ComputerIP;
        packet.pktHdr.computerName = Settings.ComputerName;
        packet.pktHdr.computerCustomName = Settings.ComputerCustomName;

        packet.pktData.command = command;
        packet.pktData.data = data_message;

        return packet;
    }

    public static PACKET CreatePacket(Command command, DATA_FILE data_file)
    {
        PACKET packet;
        packet.pktHdr.computerIp = Settings.ComputerIP;
        packet.pktHdr.computerName = Settings.ComputerName;
        packet.pktHdr.computerCustomName = Settings.ComputerCustomName;

        packet.pktData.command = command;
        packet.pktData.data = data_file;

        return packet;
    }

(de) serialization above.

Simple example:

PACKET packet = Packet.CreatePacket(command, data_file);
byte[] byData = Packet.Serialize(packet);

other end:

PACKET returnPacket = (PACKET)Packet.Deserialize(socketData.dataBuffer, typeof(PACKET));
                        // Get file
string fileName = returnPacket.pktData.GetData<DATA_FILE>().fileName;
long fileSize = returnPacket.pktData.GetData<DATA_FILE>().fileSize;

All seems to be working nice and dandy :)

like image 772
Metalstorm Avatar asked Dec 20 '10 05:12

Metalstorm


People also ask

Can you put a struct inside a struct?

C Nested StructureOne structure can be declared inside other structure as we declare structure members inside a structure. The structure variables can be a normal structure variable or a pointer variable to access the data.

How do you call a struct inside another struct?

struct A { int data; B b; }; To do that, the compiler needs to already know what B is, so declare that struct before you declare A . Well, you can say struct B , but again, that's just the type. You still need give it a name.

Can structs be changed?

Once a struct is constant, it is constant. It can't change.

How do you write a struct in a struct?

Syntax of structstruct structureName { dataType member1; dataType member2; ... }; For example, struct Person { char name[50]; int citNo; float salary; }; Here, a derived type struct Person is defined.


1 Answers

This question needs a clear answer, so I'll try to sum up:

If you want to take a C# data structure and convert it into a byte array, you can do it with structs and Marshaling, or with classes (or structs, but why would you) and a serialization framework (like BinaryFormatter), or custom serialization logic (like with BinaryWriter). We could have a debate about which is better, but lets assume for now that we're going with structs, and we're using Marshaling. Although I will say, that structs are very limited, and should be used mainly as necessary for interop with Win32 API functions.

So the issue is, we have a container struct, which may contain one of two types of child structs. If you're going to Marshal a struct, things like generics and or using a common Interface for your child struct types are not going to fly. Basically you're only option is to have the container have both structs and a bool flag indicating which of the structures is to be used. This has the downside of increasing the size of your packets because you're sending the unused child struct as well.

In the case at hand, the result looks like this:

public struct PACKET_DATA
{
    public Command command;
    public string data;
    public bool is_message_packet;
    public DATA_MESSAGE data_message;
    public DATA_FILE data_file;
};

That said, in your case using structs and Marshalling is only really going to work within your own process, because your structs contain Strings. When a struct contains pointers to non-fixedlength strings, those strings are allocated elsewhere and will not be part of the byte array you copy, only pointers to them will be. You also need to call Marshal.DestroyStructure at some point, with the IntPtr you passed to StructureToPtr, in order to clean up these string resources.

So the moral of the story: can you make structs that do what you asked initially: yes. Should you be using them like you are: no. Because you have a variable size data structure that you are trying to send over a network (I presume since the struct is called PACKET), structs are not going to work, you really need to use some kind of serialization framework or custom serialization logic.

like image 168
Paul Wheeler Avatar answered Oct 23 '22 18:10

Paul Wheeler