上篇文章 《(6)程序集加载上下文》 已经告诉了我们各种程序集上下文,现在来看看.Net中是如何支持这些上下文的。
一、Assembly类提供的多个载入动态程序集方法
1. Load(),LoadFile(),LoadFrom(),LoadWithPartialName(),ReflectionOnlyLoad(),ReflectionOnlyLoadFrom(),UnsafeLoadFrom()
1) Load()
可通过程序集的唯一标识符AssemblyName对象或程序集的长|短格式名称字符串或基于通用对象文件格式(COFF,Common Object File Format)的映像byte[] 来加载程序集。
a) 实现在加载目标程序集时不锁定它
byte[] buffer = System.IO.File.ReadAllBytes(ass.Location);
Assembly assembly = Assembly.Load(buffer);
暂时先将目标程序集锁定,然后把程序集内容复制到内存中,读取后将程序集解锁并从内存中加载目标程序集的拷贝。
2) LoadWithPartialName() 已过时,使用Load()代替。
使用部分名称从应用程序目录或从全局程序集缓存加载程序集。注意:此方法加载不到程序集返回null,不会报错。
3) LoadFile()
加载指定路径上的程序集文件的内容。
4) LoadFrom()
可通过给定程序集文件名或路径、哈希值及哈希算法来加载程序集。
5) ReflectionOnlyLoad()
可用程序集的长|短格式名称字符串或基于通用对象文件格式(COFF)的映像来加载程序集。程序集被加载到调用方的应用程序域的只反射上下文中。
6) ReflectionOnlyLoadFrom()
将给定路径的程序集加载到只反射上下文中。
7) UnsafeLoadFrom()
使用包含程序集清单的文件的名称或路径。绕过某些安全检查,将程序集加载到加载源上下文中。
8) 差异
a) Load(string|AssenblyName)\LoadFrom()\LoadWithPartialName()\ReflectionOnlyLoad()\ReflectionOnlyLoadFrom()\UnsafeLoadFrom()
i. Load(string|AssemblyName)\LoadWithPartialName()\ReflectionOnlyLoad() 使用程序集(长|短)名称加载。
ii. LoadFrom()\ReflectionOnlyLoadFrom()\UnsafeLoadFrom() 使用程序集文件名或路径加载。(注意有后缀如 .dll )
iii. 内部实现会先获取到AssemblyName,调用InternalLoadAssemblyName来加载程序集,InternalLoadAssemblyName若有传入Evidence参数的就进行Evidence凭据验证;然后再检查路径访问权限(除了UnsafeLoadFrom()):根据程序集标识探测路径或传入路径参数来判断是否为本地文件(file://开头的URI),然后进行相应的本地文件访问权限(FileIOPermission)或Internal资源访问权限(WebPermission)判断,再调用 extern RuntimeAssembly _nLoad() 进行程序集加载。
(不支持文件传输协议 (FTP)。如果为 assemblyFile 提供的 URI 是一个 FTP 地址,则不会加载程序集。不引发异常。)
iv. 在.NET Framework 2.0 以后比如 Assembly.Load 方法有缓存,第一次加载目标程序集的失败或者成功的状态都会被缓存,这样在你下一次加载目标程序集时会直接从缓存里取目标程序集的内容和状态。
当失败后我们可能想要再加载一次或者加载多次直到成功为止,这时需要在app.config 中加入如下配置信息来禁用缓存:
<runtime> <disableCachingBindingFailures enabled="1" /> </runtime>
注意: ReflectionOnlyLoad()\ReflectionOnlyLoadFrom() 只反射加载不会使用缓存。
v. ReflectionOnlyLoad()\ReflectionOnlyLoadFrom()
1) 两个方法内部调用同Load()\LoadFrom(),只是在调用加载内部方法时“只反射方法”传入bool参数introspection(检查)为true。
2) 这两个方法是将程序集载入到“只反射上下文”,所以不可以在通过ReflectionOnly 方法加载进来的程序集执行任何方法,包括构造函数,只可以获取程序集的信息和类型。可以使用Assembly实例的ReflectionOnly属性确定该程序集是否加载到了只反射上下文中。
3) 这两个方法在载入程序集时不会将依赖程序集载入,需自行处理ReflectionOnlyAssemblyResolve事件来自行加载依赖程序集;(ReflectionOnlyLoad(byte[]) 重载也一样)
4) 加载到只反射上下文中的代码无法执行。这意味着不能创建自定义特性的实例,因为这将需要执行其构造函数。若要在只反射上下文中加载和检查自定义特性,请使用 CustomAttributeData 类。可以通过使用静态 CustomAttributeData.GetCustomAttributes 方法的相应重载获取此类的实例。
vi. LoadFrom()
在LoadFrom载入一个程序集时,会先检查前面是否已经载入过具有相同长名称的程序集(AssemblyName.FullName)----注意程序集内容不一样但FullName相同后面进行的载入会直接取前面载入的缓存。
可以通过LoadFile()方法加载不同路径拥有相同AssemblyName.FullName的多个程序集。
另外:ReflectionOnlyLoadFrom()此方法载入相同AssemblyName.FillName程序集时会报错,eg:API 限制: 程序集“file:///C:\新建文件夹\ClassLibrary123.dll”已从其他位置加载。无法从同一个 Appdomain 中的另一新位置加载该程序集。
b) LoadFile(path)
调用 extern RuntimeAssembly nLoadFile() 进行程序集加载,不会加载程序的依赖项。
c) Load(byte[])\ReflectionOnlyLoad(byte[])
调用 extern RuntimeAssembly nLoadImage() 进行程序集加载。
2. 动态加载程序集使用证据有两种截然不同的方式:
a) 将输入证据与加载程序所收集的证据合并,以创建用于策略决策的最终证据集。使用传入 Evidence 证据对象的方法包括 Assembly.Load、Assembly.LoadFrom 和 Activator.CreateInstance。
b) 原封不动地使用输入证据作为用于策略决策的最终证据集。使用这种语义的方法包括 Assembly.Load(byte[]) 和 AppDomain.DefineDynamicAssembly()。
在加载时,程序集的证据用作安全策略的输入。安全策略是由企业和计算机的管理员以及用户策略设置建立的,它在执行时确定向所有托管代码授予的权限组。可以为该程序集(如果该程序集具有签名工具生成的签名)的发行者建立安全策略,或者为该程序集的下载网站和区域(就 Internet Explorer 而言)建立安全策略,也可以为该程序集的强名称建立该策略。例如,一台计算机的管理员可以建立这样一种安全策略:它允许从某一网站下载由指定软件公司签发用以访问计算机上的数据库的所有代码,但不授予对该计算机磁盘的写访问权。
二、使用Fuslogvw.exe程序集绑定日志查看器显示程序集绑定的详细信息,优化性能
启动:
您必须具有管理员权限运行 fuslogvw.exe。
1、如果您在计算机上安装的 Visual Studio: 的任务栏上,单击Start,单击All Programs,单击Visual Studio,单击Visual Studio Tools,用鼠标右键单击Visual Studio Command Prompt,然后单击以管理员身份运行在快捷菜单中。
- 或 -
2、如果您必须在计算机上安装 Windows SDK: 的任务栏上,单击Start,单击All Programs,为 Windows SDK 中单击该文件夹,用鼠标右键单击Command Prompt (或CMD Shell),然后单击以管理员身份运行在快捷菜单中。
在管理员命令提示符下键入命令:fuslogvw
首先,按如下设置就可跟踪程序集绑定的详细信息。
几个跟踪示例:
1. 使用 Load() 长名称加载程序集
注意长名称和短名称加载程序集探测中对策略文件的影响
Eg:
使用长名称加载程序集并设置<codeBase>如下图,正确应用了配置策略
因为在使用如< assemblyIdentity >、< codeBase >等元素需要提供版本,公钥等信息,所以使用短名称不会应用配置策略。
2. 通过执行两次失败加载操作,可以验证下面方法在探测程序集时会使用已经加载过的缓存内容和状态
Load(string|AssenblyName) ,LoadWithPartialName(),LoadFrom(),UnsafeLoadFrom()
3. LoadFile() 加载程序集过程
如果根据指定路径绑定成功,则在“没有上下文”的情况下进行加载程序集;【如果成功加载】,那么CLR还将尝试根据加载到的程序集长名称标识调用 Load() 将此程序集同时加载到“默认加载上下文”中。
这边的两次绑定不是切换,所以为了避免出现类型标识问题(即:将同一个程序集加载到不同上下文中),尽量避免使用该方法。
4. Load(byte[]) 加载程序集绑定信息
使用 byte[] 内存进行程序集加载的不会产生程序集绑定日志。(如果加载到的程序集标识(在应用策略后创建的)与全局程序集缓存中的程序集标识匹配,将会从全局程序集缓存加载程序集,产生程序集绑定信息,这时只加载到“默认加载上下文”中)
5. LoadFrom()\UnsafeLoadFrom()
1) 使用文件名绑定(不是具体路径)
先使用文件名获取到程序集信息,然后根据此程序集的长名称标识调用 Load() 将此加载从“加载位置上下文”切换到“默认加载上下文”。
ReflectionOnlyLoadFrom() 使用文件名绑定时,不会发生上面的切换操作,直接加在到“加载上下文”中;使用具体路径时,不会在GAC和其他位置搜索文件,只探测指定路径位置的程序集。
6. Load()\LoadWithPartialName() 执行部分绑定信息绑定过程
先使用部分绑定信息获取到程序集信息,然后根据此程序集的长名称标识再调用Load()方法执行加载程序集。
使用ReflectionOnlyLoad()方法不会应用版本策略,所以你指定的是哪个版本,获得的就是哪个版本。如果要自行为一个程序集标识指定版本控制策略,可将字符串传给AppDomain的ApplyPolicy()。
所以在执行程序集加载时:
1) 尽量使用程序集长名称或完整路径进行加载,以避免重复探测程序集开销。
2) 使用 LoadFile() 要注意避免不要将同一程序集加载到不同上下文中。
3) 对于只需获取程序集信息而不通过反射操作成员时,要使用“只反射加载”
《反射机制》系列:
参考资源: