ReflectionOnlyLoadFrom 后无法从程序集中获取类型 [英] Cannot get types from assembly after 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
反射和加载方法对于插件系统加载的不同阶段很有用管道:
- 第一关 - 使用
ReflectionOnlyLoad
作为检查插件的一种方式,看看它是否有效;也许您想安全地运行一些安全检查,因为在此阶段没有任何插件代码可以运行 - 第二遍——验证插件后,就可以安全地
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.
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:
- 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 - second pass - after verifying the plug-in, you can safely
Load
/LoadFrom
the assembly and execute the code
这篇关于ReflectionOnlyLoadFrom 后无法从程序集中获取类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!