Partager via


Résolution des chargements d'assemblys

Le .NET Framework fournit l'événement AppDomain.AssemblyResolve pour les applications qui requièrent un meilleur contrôle du chargement d'assembly. En gérant cet événement, votre application peut charger un assembly dans le contexte de chargement depuis l'extérieur des chemins d'accès de détection normaux, sélectionner la version d'assembly à charger parmi plusieurs, émettre un assembly dynamique et le retourner, et ainsi de suite. Cette rubrique fournit des instructions pour la gestion de l'événement AssemblyResolve.

RemarqueRemarque

Pour résoudre les chargements d'assemblys dans le contexte ReflectionOnly, utilisez l'événement AppDomain.ReflectionOnlyAssemblyResolve à la place.

Fonctionnement de l'événement AssemblyResolve

Lorsque vous enregistrez un gestionnaire pour l'événement AssemblyResolve, le gestionnaire est appelé chaque fois que le runtime ne peut pas lier un assembly par nom. Par exemple, l'appel des méthodes suivantes à partir du code utilisateur peut provoquer le déclenchement de l'événement AssemblyResolve :

Opérations effectuées par le gestionnaire d'événements

Le gestionnaire de l'événement AssemblyResolve reçoit le nom complet de l'assembly à charger dans la propriété ResolveEventArgs.Name. Si le gestionnaire ne reconnaît pas le nom de l'assembly, il retourne null (Nothing dans Visual Basic, nullptr dans Visual C++).

Si le gestionnaire reconnaît le nom de l'assembly, il peut charger et retourner un assembly qui satisfait la requête. La liste suivante décrit certains scénarios d'exemple.

  • Si le gestionnaire connaît l'emplacement d'une version de l'assembly, il peut charger ce dernier à l'aide de la méthode Assembly.LoadFrom ou Assembly.LoadFile et retourner l'assembly chargé si l'opération a réussi.

  • Si le gestionnaire a accès à une base de données à partir d'assemblys stockés sous forme de tableaux d'octets, il peut charger un tableau d'octets à l'aide de l'une des surcharges de méthode Assembly.Load qui prennent un tableau d'octets.

  • Le gestionnaire peut générer un assembly dynamique et le retourner.

RemarqueRemarque

Le gestionnaire doit charger l'assembly dans le contexte LoadFrom, dans le contexte de chargement, ou sans contexte.Si le gestionnaire charge l'assembly dans le contexte ReflectionOnly en utilisant la méthode Assembly.ReflectionOnlyLoad ou Assembly.ReflectionOnlyLoadFrom, la tentative de chargement qui a déclenché l'événement AssemblyResolve échoue.

Il est de la responsabilité du gestionnaire d'événements de retourner un assembly approprié. Le gestionnaire peut analyser le nom complet de l'assembly demandé en passant la valeur de propriété ResolveEventArgs.Name au constructeur AssemblyName(String). En commençant par .NET Framework version 4, le gestionnaire peut utiliser la propriété ResolveEventArgs.RequestingAssembly pour déterminer si la requête actuelle est une dépendance d'un autre assembly. Ces informations peuvent aider à identifier un assembly qui satisfera la dépendance.

Le gestionnaire d'événements peut retourner une version de l'assembly différente de celle qui a été demandée.

Dans la plupart des cas, l'assembly qui est retourné par le gestionnaire apparaît dans le contexte de chargement, indépendamment du contexte dans lequel le gestionnaire le charge. Par exemple, si le gestionnaire utilise la méthode Assembly.LoadFrom pour charger un assembly dans le contexte LoadFrom, l'assembly apparaît dans le contexte de chargement lorsque le gestionnaire le retourne. Toutefois, dans le cas suivant, l'assembly apparaît sans contexte lorsque le gestionnaire le retourne :

Pour plus d'informations sur les contextes, consultez la surcharge de la méthode Assembly.LoadFrom(String).

Plusieurs versions du même assembly peuvent être chargées dans le même domaine d'application. Cette pratique n'est pas recommandée, car elle peut entraîner des problèmes d'assignation de type. Consultez Meilleures pratiques pour le chargement d'assembly.

Ce que le gestionnaire d'événements ne doit pas faire

La règle principale pour gérer l'événement AssemblyResolve est que vous ne devez pas essayer de retourner un assembly que vous ne reconnaissez pas. Lorsque vous écrivez le gestionnaire, vous devez savoir quels assemblys peuvent provoquer le déclenchement de l'événement. Votre gestionnaire doit retourner null pour d'autres assemblys.

Remarque importanteImportant

En commençant par .NET Framework 4, l'événement AssemblyResolve est déclenché pour les assemblys satellites.Cette modification affecte un gestionnaire d'événements qui a été écrit pour une version antérieure du .NET Framework, si le gestionnaire tente de résoudre toutes les requêtes de chargement d'assembly.Les gestionnaires d'événements qui ignorent les assemblys qu'ils ne reconnaissent pas ne sont pas affectés par cette modification : Ils retournent la valeur null et les mécanismes de secours normaux sont suivis.

Lors du chargement d'un assembly, le gestionnaire d'événements ne doit pas utiliser les surcharges de méthode AppDomain.Load ou Assembly.Load qui peuvent provoquer le déclenchement de l'événement AssemblyResolve de manière récursive, car cela peut provoquer un dépassement de capacité de la pile. (Consultez la liste fournie plus haut dans cette rubrique.) Cela se produit même si vous fournissez la gestion des exceptions pour la demande de chargement, car aucune exception n'est levée jusqu'à ce que tous les gestionnaires d'événements aient été retournés. Par conséquent, le code suivant provoque un dépassement de capacité de la pile si MyAssembly est introuvable :

Imports System
Imports System.Reflection

Class BadExample

    Shared Sub Main()

        Dim ad As AppDomain = AppDomain.CreateDomain("Test")
        AddHandler ad.AssemblyResolve, AddressOf MyHandler

        Try
            Dim obj As object = ad.CreateInstanceAndUnwrap(
                "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
                "MyType")
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try
    End Sub

    Shared Function MyHandler(ByVal source As Object, _
                              ByVal e As ResolveEventArgs) As Assembly
        Console.WriteLine("Resolving {0}", e.Name)
        Return Assembly.Load(e.Name)
    End Function
End Class

' This example produces output similar to the following:
'
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'...
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'
'Process is terminated due to StackOverflowException.
using System;
using System.Reflection;

class BadExample
{
    static void Main()
    {
        AppDomain ad = AppDomain.CreateDomain("Test");
        ad.AssemblyResolve += MyHandler;

        try
        {
            object obj = ad.CreateInstanceAndUnwrap(
                "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
                "MyType");
        } 
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static Assembly MyHandler(object source, ResolveEventArgs e) 
    {
        Console.WriteLine("Resolving {0}", e.Name);
        return Assembly.Load(e.Name);
    }
} 

/* This example produces output similar to the following:

Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
...
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null

Process is terminated due to StackOverflowException.
 */
using namespace System;
using namespace System::Reflection;

ref class Example
{
internal:
    static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e) 
    {
        Console::WriteLine("Resolving {0}", e->Name);
        return Assembly::Load(e->Name);
    }
};

void main()
{
    AppDomain^ ad = AppDomain::CreateDomain("Test");
    ad->AssemblyResolve += gcnew ResolveEventHandler(&Example::MyHandler);

    try
    {
        Object^ obj = ad->CreateInstanceAndUnwrap(
            "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
            "MyType");
    } 
    catch (Exception^ ex)
    {
        Console::WriteLine(ex->Message);
    }
}

/* This example produces output similar to the following:

Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
...
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null

Process is terminated due to StackOverflowException.
 */

Voir aussi

Autres ressources

Meilleures pratiques pour le chargement d'assembly

Utilisation des domaines d'application