【问题标题】:Office Automation hangs with Windows 7 and Office 2007Office 自动化在 Windows 7 和 Office 2007 中挂起
【发布时间】:2016-12-27 14:51:28
【问题描述】:

我有一个 Delphi XE8 程序,我在其中使用 Office 自动化构建 Excel 文件。构建需要几秒钟,所以我将这项工作放到后台线程中,以便我的程序保持响应并且我可以处理取消请求。

我是这样设置的:

TTask.Run(
  procedure
  var
    oXL, oSheet, o2Sheet, oRng, VArray : Variant;
  begin
    oXL := CreateOleObject('Excel.Application');
    oXL.Visible := False;
    oXL.DisplayAlerts := false;

    ... (all the processing to build the Excel file)

    oXL.Application.Workbooks[1].SaveAs(ExcelFilename, 51 { = xlWorkbookDefault } );
    oXL.Workbooks.Close;
    oXL.Application.Quit;
    oXL := Unassigned;
  end
);

这段代码工作得非常好,就像我想要的那样。它将 Excel 工作簿创建为后台任务,完成后从任务正确返回到主线程,并且应用程序保持响应。

我认为一切都很棒。它可以在装有 Office 2016 的 Windows 10 计算机上运行。我有几个用户也在他们的计算机上成功运行了它。

但我收到一位用户的报告,其中程序在线程中挂起。我能够进行一些测试。当代码不在他机器上的线程中运行时,它运行良好(当然,应用程序不再保持响应并且我无法处理取消请求)。

我能看出用户机器的唯一不同之处是他们运行的是 Windows 7 和 Office 2007。

我不能 100% 确定 Office 自动化是线程安全的。我可以将它们全部放入一个线程中,并了解某些自动化命令(哪些?)可能需要同步?如果它不是线程安全的,那么在生成 Excel 文件时我还能如何让我的应用程序保持响应?

难道是 Windows 7 与 Windows 10 或 Office 2007 与 Office 2016 上的 Office 自动化之间的差异导致旧版本中的任务失败?如果是这样,是否有解决方法?

【问题讨论】:

  • 真正的代码肯定不会说 51。如果是的话,你必须停止使用魔术常量并定义适当的常量。
  • @DavidHefferman - 我不得不在网上搜索要使用的代码 (xlWorkbookDefault) 以及它的值是什么,因为 Delphi XE8 Pro 包中不包含办公自动化常量。
  • 所以在你的代码中定义它们。
  • 办公自动化常量在 Delphi 导入单元中定义(参见 Excel2010.pas,在您的 $(BDSDIR)\OCX\Servers 文件夹中)。它被定义为xlWorkbookDefault = $00000033;,这就是它应该在您的代码中定义的方式。为了将来参考,您可以使用 Components->Import Component 导入 Excel 类型库,它创建的 Delphi 单元将包含您需要的所有定义,您可以将其用作参考而不是在网上搜索。
  • 还需要注意的是,虽然我假设您没有在这里执行此操作,但您无法在登录用户之外自动执行 Office 应用程序(例如,无法通过服务或计划任务执行此操作)没有以当前登录用户的身份运行)。除了没有加载用户配置文件之外,最大的问题是有时 Office 应用程序会显示一个模式对话框 - 任何人都无法查看或单击它。这将导致自动化的 Office 应用程序看起来挂起。

标签: excel multithreading delphi automation


【解决方案1】:

明显的缺陷是您没有在线程中初始化 COM。致电CoInitializeCoInitializeEx

从后台线程自动化 Excel 肯定没有问题。你正在尝试的是可能的。可以想象,您没有显示的代码中存在错误,但缺少 COM 初始化是问题代码中唯一可以看到的错误。

当然,当您不控制线程的生命周期时,COM 初始化并不容易。线程库创建线程,您不知道该线程中已经运行了什么。如果我是你,我会明确地创建一个线程并避免线程库的基于任务的方法。

【讨论】:

  • 线程只运行 Excel 文件的构建。这不是由于我的代码中的错误,因为只放入 CreateOleObject 语句和 Close/Quit 语句并将其他所有内容都排除在外具有相同的结果。我在我的问题中没有注意到的可能(或可能不)重要的是,如果我将 CreateOleObject 和 Close/Quit 对象放入 Synchronize 语句中,那么它可以在我用户的机器上运行。当然,这只是让它再次在主线程中运行。
  • 这肯定是你代码中的一个错误。始终怀疑自己的代码高于一切。正如我所说,可见的大错误是您没有初始化 COM。在 VCL 应用程序中,COM 在主线程上初始化。
  • 从技术上讲,它不是我的代码中的错误。这是一个错误,因为我在代码中遗漏了一些东西。 :-)
  • 省略基本代码是错误的,就像包含错误代码一样。
  • 我添加了 CoInitializeEx(nil, coInit);声明我的任务并更新我上面的问题以显示它。该代码在我的机器和其他几台用户机器上仍然可以正常工作。我在装有 Windows 7 和 Office 2007 的用户机器上对其进行了测试,但 Office 自动化仍然挂起。所以我的问题仍然没有解决。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-16
  • 1970-01-01
  • 2010-11-22
  • 1970-01-01
  • 2014-09-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多