Excel互操作COM不closures

我有一些代码打开电子表格,读取一些值,然后closures工作表。 我需要为多个文件做这个。 我遇到的问题是Excel应用程序实例没有退出,因此,当我运行我的进程的几个文件,我结束了几个excel.exe进程运行。 任何想法如何让Excelclosures?

static void ParseFile(string file) { try { System.Console.WriteLine("parsing:" + file); Excel.Application excel = new Excel.Application(); Excel.Workbook wb = excel.Workbooks.Open(file); Excel.Worksheet ws = wb.Worksheets[1]; for (int i = 2; i < 27; i++) { log(ws.Cells[i, 1].Text); } wb.Close(false); excel.Quit(); excel = null; ws = null; wb = null; } catch (Exception ex) { log(ex.Message); } } 

——更新12/11/12 ——–仍然打开excel实例——-

 static void ParseFile(string file) { try { log("parsing:" + file); Excel.Application excel = new Excel.Application(); Excel.Workbook wb = excel.Workbooks.Open(file); Excel.Worksheet ws = wb.Worksheets[1]; //do some stuff here wb.Close(false); excel.Quit(); GC.Collect(); GC.WaitForPendingFinalizers(); Marshal.FinalReleaseComObject(ws); Marshal.FinalReleaseComObject(wb); Marshal.FinalReleaseComObject(excel); excel = null; ws = null; wb = null; System.GC.Collect(); } catch (Exception ex) { log(ex.Message); } } 

  1. 使用Marshal.FinalReleaseComObject()释放资源

  2. 如果您一个接一个地读取多个文件,那么性能上最好只打开/closuresExcel 应用程序一次,并将打开/closures应用程序部分移动到单独的函数

重构代码:

 static Excel.Application OpenExcel(){ Excel.Application excel = null; try{ excel = new Excel.Application(); return excel; } catch(Exception ex){ log(ex.Message); return null; } } static void ParseFile(string file) { try { System.Console.WriteLine("parsing:" + file); Excel.Workbook wb = excel.Workbooks.Open(file); Excel.Worksheet ws = wb.Worksheets[1]; for (int i = 2; i < 27; i++) { log(ws.Cells[i, 1].Text); } wb.Close(false); } catch (Exception ex) { log(ex.Message); } finally{ Marshal.FinalReleaseComObject(ws); Marshal.FinalReleaseComObject(wb); ws = null; wb = null; } } static void CloseExcel(Excel.Application excel){ try{ excel.Quit(); } catch(Exception ex){ log(ex.Message); } finally{ Marshal.FinalReleaseComObject(excel); excel = null; } } 

用法:

 Excel.Application excel = OpenExcel(); if(excel != null){ // Parse files in a loop ParseFile("fileName"); // Close excel after parsing all files CloseExcel(excel); } 

您可以在实现IDisposable的实际COM对象周围使用包装器对象,以便它可以与C#的using语句一起using

这样做的好处是,它将促进可读的代码,它将适用于任何COM对象。 下面是运行时分派的例子:

 using System; using System.Reflection; using System.Runtime.InteropServices; public class ComRef<T> : IDisposable where T : class { private T reference; public T Reference { get { return reference; } } public ComRef(T o) { reference = o; } public void Dispose() { if (reference != null) { Marshal.ReleaseComObject(reference); reference = null; } } } public class Test { static void Main() { Type excelAppType = Type.GetTypeFromProgID("Excel.Application"); using (var comRef = new ComRef<object>(Activator.CreateInstance(excelAppType))) { var excel = comRef.Reference; // ... excel.GetType().InvokeMember("Quit", BindingFlags.InvokeMethod, null, excel, null); } } } 

但是,如果您已经导入了Excel的types库或任何其他types的库,那么您可能需要一些更友好的东西:

 public class CoClassComRef<T> : ComRef<T> where T : class, new() { public CoClassComRef() : base(new T()) { } } public class Test { static void Main() { using (var comRef = new CoClassComRef<Excel.Application>()) { var excel = comRef.Reference; // ... excel.Quit(); } } } 

你应该确保你没有捕获comRef.ReferencecomRef.Reference using语句的某个字段或variables。

请注意,我没有考虑到线程安全性和适当的Dispose实现 。 如果只使用带有using语句的ComRef ,则线程安全并不重要。 一个适当的Dispose实现将与一个终结器协同工作,但是这里没有必要,因为using基本上是一个try-finally 。 如果您不using语句中的ComRef并且不调用Dispose ,则ComRef将被简单地垃圾收集,并且带有底层的COM对象,如果只有此ComRef引用它,将会被释放。

最后,我没有使用Marshal.FinalReleaseComObject ,因为当你确定要释放底层的COM对象(至less是托pipe环境的所有引用)时,不pipe它有多less次(重新)进入了托pipe的世界。 然而,如果你感到幸运,你可能只是创build一个新的构造函数,它也接收一个布尔值,说明是否应该调用FinalReleaseComObject而不是ReleaseComObject 。 网上search这些方法的第一个结果将指向文章和博客文章,详细说明为什么他们通常是邪恶的,比其他更多。

结束了杀死进程,这是唯一的工作。

  [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); /// <summary> Tries to find and kill process by hWnd to the main window of the process.</summary> /// <param name="hWnd">Handle to the main window of the process.</param> /// <returns>True if process was found and killed. False if process was not found by hWnd or if it could not be killed.</returns> public static bool TryKillProcessByMainWindowHwnd(int hWnd) { uint processID; GetWindowThreadProcessId((IntPtr)hWnd, out processID); if (processID == 0) return false; try { Process.GetProcessById((int)processID).Kill(); } catch (ArgumentException) { return false; } catch (Exception ex) { return false; } return true; } static void ParseFile(string file) { try { log("parsing:" + file); Excel.Application excel = new Excel.Application(); Excel.Workbook wb = excel.Workbooks.Open(file); Excel.Worksheet ws = wb.Worksheets[1]; //do some stuff here wb.Close(false); int hWnd = excel.Application.Hwnd; excel.Quit(); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); Marshal.FinalReleaseComObject(ws); Marshal.FinalReleaseComObject(wb); Marshal.FinalReleaseComObject(excel); excel = null; ws = null; wb = null; GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); TryKillProcessByMainWindowHwnd(hWnd); } catch (Exception ex) { log(ex.Message); } }