Le BSTR comment fonctionne t'il exactement ????

cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 - 20 sept. 2010 à 09:15
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 - 23 sept. 2010 à 12:45
Bonjour à tous,

Voila, cela fait plusieurs jours que j'essaie de comprendre le format de BSTR
Et il reste encore pas mal de vide dans mon esprit

J'ai compris qu'il etait constitué d'une "entete" de 4 octets comprenant la longueur de la string sur laquelle il pointe.

Mais comment fait on pour acceder à cette longueur, par quelle instruction ???

Lorque je fait :

Dim a As String
a = "AndreBernard"
MsgBox VarPtr(a)

J'obtiens le pointeur de la variable contenant l'adresse de la string, c'est bien ça ???
Mais pour obtenir le chiffre 12 qui est la longueur de cette variable...comment fait on ???

Merci de votre aide et bonne journée

33 réponses

Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
22 sept. 2010 à 22:32
hein ?

Renfield - Admin CodeS-SourceS - MVP Visual Basic & Spécialiste des RegExp
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
23 sept. 2010 à 09:05
Tu as raison....meme moi j'arrive plus a me comprendre
Ces histoires de pointeurs de pointeurs, de pointeurs de string, de pointeur de structure....

Ce que je voulais dire, c'est que je créé la nouvelle structure et l'envoie a la DLL
Dim Newdata As myStruct
 mGetData Newdata


Dans la DLL ce que je reçois c'est la meme chose que pour la fonction SomeData, c'est un pointeur sur la structure
 Dim Somedata As myStruct
 Somedata.Foo = 10
 Somedata.Bar = 8
 Somedata.Str = "Hello"
 mSetData Somedata


Dans le langage de la DLL ça s'ecrit mGetData(*myData.myStruct)
Apres, j'ai attribué les long, comme tu me l'a dit, puisque on y touche pas
*mydata\bar = mydata\bar
*mydata\foo = mydata\foo
j'ai libéré le pointeur de la str reçu comme tu me l'as dit aussi
SysFreeString_(*myData\str)
Jusqu'a la...je sais pas si ça a marché, mais bon je crois y etre arrivé

Mais c'est la que les atheniens s'atteignirent
J'ai essayé de "realloquer" (Joli nouveau verbe pour le dico de 2011 ) ma nouvelle string bien plus longue
Temp = SysAllocString_("Je suis une burne, mais j'me soigne")
Puis c'est apres que je ne sais pas quoi faire de ce TEMP

Premiere question
Faut il ecrire en mémoire à l'adresse de *myData\str ???

Seconde question
J'ai lu un code dans le langage de la DLL, et apparement SysAllocString ne fonctionne qu'en UNICODE, est-ce vrai ??? car cela m'obligerait a compiler ma DLL en UNICODE, je peux le faire, mais je ne le peut pas pour des raisons techniques.
0
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
23 sept. 2010 à 09:12
ok...
je dirai fournir à *myData\str le pointeur vers le BSTR, comme fait par VB...
et eviter que la dll ne fasse le ménage toute seule, si besoin, c'est a VB de faire le future SysFreeString

Concernant ton histoire d'Unicode, je n'en sais rien...

Renfield - Admin CodeS-SourceS - MVP Visual Basic & Spécialiste des RegExp
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
23 sept. 2010 à 09:27
Bon pour essayer de comprendre toutes les possibilités d'envoi de VB, existe t'il un tableau qui donne le format de l'envoi de VB a une API

Exemple:

[b]ByRef + Structure = Pointeur de la string en ANSI + LPSTR
ByRef + Variant = Pointeur de la string en ANSI + LPSTR
Byref + Long = Pointeur de la variable en Long
Byref + String = Pointeur de la string en UNICODE + BSTR
Byval + Structure = ????
ByRef + Variant = ???
Byref + String = ???
Etc .../b

Car avec toutes ces possibilités, je comprend jamais ce que j'envoie ou je reçois
0

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

Posez votre question
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
23 sept. 2010 à 09:34
Pas a ma connaissance...

faut bien voir la pile, de toute facon, puisque les appels APIs se font en __stdcall

du coup, on ne push toujours que des Long...

Byref + Long => pointeur vers le Long
Byval + Long => Long tel quel
Byval + String => Special case, allocation char* avec chaine Unicode convertie en ascii. transmission du pointeur vers debut du tableau
Byref + String => Crash assuré (la plupart du temps)
Byval + Struct => impossible
Byref + ... => Pointeur vers ...


Du coup, ca me fait me dire que sinon joue avec une structure, on risque de se retrouvé embetté avec cette conversion de String...

Je ferai des essais dans l'après midi, en C...
me permettra de te répondre avec du concrêt

Renfield - Admin CodeS-SourceS - MVP Visual Basic & Spécialiste des RegExp
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
23 sept. 2010 à 09:53
Merci infiniment RENFIELD !!!

En fait si tu arrivais, ce que je doute pas d'ailleur, a envoyer une structure complexe, a une DLL en C, la modifier dans la DLL, a loisir, string plus grande, plus petite, etc..
Et que tu retournes dans VB la structure modifiée, et que tu puisse partager les deux sources, cela finirait mon calvaire.
Car en fait la DLL est dans un langage BASIC mais tres proche du C au niveau comportement.
Donc il me suffirait d'essayer de convertir le code C, ou bien de demander aux copains du langage BASIC de le faire pour moi et comme ça, j'aurais fini de te casser les noisettes, avec cette histoire de passage entre porte et fenetres

Si j'abusais de ta bonté....pendant que tu y est, pourrais tu aussi passer un tableau de string, un tableau d'integer, et meme une string simple, les modifier comme pour la structure et les retourner.

En un seul code, tu pourrais soulager ma vie qui est devenu un enfer depuis 5 ans que j'essaie de faire converser VB et ce basic
Car j'aurais toutes les possibilités de variables qui se transmettent de l'un a l'autre

Dans ce BASIC, y'en a d'autres, mais heureusement pour moi, VB ne les gere pas...enfin a ma connaissance
Les MAP (C'est des tableaux mais qui s'appellent par une string au lieu d'un integer)
Le LIST (C'est un style de mini base de données, tu fait des NEXT, des INSERT, etc)

Voila...je m'excuse d'abuser de ta patience, mais je demande de partout et peu de gens peuvent aider sur deux langages à la fois, j'ai le cul entre deux chaises, comme le dit le langage populaire.
Avoir une base de C de ce que je desire faire, m'aiderais surement, j'y comprendrais rien, mais pleins de gentils membres sur d'autres forums comprennent ....eux
0
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
23 sept. 2010 à 10:25
on verra bien si ca fonctionne...
pour info, en VB, les tableaux, ce sont des pointeurs de SafeArray

Renfield - Admin CodeS-SourceS - MVP Visual Basic & Spécialiste des RegExp
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
23 sept. 2010 à 10:51
pour info, en VB, les tableaux, ce sont des pointeurs de SafeArray

Et ben y manquait plus que ça
Mais pourquoi VB fait jamais rien comme les autres
Apparement on a pas le choix ????

Alooooorrrs !!!!
Regardons tout ce que j'ai dans ma besace et dont j'y comprend fichtre rien ou presque

Les BSTR
L'UNICODE
Les BYVAL
Les BYREF
VarPtr()
StrPtr()

Et ouuuuaaaiihhh !!!
Un petit nouveau
Le SafeArray

Dans mon basic, un copain viens juste de pondre un code sur ça...
J'ai pas tout compris comme d'habitude, c'est la que j'ai su que SysAllocString etait en UNICODE

Je ne sais pas si ça peut t'aider, bien que je pense pas que tu en est vraiment besoin vu ton niveau, mais bon des fois une autre approche ...
;Begin with an array of SAFEARRAYBOUND structures; one for each dimension.
    Dim safeArrayBound.SAFEARRAYBOUND(1)
      With safeArrayBound(0)
        \lLbound = 1
        \cElements = 20
      EndWith
      With safeArrayBound(1)
        \lLbound = 1
        \cElements = 20
      EndWith
  ;Now create the array and check for success.
    *safeArray.SAFEARRAY = SafeArrayCreate_(#VT_BSTR, 2, @safeArrayBound())
    If *safeArray
      ;Populate the array.
        Dim indices(1)
        For i = 1 To 20
          For j = 1 To 20
            indices(0) = i
            indices(1) = j
            temp = SysAllocString_("(" + Str(i) + ", " + Str(j) + ")")
            SafeArrayPutElement_(*safeArray, @indices(), temp)
            SysFreeString_(temp)
          Next
        Next
    EndIf



As for SafeArrayAccessData_(), you don't need it because all it does is return a pointer to the pvData field of the SAFEARRAY structure. Use the following instead (again in Unicode mode) :

*ptrString.string = *safearray\pvData
For i = 1 To 400
  Debug *ptrString\s
  *ptrString + SizeOf(STRING)
Next


Je lui ai posé la question de savoir si on pouvait eviter l'UNICODE dans la DLL
A ça il a repondu :
SysAllocString_() should not be used for Ansi strings. It may work, but in such cases you are just lucky as it depends on the memory bytes immediately following your string. For Ansi use the appropriate API function.


Je lui ai demander quelle etait ces soi-disantes API appropriées
SysAllocStringByteLen_().

If you set up a prototype using a p-Unicode pseudotype then you can quickly create Unicode BSTRs and then you can use SysAllocString_() for all strings. I do this in COMate. You then have to use #PB_Unicode when retrieving the strings mind.

Apres autant dire que pour la deuxieme phrase, meme sous la torture je ne dit pas autant d'insanités

En fin de compte seul les gens qui maitrisent VB, peuvent faire quelque chose pour moi, car les maitres du C sont completement perdus devant autant de comportements, differents de leur langage.
VB est un langage simple quand on reste dans les choses simples, apres il deviens compliqué quand on sort des sentiers battus.
Pour l'autre basic, c'est tout l'inverse, j'en ai bavé pour faire juste une fenetre, car comme il est bas niveau et que rien n'est fait automatiquement faut tout te taper, meme la gestion evenementielle.
Donc tu regrette VB a ce moment et tu es mort de rire quand tu vois tout ce qui faut faire pour ouvrir une simple fenetre et dire bonjour
Puis a la longue la tendance s'inverse, les choses compliquées deviennent simples (Enfin pas trop pour moi, car pour moi tout est compliqué....meme ouvrir l'IDE ou le RAD ) mais meme un peu avec moi, ce qui est compliqué en VB est "enfantin" pour ce BASIC.
C'est pour cette raison que l'association des deux est vraiment pour moi, en tout cas, une super idée et ouvre des perspectives infinies
0
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
23 sept. 2010 à 11:54
ok, donc, en temps reel...

Pas de struct pour le moment, ni de tableau, juste des chaines de caractères (BSTR)

char szReturn[MAX_PATH];

__declspec(dllexport) BSTR __stdcall RetrieveTempFilename(char* szDirectory, char* szPrefix)
{
     char szBuffer[MAX_PATH];
     if(GetTempFileName(szDirectory,szPrefix,0,szBuffer) == 0) 
          lstrcpy(szReturn,"ERROR");
     else
          lstrcpy(szReturn,szBuffer);
     return SysAllocString((OLECHAR*)szReturn);
}

__declspec(dllexport) void __stdcall RetrieveTempFilenameBYREF(char* szDirectory, char* szPrefix, BSTR* Res)
{
     char szBuffer[MAX_PATH];
     if(GetTempFileName(szDirectory,szPrefix,0,szBuffer) == 0) 
          lstrcpy(szReturn,"ERROR");
     else
          lstrcpy(szReturn,szBuffer);
 SysFreeString(*Res);
     *Res = SysAllocString((OLECHAR*)szReturn);
}

__declspec(dllexport) void __stdcall AlterString(BSTR* Res)
{
     lstrcat((char*)*Res, "Modified\0");
}

__declspec(dllexport) void __stdcall SmallString(BSTR* Res)
{
     lstrcpy((char*)*Res, "small");
}

__declspec(dllexport) void __stdcall LongString(BSTR* Res)
{
     lstrcpy((char*)*Res, "long string that will be cut...");
}

__declspec(dllexport) void __stdcall LongString_S(BSTR* Res)
{
     *Res = SysAllocString((OLECHAR*)"long string that will be cut...");
}


Private Declare Function RetrieveTempFilename Lib "E:\Developpement\TestDll\Debug\Testdll.dll" (ByVal szDirectory As String, ByVal szPrefix As String) As String
Private Declare Sub RetrieveTempFilenameBYREF Lib "E:\Developpement\TestDll\Debug\Testdll.dll" (ByVal szDirectory As String, ByVal szPrefix As String, ByRef Result As String)
Private Declare Sub AlterString Lib "E:\Developpement\TestDll\Debug\Testdll.dll" (ByRef Result As String)
Private Declare Sub SmallString Lib "E:\Developpement\TestDll\Debug\Testdll.dll" (ByRef Result As String)
Private Declare Sub LongString Lib "E:\Developpement\TestDll\Debug\Testdll.dll" (ByRef Result As String)
Private Declare Sub LongString_S Lib "E:\Developpement\TestDll\Debug\Testdll.dll" (ByRef Result As String)

Private Sub Form_Load()
Dim sResult As String
    MsgBox RetrieveTempFilename(vbNullString, vbNullString), , "Return BSTR"
    RetrieveTempFilenameBYREF vbNullString, vbNullString, sResult
    MsgBox sResult, , "ByRef BSTR"
    
    sResult = Space$(10)
    SmallString sResult
    MsgBox sResult, , "SmallString => 'small'"
    
    LongString sResult
    MsgBox sResult, , "LongString Unsafe => 'long st[?????]'"
    
    LongString_S sResult
    MsgBox sResult, , "LongString_S : String will be reallocated large enough"
End Sub


ma dll est en ASCII, pas de souci pourtant avec SysAllocString ^^

Renfield - Admin CodeS-SourceS - MVP Visual Basic & Spécialiste des RegExp
0
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
23 sept. 2010 à 12:04
nb: gaffe a mettre des SysFreeString ou il faut, quand Res est un BSTR existant, quand tu fais des SysAllocString, pas comme j'ai pu le faire dans mon code (trop) vite fait

pas de souci avec les Structures :

struct Data {
    int Val;
    BSTR Str;
};

__declspec(dllexport) void __stdcall GetData(Data* vtData)
{
    vtData->Val = 3112;
if (vtData->Str)
SysFreeString(vtData->Str);
vtData->Str= SysAllocString((OLECHAR*)"Hello world from Struct...");
}


Private Declare Sub GetData Lib "E:\Developpement\TestDll\Debug\Testdll.dll" (ByRef Data As Data)

Private Type Data
    Val As Long
    Str As String
End Type

Private Sub Form_Load()
Dim tData As Data
    GetData tData
    MsgBox tData.Val & vbNewLine & tData.Str
End Sub




Renfield - Admin CodeS-SourceS - MVP Visual Basic & Spécialiste des RegExp
0
cs_andrebernard Messages postés 404 Date d'inscription lundi 9 juin 2003 Statut Membre Dernière intervention 4 septembre 2013 1
23 sept. 2010 à 12:39
Et ben dis donc....t'es une usine a code
Je te remercie beaucoup de toute cette peine que tu donne pour moi

Bon comme tu le dis ....en temps reel...enfin plutot "pas a pas" pour moi

J'ai voulu creer la DLL en VC6++....et comme je te l'ai dit ...je ne sais meme pas ouvrir le RAD

J'ai donc cliqué sur le beau raccourci VC++ dont je reve de cliquer dessus au moins depuis 10 ans (Jusqu'a la ça mange pas de pain)
J'ai fait nouveau projet / Dynamic library
La, il a déja commencé a etre grossier, en me demandant si je la voulais vide, moitié vide, et meme pleine avec des retours...j'ai meme cru qu'on parlait de bouteilles ....

La premiere fois, j'ai repondu vide et collé ton code, et c'est encore plus fort qu'en VB, "15 error" et pas moyen de compiler
J'ai donc recommencé a moitié plein, et cette fois je peux compiler, mais mes erreus elles m'ont suivi

--------------------Configuration: TestDll - Win32 Debug--------------------
Compiling...
StdAfx.cpp
Compiling...
TestDll.cpp
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(17) : error C2143: syntax error : missing ';' before '__stdcall'
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(17) : error C2501: 'BSTR' : missing storage-class or type specifiers
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(24) : error C2065: 'SysAllocString' : undeclared identifier
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(24) : error C2065: 'OLECHAR' : undeclared identifier
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(24) : error C2059: syntax error : ')'
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(25) : warning C4508: 'RetrieveTempFilename' : function should return a value; 'void' return type assumed
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(27) : error C2061: syntax error : identifier 'BSTR'
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(34) : error C2065: 'SysFreeString' : undeclared identifier
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(34) : error C2065: 'Res' : undeclared identifier
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(34) : error C2100: illegal indirection
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(35) : error C2100: illegal indirection
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(35) : error C2059: syntax error : ')'
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(39) : error C2448: '' : function-style initializer appears to be a function definition
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(44) : error C2448: '' : function-style initializer appears to be a function definition
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(49) : error C2448: '' : function-style initializer appears to be a function definition
C:\RetourStringC(Renfield)\TestDll\TestDll.cpp(54) : error C2448: '' : function-style initializer appears to be a function definition
Error executing cl.exe.

TestDll.dll - 15 error(s), 1 warning(s)


Alors comme tu peux le voir..dire que je part de zero avec C...est un doux euphemisme
0
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
23 sept. 2010 à 12:44
prend vide
ce n'est rien, ces messages, suffit d'ajouter le include.
voir le code actuel, incluant un tableau de String ^^

#include <windows.h>

char szReturn[MAX_PATH];

struct Data {
    int Val;
    BSTR Str;
};

__declspec(dllexport) BSTR __stdcall RetrieveTempFilename(char* szDirectory, char* szPrefix)
{
     char szBuffer[MAX_PATH];
     if(GetTempFileName(szDirectory,szPrefix,0,szBuffer) == 0) 
          lstrcpy(szReturn,"ERROR");
     else
          lstrcpy(szReturn,szBuffer);
     return SysAllocString((OLECHAR*)szReturn);
}

__declspec(dllexport) void __stdcall RetrieveTempFilenameBYREF(char* szDirectory, char* szPrefix, BSTR* Res)
{
     char szBuffer[MAX_PATH];
     if(GetTempFileName(szDirectory,szPrefix,0,szBuffer) == 0) 
          lstrcpy(szReturn,"ERROR");
     else
          lstrcpy(szReturn,szBuffer);
 if (*Res)
SysFreeString(*Res);
     *Res = SysAllocString((OLECHAR*)szReturn);
}

__declspec(dllexport) void __stdcall AlterString(BSTR* Res)
{
     lstrcat((char*)*Res, "Modified\0");
}

__declspec(dllexport) void __stdcall SmallString(BSTR* Res)
{
     lstrcpy((char*)*Res, "small");
}

__declspec(dllexport) void __stdcall LongString(BSTR* Res)
{
     lstrcpy((char*)*Res, "long string that will be cut...");
}

__declspec(dllexport) void __stdcall LongString_S(BSTR* Res)
{
if (*Res)
SysFreeString(*Res);
     *Res = SysAllocString((OLECHAR*)"long string that will be cut...");
}

__declspec(dllexport) void __stdcall GetData(Data* vtData)
{
    vtData->Val = 3112;
if (vtData->Str)
SysFreeString(vtData->Str);
vtData->Str= SysAllocString((OLECHAR*)"Hello world from Struct...");
}

__declspec(dllexport) long __stdcall SolarSystem(SAFEARRAY** vtData)
{
SAFEARRAYBOUND tDim;
char sPlanet[10];

if (*vtData)
SafeArrayDestroy(*vtData);

tDim.lLbound = 0;
tDim.cElements = 9;
*vtData = SafeArrayCreate(VT_BSTR, 1, &tDim);

for(LONG i = 0; i<9; i++) {
switch(i) {
case 0:
lstrcpy(sPlanet, "Mercure");
break;
case 1: 
lstrcpy(sPlanet, "Venus");
break;
case 2: 
lstrcpy(sPlanet, "Terre");
break;
case 3: 
lstrcpy(sPlanet, "Mars");
break;
case 4: 
lstrcpy(sPlanet, "Jupiter");
break;
case 5: 
lstrcpy(sPlanet, "Saturne");
break;
case 6: 
lstrcpy(sPlanet, "Uranus");
break;
case 7: 
lstrcpy(sPlanet, "Neptune");
break;
case 8: 
lstrcpy(sPlanet, "Pluton");
break;
};
SafeArrayPutElement(*vtData, &i, SysAllocString((OLECHAR*)sPlanet));
}
return 9;
}



pour tester les tableaux:
Private Declare Function SolarSystem Lib "E:\Developpement\TestDll\Debug\Testdll.dll" (ByRef Data() As String) As Long

Private Sub Form_Load()
Dim xsPlanets() As String
Dim i As Long
    For i = 0 To SolarSystem(xsPlanets) - 1
        MsgBox xsPlanets(i)
    Next
End Sub


dans ton projet Cpp, faudra ajouter le /DEF:
LIBRARY TestDll
EXPORTS
     RetrieveTempFilename
     RetrieveTempFilenameBYREF
     AlterString
     SmallString
     LongString
     GetData
     SolarSystem
     


Et le référencer dans les propriétés de link du projet.
Selon la version de ton RAD, faudra aussi surement desactiver l'Unicode

Renfield - Admin CodeS-SourceS - MVP Visual Basic & Spécialiste des RegExp
0
Renfield Messages postés 17287 Date d'inscription mercredi 2 janvier 2002 Statut Modérateur Dernière intervention 27 septembre 2021 74
23 sept. 2010 à 12:45
NB: Je vais arreter la pour le moment... si tu veux le projet cpp par mail, envoies moi ton adresse par MP

apres, ce n'est que combinaison de tout ce code.

Renfield - Admin CodeS-SourceS - MVP Visual Basic & Spécialiste des RegExp
0
Rejoignez-nous