我使用“StartServiceCtrlDispatcher”函数在windows中注册一个回调函数(称为ServiceMain),但我声明的回调函数是用错误的调用约定编译的。
事情是某些计算机上的 ,当应用程序从回调函数返回时,应用程序崩溃,但在其他计算机上,应用程序没有崩溃。
现在,一旦我发现bug一切正常,但我只是不明白为什么在某些计算机上它能正常工作而不会崩溃?
谢谢! : - )
答案 0 :(得分:5)
这些都是特定于Windows的,我们在这里不是标准的C ++。
签出documentation of StartServiceDispatcher
它只有一个参数,并被声明为WINAPI
,这反过来意味着__stcall
调用约定。
对于独立函数,__stdcall
是两个主要调用约定之一。另一个是__cdecl
。机器代码级别差异只是谁恢复堆栈指针:__stdcall
它是函数本身,而__cdecl
它是调用代码。
当函数实际上是__stdcall
但被调用就像它是__cdecl
一样,情况是有两次尝试恢复堆栈指针:一个在函数出口处,一个在调用代码中。函数中的那个将成功。根据调用代码中的尝试如何完成,它可以彻底解决问题(例如,如果只是添加所需的偏移量,将堆栈指针视为相对的),或者它可能没有任何有害影响。但它很可能造成混乱,因为关于从函数返回的堆栈指针值的假设是不正确的。
当函数实际为__cdecl
时,它本身不会恢复堆栈指针,因为这是调用代码的责任。如果调用代码将其视为__stdcall
,那么调用代码也不会恢复它,因为从调用代码的视图中,函数正在执行该操作。结果,如果你没有得到早期崩溃(因为假设破坏),那么重复的调用,比如循环,会占用堆栈空间。
这都是未定义的行为。
Undefined Behavior的一个属性是它可以做任何事情,包括显然有效......
干杯&第h。,
答案 1 :(得分:2)
调用约定的细节不同,例如保留哪些寄存器。如果你碰巧没有在那些寄存器中存储任何你需要的东西,那么当它们不必存在时它们就被删除并不重要。同样,如果你的调用约定不同于它如何处理返回值,如果你没有返回任何东西,那么它并不重要。
幸运的是,x64只有一个调用约定,而这整个混乱将会在过去。
答案 2 :(得分:1)
应用程序崩溃的计算机可能一直在使用.NET Framework第4版。
查看以下文章: http://msdn.microsoft.com/en-us/library/ee941656.aspx
它在互操作性 - 平台调用:
下声明了以下内容“为了提高与非托管代码的互操作性能,平台调用中不正确的调用约定现在导致应用程序失败。在以前的版本中,编组层将这些错误解决了堆栈。”
答案 3 :(得分:0)
这与内存中的当前内容有关。我们假设您有两个这样的函数:
void stdcall f1(...) { ... }
void cdecl f2(...) { ... }
stdcall
是Windows调用约定,而大多数编译器使用cdecl
。它们之间的区别在于谁有责任在通话结束后清除堆栈。在stdcall
中,被叫方(f1
或f2
)在cdecl
中执行了此操作。
毕竟,堆栈中充满了未知值。因此,当它被清理(错误地)时,您在堆栈中访问的下一个值是不确定的。它很可能是一个可接受的值,或者它可能是一个非常糟糕的值。原则上,这是堆栈溢出(错误,而不是站点)的工作方式。