webentwicklung-frage-antwort-db.com.de

Seltsam MSC 8.0 Fehler: "Der Wert von ESP wurde bei einem Funktionsaufruf nicht ordnungsgemäß gespeichert ... "

Wir haben kürzlich versucht, einige unserer Visual Studio-Projekte in Bibliotheken aufzuteilen, und in einem Testprojekt schien alles gut zu kompilieren und zu bauen, wobei eines der Bibliotheksprojekte als Abhängigkeit dient. Der Versuch, die Anwendung auszuführen, hat jedoch zu der folgenden bösen Laufzeitfehlermeldung geführt:

Laufzeitüberprüfung fehlgeschlagen # 0 - Der Wert von ESP wurde bei einem Funktionsaufruf nicht ordnungsgemäß gespeichert. Dies ist normalerweise das Ergebnis eines Aufrufs eines Funktionszeigers, der mit einer anderen Aufrufkonvention deklariert wurde.

Wir haben noch nicht einmal Aufrufkonventionen (__cdecl usw.) für unsere Funktionen angegeben, sodass alle Compilerschalter auf der Standardeinstellung stehen. Ich habe geprüft, und die Projekteinstellungen sind konsistent für den Aufruf von Konventionen in der Bibliothek und den Testprojekten.

Update: Einer unserer Entwickler hat die Projekteinstellung "Basic Runtime Checks" von "Both (/ RTC1, entspricht/RTCsu)" in "Default" geändert und die Laufzeit verschwindet, wodurch das Programm scheinbar korrekt ausgeführt wird. Ich traue dem überhaupt nicht. War das eine richtige Lösung oder ein gefährlicher Hack?

45
user11180

Dieser Debug - Fehler bedeutet, dass das Stack - Pointer - Register nach dem Funktionsaufruf nicht auf seinen ursprünglichen Wert zurückgesetzt wird, dh dass der Anzahl der pushes vor dem Funktionsaufruf nicht die gleiche Anzahl von pops gefolgt wurde Anruf.

Ich kenne zwei Gründe dafür (beide mit dynamisch geladenen Bibliotheken). # 1 beschreibt VC++ in der Fehlermeldung, aber ich glaube nicht, dass dies die häufigste Fehlerursache ist (siehe # 2).

1) Nicht übereinstimmende Aufrufkonventionen:

Der Anrufer und der Angerufene haben keine richtige Vereinbarung darüber, wer was tun wird. Wenn Sie zum Beispiel eine DLL - Funktion aufrufen, die _stdcall lautet, haben Sie sie aus irgendeinem Grund als _cdecl (Standard in VC++) in Ihrem Aufruf deklariert. Dies würde häufig passieren, wenn Sie verschiedene Sprachen in verschiedenen Modulen usw. verwenden. 

Sie müssten die Deklaration der fehlerhaften Funktion überprüfen und sicherstellen, dass sie nicht zweimal und anders deklariert wird.

2) Nicht übereinstimmende Typen:

Der Anrufer und der Angerufene werden nicht mit denselben Typen kompiliert. Ein allgemeiner Header definiert beispielsweise die Typen in der API und wurde kürzlich geändert, und ein Modul wurde erneut kompiliert, das andere jedoch nicht - d. H. Einige Typen können im Anrufer und im Gespräch eine andere Größe haben.

In diesem Fall drückt der Aufrufer die Argumente einer Größe, aber der Angerufene (wenn Sie _stdcall verwenden, bei dem der Angerufene den Stapel bereinigt) zeigt die andere Größe an. Das ESP wird daher nicht auf den korrekten Wert zurückgesetzt.

(Natürlich scheinen diese Argumente und andere unter ihnen in der aufgerufenen Funktion verstümmelt zu sein, aber manchmal können Sie das auch ohne sichtbaren Absturz überleben.)

Wenn Sie Zugriff auf den gesamten Code haben, kompilieren Sie ihn einfach neu.

47

Ich habe das in einem anderen Forum gelesen

Ich hatte das gleiche Problem, aber ich habe es einfach behoben. Ich habe dieselbe Fehlermeldung aus dem folgenden Code erhalten:

HMODULE hPowerFunctions = LoadLibrary("Powrprof.dll");
typedef bool (*tSetSuspendStateSig)(BOOL, BOOL, BOOL);

tSetSuspendState SetSuspendState = (tSuspendStateSig)GetProcAddress(hPowerfunctions, "SetSuspendState");

result = SetSuspendState(false, false, false); <---- This line was where the error popped up. 

Nach einiger Untersuchung habe ich eine der Zeilen geändert in:

typedef bool (WINAPI*tSetSuspendStateSig)(BOOL, BOOL, BOOL);

was das Problem gelöst hat. Wenn Sie in der Header-Datei nachsehen, wo SetSuspendState gefunden wird (powrprof.h, Teil des SDK), wird der Prototyp der Funktion folgendermaßen definiert: 

BOOLEAN WINAPI SetSuspendState(BOOLEAN, BOOLEAN, BOOLEAN);

Ihr habt also ein ähnliches Problem. Wenn Sie eine gegebene Funktion aus einer DLL aufrufen, ist ihre Signatur wahrscheinlich deaktiviert. (In meinem Fall war es das fehlende WINAPI-Schlüsselwort). 

Hoffe, das hilft allen zukünftigen Menschen! :-)

Prost.

18
Khaled

Den Scheck zum Schweigen zu bringen, ist nicht die richtige Lösung. Sie müssen herausfinden, was mit Ihren Anrufkonventionen durcheinander ist.

Es gibt mehrere Möglichkeiten, die Aufruffunktion einer Funktion zu ändern, ohne sie explizit anzugeben. extern "C" macht es, STDMETHODIMP/IFACEMETHODIMP auch, andere Makros können es auch tun.

Ich glaube, wenn Sie Ihr Programm unter WinDBG ( http://www.Microsoft.com/whdc/devtools/debugging/default.mspx ) ausführen, sollte die Laufzeit an der Stelle abbrechen, an der Sie dieses Problem haben. Sie können sich den Aufrufstack ansehen und herausfinden, welche Funktion das Problem hat, und dann dessen Definition und die vom Aufrufer verwendete Deklaration betrachten.

11
Franci Penov

Ich habe diesen Fehler festgestellt, als der Code versuchte, eine Funktion für ein Objekt aufzurufen, das nicht den erwarteten Typ hatte.

Also Klassenhierarchie: Eltern mit Kindern: Child1 und Child2

Child1* pMyChild = 0;
...
pMyChild = pSomeClass->GetTheObj();// This call actually returned a Child2 object
pMyChild->SomeFunction();          // "...value of ESP..." error occurs here
5
Tinclon

Ich habe einen ähnlichen Fehler für AutoIt-APIs erhalten, die ich vom VC++ - Programm aus aufgerufen habe.

    typedef long (*AU3_RunFn)(LPCWSTR, LPCWSTR);

Als ich jedoch die Deklaration, die WINAPI enthält, geändert hat, ist das Problem verschwunden.

Code ohne Fehler sieht folgendermaßen aus:

typedef long (WINAPI *AU3_RunFn)(LPCWSTR, LPCWSTR);

AU3_RunFn _AU3_RunFn;
HINSTANCE hInstLibrary = LoadLibrary("AutoItX3.dll");
if (hInstLibrary)
{
  _AU3_RunFn = (AU3_RunFn)GetProcAddress(hInstLibrary, "AU3_WinActivate");
  if (_AU3_RunFn)
     _AU3_RunFn(L"Untitled - Notepad",L"");
  FreeLibrary(hInstLibrary);
}
5
Chandan

Es sei darauf hingewiesen, dass dies auch ein Visual Studio-Fehler sein kann.

Ich habe dieses Problem auf VS2017, Win10 x64 erhalten. Anfangs machte es Sinn, da ich seltsame Dinge tat, indem ich dies zu einem abgeleiteten Typ machte und es in ein Lambda wickelte. Ich habe den Code jedoch auf ein früheres Commit zurückgesetzt und den Fehler trotzdem erhalten, obwohl er vorher nicht vorhanden war. 

Ich habe versucht, das Projekt neu zu starten und dann neu zu erstellen, und dann ist der Fehler verschwunden.

2
stands2reason

Ich habe diesen Fehler beim Aufrufen einer Funktion in einer DLL erhalten, die mit einer vor Version 2005 erstellten Version von Visual C++ aus einer neueren Version von VC (2008) ..__ kompiliert wurde. Die Funktion hatte diese Signatur:

LONG WINAPI myFunc( time_t, SYSTEMTIME*, BOOL* );

Das Problem war, dass die Größe von time_t in der Version vor 2005 32 Bit betrug, seit VS2005 jedoch 64 Bit (ist als _time64_t definiert). Der Aufruf der Funktion erwartet eine 32-Bit-Variable, erhält jedoch eine 64-Bit-Variable, wenn sie von VC> = 2005 aufgerufen wird. Da die Parameter von Funktionen bei Verwendung der WINAPI-Aufrufkonvention über den Stack übergeben werden, wird der Stack beschädigt und die obige Fehlermeldung ("Laufzeitprüfung Fehler # 0 ...").

Um dies zu beheben, ist es möglich

#define _USE_32BIT_TIME_T

vor dem Einfügen der Header-Datei von DLL oder - besser - ändern Sie die Signatur der Funktion in der Header-Datei in Abhängigkeit von der VS-Version (Vor-2005-Versionen kennen _time32_t nicht!):

#if _MSC_VER >= 1400
LONG WINAPI myFunc( _time32_t, SYSTEMTIME*, BOOL* );
#else
LONG WINAPI myFunc( time_t, SYSTEMTIME*, BOOL* );
#endif

Beachten Sie, dass Sie im aufrufenden Programm natürlich _time32_t anstelle von time_t verwenden müssen.

2
oli_arborum

Ein anderer Fall, in dem esp durcheinander geraten kann, ist ein versehentlicher Pufferüberlauf, normalerweise durch falsche Verwendung von Zeigern, um über die Grenzen eines Arrays hinauszuarbeiten. Angenommen, Sie haben eine C-Funktion, die aussieht

int a, b[2];

Wenn Sie in b[3] schreiben, ändert sich wahrscheinlich a. Überall dort, wo wahrscheinlich die gespeicherte esp auf dem Stack gespeichert wird.

1
Alex M

In meiner MFC C++ - Anwendung tritt das gleiche Problem wie in Weird MSC 8.0 Fehler auf: "Der Wert von ESP wurde bei einem Funktionsaufruf nicht ordnungsgemäß gespeichert ..." . Das Posting hat über 42.000 Aufrufe und 16 Antworten/Kommentare, von denen keiner den Compiler als das Problem verantwortlich machte. Zumindest in meinem Fall kann ich zeigen, dass der VS2015-Compiler einen Fehler aufweist.

Mein Dev- und Test-Setup sieht folgendermaßen aus: Ich habe 3 PCs, auf denen jeweils Win10 Version 10.0.10586 ausgeführt wird. Alle kompilieren mit VS2015, aber hier ist der Unterschied. Zwei der VS2015s haben Update 2, während das andere Update 3 angewendet hat. Der PC mit Update 3 funktioniert, aber die anderen beiden mit Update 2 schlagen mit dem gleichen Fehler wie in dem obigen Posting bekannt. Mein MFC-C++ - App-Code ist auf allen drei PCs identisch.

Fazit: Zumindest in meinem Fall enthielt die Compilerversion (Update 2) für meine App einen Fehler, der meinen Code brach. In meiner App wird std :: packaged_task intensiv verwendet. Ich gehe davon aus, dass das Problem in diesem relativ neuen Compilercode lag.

1
rtischer8277

Erstellen Sie statische Bibliotheken oder DLLs? Wenn DLLs, wie werden die Exporte definiert; Wie werden die Importbibliotheken erstellt?

Sind die Prototypen für die Funktionen in den libsgenauidentisch mit den Funktionsdeklarationen, in denen die Funktionen definiert sind?

1
Michael Burr

Dies ist mir beim Zugriff auf ein COM-Objekt (Visual Studio 2010) passiert. Ich habe die GUID für eine andere Schnittstelle A für in meinem Aufruf von QueryInterface übergeben, aber dann habe ich den abgerufenen Zeiger als Schnittstelle B umgewandelt. Dies führte dazu, dass ein Funktionsaufruf mit einer vollständigen Signatur ausgeführt wurde, die den Stack ( und ESP) durcheinander gebracht.

Durch Übergeben der GUID für Schnittstelle B wurde das Problem behoben.

1
Neil Lamoureux

hast du irgendwelche typedef'd Funktionsprototypen (zB int (* fn) (int a, int b))

wenn Sie dom sind, haben Sie möglicherweise den Prototyp falsch verstanden.

ESP ist ein Fehler beim Aufruf einer Funktion (können Sie feststellen, welche im Debugger?) Die Parameter nicht übereinstimmt - dh der Stack hat den Zustand wiederhergestellt, in dem er beim Aufruf der Funktion gestartet wurde.

Sie können dies auch erhalten, wenn Sie C++ - Funktionen laden, die extern deklariert werden müssen. C-C verwendet cdecl. C++ verwendet standardmäßig die Aufrufkonvention stdcall (IIRC). Legen Sie einige externe C-Wrapper um die importierten Funktionsprototypen, und Sie können dies beheben.

Wenn Sie es im Debugger ausführen können, wird die Funktion sofort angezeigt. Wenn nicht, können Sie DrWtsn32 so einrichten, dass ein Minidump erstellt wird, den Sie in windbg laden können, um den Callstack zum Zeitpunkt des Fehlers anzuzeigen (Sie benötigen jedoch Symbole oder eine Map-Datei, um die Funktionsnamen zu sehen).

1
gbjbaanb

Ich hatte genau diesen Fehler, nachdem ich Funktionen in eine DLL verschoben und die DLL mit LoadLibrary und GetProcAddress dynamisch geladen hatte. Ich hatte wegen der Verzierung extern "C" für die Funktion in der DLL erklärt. Das hat sich also auch in __cdecl geändert. Ich habe Funktionszeiger zu __stdcall im Ladecode erklärt. Nachdem ich den Funktionszeiger im Ladecode von __stdcall in __cdecl geändert hatte, verschwand der Laufzeitfehler.

1
Waldo Alvarez

Sie würden diesen Fehler erhalten, wenn die Funktion mit einer anderen Aufrufkonvention als der aufgerufen wird, zu der sie kompiliert wird.

Visual Studio verwendet eine Standardeinstellung für die Aufrufkonvention, die in den Optionen des Projekts dekaliert wird. Prüfen Sie, ob dieser Wert in den ursprünglichen Projekteinstellungen und in den neuen Bibliotheken gleich ist. Ein übermäßig ehrgeiziger Dev hätte dies im Original auf _stdcall/Pascal setzen können, da er die Codegröße im Vergleich zum Standard-cdecl reduziert. Der Basisprozess würde also diese Einstellung verwenden und die neuen Bibliotheken erhalten den Standard-cdecl, der das Problem verursacht

Da Sie gesagt haben, dass Sie keine speziellen Aufrufkonventionen verwenden, scheint dies eine gute Wahrscheinlichkeit zu sein.

Machen Sie auch einen Vergleich der Header, um zu sehen, ob die Deklarationen/Dateien, die der Prozess sieht, die gleichen sind, mit denen die Bibliotheken kompiliert werden.

ps: Die Warnung erlischt, ist BAAAD. Der zugrunde liegende Fehler bleibt bestehen.

1
computinglife

Wenn Sie Rückruffunktionen mit der Windows-API verwenden, müssen diese mit CALLBACK und/oder WINAPI deklariert werden. Dadurch werden geeignete Dekorationen angewendet, damit der Compiler Code generiert, der den Stapel ordnungsgemäß löscht. Auf dem Compiler von Microsoft wird beispielsweise __stdcall hinzugefügt.

Windows hat immer die __stdcall-Konvention verwendet, da dies zu (etwas) kleinerem Code führt, wobei die Bereinigung in der aufgerufenen Funktion und nicht an jeder Aufrufstelle stattfindet. Es ist jedoch nicht mit den Varargs-Funktionen kompatibel (da nur der Aufrufer weiß, wie viele Argumente er gedrückt hat).

0
Mike Dimmick

ESP ist der Stapelzeiger. Laut dem Compiler wird Ihr Stack-Pointer also durcheinander gebracht. Es ist schwer zu sagen, wie (oder ob) dies geschehen könnte, ohne Code zu sehen.

Welches ist das kleinste Codesegment, das Sie erhalten können, um dies zu reproduzieren?

0
Nick

Hier ist ein reduziertes C++ - Programm, das diesen Fehler erzeugt. Kompiliert mit (Microsoft Visual Studio 2003) führt der oben genannte Fehler. 

#include "stdafx.h"
char* blah(char *a){
  char p[1];
  strcat(p, a);
  return (char*)p;
}
int main(){
  std::cout << blah("a");
  std::cin.get();
}

FEHLER: "Laufzeitprüfung fehlgeschlagen # 0 - Der Wert von ESP wurde bei einem Funktionsaufruf nicht ordnungsgemäß gespeichert. Dies ist in der Regel das Aufrufen einer Funktion, die mit einer Aufrufkonvention mit einem deklarierten Funktionszeiger deklariert wurde mit einer anderen Aufrufkonvention. "

0
Eric Leschinski

Nicht die beste Antwort, aber ich habe einfach meinen Code von Grund auf neu kompiliert (in VS neu aufbauen) und dann ist das Problem behoben.

0
Kobbe

Ich hatte das gleiche Problem hier bei der Arbeit. Ich habe einen sehr alten Code aktualisiert, der einen FARPROC-Funktionszeiger aufrief. Wenn Sie es nicht wissen, sind FARPROC Funktionszeiger mit ZERO-Sicherheit. Es ist das C-Äquivalent eines typdef-Funktionszeigers, ohne dass der Compiler-Typ überprüft wird. Zum Beispiel, Sie haben eine Funktion, die 3 Parameter benötigt. Sie zeigen einen FARPROC darauf und rufen ihn dann mit 4 statt mit 3 Parametern auf. Der zusätzliche Parameter drückte zusätzlichen Müll auf den Stapel, und wenn er abfällt, unterscheidet sich ESP jetzt vom Start. Also löste ich es, indem ich den zusätzlichen Parameter für den Aufruf des FARPROC-Funktionsaufrufs entfernte. 

0
C Johnson