c# - RCW 终结器访问冲突

标签 c# visual-studio-2012 com-interop access-violation rcw

我正在使用 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/

相关文章:

c# - asp.net MVC 3 中的 ViewData 和 PageData 有什么区别?

c# - 具有返回修改后的父级的方法的设计模式

c# - nuget 无法识别已安装的包

unit-testing - Visual Studio 2012单元测试: Controller must have testable element property set up

windows-8 - 如何在没有 Windows 应用商店的情况下分发编译的 Windows 8 Metro 应用程序?

.net - 从 .NET 安全地释放 COM 对象引用

excel - `Range.Formula` 是 COM 对象吗?

c# - 在 C# 中读取随机访问文件

C#/ASP.NET 核心 6 : transform weird json to something useful

c# - 什么时候需要/适合使用 InAttribute 和 OutAttribute 进行 COM Interop