这是一个非常常见的问题,我决定问这个问题,因为这个问题在今天可能会有不同的答案。希望这些答案有助于理解使用COM对象的正确方法。 就个人而言,在对此主题发表不同意见后,我感到非常困惑。
过去5年,我曾经使用COM对象,规则对我来说非常清楚:
你们中的一些人在阅读完最后一行后可能会感到沮丧,这就是我所知道的如何正确创建/发布Com对象,我希望能得到更清晰无误的答案。
以下是我在此主题中找到的一些链接。他们中的一些人告诉他们需要调用ReleaseComObject而其中一些不需要。
“......在VSTO场景中,您通常不必使用ReleaseCOMObject ....”
“......您应该使用此方法释放包含引用的基础COM对象......”
更新
此问题已被标记为过于宽泛。根据要求,我将尝试简化并提出更简单的问题。
答案 0 :(得分:16)
.NET / COM互操作设计精良,工作正常。特别是,.NET垃圾收集器正确跟踪COM引用,并且当它们没有剩余的运行时引用时将正确释放COM对象。通过调用Marshal.ReleaseComObject(...)
或Marshal.FinalReleaseComObject(...)
来干扰COM对象的引用计数是一种危险但常见的反模式。不幸的是,一些不好的建议来自微软。
您的.NET代码可以正确地与COM交互,同时忽略所有5条规则。 如果确实需要触发不再从运行时引用的COM对象的确定性清理,则可以安全地强制GC(并可能等待终结器完成)。否则,您不必在代码中执行任何特殊操作,以处理COM对象。
有一个重要的警告,可能导致混淆垃圾收集器的作用。在调试.NET代码时,局部变量人为地将其生命周期延长到方法的末尾,以支持在调试器下观察变量。这意味着您可能仍然拥有对COM对象的托管引用(因此GC不会清理),而不仅仅是期望表单只是查看代码。此问题的一个很好的解决方法(仅在调试器下发生)是从GC清理调用中拆分COM调用的范围。
例如,这里有一些与Excel交互的C#代码,并且可以正常清理。您可以粘贴到控制台应用程序中(只需添加对Microsoft.Office.Interop.Excel的引用):
using System;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;
namespace TestCsCom
{
class Program
{
static void Main(string[] args)
{
// NOTE: Don't call Excel objects in here...
// Debugger would keep alive until end, preventing GC cleanup
// Call a separate function that talks to Excel
DoTheWork();
// Now let the GC clean up (repeat, until no more)
do
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
while (Marshal.AreComObjectsAvailableForCleanup());
}
static void DoTheWork()
{
Application app = new Application();
Workbook book = app.Workbooks.Add();
Worksheet worksheet = book.Worksheets["Sheet1"];
app.Visible = true;
for (int i = 1; i <= 10; i++) {
worksheet.Cells.Range["A" + i].Value = "Hello";
}
book.Save();
book.Close();
app.Quit();
// NOTE: No calls the Marshal.ReleaseComObject() are ever needed
}
}
}
您将看到Excel进程正确关闭,表明所有COM对象都已正确清理。
VSTO不会改变任何这些问题 - 它只是一个包装和扩展本机Office COM对象模型的.NET库。
关于此问题存在大量虚假信息和混淆,包括MSDN和StackOverflow上的许多帖子。
最终让我深入了解并找出正确建议的是这篇文章https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-considered-dangerous/以及在StackOverflow答案的调试器下发现引用保持活动的问题。
此一般指导的一个例外是COM对象模型要求以特定顺序释放接口。此处描述的GC方法无法控制GC释放COM对象的顺序。
我没有提及是否会违反COM合同。通常,我希望COM层次结构使用内部引用来确保正确管理对序列的任何依赖性。例如。在Excel的情况下,可以期望Range对象保持对父Worksheet对象的内部引用,以便对象模型的用户不需要显式保持两者都活着。
有些情况甚至Office应用程序对释放COM对象的顺序很敏感。一个案例似乎是使用OLE嵌入时 - 请参阅https://blogs.msdn.microsoft.com/vsofficedeveloper/2008/04/11/excel-ole-embedding-errors-if-you-have-managed-add-in-sinking-application-events-in-excel-2/
因此,如果对象以错误的顺序发布,则可能会创建一个失败的COM对象模型,并且与这种COM模型的互操作将需要更多的关注,并且可能需要手动干涉引用。 / p>
但是对于与Office COM对象模型的一般互操作,我同意VS blog post调用&#34; Marshal.ReleaseComObject - 一个伪装成解决方案的问题&#34;。