Search code examples
c#visual-studioexceptiondllmismatch

Assembly loaded at runtime throwing manifest mismatch exception


I need to dynamically define the source for an external assembly in a C# application (the reference is added to the project, but with copy local set to false).

If I add the path for the reference in the App.config file everything works fine:

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Siemens.Engineering" culture="neutral" publicKeyToken="d29ec89bac048f84"/>

        <codeBase version="18.0.0.0" href="FILE://C:\Program Files\Siemens\Automation\Portal V18\PublicAPI\V18\Siemens.Engineering.dll"/>
      </dependentAssembly>
     
    </assemblyBinding>
</runtime>

This works properly and I can user the library without issue.

However, I need to load the reference dynamically (the location might change at runtime or I might need to use different but compatible version). In order to do this I delete the reference from App.config and register a callback to the AppDomain.CurrentDomain.AssemblyResolve event (which triggers when a assembly resolution fails). Then, in this callback, I dynamically load the assembly. In the example below I am loading the same exact reference as the one I had in App.config:

static void Main(string[] args)
{
    var loadedReferences = AppDomain.CurrentDomain.GetAssemblies();
    AppDomain.CurrentDomain.AssemblyResolve += MyResolver;

    doStuff();
}

private static Assembly MyResolver(object sender, ResolveEventArgs args)
{
    String assemblyName = "C:\\Program Files\\Siemens\\Automation\\Portal V18\\PublicAPI\\V18\\Siemens.Engineering.dll";
    Assembly thisA = Assembly.LoadFrom(assemblyName);

    return thisA;    
}

I can see in the debugger that the proper assembly is being loaded. However, when I try to call any method from that library I get the following error about a mismatched manifest:

FileLoadException: Could not load file or assembly 'Siemens.Engineering.Contract, Version=1800.100.4201.1, Culture=neutral, PublicKeyToken=37a18b206f7724a6' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

FileLoadException: The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

I really don't understand it, given that I am loading the same DLL. Any hints appreciated!


Solution

  • As it was pointed out to me, the problem is that when an assembly failed to load and triggered the OnResolve I wasn't checking which actual assembly it was, and was always loading the same one. The solution is to simply check the name of the offending assembly in the event argument and act accordingly.

    Someone asked in the comments so I'll add my implementation. In my particular case I have a method which allows the user to select which version of the dll to load and registers the AssemblyResolve callback:

    public static void RegisterSiemensAssemblies(string tiaPortalVersion)
            {
    
                // ** Resolve Siemens Openness assemblies for TiaWrapperLib library
                if (String.IsNullOrEmpty(tiaPortalVersion))
                {
                    TiaWrapperLib.Utils.Resolver.SetAssemblyPaths("15.0", "15.0.0.0");                   // By default load DLLs for Tia Portal v15, Openness v15
                }
                else
                {
                    if (tiaPortalVersion == "18.0")
                        TiaWrapperLib.Utils.Resolver.SetAssemblyPaths("18.0", "18.0.0.0");              // If TIA Portal 18 load Openness v18 
                    else if (tiaPortalVersion == "14.0" || tiaPortalVersion == "15.0" || tiaPortalVersion == "15.1" || tiaPortalVersion == "16.0" || tiaPortalVersion == "17.0")
                        TiaWrapperLib.Utils.Resolver.SetAssemblyPaths(tiaPortalVersion, "15.0.0.0");    // IF any other TIA Portal load Openness v15
                    else
                        TiaWrapperLib.Utils.Resolver.SetAssemblyPaths("15.0", "15.0.0.0");              // If invalid version load Openness v15 from TIA Portal v15
                }
    
                AppDomain.CurrentDomain.AssemblyResolve += TiaWrapperLib.Utils.Resolver.OnResolve;      // Add callback to resolve Siemens assemblies in real time when the DLLs aren't found
    
            }
    

    And then the callback sets the proper assemblies:

     public static Assembly OnResolve(object sender, ResolveEventArgs args)
            {
                //var executingAssembly = Assembly.GetExecutingAssembly();
                var assemblyName = new AssemblyName(args.Name);
                string path = "";
    
                // If not resolving Siemens assemblies
                if (!assemblyName.Name.EndsWith("Siemens.Engineering")
                    && !assemblyName.Name.EndsWith("Siemens.Engineering.Hmi"))
                    return null;
    
                // Initialize assembly paths for latest versions
                if (AssemblyPath == null || AssemblyPathHmi == null)
                {
                    var tiaVersion = GetEngineeringVersions().Last();
                    var opennessVersion = GetOpennessVersions(tiaVersion).Last();
                    SetAssemblyPaths(tiaVersion, opennessVersion);
                }
    
                if (assemblyName.Name.EndsWith("Siemens.Engineering"))
                    path = AssemblyPath;
                if (assemblyName.Name.EndsWith("Siemens.Engineering.Hmi"))
                    path = AssemblyPathHmi;
    
                if (string.IsNullOrEmpty(path) || !File.Exists(path))
                {
                    throw new FileLoadException($"Could not load assembly '{assemblyName}' from path '{path}'.");
                }
    
                return Assembly.LoadFrom(path);
            }