C++ 奇怪的 MSC 8.0 错误:“ESP 的值没有在函数调用中正确保存......”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/142644/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Weird MSC 8.0 error: "The value of ESP was not properly saved across a function call..."
提问by user11180
We recently attempted to break apart some of our Visual Studio projects into libraries, and everything seemed to compile and build fine in a test project with one of the library projects as a dependency. However, attempting to run the application gave us the following nasty run-time error message:
我们最近尝试将我们的一些 Visual Studio 项目分解为库,并且在一个测试项目中,一切似乎都可以正常编译和构建,其中一个库项目作为依赖项。但是,尝试运行该应用程序给了我们以下令人讨厌的运行时错误消息:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function pointer declared with a different calling convention.
运行时检查失败 #0 - ESP 的值未在函数调用中正确保存。这通常是调用使用不同调用约定声明的函数指针的结果。
We have never even specified calling conventions (__cdecl etc.) for our functions, leaving all the compiler switches on the default. I checked and the project settings are consistent for calling convention across the library and test projects.
我们甚至从未为我们的函数指定调用约定(__cdecl 等),将所有编译器开关保留为默认值。我检查了项目设置对于跨库和测试项目的调用约定是一致的。
Update: One of our devs changed the "Basic Runtime Checks" project setting from "Both (/RTC1, equiv. to /RTCsu)" to "Default" and the run-time vanished, leaving the program running apparently correctly. I do not trust this at all. Was this a proper solution, or a dangerous hack?
更新:我们的一位开发人员将“Basic Runtime Checks”项目设置从“Both (/RTC1, equiv. to /RTCsu)”更改为“Default”,运行时消失了,使程序明显正确运行。我根本不相信这一点。这是一个正确的解决方案,还是一个危险的黑客?
回答by Nikola Gedelovski
This debug error means that the stack pointer register is not returned to its original value after the function call, i.e. that the number of pushesbefore the function call were not followed by the equal number of popsafter the call.
这个调试错误手段,该函数调用,即数之后的堆栈指针寄存器不返回到其原始值推没有随后相等数目的函数调用前弹出的呼叫之后。
There are 2 reasons for this that I know (both with dynamically loaded libraries). #1 is what VC++ is describing in the error message, but I don't think this is the most often cause of the error (see #2).
我知道这有两个原因(都有动态加载的库)。#1 是 VC++ 在错误消息中描述的内容,但我认为这不是导致错误的最常见原因(请参阅 #2)。
1) Mismatched calling conventions:
1) 不匹配的调用约定:
The caller and the callee do not have a proper agreement on who is going to do what. For example, if you're calling a DLL function that is _stdcall
, but you for some reason have it declared as a _cdecl
(default in VC++) in your call. This would happen a lot if you're using different languages in different modules etc.
调用者和被调用者没有就谁将要做什么达成适当的协议。例如,如果您正在调用一个 DLL 函数_stdcall
,但由于某种原因,您在调用中将其声明为 a _cdecl
(VC++ 中的默认值)。如果您在不同的模块等中使用不同的语言,这种情况会发生很多。
You would have to inspect the declaration of the offending function, and make sure it is not declared twice, and differently.
您必须检查违规函数的声明,并确保它没有被声明两次,而且不同。
2) Mismatched types:
2) 不匹配的类型:
The caller and the callee are not compiled with the same types. For example, a common header defines the types in the API and has recently changed, and one module was recompiled, but the other was not--i.e. some types may have a different size in the caller and in the callee.
调用者和被调用者不是用相同的类型编译的。例如,一个公共头文件定义了 API 中的类型并且最近发生了变化,一个模块被重新编译,但另一个没有 - 即某些类型在调用者和被调用者中可能具有不同的大小。
In that case, the caller pushes the arguments of one size, but the callee (if you're using _stdcall
where the callee cleans the stack) pops the different size. The ESP is not, thus, returned to the correct value.
在这种情况下,调用者会推送一种大小的参数,但被调用者(如果您使用的_stdcall
是被调用者清理堆栈的地方)会弹出不同大小的参数。因此,ESP 不会返回到正确的值。
(Of course, these arguments, and others below them, would seem garbled in the called function, but sometimes you can survive that without a visible crash.)
(当然,这些参数以及它们下面的其他参数在被调用的函数中似乎是乱码,但有时您可以在没有明显崩溃的情况下幸免于难。)
If you have access to all the code, simply recompile it.
如果您可以访问所有代码,只需重新编译即可。
回答by Khaled
I read this in other forum
我在其他论坛读过这个
I was having the same problem, but I just FIXED it. I was getting the same error from the following code:
我遇到了同样的问题,但我只是修复了它。我从以下代码中得到了同样的错误:
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.
After some investigation, I changed one of the lines to:
经过一番调查,我将其中一行更改为:
typedef bool (WINAPI*tSetSuspendStateSig)(BOOL, BOOL, BOOL);
which solved the problem. If you take a look in the header file where SetSuspendState is found (powrprof.h, part of the SDK), you will see the function prototype is defined as:
这解决了问题。如果您查看 SetSuspendState 所在的头文件(powrprof.h,SDK 的一部分),您将看到函数原型定义为:
BOOLEAN WINAPI SetSuspendState(BOOLEAN, BOOLEAN, BOOLEAN);
So you guys are having a similar problem. When you are calling a given function from a .dll, its signature is probably off. (In my case it was the missing WINAPI keyword).
所以你们也有类似的问题。当您从 .dll 调用给定函数时,其签名可能已关闭。(就我而言,它是缺少的 WINAPI 关键字)。
Hope that helps any future people! :-)
希望对未来的人有所帮助!:-)
Cheers.
干杯。
回答by Franci Penov
Silencing the check is not the right solution. You have to figure out what is messed up with your calling conventions.
使支票静音不是正确的解决方案。你必须弄清楚你的调用约定有什么问题。
There are quite a few ways to change the calling convetion of a function without explicitly specifying it. extern "C" will do it, STDMETHODIMP/IFACEMETHODIMP will also do it, other macros might do it as well.
有很多方法可以在不明确指定的情况下更改函数的调用对流。extern "C" 会这样做,STDMETHODIMP/IFACEMETHODIMP 也会这样做,其他宏也可能会这样做。
I believe if run your program under WinDBG (http://www.microsoft.com/whdc/devtools/debugging/default.mspx), the runtime should break at the point where you hit that problem. You can look at the call stack and figure out which function has the problem and then look at its definition and the declaration that the caller uses.
我相信如果在 WinDBG ( http://www.microsoft.com/whdc/devtools/debugging/default.mspx)下运行您的程序,运行时应该会在您遇到该问题时中断。您可以查看调用堆栈并找出哪个函数有问题,然后查看其定义和调用者使用的声明。
回答by Tinclon
I saw this error when the code tried to call a function on an object that was not of the expected type.
当代码尝试在非预期类型的对象上调用函数时,我看到了此错误。
So, class hierarchy: Parent with children: Child1 and Child2
因此,类层次结构:有孩子的父级:Child1 和 Child2
Child1* pMyChild = 0;
...
pMyChild = pSomeClass->GetTheObj();// This call actually returned a Child2 object
pMyChild->SomeFunction(); // "...value of ESP..." error occurs here
回答by Chandan
I was getting similar error for AutoIt APIs which i was calling from VC++ program.
我从 VC++ 程序调用的 AutoIt API 遇到了类似的错误。
typedef long (*AU3_RunFn)(LPCWSTR, LPCWSTR);
However, when I changed the declaration which includes WINAPI, as suggested earlier in the thread, problem vanished.
但是,当我按照线程前面的建议更改包含 WINAPI 的声明时,问题就消失了。
Code without any error looks like:
没有任何错误的代码如下所示:
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);
}
回答by oli_arborum
I was getting this error calling a function in a DLL which was compiled with a pre-2005 version of Visual C++ from a newer Version of VC (2008). The function had this signature:
我在调用 DLL 中的函数时遇到此错误,该 DLL 是使用 2005 年之前版本的 Visual C++ 从较新版本的 VC(2008)编译的。该函数具有以下签名:
LONG WINAPI myFunc( time_t, SYSTEMTIME*, BOOL* );
The problem was that time_t
's size is 32 bits in pre-2005 version, but 64 bits since VS2005 (is defined as _time64_t
). The call of the function expects a 32 bit variable but gets a 64 bit variable when called from VC >= 2005. As parameters of functions are passed via the stack when using WINAPI
calling convention, this corrupts the stack and generates the above mentioned error message ("Run-Time Check Failure #0 ...").
问题是time_t
2005 之前的版本中 的大小为 32 位,但自 VS2005 以来为 64 位(定义为_time64_t
)。函数的调用需要 32 位变量,但从 VC >= 2005 调用时得到 64 位变量。由于使用WINAPI
调用约定时函数的参数是通过堆栈传递的,这会破坏堆栈并生成上述错误消息( “运行时检查失败 #0 ...”)。
To fix this, it is possible to
为了解决这个问题,可以
#define _USE_32BIT_TIME_T
before including the header file of the DLL or -- better -- change the signature of the function in the header file depending on the VS version (pre-2005 versions don't know _time32_t
!):
在包含 DLL 的头文件之前,或者——更好——根据 VS 版本更改头文件中函数的签名(2005 之前的版本不知道_time32_t
!):
#if _MSC_VER >= 1400
LONG WINAPI myFunc( _time32_t, SYSTEMTIME*, BOOL* );
#else
LONG WINAPI myFunc( time_t, SYSTEMTIME*, BOOL* );
#endif
Note that you need to use _time32_t
instead of time_t
in the calling program, of course.
请注意,当然,您需要在调用程序中使用_time32_t
而不是time_t
。
回答by Waldo Alvarez
I was having this exact same error after moving functions to a dll and dynamically loading the dll with LoadLibrary and GetProcAddress. I had declared extern "C" for the function in the dll because of the decoration. So that changed calling convention to __cdecl as well. I was declaring function pointers to be __stdcall in the loading code. Once I changed the function pointer from __stdcall to__cdecl in the loading code the runtime error went away.
在将函数移动到 dll 并使用 LoadLibrary 和 GetProcAddress 动态加载 dll 后,我遇到了完全相同的错误。由于装饰,我已经为 dll 中的函数声明了 extern "C"。所以这也将调用约定更改为 __cdecl。我在加载代码中将函数指针声明为 __stdcall。一旦我在加载代码中将函数指针从 __stdcall 更改为 __cdecl,运行时错误就消失了。
回答by stands2reason
It's worth pointing out that this can also be a Visual Studio bug.
值得指出的是,这也可能是 Visual Studio 的错误。
I got this issue on VS2017, Win10 x64. At first it made sense, since I was doing weird things casting this to a derived type and wrapping it in a lambda. However, I reverted the code to a previous commit and still got the error, even though it wasn't there before.
我在 VS2017、Win10 x64 上遇到了这个问题。起初这是有道理的,因为我正在做一些奇怪的事情,将它转换为派生类型并将其包装在 lambda 中。但是,我将代码恢复到以前的提交,但仍然收到错误,即使它以前不存在。
I tried restarting and then rebuilding the project, and then the error went away.
我尝试重新启动然后重建项目,然后错误消失了。
回答by rtischer8277
In my MFC C++ app I am experiencing the same problem as reported in Weird MSC 8.0 error: “The value of ESP was not properly saved across a function call…”. The posting has over 42K views and 16 answers/comments none of which blamed the compiler as the problem. At least in my case I can show that the VS2015 compiler is at fault.
在我的 MFC C++ 应用程序中,我遇到了与奇怪的 MSC 8.0 错误中报告的相同的问题:“ESP 的值没有在函数调用中正确保存......”。该帖子有超过 42K 的浏览量和 16 个答案/评论,其中没有一个将问题归咎于编译器。至少在我的情况下,我可以证明 VS2015 编译器有问题。
My dev and test setup is the following: I have 3 PCs all of which run Win10 version 10.0.10586. All are compiling with VS2015, but here is the difference. Two of the VS2015s have Update 2 while the other has Update 3 applied. The PC with Update 3 works, but the other two with Update 2 fail with the same error as reported in the posting above. My MFC C++ app code is exactly the same on all three PCs.
我的开发和测试设置如下:我有 3 台 PC,它们都运行 Win10 版本 10.0.10586。都是用 VS2015 编译的,但这里有区别。两个 VS2015 应用了更新 2,而另一个应用了更新 3。带有 Update 3 的 PC 可以工作,但带有 Update 2 的另外两个 PC 失败,并出现与上述帖子中报告的相同的错误。我的 MFC C++ 应用程序代码在所有三台 PC 上都完全相同。
Conclusion: at least in my case for my app the compiler version (Update 2) contained a bug that broke my code. My app makes heavy use of std::packaged_task so I expect the problem was in that fairly new compiler code.
结论:至少在我的应用程序中,编译器版本(更新 2)包含一个破坏我代码的错误。我的应用程序大量使用 std::packaged_task 所以我预计问题出在那个相当新的编译器代码中。
回答by Neil Lamoureux
This happened to me when accessing a COM object (Visual Studio 2010). I passed the GUID for another interface A for in my call to QueryInterface, but then I cast the retrieved pointer as interface B. This resulted in making a function call to one with an entirely signature, which accounts for the stack (and ESP) being messed up.
这发生在我访问 COM 对象(Visual Studio 2010)时。我在对 QueryInterface 的调用中传递了另一个接口 A 的 GUID,但随后我将检索到的指针转换为接口 B。这导致对一个具有完整签名的函数调用,这说明堆栈(和 ESP)被弄乱。
Passing the GUID for interface B fixed the problem.
传递接口 B 的 GUID 解决了该问题。