Qu'est-ce que __stdcall?

151

J'apprends la programmation Win32 et le WinMainprototype ressemble à ceci:

int WINAPI WinMain ( HINSTANCE instance, HINSTANCE prev_instance, PSTR cmd_line, int cmd_show )

Je ne savais pas à quoi WINAPIservait cet identifiant et j'ai trouvé:

#define WINAPI      __stdcall

Qu'est-ce que cela fait? Je suis confus par le fait que cela ait quelque chose après un type de retour. A quoi ça sert __stdcall? Qu'est-ce que cela signifie quand il y a quelque chose entre le type de retour et le nom de la fonction?

Tristan Havelick
la source
2
@AndrewProck Le changement "factice" était correct dans ce cas, mais en général, vous pouvez utiliser  s pour contourner le minimum de 6 caractères stupides (et contre-productifs - cas d'espèce).
Adi Inbar

Réponses:

170

__stdcallest la convention d'appel utilisée pour la fonction. Cela indique au compilateur les règles qui s'appliquent pour configurer la pile, pousser des arguments et obtenir une valeur de retour.

Il y a un certain nombre d'autres conventions d'appel, __cdecl, __thiscall, __fastcallet merveilleusement nommé __declspec(naked). __stdcallest la convention d'appel standard pour les appels système Win32.

Wikipedia couvre les détails .

Cela compte principalement lorsque vous appelez une fonction en dehors de votre code (par exemple une API OS) ou que l'OS vous appelle (comme c'est le cas ici avec WinMain). Si le compilateur ne connaît pas la convention d'appel correcte, vous aurez probablement des plantages très étranges car la pile ne sera pas gérée correctement.

Rob Walker
la source
8
Voir cette question pour un exemple de plantages très stricts stackoverflow.com/questions/696306
...
Si je ne me trompe pas, ces conventions d'appel contrôlent la manière dont le compilateur produit le code d'assembly. Lors de l'interfaçage avec le code d'assembly, il est alors essentiel que la convention d'appel soit prise en compte pour éviter les problèmes de pile. Il y a un joli tableau ici documentant certaines conventions: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller
Peut-on dire que cela désactiverait certaines optimisations illégales?
kesari
40

C ou C ++ lui-même ne définissent pas ces identificateurs. Ce sont des extensions de compilateur et représentent certaines conventions d'appel. Cela détermine où placer les arguments, dans quel ordre, où la fonction appelée trouvera l'adresse de retour, et ainsi de suite. Par exemple, __fastcall signifie que les arguments des fonctions sont passés sur les registres.

L' article de Wikipédia donne un aperçu des différentes conventions d'appel qui s'y trouvent.

Johannes Schaub - litb
la source
18

Les réponses jusqu'à présent ont couvert les détails, mais si vous n'avez pas l'intention de passer à l'assemblage, tout ce que vous devez savoir est que l'appelant et l'appelé doivent utiliser la même convention d'appel, sinon vous obtiendrez des bogues qui sont difficiles à trouver.

MovEaxEsp
la source
12

Je conviens que toutes les réponses à ce jour sont correctes, mais voici la raison. Les compilateurs C et C ++ de Microsoft fournissent diverses conventions d'appel pour la vitesse (prévue) des appels de fonction dans les fonctions C et C ++ d'une application. Dans chaque cas, l'appelant et l'appelé doivent s'entendre sur la convention d'appel à utiliser. Maintenant, Windows lui-même fournit des fonctions (API), et celles-ci ont déjà été compilées, donc lorsque vous les appelez, vous devez vous y conformer. Tous les appels aux API Windows et les rappels des API Windows doivent utiliser la convention __stdcall.

Programmeur Windows
la source
3
et il ne faut pas le confondre avec _standard_call en ce sens qu'il est standard-c! on pourrait penser que ce serait le but de __stdcall si on ne sait pas mieux
Johannes Schaub - litb
3
Un petit détail: il y a quelques API Windows qui utilisent __cdecl au lieu de __stdcall - généralement celles qui acceptent un nombre variable de paramètres, comme wsprintf ().
Michael Burr
Vous avez raison. Il est nommé pour ressembler à une fonction CRT mais c'est une API. Par hasard, savez-vous comment P / l'invoquer depuis C #?
Programmeur Windows
Je n'ai pas testé cela, mais pinvoke.net donne cette signature: "static extern int wsprintf ([Out] StringBuilder lpOut, string lpFmt, ...);"
Michael Burr
Mon intuition dit que le compilateur C # ne saurait pas utiliser la convention __cdecl sur celui-là.
Programmeur Windows
5

Cela a à voir avec la façon dont la fonction est appelée - essentiellement l'ordre dans lequel les choses sont placées sur la pile et qui est responsable du nettoyage.

Voici la documentation, mais cela ne veut pas dire grand chose à moins que vous ne compreniez la première partie:
http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

Joël Coehoorn
la source
4

__stdcall est utilisé pour mettre les arguments de la fonction dans la pile. Une fois la fonction terminée, il désalloue automatiquement la mémoire. Ceci est utilisé pour les arguments fixes.

void __stdcall fnname ( int, int* )
{
    ...
}

int main()
{
    CreateThread ( NULL, 0, fnname, int, int*...... )
}

Ici, le fnname a des arguments qu'il pousse directement dans la pile.

philippe lhardy
la source
1

Je n'ai jamais eu à l'utiliser avant jusqu'à aujourd'hui. C'est parce que dans mon code, j'utilise le multi-threading et que l'API multi-threading que j'utilise est celle de Windows (_beginthreadex).

Pour démarrer le fil:

_beginthreadex(NULL, 0, ExecuteCommand, currCommand, 0, 0);

La fonction ExecuteCommand DOIT utiliser le mot clé __stdcall dans la signature de méthode pour que beginthreadex l'appelle:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
Katianie
la source