Fournir les fonctions CALLBACK autres que stdcall
Le problème est différent des appels de pointeurs defonction. En effet, on na pas besoin dobjet mais simplement dune 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.
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.
Transformer en appel stdcall un appel
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.
Le code aura la structuration suivante :
Public TypeCallback
lpfn As Long
Code(0 To <taille nécessaire>) As Long
End Type
Public SubInitCallback(ByRef lpCB As Callback, ByVal lpfnCallback As Long, <autresparamètres dépendants de la convention dappel>)
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
Cdecl
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.
Il faut donc :
- On alloue de lespace sur la pile pour un double des paramètres
- On copie les paramètres et ladresse de retour dans cet emplacement
- On saute dans la fonction
On aura donc la pile suivante avant lappel à la fonctioncallback VB :
Paramètre-n
Paramètre-2
Paramètre-1
Paramètre-n
Paramètre-2
Paramètre-1
ReturnAddress
En prenant en compte le fait quil 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
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
test [eax],ecx //on vérifie que la page est chargée avant de
//copier au cas où onaurait changé de page
mov [eax],edi //on sauvegarde les registres EDI et ESI
mov [eax + 4],esi //
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
shr ecx,2 //copie 4 octetspar 4 octets
rep movsd //on copie les ecxDWORDs
mov esp,eax //on place la pile à l'adresse des (données
//copiées + EDI + ESI)
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
Fastcall
Le problème est lordre des paramètres :
- Sil y a un ou plusieurs paramètres entiers ou pointeurs (<= 4 octets), on les met en premier dans le prototype VB
- On met donc tout autres paramètres en suivant dans leur ordre de déclaration
Le code dépend du nombre de paramètres pouvant être mis dansles registres :
- Sil ny en a pas : lordre des paramètres ne change pas
- Sil y en a un, le code met celui ci (ECX) en premier paramètre
- Sil y en a deux, le code met ECX en premier et EDX en deuxième
Le code sera le suivant :
pop eax //on récupère l'adresse deretour
//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
push eax //on remet l'adresse deretour
mov eax,0x12345678 //on copie l'adresse de la fonction stdcall à
//appeler : 0x12345678sera remplacé par l'adresse
jmp eax //on appelle la fonction
Thiscall
Le plus dure est de connaître la structure de lobjet C++, àsavoir les variables privées. Il faut pour cela, disposer du fichier .h ettraduire les membres de type variable de lobjet 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 silcontient des fonctions virtuelles. Un objet C++ nest 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.
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
mov eax,0x12345678 //on copie l'adresse de la fonction stdcall à
//appeler : 0x12345678sera remplacé par l'adresse
jmp eax //on appelle la fonction
Pascal
Là cest 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.