症状
在使用.net调用 Microsoft Office 应用程序时,Office 应用程序在调用Quit方法时不会退出。
原因
Visual Studio.NET 从托管代码调用 COM 对象时,它会自动创建运行时可调用包装 (RCW)。RCW 将.NET 应用程序和 COM 对象之间的调用封送。RCW 保持该 COM 对象上的引用计数。因此,如果尚未释放 RCW 上的所有引用,COM 对象不会退出。
解决方案
若要确保 Office 应用程序将退出,确保自动化代码满足以下条件:
- 将每个对象声明为新变量。例如,将下面的代码行的更改
oBook = oExcel.Workbooks.Add()
dim oBooks as Excel.WorkbooksoBooks = oExcel.WorkbooksoBook = oBooks.Add()
- 在循环中使用System.Runtime.InteropServices.Marshal.ReleaseComObject ,直到完成后使用的对象,则返回 0。System.Runtime.InteropServices.Marshal.ReleaseComObject递减 RCW,而该环路的引用计数将确保不管如何释放基础 COM 组件很多时候它已重新进入 CLR。
- 若要解除对变量的引用,设置为空值或空值的变量等。
- 使用 Office 应用程序对象的Quit方法告诉服务器关闭。
示例
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim oApp As New Excel.Application() Dim oBooks As Excel.Workbooks = oApp.Workbooks Dim oBook As Excel.Workbook = oBooks.Add Dim oSheet As Excel.Worksheet = oApp.ActiveSheet NAR(oSheet) oBook.Close(False) NAR(oBook) NAR(oBooks) oApp.Quit() NAR(oApp) Debug.WriteLine("Sleeping...") System.Threading.Thread.Sleep(5000) Debug.WriteLine("End Excel")End SubPrivate Sub NAR(ByVal o As Object) Try While (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0) End While Catch Finally o = Nothing End Try End Sub
如果您使用的 Visual C#.NET,引用NAR()函数的代码:
private void NAR(object o){ try { while (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0) ; } catch {} finally { o = null; }}
注:从开始.NET Framework 2.0,您可以使用System.Runtime.InteropServices.Marshal.FinalReleaseComObject来代替 while 循环调用System.Runtime.InteropServices.Marshal.ReleaseComObject来实现相同的结果。
故障排除
请注意,如果您按照中所述的步骤"步骤重现行为"一节,并在服务器仍然没有关闭向下,您可以使用GC。Collect()方法和GC。WaitForPendingFinalizers()后释放的最后一个对象的方法。因为运行库对GC,RCW 执行垃圾回收。Collect()方法强制垃圾回收器在运行,并可能释放任何仍有 RCW 的引用。GC。Collect()方法尝试回收可用的最大内存。请注意,这不能保证所有内存都被都回收。