如何释放 COM Interop 对象以便被调用程序能够退出






4.96/5 (11投票s)
COM Interop 运行时可调用包装器 (RCW) 是一个“黑盒”,它创建对需要释放的内存的引用,才能使被调用程序结束。
引言
如果在你的 .NET 代码中没有正确释放 COM Interop 对象,那么你的 .NET 程序通过 COM Interop 通信的程序将仍然停留在 Windows 操作系统任务列表中,没有可见窗口。你可以在任务管理器的“详细信息”选项卡中看到它。
背景
公共语言运行时通过一个名为运行时可调用包装器 (RCW) 的代理暴露 COM 对象。对于 .NET 应用程序来说,RCW 看起来像一个普通的 .NET 对象。它处理 .NET 客户端和 COM 对象之间的调用(例如,EXCEL.EXE 应用程序和 EXCEL.EXE 中的对象),在 .NET 客户端和 COM 对象之间传递参数和返回值;并在必要时进行转换。
对于每个 COM 对象,.NET 运行时都会创建一个 RCW,无论对该对象的引用数量多少。每次对已实例化 COM 对象的 RCW 建立新的引用时,引用计数都会增加。调用 COM 对象的“Quit
”或“Exit
”方法,然后调用“Dispose
”方法,并不能释放所有引用。本技巧中的示例代码将确保释放所有引用,以便被调用的 COM 程序能够正确结束。
COM Interop 的编码模型
- 仅在需要时才实例化 COM 对象。
- 使用
FinalReleaseComObject(obj)
在对象不再需要时释放它们。 - 首先调用
FinalReleaseComObject
来释放最近创建的对象。 - 使用 .NET 垃圾回收方法来强制垃圾回收。
COM Interop 示例
我最初的示例是 VB .NET 代码。我使用了 developerFusion VB .NET 到 C# 转换工具 将我的 VB .NET 代码转换为本技巧中包含的 C# 代码。
VB 示例
' Declare a COM object
Dim oApp As Excel.Application
' Start Excel
oApp = DirectCast(CreateObject("Excel.Application"), Excel.Application)
oApp.Visible = True
Application.DoEvents()
' Open file
oApp.Workbooks.Open(strNewFilename, Excel.XlUpdateLinks.xlUpdateLinksNever)
Application.DoEvents()
' Run the CreateInventoryWorksheets Macro
oApp.Run("CreateInventoryWorksheets")
Application.DoEvents()
' Macro is finished
' Close the file
oApp.ActiveWorkbook.Close(True)
Application.DoEvents()
' Tell Excel to quit
oApp.Quit()
Application.DoEvents()
'
'
' This sample has only one COM object declared.
' For programs with multiple COM objects declared, repeat the following two
' lines of code for each COM object in reverse order of creation.
'
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oApp)
oApp = Nothing ' Set each COM Object to Nothing
'
' After all of the COM objects have been released and set to Nothing, do the following:
GC.Collect() ' Start .NET CLR Garbage Collection
GC.WaitForPendingFinalizers() ' Wait for Garbage Collection to finish
C# 示例
// Declare a COM object
Excel.Application oApp = default(Excel.Application);
// Start Excel
oApp = (Excel.Application)Interaction.CreateObject("Excel.Application");
oApp.Visible = true;
Application.DoEvents();
// Open file
oApp.Workbooks.Open(strNewFilename, Excel.XlUpdateLinks.xlUpdateLinksNever);
Application.DoEvents();
// Run the CreateInventoryWorksheets Macro
oApp.Run("CreateInventoryWorksheets");
Application.DoEvents();
// Macro is finished
// Close the file
oApp.ActiveWorkbook.Close(true);
Application.DoEvents();
// Tell Excel to quit
oApp.Quit();
Application.DoEvents();
//
//
// This sample has only one COM object declared.
// For programs with multiple COM objects declared, repeat the following two
// lines of code for each COM object in reverse order of creation.
//
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oApp);
oApp = null; // Set each COM Object to null
//
// After all of the COM objects have been released and set to null, do the following:
GC.Collect(); // Start .NET CLR Garbage Collection
GC.WaitForPendingFinalizers(); // Wait for Garbage Collection to finish
关注点
以下是关于 COM Interop 的几篇文章的链接。感谢这些作者撰写了优秀的文章。
- .NET - COM 互操作性
- 了解 .NET 应用程序中的经典 COM 互操作性
- .NET 中正确释放 COM 对象的方法
- .NET COM 互操作 - 与 RCW 内存泄漏作斗争
- 运行时可调用包装器
历史
- 2013 年 3 月 29 日 - 初始版本
- 2013 年 10 月 23 日 - 细微修改
- 2013 年 11 月 12 日 - 删除了 ReleaseComObject 的 While 循环,并替换为 FinalReleaseComObject