奇怪的 MSC 8.0 错误:“ESP 的值没有在函数调用中正确保存..."
我们最近尝试将我们的一些 Visual Studio 项目分解为库,并且在以库项目之一作为依赖项的测试项目中,一切似乎都可以正常编译和构建.然而,尝试运行该应用程序给了我们以下令人讨厌的运行时错误消息:
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:
运行时检查失败 #0 - ESP 的值未在函数调用中正确保存.这通常是调用使用不同调用约定声明的函数指针的结果.
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.
我们甚至从未为我们的函数指定调用约定(__cdecl 等),将所有编译器开关保留为默认值.我检查过,项目设置对于跨库和测试项目的调用约定是一致的.
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.
更新:我们的一位开发人员将基本运行时检查"项目设置从两者(/RTC1,相当于/RTCsu)"更改为默认",运行时消失了,使程序明显正确运行.我根本不相信这一点.这是一个合适的解决方案,还是一个危险的黑客?
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?
推荐答案
这个调试错误是指函数调用后栈指针寄存器没有返回到原来的值,即pushs 在函数调用之前没有跟在调用之后相同数量的pops.
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 pushes before the function call were not followed by the equal number of pops after the call.
我知道有两个原因(都与动态加载的库).#1 是 VC++ 在错误消息中描述的内容,但我认为这不是导致错误的最常见原因(参见 #2).
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) 不匹配的调用约定:
调用者和被调用者没有就谁将要做什么达成适当的协议.例如,如果您正在调用 _stdcall
的 DLL 函数,但由于某种原因,您在调用中将其声明为 _cdecl
(VC++ 中的默认值).如果您在不同的模块等中使用不同的语言,这种情况会发生很多.
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.
您必须检查违规函数的声明,并确保它没有被声明两次,而且不同.
You would have to inspect the declaration of the offending function, and make sure it is not declared twice, and differently.
2) 类型不匹配:
调用者和被调用者不是用相同的类型编译的.例如,一个公共头文件定义了 API 中的类型并且最近发生了变化,一个模块被重新编译,但另一个没有 - 即某些类型在调用方和被调用方中可能具有不同的大小.
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.
在这种情况下,调用者推送一种大小的参数,但被调用者(如果您使用 _stdcall
被调用者清理堆栈的地方)弹出不同大小的参数.因此,ESP 没有返回到正确的值.
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.
(当然,这些参数以及它们下面的其他参数在被调用的函数中看起来是乱码,但有时您可以在没有明显崩溃的情况下幸免于难.)
(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.
相关文章