[VB2010]Structure complexe et marshalling

FT8alain - 16 mai 2013 à 20:04
 FT8alain - 5 juin 2013 à 18:53
bonjour,

Je cale sur un problème depuis quelques jours.
Voici ma structure
----------------------------
public structure struc1
public a as byte
public b as byte
public c() as Ushort
end structure

public s() as struct1
----------------------------
j'initialise ma structure ainsi

dim i as byte
redim s(10)
for i = 0 to 9
redim s(i).c(9)
next

Jusque là, pas de problème.
Maintenant, je reçois, via un port série, un tableau de byte (trame(100)) agencé comme la structure que je voudrais mettre dans ma structure

Mes questions :
1 - comment trouver la taille de ma structure ? La méthode marshall.sizeof(s) ne fonctionne pas
2 - bytePtr Marshal.AllocHGlobal(len_trame) avec len_trame taille de ma structure
Marshal.Copy(trame, 0, bytePtr, len_trame)
s = CType(Marshal.PtrToStructure(bytePtr, GetType(struct1)), struct1)
Ca ne fonctionne pas :(

Merci d'avance.

A+
Alain

9 réponses

Utilisateur anonyme
16 mai 2013 à 22:25
Salut,

Le choix du Marshaling n'est peut-être pas judicieux. D'où proviennent les données reçues depuis ton port série ? Qui les agencent ? A-t-on la possibilité de modifier la structure des données reçues ?
0
Bonjour,

Merci de ta réponse.

Les données viennent, via une liaison série et un protocole proche de MODBUS, d'un système d'automatisme et de régulation. Ce système est fermé et donc non modifiable. Je ne connais que les structures d'échange (écriture et lecture).
Certaines structures sont assez simples n'ayant pas de tableau à l'intérieur, genre :
-------------------------
Public structure struct2
public aa as byte
public bb as byte
public cc as short
end structure
public ss as struct2
-------------------------
J'utilise donc :
-------------------------
len_trame = Marshal.SizeOf(ss)
bytePtr = Marshal.AllocHGlobal(len_trame)
ReDim trame(len_trame)
'copie en mémoire de buffer reception (que les données, en calculant poids fort / faible)
For i = 0 To len_trame - 1 Step 2
trame(i + 1) = com.buffer_reception(5 + i)
trame(i) = com.buffer_reception(i + 6)
Next
Marshal.Copy(trame, 0, bytePtr, len_trame)
ss = CType(Marshal.PtrToStructure(bytePtr, GetType(struct2)), struct2)
---------------------------

Là, ça fonctionne bien mais dès que j'ai un tableau dans la structure, je ne sais pas comment faire.

A+
Alain
0
NHenry Messages postés 15123 Date d'inscription vendredi 14 mars 2003 Statut Modérateur Dernière intervention 20 mai 2024 159
17 mai 2013 à 09:20
Bonjour,

Essayes de fixer en dur la longueur des données :
public c() as Ushort
à remplacer par (par exemple) :
public c(9) as Ushort

Mon site
0
Utilisateur anonyme
17 mai 2013 à 16:16
Bon voilà,

Mon code test est un peu brouillon. Désolé.
Il te faut rajouter un flag <MarshalAs(UnmanagedType.ByValArray, SizeConst:=9)> _ sur ton array UShort.
Voici le code :
Imports System.Runtime.InteropServices

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'initialisation d'une structure pour test reversible.
        Dim ms As struc1 = New struc1(9)
        ms.a = 255
        ms.b = 1
        ms.c(5) = 6

        Dim data() As Byte = StructureToBytes(ms)

        Stop

        Dim newstruct As struc1 = BytesToStructure(data)

        Stop

    End Sub


    Private Function StructureToBytes(ByVal str As struc1) As Byte()
        Dim size As Integer = Marshal.SizeOf(str)
        Dim arr(size) As Byte
        Dim ptr As IntPtr = Marshal.AllocHGlobal(size)
        Marshal.StructureToPtr(str, ptr, True)
        Marshal.Copy(ptr, arr, 0, size)
        Marshal.FreeHGlobal(ptr)
        Return arr
    End Function

    Private Function BytesToStructure(ByVal arr() As Byte) As struc1
        Dim str As struc1 = New struc1
        Dim size As Integer = Marshal.SizeOf(str)
        Dim ptr As IntPtr = Marshal.AllocHGlobal(size)
        Marshal.Copy(arr, 0, ptr, size)
        str = CType(Marshal.PtrToStructure(ptr, str.GetType()), struc1)
        Marshal.FreeHGlobal(ptr)
        Return str
    End Function

End Class

<Serializable()> _
Public Structure struc1
    Public a As Byte
    Public b As Byte
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=9)> _
    Public c() As UShort
    Sub New(ByVal val As Integer)
        ReDim c(val)
    End Sub
End Structure
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Salut,

Ca m'a l'air très intéressant. Je vais tester et revient dire si ça fonctionne.

Merci beaucoup de ton aide.

A+
Alain
0
Utilisateur anonyme
17 mai 2013 à 18:49
Remarque: SizeConst doit être fixé à 10 et l'attribut Serializable n'est pas utile
0
Salut banana32,

Ca fonctionne bien pour une structure ayant un tableau de défini. Maintenant, si je n'abuse pas trop, j'ai encore des questions:

Public Structure struc1
    Public a As Byte
    Public b As Byte
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=9)> _
    Public c() As UShort
    Sub New(ByVal val As Integer)
        ReDim c(val)
    End Sub
End Structure
public structure struc3
    public aa() as struct1
    .
    .
    .
end structure
public enfin as struc3

Je reçois une trame de 80 bytes représentant 4 x Struc1 -> enfin.aa(0) à enfin.aa(3) et je dois écrire une telle trame (structure -> byte())
Bien sûr, je peux utiliser des For/Next de 0 à 3 en découpant ma trame en 4 mais j'espère qu'il y a une solution plus élégante

De plus, comment utiliser tes fonctions BytesToStructure et StructureToBytes avec diverses structures (peut-être un byval objet ?) pour en faire des fonctions standards

Merci d'avance.

A+
Alain
0
Utilisateur anonyme
21 mai 2013 à 23:34
1/ Il n'y a rien d'inélégant du fait de découper ta trame par une boucle et de les rassembler dans une List(Of par exemple
Dim MaListeFinale As New List(Of struc1)
For x As Integer = 0 To trame.Length - 1 Step 20
    Dim arr(19) As Byte
    Array.ConstrainedCopy(trame, x, arr, 0, 19)
    MaListeFinale.Add(BytesToStructure(arr))
Next
'ici par exemple: MaListeFinale(1) représente la 2ème structure de la trame
...

2/ On peut standardiser la fonction StructureToBytes facilement car on peut lui passer une structure par un type objet en paramètre :
Private Function StructureToBytes(ByVal obj As Object) As Byte()
    Dim size As Integer = Marshal.SizeOf(obj)
    Dim arr(size) As Byte
    Dim ptr As IntPtr = Marshal.AllocHGlobal(size)
    Marshal.StructureToPtr(obj, ptr, True)
    Marshal.Copy(ptr, arr, 0, size)
    Marshal.FreeHGlobal(ptr)
    Return arr
End Function

Tu comprendras que la fonction inverse qui renvoie un type précis doit être adaptée à chacune de tes structures. Elle pourrait renvoyer un objet mais celui-ci devrait quand même être testé au bout du compte. Donc elle ressemblerait à une 'usine à gaz' et uniquement valable au sein de ton projet. Mais ça reste possible.
0
Salut,

Excuse le retard pour te répondre.
Je suis resté simple avec des for/next
J'ai réussi pour toutes mes structures.
Maintenant, j'ai des problèmes de "rezising" d'écran. Je vais aller voir dans la rubrique adéquate.

Merci de ton aide.

A+
Alain
0
Rejoignez-nous