运行时可调用包装(RCW)范围-进程还是应用程序域?
引用非托管COM对象时,Runtime Callable Wrapper(RCW)的范围是什么?根据文档:
The runtime creates exactly one RCW for each COM object, regardless of the number of references that exist on that object.
如果我不得不"猜测",那么这种解释应该意味着"每个过程一个",但这是真的吗?任何其他文档将非常欢迎。
我的应用程序在其自己的应用程序域(它是Outlook加载项)中运行,并且我想知道如果我在循环中使用Marshal.ReleaseComObject(x)直到计数达到0(按照建议)时会发生什么。它将释放来自其他加载项的引用(在同一Outlook进程中的其他应用程序域中运行)吗?
编辑:完美,现在的困惑更大。根据2个答案(来自Lette和Ilya),我们有2个不同的答案。 MSDN官方文档说每个进程(适用于2.0版或者更高版本),但缺少用于ver的这句话。文档的1.1.
同时,在梅森·本迪克森(Mason Bendixen)的文章中,它说这是针对每个应用程序域的。
由于他的文章较旧(2007年4月),我给他发送了一封电子邮件,要求其澄清,但是如果其他人必须添加一些内容,请这样做。
谢谢
解决方案
根据相同的文档:
The runtime maintains a single RCW per process for each object.
我认为我们可以放心地假设object = instance,因此,如果addins / AppDomains不保存对同一实例的引用,则对ReleaseComObject
的调用不会释放对在其他地方创建的实例的引用。
编辑:文档的措词可能是错误的,如其他地方所述。如果是这样,由于加载项在单独的AppDomain中运行,因此我们很幸运。即使不同的加载项引用了相同的实例(例如Outlook中的Message对象),在AppDomain中调用的ReleaseComObject
也不会导致其他AppDomain中的RCW丢失对该实例的引用。
In managed, we have a per app domain cache mapping canonical IUnknowns back to RCWs. When an IUnknown enters the system (through a marshal call, through activation, as a return parameter from a method call, etc.), we check the cache to see if an RCW already exists for the COM object. If a mapping exists, a reference to the existing RCW is returned. Otherwise a new RCW is created and a cache mapping is added.
从梅森的博客
Ilya引用的Mason Bendixen博客文章是正确的:RCW的作用域是AppDomain,而不是过程。我只能猜测Runtime Callable Wrapper(MSDN 2.0)文章说的是"随意"。该文章在一般意义上不一定是错误的,因为最典型的情况是仅使用一个AppDomain执行该语句,但是该语句在技术上并不准确。
关于具体问题:
"I would like to know what happens if I use Marshal.ReleaseComObject(x) in a loop until it's count reaches 0 (as recommended). Will it release references from other addins (running in other application domain in the same Outlook process)??"
答案取决于我们如何设置外接程序。通常,如果我们不采取预防措施,那么答案是肯定的,它将影响在同一AppDomain中运行的其他加载项中的引用。但是,由于我们声明自己是从单独的AppDomain运行的,因此,不会,它不会。
有一个COM Shim向导版本2.3.1,可用于隔离外接程序。可以在此处找到COM Shim向导的文档:使用COM Shim向导版本2.3.1隔离Microsoft Office Extensions。
COM Shim向导使用反射来构建自定义的COM前端加载程序,该加载程序将外接程序集加载到单独的AppDomain中。这从两个方面创造了安全性:
(1)通过使用单独的,自定义的COM入口点,Microsoft Office与所有其他外接程序可以正确地分别标识外接程序。否则,默认情况下,所有加载项都共享相同的默认mscoree.dll加载程序。共享同一加载程序的问题是,如果任何加载项发生崩溃,则Microsoft Office将mscoree.dll识别为问题的根源,并且下次将不会自动加载它。我们可以手动将其重新打开,但是由于别人的加载项有问题,加载项下次将不会自动加载!
(2)通过将程序集加载到单独的AppDomain中,可以将运行时可调用包装(RCW)与加载到同一进程中的其他加载项隔离。在这种情况下,如果调用Marshal.ReleaseComObject(object)或者Marshal.FinalReleaseComObject(object),则不会影响其他任何加载项。更重要的是,如果其他任何外接程序进行了此类调用,则可以防止外接程序被损坏。 :-)
使用COM Shim向导的缺点是,通过在单独的AppDomain之外进行操作,会产生额外的编组开销。我认为对于Microsoft Outlook加载项来说,这应该不会引起注意。但是,对于某些需要大量调用对象模型的密集型例程,这可能是一个因素,例如Microsoft Excel加载项有时可能就是这种情况。
我们说我们已经在从单独的AppDomain运行加载项。如果这是真的,那么我们已经与其他AppDomain脱离了Marshal.ReleaseComObject(object)和Marshal.FinalReleaseComObject(object)调用。 (顺便说一下,我对操作方式感到好奇。我们是否明确创建了自己的AppDomain?Visual Studio中的默认加载项模板没有在单独的AppDomain中运行,而是使用mscoree.dll加载。)
如果我们创建自己的AppDomain,则代码是隔离的,但是其标识可能不会与其他加载项分开,因为加载项仍将共享默认的mscoree.dll加载程序,除非我们使用其他方式解决这个问题。
我希望这有帮助...