我有一些代码可以打开电子表格,读取一些值,然后关闭工作表。我需要为多个文件执行此操作。我遇到的问题是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);
}
}
答案 0 :(得分:3)
使用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);
}
答案 1 :(得分:2)
您可以在实现IDisposable
的实际COM对象周围使用包装器对象,以便它可以与C#'s using
statement一起使用。
这样做的好处是它可以提升可读代码,并且它适用于任何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
语句更长的某个字段或变量。
请注意,我没有过多考虑线程安全性和正确的Dispose
implementation。如果您只使用ComRef
using
语句,则线程安全并不重要。正确的Dispose
实现会与终结器进行协调,但这里没有必要,因为using
基本上是try-finally
。如果您在ComRef
语句中使用using
并且未调用Dispose
,则ComRef
将被简单地进行垃圾收集,并使用它来获取基础COM对象,这将是如果只有ComRef
引用它,则会被释放。
最后,我没有使用Marshal.FinalReleaseComObject
,因为当您绝对确定要释放底层COM对象时(至少来自托管的所有引用)环境)无论它有多少次(重新)进入管理世界。但是,如果你感到幸运,你可能只是创建一个新的构造函数,它也会收到一个布尔说明是否应该调用FinalReleaseComObject
而不是ReleaseComObject
。网络搜索任何这些方法的第一个结果将指向文章和博客文章,详细说明为什么它们通常是邪恶的,比另一个更多。
答案 2 :(得分:2)
结束杀死进程,这是唯一有效的方法。
[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);
}
}
答案 3 :(得分:1)
我想就这个问题分享一些经验。
按照此网页中所述的“规则”,我重新构造了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实例并选择自己释放它们。
-克里斯