Excel范围对象的COM Interop RCW引用计数

时间:2015-05-21 15:01:29

标签: c# excel vsto com-interop

据我所知,在COM Interop中,如果我们越过.NET / COM边界,我们会在RCW内部计数器中得到一个增量。 所以我创建了一个VSTO Excel工作簿(2013)应用程序并运行代码:

private void RCWWorkbooks()
{
Excel.Workbooks wbs = Application.Workbooks;Excel.Workbook book1 = wbs[1];
Excel.Workbook book2 = wbs[1];
Excel.Workbook book3 = wbs[1];
Debug.WriteLine("Book3:= " + Marshal.ReleaseComObject(book3));
Debug.WriteLine("Book2:= " + Marshal.ReleaseComObject(book2));
Debug.WriteLine("Book1:= " + Marshal.ReleaseComObject(book1));
}

输出正如我所料:

Book3:=2
Book2:=1
Book1:=0

即。我们有3个引用,RCW中的总计数为3,当我调用ReleaseCOMObject时,每个引用减1;

我为工作表测试做了同样的事情: 这次我得到了结果:

private void RCWSheets()
{
Excel.Sheets wks = Application.Workbooks[1].Worksheets;
Excel.Worksheet sht1 = wks[1];
Excel.Worksheet sht2 = wks[1];
Excel.Worksheet sht3 = wks[1];
Debug.WriteLine("Sheet3:= " + Marshal.ReleaseComObject(sht3));
Debug.WriteLine("Sheet2:= " + Marshal.ReleaseComObject(sht2));
Debug.WriteLine("Sheet1:= " + Marshal.ReleaseComObject(sht1));
}

输出并不完全符合我的预期。

Sheet3:=3
Sheet2:=2
Sheet1:=1

我无法理解为什么sheet3:= 3。我原以为这是2。

接下来,我尝试使用以下代码进行范围测试:

private void RCWRanges()
{
Excel.Worksheet sht = Application.Workbooks[1].Worksheets[1];
Excel.Range r1 = sht.Range["A1"];
Excel.Range r2 = sht.Range["A1"];
Excel.Range r3 = sht.Range["A1"];
Debug.WriteLine("Range3:= " + Marshal.ReleaseComObject(r3));
Debug.WriteLine("Range2:= " + Marshal.ReleaseComObject(r2));
Debug.WriteLine("Range1:= " + Marshal.ReleaseComObject(r1));
}

同样,输出并不像我预期的那样:

Range3:=0
Range2:=0
Range1:=0

所以我的问题是:

  1. 为什么纸张测试会返回额外的计数。它返回了我期待两个的地方。
  2. 为什么范围测试对所有引用计数都返回0?这告诉我,范围请求不会跨越.NET / COM障碍。
  3. 由于

2 个答案:

答案 0 :(得分:0)

  

如果我们越过.NET / COM边界,我们在RCW内部计数器中得到一个增量。

你走错了路。 RCW中没有计数器。计数器位于COM对象中,而不是RCW。 RCW是COM对象的托管表示,它将您对非托管代码的调用编组。

  

为什么表格测试会返回额外的计数。它回到了我期待两个的地方。

可以有任何其他加载项可以使用同一个对象(不仅仅是你)。因此,在这种情况下调用 FinalReleaseComObject 方法可能会破坏应用程序并导致在运行时抛出异常。

  

为什么范围测试对所有参考计数都返回0?这告诉我,范围请求不会跨越.NET / COM障碍。

似乎根据传递给索引器的参数创建了一个新的COM对象。尝试传递不同的值 - A1,B1,C1。我想你会得到同样的照片。

答案 1 :(得分:0)

经过一些调试......

在整个应用程序域的函数调用之间,工作表对象的托管代码中返回并使用的代理仍然存在。 例如,假设您有两个方法称为顺序Method1和Method2,并且每个方法都设置对工作表对象的引用。第一种方法将为工作表创建代理。第二个调用将使用相同的代理来对不同的工作表对象进行不同的引用。所以RCW计数现在是两个。

但是,对于范围对象,同样的代理似乎不是这种情况。看起来像是为不同的范围引用创建了单独的代理。如果你使用测试,你可以看到这个:

Excel.Range r1 = sht1.Range["A1"];
Excel.Range r2 = sht1.Range["A1"];
Debug.WriteLine("r1=r2: " + ReferenceEquals(r1,r2));

返回False。这解释了为什么在原始问题中计数都显示为零。

至于对象清理,我相信最好的方法是做这个组合的东西。为互操作调用拥有自己的app域意味着所有引用都将被正确处理,VSTO会这样做。或者使用垫片。 在某些情况下还有可能将这个完全留给gabage收集器可能不是你需要的东西 - 例如,你可能有很多大型物品想要现在免费而不是等待。在那种情况下,我认为手动发布是好的。还有人工调用垃圾。但我个人认为有什么时候可以打电话给什么。