Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
.NET fournit l’événement AppDomain.AssemblyResolve pour les applications nécessitant un meilleur contrôle sur le chargement des assemblies. En gérant cet événement, votre application peut charger un assembly dans le contexte de chargement en dehors des chemins de détection normaux, sélectionner parmi plusieurs versions d’assembly à charger, émettre un assembly dynamique et le retourner, et ainsi de suite. Cette rubrique fournit des conseils pour gérer l’événement AssemblyResolve .
Remarque
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 inscrivez un gestionnaire pour l’événement AssemblyResolve , le gestionnaire est appelé chaque fois que le runtime ne parvient pas à se lier à un assembly par nom. Par exemple, l’appel des méthodes suivantes à partir du code utilisateur peut déclencher l’événement AssemblyResolve :
surcharge de méthode AppDomain.Load ou Assembly.Load dont le premier argument est une chaîne qui représente le nom complet de l’assembly à charger (autrement dit, la chaîne retournée par la propriété Assembly.FullName) ;
Surcharge de méthode AppDomain.Load ou surcharge de méthode Assembly.Load dont le premier argument est un objet AssemblyName qui identifie l’assemblage à charger.
Surcharge de méthode Assembly.LoadWithPartialName
surcharge de méthode AppDomain.CreateInstance ou AppDomain.CreateInstanceAndUnwrap qui instancie un objet dans un autre domaine d’application.
Ce que fait le gestionnaire d’événements
Le gestionnaire de l’événement AssemblyResolve reçoit le nom d'affichage 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 (C#), (Visual Basic) Nothing ou nullptr (Visual C++).
Si le gestionnaire reconnaît le nom de l’assembly, il peut charger et retourner un assembly qui répond à la demande. La liste suivante décrit certains exemples de scénarios.
Si le gestionnaire connaît l’emplacement d’une version de l’assembly, il peut charger l’assembly en utilisant la méthode Assembly.LoadFrom ou Assembly.LoadFile, et retourner l’assembly chargé en cas de réussite.
Si le gestionnaire a accès à une base de données d'assemblages stockés sous forme de tableaux d’octets, il peut charger un tableau d’octets grâce à une des surcharges de méthode prenant un tableau d’octets.
Le gestionnaire peut générer un assembly dynamique et le retourner.
Remarque
Le gestionnaire doit charger l’assembly dans le contexte de chargement par chemin d’accès, dans le contexte de chargement, ou sans contexte. Si le gestionnaire charge l'assembly dans le contexte de réflexion uniquement en utilisant la méthode Assembly.ReflectionOnlyLoad ou Assembly.ReflectionOnlyLoadFrom, la tentative de chargement qui a déclenché l'événement AssemblyResolve échoue.
Il appartient au gestionnaire d’événements de retourner un assembly approprié. Le gestionnaire peut analyser le nom d'affichage de l'assembly demandé en passant la valeur de propriété ResolveEventArgs.Name au constructeur AssemblyName(String). À compter du .NET Framework 4, le gestionnaire peut utiliser la ResolveEventArgs.RequestingAssembly propriété pour déterminer si la requête actuelle est une dépendance d’un autre assembly. Ces informations peuvent aider à identifier un assemblage qui satisfera la dépendance.
Le gestionnaire d’événements peut retourner une version différente de l’assembly que la version demandée.
Dans la plupart des cas, l’assembly retourné par le gestionnaire apparaît dans le contexte de chargement, quel que soit le contexte dans lequel le gestionnaire le charge. Par exemple, si le gestionnaire utilise la Assembly.LoadFrom méthode pour charger un assembly dans le contexte de chargement, 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 :
Le gestionnaire charge un assembly sans contexte.
La ResolveEventArgs.RequestingAssembly propriété n’est pas null.
L’assemblage demandeur (c'est-à-dire, l'assemblage retourné par la propriété ResolveEventArgs.RequestingAssembly) a été chargé sans contexte.
Pour plus d’informations sur les contextes, consultez la surcharge de 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’affectation de type. Consultez les meilleures pratiques pour le chargement d’assemblage.
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. Quand vous écrivez le gestionnaire, vous devez avoir connaissance des assemblys qui peuvent provoquer le déclenchement de l’événement. Votre gestionnaire doit retourner null pour d’autres assemblies.
Importante
À compter du .NET Framework 4, l’événement AssemblyResolve est déclenché pour les assemblies satellites. Cette modification affecte un gestionnaire d’événements écrit pour une version antérieure du .NET Framework, si le gestionnaire tente de résoudre toutes les demandes de chargement d’assembly. Les gestionnaires d’événements qui ignorent les assemblies qu’ils ne reconnaissent pas ne sont pas affectés par cette modification : ils retournent null, et les mécanismes de secours normaux sont appliqués.
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 lever l’événement AssemblyResolve de façon récursive, car cela peut entraîner un dépassement de pile. (Consultez la liste fournie précédemment 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 tant que tous les gestionnaires d’événements n’ont pas retourné. Le code suivant provoque donc un dépassement de capacité de la pile si MyAssembly est introuvable :
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);
// DO NOT DO THIS: This causes a StackOverflowException
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.
*/
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)
// DO NOT DO THIS: This causes a StackOverflowException
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 namespace System;
using namespace System::Reflection;
ref class Example
{
internal:
static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e)
{
Console::WriteLine("Resolving {0}", e->Name);
// DO NOT DO THIS: This causes a StackOverflowException
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.
*/
La bonne façon de gérer AssemblyResolve
Lors de la résolution des assemblys à partir du gestionnaire d’événements AssemblyResolve, une StackOverflowException sera finalement levée si le gestionnaire utilise les appels de méthode Assembly.Load ou AppDomain.Load. Au lieu de cela, utilisez LoadFile ou utilisez des LoadFrom méthodes, car elles ne déclenchent pas l’événement AssemblyResolve .
Imaginez que MyAssembly.dll se trouve près de l’assembly en cours d’exécution, dans un emplacement connu; il peut être résolu à l’aide de Assembly.LoadFile compte tenu du chemin d’accès à l’assembly.
using System;
using System.IO;
using System.Reflection;
class CorrectExample
{
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);
var path = Path.GetFullPath("../../MyAssembly.dll");
return Assembly.LoadFile(path);
}
}
Imports System.IO
Imports System.Reflection
Class CorrectExample
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)
Dim fullPath = Path.GetFullPath("../../MyAssembly.dll")
Return Assembly.LoadFile(fullPath)
End Function
End Class
using namespace System;
using namespace System::IO;
using namespace System::Reflection;
ref class Example
{
internal:
static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e)
{
Console::WriteLine("Resolving {0}", e->Name);
String^ fullPath = Path::GetFullPath("../../MyAssembly.dll");
return Assembly::LoadFile(fullPath);
}
};
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);
}
}