[.Net] Marshalling, sérialisation dans un buffer

D’abord, un peu de code:

[code]using System;
using System.Runtime.InteropServices;

namespace Test
{
public static class RawSerializer
{
public static byte[] RawSerialize(object AnyObject)
{
int Size = Marshal.SizeOf(AnyObject);

		IntPtr Buffer = Marshal.AllocHGlobal(Size);
		Marshal.StructureToPtr(AnyObject, Buffer, false);
		byte[] RawData = new byte[Size];

		Marshal.Copy(Buffer, RawData, 0, Size);
		Marshal.FreeHGlobal(Buffer);
		return(RawData);
	}

	public static object RawDeserialize(byte[] RawData, Type AnyType)
	{
		int Size = Marshal.SizeOf(AnyType);

		if (Size > RawData.Length)
			return(null);

		IntPtr Buffer = Marshal.AllocHGlobal(Size);
		Marshal.Copy(RawData, 0, Buffer, Size);
		object ReturnObj = Marshal.PtrToStructure(Buffer, AnyType);

		Marshal.FreeHGlobal(Buffer);
		return(ReturnObj);
	} 
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public abstract class Packet
{
	private Int32 PropertyPacketId;

	public Int32 PacketId
	{
		get { return (PropertyPacketId); }
		set { PropertyPacketId = value; }
	}
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class AcknowledgeMessage : Packet
{
	[MarshalAs(UnmanagedType.BStr)]
	public string TestString;
}

static class Program
{
	/// <summary>
	/// The main entry point for the application.
	/// </summary>
	[STAThread]
	static void Main()
	{
		AcknowledgeMessage Message = new AcknowledgeMessage();
		Message.PacketId = 6543213;
		Message.TestString = "Hello There ,zqdoq oqshgfoqh oihqsohgsoq hfsgh dhg";

		byte[] Buffer = RawSerializer.RawSerialize(Message);
		Message = null;

		AcknowledgeMessage Result = (AcknowledgeMessage)RawSerializer.RawDeserialize(Buffer, typeof(AcknowledgeMessage));

		Console.WriteLine(Result.PacketId);
	}
}

}[/code]

Alors ce qui m’inquiètes, c’est que quelque soit la taille de Message.TestString, la taille de mon buffer fait toujours 8 bytes. Mon petit doigt me dit: bah ca doit etre des pointeurs vers les variables.

Je pensais vérifier ma théorie en mettant Message = null; mais visiblement, même après ca, il me trouve encore ma TestString correcte…

Est ce que quelqu’un peut m’expliquer? C’est une histoire de garbage collector qui a pas le temps de nettoyer la classe?

Comment faire pour que toute la classe transite dans le buffer, et non pas seulement les pointeurs?

Oui, ca marche que poru les structs, pas pour les classes. La t’as juste un pauvre pointeur qui veut pas dire grand chose. A la limite sur la meme machine, dans lememe process, apres non…
Il faut utiliser un vrai BinarySerializer et pas essayer de reinventer la poudre en essayant de bidouiller directement la memoire ce qui en .Net est LeMal™ ? :stuck_out_tongue:

Mais mais mais, ca me semblait tellement plus simple avec Marshall… je peux bien utiliser les structures, mais pas de possibilité de spécialisation avec les structures: on ne peut pas faire:

[code]public struct Packet { … }

public struct AckPacket : Packet { … }[/code]

Par contre, ce que je peux faire:

[code]public interface Packet
{
public Int32 PacketId { get; set; }
}

public struct AckPacket : Packet { … }[/code]

Mais ca me fais chier de devoir a chaque fois réimplémenter le PacketId. Une autre solution:

[code]public struct Header
{
public Int32 PacketId { … }
}

public struct AckPacket
{
public Header Header { … }
}[/code]

Ou l’on référence l’entête en tant que structure séparée dans le packet Ack. Laquelle de ces solutions est la plus “propre”?

Aucune des deux: te faut bien comprendre la difference fondamentale entre une struct et une classe deja. :stuck_out_tongue:

Ou tu utilise un vrai BinaryFormatter.

Ou tu definies tes structures « a plat » proprement comme des representation memoire de ce qui va passer dans ton paquet et tu fais

struct_1 {
int packetId;
int machin_chose;
uint bidulle_truc;
}

struct_2 {
int packetId;
int autreChose;
byte[SIZE] monMachinDeTailleFixe;
}

Et tu evites bien sur les pointeurs comme la peste, ca veut donc aussi dire les « strings ». Tu veux faire passer des trucs sur le fil, ca veut dire des representation aussi proche que possible de ce qui va se passer si tu veux faire mumuse avec la memoire. Pas d’heritage, pas d’indirection, pas d’interface, pas de properties, des data dans l’ordre, a plat. A la limite tu fais une classe autour qui remplit ta struct privee qui passe sur le fil au dernier moment.

Sinon, faire une struct qui en contient une autre ca sert quasi a rien et ca revient a tout mettre a plat a la chaine de toute facon.

struct a {
int ID;
}

struct b {
a mon_a;
}

strictement equivalent en memoire a:

struct b_toutpareil {
int ID;
}