# C struct to C# struct & vice versa



## visus (1. August 2007)

Hi,

Nachdem ich etwa 2 Wochen lang das Netz durchsucht habe um eine funktionierende Methode zu finden, bin ich nun hier gelandet.

Ich habe einen Server (in C geschrieben), der mit den Clients C structs austauscht. (Das Endianesproblem spielt z.Zt. keine Rolle, soweit nutzen alle Systeme die Network Byte Order, also Big Endian) Die bisherigen Clients agieren eher als Agenten, die auf den Systemen Updates fahren und die Systeme auf dem selben Stand halten. Soweit funktioniert das alles. Nun bin ich dabei einen Client zu schreiben, mit dem ich die Updateinformationen zum Server gelangen koennen. Ich habe also folgende Topologie:


> Clients <--> Server <--> Controller


Die Informationen, die der Server vom Controller empfaengt werden unbehandelt an die Clients gesendet. Folgende Topologie funktioniert nicht:


> Clients <--> Controller


Das liegt daran, dass die Clients nicht zeitgleich mit dem Controller verbunden sind, da sie auch mal aus sein koennen. Der Controller sendet also das struct an den Server und der wartet auf die Clients und verpasst denen dann die Updatespritze. Wie gesagt: Die Client <--> Server-Kommunikation funktioniert.

Soweit die Theorie:
Das C struct, das ich verwende sieht in etwa so aus:


```
struct packet_t
{
    /* type: dient zur Unterscheidung von Client und Controller */
    int type;
    /* checksum: sie wird aus dem body mit einem *geheimen*
     * Algorithmus generiert und dient der Abwehr von
     * Man-in-the-Middle-Attacken o.ae. */
    int checksum;
    /* body: enthaelt die Updateinformationen (XML) */
    char body[1024];
};
```
So empfangen die Clients und der Server die structs:

```
/* ... */
    struct packet_t packet;

    memset(&packet, 0, sizeof(struct packet_t));
    if (0 >= recv(client.sock.s, (char *) &packet, sizeof(struct packet_t), 0))
        return 0;
    /* ... */
```
Und so werden sie gesendet:

```
/* ... */
    if (0 >= send(client.sock.s, (char *) &packet, sizeof(struct packet_t), 0))
        return 0;
    /* ... */
```
Und jetzt kommt das Problem:
Die C# structs sind nicht so einfach zu senden/empfangen. Ich habe duzende Marshalingmethoden ausprobiert... Vergebens.
Ich packe mal die aktuelle Socket class, die ich angefangen habe ans Ende des Posts. Im Source sind noch einige Ueberreste aus vergeblichen Versuchen ... Ich habe sie jetzt mal drin gelassen um vermeindliche Loesungsvorschlaege, die ich bereits getestet habe zu demonstrieren um somit die Loesung des Problems zu beschleunigen.

Ich hoffe das bekommt einer von euch gebacken. Ich habe damit schon sehr viele Nerven verloren.

Danke schonmal,
visus


```
struct Packet
    {
        public Int32 Type;
        public Int32 Checksum;
        public String Body;
    }
    
    class Socket
    {
        private StreamReader clientStreamReader;
        private StreamWriter clientStreamWriter;
        //  LPStr, LPWStr, LPTStr, BStr, TBStr, VBByRefStr oder AnsiBStr
        [DllImport("struct.dll")]

        private static extern void StructToCharArr(int Type, int Checksum, char[] Body, [MarshalAs(UnmanagedType.LPStr)] out string Output);

        public Socket()
        {
            /* Nothing to do, yet */
        }

        public bool ConnectToServer(string IPAddress, Int32 Port)
        {
            try
            {
                TcpClient tcpClient = new TcpClient(IPAddress, Port);

                NetworkStream clientSockStream = tcpClient.GetStream();
                this.clientStreamReader = new StreamReader(clientSockStream);
                this.clientStreamWriter = new StreamWriter(clientSockStream);
            }
            catch (Exception e)
            {
                //cForm.addListBoxItem(e.Message);
                MessageBox.Show(e.Message);
                return false;
            }

            return true;
        }

        public static byte[] SerializeExact(object anything)
        {
            int structsize = Marshal.SizeOf(anything);
            IntPtr buffer = Marshal.AllocHGlobal(structsize);
            Marshal.StructureToPtr(anything, buffer, false);
            byte[] streamdatas = new byte[structsize];
            Marshal.Copy(buffer, streamdatas, 0, structsize);
            Marshal.FreeHGlobal(buffer);
            return streamdatas;
        }

        public static byte[] StrToByteArray(string str)
        {
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            return encoding.GetBytes(str);
        }

        public bool Send()
        {
            Packet packet;
            Checksum cs = new Checksum();
            string packetC;

            packet.Type = 2;
            packet.Checksum = cs.GenerateChecksum("");
            packet.Body = "";

            StructToCharArr(packet.Type, packet.Checksum, "test".ToCharArray(), out packetC);

            this.clientStreamWriter.WriteLine(packetC);
            MessageBox.Show(packetC);
            this.clientStreamWriter.Flush();

            return true;
        }
    }
```


----------



## deepthroat (1. August 2007)

Hi.

Also das mit dem Marshalling usw. würde ich nicht machen. Das ist zu kompiliziert, zu fehleranfällig und nicht einfach zu erweitern.

Wenn du sowieso XML Daten im body hast, warum sendest du nicht einfach alles als XML? Den type und die checksum kannst du doch wunderbar als Attribute in das Root Element packen.

Oder du überlegst dir eine andere Serialisierungsmethode. Ich würde die Daten jedenfalls nicht im Binärformat versenden sondern in irgend 'nem Textformat. Es gibt schließlich neben dem Endianessproblem auch das Problem des Structure-Packing.

Gruß


----------



## visus (1. August 2007)

Ich halte es fuer sehr schwierig, wenn ich die Checksum in das root Element packen wuerde. Ich hab vergessen zu erwaehnen, dass auch binaere Daten im body sein koennen. Und Binaryzeug im body, kann das XML dann nicht mehr parsebar machen. Das waere fatal. Die einfachste Loesung waeren meines Erachtens nach structs, sofern sie empfangbar und sendbar sind.

Das pack-Problem duerfte auch kein Problem sein, da es sich bei den Systemen um die selben Betriebssysteme handelt. Der Controller ist nur in C# geschrieben, da Usability Pflicht ist und das geht nunmal mit einer klickybunty-GUI einher. Damit muessen spaeter DAUs arbeiten.

Ich kam auch schon auf die Idee, dass ich eine DLL schreibe (wie man im Quelltext vielleicht noch sieht). Die DLL wuerde vom C# Programm als Argumente die Daten empfangen und als return-Value muesste sie mir dann einen byte-Array oder char-Array schicken, den ich dann einfach in den Streamwriter gebe. Dabei bin ich aber wieder gescheitert, da ich auch dann die Marshalingclass verwenden muss.

Hier mal ein Auszug aus dem DLL-Source, den ich probiert habe (und auch darin sind wieder mehrere gescheiterte Versuche zu erkennen):

```
struct packet_t
{
    int type;
    int checksum;
    char body[1024];
};

DLLIMPORT void StructToCharArr(int type, int checksum, char *body, char *pp)
{
    struct packet_t packet;
    char p[1032];
    pp = p;

    pp = (char *) malloc(sizeof(p));
    memset(pp, 0, sizeof(p));
    memset(&packet, 0, sizeof(struct packet_t));

    packet.type = type;
    packet.checksum = checksum;
    strcpy(packet.body, body);

    memcpy(pp, &packet.type, sizeof(int));
    pp += sizeof(int);

    memcpy(pp, &packet.checksum, sizeof(int));
    pp += sizeof(int);

    memcpy(pp, &packet.body, 1024);
    pp -= 2*sizeof(int);

    //MessageBox(NULL, (char *) pp, "size", 0);
}
```

... Any hints?

visus


----------

