使用Excel Interop创建新工作簿时,C#HRESULT 0x800AC472



直到最近,这个计划的C#控制台应用程序运行得很好,但大约两周后,它在我开始使用ExcelInterop的代码开头崩溃了。

当我自己运行它时,它运行得很好,但当我试图通过计划任务管理器执行它时(WindowsServer2008r2Windows72016上都有相同的问题,都正确安装了Excel和有效的许可证密钥),它在我尝试启动它后就崩溃了。

(很抱歉,如果有自己的想法或其他原因,我真的不习惯写"真正的"英语,如果有什么不清楚的地方,不要害怕要求解释)。

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

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

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

ws.Name="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并执行应用程序手动执行的步骤。请确保使用与任务调度程序用于运行程序的用户帐户相同的用户帐户。强烈建议使用实际的用户帐户,而不是一些内置的服务帐户。

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

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

如果一切都失败了,请在服务器上使用一个全新的用户帐户来运行应用程序,从而运行Excel。通常情况下,这是Excel本身的问题,你不能在代码中对此做任何事情,而且缺乏UI,也不能进行太多调试。

如果你不能访问运行Excel的服务器,你就没有办法重现这种行为,你就倒霉了。

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

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

例如

xlApp.Workbooks.Add

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

你会想做

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

然后当你使用完工作簿

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

对您获取的每个COM对象执行此操作。即,属于Interop.Excel命名空间的任何内容

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

在我的案例中,我们迁移到了一个新的服务器并安装了office 2016。Office要求用户接受许可协议。解决我问题的方法是使用远程桌面和运行计划任务的ID的凭据登录。打开Excel,尝试输入一些文本并保存文件。Office提示接受许可证。一旦被接受,计划任务就会按预期运行。我认为HRESULT:0x800AC472的异常是由于办公室正在等待接受许可证的提示。

最新更新