[英]Cleaning up Excel Interop
所以我有一個Winforms應用程序,需要從Excel工作表中讀取以填充一些字段。 為了使UI響應,我決定創建一個從Excel工作表中讀取的線程,這樣主線程就不必等待了。 如果讀取完成,然后應用程序退出,EXCEL.EXE表現良好並退出。 但是如果我在讀取仍在進行時關閉主應用程序,則EXCEL.EXE任務保持活動狀態。
我猜這是因為ExcelReader
沒有時間在它關閉之前調用destructor
?
一個可能的解決辦法是有主窗體調用ExcelReader.Cleanup
在它FormClosing
事件。 但這似乎是對封裝的可怕違反。
還有什么其他可能的解決方案? 這是我的ExcelReader代碼:
using Excel = Microsoft.Office.Interop.Excel;
class ExcelReader
{
private int sheetNum { get ; set; }
public int rowCount { get; private set; }
public int colCount { get; private set; }
public List<string> sheetValues { get; private set; }
public List<string> sheetNames { get; private set; }
Excel.Application xlApp;
Excel.Workbooks workBooks;
Excel.Workbook xlWorkbook;
Excel.Worksheet xlWorkSheet;
Excel.Range xlRange;
Excel.Range row;
Excel.Range col;
public ExcelReader(string path){
//initialize values
this.sheetNum = 1;
sheetNames = new List<string>();
sheetValues = new List<string>();
//read from excel blackmagic here
xlApp = new Excel.Application();
workBooks = xlApp.Workbooks;
xlWorkbook = workBooks.Open(path);
xlWorkSheet = xlWorkbook.Sheets[sheetNum];
xlRange = xlWorkSheet.UsedRange;
row = xlRange.Rows;
col = xlRange.Columns;
int rowCount = row.Count;
int colCount = col.Count;
this.getSheetNames(xlWorkbook);
this.getValues(xlRange, rowCount, colCount);
CleanUp();
}
~ExcelReader()
{
CleanUp();
}
private void getSheetNames(Excel.Workbook xlWorkbook)
{
var workSheets = xlWorkbook.Sheets;
int numberOfSheets = workSheets.Count;
for (int i = 1; i < numberOfSheets+1; i++)
{
sheetNames.Add(xlWorkbook.Sheets[i].Name);
}
Marshal.FinalReleaseComObject(workSheets);
}
private void getValues(Excel.Range xlRange, int rowCount, int colCount)
{
for (int i = 1; i < rowCount; i++)
{
for (int j = 1; j < colCount; j++)
{
var cells = xlRange.Cells[i, j];
var value = cells.Value2;
sheetValues.Add(value);
Marshal.FinalReleaseComObject(cells);
}
}
}
private void CleanUp()
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
col.Clear();
row.Clear();
Marshal.FinalReleaseComObject(col);
Marshal.FinalReleaseComObject(row);
xlRange.Clear();
Marshal.FinalReleaseComObject(xlRange);
//close book without saving
xlWorkbook.Close(false);
workBooks.Close();
Marshal.FinalReleaseComObject(xlWorkbook);
Marshal.FinalReleaseComObject(workBooks);
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
}
}
在Interop會話期間,Excel在某些情況下可能會很難關閉。 有時您可能需要使用Windows API調用來強制關閉您正在使用的Excel實例。
這是我在幾個項目中使用的類,用於在Interop中管理Excel。 這里的關鍵點是使用一個唯一的標題,在這種情況下是一個GUID,用於標識您正在使用的Excel的特定實例,並使用此信息強制實例關閉,如果Quit方法不起作用。
using System;
using System.Runtime.InteropServices;
namespace ExcelSupport
{
public class ExcelController : IDisposable
{
private Microsoft.Office.Interop.Excel.Application _ExcelApplication;
private bool disposedValue = false; // To detect redundant calls
private string _caption; //used to uniquely identify hidden Excel instance
//
// Windows API used to help close Excel instance
//
[DllImport("user32.dll")]
private static extern int EndTask(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll")]
private static extern IntPtr SetLastError(int dwErrCode);
public ExcelController()
{
OpenExcel(null);
}
public ExcelController(string workbookName)
{
OpenExcel(workbookName);
}
private void OpenExcel(string workbookName)
{
_ExcelApplication = new Microsoft.Office.Interop.Excel.Application();
_caption = System.Guid.NewGuid().ToString().ToUpper();
_ExcelApplication.Caption = _caption;
_ExcelApplication.Visible = false;
_ExcelApplication.DisplayAlerts = false;
if(workbookName != null)
_ExcelApplication.Workbooks.Open(workbookName);
}
public Microsoft.Office.Interop.Excel.Application Application
{
get { return _ExcelApplication; }
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (!this.disposedValue)
{
try
{
if (_ExcelApplication != null)
{
foreach (Microsoft.Office.Interop.Excel.Workbook WorkBookName in _ExcelApplication.Workbooks)
{
WorkBookName.Close(false);
}
IntPtr iHandle = IntPtr.Zero;
iHandle = new IntPtr(_ExcelApplication.Parent.Hwnd);
_ExcelApplication.DisplayAlerts = false;
_ExcelApplication.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(_ExcelApplication);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(_ExcelApplication);
SetLastError(0);
if (IntPtr.Equals(iHandle, IntPtr.Zero))
iHandle = FindWindow(null, _caption);
if (!IntPtr.Equals(iHandle, IntPtr.Zero))
{
int iRes;
uint iProcId;
iRes = (int)GetWindowThreadProcessId(iHandle, out iProcId);
if (iProcId == 0)
{
if (EndTask(iHandle) == 0)
throw new ApplicationException("Excel Instance Could Not Be Closed");
}
else
{
System.Diagnostics.Process proc = System.Diagnostics.Process.GetProcessById((int)iProcId);
proc.CloseMainWindow();
proc.Refresh();
if (!proc.HasExited)
proc.Kill();
}
}
}
}
finally
{
_ExcelApplication = null;
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
this.disposedValue = true;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.