Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
.NET poskytuje AppDomain.AssemblyResolve událost pro aplikace, které vyžadují větší kontrolu nad načítáním sestavení. Díky zpracování této události může vaše aplikace načíst sestavení do kontextu zatížení mimo normální cesty sondy, vybrat, která z několika verzí sestavení se má načíst, vygenerovat dynamické sestavení a vrátit ho atd. Toto téma obsahuje pokyny pro zpracování AssemblyResolve události.
Poznámka:
Pro vyřešení načítání sestavení v reflexním kontextu použijte místo toho událost AppDomain.ReflectionOnlyAssemblyResolve.
Jak funguje událost AssemblyResolve
Při registraci obslužné rutiny pro AssemblyResolve událost se obslužná rutina vyvolá pokaždé, když se modulu runtime nezdaří navázat spojení se sestavením jeho názvem. Voláním následujících metod z uživatelského kódu může například dojít k vyvolání události AssemblyResolve:
Přetížení AppDomain.Load metody nebo Assembly.Load přetížení metody, jehož prvním argumentem je řetězec, který představuje zobrazovaný název sestavení k načtení (to znamená řetězec vrácený Assembly.FullName vlastností).
Přetížení metody AppDomain.Load nebo Assembly.Load, jehož prvním argumentem je objekt AssemblyName, který identifikuje sestavení k načtení.
Přetížení Assembly.LoadWithPartialName metody.
Metoda přetížení AppDomain.CreateInstance nebo AppDomain.CreateInstanceAndUnwrap, která vytvoří instanci objektu v jiné aplikační doméně.
Co dělá obslužná rutina události
Obslužná rutina události AssemblyResolve obdrží zobrazovaný název sestavení, který se má načíst, ve ResolveEventArgs.Name vlastnosti. Pokud obslužná rutina nerozpozná název sestavení, vrátí null (C#), Nothing (Visual Basic) nebo nullptr (Visual C++).
Pokud obslužná rutina rozpozná název sestavení, může načíst a vrátit sestavení, které splňuje požadavek. Následující seznam popisuje některé ukázkové scénáře.
Pokud obslužná rutina zná umístění verze sestavení, může sestavení načíst pomocí Assembly.LoadFrom metody nebo Assembly.LoadFile a může v případě úspěchu vrátit načtené sestavení.
Pokud má obslužná rutina přístup k databázi sestavení uložených jako pole bajtů, může načíst pole bajtů pomocí jednoho z Assembly.Load přetížení metody, které přebírají bajtové pole.
Obslužná rutina může vygenerovat dynamické sestavení a vrátit ho.
Poznámka:
Obslužná rutina musí načíst sestavení do kontextu načítání z kontextu, do načítacího kontextu nebo bez kontextu. Pokud obslužná rutina načte sestavení do kontextu pouze pro reflexi pomocí metody Assembly.ReflectionOnlyLoad nebo metody Assembly.ReflectionOnlyLoadFrom, pokus o načtení, který vyvolal událost AssemblyResolve, selže.
Je zodpovědností obslužného procesoru události vrátit vhodné sestavení. Obslužná rutina může analyzovat název sestavení zobrazeného na žádost předáním hodnoty vlastnosti ResolveEventArgs.Name do konstruktoru AssemblyName(String). Počínaje rozhraním .NET Framework 4 může obslužná rutina použít ResolveEventArgs.RequestingAssembly vlastnost k určení, zda aktuální požadavek je závislost jiného sestavení. Tyto informace můžou pomoct identifikovat sestavení, které bude vyhovovat závislosti.
Obslužná rutina události může vrátit jinou verzi sestavení, než je požadovaná verze.
Ve většině případů se sestavení vrácené obslužnou rutinou zobrazí v kontextu načítání, bez ohledu na to, do jakého kontextu ho obslužná rutina načte. Pokud například obslužná rutina používá metodu Assembly.LoadFrom k načtení sestavení do kontextu načtení z kontextu, sestavení se zobrazí v kontextu načtení, když obslužná rutina vrátí. Nicméně v následujícím případě se sestavení zobrazí bez kontextu, když jej obslužná rutina vrátí.
Obslužná rutina načte sestavení bez kontextu.
Vlastnost ResolveEventArgs.RequestingAssembly není null.
Žádající sestavení (tj. sestavení, které je vráceno vlastností ResolveEventArgs.RequestingAssembly) bylo načteno bez kontextu.
Informace o kontextech naleznete v Assembly.LoadFrom(String) přetížení metody.
Do stejné domény aplikace lze načíst více verzí stejného sestavení. Tento postup se nedoporučuje, protože může vést k problémům s přiřazením typu. Podívejte se na osvědčené postupy pro načítání sestavení.
Co by obslužná rutina události neměla vykonávat
Primárním pravidlem pro zpracování AssemblyResolve události je, že byste se neměli pokoušet vrátit sestavení, které nerozpoznáte. Při psaní obslužné rutiny byste měli vědět, která sestavení mohou způsobit vyvolání události. Vaše obslužná rutina by měla vracet hodnotu null pro ostatní sestavení.
Důležité
Počínaje rozhraním .NET Framework 4 AssemblyResolve je událost vyvolána pro satelitní sestavení. Tato změna má vliv na obslužnou rutinu události napsanou pro starší verzi rozhraní .NET Framework, pokud se obslužná rutina pokusí vyřešit všechny požadavky na načtení sestavení. Obslužné rutiny událostí, které ignorují sestavení, která nerozpoznávají, nejsou touto změnou ovlivněny: Vrátí nulla následují normální záložní mechanismy.
Při načítání sestavení nesmí obslužná rutina události používat žádné přetížení metod AppDomain.Load nebo Assembly.Load , které mohou způsobit, že událost AssemblyResolve bude vyvolána rekurzivně, protože to může vést k přetečení zásobníku. (Viz seznam uvedený výše v tomto tématu.) K tomu dochází, i když poskytujete zpracování výjimek pro požadavek na načtení, protože není vyvolána žádná výjimka, dokud všechny obslužné rutiny událostí nevrátí. Následující kód tedy vede k přetečení zásobníku, pokud MyAssembly ho nenajdete:
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.
*/
Správný způsob zpracování AssemblyResolve
Při řešení sestavení z obslužné rutiny AssemblyResolve bude nakonec vyvolána StackOverflowException výjimka, pokud obslužná rutina používá volání metod Assembly.Load nebo AppDomain.Load. Místo toho použijte LoadFile nebo LoadFrom metody, protože nevyvolají AssemblyResolve událost.
Představte si, že MyAssembly.dll se nachází v blízkosti sestavení, které je prováděno ve známém umístění, a lze jej určit pomocí Assembly.LoadFile při dané cestě k sestavení.
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);
}
}