我们在使用C# 语言的Assembly.Load 来加载托管程序集并使用反射功能时,一般需要先通过Assembly.Load(), Assembly.LoadFrom() 等方法将目标托管程序集加载到当前应用程序域中,然后生成对应实例,最后再进行调用实例的属性或者方法。

一般情况下,我们调用Assembly.Load 一类方法是不会出问题的,但是对于以下几种情况Assembly.Load 方法无法处理:

  1. 程序集可能是延迟签名的。
  2. 程序集可能被CAS 策略保护。
  3. 宿主程序与目标程序集的处理器架构不同。
  4. 当加载目标程序集时,目标程序集中的方法可能正在运行。 (比如,模块初始化)
  5. 程序集可能应用了绑定策略, 你可能不会得到你想要的那个程序集。

我们现在关注第四种情况,因为这种情况是最常见的。我们思考以下几个问题:

1. 为什么目标程序集的方法在运行时不允许再加载一次?

准确地说是为什么在一个应用程序域(AppDomain)中加载后的程序集默认不允许再另外一个应用程序域中加载?这是因为在第一次加载应用程序集时,Assemlby.Load 方法会将此程序集锁住,以防止在自己使用过程中应用程序集被其他应用程序修改(一般指删除)。这其实与Win32 API 中的CreateFile 函数行为类似,我们都知道,在 Windows 中去占用一个文件最直接、最简单的方式就是调用 CreateFile API 函数来打开文件。具体请参照:

http://blog.csdn.net/xt_xiaotian/article/details/6362450

2. Assembly.Load 方法能否实现在加载目标程序集时不锁定它?

我们可以使用如下代码加载我们的程序集:

byte[] buffer = System.IO.File.ReadAllBytes(yourFullfileNamePath);

//Load assembly using byte array

Assembly assembly = Assembly.Load(buffer);

后台的实现是暂时先将目标程序集锁定,然后把程序集内容复制到内存中,读取后将程序集解锁并从内存中加载目标程序集的拷贝。

如果你需要通过上面的方法加载GAC 中的程序集,那么可以通过如下代码实现:

string assemblyName = "AssemblyTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fffb45e56dd478e3";

Assembly ass = Assembly.ReflectionOnlyLoad(assemblyName);

byte[] buffer = System.IO.File.ReadAllBytes(ass.Location);

Assembly assembly = Assembly.Load(buffer

3. Assembly.Load 方法的缓存

在我们使用Assembly.Load 系列方法加载目标程序集时,可能有各种情况导致加载失败,最常见的是目标程序集不存在而导致加载失败问题。失败后我们可能想要再加载一次或者加载多次直到成功为止,但是在.NET Framework 2.0 以后默认是无法实现的,原因在于.NET Framework 2.0 以后 Assembly.Load 方法有缓存,第一次加载目标程序集的失败或者成功的状态都会被缓存,这样在你下一次加载目标程序集时不会真的加载,会直接从缓存里取目标程序集的内容和状态。

对这种情况我们可以在调用Assembly.Load 方法的宿主程序的app.config 中加入如下配置信息来禁用缓存:

<?xml version="1.0"?>
<configuration>
<runtime>
<disableCachingBindingFailures enabled="1" />
</runtime>
<startup>
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>

4. 还有其他方案吗?

CLR 框架略图:

因为目前Assembly 的加载与卸载是完全由应用程序域控制的,一个程序集只可以通过应用程序域加载,只能通过应用程序域的卸载而卸载,其他任何方式都不可以!!!

那么我们可以在需要时将目标程序集加载进应用程序域,不需要时将应用程序域卸载,但是当前应用程序域的卸载只能通过关闭程序来实现。

到目前为止,看似无解了,但是我们忽略了一个事实,那就是我们可以在当前应用程序域中创建子应用程序域。可以通过在子应用程序域中进行程序集的加载,或者说只要涉及程序集加载的全部放在子应用程序域中,主应用程序域中不做任何与程序集加载有关的事情。

这部分内容我就不详细介绍了,大家可以参考:

http://www.codeproject.com/Articles/42312/Loading-Assemblies-in-Separate-Directories-Into-a

5. 我们确实需要使用Assembly.Load吗?

因为Assembly.Load 是将整个程序集以及其相关的依赖程序集全部加载进来,只要有一个出错就会导致加载失败。如果我们只是为了使用当前程序集的类型,而不是使用其方法或者属性的话就完全可以抛弃Assembly.Load 方法。

微软在.Net Framework 2.0 时介绍了几个新的程序集加载APIs:

Assembly.ReflectionOnlyLoadFrom(String assemblyFile)

Assembly.ReflectionOnlyLoad(byte[] rawAssembly)

Assembly.ReflectionOnlyLoad(String assemblyName)

基于开篇提到的Assembly.Load 方法的5种问题, Reflection Only程序集加载APIs 可以:

  1. 跳过程序集强命名认证。
  2. 跳过程序集CAS策略认证。
  3. 跳过处理器架构检查规则。
  4. 不在目标程序中执行任何方法,包括构造函数。
  5. 不应用任何绑定策略。

使用时有如下几个注意事项:

1) CLR 不会搜索目标程序集所依赖的程序集,我们必须通过ReflectionOnlyAssemblyResolve(Assembly.Load 中的对应事件是AssemblyResolve)事件手动处理。

2) 不可以在通过ReflectionOnly 方法加载进来的程序集执行任何方法,包括构造函数,只可以获取程序集的信息和类型。

3) 建议使用Assembly.ReflectionOnlyLoadFrom 方法,但是如果目标程序集在GAC中那么可以使用Assembly.ReflectionOnlyLoad方法。

具体示例代码如下:

using System;
using System.IO;
using System.Reflection; public class ReflectionOnlyLoadTest
{
private String m_rootAssembly;
public ReflectionOnlyLoadTest(String rootAssembly)
{
m_rootAssembly = rootAssembly;
} public static void Main(String[] args)
{
if (args.Length != 1)
{
Console.WriteLine("Usage: Test assemblyPath");
return;
} try
{
ReflectionOnlyLoadTest rolt = new ReflectionOnlyLoadTest(args[0]);
rolt.Run();
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}!!!", e.Message);
}
} internal void Run()
{
AppDomain curDomain = AppDomain.CurrentDomain;
curDomain.ReflectionOnlyAssemblyResolve +=
new ResolveEventHandler(MyReflectionOnlyResolveEventHandler);
Assembly asm = Assembly.ReflectionOnlyLoadFrom(m_rootAssembly);
// force loading all the dependencies
Type[] types = asm.GetTypes();
// show reflection only assemblies in current appdomain
Console.WriteLine("------------- Inspection Context --------------");
foreach (Assembly a in curDomain.ReflectionOnlyGetAssemblies())
{
Console.WriteLine("Assembly Location: {0}", a.Location);
Console.WriteLine("Assembly Name: {0}", a.FullName);
Console.WriteLine();
}
} private Assembly MyReflectionOnlyResolveEventHandler(object sender, ResolveEventArgs args)
{
AssemblyName name = new AssemblyName(args.Name);
String asmToCheck = Path.GetDirectoryName(m_rootAssembly) + "\\" + name.Name + ".dll";
if (File.Exists(asmToCheck))
{
return Assembly.ReflectionOnlyLoadFrom(asmToCheck);
} return Assembly.ReflectionOnlyLoad(args.Name);
}
}

6. 为什么没有Assembly.UnLoad 方法?

以下是CLR 产品单元经理(Unit Manager) Jason Zander 文章中的内容的整理:

1) 为了保证 CLR 中代码所引用的代码地址都是有效的,必须跟踪诸如 GC 对象和 COM CCW 之类的特殊应用。否则会出现 Unload 一个 Assembly 后,还有 CLR 对象或 COM 组件使用到这个 Assembly 的代码或数据地址,进而导致访问异常。为了避免这种错误进行的跟踪,目前是在 AppDomain 一级进行的,如果要加入 Assembly.Unload 支持,则跟踪的粒度必须降到 Assembly 一级,这虽然在技术上不是不能实现,但代价太大了。   2) 如果支持 Assembly.Unload 则必须跟踪每个 Assembly 的代码使用到的句柄和对现有托管代码的引用。例如现在 JITer 在编译方法时,生成代码都在一个统一的区域,如果要支持卸载 Assembly 则必须对每个 Assembly 都进行独立编译。此外还有一些类似的资源使用问题,如果要分离跟踪技术上虽然可行,但代价较大,特别是在诸如 WinCE 这类资源有限的系统上问题比较明显。   3) CLR 中支持跨 AppDomain 的 Assembly 载入优化,也就是 domain neutral 的优化,使得多个 AppDomain 可以共享一份代码,加快载入速度。而目前 v1.0 和 v1.1 无法处理卸载 domain neutral 类型代码。这也导致实现 Assembly.Unload 完整语义的困难性。

详细请参考: http://www.cnblogs.com/ccBoy/archive/2004/07/13/23636.html

http://blogs.msdn.com/b/jasonz/archive/2004/05/31/145105.aspx

7. 需要牢记的经验

1) 只加载自己需要直接调用的程序集,不加载目标程序集内部引用的程序集和其他无关程序集。

2) 能使用RefelectionLoad 方法加载的程序集绝不要使用Assembly.Load 方法加载。

3) 一旦出现加载错误,不要显而易见认为是程序集不存在!要检查程序集加载缓存、是否出现同一程序集被不同应用程序域加载情况等。

至此,我们已经阐述了Assembly.Load 方法的一些特性,你已经了解它了吗?

参考链接:

http://msdn.microsoft.com/en-us/library/t07a3dye(v=vs.71).aspx

http://blogs.msdn.com/b/junfeng/archive/2004/11/03/252033.aspx

http://blogs.msdn.com/b/junfeng/archive/2004/08/24/219691.aspx

http://www.sosuo8.com/article/show.asp?id=2979

http://msdn.microsoft.com/en-us/library/ms404279.aspx

http://blog.csdn.net/xt_xiaotian/article/details/6362450

http://blogs.msdn.com/b/jasonz/archive/2004/05/31/145105.aspx

http://www.cnblogs.com/ccBoy/archive/2004/07/13/23636.html

http://www.cnblogs.com/wayfarer/archive/2004/09/29/47896.html

http://www.codeproject.com/Articles/42312/Loading-Assemblies-in-Separate-Directories-Into-a

http://msdn.microsoft.com/en-us/library/ms404312.aspx

Assembly.Load 详解(c#)的更多相关文章

  1. jQuery延迟加载插件(Lazy Load)详解

    最 新版本的Lazy Load并不能替代你的网页.即便你使用JavaScript移除了图片的src属性,有些现代的浏览器仍然会加载图片.现在你必须修改你的html代 码,使用占位图片作为img标签的s ...

  2. maven assembly 配置详解

    Maven Assembly插件介绍 博客分类: 项目构建   你是否想要创建一个包含脚本.配置文件以及所有运行时所依赖的元素(jar)Assembly插件能帮你构建一个完整的发布包. Assembl ...

  3. System.load 和 System.loadLibrary详解

    System.load 和 System.loadLibrary详解 1.它们都可以用来装载库文件,不论是JNI库文件还是非JNI库文件.在任何本地方法被调用之前必须先用这个两个方法之一把相应的JNI ...

  4. top命令输出解释以及load average 详解及排查思路

    原地址: https://blog.csdn.net/zhangchenglikecc/article/details/52103737 1.top输出以及load average 详解 昨天nagi ...

  5. [转]keil使用详解

    第一节 系统概述 Keil C51是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,与汇编相比,C语言在功能上.结构性.可读性.可维护性上有明显的优势,因而易学易用.用过 ...

  6. 【ORM】--FluentNHibernate之AutoMapping详解

           上篇文章详细讨论了FluentNHibernate的基本映射的使用方法,它的映射基本用法是跟NHibernate完全一样的,首先要创建数据库链接配置文件,然后编写Table的Mappin ...

  7. 【转】详解C#中的反射

    原帖链接点这里:详解C#中的反射   反射(Reflection) 2008年01月02日 星期三 11:21 两个现实中的例子: 1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内 ...

  8. WebsitePanel(wsp)配置详解(安装指南)

    WebsitePanel(wsp)配置详解(安装指南) 铁卫士原创 估计很多同学都还不知道WebsitePanel是什么东东吧,WebsitePanel简称wsp是微软旗下,开源免费的虚拟主机系统,我 ...

  9. mvn详解

    1.前言 Maven,发音是[`meivin],"专家"的意思.它是一个很好的项目管理工具,很早就进入了我的必备工具行列,但是这次为了把project1项目完全迁移并应用maven ...

随机推荐

  1. Json学习总结(2)——Java 下的 JSON库性能比较:JSON.simple vs. GSON vs. Jackson vs. JSONP

    JSON已经成为当前服务器与WEB应用之间数据传输的公认标准,不过正如许多我们所习以为常的事情一样,你会觉得这是理所当然的便不再深入思考了.我们很少会去想用到的这些JSON库到底有什么不同,但事实上它 ...

  2. SpringBoot之通过Maven将项目打包成ROOT.war-yellowcong

    在项目中,我们通过maven的插件,将项目达成war包,然后通过jenkins,自动化部署项目. 核心的maven配置文件,下面这一段pom.xml的配置文件. 将项目打包成ROOT.war < ...

  3. [PHP]怎样在SAE的CodeIgniter项目中隐藏掉index.php

    第一步:改动项目根文件夹的config.yaml文件.加入例如以下内容: handle: - rewrite: if(!is_dir() && !is_file() && ...

  4. tomcat和nginx相互结合的优化调整

    在工作中遇到这样的情况 Tomcat为后台 nginx为反向代理 需要往后台导入数据,由于处理时间过长,导致访问时出现504和500  通过修改tomcat中maxParameterCount=&qu ...

  5. tomcat:Could not publish to the server. java.lang.IndexOutOfBoundsException

    1.将工程加入到tomcat,报上述错误 2. run--maven build 报jar包错误: invalid LOC header (bad signature) 3.根据提示找到上述jar包, ...

  6. PostgreSQL Replication之第五章 设置同步复制(2)

    5.2 理解实际影响和性能 在本章中,我们已经讨论了实际影响以及性能影响.但是,有什么好的理论性的例子吗?让我们做一个简单的基准测试,看看复制是怎么做的.我们做这样的测试来为您显示各种耐久性的级别不只 ...

  7. 高手过愚人节 Manancher模板题_双倍经验

    Code: #include <cstdio> #include <algorithm> #include <cstring> #define setIO(s) f ...

  8. python读取word文档

    周末需要做一个统计word文档字数的问题,刚开始以为很简单,因为之前做过excel表格相关的任务,所以认为利用扩展模块应该比较简单. 通过搜索,确实搜到了一个python操作word的模块,pytho ...

  9. UVA-11134 Fabled Rooks 贪心问题(区间贪心)

    题目链接:https://cn.vjudge.net/problem/UVA-11134 题意 在 n*n 的棋盘上,放上 n 个车(ju).使得这 n 个车互相不攻击,即任意两个车不在同一行.同一列 ...

  10. linux Cenos-7.0下安装jdk搭建环境变量

    搭建linux系统的java环境本人目前所知有两种方式: 第一种是从oracle官网下载linux系统的jdk版本: 第二种是使用yum命令的方式下载安装,咱们今天讲的是第一种方式. 一.工具准备: ...