可以调用Assembly.Load(byte )来引发AppDomain.AssemblyResolve事件吗?

假设我有一个AppDomain.AssemblyResolve事件的处理程序,并在处理程序中我构造一个字节数组并调用方法Assembly.Load(byte[]) 。 此方法本身是否会导致再次引发AssemblyResolve事件,并导致我的处理程序重新进入?

我的问题不仅限于可以使用C#编译器生成的程序集,它们可以包含CLR支持的abritrary元数据和可执行代码。

我做了一些实验,发现时没有发现任何情况。 我试图加载需要额外引用的程序集,试图将CAS属性添加到加载的程序集,其解码需要另一个程序集,尝试使用模块初始化程序(全局.cctor方法)加载程序集。 在任何情况下,我都没有观察到AssemblyResolve事件是从Assembly.Load(byte[])方法内部引发的,只有在某些代码稍后尝试访问加载的程序集中的类型,方法或属性时才会发生。 但我可能会在这里遗漏一些东西。

据我所知, Assembly.Load或通过其他方式加载程序集不会执行任何可由C#编译器生成的构造函数(包括静态构造函数)。 因此,您不会在常见的程序集上重新使用AssemblyResolve

正如您在问题中提到的,在Load调用期间不会执行模块初始值设定项。 CLI规范中的保证列表 – 摘录可以在Junfeng Zhang的模块初始化程序中找到。

B.模块的初始化方法在首次访问模块中定义的任何类型,方法或数据之前或之前执行。

有相关的SO问题通常讨论“在任何类型构造函数之前运行代码”,比如在Assembly load上初始化库 。 请注意.Net:加载程序集时运行代码有Marc Gravell的答案,声明由于安全性限制可能无法实现。

模块初始化程序是我能想到的唯一麻烦制造者。 C ++ / CLI中的一个简单示例:

 #include "stdafx.h" #include  using namespace msclr; using namespace ClassLibrary10; class Init { gcroot managedObject; public: Init() { managedObject = gcnew ClassLibrary1::Class1; } } Initializer; 

在初始化C运行时之后,通过模块初始化程序加载模块时,将调用Init()构造函数。 尽管在特定情况下,Assembly.Load(byte [])无法加载混合模式程序集,但您对此类代码不屑一顾。

这不是由模块初始化器引起的限制。 它们被添加到CLR v2.0中,其特定意图是类似这样的工作,在开始执行任何托管代码之前,让语言运行时初始化自己。 你遇到这样的代码的几率应该非常非常低。 当你看到它时你会知道它:)

MSDN文档声明:

AssemblyResolve事件如何工作:

注册AssemblyResolve事件的处理程序时,只要运行时无法按名称绑定到程序集,就会调用该处理程序。 例如,从用户代码调用以下方法可能导致AssemblyResolve事件被引发:

  1. AppDomain.Load方法重载或Assembly.Load方法重载,其第一个参数是一个字符串 ,表示要加载的程序集的显示名称(即Assembly.FullName属性返回的字符串)。

  2. AppDomain.Load方法重载或Assembly.Load方法重载,其第一个参数是一个AssemblyName对象 ,用于标识要加载的程序集。

它没有提到接收byte[]的重载。 我在参考源中查找,似乎接受string重载的Load在内部调用一个名为InternalLoad的方法,该方法在调用本机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

你提到了 –

在任何情况下,我都没有观察到AssemblyRoolve事件是从Assembly.Load(byte [])方法内部引发的,只有在某些代码稍后尝试访问加载的程序集中的类型,方法或属性时才会发生。 但我可能会在这里遗漏一些东西。

这里要注意的要点 –

  1. 在执行代码时,如果代码中引用了类型并且CLR检测到未加载包含该类型的程序集,则它将加载程序集。 你的观察在这里是正确的。

  2. AssemblyResolve是在AppDomain类型中定义的事件。 所以这个事件不能从Assembly.Load(byte [])内部引发

因此,如果您已在正在运行的appdomain上注册AssemblyResolve事件并调用Assembly.Load(byte []),则会在当前域中加载程序集。

现在,当调用此加载程序集中的任何类型时,可以说它恰好调用了在其他程序集中定义的另一种类型, AppDomain将调用AssemblyResolve事件来尝试加载该程序集