【问题标题】:C# HRESULT 0x800AC472 when creating a new Workbooks with Excel InteropC# HRESULT 0x800AC472 使用 Excel 互操作创建新工作簿时
【发布时间】:2023-03-12 04:51:02
【问题描述】:

直到最近,这个预定的 C# 控制台应用程序运行良好,但大约 2 周后,它在我开始使用 Excel 互操作的代码开头崩溃。

当我自己运行它时,它运行良好,但是当我尝试通过计划任务管理器执行它时(在 Windows Server 2008 r2 Windows Server 2016 上都存在同样的问题,两者都已正确安装 Excel 和有效的许可证密钥)它在我尝试启动它后立即崩溃。

(对不起,如果有我的赌注,否则我真的不习惯写“真正的”英语,如果有什么不清楚的,请不要害怕要求解释)。

计划任务管理器指出程序返回错误 0xE0434352。我已经搜索了一下,发现它实际上是一个通用错误号。所以我添加了一个 AppDomain 捕捉器来在日志文件中获取正确的错误号。我现在知道真正的错误是“HRESULT:0x800AC472”并抛出:

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

我已经测试过声明一个空工作簿,然后在其中添加一个工作表,或者使用“XlSheetType.xlWorksheet”而不是“XlWBATemplate.xlWBATWorksheet”,但仍然相同。只通过了一次,但出现同样的错误:

ws.Name = "名称";

我还阅读了有关 stackoverflow 错误的信息,但只创建了一个 Excel 进程,我尝试添加 Thread.Sleep(50//500//5000) 但它没有改变任何东西。 这是它崩溃的代码部分,我认为不需要代码的任何其他部分:

        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 ...

我显然想知道为什么这现在不再起作用了,即使您没有完美的答案,即使是提示也将不胜感激。

非常感谢您阅读到这里。

【问题讨论】:

  • 能否在目标主机任务调度程序之外的控制台中运行?
  • 你说你是在服务器上运行它——这是否与 Excel 试图显示一个对话框有关,如this question
  • 在这样的共享服务器上尝试自动化 Excel 的通常反应是不要。真的一点都不安全。
  • @DavidG 是对的。微软很清楚automating Office on a server is not supported
  • 基本上你没有释放你获得的 COM 对象。这样做太多次后,Excel 爆炸了

标签: c# scheduled-tasks excel-interop


【解决方案1】:

您需要在使用完 COM 对象后释放它们,否则即使在您调用 close 后它也会导致进程保持活动状态。

如果您在没有释放的情况下获取了太多 COM 对象,那么 Excel 将完全崩溃(堆损坏、访问冲突、内存损坏等)。

例如

xlApp.Workbooks.Add

将导致保证 COM 泄漏。 xlApp.Workbooks 将获得一个 Workbooks COM 对象,但您没有存储对它的引用。

你想做的事

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

然后当您使用完工作簿时

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

对您获得的每个 COM 对象执行此操作。 IE。任何属于 Interop.Excel 命名空间的东西

确定您是否有 COM 泄漏的最简单方法是检查任务管理器以查看您的应用程序的进程在退出后是否保持活动状态。 (假设你没有像Environment.ExitProcess.Kill 那样进行强制杀戮)

【讨论】:

  • 我尝试为每个 COM 对象添加这个(即使是那些没有在我发布的代码中显示的对象)但它仍然不起作用(正如我在“主要" post,它在第一个循环时崩溃),当程序死亡时,我仍然有 Excel 进程保持活动状态
  • 当您的应用程序崩溃时,您很有可能没有释放对象引用,除非您真的付出了努力。但是泄露的引用不是你的问题。至少不是现在。但从长远来看,您需要修复它。
  • @MatthieuFoltzer 如果您看到剩余的 excel 进程,则意味着您仍然有 COM 泄漏。时期。在过多的泄漏之后,服务异常甚至可能损坏 Excel 的内部缓冲区数组,从而导致任何 COM 操作引发异常。如果仍然无法正常工作,请先尝试重新启动服务器并重新安装 Excel。
  • @Steve 这不是 COM 的工作方式,这不是 COM HRESULT 返回码的工作方式,也不是引用泄漏的工作方式。它们是完全无害的,除非它们的资源消耗造成问题或需要释放资源锁,即打开的文件。相反,无意中释放已经释放的对象很可能会破坏内存,至少会产生异常。
  • @AlexanderGräf 这不是 COM 的工作方式,而是 Excel 的工作方式。已经看过多个现实生活中的例子。 Excel 的 COM 泄漏非常危险
【解决方案2】:

手动在服务器上启动 Excel 并执行您的应用程序手动执行的步骤。确保使用与任务计划程序运行程序相同的用户帐户。强烈建议使用实际用户帐户,而不是某些内置服务帐户。

如果你真的需要使用像“SYSTEM”这样的内置帐户,你需要确保C:\Windows\System32\config\systemprofile没有问题,比如缺少桌面文件夹。

可能 Excel 崩溃并试图要求您恢复最后一个文档,从而阻止了任何进展。当您在没有 UI 的情况下使用自动化时,有时会发生奇怪的事情。评论说应该避免无人值守的自动化,尤其是在服务器上,是正确的。一种更稳定的方法是使用可以访问 XLSX 文档并在那里进行修改的库。我们使用服务器端办公自动化将 Word 和 PowerPoint 发布为 PDF 和 XPS 文件,但只要 Office 愿意,这已经随机失败了。

如果一切都失败了,请在服务器上使用全新的用户帐户来运行应用程序和 Excel。通常是 Excel 本身有问题,你不能在代码中做任何事情,并且缺少 UI,也没有做太多的调试。

如果您无法访问运行 Excel 的服务器,那么您将无法重现该行为并且您很不走运。

【讨论】:

  • 我熟悉this PowerShell module,它可以在没有安装office的情况下与excel文件交互。我认为它是EPPlus.dll 的包装器
  • 您可以简单地使用微软提供的Open XML SDK for Office,只要您不需要做更多的事情,只需将数据转储到电子表格中即可。
  • @AlexanderGräf 我会尝试手动执行步骤,谢谢你的想法,我会回来告诉你结果
  • @AlexanderGräf 我尝试使用与计划任务相同的用户手动执行此操作,但一切正常,没有对话框或其他,即使我离开了程序保留的 Excel 进程
  • @MatthieuFoltzer 从任务计划程序启动时,应用程序使用什么用户帐户运行?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-01
  • 1970-01-01
  • 2014-07-26
相关资源
最近更新 更多