症状
从 Microsoft Microsoft Office .NET Visual Basic Microsoft Visual C# .NET 自动执行 Office 时,调用 Quit 方法时,Office 应用程序不会退出。
原因
当 Visual Studio .NET 从托管代码调用 COM 对象时,它会自动创建运行时可调用包装器 (RCW) 。 RCW 会封送 .NET 应用程序和 COM 对象之间的调用。 RCW 对 COM 对象保留引用计数。 因此,如果 RCW 上尚未释放所有引用,则 COM 对象不会退出。
解决方法
若要确保应用程序Office,请确定自动化代码是否满足以下条件:
-
将每个对象声明为新变量。 例如,更改以下代码行:
oBook = oExcel.Workbooks.Add()
将此选项更改为以下内容:
dim oBooks as Excel.Workbooks oBooks = oExcel.Workbooks oBook = oBooks.Add()
-
在循环中使用 System.Runtime.InteropServices.Marshal.ReleaseComObject,直到完成使用 对象后返回 0。 System.Runtime.InteropServices.Marshal.ReleaseComObject 会减去 RCW 的引用计数,并且循环确保基础 COM 组件已释放,不管该组件已重新输入 CLR 多少次。
-
若要释放对变量的引用,将变量设置为"无"或"Null"。
-
使用应用程序对象的 quit Office告知服务器关闭。
状态
此行为是设计使的。
更多信息
重现行为的步骤
-
启动 Visual Studio .NET。
-
在"文件"菜单上,单击"新建",然后单击"Project"。 在"Visual Basic"下,选择"Windows应用程序",然后单击"确定"。
注意 默认情况下创建 Form1。 -
添加对对象Microsoft Excel的引用。 为此,请按照下列步骤操作:
-
在"Project菜单上,单击"添加引用"。
-
在"COM"选项卡上,找到"对象库"Excel,然后单击"选择"。
对于 Microsoft Excel 2002:Microsoft Excel 10.0
对象库注意 如果尚未这样做,建议下载并安装 Microsoft Office XP 主互操作程序集 (PIA) 。
有关 XP PIA Office,请转到以下 Microsoft 知识库文章:328912 Microsoft Office XP 主互操作程序集 (PIA) 可供下载
对于 Microsoft Office Excel 2003:Microsoft Excel 11.0 对象库 -
在"添加引用"对话框中单击"确定"以接受选择。
-
-
在"视图"菜单上,单击"工具箱",然后将按钮控件拖动到 Form1 上。
-
双击"Button1"。
注意将显示窗体的代码窗口。 -
将以下代码添加到 Form1.vb 的顶部:
Imports Microsoft.Office.Interop
-
替换代码窗口中的以下代码:
Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click End Sub
替换以下代码:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim oApp As New Excel.Application() Dim oBook As Excel.Workbook = oApp.Workbooks.Add Dim oSheet As Excel.Worksheet = oApp.ActiveSheet oSheet = Nothing oBook.Close(False) oBook = Nothing oApp.Quit() oApp = Nothing Debug.WriteLine("Sleeping...") System.Threading.Thread.Sleep(5000) Debug.WriteLine("End Excel") End Sub
-
按 F5 运行应用程序。
-
打开Windows管理器"。 在Visual Studio,显示"输出"窗口以查看调试消息。 单击命令按钮。 请注意,进程Excel.exe一个实例。
-
即使应用程序Excel,Excel实例仍运行在任务列表中。 关闭对话框,注意Excel不再显示在"进程"列表中。
-
执行"解析"部分中的步骤时,Office在释放最后一个变量后退出。 使用以下代码替换步骤 5 中的 函数:
Private 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 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 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,而不是调用 System.Runtime.InteropServices.Marshal.ReleaseComObject 的 while 循环来实现相同的结果。
疑难解答
备注 如果按照"重现行为的步骤"部分中所述的步骤操作,但服务器仍未关闭,可以使用 GC。收集 () 方法和 GC。释放最后一个对象后,WaitForPendingFinalizers () 方法。 因为运行时在 RCW 上执行垃圾回收,所以 GC。Collect () 方法强制垃圾回收器运行,并可能释放 RCW 仍然具有的任何引用。 GC。Collect () 方法尝试回收可用的最大内存。 请注意,这并不保证将回收所有内存。
适用范围
本文也适用于:
-
Microsoft Visual Basic .NET (所有版本)
-
Microsoft Visual C# .NET (所有版本)
-
Microsoft Office 2016(所有版本)
-
Microsoft Office 2013(所有版本)