Fournir des fonctions callbacks autres que stdcall

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 :

  • On alloue de l’espace sur la pile pour un double des paramètres
  • On copie les paramètres et l’adresse de retour dans cet emplacement
  • On saute dans la fonction

<!--[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 :

  • S’il 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

<!--[if !supportEmptyParas * --> <!--[endif]-->

Le code dépend du nombre de paramètres pouvant être mis dansles registres :

  • S’il n’y en a pas : l’ordre des paramètres ne change pas
  • S’il y en a un, le code met celui ci (ECX) en premier paramètre
  • S’il y en a deux, le code met ECX en premier et EDX en deuxième

<!--[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.

Adresse d'origine

A voir également
Ce document intitulé « Fournir des fonctions callbacks autres que stdcall » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous