第一节:程序集加载

我们知道JIT编译器将方法的IL代码编译成本地代码时,会查看IL代码中引用了哪些类型。在运行时,JIT编译器利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所引用的类型。在AssemblyRef元数据表的记录项中,包含了构成程序集强名称的各个部分。JIT编译器获取所有这些部分,包括名称(无扩展名和路径)、版本、语言文化和公钥标记,并把它连接成一个字符串。然后,JIT编译器尝试将与该标识匹配的一个程序集加载到AppDomain中(如果还没有加载的话)。如果被加载的程序集时弱命名的,那么标识中只包含程序集的名称(不包含版本、语言文化以及公钥标记信息)。

在内部,CLR使用System.Reflection.Assembly类的静态方法Load来尝试加载这个程序集。这个方法在.NET Framework SDK文档中是公开的,可调用它显式的将一个程序集加载到AppDomain中。这个方法是CLR中的与Win 32 LoadLibrary函数等价的方法。Assembly的Load方法实际上有几个重载版本。以下是最常用的重载版本的原型:

public static Assembly Load(AssemblyName assemblyRef);

public static Assembly Load(string assemblyString);

在内部,Load导致CLR向程序集应用一个版本绑定重定向策略,并在GAC(全局程序集缓存)中查找程序集。如果没找到,就接着去应用程序的基目录、私有路径子目录和codebase位置查找。如果调用Load时,传递时一个弱命名程序集,Load就不会向程序集应用一个版本绑定重定向策略,CLR也不会去GAC中查找程序集。如果Load找到程序集,会返回对代表已加载的那个程序集的一个Assembly对象的引用。如果Load没有找到指定的程序集,会抛出一个System.IO.FileNotFoundException。

重要提示:一些开发人员可能注意到,System.AppDomain提供了一个Load方法。和Assembly的Load方法不同,AppDomain的Load是一个实例方法,它允许将一个程序集加载到一个指定的AppDomain中。该方法设计供非托管代码调用,允许宿主将一个程序集”注入”到一个AppDomain中。托管代码的开发人员一般情况下不应调用它,因为在调用AppDomain的Load方法时,需要向它传递一个标识了程序集的字符串,该方法随后会应用策略,并在一些常规位置搜索一些程序集。我们知道,AppDomain关联了一些告诉CLR如何查找程序集的设置。为了加载这个程序集,CLR将使用与指定AppDomain关联的设置,而不是与发出调用的那个AppDomain关联的设置。

然后,AppDomain的Load方法会返回对程序集的一个引用System.Reflection.Assembly不是从MarshalByRefObject派生的,所以程序集对象必须按值封送回发出调用的那个AppDomain.但是,现在CLR就会用发出调用的那个AppDomain的设置来定位并加载程序集。如果使用发出调用的那个AppDomain的策略和搜索位置没有找到指定的程序集,就会抛出一个FileNotFoundException。这个行为一般不是你所期望的,应该避免使用AppDomain的Load方法。

在大多数动态可扩展的应用程序中,Assembly的Load时程序集加载到AppDomain的首选方式。但是,它要求你实现掌握构成程序集标识的各个部分。开发人员经常要写一些工具和实用程序,以便在程序集上执行一些处理。这些工具的例子包含:ILDasm.exe,PEVerify.exe,CorFlags.exe,GACUtil.exe, Sgen.exe,SN.exe,XSD.exe等,所有这些工具都要获取引用一个程序集文件的路径名(包括文件扩展名)的命令行实参。为了以指定路径名的的方式加载一个程序集,要调用Assembly的LoadForm方法。在内部,LoadForm首先会调用System.Reflection.AssemblyName类的静态方法GetAssemblyName,该方法打开指定的文件,查找AssemblyRef元数据表的记录项,提取程序集标识信息,然后以一个System.Reflection.AssemblyName对象的形式返回这些信息(文件同时会关闭)。随后,LoadForm方法在内部调用Assembly的Load方法,将AssemblyName对象传给他。然后,CLR会应用版本绑定重定向策略,并在各个位置查找匹配的程序集,如果Load找到匹配的程序集,就会加载它,并返回代表已加载一个程序集的对象Assembly,LoadForm将返回这个值。如果Load没有找到匹配的程序集,LoadForm就会加载通过LoadForm实参传递的路径中的程序集。当然,如果以加载了一个具有相同标识的程序集,LoadFrom方法会简单的返回代表已加载程序集的一个Assembly对象。

VS的UI设计人员和其他工具一般用的是Assembly的LoadFile方法。这个方法可以从任何路径加载一个程序集,并可将具有相同标识的一个程序集多次加载到一个AppDomain中。在设计器/工具中对应用程序的UI进行了修改,CLR不会自动解析任何依懒性问题;你的代码必须向AppDomain的AssemblyResolve事件登记,并让事件回调方法显示地加载任何依赖的程序集。

如果你构建的一个工具只是通过反射来分析程序集的元数据,并希望确保程序集中的任何代码都不会执行,那么加载程序集的最佳方式就是使用Assembly的ReflectionOnlyLoadFrom 或者ReflectionOnlyLoad方法。

ReflectionOnlyLoadFrom方法将加载由路径指定的文件;文件的强名称标识不会获取,也不会在GAC和其他位置搜索。ReflectionOnlyLoad方法会在GAC、应用程序基目录、私有路径和codebase指定的位置搜索指定的程序集。但是,和Load方法不同的是,ReflectionOnlyLoad不会应用版本控制策略,所以你指定了哪个版本,获得的就是哪个版本。如果要自行为一个程序集标识指定版本控制策略,可以将字符串传给AppDomain的ApplyPolicy方法。

ReflectionOnlyLoadFrom 或者ReflectionOnlyLoad方法加载程序集时,CLR禁止程序集中的任何代码执行,试图执行由这两个方法加载的程序集中的代码,会导致CLR抛出一个异常,这两个方法允许工具加载延迟签名的程序集,这种程序集正常情况下会因为安全权限不够而无法加载。另外,这种程序集也可能是为不同的CPU架构而创建的。

利用反射来分析由这两个方法之一加载的程序集时,代码经常需要向AppDomain的ReflectionOnlyAssemblyResovle事件注册一个回调方法,以便手动加载任何引用的程序集;CLR不会自动帮你做这个事情,回调方法被调用时,它必须调用Assembly的ReflectionOnlyLoadFrom或ReflectionOnlyLoad方法,以便显示加载一个程序集,并返回对程序集的一个引用。

许多应用程序都由一个要依赖于众多DLL文件的EXE文件组成。部署这个应用程序时,所有文件都必须部署。然后,有一个技术允许只部署一个EXE文件。首先,标识出EXE文件要依赖的、同时不是作为.NET FRAMEWORK本身的一部分发布的所有DLL文件,将这些DLL添加到你的VS项目中,对于添加的这些DLL,有显示它的属性,并将它的“生成操作”更改为”嵌入的资源”。这回导致C#编译器将DLL文件嵌入EXE文件中,以后只需部署这个EXE文件即可。

在运行时,CLR会找不到依赖的DLL程序集,为了解决这个问题,应用程序初始化时,向AppDomain的ResolveAssembly事件登记一个回调方法,

            AppDomain.CurrentDomain.AssemblyResolve += (sender, arg) =>
            {
                String resourceName = "AssemblyLoadingAndReflection:" + new AssemblyName(arg.Name) + ".dll";
                using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                {
                    Byte[] assemblyData = new Byte[stream.Length];
                    stream.Read(assemblyData, 0, assemblyData.Length);
                    return Assembly.Load(assemblyData);
                }
            };

  

现在一个线程首次调用一个方法时,如果发现该方法引用了依赖的DLL文件中的一个类型,就会引发一个ResolveAssembly事件,而上诉回调代码会找到所需的嵌入的DLL资源,并调用Assembly的Load方法重载版本,从而加载所需的资源。

时间: 2024-10-13 00:59:34

第一节:程序集加载的相关文章

第23章 程序集加载和反射

程序集加载和反射,实现了在编译时对一个类型一无所知的情况下,如何在运行时发现类型的信息,创建类型的实例以及访问类型的成员.显现的功能以及效果是十分强大的,比如使用第三方提供的程序集,以及创建动态可扩展应用程序. 23.1 程序集加载 JIT编译器在将方法的IL代码编译成本地代码时,会查看IL代码中引用了哪些类型.在运行时,JIT编译器查看元数据表TypeRef和AssemblyRef来确定哪一个程序集定义了所引用的类型.在AssemblyRef元数据表的记录项中,包含了构成程序集强名称的各个部分

clr via c# 程序集加载和反射集(一)

1,程序集加载---弱的程序集可以加载强签名的程序集,但是不可相反.否则引用会报错!(但是,反射是没问题的) //获取当前类的Assembly Assembly.GetEntryAssembly() //通过Load方法加载程序集 Assembly.Load //通过LoadFrom加载指定路径名的程序集--可以时url对象. Assembly LoadFrom(string path) //只是反射,并确保程序集中的数据不被执行. ReflectionOnlyLoadFrom() Reflec

程序集加载与反射

目录 程序集加载 获取类型信息 构造类型实例 通过反射发现成员 调用成员 一.程序集加载 Load方法:CLR通过调用System.Rreflection.Assemblly类的静态方法来显示加载程序集. public static Assembly Load(AssemblyName assemblyRef); public static Assembly Load(string assemblyString); LoadFrom方法:同样我们可以使用 远程加载程序集.此方法首先打开程序集,并

程序集加载和反射

一.程序集加载 1,根据程序集名称查找程序集 public class Assembly { public static Assembly Load(AssemblyName assemblyRef); public static Assembly Load(string assemblyString); //未列出不常用的Load重载 } Assembly.Load("ConsoleApplication2");, 2,根据程序集文件路径名(包含扩展名)查找程序集 public cl

程序集加载与反射(二):实例篇

目录: 上篇:程序集加载与反射(一):基础篇 Demo:下载 一.Demo 下面这个Demo,使用了策略模式模仿了一下插件机制.我们举个一邮件发送的例子: 1.一个策略类库:Strategy,里面定义了邮件需要实现的接口:IEmailStrategy. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespa

CLR中的程序集加载

本次来讨论一下基于.net平台的CLR中的程序集加载的机制: [注:由于.net已经开源,可利用vs2015查看c#源码的具体实现] 在运行时,JIT编译器利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所引用的类型.在AssemblyRef元数据表的记录项中,包含构成程序集的强名称的各个部分.JIT编译器获取包括名称(无扩展名和路径).版本.语言文化和公钥标记,将这些连接成一个字符串.JIT编译器将该标识匹配的一个程序集加载到AppDomain中.] CLR内

第二十三章 程序集加载和反射

目录: 23.1 程序集加载 23.2 使用反射构建动态可扩展应用程序 23.3 反射的性能 23.4 设计支持加载项的应用程序 23.5 使用反射发现类型的成员 23.1 程序集加载 JIT编译器将方法的IL代码编译成本机代码时,会查看IL代码中引用了哪些类型.在运行时,JIT编译器利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所有引用的类型. 通过Assembly的Load方法加载程序集. 23.2 使用反射构建动态可扩展应用程序 23.3 反射的性能 2

clr via c# 程序集加载和反射(2)

查看,clr via c# 程序集加载和反射(1) 8,发现类型的成员: 字段,构造器,方法,属性,事件,嵌套类型都可以作为类型成员.其包含在抽象类MemberInfo中,封装了所有类型都有的一组属性. MemeberInfo的派生列表: System.Reflection.MemberInfo    System.Reflection.EventInFo System.Reflection.FieldInfo    System.Reflection.MethodBase          

程序集加载与反射(二):实战篇

目录: 上篇:http://www.cnblogs.com/sunchong/p/4550476.html Demo 一.Demo 下面这个Demo,使用了策略模式模仿了一下插件机制.我们举个一邮件发送的例子: 1.一个策略类库:Strategy,里面定义了邮件需要实现的接口:IEmailStrategy. using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sys