解析程序集加载Resolve assembly loads

.NET 为对程序集加载需要更强控制的应用程序提供了 AppDomain.AssemblyResolve 事件。.NET provides the AppDomain.AssemblyResolve event for applications that require greater control over assembly loading. 通过处理此事件,应用程序可从常规探测路径外部将程序集加载到加载上下文、从几个程序集版本中选择要加载的版本、发出动态程序集并返回此程序集,等等。By handling this event, your application can load an assembly into the load context from outside the normal probing paths, select which of several assembly versions to load, emit a dynamic assembly and return it, and so on. 本主题指导如何处理 AssemblyResolve 事件。This topic provides guidance for handling the AssemblyResolve event.

备注

若要在仅限反射的上下文中解决程序集加载问题,请改用 AppDomain.ReflectionOnlyAssemblyResolve 事件。For resolving assembly loads in the reflection-only context, use the AppDomain.ReflectionOnlyAssemblyResolve event instead.

AssemblyResolve 事件的工作原理How the AssemblyResolve event works

注册 AssemblyResolve 事件的处理程序后,每当运行时无法按名称绑定到程序集时,此处理程序都将被调用。When you register a handler for the AssemblyResolve event, the handler is invoked whenever the runtime fails to bind to an assembly by name. 例如,从用户代码中调用以下方法可能导致引发 AssemblyResolve 事件:For example, calling the following methods from user code can cause the AssemblyResolve event to be raised:

事件处理程序的功能What the event handler does

AssemblyResolve 事件的处理程序接收要加载的程序集的显示名称(在 ResolveEventArgs.Name 属性中)。The handler for the AssemblyResolve event receives the display name of the assembly to be loaded, in the ResolveEventArgs.Name property. 如果处理程序无法识别程序集名称,则返回 null (C#)、Nothing (Visual Basic) 或 nullptr (Visual C++)。If the handler does not recognize the assembly name, it returns null (C#), Nothing (Visual Basic), or nullptr (Visual C++).

如果处理程序识别了程序集名称,它可以加载并返回满足此请求的程序集。If the handler recognizes the assembly name, it can load and return an assembly that satisfies the request. 下面的列表介绍了一些示例方案。The following list describes some sample scenarios.

  • 如果处理程序知道某一版本程序集的位置,它可以使用 Assembly.LoadFromAssembly.LoadFile 方法加载程序集,然后返回已加载的程序集(如果成功)。If the handler knows the location of a version of the assembly, it can load the assembly by using the Assembly.LoadFrom or Assembly.LoadFile method, and can return the loaded assembly if successful.

  • 如果处理程序有权访问以字节数组形式存储的程序集的数据库,则它可以通过使用可采用字节数组的一种 Assembly.Load 方法重载来加载字节数组。If the handler has access to a database of assemblies stored as byte arrays, it can load a byte array by using one of the Assembly.Load method overloads that take a byte array.

  • 处理程序可以生成动态程序集并将其返回。The handler can generate a dynamic assembly and return it.

备注

处理程序必须将程序集加载到加载源上下文或加载上下文,或加载不具有上下文的程序集。如果处理程序使用 Assembly.ReflectionOnlyLoadAssembly.ReflectionOnlyLoadFrom 方法将程序集加载到仅限反射的上下文,则引发 AssemblyResolve 事件的加载尝试将失败。The handler must load the assembly into the load-from context, into the load context, or without context.If the handler loads the assembly into the reflection-only context by using the Assembly.ReflectionOnlyLoad or the Assembly.ReflectionOnlyLoadFrom method, the load attempt that raised the AssemblyResolve event fails.

事件处理程序负责返回适当的程序集。It is the responsibility of the event handler to return a suitable assembly. 通过将 ResolveEventArgs.Name 属性值传递到 AssemblyName(String) 构造函数,处理程序可以解析所请求程序集的显示名称。The handler can parse the display name of the requested assembly by passing the ResolveEventArgs.Name property value to the AssemblyName(String) constructor. 从 .NET Framework 4 开始,处理程序可使用 ResolveEventArgs.RequestingAssembly 属性确定当前请求是否是另一程序集的依赖项。Beginning with the .NET Framework 4, the handler can use the ResolveEventArgs.RequestingAssembly property to determine whether the current request is a dependency of another assembly. 此信息有助于识别满足依赖关系的程序集。This information can help identify an assembly that will satisfy the dependency.

事件处理程序返回的程序集版本可能与请求的版本不同。The event handler can return a different version of the assembly than the version that was requested.

大多数情况下,处理程序返回的程序集在加载上下文中显示,与处理程序将其加载到的上下文无关。In most cases, the assembly that is returned by the handler appears in the load context, regardless of the context the handler loads it into. 例如,如果处理程序使用 Assembly.LoadFrom 方法将程序集加载到加载源上下文,当处理程序返回此程序集时,它将在加载上下文中显示。For example, if the handler uses the Assembly.LoadFrom method to load an assembly into the load-from context, the assembly appears in the load context when the handler returns it. 但在以下情况,处理程序返回程序集时将不会显示其上下文:However, in the following case the assembly appears without context when the handler returns it:

有关上下文的信息,请参阅 Assembly.LoadFrom(String) 方法重载。For information about contexts, see the Assembly.LoadFrom(String) method overload.

可将同一程序集的多个版本加载到同一应用程序域中。Multiple versions of the same assembly can be loaded into the same application domain. 不推荐此做法,因为可能导致类型分配问题。This practice is not recommended, because it can lead to type assignment problems. 请参阅适用于程序集加载的最佳做法See Best practices for assembly loading.

事件处理程序的禁忌操作What the event handler should not do

处理 AssemblyResolve 事件的主要规则是不应试图返回无法识别的程序集。The primary rule for handling the AssemblyResolve event is that you should not try to return an assembly you do not recognize. 编写处理程序时应了解哪些程序集可能会导致引发该事件。When you write the handler, you should know which assemblies might cause the event to be raised. 处理程序集应对其他程序集返回 null。Your handler should return null for other assemblies.

重要

从 .NET Framework 4 开始,AssemblyResolve 事件针对附属程序集引发。Beginning with the .NET Framework 4, the AssemblyResolve event is raised for satellite assemblies. 此更改会影响为早期版本的 .NET Framework 编写的事件处理程序(如果此类事件处理程序尝试解决所有程序集加载请求)。This change affects an event handler that was written for an earlier version of the .NET Framework, if the handler tries to resolve all assembly load requests. 忽略无法识别的程序集的事件处理程序不受此更改的影响:这些程序会返回 NULL,并遵循正常的回退机制。Event handlers that ignore assemblies they do not recognize are not affected by this change: They return null, and normal fallback mechanisms are followed.

加载程序集时,事件处理程序禁止使用可导致递归引发 AssemblyResolve 事件的任何 AppDomain.LoadAssembly.Load 方法重载,因为可能导致堆栈溢出。When loading an assembly, the event handler must not use any of the AppDomain.Load or Assembly.Load method overloads that can cause the AssemblyResolve event to be raised recursively, because this can lead to a stack overflow. (请参阅本主题前面部分提供的列表。)即使为加载请求提供异常处理也会发生此情况,因为异常都是在所有事件处理程序返回后引发的。(See the list provided earlier in this topic.) This happens even if you provide exception handling for the load request, because no exception is thrown until all event handlers have returned. 因此,如果未找到 MyAssembly,下面的代码将导致堆栈溢出:Thus, the following code results in a stack overflow if MyAssembly is not found:

using namespace System;
using namespace System::Reflection;

ref class Example
{
internal:
    static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e) 
    {
        Console::WriteLine("Resolving {0}", e->Name);
        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.
 */
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);
        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
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)
        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.

请参阅See also