C#调用Excel退出后,任务管理器里的Excel.exe进程无法即时退出问题

最近写了个软件,要求读取两个Excel文件的数据,分析之后将新数据保存到新的文件中。软件编成后,整个操作在20秒内就完成了。但是退出后会发现任务管理器里有几个Excel.exe的进程,如果运行多次,这个进程数量也会慢慢地增加。

起初,读取和写入数据是分别创建了3个Excel.Application的对象。这样每运行一次,进程里会多出3个Excel.exe的进程。第一次改进是声明一个窗口级的私有变量,在构造函数中初始化变量对象,在窗口关闭事件里清理Excel.Application对象。第一次按网上的方法去执行发现没有效果,后来查了下微软的官方文档才搞清楚是怎么回事。先贴出正确的运行代码:

public partial class MainWindow
{
    ...
    private Excel.Application excelApp;
    public string FirstExcle { get; set; }
    ...

    public MainWindow()
    {
        ...
        excelApp = new Excel.Application();
    }
    
    ...
    private void ReadFirstExcel()
    {
        var myWorkbook = excelApp.Workbooks.Open(FirstExcel); // 注意对象的创建顺序
		var tempData = myWorkbook.Worksheets[1].Range["A:A"];
		var count = excelApp.WorksheetFunction.CountA(tempData); // 此变量不是COM对象

		// 分别读取各列数据
		var first = myWorkbook.Worksheets[1].Range[$"A2:A{count}"];
		var second = myWorkbook.Worksheets[1].Range[$"C2:C{count}"];
		var third = myWorkbook.Worksheets[1].Range[$"D2:D{count}"];
		var fourth = myWorkbook.Worksheets[1].Range[$"F2:F{count}"];
		var fifth = myWorkbook.Worksheets[1].Range[$"J2:J{count}"];

        ...

        // 不保存退出Excel
		myWorkbook.Saved = true;
		myWorkbook.Close();
		  
		// Marshal.ReleaseComObject(fifth);  // 对象的释放顺序与创建顺序相板
		// Marshal.ReleaseComObject(fourth); // 对创建的每个COM对象都要调用一次
		// Marshal.ReleaseComObject(third);
		// Marshal.ReleaseComObject(second);
		// Marshal.ReleaseComObject(first);
		// Marshal.ReleaseComObject(tempData);
		// Marshal.ReleaseComObject(myWorkbook);
        // fifth = null;                    // 使用ReleaseComObject()函数必须得有这些赋空值的语句
        // fourth = null;
        // third = null;
        // second = null;
        // tempData = null;
        // myWorkbook = null;		  

		Marshal.FinalReleaseComObject(fifth);     // 此函数对顺序没有严格的要求
		Marshal.FinalReleaseComObject(fourth);    // 推荐使用FinalReleaseComObject()函数
		Marshal.FinalReleaseComObject(third);
		Marshal.FinalReleaseComObject(second);
		Marshal.FinalReleaseComObject(first);
		Marshal.FinalReleaseComObject(tempData);
		Marshal.FinalReleaseComObject(myWorkbook);		     
    }

    ...

    private void Window_Closing(object sender, CancelEventArgs e)
	{
		if (excelApp != null)
		{
		    excelApp.Quit();
            Marshal.FinalReleaseComObject(excelApp);
			System.GC.Collect(); // 最后调用垃圾收集器进行清理
			// Marshal.ReleaseComObject(excelApp);			
			// excelApp = null; // 此语句需要配合ReleaseComObject()函数

		}
	}

}

 在C#里,清理COM资源有两个函数,分别是

public static int Marshal.ReleaseComObject(object ComObject);	  
public static int Marshal.FinalReleaseComObject(object ComObject);

ReleaseComObject(comObject)相当于调用底层的comObject->Release(),对调用顺序有严格的要求,刚开始我就因为调用顺序错误才导致没有效果。它的调用顺序最好按栈的方式调用,先创建的对象最后释放——也就是后进先出的方式。

FinalReleaseComObject(comObject)是托管代码对ReleaseComObject(comObject)的封装,相当于

while (ReleaseComObject(comObject) != 0);

此函数对顺序没有严格的要求,只要保证程序里对每个com对象调用一次,然后将变量设置为null,最后调用System.GC.Collect()方法即可清理COM资源。微软建议优先使用第二个函数,而第一个函数是让对性能有绝对要求的人使用的。它要求调用顺序一定要正确,否则底层的COM对象超出作用域就释放不了了。

这几天对这两个函数仔细测试了一下,发现在实际程序中使用

Marshal.ReleaseComObject(comObject);

函数没

Marshal.FinalReleaseComObject(comObject);

稳定。使用前者,还得将其comObject赋值为null,但有时候编译后仍然不能完全释放资源,任务管理器里还会存在。使用后者,则不会出现此现象,comObject不需要赋值为null,资源能完全释放掉。看来微软建议的函数还是有其优势的。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值