RCW终结器访问冲突

obi*_*obi 7 c# com-interop rcw access-violation visual-studio-2012

我正在使用COM interop在使用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    
Run Code Online (Sandbox Code Playgroud)

我的猜测是应用程序已经销毁了它的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
Run Code Online (Sandbox Code Playgroud)

所以我虽然会管理自己的生命,并写了一个调用Marshal.ReleaseComObject的ComReference类.这不能正常工作,在阅读之后,我不得不同意在一个自由传递参考文献的场景中调用Marshal.ReleaseComObject,这不是一个好主意. Marshal.ReleaseComObject被认为是危险的

所以问题是:有没有办法管理这种情况,以便在退出主机应用程序时不会导致AV?

Mik*_*tak 2

这个问题只有三种真正的解决方案,我认为将“Marshall.ReleaseComObject 被认为是危险的”文章解释为“不要使用 Marshall.ReleaseComObject”可能会误导您。您的结论很可能是“不要随意共享 RCW”。

您的三个解决方案是:

1:更改主机应用程序的执行方式,以在卸载自身之前卸载插件。说起来容易做起来难。如果主机进程的插件系统包含关闭事件,那么这将是处理它的好地方。所有保留 RCW 的服务都需要在关闭期间释放它们。

2:以类似于 Dispose() 的模式使用 Marshall.ReleaseComObject,确保对象仅以类似于 using 块的方式存储在本地范围内。这实现起来很简单,允许您确定性地释放 COM 引用,并且通常是一个非常好的第一种方法。

3:使用 COM 对象代理,它可以分发 RCW 的引用计数实例,然后在没有人使用这些对象时释放它们。确保这些对象的每个使用者在应用程序卸载之前进行清理。

只要您不存储/共享对托管 RCW 的引用,选项#2 就可以正常工作。我会使用#2,直到您确定您的 COM 对象具有很高的激活成本并且缓存/共享是相关的。