Excel互操作COM不关闭
我有一些代码可以打开电子表格,读取一些值,然后关闭表格。 我需要为多个文件执行此操作。 我遇到的问题是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
到一些comRef.Reference
using
语句的字段或变量。
请注意,我没有考虑线程安全性和适当的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);
}
}
链接地址: http://www.djcxy.com/p/35679.html