Подтвердить что ты не робот

Может ли вызов Assembly.Load(byte []) вызвать событие AppDomain.AssemblyResolve?

Предположим, что у меня есть обработчик события AppDomain.AssemblyResolve, а в обработчике я строю массив байтов и вызывается метод Assembly.Load(byte[]). Может ли этот метод вызвать событие AssemblyResolve снова, и заставить мой обработчик быть повторно введен?

Мой вопрос не ограничивается только сборками, которые могут быть сгенерированы с использованием компилятора С#, они могут содержать метаданные abritrary и исполняемый код, поддерживаемые средой CLR.

Я сделал несколько экспериментов и не обнаружил случаев, когда это происходит. Я попытался загрузить сборки, требующие дополнительных ссылок, попытался добавить атрибуты CAS к загруженной сборке, для декодирования которой потребуется другая сборка, попытался загрузить сборку с инициализатором модуля (глобальный метод .cctor). Ни в коем случае я не заметил, что событие AssemblyResolve должно быть поднято из метода Assembly.Load(byte[]), это произошло только в том случае, если какой-то код позже попытался получить доступ к типам, методам или атрибутам в загруженной сборке. Но я могу кое-что упустить.

4b9b3361

Ответ 1

Насколько мне известно, Assembly.Load или загрузка сборки другими способами не выполняет никаких конструкторов, которые могут быть сгенерированы компилятором С# (включая статические конструкторы). В результате вы не сможете получить reentrancy для AssemblyResolve для общедоступных сборок.

Как вы уже упоминали в вопросе, инициализаторы модулей не выполняются во время вызова Load. Покрытый в списке гарантий в спецификации CLI вы можете найти в Инициализаторы модулей от Junfeng Zhang.

В. Метод инициализатора модулей выполняется в или когда-либо раньше, первым доступом к любым типам, методам или данным, определенным в модуле

Есть связанные вопросы SO, обычно обсуждающие "код запуска перед конструкторами любого типа", например Инициализировать библиотеку при загрузке сборки. Обратите внимание, что .Net: Запуск кода при загрузке сборки имеет ответ Марка Гравелла, в котором говорится, что это может быть невозможно из-за ограничений безопасности.

Ответ 2

Инициализатор модуля является единственным создателем проблем, о котором я могу думать. Простой пример одного в С++/CLI:

#include "stdafx.h"
#include <msclr\gcroot.h>

using namespace msclr;
using namespace ClassLibrary10;

class Init {
    gcroot<ClassLibrary1::Class1^> managedObject;
public:
    Init() {
        managedObject = gcnew ClassLibrary1::Class1;
    }
} Initializer;

Конструктор Init() вызывается, когда модуль загружается через инициализатор модуля, сразу после его инициализации среды выполнения C. Вы не подключены к этому типу кода, хотя в вашем конкретном случае Assembly.Load(byte []) не может загружать сборки смешанного режима.

В противном случае это ограничение, вызванное инициализаторами модулей. Они были добавлены в CLR v2.0 с особым намерением аналогичных заданий, таких как это, получение языка выполнения для инициализации, прежде чем он начнет выполнение любого управляемого кода. Шансы, которые вы попадаете в такой код, должны быть очень и очень низкими. Вы узнаете это, когда увидите это:)

Ответ 3

Документация MSDN гласит:

Как работает событие AssemblyResolve:

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

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

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

Не упоминается перегрузка, получающая a byte[]. Я посмотрел в исходном источнике, и кажется, что Load, который принимает перегрузку string, внутренне вызывает метод с именем InternalLoad, который перед вызовом native LoadImage вызывает CreateAssemblyName, и в его документации указано:

Создает AssemblyName. Заполняет сборку, если событие AssemblyResolve было поднято.

internal static AssemblyName CreateAssemblyName(
           String assemblyString, 
           bool forIntrospection, 
           out RuntimeAssembly   assemblyFromResolveEvent)
{
        if (assemblyString == null)
           throw new ArgumentNullException("assemblyString");
        Contract.EndContractBlock();

        if ((assemblyString.Length == 0) ||
            (assemblyString[0] == '\0'))
            throw new ArgumentException(Environment.GetResourceString("Format_StringZeroLength"));

        if (forIntrospection)
            AppDomain.CheckReflectionOnlyLoadSupported();

        AssemblyName an = new AssemblyName();

        an.Name = assemblyString;
        an.nInit(out assemblyFromResolveEvent, forIntrospection, true); // This method may internally invoke AssemblyResolve event.

        return an;

Перегрузка byte[] не имеет этого, она просто вызывает нативный nLoadImage внутри QCall.dll. Это может объяснить, почему ResolveEvent не вызывается.

Ответ 4

Вы упомянули -

Ни в коем случае я не заметил, что событие AssemblyResolve должно быть поднято из внутри метода Assembly.Load(byte []), это произошло только в том случае, если некоторый код позже попытался получить доступ к типам, методам или атрибутам в загруженном сборка. Но я могу кое-что упустить.

Здесь следует отметить пункты -

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

  • AssemblyResolve - это событие, определенное в типе AppDomain. Таким образом, это событие невозможно вызвать изнутри Assembly.Load(byte [])

Следовательно, если вы уже зарегистрировались в событии AssemblyResolve на работающем appdomain и вызывают Assembly.Load(byte []), он загружает сборку в текущем домене.

Теперь, когда вызывается какой-либо тип из этой загруженной сборки, который позволяет сказать, что вызывает другой тип, определенный в какой-либо другой сборке, AppDomain вызовет событие AssemblyResolve, чтобы попытаться загрузить эту сборку.