项和参考计数时使用COM互操作C#
-
15-10-2019 - |
题
我有一个应用程序使用的办公室的互操作程序集。我知道关于"运行时可调用的包装(项)"管理的运行时间。但我不是很确定如何参考数增加。MSDN说,
项保留只是一个参照 包裹COM对象不管 数量的管理的客户叫它。
如果我理解正确,在下面的例子中,
using Microsoft.Office.Interop.Word;
static void Foo(Application wrd)
{
/* .... */
}
static void Main(string[] args)
{
var wrd = new Application();
Foo(wrd);
/* .... */
}
我穿的实例 wrd
另一方法。但这不会增加内部基准数。所以我想知道在什么情况下的基准数递增的?任何人都可以指出一个方案,其中的基准数递增的?
此外,我读了一些博客,其中说,避免使用双点时编程与COM对象。喜欢的东西, wrd.ActiveDocument.ActiveWindow
.提交人声称,编译器创建独立的变量保持的价值观,这将增加参考计数器。恕我直言,这是错误的,第一个例子证明了这一点。是正确的?
任何帮助将是伟大的!
解决方案
我也一直在研究这个问题,从事以com/.net-Interop的应用程序,对抗泄漏,悬挂和崩溃。
简短答案:每当COM对象从COM环境传递到.NET时。
长答案:
- 对于每个com对象,都有一个rcw对象[test 1] [ref 4
- 参考计数每次从com对象中请求对象时都会增加参考计数(返回com对象上的com object上的呼叫属性或方法,返回的com对象参考计数将被一个递增)[test 1
- 参考计数不会通过施放到对象的其他COM接口或移动RCW参考[测试2]来增加参考计数。
- 参考计数每次将对象作为参数传递为com [参考1]提出的事件中的参数时会增加参考计数。
附带注意:您应该 总是 您使用它们后立即释放com对象。将这项工作留给GC会导致泄漏,意外行为和事件死锁。如果您无法在其创建的sta线程上访问对象,那么这更重要。 [参考2] [参考3] [痛苦的个人经历
我希望我已经涵盖了所有案件,但是com是一个坚韧的饼干。干杯。
测试1-参考数量
private void Test1( _Application outlookApp )
{
var explorer1 = outlookApp.ActiveExplorer();
var count1 = Marshal.ReleaseComObject(explorer1);
MessageBox.Show("Count 1:" + count1);
var explorer2 = outlookApp.ActiveExplorer();
var explorer3 = outlookApp.ActiveExplorer();
var explorer4 = outlookApp.ActiveExplorer();
var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer4);
var count2 = Marshal.ReleaseComObject(explorer4);
MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 6, Equals: True
测试2-参考计数续。
private static void Test2(_Application outlookApp)
{
var explorer1 = outlookApp.ActiveExplorer();
var count1 = Marshal.ReleaseComObject(explorer1);
MessageBox.Show("Count 1:" + count1);
var explorer2 = outlookApp.ActiveExplorer();
var explorer3 = explorer2 as _Explorer;
var explorer4 = (ExplorerEvents_10_Event)explorer2;
var explorerObject = (object)explorer2;
var explorer5 = (Explorer)explorerObject;
var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer5);
var count2 = Marshal.ReleaseComObject(explorer4);
MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 4, Equals: True
除了我的经验和测试之外,我传达的资料来源:
1.约翰内斯通过的-RCW参考计数规则!= com参考计数规则
2. Eran Sandler-运行时可召唤的包装器内部和常见的陷阱
其他提示
我还没有看到RCW的代码 - 甚至不确定它是SSCLI的一部分 - 但是我必须实现类似的系统来跟踪SLIMDX中的COM对象寿命,并且不得不对RCW进行大量研究。这就是我记得的,希望它是合理准确的,但要添加一点盐。
当系统首先看到COM接口指针时,它只是进入缓存以查看该接口指针是否有RCW。据推测,缓存将使用弱参考,以免防止最终确定和收集RCW。
如果该指针有实时包装器,则系统将返回包装器 - 如果以增加接口的参考计数的方式获得了接口,则可能此时RCW系统会调用Release()。它找到了一个现场包装器,因此它知道包装器是一个参考,并且希望准确地维护一个参考。如果缓存中没有实时包装器,它将创建一个新的包装器并将其返回。
最终器的基础COM接口指针上的包装器呼叫发行版。
包装器坐在您和com对象之间,并处理所有参数编制。这还允许其采用任何接口方法的原始结果,该接口方法本身就是另一个接口指针,并通过RCW缓存系统运行该指针,以查看是否存在,然后再返回包装接口指针。
不幸的是,我对RCW系统如何处理代理对象的生成如何在应用程序域或线程公寓中发送内容;这不是我需要复制Slimdx的系统的一个方面。
你不需要任何特殊处理。运行时间只有保持一个参考COM对象。为此原因是,GC轨道所有受管理的参考文献,所以当该项超出范围和收集,COM参考被释放。当你传递一个管理基准,GC是跟踪你-这是最大的优点之一的GC基于运行时老Com/释放方案。
你不需要人工呼元帅。ReleaseComObject除非你想要更多的确定性释放。
这 接受的解决方案 有效,但这里有一些其他背景信息。
RCW在其COM对象内部包含一个或多个本机对象接口引用。
当RCW释放其基础com对象时,要么是由于收集垃圾或 Marshal.ReleaseComObject()
被调用,它发布了所有内部持有的com对象接口。
实际上,这里有很多参考计数 - 一个确定.NET的RCW何时应释放其基础com对象接口,然后每个原始的com接口中的每个com接口都像常规com中一样具有自己的参考数量。
这是获取RAW COM的代码 IUnknown
接口参考计数:
int getIUnknownReferenceCount(object comobject)
{
var iUnknown = Marshal.GetIUnknownForObject(comObject);
return Marshal.Release(iUnknown);
}
您可以使用对象的其他COM接口使用相同的操作 Marshal.GetComInterfaceForObject()
.
除了在 接受的解决方案, ,我们还可以通过调用类似的东西来人为地增加.NET RCW参考数量 Marshal.GetObjectForIUnknown()
.
这是使用该技术来获取给定com对象的RCW参考计数的示例代码:
int comObjectReferenceCount(object comObject)
{
var iUnknown = Marshal.GetIUnknownForObject(comObject);
Marshal.GetObjectForIUnknown(iUnknown);
Marshal.Release(iUnknown);
return Marshal.ReleaseComObject(comObject);
}
你需要打电话 Marshal.ReleaseComObject
在您的WRD变量上,以发布您对单词应用程序的引用。
这样,如果不可见单词,并且您关闭了应用程序,则除非您使用户可见,否则EXE也将卸载。