C# HRESULT 0x800AC472 when creating a new Workbooks with Excel Interop

0

Until recently, this scheduled C# console application worked pretty well but since something like 2 weeks, it crash at the beginning of the code where I start using Excel Interop.

When running it myself it works well, but when I try to execute it through the Scheduled Task Manager (same problem on both Windows Server 2008 r2 and Windows Server 2016, both have Excel properly installed and a valid licence key) it crash just after I tryed to start it.

(Sorry if there's mystakes or else, I'm not really used to write "real" English, if anything isn't clear don't be afraid to ask for explaination).

The Scheduled Task Manager point that the program return error 0xE0434352. I already searched a bit for that, and found that it's actually a generic error number. So I added an AppDomain catcher to get the proper error number in a log file. I now know that the true error is the "HRESULT : 0x800AC472" and throw at :

wb = xlApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);

I already tested to declare an empty workbook then add a sheet in it, or use "XlSheetType.xlWorksheet" instead of "XlWBATemplate.xlWBATWorksheet" but still the same. Only one time it passed but got same error on :

ws.Name = "Name";

I also read about a stackoverflow error but there's only one Excel proceses created, and i tryed to add a Thread.Sleep(50//500//5000) but it didn't changed anything. Here's the code part where it crash, I don't think anyother part of the code is needed :

        object misValue = System.Reflection.Missing.Value;
        String part = "";
        String pathD = "";
        List<Part> partList = new List<Part>();
        int ligne = 1;
        Workbook wb = null;
        Worksheet ws = null;

        Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
        if (xlApp == null)
        {
            Console.WriteLine("EXCEL could not be started. Check that your office installation and project references are correct.");
            return;
        }
        xlApp.Visible = false;
        xlApp.DisplayAlerts = false;

        foreach (StockRow stockRow in Requete.getStockRows())
        {
            if (stockRow.Part != part)
            {
                partList.Add(new Part(stockRow.PartCode, stockRow.Part));
                if (wb != null)
                {
                    ws.Range[ws.Cells[1, 1], ws.Cells[ligne, 9]].entirecolumn.autofit();
                    ws.Range[ws.Cells[1, 1], ws.Cells[ligne, 9]].entirerow.autofit();
                    ws.Range[ws.Cells[1, 1], ws.Cells[ligne, 9]].verticalalignment = XlVAlign.xlVAlignCenter;
                    if (!Directory.Exists(pathD))
                    {
                        Directory.CreateDirectory(pathD);
                    }
                    wb.SaveAs(pathD + "\\Stock_" + DateTime.Now.ToString("dd-MM-yyyy_HH") + "h.xlsx", XlFileFormat.xlOpenXMLWorkbook, misValue, misValue, false, false,
                        XlSaveAsAccessMode.xlNoChange, XlSaveConflictResolution.xlUserResolution, true, misValue, misValue, misValue);
                    wb.Close();
                }

                pathD = path + stockRow.PartCode;
                wb = xlApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);
                ws = (Worksheet)wb.ActiveSheet;
                ws.Name = "Name";
                ws.PageSetup.Zoom = false;
                ligne = 1;
                ws.Cells[ligne, 1] = "Column Name";
                ws.Cells[ligne, 2] = "Column Name";
                ws.Cells[ligne, 3] = "Column Name";
                ws.Cells[ligne, 4] = "Column Name";
                ws.Cells[ligne, 5] = "Column Name";
                ws.Cells[ligne, 6] = "Column Name";
                ws.Cells[ligne, 7] = "Column Name";
                ws.Cells[ligne, 8] = "Column Name";
                ws.Cells[ligne, 9] = "Column Name";

                //codes continue ...
            }
            // codes continue ...
        }
        // codes continue ...

I'd obviously like to find why this is now not working anymore, and even if u don't have the perfect answer, even a hint would be much appreciated.

Thanks a lot for reading until here.

c#
scheduled-tasks
excel-interop

2 Answers

2

Start Excel manually on the server and do the steps that your application does manually. Be sure to use the same user account that the task scheduler uses to run your program. It's highly recommended to use an actual user account instead of some built-in service account.

If you really need to use a built-in account like "SYSTEM", you need to make sure that there's nothing wrong with C:\Windows\System32\config\systemprofile, like a missing Desktop folder.

Maybe Excel crashed and tries to ask you to restore the last document, thereby blocking any progress. Sometimes weird things happen when you use automation without a UI. Comments saying that unattended automation, especially on servers, should be avoided, are correct. A more stable way is to use a library that can access XLSX documents and do your modifications there. We use server-side office automation to publish Word and PowerPoint to PDF and XPS files, and that already randomly fails whenever Office feels like it.

If everything fails, use a completely new user account on the server in which to run the application and thus Excel. Usually it's something wrong with Excel itself, which you can't do anything about in your code, and lacking a UI, not do much debugging either.

If you can't access the server on which Excel runs, there is no way for you to reproduce the behavior and you're out of luck.

answered on Stack Overflow Jan 28, 2019 by Alexander Gräf
0

You NEED to free up the COM objects after finish using them or else it would cause the process to stay alive even after you called close.

If you acquired too many COM objects without freeing then Excel will blow up completely (heap corruption, access violation, memory corruption, ect).

for example

xlApp.Workbooks.Add

would result in a guarantee COM leak. xlApp.Workbooks would acquire a Workbooks COM object but you are not storing the reference to it.

you would want to do

var workbooks = xlApp.Workbooks;
wb = workbooks.Add(...);

and then when you are done using workbooks

Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(wb);

Do this on every single COM object that you acquire. I.E. anything that belongs to the Interop.Excel namespace

The easiest way to find out if you have COM leak is to check task manager to see if your application's process stays alive after exit. (assuming you are not doing a force kill like Environment.Exit or Process.Kill)

answered on Stack Overflow Jan 28, 2019 by Steve

User contributions licensed under CC BY-SA 3.0