解析組件負載

.NET 提供需要更能控制組件載入的應用程式 AppDomain.AssemblyResolve 事件。 藉由處理這個事件,您的應用程式可以將組件從一般探查路徑外部載入到載入內容、選取要載入的數個組件版本、發出動態組件,並傳回它,以此類推。 本主題提供處理 AssemblyResolve 事件的指引。

注意

若要解析僅限反映內容中的組件載入,請改為使用 AppDomain.ReflectionOnlyAssemblyResolve 事件。

AssemblyResolve 事件運作方式

當您註冊 AssemblyResolve 事件的處理常式時,只要執行階段無法依名稱繫結至組件,就會叫用處理常式。 例如,從使用者程式碼呼叫下列方法可能會引發 AssemblyResolve 事件:

事件處理常式執行的作業

AssemblyResolve 事件的處理常式會在 ResolveEventArgs.Name 屬性中接收要載入之組件的顯示名稱。 如果處理常式無法辨識組件名稱,則會傳回 null (C#)、Nothing (Visual Basic) 或 nullptr (Visual C++)。

如果處理常式可辨識組件名稱,則可以載入並傳回滿足要求的組件。 下列清單描述一些範例情節。

  • 如果處理常式知道組件版本的位置,則可以使用 Assembly.LoadFromAssembly.LoadFile 方法來載入組件,而且可以在成功時傳回載入的組件。

  • 如果處理常式可以存取儲存為位元組陣列之組件的資料庫,則可以使用其中一個接受位元組陣列的 Assembly.Load 方法多載來載入位元組陣列。

  • 處理常式可以產生動態組件,並將它傳回。

注意

處理常式必須將組件載入到載入來源內容、載入內容,或不使用內容。 如果處理常式使用 Assembly.ReflectionOnlyLoadAssembly.ReflectionOnlyLoadFrom 方法將組件載入僅限反映的內容,則引發 AssemblyResolve 事件的載入嘗試會失敗。

它負責讓事件處理常式傳回適當的組件。 處理常式可以將 ResolveEventArgs.Name 屬性值傳遞給 AssemblyName(String) 建構函式,以剖析所要求組件的顯示名稱。 從 .NET Framework 4 開始,處理常式可以使用 ResolveEventArgs.RequestingAssembly 屬性來判斷目前的要求是否為另一個組件的相依性。 這項資訊可以協助識別將符合相依性的組件。

事件處理常式可以傳回的組件版本與所要求的版本不同。

在大部分情況下,處理常式所傳回的組件會出現在載入內容中,不論處理常式將它載入的內容為何。 例如,如果處理常式使用 Assembly.LoadFrom 方法將組件載入到載入來源內容,則在處理常式傳回載入內容時,組件就會出現在載入內容中。 不過,在下列情況下,組件在處理常式傳回它時沒有內容:

如需內容的資訊,請參閱 Assembly.LoadFrom(String) 方法多載。

相同組件的多個版本可以載入相同的應用程式定義域。 不建議這種做法,因為它可能會導致類型指派問題。 請參閱組件載入的最佳做法

事件處理常式不應該執行的作業

處理 AssemblyResolve 事件的主要規則是您不應該嘗試傳回無法辨識的組件。 當您撰寫處理常式時,應該知道哪些組件可能會引發此事件。 針對其他組件,您的處理常式應該傳回 Null。

重要

從 .NET Framework 4 開始,會針對附屬組件引發 AssemblyResolve 事件。 如果處理常式嘗試解析所有組件載入要求,則這項變更會影響針對舊版 .NET Framework 所撰寫的事件處理常式。 略過無法辨識之組件的事件處理常式不會受到這項變更的影響:這些處理常式會傳回 null,並遵循正常後援機制。

載入組件時,事件處理常式不得使用任何 AppDomain.LoadAssembly.Load 方法多載,而此方法多載可以遞迴引發 AssemblyResolve 事件,因為這可能會導致堆疊溢位。 (請參閱本主題稍早所提供的清單)。即使您提供載入要求的例外狀況處理,也會發生這種情況,因為在傳回所有事件處理常式之前不會擲回任何例外狀況。 因此,如果找不到 MyAssembly,則下列程式碼會導致堆疊溢位:

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.
*/

處理 AssemblyResolve 的正確方式

AssemblyResolve 事件處理常式解析組件時,如果處理常式使用 Assembly.LoadAppDomain.Load 方法呼叫,最終會擲回 StackOverflowException。 請改用 LoadFileLoadFrom 方法,因為這些方法不會引發 AssemblyResolve 事件。

假設 MyAssembly.dll 位於執行中的組件附近,且在已知位置,則可以使用 Assembly.LoadFile 組件的路徑加以解析。

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);
    }
}

另請參閱