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. ...
随机推荐
- 如何使用在三星840 SSD硬件加密下使用Bitlocker
我的笔记本存放到比较重要的资料,需要加密,想到我的三星EVO 850 SSD支持三种加密模式,于是想使用全盘加密模式.但是找了半天没找到一点合适的中文资料,于是怒而翻译一下加密全盘加密的Bitlock ...
- 使用Linq查找重复
namespace RemoveTheSame { class Program { static void Main(string[] args) { List<User> list = ...
- WPF DesiredSize & RenderSize
DesiredSize DesiredSize介绍 关于DesiredSize的介绍,可以查看最新微软文档对DesiredSize的介绍 DesiredSize,指的是元素在布局过程中计算所需要的大小 ...
- C# 提取PPT文本和图片的实现方案
在图文混排的文档中,我们可以根据需要将文档中的文字信息或者图片提取出来,通过C#代码可以提取Word和PDF文件中的文本和图片,那么同样的,我们也可以提取PPT幻灯片当中的文本和图片.本篇文档将讲述如 ...
- 设计模式之 SOA面向服务的体系
SOA英文直译是,面向服务的体系结构. SOA是一种设计方法,其中包含多个服务,而服务之间通过配合最终会提供一系列功能.一个服务通常以独立的形式存在于操作系统进程中. 想要看到更多玮哥的学习笔记.考试 ...
- H5页面的高度宽度100%
解决方案1: 设置如下:html,body{ min-height:100vh; background-color:#fff; }这样高度首先不会写死,而且满足最小高度是满屏 解决方案2: 可以用vh ...
- BGP:所有邻居都启动了BGP,则无须建立首尾逻辑邻居,否则就需要首尾建立逻辑邻居。
配置说明:都通过loopback 口作为bgp 连接口,并且要配置ebgp多跳,同时配置loopback口的静态路由. 以AR2为例: 第一种场景:所有直接相连的邻居都启动了BGP,则路由可以随意扩散 ...
- 网络编程中TCP基础巩固以及Linux打开的文件过多文件句柄的总结
1.TCP连接(短链接和长连接) 什么是TCP连接?TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议. 当网络通信 ...
- CADisplayLink以及定时器的使用
第一种: 用CADisplayLink可以实现不停重绘. - (CADisplayLink *)link { if (!_link) { // 创建定时器,一秒钟调用rotation方法60次 _li ...
- 【转载】DSP基础--定点小数运算
在FPGA实现算法过程中,大多数情况是用占用资源较少,延迟较低的定点数代替浮点数参与运算.那么浮点与定点数之间的区别以及转换方式是怎么的?下边这篇博文详细说明了这一问题.虽然是针对DSP芯片的,但思想 ...