Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
.NET menyediakan event AppDomain.AssemblyResolve untuk aplikasi yang memerlukan kontrol yang lebih besar atas pemanggilan assembly. Dengan menangani peristiwa ini, aplikasi Anda dapat memuat rakitan ke dalam konteks beban dari luar jalur pemeriksaan normal, memilih versi rakitan mana yang akan dimuat, memancarkan rakitan dinamis dan mengembalikannya, dan sebagainya. Topik ini memberikan panduan untuk menangani kejadian AssemblyResolve.
Nota
Untuk menyelesaikan muatan assembly dalam konteks hanya-refleksi, gunakan event AppDomain.ReflectionOnlyAssemblyResolve sebagai gantinya.
Cara kerja event AssemblyResolve
Saat Anda mendaftarkan handler untuk AssemblyResolve event, handler akan dipanggil setiap kali runtime gagal menghubungkan ke assembly berdasarkan nama. Misalnya, memanggil metode berikut dalam kode pengguna dapat menyebabkan acara AssemblyResolve dinaikkan:
Metode kelebihan beban AppDomain.Load atau kelebihan beban metode Assembly.Load yang argumen pertamanya adalah string yang mewakili nama tampilan rakitan yang akan dimuat (artinya, string yang dikembalikan oleh properti Assembly.FullName).
Metode AppDomain.Load overload atau metode Assembly.Load overload yang argumen pertamanya adalah objek AssemblyName yang mengidentifikasi assembly untuk dimuat.
Metode Assembly.LoadWithPartialName kelebihan beban.
Metode overload AppDomain.CreateInstance atau AppDomain.CreateInstanceAndUnwrap yang menginstansiasi objek di domain aplikasi lain.
Apa yang dilakukan pengendali acara
Penangan untuk kejadian AssemblyResolve menerima nama tampilan dari rakitan yang akan dimuat, di properti ResolveEventArgs.Name. Jika handler tidak mengenali nama rakitan, handler akan mengembalikan null (C#), Nothing (Visual Basic), atau nullptr (Visual C++).
Jika handler mengenali nama assembly, handler dapat memuat dan mengembalikan assembly yang memenuhi permintaan. Daftar berikut ini menjelaskan beberapa skenario sampel.
Jika handler mengetahui lokasi versi assembly, handler dapat memuat assembly dengan menggunakan metode Assembly.LoadFrom atau Assembly.LoadFile, dan dapat mengembalikan assembly yang dimuat jika berhasil.
Jika handler memiliki akses ke database assembly yang disimpan sebagai array byte, handler dapat memuat array byte dengan menggunakan salah satu metode overload Assembly.Load yang menerima array byte.
Handler dapat menghasilkan rakitan dinamis dan mengembalikannya.
Nota
Handler harus memuat rakitan ke dalam konteks load-from, ke dalam konteks beban, atau tanpa konteks. Jika handler memuat assembly ke dalam konteks khusus refleksi dengan menggunakan metode Assembly.ReflectionOnlyLoad atau Assembly.ReflectionOnlyLoadFrom, upaya pemuatan yang menaikkan event AssemblyResolve gagal.
Merupakan tanggung jawab penangan acara untuk mengembalikan assembli yang sesuai. Handler dapat mengurai nama tampilan rakitan yang diminta dengan meneruskan ResolveEventArgs.Name nilai properti ke AssemblyName(String) konstruktor. Dimulai dengan .NET Framework 4, handler dapat menggunakan elemen ResolveEventArgs.RequestingAssembly untuk menentukan apakah permintaan saat ini adalah dependensi dari assembly lain. Informasi ini dapat membantu mengidentifikasi komponen yang akan memenuhi ketergantungan.
Pengendali acara dapat mengembalikan versi perakitan yang berbeda dari versi yang diminta.
Dalam sebagian besar kasus, rakitan yang dikembalikan oleh pemuat muncul dalam konteks muatan, tidak peduli konteks di mana pemuat tersebut memuat rakitan tersebut. Misalnya, jika handler menggunakan Assembly.LoadFrom metode untuk memuat assembly ke dalam konteks load-from, assembly muncul dalam konteks beban saat handler mengembalikannya. Namun, dalam kasus berikut rakitan muncul tanpa konteks ketika handler mengembalikannya:
Handler memuat rakitan tanpa konteks.
Properti ResolveEventArgs.RequestingAssembly tidak bernilai null.
Rakitan yang meminta (yaitu, rakitan yang dikembalikan oleh properti ResolveEventArgs.RequestingAssembly) telah dimuat tanpa konteks.
Untuk informasi tentang konteks, lihat Assembly.LoadFrom(String) metode kelebihan beban.
Beberapa versi rakitan yang sama dapat dimuat ke domain aplikasi yang sama. Praktik ini tidak disarankan, karena dapat menyebabkan masalah penetapan jenis. Lihat Praktik terbaik untuk pemuatan perakitan.
Apa yang tidak boleh dilakukan oleh penanganan aktivitas
Aturan utama untuk menangani peristiwa AssemblyResolve adalah Anda tidak boleh mencoba mengembalikan assembly yang tidak Anda kenali. Ketika Anda menulis handler, Anda harus tahu assembly mana yang dapat memicu event. Handler Anda harus mengembalikan null untuk rakitan lain.
Penting
Dimulai dengan .NET Framework 4, event AssemblyResolve diaktifkan untuk assembly satelit. Perubahan ini memengaruhi penanganan aktivitas yang ditulis untuk versi .NET Framework yang lebih lama, jika handler mencoba menyelesaikan semua permintaan pemuatan rakitan. Penangan kejadian yang mengabaikan assembly yang tidak dikenali tidak terpengaruh oleh perubahan ini: Mereka mengembalikan null, dan mekanisme fallback normal diikuti.
Saat memuat rakitan, penangan kejadian tidak boleh menggunakan fungsi overloading AppDomain.Load atau Assembly.Load yang dapat menyebabkan AssemblyResolve kejadian dipicu secara rekursif, karena ini dapat menyebabkan kelebihan tumpukan. (Lihat daftar yang disediakan sebelumnya dalam topik ini.) Ini terjadi bahkan jika Anda memberikan penanganan pengecualian untuk permintaan pemuatan, karena tidak ada pengecualian yang dilemparkan sampai semua penangan peristiwa telah kembali. Dengan demikian, kode berikut menghasilkan luapan tumpukan jika MyAssembly tidak ditemukan:
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.
*/
Cara yang benar untuk menangani AssemblyResolve
Saat menyelesaikan rakitan dari penangan kejadian AssemblyResolve, sebuah StackOverflowException pada akhirnya akan dilemparkan jika penangan menggunakan panggilan metode Assembly.Load atau AppDomain.Load. Sebagai gantinya, gunakan metode LoadFile atau LoadFrom, karena tidak memicu event AssemblyResolve.
Bayangkan bahwa MyAssembly.dll terletak di dekat assembly yang dieksekusi, di lokasi yang diketahui, dan dapat diselesaikan menggunakan Assembly.LoadFile dengan memberikan jalur ke assembly tersebut.
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);
}
}