Does Kentico 10 use a different strategy for assembly loading/probing/handling than previous versions?

1

We have what seems to be a assembly loading or probing difference driven by Kentico 10 (compared to 9 and earlier) that I'm trying to understand, so I can troubleshoot assembly load errors, my current example being the following...

Example: We've been using Expert PDF components in websites from http://www.html-to-pdf.net The product has a managed ephtmltopdf.dll assembly that relies on the unmanaged "helper" DLL epengine.dll also sitting alongside in the web app's bin/ folder, and sometimes an exception is thrown when the app starts up...

Under Kentico 9 (and earlier versions) an epengine exception is thrown and appears in the Kentico Event Log whenever the website starts up.

Under Kentico 10 an epengine exception occurs and prevents the website from running altogether.

I'm trying to correlate the difference between these two behaviours under the same component configuration.


This is the epengine CMS Event log entry under Kentico 9 and earlier versions (does not prevent website from running):

Event type: Error
Event time: 7/18/2017 4:00:06 AM
Source: Discovery
Event code: E:\Kentico_V9\CMS\bin\epengine.dll
User ID: 65
User name: public
Description: Could not load file or assembly 'epengine.dll' or one of its dependencies. The module was expected to contain an assembly manifest.
The file E:\Kentico_V9\CMS\bin\epengine.dll is not an assembly or the assembly was compiled for a later version of the .NET runtime.
Machine name: OX
Event URL: /register/all
URL referrer: /Public-(1)/Search-Results
User agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; BOIE9;ENUS; rv:11.0) like Gecko

Under Kentico 10 this is the epengine error that prevents the site from running.

*** Assembly Binder Log Entry  (17/07/2017 @ 4:36:56 PM) ***

The operation failed.
Bind result: hr = 0x80131018. No description available.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  C:\Windows\SysWOW64\inetsrv\w3wp.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: DisplayName = epengine
 (Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: epengine | Domain ID: 2
WRN: A partial bind occurs when only part of the assembly display name is provided.
WRN: This might result in the binder loading an incorrect assembly.
WRN: It is recommended to provide a fully specified textual identity for the assembly,
WRN: that consists of the simple name, version, culture, and public key token.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
LOG: Appbase = file:///C:/inetpub/wwwroot/website/CMS/
LOG: Initial PrivatePath = C:\inetpub\wwwroot\website\CMS\bin
LOG: Dynamic Base = C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\672d45d4
LOG: Cache Base = C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\672d45d4
LOG: AppName = f7cc5d08
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\inetpub\wwwroot\website\CMS\web.config
LOG: Using host configuration file: C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet.config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/root/672d45d4/f7cc5d08/epengine.DLL.
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/root/672d45d4/f7cc5d08/epengine/epengine.DLL.
LOG: Attempting download of new URL file:///C:/inetpub/wwwroot/website/CMS/bin/epengine.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\inetpub\wwwroot\website\CMS\bin\epengine.dll
LOG: Entering download cache setup phase.
ERR: Error extracting manifest import from file (hr = 0x80131018).
ERR: Setup failed with hr = 0x80131018.
ERR: Failed to complete setup of assembly (hr = 0x80131018). Probing terminated.

*** Assembly Binder Log Entry  (17/07/2017 @ 4:36:56 PM) ***

The operation failed.
Bind result: hr = 0x80131018. No description available.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  C:\Windows\SysWOW64\inetsrv\w3wp.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: DisplayName = epengine
 (Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: epengine | Domain ID: 2
WRN: A partial bind occurs when only part of the assembly display name is provided.
WRN: This might result in the binder loading an incorrect assembly.
WRN: It is recommended to provide a fully specified textual identity for the assembly,
WRN: that consists of the simple name, version, culture, and public key token.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
LOG: Appbase = file:///C:/inetpub/wwwroot/website/CMS/
LOG: Initial PrivatePath = C:\inetpub\wwwroot\website\CMS\bin
LOG: Dynamic Base = C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\672d45d4
LOG: Cache Base = C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\672d45d4
LOG: AppName = f7cc5d08
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\inetpub\wwwroot\website\CMS\web.config
LOG: Using host configuration file: C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet.config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/root/672d45d4/f7cc5d08/epengine.DLL.
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/root/672d45d4/f7cc5d08/epengine/epengine.DLL.
LOG: Attempting download of new URL file:///C:/inetpub/wwwroot/website/CMS/bin/epengine.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\inetpub\wwwroot\website\CMS\bin\epengine.dll
LOG: Entering download cache setup phase.
ERR: Error extracting manifest import from file (hr = 0x80131018).
ERR: Setup failed with hr = 0x80131018.
ERR: Failed to complete setup of assembly (hr = 0x80131018). Probing terminated.

Whenever I try any of the following in both Kentico 9 (or earlier) and in 10, the epengine error always surfaces earlier in Kentico 10 preventing the website from running instead of showing inside the Kentico CMS Event Log.

  • use the Nuget sources for epengine instead
  • use to the latest release of the epengine component and try older versions too (between Expert 9.0.5 - 11.0)
  • upgrade/downgrade .NET versions
  • use same app pool settings, .NET version, ACL permissions (and vary these to test)
  • A new/base install of Kentico 10 (still surfaces the error earlier)

The difference in timing of error expression seems to be whether I use this component in Kentico 9 or 10.

What I would like to know is if there's a difference in assembly loading, probing, or handling of exceptions that might help explain why this component's error would stop the Kentico 10 ASP.NET website from loading, but NOT stop a Kentico 9 website from loading with the same IIS & .NET configuration.

(Note that I'm also tackling the PDF component error head on by contacting the vendor - ultimately resolution would be the best solution).

clr
kentico
probing
asked on Stack Overflow Jul 18, 2017 by John K • edited Jul 20, 2017 by John K

2 Answers

2

Not ideal. This solution is a workaround to the problem of the ephtmltopdf.dll and epengine.dll assemblies throwing an error when loading from the app's bin/ folder during web application spin-up causing the Kentico 10 website to not load.

This solution is based on @rocky's comment under the original question.

This effectively causes the Expert PDF component to load after the Kentico site is already running.

Not all usages of the PDF generator have been tested. The code sample here will successfully download the given URL as a PDF document using Expert PDF component in an environment in which a direct reference to the component does not work. (it works on my computer)


Steps to work around -

  1. Remove all assembly references from your application for the Expert PDF components; also remove the C# using statements. You will get compile errors where the PDF classes and constructs are used in code.

  2. Dynamically load the Expert PDF assembly from outside the bin when you need it (as shown in the code sample - see Assembly.LoadFile).

  3. Dynamically instantiate the component (as shown in the code sample - see dynamic + CreateInstance )

  4. The remainder of your Expert PDF code can remain the same.

// In ~/TestPdf.aspx.cs code-behind page, inside a Kentico 10 website 

namespace CMSApp
{
    using System;
    //using ExpertPdf.HtmlToPdf; // << Namespace no longer available to C# compiler.
    using System.Reflection;

    public partial class test1 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

            // var converter = new ExpertPdf.HtmlToPdf.PdfConverter(); // << Type no longer available to C# compiler.

            // Dynamically load the Expert PDF Assembly, Type and an instance...
            Assembly assemb = System.Reflection.Assembly.LoadFile(@"C:\KenticoBaseInstalls\Kentico10.2-app\LibMore\ExpertPdf-HtmlToPdf-v11.0.0\Bin\.NET_4.0\ephtmltopdf.dll");
            dynamic converter = assemb.CreateInstance("ExpertPdf.HtmlToPdf.PdfConverter", true);


            // Continue to use old PDF code but without compiler type checks and VS Editor Intellisense.

            byte[] pdfBytes = converter.GetPdfBytesFromUrl("https://www.iana.org/domains/reserved");
            Response.ClearHeaders();

            Response.ContentType = "application/octet-stream";
            Response.AppendHeader("Content-Disposition", "attachment; filename=example.pdf");
            Response.BinaryWrite(pdfBytes);
            Response.Flush();
            Response.End();
        }
    }
}

NOTE: There are other constructs in the ExpertPDF assembly that will fail and which you will have to resolve like the above example such as:

  • static UnitsConverter.PixelsToPoints(..)
  • HtmlToPdfArea class
  • ImageArea class
  • PdfPageSize enum
  • etc.

Addendum Notes to Solution

In the above code sample System.Reflection.Assembly.LoadFile(..) is prone to location issue between different environments or if the file is moved. A more robust substitute is to use application base subdirectories to get the assembly as follows. Note that Kentico 10 uses this strategy and we are piggybacking on their CMS folder structure:

Create an additional subfolder in ~/CMSDependencies that can be probed in the website as follows by adding the Expert PDF DLLs (both managed and unmanaged) into it. You will end up with this tree structure.

+---CMSDependencies
    +---ExpertPdfHtmlToPdf.11.0.0
            epengine.dll
            ephtmltopdf.dll

Add the name of the the Expert Pdf folder into the privatePath of the probing element in web.config without replacing any other values by using a semicolon.

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="CMSDependencies\Newtonsoft.Json.6.0.0.0;CMSDependencies\ExpertPdfHtmlToPdf.11.0.0"/>
    </assemblyBinding>
  </runtime>

Load the assembly in C# by simple name as such:

Assembly assemb = System.Reflection.Assembly.Load(new AssemblyName("ephtmltopdf"));
answered on Stack Overflow Jul 19, 2017 by John K • edited Jun 20, 2020 by Community
0

The issue was resolved by hotfixing the website to Kentico 10.0.29 (highest hotfix at time of this writing), as per suggestion from Kentico Support.

Communication included below for reference. Bold is my emphasis on the solution.

From: Kentico Support
Sent: July-25-17 9:46 AM
To: John Kane
Subject: RE: Expert PDF component not compatible with Kentico 10 unless loaded dynamically Ticket:0072002734

Hello John,

Thank you for your message. Upon some investigation it is possible that this issue was fixed in Kentico 10 hotfix 10.0.25. Could you please try applying the latest hotfix? The bug was related to the application start and our developers think this could be related.

Also, could you please check all occurrences of native dll references, including a web.config file. This comment is seems to be related and accurate - Could not load file or assembly '\bin\ABCpdf8-64.dll' or one of its dependencies. The module was expected to contain an assembly manifest

Best regards,
Juraj Ondrus
Tech. support lead

I've also verified the Expert PDF component runs properly on a hotfixed base Kentico 10 install by downloading a URL and converting it to a PDF document programatically. (See Expert PDF website and documentation for code samples.)

Quickest way to Repo this Issue

Because we are in the middle of a Kentico upgrade from 8.2 to 10 and solving multiple issues with custom code & custom components, I'm leaving the simplest repo scenario here that proves the problem when using Expert PDF, and the solution, for anybody else who might have a similar issue, during an upgrade or first install.

Repo Issue

  1. Use the Kentico 10 installer to create a new website application.
  2. Verify the website is running Kentico 10 with no hotfixes, will report as 10.0.0.
  3. Using Nuget, reference the ExpertPdfHtmlToPdf package (version 9.5 through 11 is what I tinkered with).
  4. Compile and run the website - the website does not load and instead displays a .NET error with a "epengine" message like posted in the question.

Repo Solution

  1. Apply the latest available hotfix to the Kentico 10 install, following Kentico hotfix instructions.
  2. Verify site now reports as 10.0.x where x is the applied hotfix number. Ensure it's >= #25 as mentioned by Kentico support.
  3. Compile and run the website - the error is gone and the site runs properly.
answered on Stack Overflow Jul 27, 2017 by John K

User contributions licensed under CC BY-SA 3.0