Assemblybelastingen oplossen
.NET biedt de AppDomain.AssemblyResolve gebeurtenis voor toepassingen waarvoor meer controle nodig is over het laden van assembly's. Door deze gebeurtenis te verwerken, kan uw toepassing een assembly laden in de laadcontext van buiten de normale testpaden, selecteren welke van verschillende assemblyversies moeten worden geladen, een dynamische assembly verzenden en deze retourneren, enzovoort. Dit onderwerp bevat richtlijnen voor het afhandelen van de AssemblyResolve gebeurtenis.
Notitie
Gebruik in plaats daarvan de AppDomain.ReflectionOnlyAssemblyResolve gebeurtenis voor het oplossen van assemblybelastingen in de context die alleen voor reflectie geldt.
Hoe de AssemblyResolve-gebeurtenis werkt
Wanneer u een handler registreert voor de AssemblyResolve gebeurtenis, wordt de handler aangeroepen wanneer de runtime niet op naam aan een assembly kan worden gebonden. Als u bijvoorbeeld de volgende methoden aanroept vanuit gebruikerscode, kan dit ertoe leiden dat de AssemblyResolve gebeurtenis wordt gegenereerd:
Een AppDomain.Load overbelasting van de methode of Assembly.Load overbelasting van de methode waarvan het eerste argument een tekenreeks is die de weergavenaam van de assembly aangeeft die moet worden geladen (dat wil gezegd de tekenreeks die door de Assembly.FullName eigenschap wordt geretourneerd).
Een AppDomain.Load overbelasting van de methode of Assembly.Load overbelasting van de methode waarvan het eerste argument een AssemblyName object is dat de assembly identificeert die moet worden geladen.
Een Assembly.LoadWithPartialName overbelasting van een methode.
Een AppDomain.CreateInstance overbelasting van een of AppDomain.CreateInstanceAndUnwrap methode waarmee een object in een ander toepassingsdomein wordt geïnstitueert.
Wat de gebeurtenis-handler doet
De handler voor de AssemblyResolve gebeurtenis ontvangt de weergavenaam van de assembly die moet worden geladen, in de ResolveEventArgs.Name eigenschap. Als de handler de assemblynaam niet herkent, wordt deze geretourneerd null
(C#), Nothing
(Visual Basic) of nullptr
(Visual C++).
Als de handler de assemblynaam herkent, kan deze een assembly laden en retourneren die voldoet aan de aanvraag. In de volgende lijst worden enkele voorbeeldscenario's beschreven.
Als de handler de locatie van een versie van de assembly kent, kan de assembly worden geladen met behulp van de Assembly.LoadFrom of Assembly.LoadFile methode en kan de geladen assembly worden geretourneerd als dit lukt.
Als de handler toegang heeft tot een database met assembly's die zijn opgeslagen als bytematrices, kan deze een bytematrix laden met behulp van een van de Assembly.Load methode-overbelastingen die een bytematrix nemen.
De handler kan een dynamische assembly genereren en deze retourneren.
Notitie
De handler moet de assembly laden in de load-from-context, in de belastingcontext of zonder context. Als de handler de assembly met behulp van de Assembly.ReflectionOnlyLoad of de Assembly.ReflectionOnlyLoadFrom methode in de context alleen weerspiegeling laadt, mislukt de laadpoging die de AssemblyResolve gebeurtenis heeft veroorzaakt.
Het is de verantwoordelijkheid van de gebeurtenishandler om een geschikte assembly te retourneren. De handler kan de weergavenaam van de aangevraagde assembly parseren door de ResolveEventArgs.Name eigenschapswaarde door te geven aan de AssemblyName(String) constructor. Vanaf .NET Framework 4 kan de handler de ResolveEventArgs.RequestingAssembly eigenschap gebruiken om te bepalen of de huidige aanvraag afhankelijk is van een andere assembly. Deze informatie kan helpen bij het identificeren van een assembly die voldoet aan de afhankelijkheid.
De gebeurtenis-handler kan een andere versie van de assembly retourneren dan de versie die is aangevraagd.
In de meeste gevallen wordt de assembly die wordt geretourneerd door de handler weergegeven in de laadcontext, ongeacht de context waarin de handler deze laadt. Als de handler bijvoorbeeld de Assembly.LoadFrom methode gebruikt om een assembly in de context van de belasting te laden, wordt de assembly weergegeven in de laadcontext wanneer de handler deze retourneert. In het volgende geval wordt de assembly echter zonder context weergegeven wanneer de handler deze retourneert:
De handler laadt een assembly zonder context.
De ResolveEventArgs.RequestingAssembly eigenschap is niet null.
De aangevraagde assembly (dat wil gezegd, de assembly die door de ResolveEventArgs.RequestingAssembly eigenschap wordt geretourneerd) is geladen zonder context.
Zie de overbelasting van de Assembly.LoadFrom(String) methode voor meer informatie over contexten.
Meerdere versies van dezelfde assembly kunnen in hetzelfde toepassingsdomein worden geladen. Deze procedure wordt niet aanbevolen, omdat dit kan leiden tot typetoewijzingsproblemen. Zie aanbevolen procedures voor het laden van assembly's.
Wat de gebeurtenis-handler niet mag doen
De primaire regel voor het afhandelen van de AssemblyResolve gebeurtenis is dat u niet moet proberen een assembly te retourneren die u niet herkent. Wanneer u de handler schrijft, moet u weten welke assembly's ervoor kunnen zorgen dat de gebeurtenis wordt gegenereerd. Uw handler moet null retourneren voor andere assembly's.
Belangrijk
Vanaf .NET Framework 4 wordt de AssemblyResolve gebeurtenis gegenereerd voor satellietassembly's. Deze wijziging is van invloed op een gebeurtenishandler die is geschreven voor een eerdere versie van .NET Framework, als de handler alle aanvragen voor assemblybelastingen probeert op te lossen. Gebeurtenis-handlers die assembly's negeren die ze niet herkennen, worden niet beïnvloed door deze wijziging: ze retourneren null
en normale terugvalmechanismen worden gevolgd.
Bij het laden van een assembly mag de gebeurtenis-handler geen van de AppDomain.Load overbelastingen of Assembly.Load methoden gebruiken die ervoor kunnen zorgen dat de AssemblyResolve gebeurtenis recursief wordt gegenereerd, omdat dit kan leiden tot een stack-overloop. (Zie de lijst die eerder in dit onderwerp is opgegeven.) Dit gebeurt zelfs als u uitzonderingsafhandeling voor de laadaanvraag opgeeft, omdat er geen uitzondering wordt gegenereerd totdat alle gebeurtenis-handlers zijn geretourneerd. De volgende code resulteert dus in een stack-overloop als MyAssembly
deze niet wordt gevonden:
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.
*/
De juiste manier om AssemblyResolve te verwerken
Bij het omzetten van assembly's van de AssemblyResolve gebeurtenis-handler wordt er uiteindelijk een StackOverflowException gegenereerd als de handler gebruikmaakt van de Assembly.Load aanroepen of AppDomain.Load methode. LoadFile Gebruik of LoadFrom methoden, omdat ze de AssemblyResolve
gebeurtenis niet genereren.
Stel dat deze MyAssembly.dll
zich in de buurt van de uitvoerassembly bevindt, op een bekende locatie, kan worden omgezet met behulp van Assembly.LoadFile
het pad naar de 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);
}
}