I'm having an issue with Excel Interop.
The Excel.exe doesn't close even if when I realease instances.
Here is my code :
using xl = Microsoft.Office.Interop.Excel;
xl.Application excel = new xl.Application();
excel.Visible = true;
excel.ScreenUpdating = false;
if (wordFile.Contains(".csv") || wordFile.Contains(".xls"))
{
//typeExcel become a string of the document name
string typeExcel = wordFile.ToString();
xl.Workbook workbook = excel.Workbooks.Open(typeExcel,
oMissing, oMissing, oMissing, oMissing,
oMissing, oMissing, oMissing, oMissing,
oMissing, oMissing, oMissing, oMissing,
oMissing, oMissing);
object outputFileName = null;
if (wordFile.Contains(".xls"))
{
outputFileName = wordFile.Replace(".xls", ".pdf");
}
else if (wordFile.Contains(".csv"))
{
outputFileName = wordFile.Replace(".csv", ".pdf");
}
workbook.ExportAsFixedFormat(XlFixedFormatType.xlTypePDF, outputFileName,
XlFixedFormatQuality.xlQualityStandard, oMissing,
oMissing, oMissing, oMissing, oMissing, oMissing);
object saveChanges = xl.XlSaveAction.xlDoNotSaveChanges;
((xl._Workbook)workbook).Close(saveChanges, oMissing, oMissing);
Marshal.ReleaseComObject(workbook);
workbook = null;
}
I saw that, with the Marshal.RealeaseComObject
it should be work, but nothing.
How can I fix this?
Thank you.
Rules - never use no more that one dot
-- one dot
-- Two or more dots
-- Example
In case you are desperate. Do not use this approach unless you understand what it does:
Note: This kill every process named "EXCEL".
I had to do it becase even though I've closed every single COM object in my code I still had stubborn Excel.exe process just hanging there. This is by no means the best solution, of course.
Simple rule: avoid using double-dot-calling expressions, such as this:
...because in this way you create RCW objects not only for
workbook
, but forWorkbooks
, and you should release it too (which is not possible if a reference to the object is not maintained).So, the right way will be:
@Denis Molodtsov in an attempt to be helpful suggested killing all processes named 'EXCEL'. That seems to be asking for trouble. There are already many answers that describe ways to get the process to stop after the call to excel.quit() by playing nice with COM interop. This is best if you can make it work.
@Kevin Vuilleumier had a great suggestion to send WM_CLOSE to the Excel window. I plan to test this.
If for some reason you need to kill an Excel App Object's Excel process, you can target it specifically using something like this:
I don't have time to fully test in C#, but I ran a quick test in Powershell where I'm having a problem with Excel not terminating and this approach works.
It's pretty straightforward. Excel App object's Hwnd property is the Excel process's hidden window handle. Pass excel.Hwnd to GetWindowThreadProcessId to get the process ID. Use that to open the process, finally invoke Kill().
At least we're sure we're killing the right process. Well, pretty sure. If the Excel process already terminated normally, it's process ID could be reused by a new process. To limit this possibility, it's important not to wait between calling excel.quit() and attempting to kill.
As stated in other answers, using two dots will create hidden references that cannot be closed by
Marshal.FinalReleaseComObject
. I just wanted to share my solution, which eliminates the need to rememberMarshal.FinalReleaseComObject
- it's really easy to miss, and a pain to locate the culprit.I use a generic IDisposable wrapper class which can be used on any COM object. It works like a charm, and it keeps everything nice and clean. I can even reuse private fields (e.g.
this.worksheet
). It also auto-releases the object when something throws an error, due to the nature of IDisposable (the Dispose method runs as afinally
).Alternatively, you can kill the Excel process as explained here.
First, import SendMessage function:
Then, send the WM_CLOSE message to the main window: