Errore strano di MSC 8.0: “Il valore di ESP non è stato salvato correttamente attraverso una chiamata di funzione …”

Recentemente abbiamo tentato di suddividere alcuni dei nostri progetti di Visual Studio in librerie e tutto sembrava compilare e build bene in un progetto di test con uno dei progetti di libreria come dipendenza. Tuttavia, il tentativo di eseguire l’applicazione ci ha dato il seguente brutto messaggio di errore in fase di esecuzione:

Errore in fase di esecuzione # 0 – Il valore di ESP non è stato salvato correttamente attraverso una chiamata di funzione. Questo di solito è il risultato della chiamata a un puntatore di funzione dichiarato con una convenzione di chiamata diversa.

Non abbiamo mai nemmeno specificato le convenzioni di chiamata (__cdecl ecc.) Per le nostre funzioni, lasciando tutte le opzioni del compilatore sull’impostazione predefinita. Ho controllato e le impostazioni del progetto sono coerenti per chiamare la convenzione attraverso la libreria e testare i progetti.

Aggiornamento: uno dei nostri sviluppatori ha modificato l’impostazione del progetto “Basic Runtime Checks” da “Entrambi (/ RTC1, equiv. A / RTCsu)” a “Predefinito” e il tempo di esecuzione è svanito, lasciando il programma in esecuzione in modo apparentemente corretto. Non mi fido affatto di questo. Era una soluzione adeguata o un trucco pericoloso?

Questo errore di debug significa che il registro del puntatore dello stack non viene restituito al suo valore originale dopo la chiamata alla funzione, ovvero che il numero di push prima della chiamata alla funzione non è stato seguito dal numero uguale di pop dopo la chiamata.

Ci sono 2 ragioni per questo che so (entrambe con librerie caricate dynamicmente). # 1 è ciò che VC ++ sta descrivendo nel messaggio di errore, ma non penso che questa sia la causa più frequente dell’errore (vedere # 2).

1) convenzioni di chiamata non corrispondenti:

Il chiamante e il chiamato non hanno un accordo adeguato su chi farà cosa. Ad esempio, se stai chiamando una funzione DLL che è _stdcall , ma per qualche motivo lo hai dichiarato come _cdecl (predefinito in VC ++) nella chiamata. Questo succederebbe molto se usi lingue diverse in moduli diversi, ecc.

Dovresti controllare la dichiarazione della funzione incriminata e assicurarti che non sia dichiarata due volte e diversamente.

2) Tipi non corrispondenti:

Il chiamante e il chiamato non sono compilati con gli stessi tipi. Ad esempio, un’intestazione comune definisce i tipi nell’API ed è stata modificata di recente e un modulo è stato ricompilato, mentre l’altro non lo è, ad esempio alcuni tipi potrebbero avere una dimensione diversa nel chiamante e nel chiamato.

In tal caso, il chiamante preme gli argomenti di una dimensione, ma il chiamato (se si sta usando _stdcall cui il destinatario cancella la pila) fa apparire le diverse dimensioni. L’ESP non è, quindi, restituito al valore corretto.

(Naturalmente, questi argomenti e altri sotto di loro sembrerebbero confusi nella funzione chiamata, ma a volte puoi sopravvivere senza un arresto visibile).

Se hai accesso a tutto il codice, semplicemente ricompilalo.

Ho letto questo in altri forum

Stavo avendo lo stesso problema, ma ho solo FISSO. Stavo ottenendo lo stesso errore dal seguente codice:

 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. 

Dopo alcune indagini, ho modificato una delle righe per:

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

che ha risolto il problema. Se dai un'occhiata al file di intestazione in cui si trova SetSuspendState (powrprof.h, parte dell'SDK), vedrai che la funzione prototype è definita come:

 BOOLEAN WINAPI SetSuspendState(BOOLEAN, BOOLEAN, BOOLEAN); 

Quindi voi ragazzi avete un problema simile. Quando si chiama una determinata funzione da un dll, la sua firma probabilmente non è triggers. (Nel mio caso era la parola chiave WINAPI mancante).

Spero che aiuti le persone future! 🙂

Saluti.

Distriggersre l’assegno non è la soluzione giusta. Devi capire cosa è incasinato con le tue convenzioni di chiamata.

Esistono diversi modi per modificare la convocazione di chiamata di una funzione senza specificarla esplicitamente. extern “C” lo farà, anche STDMETHODIMP / IFACEMETHODIMP lo farà, anche altre macro potrebbero farlo.

Credo che se si esegue il programma in WinDBG ( http://www.microsoft.com/whdc/devtools/debugging/default.mspx ), il runtime dovrebbe interrompersi nel punto in cui si verifica il problema. È ansible esaminare lo stack di chiamate e capire quale funzione presenta il problema, quindi osservare la sua definizione e la dichiarazione utilizzata dal chiamante.

Ho visto questo errore quando il codice ha provato a chiamare una funzione su un object che non era del tipo previsto.

Quindi, gerarchia di classi: padre con figli: Child1 e Child2

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

Stavo ricevendo un errore simile per le API di AutoIt che stavo chiamando dal programma VC ++.

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

Tuttavia, quando ho modificato la dichiarazione che include WINAPI, come suggerito in precedenza nel thread, il problema è scomparso.

Il codice senza errori è simile a:

 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); } 

Stavo ricevendo questo errore chiamando una funzione in una DLL che è stata compilata con una versione pre-2005 di Visual C ++ da una versione più recente di VC (2008). La funzione aveva questa firma:

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

Il problema era che la dimensione di time_t è di 32 bit nella versione precedente al 2005, ma 64 bit rispetto a VS2005 (è definita come _time64_t ). Il richiamo della funzione prevede una variabile a 32 bit ma ottiene una variabile a 64 bit quando viene chiamato da VC> = 2005. Poiché i parametri delle funzioni vengono passati tramite lo stack quando si utilizza la convenzione di chiamata WINAPI , questo danneggia lo stack e genera il messaggio di errore sopra menzionato (“Errore in fase di esecuzione # 0 …”).

Per risolvere questo, è ansible

 #define _USE_32BIT_TIME_T 

prima di includere il file di intestazione della DLL o – meglio – modificare la firma della funzione nel file di intestazione in base alla versione VS (le versioni precedenti al 2005 non conoscono _time32_t !):

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

Si noti che è necessario utilizzare _time32_t anziché time_t nel programma chiamante, ovviamente.

Stai creando libs o DLL statici? Se DLL, come sono definite le esportazioni; come vengono create le librerie di importazione?

I prototipi delle funzioni nelle librerie sono esattamente identici alle dichiarazioni di funzioni in cui sono definite le funzioni?

hai qualche prototipo di funzione typedef (es. int (* fn) (int a, int b))

se domini potresti aver sbagliato il prototipo.

ESP è un errore sul richiamo di una funzione (puoi dire quale nel debugger?) Che ha una mancata corrispondenza nei parametri – cioè lo stack è tornato allo stato in cui è iniziato quando hai chiamato la funzione.

Puoi anche ottenere questo se stai caricando funzioni C ++ che devono essere dichiarate extern C – C usa cdecl, C ++ usa la convenzione di chiamata stdcall per impostazione predefinita (IIRC). Metti alcuni wrapper C esterni attorno ai prototipi delle funzioni importate e potresti correggerlo.

Se riesci a eseguirlo nel debugger, vedrai la funzione immediatamente. In caso contrario, puoi impostare DrWtsn32 per creare un minidump che puoi caricare in windbg per vedere il callstack al momento dell’errore (avrai bisogno di simboli o un mapfile per vedere però i nomi delle funzioni).

Un altro caso in cui esp può essere incasinato è con un buffer overflow involontario, di solito attraverso l’uso errato di puntatori per lavorare oltre il limite di un array. Supponi di avere una funzione C simile

 int a, b[2]; 

Scrivere su b[3] probabilmente cambierà a , e in qualsiasi luogo oltre che è probabile che convoglia l’ esp salvato in pila.

Si otterrebbe questo errore se la funzione è invocata con una convenzione di chiamata diversa da quella a cui è stata compilata.

Visual Studio utilizza un’impostazione di convenzione di chiamata predefinita che viene decalificata nelle opzioni del progetto. Controlla se questo valore è lo stesso nelle impostazioni originali del progetto e nelle nuove librerie. Uno sviluppatore troppo ambizioso potrebbe averlo impostato su _stdcall / pascal nell’originale in quanto riduce la dimensione del codice rispetto al cdecl predefinito. Quindi il processo di base userebbe questa impostazione e le nuove librerie avranno il cdecl predefinito che causa il problema

Poiché hai detto che non usi convenzioni di chiamata speciali, questa sembra essere una buona probabilità.

Fai anche una diff sulle intestazioni per vedere se le dichiarazioni / file che il processo vede sono gli stessi con cui sono state compilate le librerie.

ps: Rendere l’avvertimento andare via è BAAAD. l’errore sottostante persiste ancora.

Questo mi è successo durante l’accesso a un object COM (Visual Studio 2010). Ho passato il GUID per un’altra interfaccia A per la mia chiamata a QueryInterface, ma poi ho lanciato il puntatore recuperato come interfaccia B. Ciò ha comportato l’esecuzione di una chiamata di funzione a una con una firma interamente, che tiene conto dello stack (e dell’ESP) incasinato.

Passando il GUID per l’interfaccia B risolto il problema.

Stavo avendo questo stesso errore dopo aver spostato le funzioni in una DLL e caricato dynamicmente la DLL con LoadLibrary e GetProcAddress. Avevo dichiarato “C” externale per la funzione nella dll a causa della decorazione. Così ha cambiato anche la convenzione di chiamata su __cdecl. Stavo dichiarando che i puntatori di funzione sono __stdcall nel codice di caricamento. Una volta modificato il puntatore della funzione da __stdcall a__cdecl nel codice di caricamento, l’errore di runtime è andato via.

Nella mia app MFC C ++ ho riscontrato lo stesso problema riportato nell’errore di Weird MSC 8.0: “Il valore di ESP non è stato salvato correttamente attraverso una chiamata di funzione …” . Il post ha oltre 42.000 visualizzazioni e 16 risposte / commenti, nessuna delle quali ha incolpato il compilatore come problema. Almeno nel mio caso posso dimostrare che il compilatore VS2015 è in errore.

La mia configurazione di prova e test è la seguente: Ho 3 PC che eseguono tutti la versione 10.0.10586 di Win10. Tutti stanno compilando con VS2015, ma qui è la differenza. Due dei VS2015 hanno l’aggiornamento 2 mentre l’altro ha l’aggiornamento 3 applicato. Il PC con Update 3 funziona, ma gli altri due con Update 2 non riescono con lo stesso errore riportato nel post in alto. Il mio codice app MFC C ++ è esattamente lo stesso su tutti e tre i PC.

Conclusione: almeno nel mio caso per la mia app la versione del compilatore (Aggiornamento 2) conteneva un bug che ha infranto il mio codice. La mia app fa un uso pesante di std :: packaged_task quindi mi aspetto che il problema riguardasse un codice compilatore abbastanza nuovo.

Vale la pena sottolineare che questo può anche essere un bug di Visual Studio.

Ho ricevuto questo problema su VS2017, Win10 x64. All’inizio aveva senso, dal momento che stavo facendo cose strane gettando questo su un tipo derivato e avvolgendolo in un lambda. Tuttavia, ho ripristinato il codice su un commit precedente e ho ancora ricevuto l’errore, anche se non c’era prima.

Ho provato a riavviare e quindi a ribuild il progetto, quindi l’errore è andato via.

ESP è il puntatore dello stack. Quindi, secondo il compilatore, il puntatore dello stack si sta incasinando. È difficile dire come (o se) ciò potrebbe accadere senza vedere alcun codice.

Qual è il segmento di codice più piccolo che puoi ottenere per riprodurre questo?

Se si utilizzano funzioni di callback con l’API di Windows, devono essere dichiarate utilizzando CALLBACK e / o WINAPI . Ciò applicherà le decorazioni appropriate per fare in modo che il compilatore generi un codice che pulisca correttamente la pila. Ad esempio, sul compilatore Microsoft aggiunge __stdcall .

Windows ha sempre usato la convenzione __stdcall perché porta a un codice (leggermente) più piccolo, con la pulizia che avviene nella funzione chiamata piuttosto che in ogni sito di chiamata. Tuttavia, non è compatibile con le funzioni varargs (perché solo il chiamante conosce quanti argomenti hanno premuto).

Ecco un programma C ++ ridotto che produce quell’errore. Compilato utilizzando (Microsoft Visual Studio 2003) produce l’errore sopra indicato.

 #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(); } 

ERRORE: "Errore di controllo in fase di esecuzione # 0 - Il valore di ESP non è stato salvato correttamente attraverso una chiamata di funzione, di solito è il risultato di chiamare una funzione dichiarata con una convenzione di chiamata con un puntatore di funzione dichiarato con una convenzione di chiamata diversa."

Ho avuto questo stesso problema qui al lavoro. Stavo aggiornando un codice molto vecchio che stava chiamando un puntatore a funzione FARPROC. Se non lo sai, i FARPROC sono puntatori di funzioni con sicurezza di tipo ZERO. È l’equivalente C di un puntatore a funzione typdef, senza il controllo del tipo di compilatore. Ad esempio, supponiamo di avere una funzione che richiede 3 parametri. Puntate un FARPROC su di esso, quindi chiamatelo con 4 parametri anziché 3. Il parametro extra ha spinto extra garbage nello stack, e quando si apre, ESP è ora diverso da quando è stato avviato. Quindi l’ho risolto rimuovendo il parametro extra al richiamo della chiamata alla funzione FARPROC.

Non è la migliore risposta, ma ho appena ricompilato il mio codice da zero (ricostruzione in VS) e poi il problema è andato via.