2
votes

I'm trying to use a .Net 4.6 library in a .Net 2.0 executable.

I used this COM approach and it works until assembly System.Collections.Immutable is resolved.

I'm getting ReflectionTypeLoadException with message "Could not load file or assembly 'System.Collections.Immutable, Version=1.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)"

The .Net 4.6 library uses Roslyn and Roslyn assemblies references System.Collections.Immutable, Version=1.2.1.0. And this version of the assembly is referenced in the project. But a Roslyn assembly (Microsoft.CodeAnalysis.CSharp, Version 2.6.0.0) references System.Reflection.Metadata, Version=1.4.1.0 and metadata assembly references System.Collections.Immutable, Version=1.2.0.0

When I use the .Net 4.6 library in a .Net 4.6 executable instead of .Net 2.0, all works fine, no exceptions.

I tried setting "Specific Version" of Immutable assembly to false, not working. Also tried separating .Net 2.0 and .Net 4.6 folders, not working either. Also .Net 4.6 library dll and Immutable dll are in the same folder.

app.config of .Net 2.0 executable

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="Adapter" />
    </assemblyBinding>
  </runtime>
</configuration>

app.config of .Net 4.6 library

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.2.1.0" newVersion="1.2.1.0" />
      </dependentAssembly>
      ......other dependent assemblies....
    </assemblyBinding>
  </runtime>
</configuration>

.Net 2.0 executable code

static void Main()
{
    Console.WriteLine("CLR version from EXE: {0}", Environment.Version);
    Console.WriteLine("Is64BitProcess: {0}", (IntPtr.Size == 8));

    Type myClassAdapterType = Type.GetTypeFromProgID("Net4ToNet2Adapter.MyClassAdapter");
    object myClassAdapterInstance = Activator.CreateInstance(myClassAdapterType);
    IEnchanterAdapter myClassAdapter = (IEnchanterAdapter)myClassAdapterInstance;
    myClassAdapter.DoNet4Action();
}

.Net 4.6 library code

[ComVisible(true)]
[Guid("E36BBF07-591E-4959-97AE-D439CBA392FB")]
public interface IMyClassAdapter
{
    void DoNet4Action();
}

[ComVisible(true)]
[Guid("A6574755-925A-4E41-A01B-B6A0EEF72DF0")]
public class MyClassAdapter : IMyClassAdapter
{
    private MyClass _myClass = new MyClass();

    public MyClassAdapter()
    {
        Console.WriteLine("adapter constructor");
    }

    public void DoNet4Action()
    {
        Console.WriteLine("CLR version from EXE: {0}", Environment.Version);
        Console.WriteLine("Is64BitProcess: {0}", Environment.Is64BitProcess);

        _myClass.DoNet4Action();
    }
}

class MyClass
{
    public void DoNet4Action()
    {
        var workspace = new AdhocWorkspace(); // throws exception here
    }
}

Output

CLR version from EXE: 2.0.50727.8794
Is64BitProcess: True
adapter constructor
CLR version from EXE: 4.0.30319.42000
Is64BitProcess: True

Fusion Assembly Binding Log (of when project references Version 1.2.2.0) (this again works with .Net 4.6 executable)

        FusionLog   "=== Pre-bind state information ===
LOG: DisplayName = System.Collections.Immutable, Version=1.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\n (Fully-specified)
LOG: Appbase = file:///D:/Develop/Enchanter/UnityWorkspace/bin/Debug/
LOG: Initial PrivatePath = NULL
Calling assembly : Microsoft.CodeAnalysis, Version=2.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: D:\\Develop\\Enchanter\\UnityWorkspace\\bin\\Debug\\UnityWorkspace.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\config\\machine.config.
LOG: Post-policy reference: System.Collections.Immutable, Version=1.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
LOG: Attempting download of new URL file:///D:/Develop/Enchanter/UnityWorkspace/bin/Debug/System.Collections.Immutable.DLL.
LOG: Attempting download of new URL file:///D:/Develop/Enchanter/UnityWorkspace/bin/Debug/System.Collections.Immutable/System.Collections.Immutable.DLL.
LOG: Attempting download of new URL file:///D:/Develop/Enchanter/UnityWorkspace/bin/Debug/Adapter/System.Collections.Immutable.DLL.
WRN: Comparing the assembly name resulted in the mismatch: Build Number
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.
"   string
1
have you tried adding a binding redirect in the app.config file? docs.microsoft.com/en-us/dotnet/framework/configure-apps/…user1228
You are just seeing the in-process side-by-side CLR versioning feature of .NET 4.x. Actually meant to solve DLL Hell for unmanaged host programs that use COM, but no major reason I can think of why it wouldn't work for a .NET 2.0 program. It doesn't otherwise solve your own DLL Hell, if you can't get your code to agree about which version of that assembly to use then you need a bindingRedirect in the .config file. Normally auto-generated by msbuild but it can't help here.Hans Passant
I briefly saw the Fuslogvw trace flash by before you removed it again. It told you what .config file to change.Hans Passant
Sorry, added the fusion log again, but it is for Version 1.2.2.0 (which is latest). Yet it gives almost the same errorSARI
Added app.config aboveSARI

1 Answers

0
votes

Assembly binding redirections should be added to the .Net 2.0 executable manually because Visual Studio doesn't do that automatically since they don't have a relation because of the COM interop.

New app.config of .Net 2.0 executable is:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="Adapter" />
      <dependentAssembly>
        <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.2.2.0" newVersion="1.2.2.0" />
      </dependentAssembly>
       ......other dependent assemblies....
    </assemblyBinding>
  </runtime>
</configuration>