web-dev-qa-db-fra.com

Bizarre MSC 8.0 erreur: "La valeur de ESP n’a pas été correctement enregistré lors d’un appel de fonction ... "

Nous avons récemment tenté de scinder certains de nos projets Visual Studio en bibliothèques, et tout semblait compiler et construire correctement dans un projet de test avec l'un des projets de bibliothèque en tant que dépendance. Cependant, tenter d'exécuter l'application nous a donné le message d'erreur d'exécution désagréable suivant:

Échec de la vérification à l'exécution n ° 0 - La valeur de ESP n'a pas été correctement enregistrée lors d'un appel de fonction. Cela est généralement le résultat de l'appel d'un pointeur de fonction déclaré avec une convention d'appel différente.

Nous n'avons même jamais spécifié de conventions d'appel (__cdecl, etc.) pour nos fonctions, laissant tous les commutateurs du compilateur sur la valeur par défaut. J'ai vérifié et les paramètres du projet sont cohérents pour la convention d'appel de la bibliothèque et des projets de test.

Mise à jour: l'un de nos développeurs a modifié le paramètre de projet "Vérifications d'exécution de base" de "Les deux (/ RTC1, équivalent à/RTCsu)" à "Par défaut" et l'exécution a disparu, laissant le programme apparemment en marche correctement. Je ne crois pas du tout cela. Était-ce une solution appropriée ou un piratage dangereux?

45
user11180

Cette erreur de débogage signifie que le registre du pointeur de pile n'est pas revenu à sa valeur d'origine après l'appel de la fonction, c'est-à-dire que le nombre de pushs avant l'appel de fonction n'a pas été suivi du nombre égal de pops après le appel.

Je connais deux raisons à cela (toutes deux avec des bibliothèques chargées dynamiquement). N ° 1 est ce que VC++ décrit dans le message d'erreur, mais je ne pense pas que ce soit la cause la plus fréquente de l'erreur (voir n ° 2).

1) Conventions d'appel incompatibles:

L'appelant et l'appelé ne sont pas convenus de savoir qui fera quoi. Par exemple, si vous appelez une fonction DLL qui est _stdcall, mais que, pour une raison quelconque, vous l'avez déclarée en tant que _cdecl (valeur par défaut dans VC++) dans votre appel. Cela se produirait souvent si vous utilisiez différentes langues dans différents modules, etc. 

Vous devez vérifier la déclaration de la fonction incriminée et vous assurer qu'elle n'est pas déclarée deux fois, et différemment.

2) Types incompatibles:

L'appelant et l'appelé ne sont pas compilés avec les mêmes types. Par exemple, un en-tête commun définit les types dans l'API et a récemment changé. Un module a été recompilé, mais l'autre n'était pas - c'est-à-dire. certains types peuvent avoir une taille différente dans l'appelant et dans l'appelé.

Dans ce cas, l'appelant pousse les arguments d'une taille, mais l'appelé (si vous utilisez _stdcall où l'appelant nettoie la pile) affiche une taille différente. Le ESP n'est donc pas renvoyé à la valeur correcte.

(Bien sûr, ces arguments, et d’autres en-dessous, sembleraient confus dans la fonction appelée, mais vous pouvez parfois survivre sans un crash visible.)

Si vous avez accès à tout le code, recompilez-le simplement.

47
Nikola Gedelovski

Je lis ceci dans un autre forum

J'avais le même problème, mais je l'ai juste FIXÉ. Je recevais la même erreur du code suivant:

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. 

Après une enquête, j'ai changé l'une des lignes pour:

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

qui a résolu le problème. Si vous regardez dans le fichier d’en-tête où se trouve SetSuspendState (powrprof.h, faisant partie du SDK), vous verrez que le prototype de la fonction est défini comme suit: 

BOOLEAN WINAPI SetSuspendState(BOOLEAN, BOOLEAN, BOOLEAN);

Donc, vous rencontrez un problème similaire. Lorsque vous appelez une fonction donnée à partir d'un fichier .dll, sa signature est probablement désactivée. (Dans mon cas, c'était le mot clé WINAPI manquant). 

Espérons que cela aide les futurs peuples! :-)

À votre santé.

18
Khaled

Faire taire le chèque n’est pas la bonne solution. Vous devez comprendre ce qui est brouillé avec vos conventions d'appel.

Il y a pas mal de façons de changer la convocation d'une fonction sans la spécifier explicitement. extern "C" le fera, STDMETHODIMP/IFACEMETHODIMP le fera également, d'autres macros pourraient également le faire.

Je crois que si vous exécutez votre programme sous WinDBG ( http://www.Microsoft.com/whdc/devtools/debugging/default.mspx ), le temps d’exécution devrait s’arrêter au point où vous rencontrez ce problème. Vous pouvez consulter la pile d'appels et déterminer quelle fonction pose le problème, puis consulter sa définition et la déclaration utilisée par l'appelant.

11
Franci Penov

J'ai vu cette erreur lorsque le code a tenté d'appeler une fonction sur un objet qui n'était pas du type attendu.

Donc, hiérarchie de classe: Parent avec enfants: Enfant1 et Enfant2

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

J'obtenais une erreur similaire pour les API AutoIt que j'appelais depuis le programme VC++.

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

Cependant, lorsque j'ai changé la déclaration qui inclut WINAPI, comme suggéré précédemment dans le fil de discussion, le problème a disparu.

Le code sans erreur ressemble à ceci:

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

Il convient de souligner que cela peut également être un bogue de Visual Studio.

J'ai eu ce problème sur VS2017, Win10 x64. Au début, cela avait du sens, étant donné que je faisais des choses étranges, transformant cela en un type dérivé et l’enveloppant dans un lambda. Cependant, j'ai rétabli le code par un code précédent et j'ai toujours l'erreur, même si elle n'y était pas auparavant. 

J'ai essayé de redémarrer puis de reconstruire le projet, puis l'erreur a disparu.

2
stands2reason

J'obtenais cette erreur en appelant une fonction dans une DLL compilée avec une version antérieure à 2005 de Visual C++ à partir d'une version plus récente de VC (2008) . avait cette signature:

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

Le problème était que la taille de time_t est de 32 bits dans la version antérieure à 2005, mais de 64 bits depuis VS2005 (définie comme _time64_t). L'appel de la fonction attend une variable de 32 bits, mais obtient une variable de 64 bits lorsqu'il est appelé depuis VC> = 2005. Les paramètres des fonctions étant transmis via la pile lors de l'utilisation de la convention d'appel WINAPI, la pile est corrompue et génère le message d'erreur mentionné ci-dessus ("Échec de la vérification au moment de l'exécution # 0 ...").

Pour résoudre ce problème, il est possible de

#define _USE_32BIT_TIME_T

avant d'inclure le fichier d'en-tête de la DLL ou - mieux - modifiez la signature de la fonction dans le fichier d'en-tête en fonction de la version du VS (les versions antérieures à 2005 ne connaissent pas _time32_t!):

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

Notez que vous devez bien sûr utiliser _time32_t au lieu de time_t dans le programme appelant.

2
oli_arborum

Un autre cas où esp peut être gâché est un dépassement de tampon par inadvertance, généralement dû à l'utilisation erronée de pointeurs pour dépasser la limite d'un tableau. Disons que vous avez une fonction C qui ressemble à

int a, b[2];

Écrire dans b[3] changera probablement a, et n'importe où dans le passé, risquerait de placer la esp sauvegardée sur la pile.

1
Alex M

Dans mon application MFC C++, je rencontre le même problème que celui signalé dans Erreur étrange MSC 8.0: «La valeur de ESP n'a pas été correctement enregistrée dans un appel de fonction…» . L’affichage compte plus de 42 000 vues et 16 réponses/commentaires, dont aucun n’est imputé au compilateur comme étant le problème. Au moins dans mon cas, je peux montrer que le compilateur VS2015 est en faute.

Ma configuration de test et test est la suivante: J'ai 3 PC qui exécutent tous Win10 version 10.0.10586. Tous compilent avec VS2015, mais voici la différence. Deux des VS2015 ont la mise à jour 2 tandis que l'autre a la mise à jour 3 appliquée. Le PC avec la mise à jour 3 fonctionne, mais les deux autres avec la mise à jour 2 échouent avec la même erreur que celle signalée dans l'affichage ci-dessus. Le code de mon application MFC C++ est exactement le même sur les trois ordinateurs.

Conclusion: au moins dans mon cas pour mon application, la version du compilateur (Update 2) contenait un bogue qui cassait mon code. Mon application utilise beaucoup std :: packaged_task, je suppose donc que le problème vient de ce nouveau code de compilation.

1
rtischer8277

Créez-vous des bibliothèques statiques ou des DLL? Si les DLL sont définies, comment les exportations sont-elles définies? Comment sont créées les bibliothèques d'importation?

Les prototypes des fonctions dans les bibliothèquesexactementsont-ils identiques aux déclarations de fonction dans lesquelles les fonctions sont définies?

1
Michael Burr

Cela m'est arrivé lors de l'accès à un objet COM (Visual Studio 2010). J'ai passé le GUID pour une autre interface A dans mon appel à QueryInterface, mais j’ai ensuite converti le pointeur récupéré en interface B. Cela a abouti à l’appel d’une fonction avec une signature complète, qui rend compte de la pile ( et ESP) être foiré.

Le passage du GUID pour l'interface B a résolu le problème.

1
Neil Lamoureux

avez-vous des prototypes de fonctions typedef'd (par exemple int (* fn) (int a, int b))

si vous avez peut-être mal compris le prototype.

ESP est une erreur lors de l'appel d'une fonction (pouvez-vous indiquer laquelle dans le débogueur?) Si les paramètres ne correspondent pas, c'est-à-dire que la pile est revenue à l'état dans lequel elle avait démarrée lorsque vous avez appelé la fonction.

Vous pouvez également l'obtenir si vous chargez des fonctions C++ devant être déclarées extern C - C utilise cdecl, C++ utilise la convention d'appel stdcall par défaut (IIRC). Placez des enveloppes C externes sur les prototypes de fonctions importés et corrigez-les.

Si vous pouvez l'exécuter dans le débogueur, vous verrez immédiatement la fonction. Si ce n'est pas le cas, vous pouvez configurer DrWtsn32 pour créer un mini-vidage que vous pouvez charger dans windbg pour voir la pile d'appels au moment de l'erreur (vous aurez besoin de symboles ou d'un fichier map pour voir les noms de fonctions).

1
gbjbaanb

J'avais exactement la même erreur après avoir déplacé des fonctions vers une dll et chargé dynamiquement la dll avec LoadLibrary et GetProcAddress. J'avais déclaré extern "C" pour la fonction dans la DLL à cause de la décoration. Cela a donc changé la convention d'appel en __cdecl également. Je déclarais que les pointeurs de fonction étaient __stdcall dans le code de chargement. Une fois que j'ai changé le pointeur de la fonction de __stdcall à __cdecl dans le code de chargement, l'erreur d'exécution a disparu.

1
Waldo Alvarez

Vous obtiendrez cette erreur si la fonction est appelée avec une convention d'appel différente de celle pour laquelle elle est compilée.

Visual Studio utilise un paramètre de convention d'appel par défaut qui est décalé dans les options du projet. Vérifiez si cette valeur est la même dans les paramètres du projet original et dans les nouvelles bibliothèques. Un développeur trop ambitieux aurait pu définir _stdcall/Pascal dans l’original, car cela réduirait la taille du code par rapport au cdecl par défaut. Donc, le processus de base utilise ce paramètre et les nouvelles bibliothèques obtiennent le cdecl par défaut, ce qui cause le problème.

Puisque vous avez dit que vous n'utilisiez aucune convention d'appel spéciale, cela semble être une bonne probabilité.

Faites également un diff sur les en-têtes pour voir si les déclarations/fichiers que le processus voit sont les mêmes que ceux avec lesquels les bibliothèques sont compilées.

ps: Faire disparaître l'avertissement, c'est BAAAD. l'erreur sous-jacente persiste toujours.

1
computinglife

Si vous utilisez des fonctions de rappel avec l'API Windows, celles-ci doivent être déclarées avec CALLBACK et/ou WINAPI. Cela appliquera les décorations appropriées pour que le compilateur génère un code qui nettoie correctement la pile. Par exemple, sur le compilateur de Microsoft, il ajoute __stdcall.

Windows a toujours utilisé la convention __stdcall car elle conduit à un code (légèrement) plus petit, le nettoyage ayant lieu dans la fonction appelée plutôt que sur chaque site d'appel. Cependant, ce n'est pas compatible avec les fonctions varargs (parce que seul l'appelant sait combien d'arguments il a poussé).

0
Mike Dimmick

ESP est le pointeur de pile. Donc, selon le compilateur, votre pointeur de pile est perturbé. Il est difficile de dire comment (ou si) cela pourrait se produire sans voir du code.

Quel est le plus petit segment de code que vous pouvez obtenir pour reproduire cela?

0
Nick

Voici un programme C++ simplifié qui génère cette erreur. Compilé à l'aide de (Microsoft Visual Studio 2003) produit l'erreur mentionnée ci-dessus. 

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

ERREUR: "Échec de la vérification au moment de l'exécution # 0 - La valeur de ESP n'a pas été correctement enregistrée lors d'un appel de fonction. Cela est généralement le résultat de l'appel d'une fonction déclarée avec une convention d'appel avec un pointeur déclaré avec une convention d'appel différente ".

0
Eric Leschinski

Pas la meilleure réponse mais je viens de recompiler mon code à partir de zéro (reconstruit en VS) et ensuite le problème est parti.

0
Kobbe

J'ai eu le même problème ici au travail. Je mettais à jour un très ancien code qui appelait un pointeur de fonction FARPROC. Si vous ne le savez pas, les FARPROC sont des indicateurs de fonction avec une sécurité de type ZERO. C'est l'équivalent C d'un pointeur de fonction typdef, sans vérification du type de compilateur. Par exemple, disons que vous avez une fonction qui prend 3 paramètres. Vous pointez un FARPROC sur celui-ci, puis vous l'appelez avec 4 paramètres au lieu de 3. Le paramètre supplémentaire a placé des déchets superflus dans la pile et lorsqu'il est supprimé, ESP est maintenant différent de son démarrage. Donc, je l'ai résolu en supprimant le paramètre supplémentaire à l'appel de la fonction FARPROC. 

0
C Johnson