So I have a Winforms application that needs to read from an Excel sheet to populate some fields. To make the UI responsive, I decided to create a thread to read from the Excel sheet so the main thread does not have to wait. If the read finishes, and then the application exits, EXCEL.EXE behaves nicely and exits. But if I close the main application while the read is still ongoing, then EXCEL.EXE task stays alive.
I'm guessing that is is because ExcelReader
does not have the time to call the destructor
before it closes?
One possible solution would be to have the main form call ExcelReader.Cleanup
in it's FormClosing
event. But that seems to be a horrible violation of encapsulation.
What other possible solutions are there to this? Here's my code for the 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);
}
}