6

引用非托管 COM 对象时,运行时可调用包装器 (RCW) 的范围是什么?根据文档:

运行时为每个 COM 对象只创建一个 RCW,无论该对象上存在多少引用。

如果我不得不“猜测”——这个解释应该是“每个进程一个”,但真的是这样吗?任何额外的文档都将非常受欢迎。

我的应用程序在它自己的应用程序域中运行(它是 Outlook 插件),我想知道如果我在循环中使用 Marshal.ReleaseComObject(x) 直到它的计数达到 0(如推荐的那样)会发生什么。它会释放来自其他插件的引用(在同一 Outlook 进程中的其他应用程序域中运行)吗?

编辑:完美 - 现在混乱更大了。基于 2 个答案(来自 Lette 和 Ilya),我们有 2 个不同的答案。官方MSDN 文档说每个进程(对于版本 2.0+),但是对于版本来说它缺少这句话。1.1 的文档

同时,在 Mason Bendixen 的文章中,它说它是 per appdomain。

由于他的文章很旧(2007 年 4 月),我已经给他发了一封电子邮件,要求澄清,但如果其他人必须添加一些东西,请做。

谢谢

4

3 回答 3

3

在托管中,我们有一个每个应用程序域缓存将规范 IUnknowns 映射回 RCW。当 IUnknown 进入系统时(通过编组调用、通过激活、作为方法调用的返回参数等),我们检查缓存以查看 COM 对象是否已经存在 RCW。如果存在映射,则返回对现有 RCW 的引用。否则会创建一个新的 RCW 并添加一个缓存映射。

来自梅森的博客

于 2008-10-02T15:59:28.097 回答
3

Ilya 引用的 Mason Bendixen 博客文章是正确的:RCW 的范围仅限于 AppDomain,而不是流程。我只能猜测Runtime Callable Wrapper (MSDN 2.0)文章是“随便”说的。那篇文章在一般意义上不一定是不正确的,因为最典型的是只使用单个 AppDomain 执行,但那句话在技术上并不准确。

至于你的具体问题:

“我想知道如果我在循环中使用 Marshal.ReleaseComObject(x) 直到它的计数达到 0(如推荐的那样)会发生什么。它会释放来自其他插件的引用(在同一 Outlook 进程中的其他应用程序域中运行)? ?”

答案取决于您如何设置加载项。一般来说,如果你不采取预防措施,那么答案是肯定的,它会影响在同一个 AppDomain 中运行的其他加载项中的引用。但是,既然您声明您是从一个单独的 AppDomain 运行的,那么,不,它不会。

有一个COM Shim 向导版本 2.3.1,您可以使用它来隔离您的加载项。COM Shim 向导的文档可以在这里找到:Isolating Microsoft Office Extensions with the COM Shim Wizard Version 2.3.1

COM Shim 向导使用反射来构建一个自定义的 COM 前端加载器,该加载器将加载项程序集加载到单独的 AppDomain 中。这在两个方面创造了安全性:

(1) 通过使用单独的自定义 COM 入口点,Microsoft Office 可以正确地将您的加载项与所有其他加载项分开识别。否则,默认情况下,所有加载项都共享相同的默认 mscoree.dll 加载程序。共享同一个加载器的问题是,如果任何加载项发生崩溃,那么 mscoree.dll 将被 Microsoft Office 识别为问题的根源,并且下次不会自动加载它。您可以手动重新开启,但下次您的加载项不会因为别人加载项的问题而自动加载!

(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 加载程序,除非您使用其他方式来解决这个问题。

我希望这有帮助...

于 2008-10-04T13:48:33.473 回答
1

根据相同的文档:

运行时为每个对象的每个进程维护一个 RCW 。

我认为我们可以安全地假设object = instance,因此如果 addins/AppDomains 不包含对同一实例的引用,则调用ReleaseComObject不会释放对在其他地方创建的实例的引用。

编辑:文档的措辞可能是错误的,如其他地方所述。如果是这样,因为您的加载项在单独的 AppDomain 中运行,所以您很幸运。即使不同的加载项引用相同的实例(例如 Outlook 中的消息对象),ReleaseComObject在您的 AppDomain 中调用也不会导致其他 AppDomain 中的 RCW 丢失对该实例的引用。

于 2008-10-02T15:56:02.283 回答