Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
.NET fornisce l'evento AppDomain.AssemblyResolve per le applicazioni che richiedono un maggiore controllo sul caricamento degli assembly. Gestendo questo evento, l'applicazione può caricare un assembly nel contesto di carico dall'esterno dei normali percorsi di probe, selezionare quali di diverse versioni di assembly caricare, generare un assembly dinamico e restituirlo e così via. In questo argomento vengono fornite indicazioni per la gestione dell'evento AssemblyResolve .
Annotazioni
Per la risoluzione dei caricamenti di assembly nel contesto di sola riflessione, usare invece l'evento AppDomain.ReflectionOnlyAssemblyResolve .
Funzionamento dell'evento AssemblyResolve
Quando si registra un gestore per l'evento AssemblyResolve , il gestore viene richiamato ogni volta che il runtime non riesce a eseguire l'associazione a un assembly in base al nome. Ad esempio, la chiamata dei metodi seguenti dal codice utente può causare la generazione dell'evento AssemblyResolve :
Overload AppDomain.Load del metodo o Assembly.Load del cui primo argomento è una stringa che rappresenta il nome visualizzato dell'assembly da caricare, cioè la stringa restituita dalla proprietà Assembly.FullName.
Sovraccarico AppDomain.Load del metodo o sovraccarico Assembly.Load del metodo il cui primo argomento è un AssemblyName oggetto che identifica l'assembly da caricare.
Sovraccarico di un metodo Assembly.LoadWithPartialName.
Un overload del metodo AppDomain.CreateInstance o AppDomain.CreateInstanceAndUnwrap che crea un'istanza di un oggetto in un altro dominio applicativo.
Che cosa fa il gestore eventi
Il gestore per l'evento AssemblyResolve riceve il nome dell'assembly da visualizzare, nella ResolveEventArgs.Name proprietà. Se il gestore non riconosce il nome dell'assembly, restituisce null
(C#), (Visual Basic) Nothing
o nullptr
(Visual C++).
Se il gestore riconosce il nome dell'assembly, può caricare e restituire un assembly che soddisfa la richiesta. L'elenco seguente descrive alcuni scenari di esempio.
Se il gestore conosce la posizione di una versione dell'assembly, può caricare l'assembly usando il Assembly.LoadFrom metodo o Assembly.LoadFile e restituire l'assembly caricato in caso di esito positivo.
Se il gestore ha accesso a un database di assembly archiviati come matrici di byte, può caricare una matrice di byte usando uno degli overload dei metodi Assembly.Load che accetta una matrice di byte.
Il gestore può generare un assembly dinamico e restituirlo.
Annotazioni
Il gestore deve caricare l'assembly nel contesto di caricamento, nel contesto di caricamento o senza contesto. Se il gestore carica l'assembly nel contesto di sola reflection utilizzando il metodo Assembly.ReflectionOnlyLoad o Assembly.ReflectionOnlyLoadFrom, il tentativo di caricamento che ha generato l'evento AssemblyResolve fallisce.
È responsabilità del gestore dell'evento restituire un'assemblea appropriata. Il gestore può analizzare il nome visualizzato dell'assembly richiesto passando il valore della proprietà ResolveEventArgs.Name al costruttore AssemblyName(String). A partire da .NET Framework 4, il gestore può usare la ResolveEventArgs.RequestingAssembly proprietà per determinare se la richiesta corrente è una dipendenza di un altro assembly. Queste informazioni consentono di identificare un assembly che soddisfa la dipendenza.
Il gestore eventi può restituire una versione diversa dell'assembly rispetto alla versione richiesta.
Nella maggior parte dei casi, l'assembly restituito dal gestore viene visualizzato nel contesto di caricamento, indipendentemente dal contesto in cui il gestore lo carica. Ad esempio, se il gestore usa il Assembly.LoadFrom metodo per caricare un assembly nel contesto di caricamento, l'assembly viene visualizzato nel contesto di caricamento quando il gestore lo restituisce. Tuttavia, nel caso seguente l'assembly viene visualizzato senza contesto quando il gestore lo restituisce:
Il gestore carica un assembly senza contesto.
La ResolveEventArgs.RequestingAssembly proprietà non è Null.
L'assembly richiedente, ovvero l'assembly restituito dalla proprietà ResolveEventArgs.RequestingAssembly, è stato caricato senza contesto.
Per informazioni sui contesti, vedere il metodo Assembly.LoadFrom(String) overload.
È possibile caricare più versioni dello stesso assembly nello stesso dominio applicazione. Questa procedura non è consigliata, perché può causare problemi di assegnazione dei tipi. Vedere Procedure consigliate per il caricamento di assembly.
Cosa non deve fare il gestore eventi
La regola principale per la gestione dell'evento AssemblyResolve è che non dovresti provare a restituire un assembly che non riconosci. Quando si scrive il gestore, è necessario sapere quali assembly potrebbero causare la generazione dell'evento. Il gestore deve restituire Null per altri assembly.
Importante
A partire dal .NET Framework 4, l'evento AssemblyResolve viene generato per gli assembly satellite. Questa modifica influisce su un gestore eventi scritto per una versione precedente di .NET Framework, se il gestore tenta di risolvere tutte le richieste di caricamento degli assembly. I gestori eventi che ignorano gli assembly che non riconoscono non sono interessati da questa modifica: restituiscono null
e vengono seguiti i normali meccanismi di fallback.
Durante il caricamento di un assembly, il gestore eventi non deve usare nessuna delle sovraccariche del metodo AppDomain.Load o Assembly.Load che possono far generare l'evento AssemblyResolve ricorsivamente, perché ciò può causare un overflow dello stack. Vedere l'elenco fornito in precedenza in questo argomento. Ciò si verifica anche se si fornisce la gestione delle eccezioni per la richiesta di caricamento, perché non viene generata alcuna eccezione fino a quando non vengono restituiti tutti i gestori eventi. Di conseguenza, il codice seguente genera un overflow dello stack se MyAssembly
non viene trovato:
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.
*/
Modo corretto per gestire AssemblyResolve
Quando si risolvono gli assembly dal gestore eventi AssemblyResolve, verrà lanciata un'eccezione StackOverflowException se il gestore utilizza le chiamate ai metodi Assembly.Load o AppDomain.Load. Invece, usa i metodi LoadFile o LoadFrom, poiché non generano l'evento AssemblyResolve
.
Si immagini che MyAssembly.dll
sia situato vicino all'assembly in esecuzione, in una posizione nota, e possa essere risolto utilizzando Assembly.LoadFile
con il percorso dell'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);
}
}