我正在使用 COM 互操作在使用 VS2012/.NET 4.5/Win8.1 的非托管应用程序中创建托管插件。所有互操作的东西似乎都正常,但是当我关闭应用程序时,我收到一个 MDA 异常,告诉我在释放 RCW 在完成期间持有的 COM 对象时发生了 AV。
这是调用堆栈:
clr.dll!MdaReportAvOnComRelease::ReportHandledException() + 0x91 bytes
clr.dll!**SafeRelease_OnException**() + 0x55 bytes
clr.dll!SafeReleasePreemp() + 0x312d5f bytes
clr.dll!RCW::ReleaseAllInterfaces() + 0xf3 bytes
clr.dll!RCW::ReleaseAllInterfacesCallBack() + 0x4f bytes
clr.dll!RCW::Cleanup() + 0x24 bytes
clr.dll!RCWCleanupList::ReleaseRCWListRaw() + 0x16 bytes
clr.dll!RCWCleanupList::ReleaseRCWListInCorrectCtx() + 0x9c bytes
clr.dll!RCWCleanupList::CleanupAllWrappers() + 0x2cd1b6 bytes
clr.dll!RCWCache::ReleaseWrappersWorker() + 0x277 bytes
clr.dll!AppDomain::ReleaseRCWs() + 0x120cb2 bytes
clr.dll!ReleaseRCWsInCaches() + 0x3f bytes
clr.dll!InnerCoEEShutDownCOM() + 0x46 bytes
clr.dll!WKS::GCHeap::**FinalizerThreadStart**() + 0x229 bytes
clr.dll!Thread::intermediateThreadProc() + 0x76 bytes
kernel32.dll!BaseThreadInitThunk() + 0xd bytes
ntdll.dll!RtlUserThreadStart() + 0x1d bytes
我的猜测是该应用程序已经销毁了它的 COM 对象,其中一些引用已传递给托管插件 - 并且 RCW 对 IUnknown::Release 的调用使它变得繁荣。
我可以在输出窗口 (VS) 中清楚地看到应用程序已经开始卸载其中的一些 dll。
'TestHost.exe': Unloaded 'C:\Windows\System32\msls31.dll'
'TestHost.exe': Unloaded 'C:\Windows\System32\usp10.dll'
'TestHost.exe': Unloaded 'C:\Windows\System32\riched20.dll'
'TestHost.exe': Unloaded 'C:\Windows\System32\version.dll'
First-chance exception at 0x00000001400cea84 in VST3PluginTestHost.exe: 0xC0000005: Access violation reading location 0xffffffffffffffff.
First-chance exception at 0x00000001400cea84 in VST3PluginTestHost.exe: 0xC0000005: Access violation reading location 0xffffffffffffffff.
Managed Debugging Assistant 'ReportAvOnComRelease' has detected a problem in 'C:\Program Files\Steinberg\VST3PluginTestHost\VST3PluginTestHost.exe'.
Additional Information: An exception was caught but handled while releasing a COM interface pointer through Marshal.Release or Marshal.ReleaseComObject or implicitly after the corresponding RuntimeCallableWrapper was garbage collected. This is the result of a user refcount error or other problem with a COM object's Release. Make sure refcounts are managed properly. The COM interface pointer's original vtable pointer was 0x406975a8. While these types of exceptions are caught by the CLR, they can still lead to corruption and data loss so if possible the issue causing the exception should be addressed
所以我想自己管理生命周期并编写一个调用 Marshal.ReleaseComObject 的 ComReference 类。这不能正常工作,在阅读它之后我必须同意在引用自由传递的场景中调用 Marshal.ReleaseComObject 不是一个好主意。 Marshal.ReleaseComObject Considered Dangerous
所以问题是:有没有办法管理这种情况,以便在退出主机应用程序时不引起 AV?
最佳答案
这个问题只有三个真正的解决方案,我认为将“Marshall.ReleaseComObject 被认为是危险的”文章解释为“不要使用 Marshall.ReleaseComObject”可能会误导您。您的收获可能很容易就是“不要随意共享 RCW”。
您的三个解决方案是:
1:更改主机应用程序的执行以在卸载自身之前卸载插件。说起来容易做起来难。如果宿主进程的插件系统包含一个关闭事件,那将是处理它的好地方。所有保留 RCW 的服务都需要在关闭期间释放它们。
2:以类似于 Dispose() 的模式使用 Marshall.ReleaseComObject,确保对象仅以类似于 using block 的方式存储在本地范围内。这实现起来很简单,允许您确定性地释放 COM 引用,通常是非常好的第一种方法。
3:使用 COM 对象代理,它可以分发 RCW 的引用计数实例,然后在没有人使用它们时释放这些对象。确保在卸载应用程序之前清理这些对象的每个使用者。
只要您不存储/共享对托管 RCW 的引用,选项 #2 就可以正常工作。我会使用 #2,直到您确定您的 COM 对象具有高激活成本并且缓存/共享是相关的。
关于c# - RCW 终结器访问冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20371718/