Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A .NET biztosítja a AppDomain.AssemblyResolve eseményt azoknak az alkalmazásoknak, amelyek nagyobb ellenőrzést igényelnek a könyvtárak betöltési folyamatán. Az esemény kezelésével az alkalmazás betölthet egy szerelvényt a terhelési környezetbe a normál próbaútvonalakon kívülről, kiválaszthatja, hogy melyik szerelvényverziót szeretné betölteni, dinamikus szerelvényt bocsát ki és adja vissza stb. Ez a témakör útmutatást nyújt az AssemblyResolve esemény kezeléséhez.
Megjegyzés:
A csak tükrözési kontextusban lévő összeállítási betöltések feloldásához használja helyette az AppDomain.ReflectionOnlyAssemblyResolve eseményt.
A AssemblyResolve esemény működése
Ha kezelőt regisztrál az AssemblyResolve eseményhez, a rendszer meghívja a kezelőt, ha a futtatókörnyezet nem tud név alapján csatlakozni egy assemblyhez. Ha például a következő metódusokat hívja meg a felhasználói kódból, akkor ez az AssemblyResolve eseményt okozhatja.
Olyan AppDomain.Load metódus túlterhelése vagy Assembly.Load metódusterhelése, amelynek első argumentuma egy olyan karakterlánc, amely a betöltendő szerelvény megjelenítendő nevét (azaz a Assembly.FullName tulajdonság által visszaadott sztringet) jelöli.
Olyan AppDomain.Load metódus túlterhelése vagy Assembly.Load metódus túlterhelése, amelynek első argumentuma egy AssemblyName olyan objektum, amely azonosítja a betöltendő assembly-t.
Egy Assembly.LoadWithPartialName metódus túlterhelése.
Egy AppDomain.CreateInstance vagy AppDomain.CreateInstanceAndUnwrap metódus túlterhelése, amely egy objektumot példányosítja egy másik alkalmazástartományban.
Az eseménykezelő feladata
A AssemblyResolve esemény kezelésekor megjelenik a betöltendő szerelvény neve a ResolveEventArgs.Name tulajdonságban. Ha a kezelő nem ismeri fel a szerelvény nevét, null értéket ad vissza (C#), Nothing értéket ad vissza (Visual Basic), vagy nullptr értéket ad vissza (Visual C++).
Ha a kezelő felismeri a szerelvény nevét, betölthet és visszaadhat egy olyan szerelvényt, amely megfelel a kérésnek. Az alábbi lista néhány példaforgatókönyvet ismertet.
Ha a kezelő ismeri a szerelvény egy verziójának helyszínét, betöltheti a szerelvényt a Assembly.LoadFrom módszer vagy Assembly.LoadFile használatával, és sikeres esetben visszaadhatja a betöltött szerelvényt.
Ha a kezelő hozzáfér egy adatbázishoz, amely bájttömbként tárolt szerelvényeket tartalmaz, akkor betölthet egy bájttömböt az egyik Assembly.Load metódus overload segítségével, amely bájttömböt vesz át.
A kezelő létrehozhat egy dinamikus szerelvényt, és visszaadhatja azt.
Megjegyzés:
A kezelőnek be kell töltenie a szerelvényt a "load-from" környezetbe, a terhelési környezetbe, vagy kontextus nélkül. Ha a kezelő betölti az assembly-t a csak tükrözés környezetébe a Assembly.ReflectionOnlyLoad vagy a Assembly.ReflectionOnlyLoadFrom metódus használatával, a AssemblyResolve eseményt kiváltó betöltési kísérlet meghiúsul.
Az eseménykezelő felelőssége, hogy megfelelő szerelvényt adjon vissza. A kezelő elemezheti a kért szerelvény megjelenítendő nevét úgy, hogy átadja a ResolveEventArgs.Name tulajdonság értékét a AssemblyName(String) konstruktornak. A .NET-keretrendszer 4-től kezdve a kezelő a ResolveEventArgs.RequestingAssembly tulajdonsággal meghatározhatja, hogy az aktuális kérés egy másik szerelvény függősége-e. Ezek az információk segíthetnek azonosítani egy olyan szerelvényt, amely megfelel a függőségnek.
Az eseménykezelő a kért verziótól eltérő szerelvényverziót adhat vissza.
A legtöbb esetben a kezelő által visszaadott szerelvény megjelenik a betöltési környezetben, függetlenül attól, hogy milyen környezetbe tölti azt be a kezelő. Ha például a kezelő a Assembly.LoadFrom metódus használatával tölt be egy szerelvényt a terhelési környezetbe, akkor a szerelvény akkor jelenik meg a terhelési környezetben, amikor a kezelő visszaadja azt. A következő esetben azonban az összeállítás kontextus nélkül jelenik meg, amikor a feldolgozó visszaadja.
A kezelő környezet nélkül tölt be egy assembly-t.
A ResolveEventArgs.RequestingAssembly tulajdonság nem null értékű.
Az igénylő szerelvény (azaz a ResolveEventArgs.RequestingAssembly tulajdonság által visszaadott szerelvény) környezet nélkül lett betöltve.
A környezetekkel kapcsolatos információkért lásd a metódustúlterhelést Assembly.LoadFrom(String).
Ugyanazon szerelvény több verziója is betölthető ugyanabba az alkalmazástartományba. Ez a gyakorlat nem ajánlott, mert típus-hozzárendelési problémákhoz vezethet. Tekintse meg az szerelvénybetöltés ajánlott eljárásait.
Mit ne tegyen az eseménykezelő?
Az AssemblyResolve esemény kezelésének elsődleges szabálya, hogy ne kíséreljen meg olyan szerelvényt visszaadni, amelyet nem ismer fel. A kezelő írásakor tudnia kell, hogy mely assemblyk okozhatják az eseményt. A kezelőnek null értéket kell visszaadnia más szerelvényekhez.
Fontos
A .NET-keretrendszer 4-től kezdődően az AssemblyResolve esemény aktiválódik a műholdas szerelvények esetében. Ez a módosítás a .NET-keretrendszer egy korábbi verziójához írt eseménykezelőt érinti, ha a kezelő megpróbálja feloldani az összes szerelvénybetöltési kérést. A nem felismert assemblyeket figyelmen kívül hagyó eseménykezelőket ez a változás nem érinti: Visszaadják null, és a normál visszalépési mechanizmusokat követik.
Szerelvény betöltésekor az eseménykezelő nem használhatja a AppDomain.Load vagy Assembly.Load metódus túlterheléseit, amelyek az AssemblyResolve esemény rekurzív kiváltását okozhatják, mert ez verem túlcsordulást eredményezhet. (Lásd a témakör korábbi részében megadott listát.) Ez akkor is megtörténik, ha kivételkezelést biztosít a terhelési kérelemhez, mert a rendszer addig nem ad kivételt, amíg az összes eseménykezelő vissza nem tér. Így a következő kód veremtúlcsordulást eredményez, ha MyAssembly nem található.
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.
*/
A AssemblyResolve kezelésének helyes módja
Amikor az eseménykezelőtől feloldja a AssemblyResolve szerelvényeket, a rendszer végül eldobja StackOverflowException a szerelvényt, ha a kezelő a Assembly.Load metódushívásokat AppDomain.Load használja. Ehelyett használjon LoadFile vagy LoadFrom metódusokat, mivel nem emelik ki az eseményt AssemblyResolve .
Tegyük fel, hogy MyAssembly.dll az ismert helyen, a végrehajtó szerelvény közelében található, és a hely útmutatásával Assembly.LoadFile alapján feloldható.
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);
}
}