54 votes

Erreur bizarre du MSC 8.0 : "La valeur de ESP n'a pas été correctement enregistrée lors d'un appel de fonction..."

Nous avons récemment tenté de diviser certains de nos projets Visual Studio en bibliothèques, et tout semblait bien se compiler et se construire dans un projet de test avec l'un des projets de bibliothèque comme dépendance. Cependant, en essayant d'exécuter l'application, nous avons reçu le message d'erreur d'exécution suivant :

Run-Time Check Failure #0 - La valeur de l'ESP n'a pas été correctement enregistrée lors d'un appel de fonction. C'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 pas spécifié les 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 les conventions d'appel dans la bibliothèque et les projets de test.

Mise à jour : Un de nos développeurs a changé le paramètre du projet "Basic Runtime Checks" de "Both (/RTC1, equiv. to /RTCsu)" à "Default" et le run-time a disparu, laissant le programme fonctionner apparemment correctement. Je n'ai pas du tout confiance en cela. S'agit-il d'une solution appropriée ou d'un piratage dangereux ?

4 votes

Soyez heureux que le runtime l'ait détecté pour vous. S'il ne l'avait pas fait, la prochaine chose que ferait l'ordinateur serait de déchiqueter le contenu de la pile et de se planter d'une manière épouvantable. (Déboguer une corruption de pile n'est pas pour les âmes sensibles).

0 votes

Concernant votre mise à jour : Non, ce n'est pas une bonne solution. Tout ce que vous avez fait est de désactiver les contrôles. C'est un peu comme faire l'autruche. Le problème est toujours là, et il vous explosera sans aucun doute à la figure plus tard, lorsqu'il sera encore plus difficile de le retrouver.

55voto

Nikola Gedelovski Points 425

Cette erreur de débogage signifie que le registre des pointeurs de pile n'est pas remis à sa valeur d'origine après l'appel de fonction, c'est-à-dire que le nombre de pousse avant l'appel de fonction n'ont pas été suivis du même nombre de pops après l'appel.

Il y a deux raisons à cela que je connais (toutes deux avec des bibliothèques chargées dynamiquement). #La première 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 #2).

1) Conventions d'appel non concordantes :

L'appelant et l'appelé ne se sont pas mis d'accord sur qui va faire quoi. Par exemple, si vous appelez une fonction de la DLL qui est _stdcall mais, pour une raison quelconque, vous l'avez déclaré comme une _cdecl (par défaut dans VC++) dans votre appel. Cela se produit souvent si vous utilisez des langues différentes dans des modules différents, etc.

Il faudrait inspecter la déclaration de la fonction incriminée et s'assurer qu'elle n'est pas déclarée deux fois, et différemment.

2) Types mal assortis :

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é, et un module a été recompilé, mais pas l'autre - c'est-à-dire que 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 l'option _stdcall où le destinataire du message nettoie la pile) ouvre la taille différente. L'ESP n'est donc pas remis à la valeur correcte.

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

Si vous avez accès à l'ensemble du code, il suffit de le recompiler.

0 votes

+1 bonne explication, ce serait parfait si tu mettais quelques exemples de code pour le guider

1 votes

J'ai eu la même exception, mais aucun des cas ci-dessus n'était le cas. Je me suis battu pendant plusieurs heures jusqu'à ce que je trouve le problème dans une fonction dont l'argument est un pointeur vers une fonction membre d'une autre classe. L'appel de cette fonction provoquait une corruption de la pile. La solution à ce genre de problème peut être trouvée ici : stackoverflow.com/questions/8676879/

0 votes

Possibilité 3 - noms mal assortis lors de la récupération d'un pointeur de fonction (peut-être via un appel à getProcAddress("theWrongFuntionName"). C'est ce que j'ai fait ! Ce qui s'est passé : J'ai lié un pointeur vers la fonction nommée à un prototype de pointeur de fonction (via un typedef). Tout semble correct - pas d'erreur de compilation, mais vous appelez la mauvaise fonction au moment de l'exécution. Je suppose que vous devez être assez malchanceux pour taper un nom qui existe réellement dans votre dll, mais qui n'est pas celui que vous voulez, sinon vous serez sauvé et obtiendrez null en retour de getProcAddress().

20voto

Khaled Points 101

J'ai lu ceci dans un autre forum

J'avais le même problème, mais je viens de le résoudre. J'obtenais la même erreur avec le 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 quelques recherches, j'ai changé une des lignes en :

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

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

BOOLEAN WINAPI SetSuspendState(BOOLEAN, BOOLEAN, BOOLEAN);

Vous rencontrez donc un problème similaire. Lorsque vous appelez une fonction donnée à partir d'une .dll, sa signature est probablement erronée. (Dans mon cas, il s'agissait du mot-clé WINAPI manquant).

J'espère que cela aidera les futures personnes ! :-)

A la vôtre.

0 votes

"Dans mon cas, c'était le mot-clé WINAPI manquant". - Ce n'est pas un mot-clé. C'est un symbole de préprocesseur, qui s'étend à la convention d'appel. Une question sur les conventions d'appel mal assorties devrait au moins contenir le terme "convention d'appel" .

0 votes

C'est exactement le problème que j'ai rencontré avec le compound type ou quel que soit son nom actuel. Je ne savais pas où mettre WINAPI et je l'ai simplement laissé de côté lors du chargement explicite de la dll pour obtenir un message d'erreur. D3D12GetDebugInterface() . Je m'étais amusé avec des arguments mais c'était exactement comme vous l'aviez dit avec le winapi.

11voto

Franci Penov Points 45358

Faire taire le chèque n'est pas la bonne solution. Vous devez trouver ce qui ne va pas dans vos conventions d'appel.

Il y a plusieurs façons de changer la convetion d'appel d'une fonction sans la spécifier explicitement. extern "C" le fera, STDMETHODIMP/IFACEMETHODIMP le fera aussi, d'autres macros peuvent le faire aussi.

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

6voto

Tinclon Points 467

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

Donc, la hiérarchie des classes : Parent avec enfants : Child1 et Child2

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

5voto

Chandan Points 338

J'ai obtenu une erreur similaire pour les API d'AutoIt que j'appelais depuis un programme VC++.

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

Cependant, lorsque j'ai modifié 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);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X