.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++ 中的中止错误

在 MS Visual C++ 中调试未处理异常的正确方法

使用 Visual C++ 2008 时如何解决未处理的异常错误?

在 Visual Studio 中处理异常