Rapatrier un tableau de string de ma DLL

cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 - 14 mai 2009 à 17:30
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 - 17 mai 2009 à 16:56
Bonjour à tous

J'ai une DLL standard (donc non activeX) qui est ecrite dans un autre language.
Dans cette DLL, j'ai des fonctions qui me retournent des variables strings par leur adresse.

Dans mon code VB, je recupere les dites variable strings, dont j'ai l'adresse, grace à ce code :

Public Declare Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, ByVal lpString1 As Any) As Long
Public Declare Function lstrlen Lib "kernel32.dll" Alias "lstrlenA" (ByVal lpString As Any) As Long 'Détermine la longeur d'une chaine passée en argument.

Dim RetourDll As String
RetourDll = Space$(lstrlen(AdresseVariable))
lstrcpy VariableString, RetourAdresse

Jusqu'a maintenant, j'avais juste besoin de variables, mais j'aimerais faire pareil avec un tableau de string.
Et c'est la que les atheniens s'atteignirent :-(

Ma DLL me renvoie cette fois l'adresse du tableau de string, mais je n'arrive pas a recuperer tout le tableau :-(
Voie meme que je recupre quedal.....

Si quelqu'un pouvait me donner un petit coup de main, ce serait gentil :-)

Je vous remercie
Bonne journée

10 réponses

cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
15 mai 2009 à 01:27
Salut
Les chaines ont en effet une adresse fixe, c'est à dire qu'on retrouve la 'zone mémoire' du texte à partir de son adresse 'mémoire'.
Pour les tableaux, c'est pareil si la longueur de ses éléments est fixe, par exemple, un tableau de Long (4 octets) ou un tableau de chaines dont la longueur est fixe (dimensionné en String * 20, par exemple).
Par contre, pour les tableaux de chaines à longueur indéterminée, le pointeur vise non pas le texte, mais l'adresse du texte.
De cette manière, le tableau est un tableau normal de variables de 16 octets dont le contenu désigne les emplacements mémoires de chaque chaine.

  The base of the array is defined as follows (each block represents a byte):
      NAME      BYTES     DESCRIPTION
    ----------- ------- -------------------------------------------------
    cDim          2        A count of the number of dimensions in the array.
    fFeature      2        Flags
    cbElements    4        Size of each element in the array.
    cLocks        4        Used to lock the array.
    pvData        4        A pointer to the first data element in the array.
    rgsabound     8*cDim   Safe_Array_Bound array.

PointeurTableau = 399B920                 Ici, la structure du tableau :
----------------------------
 0 (399B920) >> . 1h  (1) [00000001]   \  cDim : Nbr dimensions : x(0)=1, x(0,0)=2 ...
 1 (399B921) >> . 0h  (0) [00000000]   /
 2 (399B922) >> ? 80h (128) [10000000] \  Flags : Type de datas (Voir lien 1)
 3 (399B923) >> . 1h  (1) [00000001]   /
 4 (399B924) >> . 4h  (4) [00000100]   \
 5 (399B925) >> . 0h  (0) [00000000]    | cbElements : Taille des éléments dans le
 6 (399B926) >> . 0h  (0) [00000000]    |   SafeArray (Integer=2, Long=4 ...)
 7 (399B927) >> . 0h  (0) [00000000]   /
 8 (399B928) >> . 0h  (0) [00000000]   \
 9 (399B929) >> . 0h  (0) [00000000]    | cLocks : Pas bien compris à quoi ça
10 (399B92A) >> . 0h  (0) [00000000]    |   pouvait servir "bloquer un tableau" ...
11 (399B92B) >> . 0h  (0) [00000000]   /
12 (399B92C) >> ð F0h (240) [11110000] \  pvData : L'adresse de la zone de stockage
13 (399B92D) >> . 17h  (23) [00010111]  |   des données (pour des Long) ou des
14 (399B92E) >> – 96h (150) [10010110]  |   adresses (pour les String)
15 (399B92F) >> . 3h  (3) [00000011]   /
16 (399B930) >> . 5h  (5) [00000101]   \
17 (399B931) >> . 0h  (0) [00000000]    | cElements : Nombre d'éléments dans le
18 (399B932) >> . 0h  (0) [00000000]    |    tableau (ici 5 car 0 à 4)
19 (399B933) >> . 0h  (0) [00000000]   /
20 (399B934) >> . 0h  (0) [00000000]   \
21 (399B935) >> . 0h  (0) [00000000]    | lLbound : Index du Premier élément
22 (399B936) >> . 0h  (0) [00000000]    |
23 (399B937) >> . 0h  (0) [00000000]   /
24 (399B938) >> . 10h  (16) [00010000] \
25 (399B939) >> . 0h  (0) [00000000]    | Idem pour dimension suivante (cas des
26 (399B93A) >> . 6h  (6) [00000110]    |   tableaux x(0,0) - pas utilisé ici)
27 (399B93B) >> . 0h  (0) [00000000]   /
28 (399B93C) >> § A7h (167) [10100111] \
29 (399B93D) >> . 1h  (1) [00000001]    | ...
30 (399B93E) >> . Ch  (12) [00001100]   |
31 (399B93F) >> . 1h  (1) [00000001]   /

Liens utiles :
<Excellente source explicative>
<How Visual Basic 6 Stores Data>
<Win32 API Programming with Visual Basic - Chapitre 6>

Vala
Jack, MVP VB
NB : Je ne répondrai pas aux messages privés

<hr />Le savoir est la seule matière qui s'accroit quand on la partage (Socrate)
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
15 mai 2009 à 08:03
Bonjour Jack

Tout d'abord merci beaucoup de cette explication plus que precise, et de tes liens ;-)

Je t'avoue que le tableau que tu m'a envoyé est super interessant, mais un peu dur a comprendre pour un debutant dans la gestion memoire comme moi :-(
Je vais bien le garder, car a force de le regarder et utiliser la memoire, je devrais pouvoir le comprendre

Donc si j'ai bien compris, meme en cette periode de crise, un miracle est toujours possible :-)), le retour de ma DLL est l'adresse d'un tableau de pointeur memoire si mon tableau n'est pas dimensionné, ou bien un tableau d'adresse de variables qui devraient etre les unes a la suite des autres séparée je crois par un chr(0),  si il est dimensionné :-)

Alors comment faire pour rappatrier ce tableau complet soit de pointeur, soit de variables ????
J'ai l'impression qu'un caractere style chr(0) ou autre l'empeche de lire la memoire à partir de l'adresse que renvoie la DLL

Par contre j'ai essayé une bidouille.
Dans la DLL au lieu de renvoyer un tableau, je renvoie une phrase avec chaque element séparé par un chr(13)
Et la evidemment ça marche nickel
Je fais un debug.print et hop tout le tableau apparait avec evidemment aussi les retours à la ligne :-)

Mais j'aurais aimé une methode plus pro, plus conventionnelle, et surtout pouvoir rappatrier un vrai tableau dans VB.
Qu'est ce qui empeche de rapatrier ce tableau complet avec ses caracteres speciaux separateurs ????
0
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
15 mai 2009 à 09:55
Salut,
le problème du strcpy() avec des tableaux String est que strcpy stoppe au 1er ZERO rencontré car une chaine finit au 1er 0.
c'est fait ainsi:
char* strcpy(char *dest, char *src)
{
char *d = dest;
while(*d = *src++) d++;
return dest;
}

DWORD strlen(char *src)
{
char *c = src;
while(*c) c++;
return (c - src);
}

Les routines strcpy, strlen et autres sont faites pour traiter les chaines du point de vue du code natif, c'est totalment à exclure pour des String qui sont en interne des BSTR.
En effet, place un chr(0) au début d'une chaine VB et tu verras que tu ne récupèreras plus rien.

Pour conclure, va falloir faire ta propre dll au lieu d'utiliser l'API.
En PSEUDO CODE:
BSTR __stdcall bnConcatArrayBstr(SafeArray *pBstrs)
{
char *pmem, *d, *c;
DWORD i;
DWORD len;
DWORD count = 0;
BSTR res;
for(i = 0; i < cDim; i++) count += pBstrs[i].cbElement;
// gros benef rapport à VB est ici
// nenni multi allocs desallocs avec leurs recopies memoire
pmem = (char*) HeapAlloc(GetProcessHeap(), 0, count + 1);
d = pmem;
for(i = 0; i < cDim; i++) {
c = pBstrs[i].pvData;
len = pBstrs[i].cbElement;
while(len) { // MEMCPY AU LIEU DE STRCPY
*d++ = *c++;
len--;
}
}
res = SysAllocStringByteLen(pmem, count);
HeapFree(GetProcessHeap(), 0, pmem);
return res;
}

ATTENTION que c'est du pseudo code, juste un exemple sur la méthode car il faut déplacer un pointeur sur l'élem suivant alors qu'ici pour simplicité j'ai regardé la chose comme un simple tableau.
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
15 mai 2009 à 14:35
Bonjour BRUNEWS

Decidement, j'ai drolement des caids de la programmation qui me repondent aujourd'hui, et je vous en remercie.

BRUNEWS tu va pas etre content .....  :-(
Mais alors pas etre content du tout :-((

Je me ferais couper un bras pour pouvoir converser avec toi, du plus grand language
C
elui que j'admire et que je reve depuis 20 ans de faire plus que d'allumer l'IDE :-(
J'ai acheté des dizaines de livres qui parle du C
reateur.
J'ai acheté aussi 2 ou 3 IDE du C
oncepteur
Du language des languages, sans parler du maitre ASM bien sur ;-)

Mais je ne suis pas assez calé pour faire quoi que ce soit avec lui :-(
Je vais donc te decevoir, et de cela je suis sincerement triste.
Je regarde mes etageres quotidiennement, pleine de ces jolis boites et livres, et malgré mon admiration pour lui.....et ben j'en sais pas plus :-(

En fait ce que je n'avais pas compris, c'est qu'un livre il ne suffit pas de l'acheter pour devenir intelligent......il faut le lire :-))))

En bref, tu as devant toi un bignolo, qui essai de jouer a l'apprenti sorcier en bidouillant des trucs dont il n'a malheureusement pas l'ombre d'une idée comment ils marchent.
Mais je te promet que pourtant, je suis depuis pres de 30 ans, un des plus grands fan de la programmation et surtout de vous "les programmeurs".

Donc le bricoleur du dimanche a qui tu viens de faire l'honneur d'adresser la parole, aurait voulu faire ça en VB.
Mais je sais que VB est pas tres copain avec les PEEK et compagnie :-(
Car ce language a été créé, pour que les burnes comme moi, n'aille pas trifouiller dans la mémoire pour planter le PC :-))

Alors si tu avais, une combine pour rappatrier ce tableau avec VB......deja que je suis un de tes FAN (sans que tu le sache), car je lis souvent tes POSTS un peu partout sans les comprendre evidemment :-), alors la je te serais reconnaissant à vie.

L'avantage des mauvais......c'est qu'ils n'ont pas de limite dans leur exigences et qu'ils osent tout.......puisqu'il n'ont pas non plus une once d'idée de ce qu'il demandent :-)))


En tout cas, cela m'a fait plaisir que tu t'interesse a un pauvre ere comme moi, et que tu me parle comme si j'etais un "vrai", ne serais ce quelques minutes.

Mais votre temps est trop precieux pour que je n'abatte pas les masques immediatement
Mes DLL C, je les fait avec un language simplifié, qui est extremement puissant, mais aussi extremement simplifié
Je sais c'est pas bien.......mais c'est pour l'instant tout ce que je suis capable de faire avec mon petit cerveau

Ce n'est pas parceque l'on aime une fille à la folie et que l'on donne tout pour elle, qu'elle vous apprecie et vous le rend
Et ben j'ai un peu cette sensation avec la prog et les dizaines d'années que je lui ai consacrée :-))

Je te souhaite une excelente journée
0

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

Posez votre question
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
15 mai 2009 à 19:17
Je t'ai fait une DLL API bnVbStr.dll (2.5 Ko) que tu trouveras ici:
http://brunews.com/bnVbStr.zip
le zip fait 786 octets.


J'espère avoir bien compris le but du jeu, on passe un tableau de String et DLL retourne une String qui est la concaténation des String du tableau.
Je me suis fait test depuis VBA Excel.


Option Explicit
Declare Function bnConcatArrayStr Lib "D:\bnVbStr.dll" (pArrStr() As String) As String


Sub tstArrayStr()
  Dim arrStr(8) As String
  Dim R As String
  arrStr(0) = "salut "
  arrStr(1) = "la "
  arrStr(2) = "foule "
  arrStr(3) = "et "
  arrStr(4) = "basta "
  arrStr(5) = "FIN" & Chr(0) & "STOP"
  R = bnConcatArrayStr(arrStr())
  Debug.Print R
End Sub


Penser à changer le chemin que j'ai mis dans le 'Declare' vb.
J'ai volontairement laissé des elems du tableau non initialisés et j'ai mis un 0 dans arrStr(5), on récupère bien tous les octets.


Code de la fonction DLL:
BSTR bnConcatArrayStr(SAFEARRAY **pString)
{
  SAFEARRAY *psa;
  BSTR HUGEP *pbstr;
  BSTR R;
  char *c, *d, *pmem = 0;
  DWORD i, n, len, count = 0;
  psa = *pString;
  if(0 > SafeArrayAccessData(psa, (void HUGEP**)&pbstr)) goto goResult;
  i = psa->rgsabound->cElements;
  if(!i) goto unACCESS;
  n = --i;
  do {
    count += SysStringByteLen(pbstr[i]);
  } while(0 <= (long) --i);
  if(!count) goto unACCESS;
  pmem = (char*) HeapAlloc(GetProcessHeap(), 0, count + 1);
  if(!pmem) {
    count = 0; // pour SysAllocStringByteLen
    goto unACCESS;
  }
  d = pmem;
  i = 0;
  do {
    if(pbstr[i]) {
      len = SysStringByteLen(pbstr[i]);
      if(len) {
        c = (char*) pbstr[i];
        do {
          *d++ = *c++;
        } while(--len);
      }
    }
  } while(++i <= n);
unACCESS: SafeArrayUnaccessData(psa);
goResult: // DOIT ETRE FAIT A TOUT COUP !!!
  R = SysAllocStringByteLen(pmem, count);
  if(pmem) HeapFree(GetProcessHeap(), 0, pmem);
  return R;
}

ciao...
BruNews, MVP VC++
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
15 mai 2009 à 22:16
Tout d'abord, mille merci BRUNEWS de ton exemple.
Et surtout de ta comprehension et de ton aide.

Je sais que les gens comme toi qui maitrisent le grand C, n'apprécient guerre tous ces langages qui fleurissent de plus en plus, et qui essaient de jouer dans la cour des grands.
Je te suis donc doublement reconnaissant de bien vouloir m'aider malgré le fait que je n'utilise pas actuellement le seul vrai créateur de DLL.
Cela ajoute a ta compétence, l'ouverture d'esprit...c'est cool

En fait, comme j'y connait rien je ne saurais pas te dire si mon problème est plus simple ou plus compliqué que ton exemple.

J'ai une DLL qui normalement est de format standard, donc un des deux standard d'appel du C, mais je ne rappelle plus lequel, j'ai un vague souvenir de stdcall, mais c'est peut être pas ça.

Dedans, y'a des fonctions qui lisent un fichier TXT tout bête, et qui le met dans un tableau.
Dans cette DLL, je retourne l'adresse du tableau qui est chargée du fichier.

Je peut te dire que le tableau est declaré en STATIC ou GLOBAL dans la DLL (je ne sais pas si ça te cause) afin qu'il puisse sortir de la DLL et etre lu.
Avec ce meme language, je n'ai aucun probleme a le lire en recuperant l'adresse du tableau et en transferant le tablo de la DLL dans le tablo de l'EXE

Je n'ai aussi aucun doute sur le fait que le C, pourrait faire ça "finger in the noise", mais mon probleme c'est que ce tableau c'est dans VB que je le voudrais.
Alors si tu as deux minutes, je t'ai fais une petite DLL toute simple dans le quel il y a un tableau.
Et le but du jeu est de le transferer dans un tableau VB

Avant, j'avais un code qui disait de lire à la DLL les lignes les unes apres les autres et de les retourner.
Comme j'arrive a recuperer une STRING avec VB, il suffisait de faire une boucle en disant à la DLL de lire les lignes une par une, et de les recuperer en sortie de DLL pour les recharger dans un tablo VB

Exemple :

Public Declare Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, ByVal lpString1 As Any) As Long
Public Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As Any) As Long

Declare Function LisFichier Lib "c: \MaDLL.dll" (NumeroDeLigne As Integer) As Long

Dim AdresseRetour As Long, VariableDeRetourDeLaDll As String
Dim TabloVB(100) as String

Do

 
x = x + 1
 AdresseRetour = LisFichier(x)
 VariableDeRetourDeLaDll = Space(lstrlen(AdresseRetour))
 lstrcpy VariableDeRetourDeLaDll, AdresseRetour
 TabloVB(x) = VariableDeRetourDeLaDll

Loop Until Trim(VariableDeRetourDeLaDll)  =  ""

Ce code marche, mais il retourne les lignes du tablo de la DLL les unes apres les autres pour les deposer les unes apres les autres dans le tableau VB :-(
Je trouve ça un peu lourd, et de plus je suis obligé d'envoyer un numero de ligne à la DLL, alors que je veux le tableau entier.

Donc voila, le tableau est dans la DLL
Il faut que je puisse avoir une copie dans VB

Voici la DLL :

http://prodcons.free.fr/DllTablo.dll

La fonction dans la DLL qui charge le tableau est TabloDansLaDll sans aucun parametre à lui passer
En retour tu devrais avoir une adresse qui est celle du debut du tableau ou sont les strings

 Tablo(1) = "Coucou"
 Tablo(2) = "je suis un"
 Tablo(3) = "tableau"
 Tablo(4) = "créé et"
 Tablo(5) = "géré par"
 Tablo(6) = ...........
 Tablo(7) = ...........
 Tablo(8) = ...........
 Tablo(9) = ...........

Encore merci pour ton aide precieuse
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
16 mai 2009 à 19:31
Je viens de vérifier c'est bien en STDCALL que les fonctions sont appellées dans la DLL.
Il y a aussi moyen aussi de les appeller en CDECL mais ce n'est pas l'option par defaut ....

Je ne sais pas si c'est important pour le VB ou la suite de l'histoire ????
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
17 mai 2009 à 16:03
Bon j'ai un peu l'impression de faire un monogue et que plus personne y veut m'aider :-(

J'ai un membre du forum du language qui créé les DLL qui viens de drolement faire avancer l'affaire.
Il a trouvé la soultion que je cherchais.
Je la donne ici, peut etre que cela pourra dépanner quelqu'un qui en aura besoin

J'ai juste encore une question
Si par miracle quelqu'un connaissait une solution pour reperer la fin du tableau, afin que je puisse ne pas lire au dela dans la memoire, je lui serais reconnaissant, moi, mes enfants et les 10 generations suivantes :-))))

Private Declare Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, ByVal lpString1 As Any) As Long
Private Declare Function lstrlen Lib "kernel32.dll" Alias "lstrlenA" (ByVal lpString As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal size As Long)
Private Declare Function TabloDansLaDll Lib "DllTablo.dll" () As Long

Public Function StringDLL(Variable)
 Dim RetourAdresse As Long, RetourDll As String
 RetourAdresse = Variable
 RetourDll = Space$(lstrlen(RetourAdresse))
 lstrcpy RetourDll, RetourAdresse
 StringDLL = RetourDll
End Function

Private Sub Form_Load()
 
 Dim Tablo(0 To 20) As Long
 CopyMemory Tablo(0), ByVal TabloDansLaDll(), 80
 
 For i = 0 To 20
  Debug.Print StringDLL(Tablo(i))
 Next i

End Sub
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
17 mai 2009 à 16:53
Bon j'ai trouvé une bidouille pour que VB connaisse la longueur du tableau, et n'aille pas mettre son groin à des endroits ou y'a pas de truffes :-))
Maintenant comme je viens de le dire, si c'est moi qui trouve,.........c'est que c'est de la bidouille ... :-(

Si un des professionnels de ce forum pouvait me dire, si on peut ou ne peut pas faire mieux, je serais ravi d'avoir son avis éclairé, avant de me lancer dans cette méthode et l'appliquer à tous mes giciels
Merci d'avance de votre aide ;-)

Private Declare Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, ByVal lpString1 As Any) As Long
Private Declare Function lstrlen Lib "kernel32.dll" Alias "lstrlenA" (ByVal lpString As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal size As Long)
Private Declare Function TabloDansLaDll Lib "DllTablo.dll" () As Long

Public Function StringDLL(Variable)
 Dim RetourAdresse As Long, RetourDll As String
 RetourAdresse = Variable
 RetourDll = Space$(lstrlen(RetourAdresse))
 lstrcpy RetourDll, RetourAdresse
 StringDLL = RetourDll
End Function

Private Sub Form_Load()
  
 Dim Tablo() As Long, AdrLongTablo As Long, LongTablo As Integer
 CopyMemory AdrLongTablo, ByVal TabloDansLaDll(), 4
 LongTablo = StringDLL(AdrLongTablo) + 1
 ReDim Tablo(0 To LongTablo)
 CopyMemory Tablo(0), ByVal TabloDansLaDll(), LongTablo * 4
 
 For i = 1 To UBound(Tablo())
  'Debug.Print "Adresse : " + Str(Tablo(i))
  Debug.Print StringDLL(Tablo(i))
 Next i

End Sub
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
17 mai 2009 à 16:56
Ah excusez moi, j'ai oublié de remettre l'adresse de la DLL
http://prodcons.free.fr/DllTablo.dll
0
Rejoignez-nous