我有一个用C 6.0编写的旧应用程序的插件.文件以下列方式连接:
>开头:C 6.0 .exe(第三方应用程序)
> loads:C 6.0简单加载器.dll(正式的应用程序插件)
> loads:C 10.0简单加载器.dll(托管C/C++LI)
>加载以下之一:包含插件的C#.NET 4.0程序集
> loads: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来加速搜索,并使用它的句柄作为起始点).这往往会打破本机代码的调试.
有没有更好的方法来处理这个?
最佳答案 自从我报告此问题以来,CLR似乎已经改变了行为.由于CLR现在是开源的,因此可以看到幕后发生了什么.
CLR安装自己的向量异常处理程序.在向量异常处理期间,它会进行堆栈检查以确保有足够的空间,除非它是堆栈溢出异常.堆栈空间检查出错了,它认为它没有空间,因此它抛出堆栈溢出异常以展开堆栈足以进行实际工作.
我能够通过安装2个向量的异常处理程序来欺骗.NET而不会崩溃应用程序,一个在前和后一个.如果它是导致崩溃的异常类型,我在第一个处理程序中将异常代码更改为STACKOVERFLOW,并在第二个处理程序中将其更改回来.这样CLR认为它是堆栈溢出异常并且不会尝试进行堆栈探测.