none
CustomMarshaler + struct + nested struct RRS feed

  • Question

  • Bonjour,

    Je souhaiterais avoir quelques éclaircissement sur la manière dont je peux régler mon problème. Je développe actuellement un lecteur/extracteur de fichier iso (ISO9660). Mon objectif est de mapper sur des structures en C# quelque unes des entités du format ISO9660 (Genre le PrimaryVolumeDescriptor et les DirectoryRecord).

    Jusque là j'avais une méthode pour parser le PVD (PrimaryVolumeDescriptor) et une autre pour les DirectoryRecord. Pour ce faire, j'ai en entrée un byte[XX] buffer puis je fais par example Uint16.Parse(buffer, 0, Y), etc. Afin de réunir la définition des mes structures avec le format des entités ISO que je parse je voulais utiliser les StructLayout et les MarshalAs avec éventuellement des CustomMarshalers. Cela me permettrait aussi d'avoir une grande quantité de constante contenant les offsets de chacun des champs.

    1/ J'ai un champ dans mon ISO qui représente une taille et je veux la Marshaler pour obtenir une DateTime. Une personne a déjà tenté la chose ici mais visiblement ce n'est pas possible: http://social.msdn.microsoft.com/Forums/da-DK/clr/thread/a0a0add0-04bb-41d9-ab3b-1546772aa792

    Vaut mieux t-il, créer une instance de ma structure, lire séquentiellement mon fichier binaire, parser chaque champ et utiliser les setters pour avoir ma struct remplie ?  Dans ce cas là, utiliser une struct à la place d'une classe Object n'a plus de sens, non ?

    2/ Admettons que j'ai des structures imbriquées comme ci-dessous:

    [StructLayout(LayoutKind.Sequential)]
    public struct DirectoryRecord
    {
      ...
    }


    et

    [StructLayout(LayoutKind.Sequential)]
    public struct PVD
    {
    uint index;
    ...
    DirectoryRecord root;
    ...
    }

    et ensuite que je les utilise de la manière suivante:

    PVD pvd = (PVD)MarshalToStruct(buffer, typeof(PVD));

    Est-ce que mon champ root de la struct PVD sera automatiquement mappé sur mes données (si bien sûr tous mes champs sont bien alignés) ?

    3/ Dernière question, est-il pertinent d'utiliser un CustomMarshaler pour une structure entière ? Les performances risquent fortement d'être les mêmes qu'avec une méthode banale qui parse chacun des champ, non ? (On gagne peut-être un peu en visibilité)

    Merci d'avance

    lundi 19 novembre 2012 16:40

Réponses

  • Bonjour,

    Si la structure à une taille static et non dynamique, je vous conseille d'utiliser Marshal.PtrToStruct mais si la taille est variable, il faut Parser. Le plus rapide serait le Marshal.PtrToStruct (performance) mais après cela il faudra convertir chaque champ.

     

    Cordialement


    Merci de valider par "Proposer comme réponse" si celle-ci répond à votre demande !

    • Marqué comme réponse machukun mercredi 21 novembre 2012 14:01
    mercredi 21 novembre 2012 09:38

Toutes les réponses

  • Bonjour,

    Si la norme ISO9660 est structurelle, vous pouvez utiliser les structures avec les champs de taille définie.

    Si cette norme à une taille variable, vous êtes obligé de parser le binaire avec BinaryReader pour alimenter chaque champ.

    L'utilisation de Marshal sera plus rapide à mettre en œuvre que le parsing par champs et à mon sens plus performant.

    http://users.telenet.be/it3.consultants.bvba/handouts/ISO9960.html

     

    Cordialement


    Merci de valider par "Proposer comme réponse" si celle-ci répond à votre demande !


    • Modifié Lyamine lundi 19 novembre 2012 22:57
    lundi 19 novembre 2012 22:53
  • Merci de votre réponse. Donc vous me conseilleriez de créer un CustomMarshaler pour mes structures (un pour le PrimaryVolumeDescriptor et un autre pour mon autre structure DirectoryRecord), c'est bien cela?

    Est-il possible de créer un marshaler pour obtenir des datetime ?

    Par exemple dans ma structure je souhaiterais avoir des champs DateTime dont les valeurs seraient obtenu à partir d'un CustomMarshaler utilisé avec le [MarshalAsAttribute] ?

    struct PVD {
    ...
    [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyDateTimeMarshaler))]
    	public DateTime myDateTimeField;
    ...
    }

    Car j'ai essayé et j'ai à chaque fois le problème de "Invalid managed/unmanaged type combination (the DateTime class must be paired with Struct)"

    Et est-il possible de créer des marshaler qui utilisent d'autre champs ? Par exemple, dans ce genre de structure, on a un champ NameLength qui indique la longueur de chaine de caractères pour le champ Name, puis le champ Name qui contient les caratères. Est-il possible de marshaler le champ Name en fonction du champ NameLength (car les chaines dans la norme ISO9660 ne sont pas null-terminated)


    • Modifié machukun mardi 20 novembre 2012 13:23
    mardi 20 novembre 2012 12:38
  • DateTime est un objet purement .NET, il n'est pas possible de le faire passer dans une structure non managée.

    Si vous avez une longueur de champs définie avant le champs lui même(exemple : 0x0500hello), vous êtes obligé de tout parser via BinaryReader.

    Il est préférable pour vous de comprendre le format ISO9660 (même moi je n'ai pas encore regardé dans les détails).

    Voici un lien sur l'exploitation ISO96660 en C# :

    http://isocs.codeplex.com/

    Téléchargez le code et regardez ce qu'il en découle.

     

    Cordialement


    Merci de valider par "Proposer comme réponse" si celle-ci répond à votre demande !

    mardi 20 novembre 2012 20:34
  • Merci pour cette précision. Bon ce projet n'est ni plus ni moins un wrapper vers une API windows. Quoiqu'il en soit, je commence à me rendre compte que je ne pourrais pas utiliser mes structures comme je le souhaiterais.

    Cela m'amène à me demander s'il est toujours pertinent pour mon projet d'utiliser des structures. Est-ce que j'utilise des structures avec des types disons bruts (mon champ DateTime devient un simple char[]) puis à l'aide de Marshal.PtrToStruct j'obtiens une structure qui me permet de lire rapidement des champs pour finalement créer des Object, exploité dans le reste de mon application ? Ou, est-ce plus simple de simplement utiliser, comme vous me l'indiquer, un BinaryReader pour construire mes Object ?

    Cas 1/ binary data -> Marshal.PtrToStruct -> PrimaryVolumeDescriptorStruct -> Parser qui retourne un Object PrimaryVolumeDescriptor ?

    Cas 2/ binary data -> Parser avec binary reader qui me retourne un Object PrimaryVolumeDescriptor ?

    Pensez-vous que les performances sont équivalentes ? Auquel cas, le cas 2 semble être plus simple...

    mercredi 21 novembre 2012 08:39
  • Bonjour,

    Si la structure à une taille static et non dynamique, je vous conseille d'utiliser Marshal.PtrToStruct mais si la taille est variable, il faut Parser. Le plus rapide serait le Marshal.PtrToStruct (performance) mais après cela il faudra convertir chaque champ.

     

    Cordialement


    Merci de valider par "Proposer comme réponse" si celle-ci répond à votre demande !

    • Marqué comme réponse machukun mercredi 21 novembre 2012 14:01
    mercredi 21 novembre 2012 09:38