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();
}
}
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 made a small class that takes an excel workbook, graps the contained charts in the workbook and export each of them to PNG files.
It almost works fine, but I have a problem regarding the generated PNG files, the class doesn't close the files properly, until the user closes the application.
Here is my class:
public List<string> ExportCharts(string fileName)
{
var exportedGraphsList = new List<string>();
Excel.Workbooks xlWorkBooks = null;
Excel.Workbook xlWorkBook = null;
Excel.Application xlApp = null;
Excel.Sheets xlSheets = null;
object misValue = System.Reflection.Missing.Value;
try
{
xlApp = new Excel.ApplicationClass();
xlWorkBooks = xlApp.Workbooks;
xlWorkBook = xlWorkBooks.Open(fileName);
xlSheets = xlWorkBook.Charts;
foreach (Excel.Chart xlChart in xlSheets)
{
var exportfileName = ConfigurationManager.AppSettings["imageSavePath"] + #"\" + xlChart.Name + ".png";
xlChart.Export(exportfileName, "PNG", misValue);
exportedGraphsList.Add(exportfileName);
}
return exportedGraphsList;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\n\n" + ex.StackTrace, "Der gik noget galt");
exportedGraphsList.Clear();
return exportedGraphsList;
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
if(xlSheets != null)
Marshal.FinalReleaseComObject(xlSheets);
if (xlWorkBook != null)
{
xlWorkBook.Close(false, Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(xlWorkBook);
}
if (xlWorkBooks != null)
Marshal.FinalReleaseComObject(xlWorkBooks);
if (xlApp != null)
{
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
}
}
}
public void Dispose()
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
When this method is invoked, it starts and closes the Excel proces very nicely, but the PNG files remains open. I could of coruse put a timestamp on the exported filesnames, but I fear it will confuse the user.
Thanks in advance for any hints or ideas. :-)
Solved - it was due to the fact that I loaded the image files afterwards to the GUI.
So Excel.InterOp works fine.
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?
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();
}