DLL en Delphi lue par VBA [Résolu]

Signaler
Messages postés
82
Date d'inscription
jeudi 5 avril 2007
Statut
Membre
Dernière intervention
4 mars 2008
-
Messages postés
82
Date d'inscription
jeudi 5 avril 2007
Statut
Membre
Dernière intervention
4 mars 2008
-
Bonjour,

J'ai écrit une DLL en delphi. J'ai crée une autre application en Delphi pour accéder les fonctions de la DLL. ça marche bien. Maintenant il faut que j'appelle les fonctions de la DLL du VBA sur un fichier Excel. J'i bien declaré la fonction. ça ne marche pas. Vous savez pour quoi?

Voilà la fonction de la DLL sur Delphi:

function NU:string; stdcall;
var
  Buffer : array[0..255] of char;
  BufferSize : DWORD;
begin
  BufferSize := sizeOf(Buffer);
  GetUserName(@buffer, BufferSize);
  Result:=Buffer;
end;


 exports
 NU;

Voilà ma declaration sur le VBA d'Excel:
Declare Function NU Lib "C:\PrjDllDelphi\PrjDllDelphi.dll" () As String

Merci,
Otavio Reis

34 réponses

Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
normal, il y avait un prob dans le code que j'avais tapé ici à la va vite mais la dll (avec code) du zip indiqué plus haut tourne impec avec VB/VBA.

Il fallait mettre:
BSTR __stdcall bnGetUsername()
{
char buff[264];
DWORD len = 206;
GetUserName(buff, &len);
if(!len) buff[0] = 0;
return SysAllocStringByteLen(buff, len);
}




ciao...
BruNews, MVP VC++
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
14
Avec la traduction en Delphi peut être ?
Testée sous Word.
=====================================
library VBATest;

uses
  windows, ActiveX;

function myGetUserName(): TBSTR; stdcall;
var
  buffer: array[0..255] of Char;
  size: Cardinal;
begin
  size:= SizeOf(buffer);
  GetUserName(buffer, size);
  if size = 0 then buffer[0]:= #0;
  Result:= SysAllocStringByteLen(buffer, size);
end;

exports
  myGetUserName;

begin

end.
=====================================

Private Declare Function myGetUserName Lib "VBATest.dll" () As String

Private Sub CommandButton1_Click()
MsgBox myGetUserName
End Sub

=====================================
[troll]

Au passage, un peu de 'semi interprété' désassemblé :

add esp, $fffffefc         ; Ceci n'est pas une allocation ^^
mov [esp],  100            ; Affectation de size
push esp                   ; On pousse l'adresse de size
lea eax, [esp+$08]         ; Calcul de l'adresse du buffer
push eax                   ; On pousse l'adresse du buffer
call GetUserName
cmp dword ptr [esp], 0     ; Comparaison entre size et 0
jnz myGetUserName + $23    ; On saute si différent de 0
mov byte ptr [esp+$04], 0  ; On met 0 à la première case
mov eax,[esp]              ; On met l'adresse de size dans eax
push eax                   ; On pousse l'adresse de size
call SysAllocStringByteLen
add esp, $104              ; dés(pas une allocation)
ret

/troll
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
Faut retourner une allocation mémoire obtenue par SysAllocString().

ciao...
BruNews, MVP VC++
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
Pour info vu que je ne connais pas Delphi:
var
  Buffer : array[0..255] of char;

ça déclare un tableau local à la fonction, non ?

Si oui, c'est sur la pile (retrait de ESP) et donc adresse absolument invalide en sortie de fonction.

ciao...
BruNews, MVP VC++
Messages postés
82
Date d'inscription
jeudi 5 avril 2007
Statut
Membre
Dernière intervention
4 mars 2008

Merci de la réponse... Mais cette DLL marche bien quand je l'appelle à partir des applications Delphi et C++. Elle ne marche pas seulement avec le VB.
C'est le pb était la pile, l'adresse serait invalide avec n'importe quel application, non? 
peut être qu'il y a un pb avec la déclaration ,non?

Otavio Reis
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
Le prob n'est pas de déclaration mais de logique.
Delphi et C connaissent les pointeurs, VB que nenni donc impossible de lui en passer une autre que celle d'un BSTR.

Faut revoir tes notions d'informatique, on ne donne pas en sortie de fonction une adresse d'une variable locale, son contenu sera écrasé à tout coup dans les prochaines instructions.

ciao...
BruNews, MVP VC++
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
Exemple:

BSTR __stdcall bnGetWinDir()
{
  char buff[260];
  DWORD len;
  len = GetWindowsDirectory(buff, 260);
  if(!len) buff[0] = 0;
  return SysAllocStringByteLen(buff, len);
}

ciao...
BruNews, MVP VC++
Messages postés
17286
Date d'inscription
mercredi 2 janvier 2002
Statut
Modérateur
Dernière intervention
23 décembre 2019
69
pas besoin d'une dll delphi pour faire un  GetUserName ^^

 Environ$("UserName") suffit :p

Renfield
Admin CodeS-SourceS- MVP Visual Basic
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
C'était surement motif de tests.

ciao...
BruNews, MVP VC++
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
14
Le principal souci dans les appels de dll classique, c'est les allocations. Si tu alloue une chaîne dans une dll en C, avec un malloc par exemple, et que tu essaye de libérer tout ça dans une appli en Delphi, tu risque de gros problème. En effet, le gestionnaire de mémoire de Delphi ne comprendrat pas du tout d'où sort le pointeur.

La ton tableau est effectivement alloué sur la pile : c'est joué avec le feu -> cela ne fonctionne que dans des cas particuliers.

Donc soit il faut allouer et libérer que d'un côté, soit il faut s'arranger pour que les deux côtés utilisent le même gestionnaire de mémoire (cf sharmem en Delphi par exemple, ou les Sys*String*).

Le plus simple dans ton cas est de suivre l'exemple du Win32, ou les allocations et libérations de ce genre de chaînes se font côté exe (Comme dans GetUserName par exemple ). Le pointeur sur la zone allouée et la taille de cette zone sont envoyés à la dll, qui remplit et vérifie que la zone est assez grande.

En VB, pour allouer un bout une chaîne d'une taille précise, soit on la remplie d'espaces :

dim s as string
s = space(20)

Soit on la déclare de taille fixe :

Dim s As String * 20
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
"alloué sur la pile" est déjà un abus de langage ce qui induit les incompréhensions. Il n'y a aucune allocation pour une variable locale mais un simple retrait du registre ESP, celui ci étant replacé en sortie de fonction il est tout à fait clair que ça ne peut jamais fonctionner puisque la zone de pile peut se faire réécrire à tout moment.

ciao...
BruNews, MVP VC++
Messages postés
82
Date d'inscription
jeudi 5 avril 2007
Statut
Membre
Dernière intervention
4 mars 2008

ça veut dire que si j'utilise une variable globale, ça va marcher?

Otavio Reis
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
Nenni, une 'String' vb DOIT être un BSTR car vb appelle SysFreeString() quand la String sort de la portée.

ciao...
BruNews, MVP VC++
Messages postés
82
Date d'inscription
jeudi 5 avril 2007
Statut
Membre
Dernière intervention
4 mars 2008

Mais alors je peut conclure que créer une DLL en utilisant des pointeurs et Strings ne marche pas sur VBA? Il y a-til une solution?

Otavio Reis
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
ah mais je n'y crois pas !!!
Je t'ai donné l'exemple prêt à l'emploi et c'est tout ce que tu trouves à conclure.

ciao...
BruNews, MVP VC++
Messages postés
82
Date d'inscription
jeudi 5 avril 2007
Statut
Membre
Dernière intervention
4 mars 2008

Ok, j'ai changé la DLL. J'alloue la mémoire. Je crois que tout est bon. MAis ça marche toujours pas sur le VBA. T'as une idée?

function NU: pchar; StdCall;
Var
  Buffer : array[0..255] of char;
  BufferSize : DWORD;



begin
  BufferSize := sizeOf(Buffer);
  GetUserName(@buffer, BufferSize);





  GetMem(Result, BufferSize);




  StrCopy(Result, @Buffer);
end;





 
Je ne suis pas experient dans ce domaine.
Merci de votre aide,
Otavio Reis
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
Tu vois un rapport avec ça ???
BSTR __stdcall bnGetUsername()
{
  char buff[264];
  DWORD len;
  len = GetUserName(buff, 260);
  buff[len] = 0;
  return SysAllocStringByteLen(buff, len);
}

moi aucun.
Il faut allouer une String avec une des fonctions de la famille SysAllocString() !!!

Laisse ton Delphi et fais en C, on sort la dll en 2.5 Ko. Si c'est pour fournir à un inbterprété du code dll en semi interprété, aucun intérêt, autant faire depuis VB.

ciao...
BruNews, MVP VC++
Messages postés
7668
Date d'inscription
samedi 5 novembre 2005
Statut
Membre
Dernière intervention
22 août 2014
26
Bonsoir, otavioreis,

Tu sais quoi  ?
Quand je relis ceci, signé Renfield :
 Environ$("UserName")
et que je vois ton acharnement à alourdir ainsi ton appli avec une fonction Delphi pour faire ce qui est déjà une fonction VB faisant parfaitement cette toute petite chose
j'ai vraiment envie de pleurer....

J'ose espérer que ta Dll écrite en Delphi n'utilise pas un composant écrit en VB faisant lui-même appel à une fonction écrite en C; qui se servirait d'une fonction d'une des librairies de l'API de Windows  !!! (Quien sabe ?)
Messages postés
82
Date d'inscription
jeudi 5 avril 2007
Statut
Membre
Dernière intervention
4 mars 2008

Brunews,

j'ai essayé ta fonction et le VC++ donne le message d'erreur suivant:

Erreur 1 error C2664: 'GetUserNameW' : cannot convert parameter 1 from 'char [260]' to 'LPWSTR' c:\Documents and Settings\virtual\Desktop\temporaire\DllNUC++\DllSimple\DllSimple.cpp 29 

Otavio Reis
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
26
Faut mettre ton projet en ANSI au lieu de UNICODE.

Utilise ceci pour avoir les bons réglages:
TEMPLATES C/C++ POUR VS 2005
http://www.cppfrance.com/code.aspx?ID=36940

ciao...
BruNews, MVP VC++