Fournir les fonctions CALLBACK autres que stdcall
<!--[if !supportEmptyParas * --> <!--[endif]-->
<!--[if !supportEmptyParas * --> <!--[endif]-->
Le problème est différent des appels de pointeurs defonction. En effet, on n’a pas besoin d’objet mais simplement d’une structurequi contiendra du code ASM et un pointeur lpfn vers ce code. Il faut transformerun appel cdecl, fastcall, thiscall, (voire pascal) en stdcall de fonction demodule BAS.
<!--[if !supportEmptyParas * --> <!--[endif]-->
Nous ne traiterons ici que le casde fonction renvoyant des valeurs tenant dans les registre EAX et EDX.Sinon, il faudra tenir compte du fait que le premier paramètre est un pointeurvers une zone mémoire.
<!--[if !supportEmptyParas * --> <!--[endif]-->
= =Transformer en appel stdcall un appel…==<!--[if !supportEmptyParas * --> <!--[endif]-->
Dans tous les cas, on passera le pointeur lpfn commepointeur de fonction callback à la fonction demandeuse et on passera auxfonctions de constructions de fonction callback, le pointeur AddressOf de lafonction VB.
<!--[if !supportEmptyParas * --> <!--[endif]-->
Le code aura la structuration suivante :
<!--[if !supportEmptyParas * --> <!--[endif]-->
Public TypeCallback
lpfn As Long
Code(0 To <taille nécessaire> ) As Long
End Type
<!--[if !supportEmptyParas * --> <!--[endif]-->
Public SubInitCallback(ByRef lpCB As Callback, ByVal lpfnCallback As Long, )
WithlpCDeclCB
'on remplit avec le code fixe et les paramètres dépendantde la fonction
.Code(0) =&HXXXXXXXX
‘……
.Code(n)= &HXXXXXXXX
.lpfn =VarPtr(.Code(0))
End With
End Sub
<!--[if !supportEmptyParas * --> <!--[endif]-->
== Cdecl==<!--[if !supportEmptyParas * --> <!--[endif]-->
Le problème est que la fonction appelée ne doit passupprimer les paramètres de la pile. Hors VB supprime toujours les paramètresde la pile à la fin des fonctions (stdcall). Il faudra donc dupliquer lesparamètres pour laisser la pile dans son état normal.
<!--[if !supportEmptyParas * --> <!--[endif]-->
Il faut donc :
<!--[if !supportEmptyParas * --> <!--[endif]-->
On aura donc la pile suivante avant l’appel à la fonctioncallback VB :
<!--[if !supportEmptyParas * --> <!--[endif]-->
Paramètre-n
…
Paramètre-2
Paramètre-1
Paramètre-n
…
Paramètre-2
Paramètre-1
ReturnAddress
<!--[if !supportEmptyParas * --> <!--[endif]-->
En prenant en compte le fait qu’il faut utiliser lesregistres EDI et ESI et donc les sauvegarder, cela donne le code ASM suivant :
mov ecx, 0x12345678 //on copie lataille des donnée dans ECX :
//0x12345678 sera remplacé par la taille des paramètres
add ecx, 4 //on ajoute 4 pour l'adresse de retour
<!--[if !supportEmptyParas * --> <!--[endif]-->
lea eax,[esp - 4 * //on charge l'adresse de la pile - 4 pour EDI - 4
//pour ESI
subeax,ecx //onsoustrait la taille des paramètres à
//l'adresse nouvelle
<!--[if !supportEmptyParas * --> <!--[endif]-->
test [eax * ,ecx //on vérifie que la page est chargée avant de
//copier au cas où onaurait changé de page
<!--[if !supportEmptyParas * --> <!--[endif]-->
mov [eax * ,edi //on sauvegarde les registres EDI et ESI
mov [eax + 4 * ,esi //
<!--[if !supportEmptyParas * --> <!--[endif]-->
mov esi,esp //oncalcule l'adresse de la source pour la copie
//des paramètres
lea edi,[eax + 8 * //on calcule l'adresse de la zone des paramètres
//copiés
cld //copie vers le haut
<!--[if !supportEmptyParas * --> <!--[endif]-->
shr ecx,2 //copie 4 octetspar 4 octets
rep movsd //on copie les ecxDWORDs
<!--[if !supportEmptyParas * --> <!--[endif]-->
mov esp,eax //on place la pile à l'adresse des (données
//copiées + EDI + ESI)
<!--[if !supportEmptyParas * --> <!--[endif]-->
pop edi //on restaure les registres EDI et ESI
pop esi
mov eax,0x12345678 //on copie l'adresse de la fonction stdcall à
//appeler : 0x12345678 sera remplacé par l'adresse
jmp eax //on appelle la fonction
<!--[if !supportEmptyParas * --> <!--[endif]-->
<!--[if !supportEmptyParas * --> <!--[endif]-->
Fastcall
<!--[if !supportEmptyParas * --> <!--[endif]-->
Le problème est l’ordre des paramètres :
<!--[if !supportEmptyParas * --> <!--[endif]-->
Le code dépend du nombre de paramètres pouvant être mis dansles registres :
<!--[if !supportEmptyParas * --> <!--[endif]-->
Le code sera le suivant :
pop eax //on récupère l'adresse deretour
<!--[if !supportEmptyParas * --> <!--[endif]-->
//ce code sera soit modifié suivant lenombre de « registrables »
//s'il y a un deuxième paramètrepouvant tenir dans un registre
PUSH EDX
//s'il y a un premier paramètrepouvant tenir dans un registre
PUSH ECX
<!--[if !supportEmptyParas * --> <!--[endif]-->
push eax //on remet l'adresse deretour
<!--[if !supportEmptyParas * --> <!--[endif]-->
mov eax,0x12345678 //on copie l'adresse de la fonction stdcall à
//appeler : 0x12345678sera remplacé par l'adresse
jmp eax //on appelle la fonction
<!--[if !supportEmptyParas * --> <!--[endif]-->
<!--[if !supportEmptyParas * --> <!--[endif]-->
Thiscall
<!--[if !supportEmptyParas * --> <!--[endif]-->
Le plus dure est de connaître la structure de l’objet C++, àsavoir les variables privées. Il faut pour cela, disposer du fichier .h ettraduire les membres de type variable de l’objet en structure VB. Il ne fautpas confondre le pointeur This des objets COM (et VB) avec le pointeur This desobjets C++. Ce dernier a un pointeur vers une vtable, uniquement s’ilcontient des fonctions virtuelles. Un objet C++ n’est pas une entitéautonome au sens de COM. La seul différence entre une fonction stdcall et uneméthode thiscall est la décoration de son nom par le compilateur et le pointeurthis dans ECX.
<!--[if !supportEmptyParas * --> <!--[endif]-->
Le code devra donc simplement mettre le pointeur This de ECXcomme premier paramètre. Le code sera le suivant :
pop eax //on récupère l'adresse deretour
push ecx //on met le pointeur ThisC++ sur la pile
push eax //on remet l'adresse deretour
<!--[if !supportEmptyParas * --> <!--[endif]-->
mov eax,0x12345678 //on copie l'adresse de la fonction stdcall à
//appeler : 0x12345678sera remplacé par l'adresse
jmp eax //on appelle la fonction
<!--[if !supportEmptyParas * --> <!--[endif]-->
<!--[if !supportEmptyParas * --> <!--[endif]-->
Pascal
<!--[if !supportEmptyParas * --> <!--[endif]-->
Là c’est simple, il suffit que les paramètres soient mis enordre inverse. Si on a a,b,c dans le prototype pascal, on met c,b,a dans lafonction BAS.