abcpdf 和 XULRunner 失败或临时浏览器配置文件目录错误

abcpdf and XULRunner failure or temp browser profile directory error

在升级到最新版本的 ABCPDF 10.0.1.0 时,我似乎无法理解此错误消息。错误消息是否提供任何线索?它突然发生,重新启动应用程序池是立即修复它的唯一方法。

WebSupergoo.ABCpdf10.Internal.PDFException: Failed to add HTML: Gecko engine failed to initialize. Possible causes: XULRunner folder not found or failure to create temporary browser profile directory.
   at WebSupergoo.ABCpdf9.Doc.AddUrlHtml(String urlOrHtml, Boolean isHtml, Boolean paged, Int32 width, Boolean disableCache)
   at WebSupergoo.ABCpdf9.Doc.AddImageUrl(String url, Boolean paged, Int32 width, Boolean disableCache)
   at WebSupergoo.ABCpdf9.Doc.AddImageUrl(String url)

我的进程日志显示了这种类型的条目。

"1:46:21.9863465 PM","ABCGeckoWP.exe","4052","CreateFile","C:\Windows\Temp\ABCpdf\ABCGecko\wq3tvwof.2uc","NAME NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"1:46:21.9865561 PM","ABCGeckoWP.exe","4052","CreateFile","C:\Windows\Temp\ABCpdf\ABCGecko\wq3tvwof.2uc","NAME NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"

我遇到了同样的问题,使用以下配置帮助我解决了问题。

首先我从 website 下载并安装。

将此添加到 Web.config(或 App.Config)中的 configSections:

<section name="ABCpdf10.Section" type="WebSupergoo.ABCpdf10.ConfigSection, ABCpdf" allowLocation="true" allowDefinition="Everywhere" allowExeDefinition="MachineToLocalUser" overrideModeDefault="Allow" restartOnExternalChanges="true" requirePermission="true" />

然后将此添加到您的 Web.config(或 App.Config):

<ABCpdf10.Section>
    <Preferences>
        <clear />
        <add Key="XULRunnerDirectory" Value="C:\Program Files\WebSupergoo\ABCpdf .NET 10.1 x64\ABCGecko" />
    </Preferences>
</ABCpdf10.Section>

该文件夹可能会根据您安装的版本(32 位或 64 位)而改变。

我还从 bin 文件夹中删除了所有 ABCPdf 相关的 dll(ABCpdf10-32.dll、ABCpdf10-64.dll、ABCGeckoWP.exe 等),ABCPdf.dll 除外。 (如果您是通过 nuget 安装的,请确保将副本删除到输出目录 属性)

不确定是否有帮助,但可能值得一试。

编辑

以上也没有解决我的问题,但我注意到错误通常在我部署新代码后开始。然后在 iisreset 之后它恢复正常。

我在手册中做了更多的挖掘,还有一件事你可以尝试(我会 :))。该手册显示了许多您可以覆盖的设置,我认为有两个与错误相关:TempDirectoryClearoutSize

对于第一个,我创建了一个临时文件夹并添加了以下设置:

<add Key="TempDirectory" Value="D:\ABCPdf\Temp" />

对于第二个,我认为没有什么可做的,但手册上有如下说明:启动时,会检查 ABCpdf 临时目录。如果出现大量未使用的项目,则目录将被清除。

所以在此过程中可能会出现问题(例如通过锁定文件夹),特别是如果多个进程正在做同样的事情。您可以为每个应用程序设置一个不同的临时目录,然后看看它是如何运行的。

在 ABC PDF 支持的帮助下,我能够实施以下解决方法。

显然,当您使用 AddImageHtml 时,会启动一个后台进程,为您将 HTML 转换为 PDF。正是这个过程似乎在某个时刻挂起,此时您将开始收到此错误。 ABC PDF 10 提供了一种在闲置时终止此进程的方法(这里就是这种情况)。

//before you start creating your PDF, be sure to kill any remaining processes 
using (var tempPdf = new WebSupergoo.ABCpdf10.Doc()) { 
      tempPdf.HtmlOptions.Engine = WebSupergoo.ABCpdf10.EngineType.Gecko;
      tempPdf.HtmlOptions.EndTasks(WebSupergoo.ABCpdf10.TaskState.AllWhenBecomeIdle);
}

这段代码只是创建一个文档,将引擎类型设置为 Gecko,然后使用参数 "AllWhenBecomeIdle" 调用 EndTasks。这将杀死所有剩余的当前未执行任何操作的后台工作进程。

之后一切如常,您可以创建 PDF,转换 HTML,...

最后(在您保存 PDF 或将其流式传输到浏览器之后(或在您处理掉 Doc 变量之后),我再次执行相同的操作:终止所有剩余的后台进程。所以到目前为止,这似乎在我们的生产环境中有效。我希望它保持这种状态,直到 ABC PDF 能够提出更明确的解决方案。

我将重置代码包装在一个静态函数中,这样我就可以在任何地方调用它。这是代码:

public static class AbcPdfUtils {
    public static void ResetAbcPdfGeckoProcess()
    {
        using (var tempPdf = new WebSupergoo.ABCpdf10.Doc()) { 
            tempPdf.HtmlOptions.Engine = WebSupergoo.ABCpdf10.EngineType.Gecko;
            tempPdf.HtmlOptions.EndTasks(WebSupergoo.ABCpdf10.TaskState.AllWhenBecomeIdle);
        }
    }
}

编辑 2

Gecko 进程的停止和重新启动似乎确实会导致性能下降,因此这不是一个理想的解决方案。 我已经联系了他们的支持团队,他们说他们计划发布一个没有这个问题的新版本,但不知道什么时候会发布。 他们还建议仅在捕获此处抛出的异常时才使用 EndTasks 函数。对于我的网站,需要重写,所以我不会走这条路。

似乎修复 tempPdf.HtmlOptions.EndTasks(WebSupergoo.ABCpdf10.TaskState.AllWhenBecomeIdle); 并不是真正的修复,甚至会导致一些性能下降。我继续并删除了 try/catch 并恢复到旧版本的 ABC PDF,直到这个问题得到解决。这个版本可能不应该发布,他们在发布之前也知道这个问题。

基于 Steven Lemmens 的回答:

using (var abcpdf = new Doc())
{
    abcpdf.HtmlOptions.PageCacheClear();
    abcpdf.HtmlOptions.HideBackground = true;

    abcpdf.HtmlOptions.Engine = EngineType.Gecko;
    abcpdf.HtmlOptions.UseScript = true;
    abcpdf.HtmlOptions.Timeout = 10000;
    abcpdf.HtmlOptions.OnLoadScript = "(function(){window.ABCpdf_go = false; setTimeout(function(){window.ABCpdf_go = true;},3000);})();";

    var printContentUrl = "url for content";

    try
    {
        abcpdf.Rect.Width = 612 - 14 - 14;      //612 - 54 - 54; // whole width minus left and right
        abcpdf.Rect.Height = 792 - 46 - 46;     //792 - 54 - 54; // whole height minus top and bottom
        abcpdf.Rect.Position(14, 46);          // 54, 54 centered

        // Generate pdf from Url
        var id = abcpdf.AddImageUrl(printContentUrl);

        while (abcpdf.GetInfo(id, "Truncated") == "1")
        {
            abcpdf.Page = abcpdf.AddPage();
            id = abcpdf.AddImageToChain(id);
        }

        // Add footer with page number.
        abcpdf.Font = abcpdf.AddFont("Helvetica-Oblique");
        for (var i = 1; i <= abcpdf.PageCount; ++i)
        {
            abcpdf.PageNumber = i;
            abcpdf.Rect.Height = 54;
            abcpdf.Rect.Position(54, 0);
            abcpdf.HPos = 0.9; // align to the right
            abcpdf.VPos = 0.25; // above center
            abcpdf.AddText(string.Format("Page {0} of {1}", i, abcpdf.PageCount));
        }

        var docData = abcpdf.GetData();
        Response.Clear();
        Response.ContentType = "application/pdf";
        Response.AddHeader("Content-Disposition", "inline; filename=DynamicPdf.pdf");
        Response.BinaryWrite(docData);
    }
    catch(WebSupergoo.ABCpdf10.Internal.PDFException pdfEx)
    {
        //Log Exception here....
        ResetAbcPdfGeckoProcess();
        Response.Redirect(Request.RawUrl);
    }
    catch (Exception ex)
    {
        //Log Exception here....
        throw ex;
    }
}

public static void ResetAbcPdfGeckoProcess()
{
    using (var tempPdf = new WebSupergoo.ABCpdf10.Doc()) { 
        tempPdf.HtmlOptions.Engine = WebSupergoo.ABCpdf10.EngineType.Gecko;
        tempPdf.HtmlOptions.EndTasks(WebSupergoo.ABCpdf10.TaskState.AllWhenBecomeIdle);
    }
}

这段代码对我来说效果很好。请注意,此代码是 asp.net 页面的 Page_Load() 方法的一部分。只有在第一次尝试生成 pdf 失败时才会产生调用 ResetAbcPdfGeckoProcess() 方法的开销。如果引发 PDFException,我会执行 Response.Redirect(Request.RawUrl); 以使 Page_Load 在 Gecko 进程启动和停止后再次触发.在 Page_Load 的第二次迭代中,PDF 被正确呈现。