Office开发必备知识----为什么要释放非托管Com资源
https://www.cnblogs.com/Charltsing/p/RealeaseComObject.html
QQ:564955427
目前,国内Office插件开发的风头正盛,很多VBAer都纷纷加入到vb.net或者C#等托管语言的插件开发大军中,但是大部分人从vba转到托管语言的时候,都没有从理论上学习一下托管语言的特性,直接使用vba代码暴力翻译成托管语言,简单粗暴地在代码中使用,只要代码不报错就认为程序没问题了。
然而,直接在代码中暴力使用Com对象会在托管对象中造成释放问题,引发内存泄漏,严重时可能会导致Excel等宿主程序报错,为了解决这个问题,我下面简单介绍一下在托管代码中使用非托管Com资源的释放问题。
1、什么是Com对象
Com是微软提出来的在组件程序之间进行交互的标准,以Excel为例:application,workbook,workbooks,sheet,sheets,range等等都是Com对象。
2、为什么要释放Com对象
Office程序是非托管语言编写的,C#之类的托管语言要去操作非托管语言编写的程序资源就要解决数据交换的格式和结构问题,微软采用Interop(互操作程序集)来解决这个问题。当托管语言访问非托管语言组件的时候一般通过Com方式进行,CLR会给每个COM对象按每进程生成一个RCW(Runtime Callable Wrappers运行时可调用包装器),并用计数器记录Com对象被引用的次数,每引用一次,计数器加1;每释放一次,计数器减1。这种RCW包装会带来额外的资源开销,当计数器为0的时候,RCW资源才会被释放。所以,如果不释放Com对象,RCW(与此类似的还有CCW,COM Callable Wrapper)会造成相关内存始终被占用,即使Com对象消失,内存也不会被释放,这就是所谓的内存泄漏。
3、不释放Com资源会有什么问题
内存泄漏会导致程序可用资源被耗尽,进而产生不可预料的问题。除此之外,由于RCW的权限高于应用程序,所以只有当所有的Com资源被释放之后,应用程序的进程才能退出。这就是为什么有些人的程序在执行完毕退出之后,后台还会残留Office进程的原因。
4、我没有在代码中释放Com资源貌似程序运行得也挺好,而且msdn在VSTO开发中也没有强调这一点,为什么?
如果代码书写得当的话,GC会在后台处理Com的释放问题,同时处理RCW资源。而且从Office2007开始,应用程序在退出的时候,会处理一些泄漏的对象。所以在微软MSDN中,只是要求不要丢失对Com资源的引用即可。只要不在代码中使用隐含的Com对象引用,GC是可以处理大部分Com资源释放问题的。
5、既然GC可以解决大部分Com资源释放问题,为什么还要谈这个释放问题呢?
因为GC有些时候不靠谱。依赖GC做资源释放的最大问题是你无法控制资源的释放时机,当你处理了成千上万的单元格、工作表和工作簿之后(注意同一个工作簿只有一个RCW,而同一个Range因为引用的不同可能会有无数个RCW),你不知道是不是还有足够的内存给你做下一步操作,这会给商业程序开发带来不可预计的风险。此外,Com对象的事件订阅和取消订阅也是个大问题,GC是无法100%正确处理此类问题的,这有可能会造成严重的内存泄漏甚至宿主报错崩溃。此外,根据开发人员能力的不同,还可能会发生更严重的循环引用问题,造成Com对象的死锁,导致RCW无法释放。
6、如何手工进行非托管Com资源的释放?
参见下面的代码
注意代码的书写要慎重,不得使用会丢失引用的Com对象,例如下面的代码是一个错误示例:
上面的示例只是演示了最简单情况下COM资源的释放,实际应用中,由于GC的存在以及宿主程序对泄漏内存的处理,上面的ReleaseComObject语句也可以不需要执行。但是,不执行ReleaseComObject的前提是,开发人员应该按照规范书写代码,不要丢失对Com资源的引用,更不能造成Com资源的循环引用以及由于事件订阅导致的内存泄漏。同时还要处理好try-catch-finally的问题。
7、手工进行非托管Com资源的释放的坏处是什么?
很明显,你的代码会因显式存储和释放COM代理对象而变得臃肿和不可读。此外,各种事件中Com参数的传递也会造成复杂的RCW计数问题,这可能会导致无法正确的手工释放资源。
此外,滥用Marshal.ReleaseComObject,特别是Marshal.FinalReleaseComObject,可能会造成RCW为零断开Com对象资源,导致同时运行的其他程序或插件无法继续使用某些Com对象。因此手工释放Com资源对开发人员的素质要求较高。
顺便再补充一点,微软并不赞成在代码中显式地调用ReleaseComObject,这里牵涉到很复杂的原因。
如果你明白其中的深层次问题,不想使用ReleaseComObject的话,也许在Com对象使用完毕之后,调用两次GC来释放它也是个省事的办法。
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
因为从Office2007开始,应用程序会在退出的时候处理泄漏的对象引用,所以,在很多时候,你不会发现内存泄漏之后有什么问题,因此采用上面的方式也可以凑合解决泄漏问题。当然前提依然是开发人员应该按照规范书写代码。
当然,上述办法并不是万能的良药,在调用GC之前,你可能还需要处理一下其它的问题,以后再慢慢细述。
8、关于VSTO是否需要调用ReleaseCOMObject的问题,答案如下:
原则上你不需要在VSTO中使用ReleaseCOMObject。VSTO为每一个VSTO Add-Ins创建了一个AppDomain,当卸载VSTO Addin的时候,AppDomian也会被卸载,CLR会卸载与其相关的所有资源。但是特殊情况例外。
例如:你在一个Word中操作了PowerPoint的资源(相当于PIA操作Office),后者可能需要开发者正确使用ReleaseCOMObject来确保application能够正常关闭。
同样,需要注意的是,VSTO并不能及时地释放COM占用的资源,这可能会导致大量的内存占用,这一点和第五条谈到的GC的原因是一致的。
9、有没有其它的方案?
你可以有以下几个选择:
a、不使用pia或vsto,改用第三方控件,例如npoi之类的。
b、使用SafeComWarpper包装器之类的开源项目,为每个Com对象建立一个Dispose(可能会影响效率)。
c、微软推荐过shim和Appdomain,后者是VSTO的方案。
d、通过ExcelDna调用C API也是个可以考虑的选择。
10、总结
a.对于一般的Office插件开发来说,如果你在使用VSTO开发Office插件,那么大部分时候不需要在代码中释放Com资源,AppDomain会很好地解决这个问题。除非你需要及时释放内存空间,或者调用了另一个Com宿主(这相当于PIA操作Office)。
b.对于普通的Com应用,例如通过PIA操作Office,一定要慎重地考虑Com资源释放问题。是否使用ReleaseComObject取决于开发者的技术能力。如有可能,建议使用现成的一些SafeComWarpper包装器开源项目来最大程度上解决Com资源自动释放问题。
c.如果出于资源占用方面的考虑,或者你的程序需要支持2003或更早期的版本,你只能手工释放Com资源,请在代码开发中认真测试Com资源的RCW计数变化情况,然后慎重的使用ReleaseComObject,并且在不了解你在做什么的情况下绝不要使用FinalReleaseComObject。适当地调用GC,可以很好地帮助释放资源。
一些参考资料
https://www.add-in-express.com/creating-addins-blog/2011/11/04/why-doesnt-excel-quit/
https://www.add-in-express.com/creating-addins-blog/2008/10/30/releasing-office-objects-net/
https://www.add-in-express.com/creating-addins-blog/2013/11/05/release-excel-com-objects/
http://www.it1352.com/533884.html 我何时应使用 Marshal.FinalReleaseComObject 与 Marshal.ReleaseComObject ?
https://blogs.msdn.microsoft.com/yvesdolc/2004/04/17/discussion-of-marshal-releasecomobject-and-its-dangers/
http://jake.ginnivan.net/vsto-com-interop/
https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2003/aa679806(v%3doffice.11)
https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2003/aa679807(v%3doffice.11)
https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2003/aa679808(v%3doffice.11)
Office开发必备知识----为什么要释放非托管Com资源的更多相关文章
- 5.C#释放非托管资源1
释放非托管资源 在介绍释放非托管资源的时候,我觉得有必要先来认识一下啥叫非托管资源,既然有非托管资源,肯定有托管资源. 托管资源指的是.net可以自棕进行回收的资源,主要是指托管堆上分配的内存资源.托 ...
- C#编程(七十四)----------释放非托管资源
释放非托管资源 在介绍释放非托管资源的时候,我觉得有必要先来认识一下啥叫非托管资源,既然有非托管资源,肯定有托管资源. 托管资源指的是.net可以自棕进行回收的资源,主要是指托管堆上分配的内存资源.托 ...
- 移动web开发(一)——移动web开发必备知识
参考: 移动终端开发必备知识.http://isux.tencent.com/mobile-development-essential-knowledge.html
- C# 释放非托管资源
C#中资源分为托管资源和非托管资源. 托管资源由垃圾回收器控制如何释放,不需要程序员过多的考虑(当然也程序员也可以自己释放). 非托管资源需要自己编写代码来释放.那么编写好的释放非托管资源的代码(释非 ...
- 6.C# 释放非托管资源2
C# 释放非托管资源 C#中资源分为托管资源和非托管资源. 托管资源由垃圾回收器控制如何释放,不需要程序员过多的考虑(当然也程序员也可以自己释放). 非托管资源需要自己编写代码来释放.那么编写好的释放 ...
- Dispose模式释放非托管资源
实现方式用的是设计模式里的模板模式,基类先搭好框架,子类重写void Dispose(bool disposing) 即可. 需要注意的是基类的Finalize函数也就是析构函数调用的是虚函数void ...
- [转]在C#中使用托管资源和非托管资源的区别,以及怎样手动释放非托管资源:
托管资源指的是.NET可以自动进行回收的资源,主要是指托管堆上分配的内存资源.托管资源的回收工作是不需要人工干预的,有.NET运行库在合适调用垃圾回收器进行回收. 非托管资源指的是.NET不知道如何回 ...
- IDisposable?释放非托管资源接口
原文:https://www.cnblogs.com/luminji/archive/2011/03/29/1997812.html IDisposable高级篇:https://docs.micro ...
- 【Bugly 技术干货】Android开发必备知识:为什么说Kotlin值得一试
1.Hello, Kotlin Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 1. ...
随机推荐
- JSON 与 JS 对象的区别与对比
定义: JSON是什么?JSON是JS的一种简单数据格式,JSON是JavaScript原生格式,它是一种严格的js对象的格式,JSON的属性名必须有双引号,如果值是字符串,也必须是双引号. 问题: ...
- WPF xaml中列表依赖属性的定义
列表内容属性 如上图,是一个列表标题排序控件,我们需要定义一个标题列表,从而让调用方可以自由的设置标题信息. 在自定义控件时,会遇到列表依赖属性,那么该如何定义呢? 下面是错误的定义方式: /// & ...
- spring boot 页面根路径获取和jsp获取的不同之处(粘贴即用)
不同之处已做高亮. jsp 写法: <script type="text/javascript" src="${pageContext.request.contex ...
- response.writeHead
response.writeHead(statusCode[, statusMessage][, headers]) 查看英文版 版本历史 statusCode <number> stat ...
- CSS3图片翻转动画技术详解
CSS动画非常的有趣:这种技术的美就在于,通过使用很多简单的属性,你能创建出漂亮的消隐效果.其中代表性的一种就是CSS图片翻转效果,能让你看到一张卡片的正反两面上的内容.本文就是要用最简单的方法向大家 ...
- 【20190405】JavaScript-正则式匹配与替换结果解析
在正则式的应用中有三个函数使用得最多:exec().test()与字符串的replace(reg, options).其中test()最简单,只要字符串与正则式可以匹配,就返回true,否则返回fal ...
- JS最简单的字符串转数字类型
以前无休止的写parseInt,特别复杂,现在只需要一个加号,就可以完成对字符串的转换 1 === +"1" amazing!
- 12月16日广州.NET俱乐部下午4点爬白云山活动
正如我们在<广州.NET微软技术俱乐部与其他技术群的区别>和<广州.NET微软技术俱乐部每周三五晚周日下午爬白云山活动>里面提到的, 我们会在每周三五晚和周日下午爬白云山. ...
- 微信小程序(四) 模板的使用
模板的使用:单独建立一个页面,在另一个页面通过name属性名调用使用(注意要导入模板路径) template.wxml页面
- PHP实现开心消消乐的算法示例
本文主要介绍了关于PHP如何实现我们大家都知道的开心消消乐的算法,分享PHP教程出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 一.需求描述: 1.在一个8*8的矩阵方格中随机 ...