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

COM Interop RCW Reference Count for an Excel Range Object

据我所知,在 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

我也做了同样的工作sheet测试: 这次我得到了结果:

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.

接下来,我使用以下代码尝试了 运行ge 测试:

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. 为什么 sheet 测试 return 一个额外的计数。它 returned 3 而我期待两个。
  2. 为什么 运行ge 测试所有引用计数的 return 0?这向我表明 运行ge 请求没有跨越 .NET/COM 障碍。

谢谢

if we cross the .NET/COM boundary we get an increment in the RCW internal counter.

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

Why did the sheet test return an extra count. It returned 3 where I was expecting two.

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

Why did the range test return 0 for all the reference counts? This suggests to me that the range request doesn't cross the .NET/COM barrier.

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

经过一些调试...

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

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

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

这个returns错了。这就解释了为什么在原始问题中计数都显示为零。

至于对象清理,我认为最好的办法是综合使用。为互操作调用拥有自己的应用程序域意味着所有引用都将得到正确处理,VSTO 就是这样做的。或者使用垫片。 在某些情况下也有可能完全将其留给垃圾收集器可能不是您所需要的——例如,您可能有很多大对象现在想要释放而不是等待。在那种情况下,我认为手动发布很好。还有手动调用垃圾。但是我个人认为什么时候调用什么是有不同情况的。