CLR中的程序集加载
CLR中的程序集加载
本次来讨论一下基于.net平台的CLR中的程序集加载的机制:
【注:由于.net已经开源,可利用vs2015查看c#源码的具体实现】
在运行时,JIT编译器利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所引用的类型。在AssemblyRef元数据表的记录项中,包含构成程序集的强名称的各个部分。JIT编译器获取包括名称(无扩展名和路径)、版本、语言文化和公钥标记,将这些连接成一个字符串。JIT编译器将该标识匹配的一个程序集加载到AppDomain中。】
CLR内部加载程序集提供了4中方法,在System.Refleetion.Assembly类中:
1. 采用静态方法Load()加载程序集,可调用它显示的将一个程序集加载到AppDomain中:
【注:Assembly类的Load()存在两个重载版本】
/// <summary> /// 通过给定的程序集的显示名称来加载程序集,使用提供的证据将程序集加载到调用方的域中。 /// </summary> /// /// <returns> /// 加载的程序集。 /// </returns> /// <param name="assemblyString">程序集的显示名称。</param> <param name="assemblySecurity">用于加载程序集的证据。</param> <exception cref="T:System.ArgumentNullException"><paramref name="assemblyString"/> 为 null。</exception> <exception cref="T:System.IO.FileNotFoundException"><paramref name="assemblyString"/> 未找到。</exception> <exception cref="T:System.BadImageFormatException"><paramref name="assemblyString"/> 不是有效程序集。-或 -当前加载的是 2.0 或更高版本的公共语言运行时,而<paramref name="assemblyString"/> 是用更高版本的公共语言运行时编译的。</exception> <exception cref="T:System.IO.FileLoadException">发现一个未能加载的文件。- 或 -用两个不同的证据将一个程序集或模块加载了两次。</exception> <PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="" Read="*AllFiles*" PathDiscovery="*AllFiles*"/></PermissionSet> [SecuritySafeCritical] [Obsolete("This method is obsolete and will be removed in a future release of the .NET Framework. Please use an overload of Load which does not take an Evidence parameter. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.")] [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly Load(string assemblyString, Evidence assemblySecurity) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return (Assembly) RuntimeAssembly.InternalLoad(assemblyString, assemblySecurity, ref stackMark, false); } /// <summary> /// 通过给定程序集的长格式名称加载程序集。 /// </summary> /// /// <returns> /// 加载的程序集。 /// </returns> /// <param name="assemblyString">程序集名称的长格式。</param> <exception cref="T:System.ArgumentNullException"><paramref name="assemblyString"/> 为 null。</exception> <exception cref="T:System.ArgumentException"><paramref name="assemblyString"/> 是零长度字符串。</exception> <exception cref="T:System.IO.FileNotFoundException"><paramref name="assemblyString"/> 未找到。</exception> <exception cref="T:System.IO.FileLoadException">发现一个未能加载的文件。</exception> <exception cref="T:System.BadImageFormatException"><paramref name="assemblyString"/> 不是有效程序集。- 或 -当前加载的是 2.0 或更高版本的公共语言运行时,而<paramref name="assemblyString"/> 是用更高版本的公共语言运行时编译的。</exception> <PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="" Read="*AllFiles*" PathDiscovery="*AllFiles*"/></PermissionSet> [SecuritySafeCritical] [__DynamicallyInvokable] [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly Load(string assemblyString) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return (Assembly) RuntimeAssembly.InternalLoad(assemblyString, (Evidence) null, ref stackMark, false); }
在内部,Load导致CLR向程序集应用一个版本绑定重定向策略,并在GAC(全局程序集缓存)中查找程序集。如果没有找到,就接着去应用程序的基目录、私有路径目录和codebase位置查找。如果Load找到指定的程序集,会返回对代表已加载的那个程序集的一个Assembly对象的引用。如果没有找到,则会抛出一个异常。
【注:System.AppDomain提供了一个Load方法,这与Assembly的静态Load方法不同,AppDoamin的Load是一个实例方法,它允许将一个程序集加载到一个指定的AppDoamin中,该方法设计供非托管代码调用,语序宿主将一个程序集“注入”一个特定的AppDoamin。】
2.采用Assembly的LoadFrom方法,指定路径名的方式加载程序集:
/// <summary> /// 已知程序集的文件名或路径,加载程序集。 /// </summary> /// /// <returns> /// 加载的程序集。 /// </returns> /// <param name="assemblyFile">包含程序集清单的文件的名称或路径。</param> <exception cref="T:System.ArgumentNullException"><paramref name="assemblyFile"/> 为 null。</exception> <exception cref="T:System.IO.FileNotFoundException">未找到<paramref name="assemblyFile"/>,或者尝试加载的模块没有指定文件扩展名。</exception> <exception cref="T:System.IO.FileLoadException">发现一个未能加载的文件。</exception> <exception cref="T:System.BadImageFormatException"><paramref name="assemblyFile"/> 不是有效的程序集;例如, 位进程中的 位程序集。有关更多信息,请参见异常主题。- 或 -当前加载的是 2.0 或更高版本的公共语言运行时,而<paramref name="assemblyFile"/> 是用更高版本的公共语言运行时编译的。</exception> <exception cref="T:System.Security.SecurityException">在没有所需<see cref="T:System.Net.WebPermission"/> 的情况下,指定了不以“file://”开始的基本代码。</exception> <exception cref="T:System.ArgumentException"><paramref name="assemblyFile"/> 参数是空字符串 ("")。</exception> <exception cref="T:System.IO.PathTooLongException">程序集名称的长度大于 MAX_PATH 个字符。</exception> <PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="" Read="*AllFiles*" PathDiscovery="*AllFiles*"/></PermissionSet> [SecuritySafeCritical] [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly LoadFrom(string assemblyFile) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return (Assembly) RuntimeAssembly.InternalLoadFrom(assemblyFile, (Evidence) null, (byte[]) null, AssemblyHashAlgorithm.None, false, false, ref stackMark); }
(1).在内部,LoadFrom首先会调用Syatem.Reflection.AssemblyName类的静态方法GetAssemblyName。该方法打开指定的文件,查找AssemblyRef元数据表的记录项,提取程序集标识信息。
(2).以一个AssembleName对象的形式返回这些信息。
(3).LoadFrom方法内部调用Assembly的Load方法,将Assembly对象传递给他。
(4).CLR会为应用版本绑定重定向策略,并在各个位置查找匹配的程序集。
3.采用Assembly的LoadFile方法,这个方法可以从任意路径加载一个程序集,并可将具有相同标识的一个程序集多次加载到一个AppDoamin中。
/// <summary> /// 加载指定路径上的程序集文件的内容。 /// </summary> /// /// <returns> /// 加载的程序集。 /// </returns> /// <param name="path">要加载的文件的完全限定路径。</param> <exception cref="T:System.ArgumentException"><paramref name="path"/> 参数不是绝对路径。</exception> <exception cref="T:System.ArgumentNullException"><paramref name="path"/> 参数为 null。</exception><exception cref="T:System.IO.FileLoadException">发现一个未能加载的文件。</exception> <exception cref="T:System.IO.FileNotFoundException"><paramref name="path"/> 参数为空字符串 ("") 或不存在。</exception> <exception cref="T:System.BadImageFormatException"><paramref name="path"/> 不是有效程序集。- 或 -当前加载的是 2.0 或更高版本的公共语言运行时,而 <paramref name="path"/> 是用更高版本的公共语言运行时编译的。</exception> <PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="" Unrestricted="true"/> <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="" Flags="ControlEvidence"/></PermissionSet> [SecuritySafeCritical] public static Assembly LoadFile(string path) { AppDomain.CheckLoadFileSupported(); new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, path).Demand(); return (Assembly) RuntimeAssembly.nLoadFile(path, (Evidence) null); } /// <summary> /// 通过给定的程序集的路径来加载程序集,使用提供的证据将程序集加载到调用方的域中。 /// </summary> /// /// <returns> /// 加载的程序集。 /// </returns> /// <param name="path">程序集文件的完全限定路径。</param> <param name="securityEvidence">用于加载程序集的证据。</param> <exception cref="T:System.ArgumentException"><paramref name="path"/> 参数不是绝对路径。</exception> <exception cref="T:System.ArgumentNullException"><paramref name="path"/> 参数为 null。</exception> <exception cref="T:System.IO.FileNotFoundException"><paramref name="path"/> 参数为空字符串 ("") 或不存在。</exception> <exception cref="T:System.IO.FileLoadException">发现一个未能加载的文件。</exception> <exception cref="T:System.BadImageFormatException"><paramref name="path"/> 不是有效程序集。- 或 -当前加载的是 2.0 或更高版本的公共语言运行时,而 <paramref name="path"/> 是用更高版本的公共语言运行时编译的。</exception> <exception cref="T:System.NotSupportedException"><paramref name="securityEvidence"/> 不是 null。默认情况下,旧的 CAS 策略中未启用 .NET Framework ; 如果未启用), <paramref name="securityEvidence"/> 必须是 null。</exception> <PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="" Unrestricted="true"/> <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="" Flags="ControlEvidence"/></PermissionSet> [SecuritySafeCritical] [Obsolete("This method is obsolete and will be removed in a future release of the .NET Framework. Please use an overload of LoadFile which does not take an Evidence parameter. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.")] [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlEvidence)] public static Assembly LoadFile(string path, Evidence securityEvidence) { AppDomain.CheckLoadFileSupported(); if (securityEvidence != null && !AppDomain.CurrentDomain.IsLegacyCasPolicyEnabled) throw new NotSupportedException(Environment.GetResourceString("NotSupported_RequiresCasPolicyImplicit")); new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, path).Demand(); return (Assembly) RuntimeAssembly.nLoadFile(path, securityEvidence); }
通过LoadFile加载程序集时,CLR不会自动解析任何依赖性问题,代码必须向AppDomain的AssemblyReaolve事件登记,并让事件回调方法显示的加载依赖的程序集。
4.如果需要构建的一个工具只是通过反射来分析程序集的元数据,并希望确保程序集中的任何代码都不会执行,那么程序集的最佳方式就是使用Assembly的ReflectionOnlyLoadFrom方法或者使用ReflectionOnlyLoad方法。
/// <summary> /// 将给定显示名称的程序集加载到只反射上下文中。 /// </summary> /// /// <returns> /// 加载的程序集。 /// </returns> /// <param name="assemblyString">程序集的显示名称,由 <see cref="P:System.Reflection.AssemblyName.FullName"/> 属性返回。</param> <exception cref="T:System.ArgumentNullException"><paramref name="assemblyString"/> 为 null。</exception> <exception cref="T:System.ArgumentException"><paramref name="assemblyString"/> 为空字符串 ("")。</exception> <exception cref="T:System.IO.FileNotFoundException"><paramref name="assemblyString"/> 未找到。</exception> <exception cref="T:System.IO.FileLoadException"><paramref name="assemblyString"/> 已找到,但是不能加载。</exception> <exception cref="T:System.BadImageFormatException"><paramref name="assemblyString"/> 不是有效程序集。- 或 -当前加载的是 2.0 或更高版本的公共语言运行时,而<paramref name="assemblyString"/> 是用更高版本的公共语言运行时编译的。</exception> <PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="" Read="*AllFiles*" PathDiscovery="*AllFiles*"/></PermissionSet> [SecuritySafeCritical] [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly ReflectionOnlyLoad(string assemblyString) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return (Assembly) RuntimeAssembly.InternalLoad(assemblyString, (Evidence) null, ref stackMark, true); } /// <summary> /// 将给定路径的程序集加载到只反射上下文中。 /// </summary> /// /// <returns> /// 加载的程序集。 /// </returns> /// <param name="assemblyFile">包含程序集清单的文件的路径。</param> <exception cref="T:System.ArgumentNullException"><paramref name="assemblyFile"/> 为 null。</exception> <exception cref="T:System.IO.FileNotFoundException">未找到 <paramref name="assemblyFile"/>,或者尝试加载的模块没有指定文件扩展名。</exception> <exception cref="T:System.IO.FileLoadException"><paramref name="assemblyFile"/> 已找到,但是未能加载。</exception> <exception cref="T:System.BadImageFormatException"><paramref name="assemblyFile"/> 不是有效程序集。- 或 -当前加载的是 2.0 或更高版本的公共语言运行时,而<paramref name="assemblyFile"/> 是用更高版本的公共语言运行时编译的。</exception> <exception cref="T:System.Security.SecurityException">在没有所需 <see cref="T:System.Net.WebPermission"/> 的情况下,指定了不以“file://”开始的基本代码。</exception> <exception cref="T:System.IO.PathTooLongException">程序集名称的长度大于 MAX_PATH 个字符。</exception> <exception cref="T:System.ArgumentException"><paramref name="assemblyFile"/> 为空字符串 ("")。</exception> <PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="" Read="*AllFiles*" PathDiscovery="*AllFiles*"/></PermissionSet> [SecuritySafeCritical] [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly ReflectionOnlyLoadFrom(string assemblyFile) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return (Assembly) RuntimeAssembly.InternalLoadFrom(assemblyFile, (Evidence) null, (byte[]) null, AssemblyHashAlgorithm.None, true, false, ref stackMark); }
ReflectionOnlyLoadFrom方法加载有路径指定的文件,文件的强名称标识不会获取,也不会在GAC和其他位置搜索文件。ReflectionOnlyLoad方法会在GAC、应用程序基目录、私有路径和codebase指定的位置搜索指定的程序集,该方法不会应用版本控制策略,因此在指定的是那个版本,获取的就是那个版本。如果要自行为一个程序集标识指定版本控制策略,可将字符串传给AppDoamin的ApplyPolicy方法。
用ReflectionOnlyLoadFrom或ReflectionOnlyLoad方法加载程序集时,CLR禁止程序集中的任何代码执行,如果试图执行,则会抛出异常。
CLR中的程序集加载的更多相关文章
- clr via c# 程序集加载和反射(2)
查看,clr via c# 程序集加载和反射(1) 8,发现类型的成员: 字段,构造器,方法,属性,事件,嵌套类型都可以作为类型成员.其包含在抽象类MemberInfo中,封装了所有类型都有的一组属性 ...
- clr via c# 程序集加载和反射集(一)
1,程序集加载---弱的程序集可以加载强签名的程序集,但是不可相反.否则引用会报错!(但是,反射是没问题的) //获取当前类的Assembly Assembly.GetEntryAssembly() ...
- 【CLR】详解CLR中的程序集
目录结构: contents structure [+] 程序集的简介 为程序集分配强名称 如何指定程序集的版本资源信息 如何对程序集签名 全局程序集缓存 如何查看程序集的信息 强命名程序集防串改 1 ...
- 重温CLR(十七)程序集加载和反射
本章主要讨论在编译时对一个类型一无所知的情况下,如何在运行时发现类型的信息.创建类型的实例以及访问类型的成员.可利用本章讲述的内容创建动态可扩展应用程序. 反射使用的典型场景一般是由一家公司创建宿主应 ...
- 【C#进阶系列】23 程序集加载和反射
程序集加载 程序集加载,CLR使用System.Reflection.Assembly.Load静态方法,当然这个方法我们自己也可以显式调用. 还有一个Assembly.LoadFrom方法加载指定路 ...
- .NET 的程序集加载上下文
原文:.NET 的程序集加载上下文 我们编写的 .NET 应用程序会使用到各种各样的依赖库.我们都知道 CLR 会在一些路径下帮助我们程序找到依赖,但如果我们需要手动控制程序集加载路径的话,需要了解程 ...
- 关于asp.net中页面事件加载的先后顺序
一.ASP.NET 母版页和内容页中的事件 母版页和内容页都可以包含控件的事件处理程序.对于控件而言,事件是在本地处理的,即内容页中的控件在内容页中引发事件,母版页中的控件在母版页中引发事件.控件事件 ...
- Android什么时候进行View中Background的加载
对大多数Android的开发者来说,最经常的操作莫过于对界面进行布局,View中背景图片的加载是最经常做的.但是我们很少关注这个过程,这篇文章主要解析view中背景图片加载的流程.了解view中背景图 ...
- Java--自定义Class并且在内存中编译,加载,实例化
本文的目的: 使用者在程序运行期间,可以动态的写Java Class,不需要生成任何.Class文件就可以完全在内存中编译,加载,实例化. 1.需要用到的组件介绍 1)JavaCompiler:用于编 ...
随机推荐
- jGestures: jQuery的手势事件插件
官网地址:http://jgestures.codeplex.com/文档版本号: v0.7,由neuedigitale编辑,2012年5月8日最新稳定版: jGestures v0.90 - sha ...
- "SQL Server does not handle comparison of NText, Text, Xml, or Image data types."
"SQL Server does not handle comparison of NText, Text, Xml, or Image data types." sql2000 ...
- C#_基础,初始化器
对象初始化器 在没有对象初始化器之前,我们创建一个对象大概需要经过这么两个步骤,首先new一个对象,然后给每个字段赋值.而有了对象初始化器之后,原本需要几行代码才能完成的任务变成一行代码就可以完成,简 ...
- 多线程中使用CheckForIllegalCrossThreadCalls = false访问窗口-转
在多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,如果需要访问窗口中的控件,可以在窗口构造函数中将CheckForIllegalCrossThreadCalls设置为 false publi ...
- 你必须知道的Javascript 系列
JavaScript是见过最多人说它“有趣”,“好玩”的一门语言.不仅仅是因为它的灵活性,包括它本身很多的特性,比如说原型链,作用域链都是非常好玩的东西.现在已经有很多的JavaScript设计模式, ...
- Hadoop学习笔记—1.基本介绍与环境配置
一.Hadoop的发展历史 说到Hadoop的起源,不得不说到一个传奇的IT公司—全球IT技术的引领者Google.Google(自称)为云计算概念的提出者,在自身多年的搜索引擎业务中构建了突破性的G ...
- 细说 Data URI
Data URL 早在 1995 年就被提出,那个时候有很多个版本的 Data URL Schema 定义陆续出现在 VRML 之中,随后不久,其中的一个版本被提上了议案——将它做个一个嵌入式的资源放 ...
- Repository 仓储,你的归宿究竟在哪?(二)-这样的应用层代码,你能接受吗?
写在前面 关于"Repository 仓储,你的归宿究竟在哪?"这个系列,本来是想写个上下篇,但是现在觉得,很有多东西需要明确,我也不知道接下来会写多少篇,所以上一篇的标题就改成了 ...
- Mac下安装与配置Go语言开发环境
1.官网下载安装包(需FQ) https://storage.googleapis.com/golang/go1.7.darwin-amd64.pkg 2.配置Go环境变量GOPATH和GOBIN ( ...
- 每天一个linux命令(46):vmstat命令
vmstat是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存.进程.CPU活动进行监控.他是对系统的整体情况进行统计,不足之处是无法对某个进程进行深 ...