【发布时间】: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