我们最近试图将一些Visual Studio项目拆分成库,一切似乎都在一个测试项目中编译和构建,其中一个库项目作为依赖项.但是,尝试运行该应用程序给了我们以下令人讨厌的运行时错误消息:
运行时检查失败#0 - ESP的值未在函数调用中正确保存.这通常是调用使用不同调用约定声明的函数指针的结果.
我们甚至从未为函数指定调用约定(__cdecl等),使所有编译器开关都处于默认状态.我检查过,项目设置与整个库和测试项目的调用约定是一致的.
更新:我们的一个开发人员将"基本运行时检查"项目设置从"两者(/ RTC1,等同于/ RTCsu)"更改为"默认",运行时间消失,使程序运行正常.我完全不相信这一点.这是一个合适的解决方案,还是一个危险的黑客?
此调试错误意味着在函数调用之后堆栈指针寄存器不会返回其原始值,即函数调用之前的推送次数在调用之后没有跟随相同数量的pop.
我知道有两个原因(两者都有动态加载的库).#1是VC++在错误消息中描述的内容,但我不认为这是导致错误的最常见原因(参见#2).
1)不匹配的呼叫约定:
呼叫者和被呼叫者没有就谁将要做什么达成适当的协议.例如,如果您正在调用DLL函数_stdcall
,但由于某种原因,您在调用中将其声明为_cdecl
(VC++中的默认值).如果你在不同的模块中使用不同的语言等,这会发生很多.
您必须检查违规函数的声明,并确保它不会被声明两次,并且不同.
2)不匹配的类型:
调用者和被调用者不使用相同的类型进行编译.例如,公共头定义API中的类型并且最近已更改,并且一个模块被重新编译,但另一个不是 - 即某些类型在调用者和被调用者中可能具有不同的大小.
在这种情况下,调用者推送一个大小的参数,但被调用者(如果你正在使用_stdcall
被调用者清理堆栈的地方)弹出不同的大小.因此,ESP不会返回正确的值.
(当然,这些参数以及它们下面的其他参数在被调用的函数中看似乱码,但有时你可以在没有明显崩溃的情况下幸存下来.)
如果您可以访问所有代码,只需重新编译即可.
我在其他论坛上看过这个
我有同样的问题,但我只是固定它.我从以下代码得到了同样的错误:
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.
经过一番调查,我改变了其中一条线:
typedef bool (WINAPI*tSetSuspendStateSig)(BOOL, BOOL, BOOL);
这解决了问题.如果您查看找到SetSuspendState的头文件(powrprof.h,SDK的一部分),您将看到函数原型定义为:
BOOLEAN WINAPI SetSuspendState(BOOLEAN, BOOLEAN, BOOLEAN);
所以你们有类似的问题.当您从.dll调用给定函数时,其签名可能已关闭.(在我的例子中,它是缺少的WINAPI关键字).
希望能帮助任何未来的人!:-)
干杯.
沉默检查不是正确的解决方案.你必须弄清楚你的调用约定搞砸了什么.
有很多方法可以在不明确指定函数的情况下更改函数的调用对话.extern"C"会这样做,STDMETHODIMP/IFACEMETHODIMP也会这样做,其他宏也可以这样做.
我相信如果在WinDBG下运行你的程序(http://www.microsoft.com/whdc/devtools/debugging/default.mspx),运行时应该在遇到问题的时候中断.您可以查看调用堆栈并确定哪个函数有问题,然后查看其定义和调用者使用的声明.
当代码试图在不符合预期类型的对象上调用函数时,我看到了这个错误.
因此,类层次结构:具有子项的父级:Child1和Child2
Child1* pMyChild = 0; ... pMyChild = pSomeClass->GetTheObj();// This call actually returned a Child2 object pMyChild->SomeFunction(); // "...value of ESP..." error occurs here
我在从VC++程序调用的AutoIt API中遇到了类似的错误.
typedef long (*AU3_RunFn)(LPCWSTR, LPCWSTR);
但是,当我更改包含WINAPI的声明时,正如之前在线程中所建议的那样,问题就消失了.
没有任何错误的代码如下所示:
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); }