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); } }
-
使用
Marshal.FinalReleaseComObject()
释放资源 -
如果您一个接一个地读取多个文件,那么性能上最好只打开/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.Reference
到comRef.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); } }