[英]Excel interop COM doesn't close
我有一些代碼可以打開電子表格,讀取一些值,然后關閉工作表。 我需要為多個文件執行此操作。 我遇到的問題是Excel應用程序實例沒有退出,因此當我運行幾個文件的進程時,我最終運行了幾個excel.exe進程。 我有什么想法讓Excel關閉?
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()
釋放資源
如果您一個接一個地讀取多個文件,那么性能方面最好只打開/關閉Excel 應用程序一次並將打開/關閉應用程序部分移動到單獨的功能
重構代碼:
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的類型庫或任何其他類型庫,那么您可能需要更友好的東西:
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
捕獲到比using
語句comRef.Reference
的某個字段或變量。
請注意,我沒有考慮過線程安全性和正確的Dispose
實現 。 如果只使用帶有using
語句的ComRef
,則線程安全並不重要。 一個適當的Dispose
實現可以與終結器一起使用,但是這里沒有必要,因為using
基本上是try-finally
。 如果你沒有在using
語句中使用ComRef
並且沒有調用Dispose
,那么ComRef
將被簡單地進行垃圾收集,並且使用底層COM對象,如果只有這個ComRef
引用它,它將被釋放。
最后,我沒有使用Marshal.FinalReleaseComObject
,因為當您完全確定要釋放底層COM對象(至少是來自托管環境的所有引用)時,無論它有多少次(重新),都會使用它。進入管理世界。 但是,如果你覺得幸運,你可能只是創建一個新的構造函數,它也接收一個布爾表示是否應該調用FinalReleaseComObject
而不是ReleaseComObject
。 在網絡上搜索任何這些方法的第一個結果將指向文章和博客文章,詳細說明為什么它們通常是邪惡的,比另一個更多。
結束殺死進程,這是唯一有效的方法。
[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);
}
}
我想分享一些關於同樣問題的經驗。
我重新構建了我的Excel Interop代碼,以確保我永遠不會有多個屬性期,遵循此網頁中所述的“規則”: http : //jake.ginnivan.net/vsto-com-interop/
例如:
ExcelApplication.ExcelWorkbook.Workbookworksheet.WorksheetRange.Range.Count
(誇張一點)。
- 我用以下項目替換了這些字符串......
var CurrentRange = currentMDL.CurrentMDL_xlRange;
var CurrentRangeColumns = CurrentRange.Columns;
var CurrentRangeWorksheet = currentMDL.CurrentMDL_Worksheet;
var CurrentRangeWorksheetCells = CurrentRangeWorksheet.Cells;
從這里開始,我能夠更加干凈地利用我想要的東西。
例如:
for(int i = 1; i <= CurrentRangeColumns.Count; i++)
{
//Doing stuff
}
在完成所有操作后,我確保在打開它的同一方法中關閉我的Excel文檔。 我打算重新訪問它,看看我是否能夠遠程關閉Excel文檔。
最后,我確保跟進我的Excel處理方法中使用的所有COM對象的一些版本。
例如:
Marshal.FinalReleaseComObject(CurrentRange);
Marshal.FinalReleaseComObject(CurrentRangeCells);
Marshal.FinalReleaseComObject(CurrentRangeRows);
操作順序在這里很重要。 我確保關閉我的工作簿,然后關閉Excel應用程序,最后發布我的COM對象。 關於ComObject版本的使用,我與我合作的工程師進行了交談。 他說我不應該使用這些調用,因為垃圾收集應該最終清理我的爛攤子。 通過我的學習,我無法獲得垃圾收集來關閉我的Excel實例並選擇自己發布它們。
-克里斯
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.