I'm writing a program where you can enter stuff into an excel via a button.
When the button is pressed the Application is started it interacts with the spreadsheet.
But when it's finished interacting the process should be closed completely but it hangs in the Task Manager.
I've tried numerous solutions to close it but it won't.
Here is my code
using Microsoft.Office.Interop.Excel;
using System.Linq;
using System.Runtime.InteropServices;
using Application = Microsoft.Office.Interop.Excel.Application;
.
.
.
.
Application oxl = null;
Workbooks wbs = null;
Workbook wb = null;
Worksheet ws = null;
Sheets wss = null;
Range r = null;
Range orange2 = null;
Range resultRange = null;
try {
oxl = new Application();
wbs = oxl.Workbooks;
wb = wbs.Open(Settings.Default.excelPath);
wss = wb.Worksheets;
ws = (Worksheet) wss[2];
r = ws.Range["A2:A924"];
foreach(string tofind in tofinds) {
resultRange = null;
string[] s = data[tofind];
resultRange = r.Find(
What: tofind,
LookIn: XlFindLookIn.xlValues,
LookAt: XlLookAt.xlPart,
SearchOrder: XlSearchOrder.xlByRows,
SearchDirection: XlSearchDirection.xlPrevious,
MatchCase: true);
if (resultRange != null) {
int i = resultRange.Row;
orange2 = ws.Cells[i, 2];
if (orange2.Value == null) {
orange2 = ws.Cells[i, 2];
//s[0]= -3.94e6
orange2.Value = s[0].Split((char)
'e')[0].Replace("-", "");
orange2.NumberFormat = "0.00";
}
} else {
//Log to Console
}
}
wb.Save();
} catch (IOException e) {
Console.WriteLine(e.Message);
} finally {
Marshal.FinalReleaseComObject(orange2);
Marshal.FinalReleaseComObject(r);
Marshal.FinalReleaseComObject(resultRange);
orange2 = null;
r = null;
resultRange = null;
foreach(Worksheet sheet in wss) {
Marshal.FinalReleaseComObject(sheet);
}
Marshal.FinalReleaseComObject(wss);
wss = null;
wb.Close(0);
wbs.Close();
foreach(Workbook workbook in wbs) {
Marshal.FinalReleaseComObject(workbook);
}
Marshal.FinalReleaseComObject(wb);
wb = null;
Marshal.FinalReleaseComObject(wbs);
wbs = null;
oxl.Application.Quit();
oxl.Quit();
Marshal.FinalReleaseComObject(oxl);
oxl = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Final");
}
Found the Solution.
If you get your Range by
Range r = sheet.Cells[1,1];
Marshal.FinalReleaseComObject(r);
r = null;
Cells[1,1] creates an own COM object
Rang r1 = sheet.Cells;
Range r = r1[1,1];
Marshal.FinalReleaseComObject(r1);
r1 = null;
Marshal.FinalReleaseComObject(r1);
r1 = null;
by doing that it worked for me
Related
I developed winform application that read from excel and transform it to text files.
I used Microsoft.Office.Interop.Excel library in order to work with Excel.
Here is my code:
private Excel.Application excel = null;
private Excel.Sheets sheets = null;
private Excel.Workbook excelWorkbook = null;
private Excel.Workbooks excelWorkbooks = null;
private Excel._Worksheet worksheet = null;
private Excel.Range usedRange = null;
public ExcelFacade() { }
public ExcelFacade(string fileName)
{
excel = new Excel.Application();
excelWorkbooks = excel.Workbooks;
excelWorkbook = excelWorkbooks.Open(fileName, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
sheets = excelWorkbook.Sheets;
}
After I finished work with Excel I call next method (from here):
public void Dispose()
{
foreach (Microsoft.Office.Interop.Excel.Worksheet sheet in sheets)
{
while (Marshal.ReleaseComObject(sheet) != 0) { }
}
excelWorkbook.Close();
excelWorkbooks.Close();
excel.Quit();
var chromeDriverProcesses = Process.GetProcesses().
Where(pr => pr.ProcessName.ToLower().Contains("excel"));
foreach (var process in chromeDriverProcesses)
{
process.Kill();
}
//while (Marshal.ReleaseComObject(usedRange) != 0) { }
//while (Marshal.ReleaseComObject(worksheet) != 0) { }
while (Marshal.ReleaseComObject(sheets) != 0) { }
while (Marshal.ReleaseComObject(excelWorkbook) != 0) { }
while (Marshal.ReleaseComObject(excelWorkbooks) != 0) { }
//while (Marshal.ReleaseComObject(excel.Application) != 0)
//{ }
while (Marshal.ReleaseComObject(excel) != 0) { }
usedRange = null;
worksheet = null;
excelWorkbook = null;
excelWorkbooks = null;
sheets = null;
excel = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
However, if I change excel and rerun my application, the updated excel is not taken by application and it looks like it read from old excel. Such behavior is look like caching and I have no idea how to disable it.
The aforementioned is strengthened by the fact that if I change something in my code, e.g. white space, and rebuild the application, it works brilliant and take right Excel file.
Any suggestions?
As #vbnet3d said, using ClosedXML library solves all problems
I am new to C#, working on a project recently. having problem about saving the excel file part. I want the user to be able to choose where to save the file (BTW, I don't want the file to be read-only). It is like file save dialog. I am using C# console .Net. I can save it to a specific location(with a path) now. Another problem, somehow, the excel file created by the program will be running forever in the background. Can anyone please help me? I have been working on this part for 2 days. Thank you so much.
Code:
private static void excelFile(System.Data.DataTable dtTable,List<Dictionary<string, object>> list)
{
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
string filepath = AppDomain.CurrentDomain.BaseDirectory;
Console.WriteLine(filepath);
string filename = #"output1.xlsx";
Console.WriteLine(filepath + filename);
// Microsoft.Office.Interop.Excel.Workbook wkbook = null;
var wkbook = xlApp.Workbooks.Add(Type.Missing);
wkbook.SaveAs(ReadOnlyRecommended: false);
_Worksheet wksheet = wkbook.ActiveSheet;
wksheet.Name = "APPLE";
try
{
for (var i = 0; i < dtTable.Columns.Count; i++)
{
wksheet.Cells[1, i + 1] = dtTable.Columns[i].ColumnName;
}
//rows
for (var i = 0; i < dtTable.Rows.Count; i++)
{
for (var j = 0; j < dtTable.Columns.Count; j++)
{
wksheet.Cells[i + 2, j + 1] = dtTable.Rows[i][j];
}
}
//System.IO.FileInfo fileInfo = new System.IO.FileInfo(filename);
//File.SetAttributes(filename, ~FileAttributes.ReadOnly);
//File.SetAttributes(filename, ~FileAttributes.Hidden);
//xlApp.ActiveWorkbook.Save();
// wkbook.Save();
//wkbook.Close();
//xlApp.ActiveWorkbook.Close();
xlApp.Workbooks.Close();
xlApp.Quit();
}
finally
{
if (wksheet != null) Marshal.ReleaseComObject(wksheet);
if (wkbook != null) Marshal.ReleaseComObject(wkbook);
if (xlApp != null) Marshal.ReleaseComObject(xlApp);
}
}
Finally I figured it out. The code below works.(ignore the catch block )
Code:
private static void excelFile(System.Data.DataTable dtTable, List<Dictionary<string, object>> list)
{
// File save dialog
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "Execl files (*.xls)|*.xls";
saveFileDialog.FilterIndex = 0;
saveFileDialog.RestoreDirectory = true;
saveFileDialog.CreatePrompt = true;
saveFileDialog.FileName = null;
saveFileDialog.Title = "Save path of the file to be exported";
//string filepath = AppDomain.CurrentDomain.BaseDirectory;
Excel.Application xlApp = null;
Excel.Workbooks wkbooks = null;
Excel.Workbook wkbook = null;
Excel.Sheets wksheets = null;
Excel.Worksheet wksheet = null;
try
{
xlApp = new Excel.Application();
wkbooks = xlApp.Workbooks;
wkbook = wkbooks.Add();
wksheets = wkbook.Sheets;
wksheet = wksheets.Add();
Console.WriteLine(list[0].Values);
wksheet.Name = "APPLE";
try
{
Console.WriteLine("It is working.");
for (var i = 0; i < dtTable.Columns.Count; i++)
{
wksheet.Cells[1, i + 1] = dtTable.Columns[i].ColumnName;
}
//rows
for (var i = 0; i < dtTable.Rows.Count; i++)
{
for (var j = 0; j < dtTable.Columns.Count; j++)
{
wksheet.Cells[i + 2, j + 1] = dtTable.Rows[i][j];
}
}
//wkbook.SaveAs(saveFileDialog, XlFileFormat.xlExcel8,false,
//false, false,false, XlSaveAsAccessMode.xlNoChange, Type.Missing,
//Type.Missing, Type.Missing,Type.Missing, Type.Missing);
Console.WriteLine("Processing!");
Console.WriteLine("File saved.");
wkbooks.Close();
wkbook.Close(false, Missing.Value, Missing.Value);
xlApp.Quit();
}
catch(Exception exp)
{
}
}
catch (Exception e)
{
//Console.WriteLine(e.ToString());
}
finally
{
if (wksheets != null) Marshal.ReleaseComObject(wksheets);
if (wkbook != null) Marshal.ReleaseComObject(wkbook);
if (wkbooks != null) Marshal.ReleaseComObject(wkbooks);
if (xlApp != null) Marshal.ReleaseComObject(xlApp);
}
}
}
I am trying to write data in excel work sheet using .Net. However, for some reason whole application terminates (No exception, no warning, no message) when row counter increases to around 900. Whereas, there could be up to 10000 entries.
Furthermore it also terminates if i put a sleep statement, when filling contents. I suspect that there is some sort of timeout associated with excel objects?
Any Ideas what could be causing this? Thanks
Here is the main function -
public void ProduceExcelReport(string reportDirPath, List<MedlineRecord> medlineRecords, List<AccessionRecord> accessionRecords,
List<string> dataBanksOfInterest, bool overwrite)
{
Excell.Application excelApp = null;
Excell.Workbooks excelWorkBooks = null;
Excell.Workbook excelWorkBook = null;
Excell.Sheets worksheets = null;
Excell.Worksheet overAllWorkSheet = null;
Excell.Worksheet parsingWorkSheet = null;
Excell.Worksheet errorWorkSheet = null;
Excell.Worksheet noCitationsSheet = null;
Excell.Worksheet outOfScopeWorksheet = null;
try
{
excelApp = new Excell.Application();
string fullReportPath = Path.Combine(reportDirPath, ReportName);
if (File.Exists(fullReportPath))
{
if (!overwrite)
{
Logger.Warning(string.Format("File {0} already exists. Skipping report production." +
"You can change behaviour by editing OverwriteFilesWithNameConflict tag in settings",
fullReportPath));
return;
}
Logger.Warning(string.Format("File {0} already exists. Overwritting old report." +
"You can change behaviour by editing OverwriteFilesWithNameConflict tag in settings",
fullReportPath));
}
// Initialise
excelApp.DisplayAlerts = false;
object misValue = System.Reflection.Missing.Value;
excelWorkBooks = excelApp.Workbooks;
excelWorkBook = excelWorkBooks.Add(misValue);
worksheets = excelWorkBook.Worksheets;
overAllWorkSheet = (Excell.Worksheet) worksheets.Item[1];
parsingWorkSheet = (Excell.Worksheet) worksheets.Item[2];
errorWorkSheet = (Excell.Worksheet) worksheets.Item[3];
noCitationsSheet = (Excell.Worksheet) worksheets.Add();
outOfScopeWorksheet = (Excell.Worksheet)worksheets.Add();
// Filling contents
FillOverallReportContents(ref overAllWorkSheet, accessionRecords);
FillMedlineParsingReportContents(ref parsingWorkSheet, medlineRecords, dataBanksOfInterest); // <- Terminates when writting contents
FillErrorReportContents(ref errorWorkSheet, medlineRecords);
FillNoCitationReportContents(ref noCitationsSheet, medlineRecords);
FillOutOfScopeReportContents(ref outOfScopeWorksheet, medlineRecords, dataBanksOfInterest);
excelWorkBook.SaveAs(fullReportPath);
excelWorkBook.Close(true, misValue, misValue);
excelApp.Quit(); // Quiting excel app
Logger.Info(string.Format("Excel report at {0} was successfully created", fullReportPath));
}
catch (Exception e)
{
Logger.ExceptionError("Exception occured when trying to write Excel Report", e);
}
finally
{
if (overAllWorkSheet != null) Marshal.ReleaseComObject(overAllWorkSheet);
if (parsingWorkSheet != null) Marshal.ReleaseComObject(parsingWorkSheet);
if (noCitationsSheet != null) Marshal.ReleaseComObject(noCitationsSheet);
if (errorWorkSheet != null) Marshal.ReleaseComObject(errorWorkSheet);
if (outOfScopeWorksheet != null) Marshal.ReleaseComObject(outOfScopeWorksheet);
if (worksheets != null) Marshal.ReleaseComObject(worksheets);
if (excelWorkBook != null) Marshal.ReleaseComObject(excelWorkBook);
if (excelWorkBooks != null) Marshal.ReleaseComObject(excelWorkBooks);
if (excelApp != null) Marshal.ReleaseComObject(excelApp);
}
}
Here is one of the functions that fills worksheet contents
private void FillMedlineParsingReportContents(ref Excell.Worksheet excelWorkSheet, List<MedlineRecord> medlineRecords, List<string> dataBanksOfInterest)
{
// Write the worksheet contents
AddFirstRow(ref excelWorkSheet);
excelWorkSheet.Name = "Medline XML Parsing Summary";
excelWorkSheet.Cells[2, 6] = string.Format("Total articles: {0}", medlineRecords.Count);
excelWorkSheet.Cells[3, 6] = string.Format("Total number dataBank list tags: {0}", (from element in medlineRecords
where element.DataBanksDict.Count > 0
select element).Count());
excelWorkSheet.Cells[4, 6] = string.Format("Total number dataBank tags: {0}", (from element in medlineRecords
select element.DataBanksDict.Count).Sum());
excelWorkSheet.Cells[5, 6] = string.Format("Total number accession tags: {0}", (from element in medlineRecords
select element.GetAccessionRecords().Count).Sum());
int i = 2;
foreach (MedlineRecord record in Utilities.FilterOutMedlineRecords(medlineRecords, dataBanksOfInterest))
{
foreach (AccessionRecord accessionRecord in record.GetAccessionRecords())
{
excelWorkSheet.Cells[i, 1] = accessionRecord.BankName;
excelWorkSheet.Cells[i, 2] = accessionRecord.AccessionId;
excelWorkSheet.Cells[i, 3] = accessionRecord.PMID;
excelWorkSheet.Cells[i, 4] = accessionRecord.FileName;
i++;
// Terminates when I reaches around 800-900
Console.WriteLine(i);
}
}
}
I've made an application that logs data to an excel file.
I can't open the excel file until I've closed the application.
In task manager excel.exe is still running after I've closed the excel application in code.
I've tried at least 11 ways of closing excel but it doesn't work.
All the code is executed from a thread:
thread = new Thread(() => export_parameters(path));
public class xlsx_logfile
{
private Microsoft.Office.Interop.Excel._Application excel;//excel application that is used for creating excel workbooks(files)
private Microsoft.Office.Interop.Excel.Workbooks workbooks;
private Microsoft.Office.Interop.Excel._Workbook workbook;//excel workbook(file)
private Microsoft.Office.Interop.Excel._Worksheet worksheet;
private string filename;
public xlsx_logfile(string path)
{
//destructor?
excel = new Microsoft.Office.Interop.Excel.Application();
workbooks = excel.Workbooks;
workbook = workbooks.Add(Type.Missing);
worksheet = null;
worksheet = workbook.ActiveSheet;
worksheet.Name = "sensordata";
filename = path;
}
public void column_add(uint column_zerobased_index,string header, string format, double[] data)
{
Microsoft.Office.Interop.Excel.Range formatRange;
Microsoft.Office.Interop.Excel.Range c1;
//Microsoft.Office.Interop.Excel.Range c2;
column_zerobased_index = 0;
//var worksheet = workbook.ActiveSheet;
c1 = worksheet.Cells[1, column_zerobased_index+1];
//c2 = oSheet.Cells[4, 4];
formatRange = worksheet.get_Range(c1,c1);//column_zerobased_index + 1
formatRange.EntireColumn.NumberFormat = "0.0";
worksheet.Cells[1, column_zerobased_index+1].NumberFormat = "#";
worksheet.Cells[1, column_zerobased_index + 1] = header;
//todo use: https://stackoverflow.com/questions/3989122/microsoft-office-interop-excel-really-slow
for (int i = 0; i < data.Length; ++i)
{
worksheet.Cells[i + 1 + 1, column_zerobased_index+1] = data[i];
}
// Cleanup
GC.Collect();
GC.WaitForPendingFinalizers();
System.Runtime.InteropServices.Marshal.ReleaseComObject(formatRange);
System.Runtime.InteropServices.Marshal.ReleaseComObject(c1);
}
void close(string filename)
{
//worksheet = null;
//workbook.Close();
//workbook = null;
//excel = null;
//excel.Quit();
//worksheet = null;
//workbook = null;
//excel = null;
//worksheet = null;
//workbook.Close();
//workbook = null;
//excel.Quit();
//excel = null;
//worksheet = null;
//workbook.Close();
//workbook = null;
//var workbooks_local = excel.Workbooks;
//var workbook_local = workbooks_local.Open(filename);
//workbook_local.Close();
//workbooks_local.Close();
//excel.Quit();
//System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook_local);
//System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks_local);
//System.Runtime.InteropServices.Marshal.ReleaseComObject(excel);
//System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheet);
//worksheet = null;
////workbook.Close();
//System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
//workbook = null;
////excel.Quit();
//System.Runtime.InteropServices.Marshal.ReleaseComObject(excel);
//excel = null;
////based on https://stackoverflow.com/questions/1526685/c-how-can-i-open-and-close-an-excel-workbook
//workbook.Close(false, filename, null);
//System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
//excel.Quit();
//System.Runtime.InteropServices.Marshal.ReleaseComObject(excel);
//workbook = null;
//excel = null;
//GC.Collect();
//GC.WaitForPendingFinalizers();
//GC.Collect();
//GC.WaitForPendingFinalizers();
//var aap = System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
//System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheet);
//worksheet = null;
//workbook.Close();
//System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
//workbook = null;
//workbooks.Close();
//System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
//workbooks = null;
//excel.Quit();
//System.Runtime.InteropServices.Marshal.ReleaseComObject(excel);
//excel = null;
// Cleanup
GC.Collect();
GC.WaitForPendingFinalizers();
//System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlRng);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(worksheet);
workbook.Close(Type.Missing, Type.Missing, Type.Missing);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(workbook);
excel.Quit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(excel);
}
public void save()
{
workbook.SaveAs(filename);
close(filename);
}
}
edit: this question is different from Closing Excel Application Process in C# after Data Access because I'm executing from a thread, not a method. The provided answers didn't work.
I solved the problem myself:
private Microsoft.Office.Interop.Excel._Application excel;//excel application that is used for creating excel workbooks(files)
private Microsoft.Office.Interop.Excel._Workbook workbook;//excel workbook(file)
private Microsoft.Office.Interop.Excel._Worksheet worksheet;
excel = new Microsoft.Office.Interop.Excel.Application();
workbook = excel.Workbooks.Add();
worksheet = workbook.ActiveSheet;
workbook.Close();
excel.Quit();
GC.Collect();
GC.WaitForPendingFinalizers();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(worksheet);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(workbook);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(excel);
worksheet = null;
workbook = null;
excel = null;
I've got unit test with this code:
Excel.Application objExcel = new Excel.Application();
Excel.Workbook objWorkbook = (Excel.Workbook)(objExcel.Workbooks._Open(#"D:\Selenium\wszystkieSeba2.xls", true,
false, Missing.Value, Missing.Value, Missing.Value,
Missing.Value, Missing.Value, Missing.Value,
Missing.Value, Missing.Value, Missing.Value,
Missing.Value));
Excel.Worksheet ws = (Excel.Worksheet)objWorkbook.Sheets[1];
Excel.Range r = ws.get_Range("A1", "I2575");
DateTime dt = DateTime.Now;
Excel.Range cellData = null;
Excel.Range cellKwota = null;
string cellValueData = null;
string cellValueKwota = null;
double dataTransakcji = 0;
string dzien = null;
string miesiac = null;
int nrOperacji = 1;
int wierszPoczatkowy = 11;
int pozostaĆo = 526;
cellData = r.Cells[wierszPoczatkowy, 1] as Excel.Range;
cellKwota = r.Cells[wierszPoczatkowy, 6] as Excel.Range;
if ((cellData != null) && (cellKwota != null))
{
object valData = cellData.Value2;
object valKwota = cellKwota.Value2;
if ((valData != null) && (valKwota != null))
{
cellValueData = valData.ToString();
dataTransakcji = Convert.ToDouble(cellValueData);
Console.WriteLine("data transakcji to: " + dataTransakcji);
dt = DateTime.FromOADate((double)dataTransakcji);
dzien = dt.Day.ToString();
miesiac = dt.Month.ToString();
cellValueKwota = valKwota.ToString();
}
}
r.Cells[wierszPoczatkowy, 8] = "ok";
objWorkbook.Save();
objWorkbook.Close(true, #"C:\Documents and Settings\Administrator\Pulpit\Selenium\wszystkieSeba2.xls", true);
objExcel.Quit();
Why after finish test I'm still having excel in process (it does'nt close)
And : is there something I can improve to better perfomance ??
Excel 2007 and .net 3.5
I use a code segment like this to forcefully close it:
public void DisposeExcelInstance()
{
//oXL.DisplayAlerts = false;
//oWB.Close(null, null, null);
//oXL.Quit();
//oWB.Close(null, null, null);
//oXL.Quit();
///KNG - CLEANUP code
oXL.DisplayAlerts = false;
oWB.Close(null, null, null);
oXL.Workbooks.Close();
oXL.Quit();
if (oResizeRange != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(oResizeRange);
if (oSheet != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(oSheet);
if (oWB != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(oWB);
if (oXL != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(oXL);
oSheet = null;
oWB = null;
oXL = null;
GC.Collect(); // force final cleanup!
}
Maybe ur reference count is off:
http://support.microsoft.com/kb/317109
In addition to Kangkan's answer, you can also try to use an existing Excel instance (if there is still an unclosed instance running):
try {
objExcel = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
} catch (Exception) {
if (objExcel == null) {
objExcel = new Excel.Application();
}
}
try something not using Excel automation:
How can I create an Excel compatible Spreadsheets on the server side in C#?