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>///<paramname="assemblyString">程序集的显示名称。</param><paramname="assemblySecurity">用于加载程序集的证据。</param> <exceptioncref="T:System.ArgumentNullException"><paramrefname="assemblyString"/>为null。</exception> <exceptioncref="T:System.IO.FileNotFoundException"><paramrefname="assemblyString"/>未找到。</exception> <exceptioncref="T:System.BadImageFormatException"><paramrefname="assemblyString"/>不是有效程序集。-或-当前加载的是2.0或更高版本的公共语言运行时,而<paramrefname="assemblyString"/>是用更高版本的公共语言运行时编译的。</exception> <exceptioncref="T:System.IO.FileLoadException">发现一个未能加载的文件。-或-用两个不同的证据将一个程序集或模块加载了两次。</exception> <PermissionSet><IPermissionclass="System.Security.Permissions.FileIOPermission,mscorlib,Version=2.0.3600.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"version="1"Read="*AllFiles*"PathDiscovery="*AllFiles*"/></PermissionSet>[SecuritySafeCritical][Obsolete("Thismethodisobsoleteandwillberemovedinafuturereleaseofthe.NETFramework.PleaseuseanoverloadofLoadwhichdoesnottakeanEvidenceparameter.Seehttp://go.microsoft.com/fwlink/?LinkID=155570formoreinformation.")][MethodImpl(MethodImplOptions.NoInlining)]publicstaticAssemblyLoad(stringassemblyString,EvidenceassemblySecurity){StackCrawlMarkstackMark=StackCrawlMark.LookForMyCaller;return(Assembly)RuntimeAssembly.InternalLoad(assemblyString,assemblySecurity,refstackMark,false);}///<summary>///通过给定程序集的长格式名称加载程序集。///</summary>//////<returns>///加载的程序集。///</returns>///<paramname="assemblyString">程序集名称的长格式。</param><exceptioncref="T:System.ArgumentNullException"><paramrefname="assemblyString"/>为null。</exception><exceptioncref="T:System.ArgumentException"><paramrefname="assemblyString"/>是零长度字符串。</exception><exceptioncref="T:System.IO.FileNotFoundException"><paramrefname="assemblyString"/>未找到。</exception><exceptioncref="T:System.IO.FileLoadException">发现一个未能加载的文件。</exception><exceptioncref="T:System.BadImageFormatException"><paramrefname="assemblyString"/>不是有效程序集。-或-当前加载的是2.0或更高版本的公共语言运行时,而<paramrefname="assemblyString"/>是用更高版本的公共语言运行时编译的。</exception><PermissionSet><IPermissionclass="System.Security.Permissions.FileIOPermission,mscorlib,Version=2.0.3600.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"version="1"Read="*AllFiles*"PathDiscovery="*AllFiles*"/></PermissionSet>[SecuritySafeCritical][__DynamicallyInvokable][MethodImpl(MethodImplOptions.NoInlining)]publicstaticAssemblyLoad(stringassemblyString){StackCrawlMarkstackMark=StackCrawlMark.LookForMyCaller;return(Assembly)RuntimeAssembly.InternalLoad(assemblyString,(Evidence)null,refstackMark,false);}
在内部,Load导致CLR向程序集应用一个版本绑定重定向策略,并在GAC(全局程序集缓存)中查找程序集。如果没有找到,就接着去应用程序的基目录、私有路径目录和codebase位置查找。如果Load找到指定的程序集,会返回对代表已加载的那个程序集的一个Assembly对象的引用。如果没有找到,则会抛出一个异常。
【注:System.AppDomain提供了一个Load方法,这与Assembly的静态Load方法不同,AppDoamin的Load是一个实例方法,它允许将一个程序集加载到一个指定的AppDoamin中,该方法设计供非托管代码调用,语序宿主将一个程序集“注入”一个特定的AppDoamin。】
2.采用Assembly的LoadFrom方法,指定路径名的方式加载程序集:
///<summary>///已知程序集的文件名或路径,加载程序集。///</summary>//////<returns>///加载的程序集。///</returns>///<paramname="assemblyFile">包含程序集清单的文件的名称或路径。</param><exceptioncref="T:System.ArgumentNullException"><paramrefname="assemblyFile"/>为null。</exception><exceptioncref="T:System.IO.FileNotFoundException">未找到<paramrefname="assemblyFile"/>,或者尝试加载的模块没有指定文件扩展名。</exception><exceptioncref="T:System.IO.FileLoadException">发现一个未能加载的文件。</exception><exceptioncref="T:System.BadImageFormatException"><paramrefname="assemblyFile"/>不是有效的程序集;例如,64位进程中的32位程序集。有关更多信息,请参见异常主题。-或-当前加载的是2.0或更高版本的公共语言运行时,而<paramrefname="assemblyFile"/>是用更高版本的公共语言运行时编译的。</exception><exceptioncref="T:System.Security.SecurityException">在没有所需<seecref="T:System.Net.WebPermission"/>的情况下,指定了不以“file://”开始的基本代码。</exception><exceptioncref="T:System.ArgumentException"><paramrefname="assemblyFile"/>参数是空字符串("")。</exception><exceptioncref="T:System.IO.PathTooLongException">程序集名称的长度大于MAX_PATH个字符。</exception><PermissionSet><IPermissionclass="System.Security.Permissions.FileIOPermission,mscorlib,Version=2.0.3600.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"version="1"Read="*AllFiles*"PathDiscovery="*AllFiles*"/></PermissionSet>[SecuritySafeCritical][MethodImpl(MethodImplOptions.NoInlining)]publicstaticAssemblyLoadFrom(stringassemblyFile){StackCrawlMarkstackMark=StackCrawlMark.LookForMyCaller;return(Assembly)RuntimeAssembly.InternalLoadFrom(assemblyFile,(Evidence)null,(byte[])null,AssemblyHashAlgorithm.None,false,false,refstackMark);}
(1).在内部,LoadFrom首先会调用Syatem.Reflection.AssemblyName类的静态方法GetAssemblyName。该方法打开指定的文件,查找AssemblyRef元数据表的记录项,提取程序集标识信息。
(2).以一个AssembleName对象的形式返回这些信息。
(3).LoadFrom方法内部调用Assembly的Load方法,将Assembly对象传递给他。
(4).CLR会为应用版本绑定重定向策略,并在各个位置查找匹配的程序集。
3.采用Assembly的LoadFile方法,这个方法可以从任意路径加载一个程序集,并可将具有相同标识的一个程序集多次加载到一个AppDoamin中。
///<summary>///加载指定路径上的程序集文件的内容。///</summary>//////<returns>///加载的程序集。///</returns>///<paramname="path">要加载的文件的完全限定路径。</param><exceptioncref="T:System.ArgumentException"><paramrefname="path"/>参数不是绝对路径。</exception><exceptioncref="T:System.ArgumentNullException"><paramrefname="path"/>参数为null。</exception><exceptioncref="T:System.IO.FileLoadException">发现一个未能加载的文件。</exception><exceptioncref="T:System.IO.FileNotFoundException"><paramrefname="path"/>参数为空字符串("")或不存在。</exception><exceptioncref="T:System.BadImageFormatException"><paramrefname="path"/>不是有效程序集。-或-当前加载的是2.0或更高版本的公共语言运行时,而<paramrefname="path"/>是用更高版本的公共语言运行时编译的。</exception><PermissionSet><IPermissionclass="System.Security.Permissions.FileIOPermission,mscorlib,Version=2.0.3600.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"version="1"Unrestricted="true"/><IPermissionclass="System.Security.Permissions.SecurityPermission,mscorlib,Version=2.0.3600.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"version="1"Flags="ControlEvidence"/></PermissionSet>[SecuritySafeCritical]publicstaticAssemblyLoadFile(stringpath){AppDomain.CheckLoadFileSupported();newFileIOPermission(FileIOPermissionAccess.Read|FileIOPermissionAccess.PathDiscovery,path).Demand();return(Assembly)RuntimeAssembly.nLoadFile(path,(Evidence)null);}///<summary>///通过给定的程序集的路径来加载程序集,使用提供的证据将程序集加载到调用方的域中。///</summary>//////<returns>///加载的程序集。///</returns>///<paramname="path">程序集文件的完全限定路径。</param><paramname="securityEvidence">用于加载程序集的证据。</param><exceptioncref="T:System.ArgumentException"><paramrefname="path"/>参数不是绝对路径。</exception><exceptioncref="T:System.ArgumentNullException"><paramrefname="path"/>参数为null。</exception><exceptioncref="T:System.IO.FileNotFoundException"><paramrefname="path"/>参数为空字符串("")或不存在。</exception><exceptioncref="T:System.IO.FileLoadException">发现一个未能加载的文件。</exception><exceptioncref="T:System.BadImageFormatException"><paramrefname="path"/>不是有效程序集。-或-当前加载的是2.0或更高版本的公共语言运行时,而<paramrefname="path"/>是用更高版本的公共语言运行时编译的。</exception><exceptioncref="T:System.NotSupportedException"><paramrefname="securityEvidence"/>不是null。默认情况下,旧的CAS策略中未启用.NETFramework4;如果未启用),<paramrefname="securityEvidence"/>必须是null。</exception><PermissionSet><IPermissionclass="System.Security.Permissions.FileIOPermission,mscorlib,Version=2.0.3600.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"version="1"Unrestricted="true"/><IPermissionclass="System.Security.Permissions.SecurityPermission,mscorlib,Version=2.0.3600.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"version="1"Flags="ControlEvidence"/></PermissionSet>[SecuritySafeCritical][Obsolete("Thismethodisobsoleteandwillberemovedinafuturereleaseofthe.NETFramework.PleaseuseanoverloadofLoadFilewhichdoesnottakeanEvidenceparameter.Seehttp://go.microsoft.com/fwlink/?LinkID=155570formoreinformation.")][SecurityPermission(SecurityAction.Demand,Flags=SecurityPermissionFlag.ControlEvidence)]publicstaticAssemblyLoadFile(stringpath,EvidencesecurityEvidence){AppDomain.CheckLoadFileSupported();if(securityEvidence!=null&&!AppDomain.CurrentDomain.IsLegacyCasPolicyEnabled)thrownewNotSupportedException(Environment.GetResourceString("NotSupported_RequiresCasPolicyImplicit"));newFileIOPermission(FileIOPermissionAccess.Read|FileIOPermissionAccess.PathDiscovery,path).Demand();return(Assembly)RuntimeAssembly.nLoadFile(path,securityEvidence);}
通过LoadFile加载程序集时,CLR不会自动解析任何依赖性问题,代码必须向AppDomain的AssemblyReaolve事件登记,并让事件回调方法显示的加载依赖的程序集。
4.如果需要构建的一个工具只是通过反射来分析程序集的元数据,并希望确保程序集中的任何代码都不会执行,那么程序集的最佳方式就是使用Assembly的ReflectionOnlyLoadFrom方法或者使用ReflectionOnlyLoad方法。
///<summary>///将给定显示名称的程序集加载到只反射上下文中。///</summary>//////<returns>///加载的程序集。///</returns>///<paramname="assemblyString">程序集的显示名称,由<seecref="P:System.Reflection.AssemblyName.FullName"/>属性返回。</param><exceptioncref="T:System.ArgumentNullException"><paramrefname="assemblyString"/>为null。</exception><exceptioncref="T:System.ArgumentException"><paramrefname="assemblyString"/>为空字符串("")。</exception><exceptioncref="T:System.IO.FileNotFoundException"><paramrefname="assemblyString"/>未找到。</exception><exceptioncref="T:System.IO.FileLoadException"><paramrefname="assemblyString"/>已找到,但是不能加载。</exception><exceptioncref="T:System.BadImageFormatException"><paramrefname="assemblyString"/>不是有效程序集。-或-当前加载的是2.0或更高版本的公共语言运行时,而<paramrefname="assemblyString"/>是用更高版本的公共语言运行时编译的。</exception><PermissionSet><IPermissionclass="System.Security.Permissions.FileIOPermission,mscorlib,Version=2.0.3600.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"version="1"Read="*AllFiles*"PathDiscovery="*AllFiles*"/></PermissionSet>[SecuritySafeCritical][MethodImpl(MethodImplOptions.NoInlining)]publicstaticAssemblyReflectionOnlyLoad(stringassemblyString){StackCrawlMarkstackMark=StackCrawlMark.LookForMyCaller;return(Assembly)RuntimeAssembly.InternalLoad(assemblyString,(Evidence)null,refstackMark,true);}///<summary>///将给定路径的程序集加载到只反射上下文中。///</summary>//////<returns>///加载的程序集。///</returns>///<paramname="assemblyFile">包含程序集清单的文件的路径。</param><exceptioncref="T:System.ArgumentNullException"><paramrefname="assemblyFile"/>为null。</exception><exceptioncref="T:System.IO.FileNotFoundException">未找到<paramrefname="assemblyFile"/>,或者尝试加载的模块没有指定文件扩展名。</exception><exceptioncref="T:System.IO.FileLoadException"><paramrefname="assemblyFile"/>已找到,但是未能加载。</exception><exceptioncref="T:System.BadImageFormatException"><paramrefname="assemblyFile"/>不是有效程序集。-或-当前加载的是2.0或更高版本的公共语言运行时,而<paramrefname="assemblyFile"/>是用更高版本的公共语言运行时编译的。</exception><exceptioncref="T:System.Security.SecurityException">在没有所需<seecref="T:System.Net.WebPermission"/>的情况下,指定了不以“file://”开始的基本代码。</exception><exceptioncref="T:System.IO.PathTooLongException">程序集名称的长度大于MAX_PATH个字符。</exception><exceptioncref="T:System.ArgumentException"><paramrefname="assemblyFile"/>为空字符串("")。</exception><PermissionSet><IPermissionclass="System.Security.Permissions.FileIOPermission,mscorlib,Version=2.0.3600.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"version="1"Read="*AllFiles*"PathDiscovery="*AllFiles*"/></PermissionSet>[SecuritySafeCritical][MethodImpl(MethodImplOptions.NoInlining)]publicstaticAssemblyReflectionOnlyLoadFrom(stringassemblyFile){StackCrawlMarkstackMark=StackCrawlMark.LookForMyCaller;return(Assembly)RuntimeAssembly.InternalLoadFrom(assemblyFile,(Evidence)null,(byte[])null,AssemblyHashAlgorithm.None,true,false,refstackMark);}
ReflectionOnlyLoadFrom方法加载有路径指定的文件,文件的强名称标识不会获取,也不会在GAC和其他位置搜索文件。ReflectionOnlyLoad方法会在GAC、应用程序基目录、私有路径和codebase指定的位置搜索指定的程序集,该方法不会应用版本控制策略,因此在指定的是那个版本,获取的就是那个版本。如果要自行为一个程序集标识指定版本控制策略,可将字符串传给AppDoamin的ApplyPolicy方法。
用ReflectionOnlyLoadFrom或ReflectionOnlyLoad方法加载程序集时,CLR禁止程序集中的任何代码执行,如果试图执行,则会抛出异常。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。