web-dev-qa-db-fra.com

Quelle est la signification et l'utilisation de __stdcall?

Je suis tombé sur __stdcall beaucoup ces jours-ci.

MSDN n'explique pas très clairement ce que cela signifie vraiment, quand et pourquoi devrait-il être utilisé, le cas échéant.

J'apprécierais que quelqu'un fournisse une explication, de préférence avec un exemple ou deux.

67
vehomzzz

Toutes les fonctions en C/C++ ont une convention d'appel particulière. Le point d'une convention d'appel est d'établir comment les données sont transmises entre l'appelant et l'appelé et qui est responsable des opérations telles que le nettoyage de la pile d'appels.

Les conventions d'appel les plus courantes sur Windows sont

  • __stdcall, Pousse les paramètres sur la pile, dans l'ordre inverse (de droite à gauche)
  • __cdecl, Pousse les paramètres sur la pile, dans l'ordre inverse (de droite à gauche)
  • __clrcall, Charge les paramètres dans la pile d'expressions CLR dans l'ordre (de gauche à droite).
  • __fastcall, Stocké dans les registres, puis poussé sur la pile
  • __thiscall, Poussé sur la pile; ce pointeur stocké dans ECX

L'ajout de ce spécificateur à la déclaration de fonction indique essentiellement au compilateur que vous souhaitez que cette fonction particulière ait cette convention d'appel particulière.

Les conventions d'appel sont documentées ici

Raymond Chen a également fait une longue série sur l'histoire des différentes conventions d'appel (5 parties) à partir d'ici.

59
JaredPar

Traditionnellement, les appels de fonction C sont effectués avec l'appelant poussant certains paramètres sur la pile, appelant la fonction, puis éclatant la pile pour nettoyer ces arguments poussés.

/* example of __cdecl */
Push arg1
Push arg2
Push arg3
call function
add sp,12 // effectively "pop; pop; pop"

Remarque: La convention par défaut - illustrée ci-dessus - est connue sous le nom de __cdecl.

L'autre convention la plus populaire est __stdcall. Dans celui-ci, les paramètres sont à nouveau poussés par l'appelant, mais la pile est nettoyée par l'appelé. Il s'agit de la convention standard pour les fonctions de l'API Win32 (telle que définie par la macro WINAPI dans), et elle est aussi parfois appelée la convention d'appel "Pascal".

/* example of __stdcall */
Push arg1 
Push arg2 
Push arg3 
call function // no stack cleanup - callee does this

Cela ressemble à un détail technique mineur, mais s'il y a un désaccord sur la façon dont la pile est gérée entre l'appelant et l'appelé, la pile sera détruite d'une manière qui est peu susceptible d'être récupérée. Étant donné que __stdcall effectue le nettoyage de la pile, le code (très petit) pour effectuer cette tâche se trouve à un seul endroit, plutôt que d'être dupliqué dans chaque appelant comme dans __cdecl. Cela rend le code très légèrement plus petit, bien que l'impact sur la taille ne soit visible que dans les grands programmes.

Les fonctions variadiques comme printf () sont presque impossibles à obtenir correctement avec __stdcall, car seul l'appelant sait vraiment combien d'arguments ont été passés afin de les nettoyer. L'appelé peut faire de bonnes suppositions (par exemple, en regardant une chaîne de format), mais le nettoyage de la pile devrait être déterminé par la logique réelle de la fonction, et non par le mécanisme de convention d'appel lui-même. Par conséquent, seul __cdecl prend en charge les fonctions variadic afin que l'appelant puisse faire le nettoyage.

Décorations de nom de symbole de l'éditeur de liens: Comme mentionné dans une puce ci-dessus, appeler une fonction avec la "mauvaise" convention peut être désastreux, donc Microsoft dispose d'un mécanisme pour éviter que cela ne se produise. Cela fonctionne bien, même si cela peut être exaspérant si l'on ne connaît pas les raisons. Ils ont choisi de résoudre ce problème en codant la convention d'appel dans les noms de fonction de bas niveau avec des caractères supplémentaires (qui sont souvent appelés "décorations"), et ceux-ci sont traités comme des noms non liés par l'éditeur de liens. La convention d'appel par défaut est __cdecl, mais chacune peut être demandée explicitement avec le/G? paramètre au compilateur.

__cdecl (cl/Gd ...)

Tous les noms de fonction de ce type sont précédés d'un trait de soulignement et le nombre de paramètres n'a pas vraiment d'importance car l'appelant est responsable de la configuration et du nettoyage de la pile. Il est possible que l'appelant et l'appelé soient confus sur le nombre de paramètres réellement passés, mais au moins la discipline de pile est maintenue correctement.

__stdcall (cl/Gz ...)

Ces noms de fonction sont préfixés par un trait de soulignement et ajoutés par @ plus le nombre d'octets de paramètres passés. Par ce mécanisme, il n'est pas possible d'appeler une fonction avec le "mauvais" type, ou même avec le mauvais nombre de paramètres.

__fastcall (cl/Gr ...)

Ces noms de fonction commencent par un signe @ et sont suffixés du nombre de paramètres @, tout comme __stdcall.

Exemples:

Declaration                        ----------------------->    decorated name


void __cdecl foo(void);            ----------------------->    _foo

void __cdecl foo(int a);           ----------------------->    _foo

void __cdecl foo(int a, int b);    ----------------------->    _foo

void __stdcall foo(void);          ----------------------->    _foo@0

void __stdcall foo(int a);         ----------------------->    _foo@4

void __stdcall foo(int a, int b);  ----------------------->    _foo@8

void __fastcall foo(void);         ----------------------->    @foo@0

void __fastcall foo(int a);        ----------------------->    @foo@4

void __fastcall foo(int a, int b); ----------------------->    @foo@8
58
edge

__stdcall est une convention d'appel: un moyen de déterminer comment les paramètres sont passés à une fonction (sur la pile ou dans les registres) et qui est responsable du nettoyage après le retour de la fonction (l'appelant ou l'appelé).

Raymond Chen a écrit un blog sur les principales conventions d'appel x86 , et il y a aussi un Nice article CodeProject .

Pour la plupart, vous ne devriez pas avoir à vous en préoccuper. Le seul cas dans lequel vous devriez le faire est si vous appelez une fonction de bibliothèque qui utilise autre chose que la valeur par défaut - sinon le compilateur générera le mauvais code et votre programme se bloquera probablement.

7
Nick Meyer

Malheureusement, il n'y a pas de réponse simple pour savoir quand l'utiliser et quand non.

__stdcall signifie que les arguments d'une fonction sont poussés sur la pile du premier au dernier. C'est par opposition à __cdecl, ce qui signifie que les arguments sont poussés du dernier au premier, et __fastcall, qui place les quatre premiers (je pense) arguments dans les registres, et le reste va sur la pile.

Vous avez juste besoin de savoir ce que l'appelé attend, ou si vous écrivez une bibliothèque, ce que vos appelants attendent probablement, et assurez-vous de documenter la convention que vous avez choisie.

6
Jonathan

Il spécifie une convention d'appel pour une fonction. Une convention d'appel est un ensemble de règles sur la façon dont les paramètres sont transmis à une fonction: dans quel ordre, par adresse ou par copie, qui doit nettoyer les paramètres (appelant ou appelé), etc.

3
sbi

__stdcall désigne une convention d'appel (voir ce PDF pour plus de détails). Cela signifie qu'il spécifie comment les arguments de fonction sont poussés et extraits de la pile et qui est responsable.

__stdcall n'est qu'une des nombreuses conventions d'appel et est utilisé dans WINAPI. Vous devez l'utiliser si vous fournissez des pointeurs de fonction en tant que rappels pour certaines de ces fonctions. En général, vous n'avez pas besoin de désigner de convention d'appel spécifique dans votre code, mais utilisez simplement la valeur par défaut du compilateur, sauf dans le cas indiqué ci-dessus (fournissant des rappels au code tiers).

2
gimpf

__ stdcall est la convention d'appel utilisée pour la fonction. Cela indique au compilateur les règles qui s'appliquent pour configurer la pile, pousser les arguments et obtenir une valeur de retour. Il existe un certain nombre d'autres conventions d'appel comme __ cdecl , __ thiscall , __ fastcall et __ nu .

__ stdcall est la convention d'appel standard pour les appels système Win32.

Plus de détails peuvent être trouvés sur Wikipedia .

1
Anand

C'est une convention d'appel selon laquelle les fonctions WinAPI doivent être appelées correctement. Une convention d'appel est un ensemble de règles sur la façon dont les paramètres sont transmis à la fonction et comment la valeur de retour est transmise à partir de la fonction.

Si l'appelant et le code appelé utilisent des conventions différentes, vous rencontrez un comportement indéfini (comme n tel crash à l'aspect étrange ).

Les compilateurs C++ n'utilisent pas __stdcall par défaut - ils utilisent d'autres conventions. Donc, pour appeler des fonctions WinAPI à partir de C++, vous devez spécifier qu'elles utilisent __stdcall - cela se fait généralement dans les fichiers d'en-tête du SDK Windoes et vous le faites également lors de la déclaration des pointeurs de fonction.

1
sharptooth

mettez simplement quand vous appelez la fonction, elle est chargée dans la pile/le registre. __stdcall est une convention/voie (argument de droite d'abord, puis argument de gauche ...), __decl est une autre convention qui est utilisée pour charger la fonction sur la pile ou les registres.

Si vous les utilisez, vous demandez à l'ordinateur d'utiliser cette méthode spécifique pour charger/décharger la fonction pendant la liaison et, par conséquent, vous n'obtiendrez pas de non-concordance/plantage.

Sinon, l'appelé de fonction et l'appelant de fonction peuvent utiliser des conventions différentes entraînant le plantage du programme.

1
dev ray