I am trying to release Excel objects after using them. Here is my code:
Excel._Application app = new Excel.Application();
Excel._Workbook workbook = app.Workbooks.Add(Type.Missing);
Excel._Worksheet worksheet = null;
app.Visible = true;
worksheet.Cells[1, 1] = "test string";
workbook.SaveAs("C:\testfile.xlsx");
object misValue = System.Reflection.Missing.Value;
workbook.Close(true, misValue, misValue);
app.Quit();
releaseObject(worksheet);
releaseObject(workbook);
releaseObject(app);
My releaseObject method:
private void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(obj);
obj = null;
}
catch (Exception ex)
{
obj = null;
MessageBox.Show("Unable to release the Object " + ex.ToString());
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
At the first time the debugger goes into releaseObject, it gives an error in first "GC.WaitForPendingFinalizers();" line:
Context 0xdf76a0' is disconnected. Releasing the interfaces from the
current context (context 0xdf7450). This may cause corruption or data
loss. To avoid this problem, please ensure that all
contexts/apartments stay alive until the application is completely
done with the RuntimeCallableWrappers that represent COM components
that live inside them.
How can I figure it out?
Related
After reading some posts and trying some things. I am still not getting excel to close properly after releasing the objects.
I do the following below:
Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
if (xlApp == null)
{
//MessageBox.Show("Excel is not properly installed!!");
return;
}
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
object misValue = System.Reflection.Missing.Value;
if (!System.IO.File.Exists("file.xlsx"))
{
xlWorkBook = xlApp.Workbooks.Add(misValue);
}
else
{
xlWorkBook = xlApp.Workbooks.Open("file.xlsx", 0, false, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", true, false, 0, true, 1, 0);
}
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
xlWorkSheet.Name = "Sheet Name";
Then close excel and get rid of the objects:
xlApp.DisplayAlerts = false;
xlWorkBook.SaveAs("file.xlsx");
xlWorkBook.Close(true, "file.xlsx", misValue);
xlApp.Application.Quit();
xlApp.Quit();
Marshal.ReleaseComObject(xlWorkSheet);
Marshal.ReleaseComObject(xlWorkBook);
Marshal.ReleaseComObject(xlApp);
xlApp = null;
even after I do this I still see excel.exe in the task manager. Can someone help me out with what i am doing wrong here. I would really appreciate it.
here is what i have been using to kill the process. it works, but if someone has a more elegant solution i'd be happy to know as well!
private void releaseObject(object obj)
{
try
{
Marshal.ReleaseComObject(obj);
obj = null;
var process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (var p in process)
{
if (!string.IsNullOrEmpty(p.ProcessName))
{
try
{
p.Kill();
}
catch { }
}
}
}
catch (Exception ex)
{
obj = null;
MessageBox.Show("Unable to release the Excel Object " + ex.ToString());
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
call this on each object
releaseObject(worksheet);
releaseObject(workbook);
releaseObject(xlapplication);
I want to check if excel is open. If it is, I want to save the active workbook and close excel. Essentially, my goal is to kill every excel process, but before doing so I want to save the excel workbook. This is my code...
Process[] localByName = Process.GetProcessesByName("excel");
for (int i = 0; i < localByName.Length; i++)
{
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Workbook wb = xlApp.ActiveWorkbook;
wb.Save(); // I get an error here. "Object reference not set to an instance of an object".
GC.Collect();
GC.WaitForPendingFinalizers();
wb.Close();
Marshal.FinalReleaseComObject(wb);
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
}
I read that xlApp.ActiveWorkbook gets the workbook in read only mode. Could this be my problem? If so please propose a successful way to go about this. Thank you!
> Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application(); this creates a new instance of the Excel.Application COM Object and does not return an active instance. To do so, try
Excel.Application xlApp = null;
while(xlApp == null)
{
try
{
xlApp = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
}
catch (System.Runtime.InteropServices.COMException ex)
{
// no more instances/error, exit out of loop
break;
}
// do whatever with the instance
Workbook wb = xlApp.ActiveWorkbook;
wb.Save();
GC.Collect();
GC.WaitForPendingFinalizers();
wb.Close();
Marshal.FinalReleaseComObject(wb);
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
// set null to continue loop
xlApp = null;
}
Source: Get instance of Excel application with C# by Handle
I have been trying to import data from an excel sheet into a DataTable. The problem is that after i finish, excel would not terminate the process. I do not want to terminate by process name using System.Diagnostics as that approach would terminate all excel instances rather than the one that was created by the application. I know that this question was posted over here before multiple times, but none of the solutions seem to work for me. I am probably missing something and can not see it.
below is my code:
private void importToolStripMenuItem_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "Excel WorkBooks (*.xlsx)|*.xlsx";
openFileDialog1.FilterIndex = 1;
openFileDialog1.Multiselect = false;
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
string empRange = "D4";
string emptxid = "K4";
object oMissing = System.Reflection.Missing.Value;
string path = openFileDialog1.FileName;
oExcel.Application xlApp;
oExcel.Workbooks xlWorkBooks;
oExcel.Workbook xlWorkBook;
oExcel.Sheets xlSheets;
oExcel.Worksheet xlWorkSheetDATA;
oExcel.Worksheet xlWorkSheetEMP;
oExcel.Range range;
xlApp = new oExcel.Application();
xlWorkBooks = xlApp.Workbooks;
xlWorkBook = xlWorkBooks.Open(path);
xlSheets = xlWorkBook.Worksheets;
xlWorkSheetDATA = (oExcel.Worksheet)xlSheets.get_Item(DATASheetName);
xlWorkSheetEMP = (oExcel.Worksheet)xlSheets.get_Item(EMPSheetName);
xlWorkSheetEMP.Activate();
range = xlWorkSheetEMP.get_Range(empRange, empRange);
xlWorkSheetDATA.Activate();
range = xlWorkSheetDATA.get_Range(emptxid, emptxid);
xlApp.DisplayAlerts = false;
xlWorkSheetDATA.Columns.ClearFormats();
xlWorkSheetDATA.Rows.ClearFormats();
int iTotalColumns = xlWorkSheetDATA.UsedRange.Columns.Count;
int iTotalRows = xlWorkSheetDATA.UsedRange.Rows.Count;
xlApp.Visible = true;
DataTable dt = new DataTable();
addColumns(iTotalColumns, dt);
insertIntoDataTable(iTotalRows, dt, path);
//clean-up
System.Runtime.InteropServices.Marshal.ReleaseComObject(range);
range = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkSheetEMP);
xlWorkSheetEMP = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkSheetDATA);
xlWorkSheetDATA = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets);
xlSheets = null;
xlWorkBook.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBook);
xlWorkBook = null;
xlWorkBooks.Close();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBooks);
xlWorkBooks = null;
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
xlApp = null;
}
}
Write a method to release the objects such as this example
private void ReleaseOfficeObject(object o)
{
Marshal.ReleaseComObject(o);
GC.Collect();
GC.WaitForPendingFinalizers();
Marshal.FinalReleaseComObject(o);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
then to call it
ReleaseOfficeObject(range);
ReleaseOfficeObject(xlWorkSheetDATA);
ReleaseOfficeObject(xlSheets);
xlWorkBook.Close(false);
ReleaseOfficeObject(xlWorkBook);
xlWorkBook = null;
xlWorkBooks.Close();
ReleaseOfficeObject(xlWorkBooks);
xlWorkBooks = null;
xlApp.Quit();
ReleaseOfficeObject(xlApp);
You never need to call Marshal.ReleaseComObject in this context. The runtime is perfectly able to keep track of COM objects and release them when they are no longer referenced. Calling Marshal.ReleaseComObject is a confusing anti-pattern that sadly even some Microsoft documentation mistakenly suggests. You also don't have to set local variables to null - the references in local variables will be out of scope when the method completes.
You do have to be careful with this kind of code in debug builds. References in a method are artificially kept alive until the end of the method so that they will still be accessible in the debugger. This means your local xlApp variable might not be cleaned up by calling the GC inside that method.
You should run the garbage collection twice, reference cycles could be broken in the first collection, but you need a second collection to be sure all cleanup is done for those references.
To avoid this issue, you might follow a pattern like this:
private void importToolStripMenuItem_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "Excel WorkBooks (*.xlsx)|*.xlsx";
openFileDialog1.FilterIndex = 1;
openFileDialog1.Multiselect = false;
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
string path = openFileDialog1.FileName;
// Call another method that wraps all the Excel COM stuff
// That prevents debug builds from confusing the lifetime of COM references
DoExcelStuff(path);
// When back here, all the Excel references should be out of scope
// Run the GC (twice) to clean up all COM references
// (This can be pulled out into a helper method somewhere)
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
private void DoExcelStuff(string path)
{
// All your Excel COM interop goes here
string empRange = "D4";
string emptxid = "K4";
object oMissing = System.Reflection.Missing.Value;
// ... variable declarations
xlApp = new oExcel.Application();
// More xlApp, workbooks etc...
// Done - tell Excel to close
xlApp.Quit();
// No need for Marshal.ReleaseComObject(...)
// or setting variables to null here!
}
I am currently trying to email the graph/chart from a .xls as an image.
I can get the graph/chart send the email fine.
My issue is when i look in the task manager there is a "EXCEL.EXE" still running after i have called xlApp.quit()
Any help would be appreciated.
Here is the code i am currently using.
Excel.Application xlApp;
Excel.Workbooks xlBooks;
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
Excel.ChartObject xlChartObject;
Excel.Chart xlChart;
object misValue = System.Reflection.Missing.Value;
xlApp = new Excel.Application();
xlBooks = xlApp.Workbooks;
xlWorkBook = xlBooks.Add(Properties.Settings.Default.FileToSend);
xlWorkSheet = xlWorkBook.Sheets[1];
xlWorkSheet.EnablePivotTable = true;
string filename = System.IO.Path.GetTempFileName();
xlChartObject = xlWorkSheet.ChartObjects(1);
xlChart = xlChartObject.Chart;
xlChart.Export(filename + ".gif");
xlWorkBook.Close(false, misValue, misValue);
xlBooks.Close();
xlApp.Application.Quit();
if (xlChart != null)
Marshal.ReleaseComObject(xlChart); xlChart = null;
if (xlChartObject != null)
Marshal.ReleaseComObject(xlChartObject); xlChartObject = null;
if (xlWorkSheet != null)
Marshal.ReleaseComObject(xlWorkSheet); xlWorkSheet = null;
if (xlWorkBook != null)
Marshal.ReleaseComObject(xlWorkBook); xlWorkBook = null;
if (xlBooks != null)
Marshal.ReleaseComObject(xlBooks); xlBooks = null;
if (xlApp != null)
Marshal.ReleaseComObject(xlApp); xlApp = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Ok have edited my code and still not closing excel.
It does however die when i exit the program.
Thanks
You need to free every object that you use.
It seems that xlApp.Workbooks is used by is not freed.
As a side note, it could also be that there was an exception and thus your cleanup code was missed.
Try something using a try/catch/finally like the following:
Excel.Application xlApp = null;
Excel.Workbook xlWorkBook = null;
Excel.Workbooks xlWorkBooks = null;
Excel.Worksheet xlWorkSheet = null;
object misValue = System.Reflection.Missing.Value;
try
{
xlApp = new Excel.Application();
xlWorkBooks = xlApp.Workbooks;
xlWorkBook = xlWorkBooks.Add(Properties.Settings.Default.FileToSend);
xlWorkSheet = xlWorkBook.Sheets[1];
xlWorkSheet.EnablePivotTable = true;
string filename = System.IO.Path.GetTempFileName();
xlWorkSheet.ChartObjects("Chart 1").Chart.Export(filename + ".gif");
xlWorkBook.Close(false, misValue, misValue);
xlApp.Quit();
}
catch(Exception ex)
{
// handle error...
}
finally
{
if (xlWorkSheet != null)
Marshal.ReleaseComObject(xlWorkSheet);
if (xlWorkBook != null)
Marshal.ReleaseComObject(xlWorkBook);
if (xlWorkBooks != null)
Marshal.ReleaseComObject(xlWorkBooks);
if (xlApp != null)
Marshal.ReleaseComObject(xlApp);
}
Try adding a second gc.collect after.
GC.Collect();
GC.WaitForPendingFinalizers(); //gc calls finalize on objects
GC.Collect(); //collect objects just finalized
mehow mentioned implementing IDisposable. I would recommend this as well.
Yay finally!
Got it working.
I added
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
out of the function that i was using EXCEL.
private void runExcelWork()
{
//xlApp, xlBooks, xlWorksheet etc.. Is defined in this function
//Do your work with Excel here.
//Clean all excel objects here.
}
public void runExcel()
{
runExcelWork();
//call GC
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
//at this point EXCEL.EXE closes
}
Thank you to all who helped me with this issue!
I hope someone else finds this useful.
Have you tried something along the lines of:
xlWorkBook.Close();
xlApp.Application.Quit(false);
xlApp = null;
This should clean up any remaining excel.exe processes
You could try to kill the process directly. (Processname could be in capital letters)
try
{
foreach (var process in Process.GetProcessesByName("excel"))
{
process.Kill();
}
}
I'm running into an issue with some code I'm debugging. Excel interop is used to extract some values from a workbook; however, Excel remains open after the program has exited. I've tried the traditional solution, but it still keeps a reference to Excel open on all machines where the code is run
private void TestExcel()
{
Excel.Application excel = new Excel.Application();
Excel.Workbooks books = excel.Workbooks;
Excel.Workbook book = books.Open("C:\\test.xlsm");
book.Close();
books.Close();
excel.Quit();
Marshal.ReleaseComObject(book);
Marshal.ReleaseComObject(books);
Marshal.ReleaseComObject(excel);
}
Even this simple piece of code keeps the process running with multiple files (xlsm, xlsx, xls). Right now we have a workaround in place to kill the Excel processes we've opened, but I'd much rather get this working for my own sanity.
I should add that I have it narrowed down to the Workbook variable. If I remove the call to books.Open() and all references to book then it closes successfully.
This has worked successfully for me:
xlApp.Quit();
//release all memory - stop EXCEL.exe from hanging around.
if (xlWorkBook != null) { Marshal.ReleaseComObject(xlWorkBook); } //release each workbook like this
if (xlWorkSheet != null) { Marshal.ReleaseComObject(xlWorkSheet); } //release each worksheet like this
if (xlApp != null) { Marshal.ReleaseComObject(xlApp); } //release the Excel application
xlWorkBook = null; //set each memory reference to null.
xlWorkSheet = null;
xlApp = null;
GC.Collect();
This code works for me.
//Declare separate object variables
Excel.Application xlApp = new Excel.Application();
Excel.Workbooks xlWorkbooks = xlApp.Workbooks;
Excel.Workbook xlWorkbook = xlWorkbooks.Add(Missing.Value);
Excel.Worksheet xlWorksheet = (Excel.Worksheet)xlWorkbook.Worksheets.get_Item(1);
//Create worksheet
xlWorkbook.Close(false, Missing.Value, Missing.Value);
xlWorkbooks.Close();
xlApp.Quit();
Marshal.FinalReleaseComObject(xlWorksheet);
Marshal.FinalReleaseComObject(xlWorkbook);
Marshal.FinalReleaseComObject(xlWorkbooks);
Marshal.FinalReleaseComObject(xlApp);
xlWorksheet = null;
xlWorkbook = null;
xlWorkbooks = null;
xlApp = null;
GC.Collect();
This article from Microsoft has some good information regarding this issue.
I am a total COM amateur, used it for a minor thing in one project quite a long time ago, but here's a snippet I used there. I probably found it somewhere online, don't remember. In any case, I paste it its full glory ;)
public static class ComBlackBox
{
public static void ReleaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (ArgumentException ex)
{
obj = null;
MessageBox.Show("Unable to release the Object " + ex.Message);
}
finally
{
GC.Collect();
}
}
}
I'm unable to try it out now, but it probably worked (I honestly don't remember any details). Maybe it will help you out. Feel free to point out any obvious problems with this code, I really am far from being COM-literate ;)
This is how I got around this problem:
// Store the Excel processes before opening.
Process[] processesBefore = Process.GetProcessesByName("excel");
// Open the file in Excel.
Application excelApplication = new Application();
Workbook excelWorkbook = excelApplication.Workbooks.Open(Filename);
// Get Excel processes after opening the file.
Process[] processesAfter = Process.GetProcessesByName("excel");
// Now find the process id that was created, and store it.
int processID = 0;
foreach (Process process in processesAfter)
{
if (!processesBefore.Select(p => p.Id).Contains(process.Id))
{
processID = process.Id;
}
}
// Do the Excel stuff
// Now close the file with the COM object.
excelWorkbook.Close();
excelApplication.Workbooks.Close();
excelApplication.Quit();
// And now kill the process.
if (processID != 0)
{
Process process = Process.GetProcessById(processID);
process.Kill();
}