0

I am doing a Windows Service, and I am using Microsoft.Office.Interop.Excel to create a new excel file, edit it, and save it.

After being done with it, and closing the appropriate objects (worksheet, workbook), quitting the excel app, and Marshal releasing COM objects, the process is still lingering.

    private void doWork()
    {
        var excelApp = new MsExcel.Application();
        var workBooks = excelApp.Workbooks;
        var workbook = excelApp.Workbooks.Add();
        var sheets = workbook.Sheets;
        _worksheet = workbook.Sheets[1];

        //Do Work Here

        _worksheet.SaveAs(filePath);
        workbook.Close(false,System.Reflection.Missing.Value,System.Reflection.Missing.Value);
        workBooks.Close();
        releaseObject(_worksheet);
        releaseObject(sheets);
        releaseObject(workbook);
        releaseObject(workBooks);
        excelApp.Quit();
        releaseObject(excelApp);

    }

    private void releaseObject(object obj)
    {
        try
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
            obj = null;
        }
        catch (Exception ex)
        {
            _log.Error($"Unable to release object {obj} Error:" + ex.ToString());
            obj = null;
        }
        finally
        {
            GC.Collect();
        }
    }

I saw a couple of other posts that suggested the releasing of the objects, plus the reference to Workbooks and Sheets. Not sure, why this is still not working.

havan
  • 164
  • 2
  • 11
  • Is `_worksheet` a field? That could be causing it. You could try having it as a local variable and passing it the methods that need it. – stuartd May 31 '18 at 16:00
  • So you tagged this excel, but it isn’t an excel issue... – Solar Mike May 31 '18 at 16:04
  • How is this not an excel issue @SolarMike? *I am using Microsoft.Office.Interop.Library to create a new excel file, edit it, and save it.* – Liam May 31 '18 at 16:08
  • What calls the library ? Opening the file with excel and running the macro? Or is this from a different program? – Solar Mike May 31 '18 at 16:10
  • @stuartd: good point. I'll try without the field. – havan May 31 '18 at 16:36
  • @SolarMike: literally the first 5 lines are using Classes from the library. It is Interop.Excel not library. – havan May 31 '18 at 16:36
  • @havan read this answer, has some good points : https://stackoverflow.com/a/1893653/4961700 – Solar Mike May 31 '18 at 17:16
  • It was the fact, that it was a field. Once I changed it to a variable, and passed it around, it worked like a charm. Thank you all for your input – havan May 31 '18 at 17:23

2 Answers2

0

Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment.

If you are building a solution that runs in a server-side context, you should try to use components that have been made safe for unattended execution. Or, you should try to find alternatives that allow at least part of the code to run client-side. If you use an Office application from a server-side solution, the application will lack many of the necessary capabilities to run successfully. Additionally, you will be taking risks with the stability of your overall solution. Read more about that in the Considerations for server-side Automation of Office article.

As a workaround you may consider using One XML SDK or any other third-party components designed for the server-side or service execution.

Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45
0

You should not be calling ReleaseComObject. That is a old wives tale that started long ago but it is incorrect.

If you are seeing excel instances stick around it likely means your program terminated without running the garbage collectors and references where still held to the com objects. The best way to solve your problem is get rid of your release function entirely, then call GC.Collect() after the doWork() method returns.

private void doWork()
{
    try
    {
        doWorkImpl();
    }
    finally
    {
        GC.Collect(); //The GC collect needs to be here, after the scope of doWorkImpl ends.
        GC.WaitForPendingFinalizers(); //The excel instance gets closedin the finalizer.
    }
}

private void doWorkImpl()
{
    var excelApp = new MsExcel.Application();
    var workBooks = excelApp.Workbooks;
    var workbook = excelApp.Workbooks.Add();
    var worksheet = workbook.Sheets[1]; //This needs to be a local variable, not a class variable.

    //Do Work Here

    worksheet.SaveAs(filePath);
    workbook.Close(false,System.Reflection.Missing.Value,System.Reflection.Missing.Value);
    workBooks.Close();
    excelApp.Quit();

}

This should cause the excel instance to go way so long as no exception was thrown and excelApp.Quit() is successfully called.

However, if you are only working with xlsx files instead of xsl files I would highly recommend moving away from the com interop and just use the SDK microsoft provides for interacting directly with .xlsx files.

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431