Quelques outils pour résoudre les problèmes de chargement d’assemblies

Dans le cas de BadImageFormatException, parmi toutes les dépendances d’un exécutable, il est parfois difficile d’identifier l’assembly dont l’architecture cible est incompatible avec celle de l’exécutable. Certains outils permettent d’avoir plus d’informations sur les dépendances d’une assembly et de visualiser le détail de chargement des assemblies pour un exécutable donné.

Fusion

“Fusion” est le nom de code de l’élément qui charge les dépendances d’une assembly en .NET. Lorsque le code contenu dans une assembly nécessite le chargement d’une autre assembly, à partir du nom et de la version requise, “Fusion” va chercher cette assembly dans différent répertoire: dans le répertoire de l’assembly d’origine puis dans le GAC…
L’intérêt de “Fusion” est qu’il loggue les assemblies qu’il recherche ainsi que les différents répertoires dans lesquels il a effectué cette recherche. En cas de problème lors du chargement d’une assembly, on peut arriver à en connaître la cause.

Activer les logs de “Fusion”

D’abord, il faut activer les logs de “Fusion” en modifiant certaines valeurs dans la base de registre (regedit.exe):

  1. Affecter la valeur DWORD “1” à la clé HKLM\Software\Microsoft\Fusion\ForceLog
  2. Affecter le chemin d’un répertoire à la valeur de .HKLM\Software\Microsoft\Fusion\LogPath de façon à indiquer l’endroit où les logs seront générés. Par exemple: C:\FusionLog\.
  3. Affecter la valeur DWORD “1” à la clé HKLM\Software\Microsoft\Fusion\LogFailures (Facultatif)
  4. Affecter la valeur DWORD “1” à la clé HKLM\Software\Microsoft\Fusion\LogResourceBinds (Facultatif)

Visionner les logs

Pour visionner les logs, on peut s’aider de fuslogvw.exe (fusion log viewer). Cet outil est livré avec le SDK Windows et est accessible en utilisant la ligne de commandes Visual Studio. Le chemin de l’exécutable est, par exemple pour Windows 8.1 avec le framework 4.5.1:

C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1a\bin\NETFX 4.5.1 Tools\fuslogvw.exe

En regardant les logs, on peut avoir une idée de l’assembly qui a été la source de l’erreur et la raison de l’échec du chargement. Par exemple:

The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from:  C:\WINDOWS\Microsoft.NET\Framework\v2.0.60425\mscorwks.dll
Running under executable  C:\Program Files\Microsoft Visual Studio 8\Team Tools\Performance Tools\XXXXviewer.exe
— A detailed error log follows.

=== Pre-bind state information ===
LOG: User = REDMOND\XXXX
LOG: DisplayName = XXXXvisualization, Version=8.0.0.0, Culture=neutral, PublicKeyToken=XXXXXXXXXXXX
 (Fully-specified)
LOG: Appbase = file:///C:/Program Files/Microsoft Visual Studio 8/Team Tools/Performance Tools/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = XXXXviewer.exe
Calling assembly : XXXXviewer, Version=8.0.0.0, Culture=neutral, PublicKeyToken=XXXXXXXXXXXX
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v2.0.60425\config\machine.config.
LOG: Post-policy reference: XXXXvisualization, Version=8.0.0.0, Culture=neutral, PublicKeyToken=XXXXXXXXXX
LOG: GAC Lookup was unsuccessful.
LOG: Attempting download of new URL file:///C:/Program Files/Microsoft Visual Studio 8/Team Tools/Performance Tools/XXXXvisualization.DLL.
LOG: Attempting download of new URL file:///C:/Program Files/Microsoft Visual Studio 8/Team Tools/Performance Tools/XXXXvisualization/XXXXvisualization.DLL.
LOG: Attempting download of new URL file:///C:/Program Files/Microsoft Visual Studio 8/Team Tools/Performance Tools/XXXXvisualization.EXE
LOG: Attempting download of new URL file:///C:/Program Files/Microsoft Visual Studio 8/Team Tools/Performance Tools/XXXXvisualization/XXXXvisualization.EXE.
LOG: All probing URLs attempted and failed.

Ici le chargement a échoué car l’assembly n’a pas été trouvée.

Plus d’informations à propos de fuslogvw.exe sur MSDN.

Ne pas oublier de désactiver les traces après debug

L’écriture des logs détaillés se fait au prix d’une petite baisse de performance. D’autre part, ces logs peuvent occuper beaucoup d’espace disque si on ne les supprime pas régulièrement.

DependancyWalker

DependancyWalker” est un utilitaire qui permet de voir toutes les dépendances d’une assembly, on peut ainsi vérifier que les dépendances existent et sont de la bonne version ou architecture.

“DependancyWalker” peut être téléchargé sur www.dependencywalker.com/.

Identifier les dépendances par programmation

On peut lister les dépendances d’une assembly de la façon suivante:

using System.Reflection;
...
public void DisplayReferencedAssemblies(string assemblyPath)
{
  Assembly assembly = Assembly.LoadFrom(assemblyPath);
  AssemblyName[] referencedAssemblyNames = assembly.GetReferencedAssemblies();
  foreach (var referencedAssemblyName in referencedAssemblyNames)
  {
    Console.WriteLine("Assembly name: {0}, Version={1}", 
      referencedAssemblyName.FullName,
      referencedAssemblyName.Version);
  }
}
Dépendances dans un contexte managé

Cette méthode permet de référencer les dépendances dans un contexte purement managé. Si il y a des dépendances natives ne seront pas listées.

Chargement d’un assembly

Quand on écrit Assembly.LoadFrom(assemblyPath), on charge l’assembly dans le domaine d’application courant. Cette ligne ne peut s’exécuter que si l’assembly à charger est compatible avec l’architecture de l’exécutable:

  • “x86” ou “AnyCPU” si l’exécutable est exécuté en 32 bits,
  • “x64” ou “AnyCPU” si l’exécutable est exécuté en 64 bits.

Pour afficher la version du framework

Pour obtenir la version du framework, on peut exécuter le code suivant:

Assembly assembly = Assembly.LoadFrom(assemblyPath);
object[] attributes = assembly.GetCustomAttributes(true);
Type targetFrameworkAttributeType = 
    typeof(System.Runtime.Versioning.TargetFrameworkAttribute);
System.Runtime.Versioning.TargetFrameworkAttribute fwkAttribute = 
    (System.Runtime.Versioning.TargetFrameworkAttribute)attributes
        .FirstOrDefault(p => p.GetType() == targetFrameworkAttributeType);

// Pour afficher la version sous la forme ".NETFramework,Version=vX.X"
Console.WriteLine(fwkAttribute.FrameworkName);

// Pour afficher la version sous la forme ".NET Framework X.X"
Console.WriteLine(fwkAttribute.FrameworkDisplayName);

Pour afficher la version du CLR d’une assembly

Assembly assembly = Assembly.LoadFrom(assemblyPath);
Console.WriteLine(assembly.ImageRuntimeVersion);
Références

Leave a Reply