Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Platforma .NET udostępnia zdarzenie AppDomain.AssemblyResolve dla aplikacji, które wymagają większej kontroli nad ładowaniem zestawów. Dzięki obsłudze tego zdarzenia aplikacja może załadować zestaw do kontekstu ładowania spoza normalnych ścieżek sondowania, wybrać kilka wersji zestawu do załadowania, emitować zestaw dynamiczny i zwracać go itd. Ten temat zawiera wskazówki dotyczące obsługi zdarzenia AssemblyResolve.
Uwaga / Notatka
W przypadku rozpoznawania obciążeń zestawów w kontekście tylko odbicia należy użyć AppDomain.ReflectionOnlyAssemblyResolve zdarzenia.
Jak działa zdarzenie AssemblyResolve
Podczas rejestrowania obsługi dla zdarzenia AssemblyResolve, program obsługi jest uruchamiany za każdym razem, gdy środowisko uruchomieniowe nie może powiązać się z zestawem po nazwie. Na przykład wywołanie z kodu użytkownika następujących metod może spowodować wystąpienie zdarzenia AssemblyResolve:
AppDomain.Load Przeciążenie metody lub Assembly.Load przeciążenie metody, którego pierwszy argument jest ciągiem reprezentującym nazwę wyświetlaną zestawu do załadowania (czyli ciąg zwracany przez Assembly.FullName właściwość).
AppDomain.Load Przeciążenie metody lub Assembly.Load przeciążenie metody, którego pierwszy argument jest obiektem AssemblyName identyfikującym zestaw, który ma zostać załadowany.
Przeciążenie metody Assembly.LoadWithPartialName.
Przeciążenie metody AppDomain.CreateInstance lub AppDomain.CreateInstanceAndUnwrap, które instancjuje obiekt w innej domenie aplikacji.
Co robi obsługa zdarzeń
Procedura obsługi zdarzenia AssemblyResolve odbiera nazwę wyświetlaną zestawu do załadowania we właściwości ResolveEventArgs.Name. Jeśli program obsługi nie rozpoznaje nazwy zestawu, zwraca wartość null
(C#), Nothing
(Visual Basic) lub nullptr
(Visual C++).
Jeśli program obsługi rozpoznaje nazwę zestawu, może załadować i zwrócić zestaw spełniający żądanie. Poniższa lista zawiera opis niektórych przykładowych scenariuszy.
Jeśli program obsługi zna lokalizację wersji zestawu, może załadować zestaw, używając metody Assembly.LoadFrom lub Assembly.LoadFile, i może zwrócić załadowany zestaw, jeśli się powiedzie.
Jeśli obsługujący ma dostęp do bazy danych zestawów przechowywanych jako tablice bajtów, może załadować tablicę bajtów, korzystając z jednego z przeciążeń metody Assembly.Load, które odbierają tablicę bajtów.
Program obsługi może wygenerować zestaw dynamiczny i zwrócić go.
Uwaga / Notatka
Program obsługi musi załadować zestaw do kontekstu "load-from", do kontekstu ładowania lub bez kontekstu. Jeśli program obsługi ładuje zestaw do kontekstu tylko-do-odbicia, używając metody Assembly.ReflectionOnlyLoad lub Assembly.ReflectionOnlyLoadFrom, próba załadowania, która wywołała zdarzenie AssemblyResolve, zakończy się niepowodzeniem.
Jest to odpowiedzialność obsługującego zdarzenia za zwrócenie odpowiedniej kompilacji. Obsługiwarka może przeanalizować nazwę wyświetlaną żądanego zestawu, przekazując wartość właściwości ResolveEventArgs.Name do konstruktora AssemblyName(String). Począwszy od .NET Framework 4, program obsługi może użyć właściwości ResolveEventArgs.RequestingAssembly, aby określić, czy bieżące żądanie jest zależnością innego zestawu. Te informacje mogą pomóc w zidentyfikowaniu zestawu, który spełni zależność.
Program obsługi zdarzeń może zwrócić inną wersję zestawu niż żądana wersja.
W większości przypadków zestaw zwracany przez program obsługi pojawia się w kontekście ładowania, niezależnie od kontekstu, do którego program obsługi go ładuje. Na przykład, jeśli obsługujący używa metody Assembly.LoadFrom w celu załadowania zestawu do kontekstu z ładowania, zestaw pojawi się w kontekście ładowania, gdy obsługujący go zwróci. Jednak w następującym przypadku zestaw jest wyświetlany bez kontekstu, gdy program obsługi zwróci go:
Obsługiwacz ładuje zestaw bez kontekstu.
Właściwość ResolveEventArgs.RequestingAssembly nie ma wartości null.
Zestaw żądający (czyli zestaw zwracany przez ResolveEventArgs.RequestingAssembly właściwość) został załadowany bez kontekstu.
Aby uzyskać informacje o kontekstach, zobacz przeciążenie metody Assembly.LoadFrom(String).
Wiele wersji tego samego zestawu można załadować do tej samej domeny aplikacji. Ta praktyka nie jest zalecana, ponieważ może prowadzić do problemów z przypisywaniem typów. Zobacz Najlepsze rozwiązania dotyczące ładowania zestawów.
Co program obsługi zdarzeń nie powinien wykonywać
Podstawową regułą obsługi AssemblyResolve zdarzenia jest to, że nie należy próbować zwracać zestawu, którego nie rozpoznajesz. Podczas pisania programu obsługi należy wiedzieć, które zestawy mogą powodować wywoływanie zdarzenia. Procedura obsługi powinna zwracać wartość null dla innych zestawów.
Ważne
Począwszy od .NET Framework 4, zdarzenie AssemblyResolve jest generowane dla zestawów satelitarnych. Ta zmiana dotyczy procedury obsługi zdarzeń napisanej dla starszej wersji .NET Framework, jeśli procedura próbuje rozwiązać wszystkie żądania ładowania zestawów. Programy obsługi zdarzeń, które nie rozpoznają i ignorują nieznane im zestawy, nie mają wpływu na tę zmianę: zwracają null
, a normalne mechanizmy rezerwowe są przestrzegane.
Podczas ładowania zestawu program obsługi zdarzeń nie może używać żadnego z przeciążeń metody AppDomain.Load lub Assembly.Load , które mogą powodować rekursywne wywoływanie zdarzenia AssemblyResolve, ponieważ może to prowadzić do przepełnienia stosu. (Zobacz listę podaną wcześniej w tym temacie). Dzieje się tak nawet w przypadku podania obsługi wyjątków dla żądania ładowania, ponieważ nie jest zgłaszany żaden wyjątek do momentu zwrócenia wszystkich procedur obsługi zdarzeń. W związku z tym poniższy kod powoduje przepełnienie stosu, jeśli MyAssembly
nie zostanie znalezione:
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.
*/
Prawidłowy sposób obsługi rozwiązania AssemblyResolve
Podczas rozpoznawania zestawów z AssemblyResolve programu obsługi zdarzeń w końcu zgłosi StackOverflowException, jeśli program obsługi wywołuje metodę Assembly.Load lub AppDomain.Load. Zamiast tego należy użyć metod LoadFile lub LoadFrom, ponieważ nie zgłaszają zdarzenia AssemblyResolve
.
Wyobraź sobie, że MyAssembly.dll
znajduje się w pobliżu wykonywanego zestawu, w znanej lokalizacji, można go rozpoznać przy użyciu Assembly.LoadFile
podanej ścieżki do zestawu.
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);
}
}