CopyMemory, Len, LenB et la structure, comment tout faire fonctionner ? [Résolu]

NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 30 juin 2009 à 15:53 - Dernière réponse : NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention
- 16 juil. 2009 à 17:09
Bonjour,

J'ai actuellement la maintenance d'un projet professionnel codé en VB6.
Ce projet est composé de plusieurs applications communicantes entre elles par Winsock.
Les données échangées sont des structures "sérialisées" par CopyMemory.
Ces structures peuvent contenir d'autres structure, etc... en cascade.

Le problème est que durant la (dé)sérialisation il arrive parfois de ne pas retrouver toutes les données :
Exemple avec une structure qui me pose problème :
Elle est composée de plusieurs éléments et se termine par un Long nommé "End".
Quand je regarde pour connaitre sa position (et aussi pour connaitre la taille de la structure), je ne comprends plus rien :
VarPtr(toto.End) - VarPtr(toto) = 31612
Recherche de la position dans le tableau résultant du CopyMemory dans un Byte() : 30912
Donc le champs n'est pas mis au même endroit, là je ne comprend pas.
Pour la taille de la structure, j'arrive à :
Len(toto) = 29899
LenB(toto) = 31616

Mon problème est que je ne veux pas faire un buffer overflow, mais que je veux transférer toutes mes données.
Quelle(s) valeur(s) dois-je prendre ?
La position détectée ne correspond à aucunes valeurs (ni Len, ni LenB), et dans le tableau de résultat, dans les 4 derniers octets (même les 8 derniers), il y a que la valeur 0 donc pas mon Long.

Merci d'avance pour votre aide.

Pour information voici le code utilisé pour recherché l'élément dans le tableau d'octet de résultat :
ReDim ltData(0 To Max(Len(toto), LenB(toto)))
For j = 0 To 255
    ltPosition(j) = -1
    '------------------------------------
    toto.End = j
    '------------------------------------
    CopyMemory ltData(0), toto, UBound(ltData) - LBound(ltData) + 1
    For i = LBound(ltData) To UBound(ltData)
        If ltData(i) = j Then
            ltPosition(j) = i
            Exit For
        End If
    Next i
Next j

Ce code parcours les 256 possibilité d'un octet pour éviter les détections erronées et retourne le résultat dans le tableau ltPosition.

http://nhen0039.chez-alice.fr/index.php
Afficher la suite 

Votre réponse

18 réponses

Meilleure réponse
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 1 juil. 2009 à 10:01
3
Merci
J'ai refais quelques tests :

Résultats : VarPtr - RecherchePosition - Len - LenB
Champ testé : Dummy

Private Type TTest
    Chaine1 As String * 96
    Chaine2 As String * 96
    Dummy As Long
End Type
384 - 192 - 196 - 388
Me serais-je trompé ?

Private Type TTest
    TblWordEquipementInData As Integer
    FillByte As Byte
    FillByte2 As Byte
    Start As Long
    EquipementId As Long
    Dummy As Long
End Type
12 -12 - 16 - 16
Là OK

Private Type TTest
    FillByte As Byte
    FillByte2 As Byte
    Start As Long
    TblWordEquipementInData As Integer
    EquipementId As Long
    Dummy As Long
End Type
16 - 16 - 16 - 20

Private Type TTest
    FillByte As Byte
    FillByte2 As Byte
    TblWordEquipementInData As Integer
    Start As Long
    EquipementId As Long
    TestStr As String * 8
    Dummy As Long
End Type
28 - 20 - 24 - 32

Private Type TTest
    FillByte As Byte
    FillByte2 As Byte
    TblWordEquipementInData As Integer
    Start As Long
    EquipementId As Long
    TestStr As String * 8
    ByteArray(0 To 7) As Byte
    Dummy As Long
End Type
36 - 28 - 32 - 40

Private Type TTest1
    FillByte As Byte
    FillByte2 As Byte
    TblWordEquipementInData As Integer
End Type
Private Type TTest
    FillByte As Byte
    FillByte2 As Byte
    TblWordEquipementInData As Integer
    Start As Long
    EquipementId As Long
    SousType As TTest1
    Dummy As Long
End Type
16 - 16 - 20 - 20
Ça se comprend sans pb.

Private Type TTest1
    FillByte As Byte
    TblWordEquipementInData As Integer
    FillByte2 As Byte

End Type
Private Type TTest
    FillByte As Byte
    FillByte2 As Byte
    TblWordEquipementInData As Integer
    Start As Long
    EquipementId As Long
    SousType As TTest1
    Dummy As Long
End Type
20 - 20 - 20 - 24

Private Type TTest1
    FillByte As Byte
    FillByte2 As Byte
    TblWordEquipementInData As Integer
End Type
Private Type TTest
    FillByte As Byte
    FillByte2 As Byte
    TblWordEquipementInData As Integer
    Start As Long
    EquipementId As Long
    SousType(0 To 3) As TTest1
    Dummy As Long
End Type
28 - 28 - 32 - 32
Logique

Private Type TTest1
    FillByte2 As Byte
    TblWordEquipementInData As Integer
    FillByte As Byte
End Type
Private Type TTest
    FillByte As Byte
    FillByte2 As Byte
    TblWordEquipementInData As Integer
    Start As Long
    EquipementId As Long
    SousType(0 To 4) As TTest1
    Dummy As Long
End Type
44 - 44 - 36 - 48
C'est clair que le Len est un peu à coté de la plaque

Private Type TTest1
    FillByte2 As Byte
    FillByte As Byte
    TblWordEquipementInData As Integer
End Type
Private Type TTest
    FillByte As Byte
    FillByte2 As Byte
    TblWordEquipementInData As Integer
    Start As Long
    EquipementId As Long
    TestStr As String * 9
    SousType(0 To 4) As TTest1
    Dummy As Long
End Type
52 - 44 - 45 - 56

Suite à ces tests, il apparait clairement que VatPtr et LenB sont toujours d'accord.
Reste toujours mon CopyMemory qui me met ma variable selon une logique qui m'échappe.

Il semblerais que ce soit les String qui mettent un peu le boxon, car le Len prend un caractère = 1 octet (comme le CopyMemory) alors que LenB ( et VarPtr) considèrent 2 octets (unicode ou alignement des octets ?). Et si on ajoute à cela l'alignement, on arrive à un résultat surprenant.

Donc pour faire suite à mon message précédent, c'est un effet pervers qui pose pb.
Je vais essayer pour voir avec CopyMemory si LenB ne risque pas de bufferOverflow.

http://nhen0039.chez-alice.fr/index.php

Merci NHenry 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 93 internautes ce mois-ci

Commenter la réponse de NHenry
Meilleure réponse
Renfield 17308 Messages postés mercredi 2 janvier 2002Date d'inscription 22 août 2018 Dernière intervention - 1 juil. 2009 à 10:08
3
Merci
les chaines c'est dangereux...
prends un tableaux de Bytes, c'est moins conplexe.

un passage via StrConv, ça ira tout seul:

MonTableau = StrConv(data, vbFromUnicode)

Merci Renfield 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 93 internautes ce mois-ci

Commenter la réponse de Renfield
Meilleure réponse
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 1 juil. 2009 à 10:13
3
Merci
Bonjour,

Pour le test concernant le BufferOverflow, le résultat est clair, il ne faut pas utiliser LenB :
En reprenant la dernière structure de mon précédent message :

Private Sub Form_Load()
Static toto As TTest
Dim ltData() As Byte
Dim j As Long

ReDim ltData(0 To LenB(toto) - 1)
For i = LBound(ltData) To UBound(ltData)
    ltData(i) = 0
Next i
For j = 0 To 255
    CopyMemory toto, ltData(0), UBound(ltData) - LBound(ltData) + 1
Next j
End Sub

Le but de ce code est simple, faire 256 fois un CopyMemory de 0x00 de taille LenB dans la structure, rien de bien compliqué.
Résultat, l'execution en pas à pas montre clairement un problème :
Avec LenB, on ne passe qu'une fois dans la boucle, en sortie j=1 (étrange :) )
Avec 48 à la place LenB, aucun pb, on passe bien 256 fois dans le code

Donc, LenB et CopyMemory, c'est pas compatible (sauf si on a aucune chaine de caractère dans les structures).

http://nhen0039.chez-alice.fr/index.php

Merci NHenry 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 93 internautes ce mois-ci

Commenter la réponse de NHenry
Renfield 17308 Messages postés mercredi 2 janvier 2002Date d'inscription 22 août 2018 Dernière intervention - 30 juin 2009 à 16:28
0
Merci
quelle est la structure ? quels en sont les champs ?
Commenter la réponse de Renfield
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 30 juin 2009 à 16:44
0
Merci
Bonjour,

Pour faire court (si tu veux plus d'élément, je peux te passer ça en privé) :

Public Type TServeurData
    Start As Long
    EquipementId As Long
   
    IlotInData As TAutomationInDataIlot
    IlotInId As Long
    QuaiInId(MAX_QUAIS_ILOT + 1) As Long
    QuaiInData(MAX_QUAIS_ILOT + 1) As TAutomationInDataQuai
   
    'Code masqué
   
    TblWordEquipementInData(EQUIPEMENT_WORD_TABLE_LEN_IN) As Integer
    TblWordEquipementOutData(EQUIIPEMENT_WORD_TABLE_LEN_OUT) As Integer

    PipeInId As Long
   
    FillByte(336) As Byte
   
    Dummy As Long   ' do not remove
    End As Long
End Type

Toutes les chaines et tous les tableaux contenus dans les sous structures sont de taille fixe.

Bien que nommer une variable "End" ne sois pas forcément super, ça fonctionne

en essayant avec Dummy :
31608 - 30908 - 29899 - 31616
et pour End c'était :
31612 - 30912 - 29899 - 31616

La position est bien décalée de 4 octets, et la taille est la même.

Merci de t'intéresser à mon problème.

http://nhen0039.chez-alice.fr/index.php
Commenter la réponse de NHenry
Renfield 17308 Messages postés mercredi 2 janvier 2002Date d'inscription 22 août 2018 Dernière intervention - 30 juin 2009 à 17:04
0
Merci
VB aligne très certainement les champs de tes structures sur 4 octets, pour y acceder plus facilement (sans avoir a faire des masques), ce qui explique peut etre ces trous.

gaffe également a la representation mémoire d'un Long (Little/Big Endian, etc.)

le fait que tu aies des sous structures n'aide pas non plus, a mon humble avis.
Commenter la réponse de Renfield
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 30 juin 2009 à 20:17
0
Merci
Bonjour,

Donc la structure fait quelle taille ? 30916 octets ? et comment en être sûr et récupérer la valeur automatiquement, car j'ai plusieurs structures et c'est énormément utilisé dans l'application ?

Concernant la représentation, ce n'est pas le problème (d'un coté on passe de Type à Byte(), puis en face, Byte() à Type).

http://nhen0039.chez-alice.fr/index.php
Commenter la réponse de NHenry
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 30 juin 2009 à 22:04
0
Merci
Bonjour,

Suite à une discussion sur le chan #programmation, et l'aide de MyGoddess, je vais essayer de passer tout ça en classes.

Je donnerais plus d'informations demain.

http://nhen0039.chez-alice.fr/index.php
Commenter la réponse de NHenry
Renfield 17308 Messages postés mercredi 2 janvier 2002Date d'inscription 22 août 2018 Dernière intervention - 1 juil. 2009 à 07:44
0
Merci
c'est bien une histoire d'alignement des structures, pour acceder plus facilement a la mémoire :

Private Type A
Start As Long
EquipementId As Long
End As Long
TblWordEquipementInData As Integer
FillByte As Byte
End Type

Private Sub Form_Load()
Dim var As A
MsgBox Len(var) => 15 donc la taille stricte des champs, additionnés
MsgBox LenB(var) => 16 la taille reele de la structure
End Sub

là, la structure est optimisée en ce sens: les champs les lus grands en premiers
en inversant simplement les champs, on obtiens:

Private Type A
TblWordEquipementInData As Integer
Start As Long
EquipementId As Long
End As Long
FillByte As Byte
End Type

on à bien sur un Len de 15
mais un LenB de 20

après TblWordEquipementInData, VB laisse donc un trou de deux octets, pour pouvoir adresser directement Start

ce qui nous fais bien 16 octets, plus 4 pour FillByte

en organisant:
Private Type A
TblWordEquipementInData As Integer
FillByte As Byte
Start As Long
EquipementId As Long
End As Long
End Type

donc l'Integer et le Byte qui se suivent, les 2 octets nécessaire après l'Integer pour aligner le Long sont comblés par FillByte

de même:
Private Type A
TblWordEquipementInData As Integer
FillByte As Byte
FillByte2 As Byte
Start As Long
EquipementId As Long
End As Long
End Type

Donne un Len et un LenB égaux, de 16 puisque nous ne générons pas de trou.

un CopyMemory devra donc TOUJOURS voir un LenB utilisé, sous peine de manquer des infos, décalées pour alignement.

Il faut également veiller a bien structurer les Types, pour miniser ce phénomène d'alignement.
Commenter la réponse de Renfield
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 1 juil. 2009 à 09:32
0
Merci
Bonjour,

Merci pour tes explications.

Mais cela n'explique pourquoi avec VarPtr, je trouve 31608 pour la position de ma variable, et 30908 en recherchant dans le tableau copié.

Donc si j'utilise la taille de LenB, pas de risque de bufferoverflow. ? Car d'après l'analyse de la position du champ, il y a quand même 700 octets d'écart.

http://nhen0039.chez-alice.fr/index.php
Commenter la réponse de NHenry
Renfield 17308 Messages postés mercredi 2 janvier 2002Date d'inscription 22 août 2018 Dernière intervention - 1 juil. 2009 à 09:35
0
Merci
si c'est un champ d'une sous-structure ou d'un tableau, possible, j'imagine, la mémoire est allouée selon la place libre, donc...
Commenter la réponse de Renfield
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 1 juil. 2009 à 10:17
0
Merci
Concernant les chaines, ce serait long dans le code de tout changer (plusieurs centaine de milliers de lignes de code dans plus de 7 projets ... ).
Donc Je vais passer ça dans la fiabilisation du produit, à faire en urgence, et qui va prendre du temps.

Merci de votre aide [../auteur/RENFIELD/2359.aspx Renfield] et MyGoddess, je pars de suite faire un compte rendu à mon supérieur pour planifier tout ça.

http://nhen0039.chez-alice.fr/index.php
Commenter la réponse de NHenry
Renfield 17308 Messages postés mercredi 2 janvier 2002Date d'inscription 22 août 2018 Dernière intervention - 1 juil. 2009 à 10:38
0
Merci
Fais le test suivant:


Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)

Private Type TTest
Chaine1 As String * 96
Chaine2 As String * 96
Dummy As Long
End Type

Private Sub Form_Load()
Dim xbData() As Byte
Dim tA As TTest
ReDim xbData(LenB(tA) - 1)

tA.Chaine1 = "Hello World !"
tA.Chaine2 = "abcdefghijklmnopqrstuvwxyz"
tA.Dummy = &HFFFFFFFF

Debug.Print Len(tA), LenB(tA), VarPtr(tA.Dummy) - VarPtr(tA)

CopyMemory xbData(0), tA, LenB(tA)

Stop
End Sub



au Stop, j'observe le tableau de bytes obtenu.

les chaines ne sont pas 'envoyées' en Unicode, on n'utilise là au final que 96 octets pour chacune des chaines.
résultat net, un tableau trop grand dont la moitié est inutilisée.

en ajoutant un Integer en plein milieu:
Private Type TTest
Chaine1 As String * 96
Zut As Integer
Chaine2 As String * 96
Dummy As Long
End Type

on met le 'souk' :

les 96 caractères de Chaine1
les 2 octets de notre Integer
les 96 caractères de Chaine2
2 octets de trou, pour aligner Dummy

et dans tout cela, Len nous renvoie 198:
96*2 + 2 +4

alors qu'en mémoire on utilise 200 ...

on doit donc pourvoir faire le calcul du tableau nécessaire en faisant:

4*(1+198 \ 4)

j'ai fait le test, ça a l'air de fonctionner comme il faut:


ReDim xbData(4 * (1 + Len(tA) \ 4) - 1)
CopyMemory xbData(0), tA, LenB(tA)
Commenter la réponse de Renfield
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 1 juil. 2009 à 11:02
0
Merci
Bonjour,

Ça fonctionne, mais il faut réaligner toutes les structures et ne pas avoir plus de 3 espace vide sur l'ensemble de la structure (donc inclu les sous structures). Ce qui dans mon cas, si ce n'est pas impossible, est très difficile, sachant que j'ai plus de 20 sous structures contenant elles aussi des sous sous structures, ...

http://nhen0039.chez-alice.fr/index.php
Commenter la réponse de NHenry
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 1 juil. 2009 à 11:20
0
Merci
Bonjour,

Sinon, il est possible d'ajouter des champs pour réaligner les champs de manière intentionnelle.
Je pars sur cette piste, plus rapide à mettre en œuvre.

http://nhen0039.chez-alice.fr/index.php
Commenter la réponse de NHenry
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 1 juil. 2009 à 19:41
0
Merci
Bonjour,

Donc, pour terminer le sujet (du moins pour le moment), j'ai donc comblé les espaces vides :

Type TTest
    UnOctet as Byte
    UnLong as Long
End Type

Devient :
Type TTest

    UnOctet as Byte
    FillField as Byte


    FillField1 as integer


    UnLong as Long

End Type

Et le Len passe de 5 à 8, et le CopyMemory peut fonctionner correctement.

Le passage en Classe, attendra un peu, le plus urgent était de faire fonctionner la communication.

Merci de votre aide.
Longue vie à CS :)

http://nhen0039.chez-alice.fr/index.php
Commenter la réponse de NHenry
Renfield 17308 Messages postés mercredi 2 janvier 2002Date d'inscription 22 août 2018 Dernière intervention - 16 juil. 2009 à 16:41
0
Merci
Probleme résumé et repris ici:

http://blogs.codes-sources.com/renfield/archive/2009/07/16/len-vs-lenb-copymemory-structures-perso-types-etc.aspx

Renfield - Admin CodeS-SourceS - MVP Visual Basic
Commenter la réponse de Renfield
NHenry 14260 Messages postés vendredi 14 mars 2003Date d'inscription 16 septembre 2018 Dernière intervention - 16 juil. 2009 à 17:09
0
Merci
Bonjour,

Concernant les chaines (omis dans ton article) :

LenB considère que 1 caractère=2 octets alors que Len (et CopyMemory) 1 caactère=1 octet.

Donc quand on a des chaines, il faut une structure alignée (pour pouvoir utiliser Len) et utiliser Len à la place de LenB, sinon BufferOverflow en vue.

Voilà pour la précision.

http://nhen0039.chez-alice.fr/index.php
Commenter la réponse de NHenry

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.