Silverlight 操作Excel 中的进程资源释放问题(续)
在前几天写到的Silverlight 操作Excel 中的进程资源释放问题 中,存在很多不完善的地方,因为在BeforeClose中处理掉Excel资源,会造成Excel无法再进行与代码的部分控制进行交互了。
于是,继续谷歌……发现一篇很不错的文章,其Demo的效果也正是我想要的,于是,Mark一下。
原文是日语的,借助翻译还是能看懂吧:(
原问题的地址是:How to release COM objects in Silverlight 4,作者地址:http://csfun.blog49.fc2.com/blog-entry-79.html (好像要用代理才能看),Demo下载地址:SilverOffice。
以下是根据我个人的理解,翻译并整理的两个关键的类。
1、ComObjectWrapper,实现IDisposable接口
1 public class ComObjectWrapper : IDisposable 2 { 3 /// <summary> 4 /// Com对象被释放时的事件 5 /// </summary> 6 internal event EventHandler ComObjectDisposed; 7 8 //对象是否被释放 9 private Boolean _disposed = false; 10 11 /// <summary> 12 /// Com对象集合 13 /// </summary> 14 private List<ComObjectWrapper> _children = new List<ComObjectWrapper>(); 15 16 /// <summary> 17 /// Com对象是否被释放的的标记 18 /// true情况下释放出来 19 /// </summary> 20 public Boolean Disposed 21 { 22 get 23 { 24 return _disposed; 25 } 26 } 27 28 /// <summary> 29 /// 增加对象到集合,并为对象绑定释放事件 30 /// </summary> 31 /// <param name="child"></param> 32 protected void AddChildren(ComObjectWrapper child) 33 { 34 _children.Add(child); 35 child.ComObjectDisposed += new EventHandler(child_ComObjectDisposed); 36 } 37 38 /// <summary> 39 /// 子对象被释放时在集合移除 40 /// </summary> 41 /// <param name="sender"></param> 42 /// <param name="e"></param> 43 private void child_ComObjectDisposed(object sender, EventArgs e) 44 { 45 _children.Remove(sender as ComObjectWrapper); 46 } 47 48 /// <summary> 49 /// 释放Com对象(集合所有对象) 50 /// </summary> 51 private void ReleaseChildren() 52 { 53 foreach (ComObjectWrapper child in _children) 54 { 55 child.ComObjectDisposed -= new EventHandler(child_ComObjectDisposed); 56 child.Dispose(); 57 } 58 59 _children.Clear(); 60 } 61 62 /// <summary> 63 /// 执行释放资源(虚函数) 64 /// </summary> 65 protected virtual void DoDispose() { } 66 67 #region IDisposable Members 68 69 /// <summary> 70 /// 释放Excel程序资源 71 /// </summary> 72 public void Dispose() 73 { 74 try 75 { 76 //释放对象 77 ReleaseChildren(); 78 79 DoDispose(); 80 81 _disposed = true; 82 83 if (ComObjectDisposed != null) 84 { 85 ComObjectDisposed(this, EventArgs.Empty); 86 } 87 } 88 catch { } 89 } 90 91 #endregion 92 }
2、InstanceManager类,对所有Excel的Com对象进行管理
1 public class InstanceManager 2 { 3 /// <summary> 4 /// Excel对象实例集合 5 /// </summary> 6 private static List<ComObjectWrapper> _excelApplications = new List<ComObjectWrapper>(); 7 8 /// <summary> 9 /// 新增一个Excel对象 10 /// </summary> 11 /// <returns></returns> 12 public static ExcelApplication CreateAppication() 13 { 14 //自动化功能是否可用 15 if (!AutomationFactory.IsAvailable) return null; 16 17 dynamic excelObject = AutomationFactory.CreateObject("Excel.Application"); 18 19 ExcelApplication application = new ExcelApplication(excelObject); 20 _excelApplications.Add(application); 21 application.ComObjectDisposed += new System.EventHandler(application_ComObjectDisposed); 22 return application; 23 } 24 25 /// <summary> 26 /// Com对象被释放时的事件 27 /// </summary> 28 /// <param name="sender"></param> 29 /// <param name="e"></param> 30 static void application_ComObjectDisposed(object sender, System.EventArgs e) 31 { 32 try 33 { 34 _excelApplications.Remove(sender as ComObjectWrapper); 35 } 36 catch { } 37 } 38 39 /// <summary> 40 /// 取得已经运行的Excel进程实例。 41 /// </summary> 42 /// <param name="create"></param> 43 /// <returns></returns> 44 public static ExcelApplication GetApplication(bool create) 45 { 46 //自动化功能是否可用 47 if (!AutomationFactory.IsAvailable) return null; 48 49 dynamic excelObject = null; 50 51 //在已经生成的com对象集合中取得没有被释放的实例 52 foreach (ExcelApplication excel in _excelApplications) 53 { 54 if (!excel.Disposed) 55 { 56 return excel; 57 } 58 } 59 60 try 61 { 62 excelObject = AutomationFactory.GetObject("Excel.Application"); 63 } 64 catch 65 { 66 if (create) 67 { 68 excelObject = AutomationFactory.CreateObject("Excel.Application"); 69 } 70 } 71 72 if (excelObject != null) 73 { 74 ExcelApplication application = new ExcelApplication(excelObject); 75 _excelApplications.Add(application); 76 application.ComObjectDisposed += new System.EventHandler(application_ComObjectDisposed); 77 return application; 78 } 79 else 80 { 81 return null; 82 } 83 } 84 85 /// <summary> 86 /// 释放所有Excel进程 87 /// </summary> 88 public static void ReleaseAll() 89 { 90 foreach (ComObjectWrapper wrapper in _excelApplications) 91 { 92 wrapper.ComObjectDisposed -= new EventHandler(application_ComObjectDisposed); 93 wrapper.Dispose(); 94 } 95 _excelApplications.Clear(); 96 } 97 }
使用时,在ExcelApplication的封装类进行调用Exit()函数即可。
注意,封装类要进行继续1中的ComObjectWrapper类, 并重写DoDispose。封装类的代码就不上了,百度或者谷歌都有:(
public class ExcelApplication : ComObjectWrapper { /// <summary>
/// Excel程序退出 /// </summary> public void Exit() { _excel.Quit(); Dispose(); } /// <summary> /// Com对象的释放处理 /// </summary> protected override void DoDispose() { try { Workbooks.Close(); Workbooks.Dispose(); _excel.Quit(); ((IDisposable)_excel).Dispose(); _excel = null; } catch { } } //other code ........... }
public class ExcelWorkbook : ComObjectWrapper
{
protected override void DoDispose() { try { ((IDisposable)workBook).Dispose(); } catch { } } //.................other code
}
11月27日补充:
在手动new一个Excel封装类的变量后(如:ExcelWorkSheet、ExcelRange),一定要记得把它手动释放进程,不然会直到程序关闭才自动释放!
var currentSheet = new ExcelWorkSheet {WorkSheet = workBook.WorkBook.ActiveSheet}; // other code to do something currentSheet.Dispose();
作者:Ivan