.NET 异常处理程序导致 Visual C++ 6.0 异常的堆栈溢出
Posted
技术标签:
【中文标题】.NET 异常处理程序导致 Visual C++ 6.0 异常的堆栈溢出【英文标题】:.NET exception handler causing stack overflow on Visual C++ 6.0 exceptions 【发布时间】:2015-06-05 16:54:55 【问题描述】:我有一个用 C++ 6.0 编写的旧应用程序的插件。这些文件以下列方式连接:
-
开始于:C++ 6.0 .exe(第三方应用程序)
加载:C++ 6.0 简单加载器 .dll(正式为应用程序插件)
加载:C++ 10.0 简单加载程序 .dll(托管 C++/CLI)
加载以下之一:包含插件的 C# .NET 4.0 程序集
加载:C++ 6.0 .dll,它为 C# 插件提供 API 以与应用程序对话
问题是一旦 .NET 4.0 被加载到 C++ 6.0 应用程序中,下一次它抛出本地异常时,.NET 使用向量化异常句柄来处理异常并且异常失败。真正糟糕的部分是向量异常处理程序自己抛出一个异常,然后它会尝试处理这个异常,但失败了,它会陷入无限循环,直到出现堆栈溢出异常。
堆栈跟踪如下所示:
// The next 7 lines repeat until the stack overflows
clr.dll!CreateHistoryReader()
clr.dll!CreateHistoryReader()
clr.dll!GetMetaDataInternalInterfaceFromPublic()
ntdll.dll!_RtlpCallVectoredHandlers@12()
ntdll.dll!_RtlCallVectoredExceptionHanders@8()
ntdll.dll!_RtlDispatchException@8()
ntdll.dll!_KiUserExceptionDispatcher@8()
// Below is an example exception that causes this:
KernelBase.dll!RaiseException()
rpcrt4.dll!RpcRaiseException()
rpcrt4.dll!I_RpcTransConnectionFreePacket()
rpcrt4.dll!I_RpcBindingInqCurrentModifiedId()
rpcrt4.dll!NdrConformantStringMemorySize()
rpcrt4.dll!NdrComplexStructMarshall()
rpcrt4.dll!SimpleTypeMemorySize()
rpcrt4.dll!NdrClientCall2()
ole32.dll!ServerRegisterClsid(void* hRpc, void* phprocess, _RegInput* pregin, _RegOutput** ppregout unligned long* prpcstat
ole32.dll!CRpcResolver::NotifyStarted(_RegInput* pRegIn, _RegOutput** ppRegOut)
ole32.dll!CClassCache::ResumeProcessClassObjects()
实际上只有两种方法可以解决这个问题,而且都不是很好:
我发现一个小程序,如果我在自己的线程上完全隔离 .NET,非 .NET 线程永远不会遇到这个问题。这在实践中不起作用,因为插件 API 需要对 .NET 插件进行同步回调。
我想出的另一个方法是遍历内存中的每个地址,直到调用“RemoveVectoredExceptionHandler(HANDLE)”成功并删除 .NET 的向量异常处理程序。 (我可以通过临时注册我自己的 VEH 并使用它的句柄作为起点来加快搜索速度)。这往往会破坏本机代码的调试。
有没有更好的方法来解决这个问题?
【问题讨论】:
【参考方案1】:自从我报告此问题后,CLR 似乎已经改变了行为。由于 CLR 现在是开源的,因此可以看到幕后发生的事情。
CLR 安装自己的向量异常处理程序。在向量化异常处理期间,它会进行堆栈检查以确保有足够的空间,除非它是堆栈溢出异常。堆栈空间检查出错,如果没有,它会认为空间不足,因此抛出堆栈溢出异常以展开堆栈以进行实际工作。
我能够通过安装 2 个矢量异常处理程序(一个之前和一个之后)来欺骗 .NET,使其不会导致应用程序崩溃。如果是导致崩溃的异常类型,我在第一个处理程序中将异常代码更改为 ***,然后在第二个处理程序中将其改回。这样 CLR 就会认为这是一个堆栈溢出异常,并且不会尝试进行堆栈探测。
【讨论】:
以上是关于.NET 异常处理程序导致 Visual C++ 6.0 异常的堆栈溢出的主要内容,如果未能解决你的问题,请参考以下文章
Visual Studio 2008 发布版本上的 c++ 应用程序出现未处理异常 - 从函数返回时发生
从 TFS 仪表板打开项目导致 Visual Studio Web 处理程序异常
std::thread,在线程中抛出异常会导致 Visual C++ 中的中止错误