.netcommanifestsxsregistration-free-com

How does SXS chooses which framework version should be loaded?


I'm currently working on getting .NET Assemblies (with COM classes) to work registration free. It works well, however i have one issue which i can't seem to pin-point the exact cause.

My issue is, assembly binding isn't done on the correct .NET Framework version.

I currently have 2 assembles (let's call them A.dll and B.dll) and they are both built using .NET 4.0.

B.dll is very small, i made it to test Registration-free activation. It has a manifest next to it, B.dll.manifest). It contains 1 class with 1 property and 1 method.

A.dll is much more complex, signed, it's manifest is embeded as a resource as a post-build event (using mt.exe).

I made a VB6 application to test them. My application has a manifest, which declares the dependencies.

If i run my application, B.dll works well but A.dll doesn't. Looking at the binding logs with fuslogvw.exe, i found that the binding of A.dll is attempted using .NET 2.0, while B.dll is done using .NET 4.0.

A.dll ends up failing with error code 0x8013101b, which is COR_E_NEWER_RUNTIME. ERR: Error extracting manifest import from file (hr = 0x8013101b).

To get it to work, i have to add a .config file to my VB6 application with this content

<?xml version="1.0"?>
<configuration>
    <startup useLegacyV2RuntimeActivationPolicy="true" >
        <supportedRuntime version="v4.0" />
    </startup>
</configuration>

Then, it binds to the proper framework version and it works.

I tougth that maybe, MT.exe changed attributes/meta-informations in my Assembly, making it think it should be loaded with 2.0. So i opened it up using ILSpy. Everything i see in there talks about 4.0 and nothing is suspicious.

I've read that the assembly should be loaded, by default, using the FW that was used to build em (in my case, they are ALL 4.0, no 2.0 anywhere). So, i don't get WHY it tries to load that particular one using 2.0.

This is an issue for me as i would like to avoid having to create/maintain but most of all DEPLOY those .config for all the applications that are gonna use this particular assembly. That would mean having around 100 .config files, in my particular case.

For reference:

The MT.exe i used is from 7.0A SDK and is version 5.2.3790.2076.

Here is the fuslogvw.exe output (sorry for the french and the messed up accentuated char, the important part is the framework version number anyways)

B, working

JRN : cette liaison démarre dans le contexte de chargement de default.
JRN : tentative de téléchargement du fichier de configuration de l'application à partir de file:///C:/RegFreeCom/BafComClient/binTB/Project1.exe.config.
JRNÂ : Le fichier de configuration C:\RegFreeCom\BafComClient\binTB\Project1.exe.config n'existe pas.
JRN : aucun fichier de configuration de l'application n'a été trouvé.
JRN : utilisation du fichier de configuration d'hôte : 
JRN : utilisation du fichier de configuration de l'ordinateur à partir de C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
JRN : stratégie non appliquée à la référence à ce stade (liaison d'assembly privée, personnalisée, partielle ou basée sur l'emplacement).
JRN : tentative de téléchargement de la nouvelle URL file:///C:/RegFreeCom/BafComClient/binTB/sidebysidenet.DLL.
JRN : le téléchargement de l'assembly a réussi. Tentative d'installation du fichier : C:\RegFreeCom\BafComClient\binTB\B.dll
JRN : entrée dans la phase d'installation à exécution à partir de la source.
JRN : le nom de l'assembly est : B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
JRN : la liaison a réussi. Elle retourne un assembly à partir de C:\RegFreeCom\BafComClient\binTB\B.dll.
JRN : l'assembly est chargé dans le contexte de chargement default.

A, not working

JRN : cette liaison démarre dans le contexte de chargement de default.
JRN : tentative de téléchargement du fichier de configuration de l'application à partir de file:///C:/RegFreeCom/BafComClient/binTB/Project1.exe.config.
JRNÂ : Le fichier de configuration C:\RegFreeCom\BafComClient\binTB\Project1.exe.config n'existe pas.
JRN : aucun fichier de configuration de l'application n'a été trouvé.
JRN : utilisation du fichier de configuration de l'ordinateur à partir de C:\Windows\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
JRN : référence post-stratégie : A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=SomeToken, processorArchitecture=MSIL
JRN : échec de la recherche dans le GAC.
JRN : tentative de téléchargement de la nouvelle URL file:///C:/RegFreeCom/BafComClient/binTB/A.DLL.
JRN : le téléchargement de l'assembly a réussi. Tentative d'installation du fichier : C:\RegFreeCom\BafComClient\binTB\A.dll
JRN : entrée dans la phase d'installation à exécution à partir de la source.
ERR : erreur lors de l'extraction de l'importation du manifeste à partir du fichier (hr = 0x8013101b).
ERR : impossible de terminer l'installation de l'assembly (hr = 0x8013101b). Détection terminée.

A, working (thanks to the config file)

JRN : cette liaison démarre dans le contexte de chargement de default.
JRN : tentative de téléchargement du fichier de configuration de l'application à partir de file:///C:/RegFreeCom/BafComClient/binTB/Project1.exe.Config.
JRN : le fichier de configuration de l'application a été trouvé (C:\RegFreeCom\BafComClient\binTB\Project1.exe.Config).
JRN : utilisation du fichier de configuration de l'application : C:\RegFreeCom\BafComClient\binTB\Project1.exe.Config
JRN : utilisation du fichier de configuration d'hôte : 
JRN : utilisation du fichier de configuration de l'ordinateur à partir de C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
JRN : référence post-stratégie : A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=SomeToken, processorArchitecture=MSIL
JRN : échec de la recherche dans le GAC.
JRN : tentative de téléchargement de la nouvelle URL file:///C:/RegFreeCom/BafComClient/binTB/A.DLL.
JRN : le téléchargement de l'assembly a réussi. Tentative d'installation du fichier : C:\RegFreeCom\BafComClient\binTB\A.dll
JRN : entrée dans la phase d'installation à exécution à partir de la source.
JRN : le nom de l'assembly est : A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=SomeToken
JRN : la liaison a réussi. Elle retourne un assembly à partir de C:\RegFreeCom\BafComClient\binTB\A.dll.
JRN : l'assembly est chargé dans le contexte de chargement default.

Solution

  • M. Miller from the CLR Team pointed me in the right direction. My manifest (generated by mt.exe from Windows SDK 7.0A) didn't include a value in the runtimeVersion of the clrClass tag.

    This was causing the CLR Loader to get into a "Capped" loading path. This capped loading path, which from what M. Miller told me, happens when a version isn't v4 or higher and it caps the value returned by the loader's "FindLatestVersion" to v2. So it tries to load assemblies using the 2.0 FW.

    Modifying the manifest to specify the proper version (v4.0.30319) and making sure nothing was cached (i had to copy my directory somewhere else, for the right version to appear in the logs), ended up solving my problem. Now the loading goes down the right path instead of going the "Capped" path.

    So this ended up being a simple manifest issue.

    FWIW, i tried generating the same manifest using mt.exe from Windows SDK 8.1 (latest one) and it generated the runtimeVersion attribute properly!

    I would like to thanks Mark Miller for his help on this matter, without his help it would have taken a while for me to figure this out!