Was ist die Bedeutung und Verwendung von __stdcall?

75

Ich bin in __stdcalldiesen Tagen viel gestoßen.

MSDN erklärt nicht sehr klar, was es wirklich bedeutet, wann und warum es verwendet werden sollte, wenn überhaupt.

Ich würde mich freuen, wenn jemand eine Erklärung geben würde, vorzugsweise mit ein oder zwei Beispielen.

vehomzzz
quelle

Antworten:

68

Alle Funktionen in C / C ++ haben eine bestimmte Aufrufkonvention. Der Zweck einer Aufrufkonvention besteht darin, festzulegen, wie Daten zwischen dem Anrufer und dem Angerufenen übertragen werden und wer für Vorgänge wie das Bereinigen des Anrufstapels verantwortlich ist.

Die beliebtesten Anrufkonventionen unter Windows sind

  • __stdcall, Schiebt die Parameter auf dem Stapel in umgekehrter Reihenfolge (von rechts nach links)
  • __cdecl, Schiebt die Parameter auf dem Stapel in umgekehrter Reihenfolge (von rechts nach links)
  • __clrcallLaden Sie die Parameter der Reihe nach (von links nach rechts) in den CLR-Ausdrucksstapel.
  • __fastcall, In Registern gespeichert, dann auf Stapel geschoben
  • __thiscallAuf Stapel geschoben; Dieser Zeiger ist in ECX gespeichert

Das Hinzufügen dieses Bezeichners zur Funktionsdeklaration teilt dem Compiler im Wesentlichen mit, dass diese bestimmte Funktion diese bestimmte Aufrufkonvention haben soll.

Die Aufrufkonventionen sind hier dokumentiert

Raymond Chen hat auch eine lange Reihe über die Geschichte der verschiedenen Aufrufkonventionen (5 Teile) gemacht, die hier beginnen.

JaredPar
quelle
Was ist mit x64-Builds?
Pablo Ariel
70

Traditionell werden C-Funktionsaufrufe ausgeführt, bei denen der Aufrufer einige Parameter auf den Stapel schiebt, die Funktion aufruft und dann den Stapel öffnet, um diese übertragenen Argumente zu bereinigen.

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

Hinweis: Die oben gezeigte Standardkonvention heißt __cdecl.

Die andere beliebteste Konvention ist __stdcall. Darin werden die Parameter erneut vom Aufrufer gepusht, aber der Stapel wird vom Angerufenen bereinigt. Dies ist die Standardkonvention für Win32-API-Funktionen (wie vom WINAPI-Makro in definiert) und wird manchmal auch als "Pascal" -Aufrufkonvention bezeichnet.

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

Dies sieht nach einem kleinen technischen Detail aus. Wenn jedoch Unstimmigkeiten darüber bestehen, wie der Stapel zwischen dem Anrufer und dem Angerufenen verwaltet wird, wird der Stapel auf eine Weise zerstört, die wahrscheinlich nicht wiederhergestellt werden kann. Da __stdcall die Stapelbereinigung durchführt, befindet sich der (sehr kleine) Code zum Ausführen dieser Aufgabe nur an einer Stelle und wird nicht wie in __cdecl in jedem Aufrufer dupliziert. Dadurch wird der Code geringfügig kleiner, obwohl die Auswirkungen auf die Größe nur in großen Programmen sichtbar sind.

Variadische Funktionen wie printf () können mit __stdcall kaum richtig ausgeführt werden, da nur der Aufrufer wirklich weiß, wie viele Argumente übergeben wurden, um sie zu bereinigen. Der Angerufene kann einige gute Vermutungen anstellen (z. B. durch Betrachten einer Formatzeichenfolge), aber die Stapelbereinigung müsste durch die tatsächliche Logik der Funktion bestimmt werden, nicht durch den Aufrufkonventionsmechanismus selbst. Daher unterstützt nur __cdecl verschiedene Funktionen, damit der Aufrufer die Bereinigung durchführen kann.

Dekorationen von Linkersymbolnamen: Wie oben in einem Aufzählungspunkt erwähnt, kann das Aufrufen einer Funktion mit der "falschen" Konvention katastrophal sein. Microsoft verfügt daher über einen Mechanismus, um dies zu verhindern. Es funktioniert gut, obwohl es verrückt sein kann, wenn man die Gründe nicht kennt. Sie haben beschlossen, dies zu beheben, indem sie die aufrufende Konvention in die Funktionsnamen auf niedriger Ebene mit zusätzlichen Zeichen (die häufig als "Dekorationen" bezeichnet werden) codieren. Diese werden vom Linker als nicht verwandte Namen behandelt. Die Standardaufrufkonvention ist __cdecl, aber jede kann explizit mit dem / G? Parameter an den Compiler.

__cdecl (cl / Gd ...)

Allen Funktionsnamen dieses Typs wird ein Unterstrich vorangestellt, und die Anzahl der Parameter spielt keine Rolle, da der Aufrufer für die Stapeleinrichtung und die Stapelbereinigung verantwortlich ist. Es ist möglich, dass ein Anrufer und ein Angeregter über die Anzahl der tatsächlich übergebenen Parameter verwirrt sind, aber zumindest die Stapeldisziplin wird ordnungsgemäß beibehalten.

__stdcall (cl / Gz ...)

Diesen Funktionsnamen wird ein Unterstrich vorangestellt und mit @ plus der Anzahl der übergebenen Bytes von Parametern versehen. Durch diesen Mechanismus ist es nicht möglich, eine Funktion mit dem "falschen" Typ oder sogar mit der falschen Anzahl von Parametern aufzurufen.

__fastcall (cl / Gr ...)

Diese Funktionsnamen beginnen mit einem @ -Zeichen und werden mit dem @ -Parameter-Zähler versehen, ähnlich wie __stdcall.

Beispiele:

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
Kante
quelle
7

__stdcall ist eine aufrufende Konvention: Eine Methode zum Bestimmen, wie Parameter an eine Funktion (auf dem Stapel oder in Registern) übergeben werden und wer für die Bereinigung nach der Rückkehr der Funktion verantwortlich ist (der Aufrufer oder der Angerufene).

Raymond Chen hat einen Blog über die wichtigsten x86-Aufrufkonventionen geschrieben , und es gibt auch einen schönen CodeProject-Artikel .

Zum größten Teil sollten Sie sich keine Sorgen machen müssen. Der einzige Fall, in dem Sie dies tun sollten, ist, wenn Sie eine Bibliotheksfunktion aufrufen, die etwas anderes als die Standardfunktion verwendet. Andernfalls generiert der Compiler den falschen Code und Ihr Programm stürzt wahrscheinlich ab.

Nick Meyer
quelle
6

Leider gibt es keine einfache Antwort, wann und wann nicht.

__stdcall bedeutet, dass die Argumente für eine Funktion vom ersten bis zum letzten auf den Stapel verschoben werden. Dies steht im Gegensatz zu __cdecl, was bedeutet, dass die Argumente von last nach first verschoben werden, und __fastcall, bei dem die ersten vier (glaube ich) Argumente in Registern abgelegt werden und der Rest auf dem Stapel abgelegt wird.

Sie müssen nur wissen, was der Angerufene erwartet, oder wenn Sie eine Bibliothek schreiben, was Ihre Anrufer wahrscheinlich erwarten, und sicherstellen, dass Sie die von Ihnen gewählte Konvention dokumentieren.

Jonathan
quelle
2
__stdcallund __cdeclunterscheiden sich nur in der Verantwortung für die Reinigung nach der Rückgabe (und der Dekoration). Die Argumentübergabe ist für beide gleich (von rechts nach links). Was Sie beschreiben, ist die Pascal-Aufrufkonvention.
a3f
3

Es gibt eine Aufrufkonvention für eine Funktion an. Eine aufrufende Konvention ist eine Reihe von Regeln, wie Parameter an eine Funktion übergeben werden: in welcher Reihenfolge, pro Adresse oder pro Kopie, wer die Parameter bereinigen soll (Anrufer oder Angerufene) usw.

sbi
quelle
2

__stdcall bezeichnet eine aufrufende Konvention ( einige Details finden Sie in diesem PDF ). Dies bedeutet, dass angegeben wird, wie Funktionsargumente vom Stapel verschoben und abgelegt werden und wer dafür verantwortlich ist.

__stdcall ist nur eine von mehreren Aufrufkonventionen und wird in der gesamten WINAPI verwendet. Sie müssen es verwenden, wenn Sie Funktionszeiger als Rückruf für einige dieser Funktionen bereitstellen. Im Allgemeinen müssen Sie in Ihrem Code keine bestimmte Aufrufkonvention angeben, sondern nur die Standardeinstellung des Compilers verwenden, mit Ausnahme des oben genannten Falls (Rückrufe auf Code von Drittanbietern).

gimpf
quelle
1

Dies ist eine Aufrufkonvention, nach der WinAPI-Funktionen ordnungsgemäß aufgerufen werden müssen. Eine aufrufende Konvention besteht aus einer Reihe von Regeln, wie die Parameter an die Funktion übergeben werden und wie der Rückgabewert von der Funktion übergeben wird.

Wenn der Aufrufer und der aufgerufene Code unterschiedliche Konventionen verwenden, stoßen Sie auf undefiniertes Verhalten (wie einen so seltsam aussehenden Absturz ).

C ++ - Compiler verwenden standardmäßig nicht __stdcall - sie verwenden andere Konventionen. Um WinAPI-Funktionen von C ++ aus aufzurufen, müssen Sie angeben, dass sie __stdcall verwenden. Dies erfolgt normalerweise in Windoes SDK-Headerdateien und auch beim Deklarieren von Funktionszeigern.

scharfer Zahn
quelle
1

Einfach ausgedrückt, wenn Sie die Funktion aufrufen, wird sie in den Stapel / das Register geladen. __stdcall ist eine Konvention / ein Weg (zuerst das rechte Argument, dann das linke Argument ...). __decl ist eine andere Konvention, mit der die Funktion auf den Stapel oder die Register geladen wird.

Wenn Sie sie verwenden, weisen Sie den Computer an, diese Funktion zum Laden / Entladen der Funktion während der Verknüpfung zu verwenden, sodass Sie keine Nichtübereinstimmung / keinen Absturz erhalten.

Andernfalls verwenden der Funktionsanrufer und der Funktionsaufruf möglicherweise unterschiedliche Konventionen, die zum Absturz des Programms führen.

dev ray
quelle
1

__stdcall ist die aufrufende Konvention, die für die Funktion verwendet wird. 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 anderer Aufrufkonventionen wie __cdecl , __thiscall , __fastcall und __naked .

__stdcall ist die Standardaufrufkonvention für Win32-Systemaufrufe.

Weitere Details finden Sie auf Wikipedia .

Anand
quelle