Che cos'è __stdcall?


151

Sto imparando a programmare Win32 e il WinMainprototipo è simile a:

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

Ero confuso su cosa fosse questo WINAPIidentificatore e ho trovato:

#define WINAPI      __stdcall

Cosa fa questo? Sono confuso da questo avere qualcosa dopo un tipo di ritorno. A cosa serve __stdcall? Che cosa significa quando c'è qualcosa tra il tipo restituito e il nome della funzione?


2
@AndrewProck In questo caso la modifica "fittizia" è stata corretta, ma in generale è possibile utilizzare  s per aggirare il minimo sciocco (e controproducente - caso al punto) di 6 caratteri.
Adi Inbar,

Risposte:


170

__stdcallè la convenzione di chiamata utilizzata per la funzione. Questo dice al compilatore le regole che si applicano per impostare lo stack, spingendo gli argomenti e ottenendo un valore di ritorno.

Ci sono una serie di altre convenzioni di chiamata, __cdecl, __thiscall, __fastcalle meravigliosamente nominati __declspec(naked). __stdcallè la convenzione di chiamata standard per le chiamate di sistema Win32.

Wikipedia copre i dettagli .

È importante soprattutto quando si chiama una funzione al di fuori del proprio codice (ad es. Un'API del sistema operativo) o quando il sistema operativo la chiama (come nel caso di WinMain). Se il compilatore non conosce la convenzione di chiamata corretta, probabilmente si verificheranno arresti anomali molto strani poiché lo stack non verrà gestito correttamente.


8
Vedere questa domanda per un esempio di incidenti molto starnge stackoverflow.com/questions/696306/...
sharptooth

Se non sbaglio, queste convenzioni di chiamata controllano come il compilatore produce il codice assembly. Quando si interfaccia con il codice assembly, è quindi fondamentale prendere in considerazione la convenzione di chiamata per evitare problemi di stack. C'è un bel tavolo qui che documenta alcune convenzioni: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller

Possiamo dire che questo disabiliterebbe alcune ottimizzazioni illegali?
kesari,

40

C o C ++ stesso non definiscono tali identificatori. Sono estensioni del compilatore e rappresentano determinate convenzioni di chiamata. Ciò determina dove inserire gli argomenti, in quale ordine, dove la funzione chiamata troverà l'indirizzo di ritorno e così via. Ad esempio, __fastcall significa che gli argomenti delle funzioni vengono passati ai registri.

L' articolo di Wikipedia fornisce una panoramica delle diverse convenzioni di chiamata presenti.


18

Le risposte finora hanno coperto i dettagli, ma se non intendi passare all'assemblaggio, tutto quello che devi sapere è che sia il chiamante che il chiamante devono usare la stessa convenzione di chiamata, altrimenti otterrai bug che sono difficili da trovare.


12

Sono d'accordo che tutte le risposte finora sono corrette, ma ecco il motivo. I compilatori C e C ++ di Microsoft forniscono varie convenzioni di chiamata per la velocità (prevista) delle chiamate di funzione all'interno delle funzioni C e C ++ di un'applicazione. In ogni caso, il chiamante e il chiamante devono concordare quale convenzione di chiamata utilizzare. Ora, Windows stesso fornisce funzioni (API) e quelle sono già state compilate, quindi quando le chiami devi conformarti ad esse. Tutte le chiamate alle API di Windows e i callback dalle API di Windows devono utilizzare la convenzione __stdcall.


3
e non deve essere confuso con _standard_call in quanto è standard-c! si potrebbe pensare che sarebbe il punto di __stdcall se non si conoscesse meglio
Johannes Schaub - litb

3
Un piccolo pignolo: ci sono alcune API di Windows che usano __cdecl invece di __stdcall - di solito quelle che accettano un numero variabile di parametri, come wsprintf ().
Michael Burr,

Hai ragione. È chiamato per sembrare una funzione CRT ma è un'API. Per caso sai come P / Invocarlo da C #?
Programmatore di Windows,

Non l'ho provato, ma pinvoke.net dà questa firma: "static extern int wsprintf ([Out] StringBuilder lpOut, string lpFmt, ...);"
Michael Burr,

La mia intuizione dice che il compilatore C # non saprebbe usare la convenzione __cdecl su quella.
Programmatore di Windows,



4

__stdcall viene utilizzato per inserire gli argomenti della funzione nello stack. Dopo il completamento della funzione, sposta automaticamente la memoria. Questo è usato per argomenti fissi.

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

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

Qui il fnname ha argomenti che spingono direttamente nello stack.


1

Non ho mai dovuto usarlo prima di oggi. È perché nel mio codice sto usando multi-threadding e l'API multi-threading che sto usando è quella di Windows (_beginthreadex).

Per iniziare la discussione:

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

La funzione ExecuteCommand DEVE utilizzare la parola chiave __stdcall nella firma del metodo per consentire a beginthreadex di chiamarla:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.