Не удается получить типы из сборки после ReflectionOnlyLoadFrom

Nov 07 2020

Я проконсультировался с кодом на сайте http://codeproject.com с заголовком «Загрузка сборок из любого места в новый домен приложений» Мариуса Бансила, но я проверил ошибку, как на прилагаемом изображении, в настоящее время я не знаю, разрешить ли вы, надеюсь, вы поможете, спасибо.

Код ссылки https://www.codeproject.com/Articles/453778/Loading-Assemblies-from-Anywhere-into-a-New-AppDom#_articleTop

Контрольная работа

public class Program
{
    [STAThread]
    public static void Main()
    {
        var project = @"D:\Github\BeyConsPlugin\BeyConsProject\bin\x64\Debug\BeyConsRevitProject.dll";//Path to assembly
        var manager = new AssemblyReflectionManager();
        var success = manager.LoadAssembly(project, Guid.NewGuid().ToString());
        if (success)
        {
            var result = manager.Reflect(project, (a) =>
            {
                return a.GetTypes();
            });
            Console.WriteLine(string.Join("\n", result.Select(x => x.Name)));
        }            
        Console.ReadKey();
        manager.UnloadAssembly(project);
    }       
}

Ошибка

Ответы

1 MickyD Nov 07 2020 at 10:16

Резольвер сборки не установлен

В коде Мариуса есть ошибка, связанная AssemblyReflectionProxyс тем, что ассемблерный преобразователь не устанавливается, если вы вызываете в LoadAssemblyотличие от того, Reflect<>что установлено.

В зависимости от того, как создается дочерний домен приложения, при загрузке сборок он может иметь доступ только к папке, указанной при создании. Если вам нужно собрать зонд в другом месте для сборки или ее зависимостей, вам понадобится Assembly Resolver. Когда .NET ищет сборку для домена, он вызывает ваш обработчик, как указано в ReflectionOnlyAssemblyResolveсобытии сборки . Если не указан или если преобразователю не удается найти сборку, он всплывает и генерирует исключение сбоя загрузки.

Предлагаю вам изменить код:

public class AssemblyReflectionProxy : MarshalByRefObject
{
  private string _assemblyPath;

  public void LoadAssembly(String assemblyPath)
  {
     try
     {
        _assemblyPath = assemblyPath;
        Assembly.ReflectionOnlyLoadFrom(assemblyPath);
     }
     catch (FileNotFoundException)
     {
        // Continue loading assemblies even if an assembly 
        // cannot be loaded in the new AppDomain.
     }
  }

...к:

public class AssemblyReflectionProxy : MarshalByRefObject
{
  private string _assemblyPath;

  public void LoadAssembly(String assemblyPath)
  {
     try
     {
        AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve // <---- add me
             += OnReflectionOnlyResolve; 

        _assemblyPath = assemblyPath;
        Assembly.ReflectionOnlyLoadFrom(assemblyPath);
     }
     catch (FileNotFoundException)
     {
        // Continue loading assemblies even if an assembly 
        // cannot be loaded in the new AppDomain.
     }
  }

Вы можете увидеть это в исходном коде Саши , на котором Мариус основал свой.

Добавить положение для разрешения путей

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

Измените, AssemblyReflectionProxyчтобы включить список путей для проверки:

public List<string> ResolvePaths { get; set; }

Затем измените OnReflectionOnlyResolve, чтобы он выглядел следующим образом:

private Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
{
     Assembly loadedAssembly =
         AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies()
             .FirstOrDefault(
               asm => string.Equals(asm.FullName, args.Name,
                   StringComparison.OrdinalIgnoreCase));

     if (loadedAssembly != null)
     {
        return loadedAssembly;
     }

    foreach (var tryFolder in ResolvePaths)
    {
        var asmName = args.Name.Split(',');
        var assemblyPath = Path.Combine(tryFolder, asmName[0] + ".dll");

        if (!File.Exists(assemblyPath))
            return null;

         return Assembly.ReflectionOnlyLoadFrom(assemblyPath);
    }
}

Что в имени?

В обеих статьях пренебрегали мелким шрифтом при использовании ReflectionOnlyLoad. Хотя Саша хотя бы упомянул, что его код предназначен для «генератора кода», я не могу не задаться вопросом, что обе статьи с их « Loading Assemblies.... в новый домен приложения» , возможно, в какой-то мере подлежат интерпретации.

Цель ReflectionOnlyLoadвсего этого - для размышлений . Если вы загрузите сборку с помощью этого метода, вы не сможете выполнить в ней какой-либо код. Вдобавок и поначалу несколько удивительно для большинства программистов-рефлекторов сборки, включая меня, то, что вызов GetCustomAttributesтакже завершится ошибкой (потому что он пытается «создать экземпляры» типов в сборке).

Если вы пишете свою собственную систему подключаемых модулей, в которой каждый подключаемый модуль имеет свой собственный домен приложения, Assemblyметоды отражения и загрузки полезны на разных этапах конвейера загрузки системы подключаемых модулей:

  1. первый проход - использовать ReflectionOnlyLoadкак способ проверить подключаемый модуль, чтобы убедиться, что он действителен; возможно, вы хотите провести некоторые проверки безопасности, зная, что ни один из кодов плагинов не может работать на этом этапе
  2. второй проход - после проверки плагина можно спокойно Load/ LoadFromсборку и выполнить код