ReflectionOnlyLoadFrom 后无法从程序集中获取类型 [英] Cannot get types from assembly after ReflectionOnlyLoadFrom

查看:69
本文介绍了ReflectionOnlyLoadFrom 后无法从程序集中获取类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在网站

解决方案

Assembly Resolver not Set

Marius 的代码在 AssemblyReflectionProxy 中存在一个错误,即如果您调用 LoadAssembly,则不会设置 Assembly Resolver,而 Reflect<>

根据子应用程序域的创建方式,在加载程序集时,它可能只能访问创建期间指定的文件夹.如果您需要在其他地方为程序集或其依赖项进行程序集探测,则需要一个程序集解析器.当 .NET 为域寻找程序集时,它将调用程序集的 ReflectionOnlyAssemblyResolve 事件中指定的处理程序.如果未指定或解析器无法定位程序集,则会冒泡并抛出加载失败异常.

我建议您更改以下代码:

公共类 AssemblyReflectionProxy : MarshalByRefObject{私有字符串_assemblyPath;public void LoadAssembly(String assemblyPath){尝试{_assemblyPath = assemblyPath;Assembly.ReflectionOnlyLoadFrom(assemblyPath);}捕获 (FileNotFoundException){//继续加载程序集,即使是程序集//无法在新的 AppDomain 中加载.}}

...到:

公共类 AssemblyReflectionProxy : MarshalByRefObject{私有字符串_assemblyPath;public void LoadAssembly(String assemblyPath){尝试{AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve//<---- 添加我+= OnReflectionOnlyResolve;_assemblyPath = assemblyPath;Assembly.ReflectionOnlyLoadFrom(assemblyPath);}捕获 (FileNotFoundException){//继续加载程序集,即使是程序集//无法在新的 AppDomain 中加载.}}

您可以在 Sacha 的 原文中看到这一点代码是Marius基于他的代码.

添加解析路径的规定

代码的另一个问题是,两者都假设在加载一个程序集时,任何依赖程序集都位于同一文件夹中,但情况并非总是如此.

更改 AssemblyReflectionProxy 以包含要探测的路径列表:

public List解析路径{获取;放;}

然后将 OnReflectionOnlyResolve 修改为如下所示:

private Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo目录){装配加载装配 =AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(asm =>string.Equals(asm.FullName, args.Name,StringComparison.OrdinalIgnoreCase));if (loadedAssembly != null){返回loadedAssembly;}foreach(ResolvePaths 中的 var tryFolder){var asmName = args.Name.Split(',');var assemblyPath = Path.Combine(tryFolder, asmName[0] + ".dll");如果 (!File.Exists(assemblyPath))返回空;返回 Assembly.ReflectionOnlyLoadFrom(assemblyPath);}}

名字有什么含义?

这两篇文章都忽略了使用 ReflectionOnlyLoad 时的细节.虽然 Sacha 至少提到了他的代码是用于代码生成器"的,我不禁怀疑这两篇文章都带有加载程序集....into a NewAppDomain" 可能在某种程度上受到解释.

ReflectionOnlyLoad 的目的就是为了 reflection.如果通过此方法加载程序集,则无法执行其中的任何代码.此外,对于包括我在内的大多数组装反射器程序员来说,一开始有些令人惊讶的是,调用 GetCustomAttributes 也会失败(因为它试图在程序集中实例化" 类型).

如果您正在编写自己的插件系统,其中每个插件都有自己的应用程序域,Assembly 反射和加载方法对于插件系统加载的不同阶段很有用管道:

  1. 第一关 - 使用 ReflectionOnlyLoad 作为检查插件的一种方式,看看它是否有效;也许您想安全地运行一些安全检查,因为在此阶段没有任何插件代码可以运行
  2. 第二遍——验证插件后,就可以安全地Load/LoadFrom程序集并执行代码

I have consulted code on the website http://codeproject.com with the title "Loading Assemblies from Anywhere into a New AppDomain" of Marius Bancila, but I tested the error as in the attached picture, currently, I don't know resolve, hope you help, thank you.

Link Code https://www.codeproject.com/Articles/453778/Loading-Assemblies-from-Anywhere-into-a-New-AppDom#_articleTop

Test

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

Error

解决方案

Assembly Resolver not Set

Marius's code has a bug in AssemblyReflectionProxy with regards that the Assembly Resolver is not set if you call LoadAssembly unlike Reflect<> which does.

Depending on how a child app domain is created, when loading assemblies it might only have access to the folder as specified during creation. If you need to assembly probe elsewhere for the assembly or its dependencies you need a Assembly Resolver. When .NET is looking for an assembly for a domain, it will call your handler as specified in the assemblie's ReflectionOnlyAssemblyResolve event. If not specified or if the resolver fails to locate the assembly, it will bubble up and throw a load fail exception.

I suggest you change the code from:

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.
     }
  }

...to:

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.
     }
  }

You can see this in Sacha's original code that on which Marius based his.

Add Provision for Resolve Paths

The other problem with the code is that both assume that when loading one assembly, any dependent assemblies are in the same folder something that may not always be the case.

Alter AssemblyReflectionProxy to include a list of paths in which to probe:

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

Then modify OnReflectionOnlyResolve to resemble the following:

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

What's in a name?

Both articles neglected to point out the fine print when using ReflectionOnlyLoad. Though Sacha did at least mention his code was for a "code generator" I can't help but wonder that both articles with their "Loading Assemblies....into a New AppDomain" are perhaps somewhat subject to interpretation.

The aim of ReflectionOnlyLoad is just that - for reflection. If you load an assembly via this method you cannot execute any code in it. Additionally and somewhat surprising at first to most assembly reflector programmers including me is that calling GetCustomAttributes will also fail (because it attempts to "instantiate" types in the assembly).

If you are writng your own plug-in system in which each plug-in has its own App Domain, the Assembly reflection and load methods are useful for different stages of the plug-in system loading pipeline:

  1. first pass - use ReflectionOnlyLoad as a way to inspect the plug-in to see if it is valid; perhaps you want to run some security checks safe in the knowledge that none of the plug-ins code can run during this phase
  2. second pass - after verifying the plug-in, you can safely Load/LoadFrom the assembly and execute the code

这篇关于ReflectionOnlyLoadFrom 后无法从程序集中获取类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆