Was ist __stdcall?

151

Ich lerne etwas über Win32-Programmierung und der WinMainPrototyp sieht folgendermaßen aus:

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

Ich war verwirrt darüber, wofür diese WINAPIKennung gedacht war und fand:

#define WINAPI      __stdcall

Was macht das? Ich bin verwirrt darüber, dass ich nach einem Rückgabetyp überhaupt etwas habe. Wofür ist __stdcall? Was bedeutet es, wenn zwischen dem Rückgabetyp und dem Funktionsnamen etwas liegt?

Tristan Havelick
quelle
2
@AndrewProck Die "Dummy" -Änderung war in diesem Fall in Ordnung, aber im Allgemeinen können Sie  s verwenden, um das alberne (und kontraproduktive - Beispiel) 6-Zeichen-Minimum zu umgehen.
Adi Inbar

Antworten:

170

__stdcallist die für die Funktion verwendete Aufrufkonvention. Dies teilt dem Compiler die Regeln mit, die für das Einrichten des Stapels, das Übertragen von Argumenten und das Abrufen eines Rückgabewerts gelten.

Es gibt eine Reihe von anderen Aufrufkonventionen, __cdecl, __thiscall, __fastcallund die wunderbar benannt __declspec(naked). __stdcallist die Standardaufrufkonvention für Win32-Systemaufrufe.

Wikipedia behandelt die Details .

Dies ist in erster Linie wichtig, wenn Sie eine Funktion außerhalb Ihres Codes aufrufen (z. B. eine Betriebssystem-API) oder das Betriebssystem Sie aufruft (wie dies hier bei WinMain der Fall ist). Wenn der Compiler die richtige Aufrufkonvention nicht kennt, kommt es wahrscheinlich zu sehr seltsamen Abstürzen, da der Stack nicht richtig verwaltet wird.

Rob Walker
quelle
8
In dieser Frage finden Sie ein Beispiel für sehr starke Abstürze Stackoverflow.com/questions/696306/…
sharptooth
Wenn ich mich nicht irre, steuern diese Aufrufkonventionen, wie der Compiler den Assemblycode erzeugt. Bei der Verbindung mit dem Assemblycode ist es wichtig, dass die Aufrufkonvention beachtet wird, um Stapelprobleme zu vermeiden. Es gibt hier eine schöne Tabelle, die einige Konventionen dokumentiert: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller
Können wir sagen, dass dies bestimmte illegale Optimierungen deaktivieren würde?
Kesari
40

C oder C ++ selbst definieren diese Bezeichner nicht. Sie sind Compiler-Erweiterungen und stehen für bestimmte Aufrufkonventionen. Dies bestimmt, wo Argumente in welcher Reihenfolge abgelegt werden sollen, wo die aufgerufene Funktion die Rücksprungadresse findet und so weiter. Zum Beispiel bedeutet __fastcall, dass Argumente von Funktionen über Register übergeben werden.

Der Wikipedia-Artikel bietet einen Überblick über die verschiedenen dort gefundenen Anrufkonventionen.

Johannes Schaub - litb
quelle
18

Die bisherigen Antworten haben die Details abgedeckt. Wenn Sie jedoch nicht beabsichtigen, zur Montage zu wechseln, müssen Sie lediglich wissen, dass sowohl der Anrufer als auch der Angerufene dieselbe Anrufkonvention verwenden müssen, da sonst Fehler auftreten sind schwer zu finden.

MovEaxEsp
quelle
12

Ich stimme zu, dass alle bisherigen Antworten richtig sind, aber hier ist der Grund. Die C- und C ++ - Compiler von Microsoft bieten verschiedene Aufrufkonventionen für die (beabsichtigte) Geschwindigkeit von Funktionsaufrufen innerhalb der C- und C ++ - Funktionen einer Anwendung. In jedem Fall müssen sich Anrufer und Angerufene darauf einigen, welche Anrufkonvention verwendet werden soll. Jetzt bietet Windows selbst Funktionen (APIs), die bereits kompiliert wurden. Wenn Sie sie aufrufen, müssen Sie sie also anpassen. Alle Aufrufe von Windows-APIs und Rückrufe von Windows-APIs müssen die Konvention __stdcall verwenden.

Windows-Programmierer
quelle
3
und es darf nicht mit _standard_call verwechselt werden, da es standard-c ist! man könnte denken, das wäre der Punkt von __stdcall, wenn man es nicht besser weiß
Johannes Schaub - litb
3
Ein kleiner Trottel: Es gibt einige Windows-APIs, die __cdecl anstelle von __stdcall verwenden - normalerweise solche, die eine variable Anzahl von Parametern annehmen, wie z. B. wsprintf ().
Michael Burr
Du hast recht. Es soll wie eine CRT-Funktion aussehen, ist aber eine API. Wissen Sie zufällig, wie man es von C # aus aufruft?
Windows-Programmierer
Ich habe dies nicht getestet, aber pinvoke.net gibt diese Signatur: "static extern int wsprintf ([Out] StringBuilder lpOut, string lpFmt, ...);"
Michael Burr
Meine Intuition besagt, dass der C # -Compiler nicht wissen würde, wie er die __cdecl-Konvention für diese verwenden soll.
Windows-Programmierer
5

Es hat damit zu tun, wie die Funktion aufgerufen wird - im Grunde die Reihenfolge, in der die Dinge auf den Stapel gelegt werden und wer für die Bereinigung verantwortlich ist.

Hier ist die Dokumentation, aber sie bedeutet nicht viel, es sei denn, Sie verstehen den ersten Teil:
http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

Joel Coehoorn
quelle
4

__stdcall wird verwendet, um die Funktionsargumente in den Stapel zu legen. Nach Abschluss der Funktion wird der Speicher automatisch freigegeben. Dies wird für feste Argumente verwendet.

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

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

Hier hat der fnname Argumente, die er direkt in den Stapel schiebt.

philippe lhardy
quelle
1

Ich musste das bis heute noch nie benutzen. Das liegt daran, dass ich in meinem Code Multithreadding verwende und die von mir verwendete Multithreading-API die Windows-API (_beginthreadex) ist.

So starten Sie den Thread:

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

Die ExecuteCommand-Funktion MUSS das Schlüsselwort __stdcall in der Methodensignatur verwenden, damit Beginthreadex es aufruft:

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