rudimath
Messages postés16Date d'inscriptionjeudi 7 octobre 2004StatutMembreDernière intervention20 août 2008
-
9 mai 2007 à 18:36
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 2014
-
14 mai 2007 à 08:38
Bonjour, j'ai un problème pour faire une macro dans Word 2000.
J'appelle une fonction d'une dll , et je reçoit le message : "Convention d'appel de dll incorrecte"
Voila le code:
Private Declare Function CreateGifFromEq Lib "MimeTex.dll" (ByVal expr As String,
ByVal fileName As String) As Integer
Sub toto()
Dim Chaine As String
Chaine = "2+3"
Dim pathimage As String
pathimage = "C:\DOCUME~1\LOCALS~1\Temp\Convertisseur.gif"
Dim nn As Integer
nn = CreateGifFromEq(Chaine, pathimage)
End Sub
J'ignore dans quel langage a été créée la dll, mais les fonctions d'appel suivantes marchent :
en Delphi:
function CreateGifFromEq(expr, fileName: PAnsiChar): Integer; cdecl;
external 'MimeTex.dll';
en c#
[System.Runtime.InteropServices.DllImport("MimeTex.dll")]
internal static extern int CreateGifFromEq(string expr, string fileName);
Merci de m'aider parce que je ne suis pas très à l'aide avec VBA
Rudi
cs_Jack
Messages postés14006Date d'inscriptionsamedi 29 décembre 2001StatutModérateurDernière intervention28 août 201579 9 mai 2007 à 19:16
Salut
En entrant la phrase de ton erreur dans un moteur de recherche, je suis tombé direct sur une page MSDN qui explique les raisons potentielles. As-tu essayé ? --> http://msdn2.microsoft.com/fr-fr/library/stw3skyb(VS.80).aspx Attention aussi :
- En C comme en .Net, les Integer sont des Long en VB6
- Quelque fois, les chaines doivent être finalisées par un Chr$(0), exemple
pathimage = "C:\DOCUME~1\LOCALS~1\Temp\Convertisseur.gif" & Chr$(0)
Sinon, cette DLL n'étant pas un élément standard, adresse toi à son créateur, il aura peut-être des exemples à te soumettre.
Vala
Jack, MVP VB
NB : Je ne répondrai pas aux messages privés
Champion du monde de boule de cristal - 2005 Le savoir est la seule matière qui s'accroit quand on la partage (Socrate)
BruNews
Messages postés21040Date d'inscriptionjeudi 23 janvier 2003StatutModérateurDernière intervention21 août 2019 9 mai 2007 à 22:57
Pour le retour, m'étonnerait aussi que la DLL retourn un 16 bits, mettre donc As Long.
Ceci dit la DLL est absolument inutilisable depuis VB6 ou précédent car ne peut utiliser que le standard stdcall et non cdecl, depuis .NET aucune idée.
Dans tous les cas, une DLL qui n'exporte pas en stdcall est à bannir, ce n'est pas le signe d'un travail pro.
rudimath
Messages postés16Date d'inscriptionjeudi 7 octobre 2004StatutMembreDernière intervention20 août 2008 9 mai 2007 à 23:27
La dll en question transforme le string expr en image qu'elle met dans pathimage. Donc dans mon exemple, elle doit convertir chaine="2+3" dans une image appelée convertisseur.gif dans le dossier C:\DOCUME~1\LOCALS~1\Temp\.
Or c'est bien se qui se passe: j'ai bien une belle image avec 2+3 dans TEMP. Seulement j ai ce message d'erreur "Convention d'appel de dll incorrecte" qui m'empeche de continuer.
Suivant vos conseils, j'ai changé integer en long, mais ça ne change rien. Pourtant ça me semble bien etre un problème de typage. Qu'est ce que devrait renvoyer ma fonction, si ce n'est pas integer ou long ?
En plus, le résultat de la fonction, cet entier, ne m'est d'aucune utilité. C'est l'image qui m'interesse. Y aurait-il un moyen pour intercepter le message d'erreur et pouvoir continuer ?
En tout cas merci pour vos idées Ca fait avancer.
Vous n’avez pas trouvé la réponse que vous recherchez ?
BruNews
Messages postés21040Date d'inscriptionjeudi 23 janvier 2003StatutModérateurDernière intervention21 août 2019 9 mai 2007 à 23:32
Ce n'est pas du tout une idée ce que j'ai dit plus haut, c'est une certitude. Cette DLL est inemployable depuis VBA sauf à lui faire un autre wrapper C exportant en stdcall et qui fera l'appel en cdecl vers la DLL.
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 201413 10 mai 2007 à 13:44
Salut,
Confirmation, c'est bien du As Long, vu que c'est du :Integer pour la déclaration Delphi que tu nous as donné. (32 bits non signés quoi)
Le souci entre cdecl et stdcall, c'est apparement surtout que le
nettoyage de la pile se fait dans l'appelant pour cdecl et dans
l'appelé pour stdcall...
Pour l'idée de BruNews, vala le code en Delphi. Non testé : je n'ai pas Delphi sur ce poste.
============================
library MimeTexWrapper;
function CreateGifFromEq(expr, fileName: PAnsiChar): Integer; cdecl; external 'MimeTex.dll';
function CreateGifFromEq2(expr, fileName: PAnsiChar): Integer; sdtcall;
begin
Result:= CreateGifFromEq(expr, fileName);
end;
exports
CreateGifFromEq2;
begin
end.
============================
Private Declare Function CreateGifFromEq2 Lib "MimeTexWrapper.dll" (ByVal expr As String, ByVal fileName As String) As Long
rudimath
Messages postés16Date d'inscriptionjeudi 7 octobre 2004StatutMembreDernière intervention20 août 2008 10 mai 2007 à 20:01
Je ne crois pas que ce soit cdecl le problème. C'était juste une façon plus rapide en écriture pour appeler la fonction. J'ai reécrit le code de façon plus classique en delphi :
Convert:=LoadLibrary('MimeTex.dll');
@CreateGifFromEq:=GetProcAddress(Convert,'CreateGifFromEq');
if @ CreateGifFromEq=nil then raise Exception.Create('La fonction CreateGifFromEq est introuvable');
Ca marche aussi. La dll semble donc hors decause. J'en reste donc sur l'idée d'une erreur de typage. Pour contourner ce problème j'ai essayé de gérer l'erreur en VBA :
Private Declare Function CreateGifFromEq Lib "MimeTex.dll" (ByVal expr As String, ByVal fileName As String) As Long
Dim n As Long
On Error GoTo erreur
n = CreateGifFromEq(Chaine, pathimage)
erreur:
Je ne sais pas si c'est très correct, mais ça semble marcher, vu que la fonction crée bien une image dans le fichier Temp et que le programme ne s'arrete pas.
Maintenant, est il préférable de faire une autre librairie ...(merci pour le code rt15) ?
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 201413 11 mai 2007 à 11:11
En résumer :
Lors de l'appel d'une routine (cas général), l'appelant prépare les paramètres de demande au processeur d'executer les instruction de la routine en question.
Les paramètres peuvent être passé sur la pile, via les registres, ou par les deux méthodes. Pour stdcall et cdecl, les paramètres sont passés par la pile, empilés de droite à gauche. Le résultat de la fonction est renvoyé dans eax (Ou eax:edx et fp0) dans les deux cas.
Cependant comme on empile les paramètres, il faut bien les dépiler un jour. Dans le cas de stdcall, les paramètres sont dépilés par l'appelés, et dans le cas d'un cdecl, les paramètres sont dépilés par l'appelante.
Donc dans ton cas, la routine de la dll ne dépile pas les paramètres car elle est en cdecl, et vba pense ne pas avoir à le faire, vu que tu lui a déclaré implicitement que la routine de la dll est en stdcall.
Bilan, la pile est décalée, et je suppose que c'est ce décalage qui est détecté APRES l'appel. (VBA doit comparer les valeurs de esp avant l'appel et après)
C'est à mon avis un banal add esp, 8 qu'il te manque...
Je sais pas comment VBA traite l'erreur, mais c'est généralement risqué de continuer l'execution après un décalage dans la pile...
rudimath
Messages postés16Date d'inscriptionjeudi 7 octobre 2004StatutMembreDernière intervention20 août 2008 11 mai 2007 à 18:04
Avec l'interception du message d'erreur, le programme se plante effectivement assez souvent. J'ai donc créé la dll mimetexwrapper.
Il n'y a plus de problème d'appel pour la fonction avec le type long. Merci rt15.
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 201413 14 mai 2007 à 08:38
Moi j'aurais dit la troisième...
Le fait de mettre Integer comme type de retour dans VBA fait que le script récupère les 16 bits de poids faible de eax (ax pour les intimes) au lieu des 32. Donc on récupère un entier partiellement, mais cela ne peut pas provoquer directement une erreur de convention d'appel. Il aurait même pu mettre As Byte que vba n'y aurait vu que du feux. Nan, le souci, c'est purement un problème de mésentante sur le travail à effectuer côté script et côté dll.
Pour le prouver, on peut faire plus intelligent que le code que j'ai proposé.
C'est en effet une peu dommage d'empiler deux fois les paramètres quand on a besoin d'un simple add esp, 8.
Pourquoi add esp, 8 ?
esp est le pointeur sur la tête de la pile. Pour nettoyer les paramètres, il est donc logique de modifier sa valeur pour déplacer la tête de la pile. Il semblerait plus logique de diminuer esp, mais non, la pile croie suivant les adresses décroissantes (Pour d'antiques raisons de meilleur exploitation de l'espace mémoire.) Pourquoi 8 ? Car les paramètres de ta fonction sont des PAnsiChar en Delphi, donc des pointeurs sur des chaines de caractères. Les pointeurs faisant 4 octets, 2 * 4 -> 8.
On va donc demander à Delphi de ne pas s'occuper de la convention d'appel en rédigeant une routine avec un unique bloque asm .. end;
En ne mettant pas de paramètres, ni de variables locales, on signal au compilo que l'on ne souhaite pas qu'il fasse de traitement au niveau de ebp.
===============================================
library Project2;
function RoutineCDecl(Titre: PAnsiChar; Texte: PAnsiChar): Integer; cdecl; external 'project3.dll';
Delphi assemble la routine comme ceci, avec juste un ret en plus de ce qu'on lui a explicitement demandé :
===============================================
1 pop dword ptr [RetAdr]
2 call RoutineCDecl
3 add esp, $08
4 push dword ptr [RetAdr]
5 ret
===============================================
1 -> La call de VBA a empiler l'adresse de retour. On la sauvegarde dans une variable globale.
2 -> On appel la routine : notre propre adresse de retour est empilée en lieu et place de l'ancienne.
3 -> On nettoie les argument que la cdecl n'a pas nettoyé.
4 -> On remet en place l'adresse de retour dans VBA.
5 -> ret va dépiler l'adresse de retour et la mettre dans eip : le code reprend dans vba normallement, et sans message d'erreur !
J'ai utiliser ce code pour tester la dll :
===============================================
Private Declare Function Wrapper Lib "project2.dll" (ByVal Titre As String, ByVal Texte As String) As Long
Private Sub CommandButton1_Click()
MsgBox Wrapper("titre", "texte")
End Sub
===============================================
Et celui-ci pour faire une dll en cdecl :
===============================================
library Project3;
uses
Windows;
function RoutineCDecl(Titre: PAnsiChar; Texte: PAnsiChar): Integer; cdecl;
begin
MessageBox(0, Titre, Texte, MB_OK);
Result:= 12;
end;