0

I have an ASP.NET Core 8.0 Web API created with C# that provides an endpoint to create PDFs from HTML markup. I use PuppeteerSharp to create the PDF, and it worked fine locally, but it does not work when deployed to IIS on a Windows Server machine.

To investigate this issue further, I have extracted the PDF creation code into a standalone application:

using PuppeteerSharp;
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace MSS.PdfExport
{
    public class Parameters
    {
        public string Html { get; set; }

        public string Base64 { get; set; }

        public Parameters()
        {
            Html = string.Empty;
            Base64 = string.Empty;
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            var streamWriter = new StreamWriter(@"C:\Temp\Test\Log.txt", false, Encoding.UTF8)
            {
                AutoFlush = true
            };

            // Read parsed HTML code.
            var stringBuilder = new StringBuilder();

            while (true)
            {
                var ch = Convert.ToChar(Console.Read());
                stringBuilder.Append(ch);

                if (ch == '\n')
                {
                    break;
                }
            }

            var sbText = stringBuilder.ToString();

            streamWriter.WriteLine($"{nameof(stringBuilder)} = {stringBuilder}");
            streamWriter.WriteLine();
            streamWriter.WriteLine();
            streamWriter.WriteLine();

            // Get HTML code.
            var html = WebUtility.UrlDecode(sbText);

            streamWriter.WriteLine($"{nameof(html)} = {html}");
            streamWriter.WriteLine();
            streamWriter.WriteLine();
            streamWriter.WriteLine();

            // Execute generator.
            try
            {
                var test = new Parameters()
                {
                    Html = html
                };

                ExportPdf(test).Wait();

                Console.WriteLine(test.Base64);

                File.WriteAllText(@"C:\Temp\Test\output.txt", test.Base64);
            }
            catch (Exception ex)
            {
                streamWriter.WriteLine();
                streamWriter.WriteLine();
                streamWriter.WriteLine();

                streamWriter.WriteLine($"{nameof(ex)} = {ex}");
            }
            finally
            {
                streamWriter.Close();
            }
        }

        private static async Task ExportPdf(Parameters parameters)
        {
            var browserFetcher = new BrowserFetcher();
            await browserFetcher.DownloadAsync();

            var browser = await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true,
                Args = ["--no-sandbox", "--disable-setuid-sandbox"]
            });

            using (var page = await browser.NewPageAsync())
            {
                await page.SetContentAsync(parameters.Html);

                var data = await page.PdfDataAsync(new PdfOptions
                {
                    PrintBackground = true
                });

                parameters.Base64 = Convert.ToBase64String(data);
            }

            await browser.CloseAsync();
        }
    }
}

The application reads a URL-encoded HTML input stream, converts it to HTML, and finally uses Puppeteer to open a headless Chromium to convert the HTML into PDF and write the results in console and write into a file.

If I save the URL-encoded HTML text into a text file and execute my application via Windows command line, it works perfectly fine!

MSS.PdfExport.exe < "C:\Temp\Test\New Text Document.txt"

However, when I call the application from my API with this code:

// Decode the HTML code.
var html = WebUtility.UrlDecode(model.Html);

_logger.LogInformation($"{this}.Index > Execute generation.");

var htmlBytes = Encoding.UTF8.GetBytes(model.Html);
var utf8Html = Encoding.UTF8.GetString(htmlBytes);

_logger.LogInformation($"{this}.Index > {nameof(utf8Html)} = {utf8Html}");
_logger.LogInformation($"{this}.Index > {nameof(utf8Html.Length)} = {utf8Html.Length}");

var startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = @"C:\Temp\PdfExport\MSS.PdfExport.exe";
startInfo.WorkingDirectory = @"C:\Temp\PdfExport\";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;

var output = string.Empty;

try
{
    using (var process = Process.Start(startInfo))
    {
        _logger.LogInformation($"{this}.Index > Process started. {nameof(startInfo)} = [{startInfo}]");

        if (process is not null)
        {
            using (var writer = process.StandardInput)
            {
                if (writer.BaseStream.CanWrite)
                {
                    writer.WriteLine(utf8Html);
                }
            }

            var reader = process.StandardOutput;
            output = reader.ReadToEnd();

            _logger.LogInformation($"{this}.Index > {nameof(output)} = \"{output}\"");

            process.WaitForExit();
        }
    }
}
catch (Exception exception)
{
    _logger.LogInformation($"{this}.Index > {nameof(exception)} = {exception}");
}

Puppeteer does not work and I get the following exception:

System.AggregateException: One or more errors occurred. (Failed to launch browser!

[112204:131028:1023/144647.887:ERROR:base\win\message_window.cc:124] Failed to register the window class for a message-only window: The requested lookup key was not found in any active activation context. (0x36B7))

PuppeteerSharp.ProcessException: Failed to launch browser! [112204:131028:1023/144647.887:ERROR:base\win\message_window.cc:124] Failed to register the window class for a message-only window: The requested lookup key was not found in any active activation context. (0x36B7)

at PuppeteerSharp.States.ProcessStartingState.StartCoreAsync(LauncherBase p) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/States/ProcessStartingState.cs:line 83
at PuppeteerSharp.States.ProcessStartingState.StartCoreAsync(LauncherBase p) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/States/ProcessStartingState.cs:line 89
at PuppeteerSharp.Launcher.LaunchAsync(LaunchOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Launcher.cs:line 77
at PuppeteerSharp.Launcher.LaunchAsync(LaunchOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Launcher.cs:line 110
at MSS.PdfExport.Program.ExportPdf(Parameters parameters) in [redacted]\MSS.PdfExport\Program.cs:line 94
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at MSS.PdfExport.Program.Main(String[] args) in [redacted]\MSS.PdfExport\Program.cs:line 69

3

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.