Поделиться через


Разрешение загрузки сборок

В .NET Framework имеется событие AppDomain.AssemblyResolve для приложений, требующих дополнительного управления загрузкой сборок. Обрабатывая это событие, приложение может загружать сборку в контекст загрузки не из каталогов, где обычно осуществляется поиск, выбирать, какую из версий сборки загрузить, создавать динамическую сборку и возвращать ее и многое другое. В этом разделе описывается использование события AssemblyResolve.

ПримечаниеПримечание

Для разрешения загрузки сборок в контексте только для отражения используйте событие AppDomain.ReflectionOnlyAssemblyResolve.

Принцип действия события AssemblyResolve

При регистрации обработчика для события AssemblyResolve обработчик вызывается каждый раз, когда среда выполнения не может связать сборку по имени. Например, вызов следующих методов из пользовательского кода может привести к возникновению события AssemblyResolve:

  • Перегрузка метода AppDomain.Load или перегрузка метода Assembly.Load, где первым аргументом является строка, представляющая отображаемое имя загружаемой сборки (то есть строка, возвращаемая свойством Assembly.FullName).

  • Перегрузка метода AppDomain.Load или перегрузка метода Assembly.Load, где первым аргументом является объект AssemblyName, идентифицирующий загружаемую сборку.

  • Перегрузка метода Assembly.LoadWithPartialName.

  • Перегрузка метода AppDomain.CreateInstance или AppDomain.CreateInstanceAndUnwrap, создающая объект в другом домене приложения.

Действие обработчика событий

Обработчик события AssemblyResolve получает отображаемое имя сборки, которую требуется загрузить, в свойство ResolveEventArgs.Name. Если обработчик не распознает имя сборки, он возвращает значение NULL (Nothing в Visual Basic, nullptr в Visual C++).

Если обработчик распознает имя сборки, он может загрузить и вернуть сборку, отвечающую запросу. В следующем списке приводится несколько возможных сценариев.

  • Если обработчик знает место расположения версии сборки, он может загрузить сборку с помощью метода Assembly.LoadFrom или Assembly.LoadFile и, если все прошло удачно, вернуть загруженную сборку.

  • Если у обработчика есть доступ к базе данных сборок, хранимых в виде массивов байтов, он может загрузить массив байтов с помощью одной из перегрузок метода Assembly.Load, принимающих массив байтов.

  • Обработчик может создать динамическую сборку и вернуть ее.

ПримечаниеПримечание

Обработчик должен загружать сборку в контекст, из которого ведется загрузка, в контекст загрузки или без контекста.Если обработчик загружает сборку в контекст только для отражения с помощью метода Assembly.ReflectionOnlyLoad или Assembly.ReflectionOnlyLoadFrom, попытка загрузки, вызвавшая событие AssemblyResolve, заканчивается неудачно.

Ответственность за возврат подходящей сборки лежит на обработчике событий. Обработчик может обработать отображаемое имя запрошенной сборки, передав значение свойства ResolveEventArgs.Name в конструктор AssemblyName(String). Начиная с .NET Framework 4, обработчик может использовать свойство ResolveEventArgs.RequestingAssembly для определения, находится ли текущий запрос в зависимости от другой сборки. Эта информация может помочь найти сборку, которая удовлетворит зависимость.

Обработчик событий может вернуть версию сборки, отличную от запрошенной.

В большинстве случаев сборка, возвращенная обработчиком, появляется в контексте загрузки, независимо от контекста, в который загружает ее обработчик. Например, если обработчик использует метод Assembly.LoadFrom для загрузки сборки в контекст, из которого ведется загрузка, сборка появляется в контексте загрузки, когда обработчик возвращает ее. Однако в следующем случае возвращенная обработчиком сборка появится без контекста.

  • Обработчик загружает сборку без контекста.

  • Значение свойства ResolveEventArgs.RequestingAssembly отличается от NULL.

  • Запрашивающая сборка (то есть сборка, возвращенная свойством ResolveEventArgs.RequestingAssembly) загружена без контекста.

Дополнительные сведения о контекстах см. в разделе о перегрузке метода Assembly.LoadFrom(String).

Несколько версий одной сборки можно загрузить в один домен приложения. Делать так не рекомендуется, поскольку это может привести к проблемам назначения типа. См. раздел Рекомендации для загрузки сборок.

Чего не должен делать обработчик событий

Основное правило обработки события AssemblyResolve заключается в том, что не следует пытаться вернуть сборку, которая не распознается. При написании обработчика следует учитывать, какие сборки могут вызвать событие. Обработчик должен возвращать значение NULL для других сборок.

Важное примечаниеВажно

Начиная с .NET Framework 4, событие AssemblyResolve вызывается для вспомогательных сборок.Это изменение затрагивает обработчик событий, написанный для более ранней версии .NET Framework, если обработчик пытается разрешить все запросы на загрузку сборок.Это изменение не затрагивает обработчики событий, игнорирующие не распознанные сборки. Такие обработчики возвращают значение NULL, и срабатывают обычные резервные механизмы.

При загрузке сборки обработчик событий не должен использовать какую-либо из перегрузок метода AppDomain.Load или Assembly.Load, которые могут вызвать рекурсивное возникновение события AssemblyResolve, так как это может привести к переполнению стека. (См. список выше в этом разделе.) Это происходит, даже если обрабатывать исключения, поскольку исключение не создается, пока все обработчики событий не закончат возврат. Таким образом, следующий код приведет к переполнению стека, если объект MyAssembly не будет найден.

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

См. также

Другие ресурсы

Рекомендации для загрузки сборок

Использование доменов приложений