DotNet程序集解析
在.NET Framework框架中,程序集是重用、安全性以及版本控制的最小单元。程序集的定义为:程序集是一个或多个类型定义文件及资源文件的集合。程序集主要包含:PE/COFF,CLR头,元数据,清单,CIL代码,元数据。
PE/COFF文件是由工具生成的,表示文件的逻辑分组。PE文件包含“清单”数据块,清单是由元数据表构成的另一种集合,这些表描述了构成程序集的文件,由程序集中的文件实现的公开导出的类型,以及与程序集关联在一起的资源或数据文件。
在托管程序集中包含元数据和IL(微软的一种中间语言),IL能够访问和操作对象类型,并提供了指令来创建和初始化对象、调用对象上的虚方法以及直接操作数组元素。
CLR头是一个小的信息块,主要包含模块在生成是所面向的CLR的major(主)和major(次)版本号;一个标志,一个MethodDef token(指定了模块的入口方法);一个可选的强名称数字签名。
元数据表示一个二进制数据块,由几个表构成:定义表,引用表,清单表。
以上是对程序集的构成做了一个简单的说明,接下来看一下程序集的一些特性:程序集定义了可重用的类型;程序集标记了一个版本号;程序集可以有关联的安全信息。
在程序运行时,JIT编译器利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所引用的类型。JIT编译器在运行时需要获取程序集的相关信息,主要包括:名称、版本、语言文化、公钥标记等,并将这些连接为一个字符串。JIT编译器会差查找该标识的程序集,如果查询到,则将该程序集加载到AppDomain。
接下来介绍一下在CLR中加载程序集的方法:
在System.Refection.Assembly类的静态方法Load来加载程序集,在加载指定程序集的操作中,会使用LoadFrom()方法,LoadFrom()具有多个重载版本,看一下LoadFrom这个方法的底层实现代码:
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
[MethodImplAttribute(MethodImplOptions.NoInlining)]
public static Assembly LoadFrom(String assemblyFile)
{
Contract.Ensures(Contract.Result<Assembly>() != null);
Contract.Ensures(!Contract.Result<Assembly>().ReflectionOnly); StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return RuntimeAssembly.InternalLoadFrom(
assemblyFile,
null, // securityEvidence
null, // hashValue
AssemblyHashAlgorithm.None,
false,// forIntrospection
false,// suppressSecurityChecks
ref stackMark);
}
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
internal static RuntimeAssembly InternalLoadFrom(String assemblyFile,
Evidence securityEvidence,
byte[] hashValue,
AssemblyHashAlgorithm hashAlgorithm,
bool forIntrospection,
bool suppressSecurityChecks,
ref StackCrawlMark stackMark)
{
if (assemblyFile == null)
throw new ArgumentNullException("assemblyFile"); Contract.EndContractBlock(); #if FEATURE_CAS_POLICY
if (securityEvidence != null && !AppDomain.CurrentDomain.IsLegacyCasPolicyEnabled)
{
throw new NotSupportedException(Environment.GetResourceString("NotSupported_RequiresCasPolicyImplicit"));
}
#endif // FEATURE_CAS_POLICY
AssemblyName an = new AssemblyName();
an.CodeBase = assemblyFile;
an.SetHashControl(hashValue, hashAlgorithm);
// The stack mark is used for MDA filtering
return InternalLoadAssemblyName(an, securityEvidence, null, ref stackMark, true /*thrownOnFileNotFound*/, forIntrospection, suppressSecurityChecks);
}
在加载程序集的操作中,LoadFrom首先会调用Syatem.Reflection.AssemblyName类的静态方法GetAssemblyName(该方法打开指定文件,查找AssemblyRef元数据表的记录项,提取程序集标识信息,然后以一个Syatem.Reflection.AssemblyName对象的形式返回这些信息),LoadFrom方法在内部调用Assembly的Load方法,将AssemblyName对象传给它,CLR会为应用版本绑定重定向策略,并在各个位置查找匹配的程序集。如果Load找到匹配的程序集,就会加载它,并返回代表已加载程序集的一个Assembly对象,LoadFrom方法将返回这个值。
加载程序的另一个方法为LoadFile,这个方法可从任意路径加载一个程序集,并可将具有相同标识的一个程序集多次加载到一个AppDoamin中。接下来可以看一下LoadFile的底层实现代码:
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static Assembly LoadFile(String path)
{ Contract.Ensures(Contract.Result<Assembly>() != null);
Contract.Ensures(!Contract.Result<Assembly>().ReflectionOnly); AppDomain.CheckLoadFileSupported(); new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, path).Demand();
return RuntimeAssembly.nLoadFile(path, null);
}
以上对程序集的结构和程序集的加载方法做了一个简单的说明,需要说明的一点是:程序集不提供卸载的功能。
以下提供几种较为常用的程序集操作方法:
1.公共属性和方法:
public static int Minutes = ;
public static int Hour = * ;
public static int Day = * * ;
private readonly int _time;
private bool IsCache { get { return _time > ; } } /// <summary>
/// 缓存时间,0为不缓存(默认值:0秒,单位:秒)
/// </summary>
public ReflectionSugar(int time = )
{
_time = time;
} /// <summary>
/// 根据程序集路径和名称获取key
/// </summary>
/// <param name="keyElementArray"></param>
/// <returns></returns>
private string GetKey(params string[] keyElementArray)
{
return string.Join("", keyElementArray);
} /// <summary>
/// key是否存在
/// </summary>
/// <param name="key">key</param>
/// <returns>存在<c>true</c> 不存在<c>false</c>. </returns>
private bool ContainsKey(string key)
{
return HttpRuntime.Cache[key] != null;
} /// <summary>
///获取Cache根据key
/// </summary>
private V Get<V>(string key)
{
return (V)HttpRuntime.Cache[key];
} /// <summary>
/// 插入缓存.
/// </summary>
/// <param name="key">key</param>
/// <param name="value">value</param>
/// <param name="cacheDurationInSeconds">过期时间单位秒</param>
/// <param name="priority">缓存项属性</param>
private void Add<TV>(string key, TV value, int cacheDurationInSeconds, CacheItemPriority priority = CacheItemPriority.Default)
{
string keyString = key;
HttpRuntime.Cache.Insert(keyString, value, null, DateTime.Now.AddSeconds(cacheDurationInSeconds), Cache.NoSlidingExpiration, priority, null);
}
2.加载程序集:
/// <summary>
/// 加载程序集
/// </summary>
/// <param name="path">程序集路径</param>
/// <returns></returns>
public Assembly LoadFile(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(path);
}
try
{
var key = GetKey("LoadFile", path);
if (IsCache)
{
if (ContainsKey(key))
{
return Get<Assembly>(key);
}
} var asm = Assembly.LoadFile(path);
if (IsCache)
{
Add(key, asm, _time);
} return asm;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
3.根据程序集获取类型:
/// <summary>
/// 根据程序集获取类型
/// </summary>
/// <param name="asm">Assembly对象</param>
/// <param name="nameSpace">命名空间</param>
/// <param name="className">类名</param>
/// <returns>程序集类型</returns>
public Type GetTypeByAssembly(Assembly asm, string nameSpace, string className)
{
try
{
var key = GetKey("GetTypeByAssembly", nameSpace, className);
if (IsCache)
{
if (ContainsKey(key))
{
return Get<Type>(key);
}
} Type type = asm.GetType(nameSpace + "." + className);
if (IsCache)
{
Add(key, type, _time);
}
return type;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
4. 创建对象实例:
/// <summary>
/// 创建对象实例
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fullName">命名空间.类型名</param>
/// <param name="assemblyName">程序集(dll名称)</param>
/// <returns></returns>
public T CreateInstance<T>(string fullName, string assemblyName)
{
var key = GetKey("CreateInstance1", fullName, assemblyName);
if (IsCache)
if (ContainsKey(key))
{
return Get<T>(key);
}
//命名空间.类型名,程序集
var path = fullName + "," + assemblyName;
//加载类型
var o = Type.GetType(path);
//根据类型创建实例
var obj = Activator.CreateInstance(o, true);
var reval = (T)obj;
if (IsCache)
Add<T>(key, reval, _time);
//类型转换并返回
return reval;
}
以上的方法中,根据加载的程序集创建对象后,将获取的返回值结构加入缓存中。
DotNet程序集解析的更多相关文章
- DotNet友元程序集解析
项目开发的过程中,调试使用的可能是最多的操作.任何代码写出来都需要经过调试和整合,以此扩展和提升程序的稳定性和可靠性.谈到.NET的单元测试,在这里就得提提.NET的友元程序集这一特性,也借用.NET ...
- Dotnet文件格式解析
0x0.序 解析过程并没有介绍对pe结构的相关解析过程,网上此类相关资料很多可自行查阅,本文只介绍了网上资料较少的从pe结构的可选头中的数据目录表中获取dotnet目录的rva和size,到完全解析d ...
- http协议请求规则与dotNet的解析
请求方法URI协议/版本 请求的第一行是"方法URL议/版本":GET/sample.jsp HTTP/1.1 以上代码中"GET"代表请求方法,"/ ...
- 使用C#的AssemblyResolve事件动态解析加载失败的程序集
我们知道反射是 依赖注入 模式的基础,依赖注入要求只在项目中引用定义接口的程序集,而不引用接口实现类的程序集,因为接口实现类的程序集应该是通过反射来动态加载的,这样才能保证接口与其实现类之间的松耦合. ...
- 使用C#的AssemblyResolve事件和TypeResolve事件动态解析加载失败的程序集
我们知道反射是 依赖注入 模式的基础,依赖注入要求只在项目中引用定义接口的程序集,而不引用接口实现类的程序集,因为接口实现类的程序集应该是通过反射来动态加载的,这样才能保证接口与其实现类之间的松耦合. ...
- 解析.NET对象的跨应用程序域访问(下篇)
转眼就到了元宵节,匆匆忙忙的脚步是我们在为生活奋斗的写照,新的一年,我们应该努力让自己有不一样的生活和追求.生命不息,奋斗不止.在上篇博文中主要介绍了.NET的AppDomain的相关信息,在本篇博文 ...
- 解析.NET对象的跨应用程序域访问--AppDomain(上篇)
在目前的项目开发中,分布式开发已经逐渐成为主流.一个项目要是没有采用分布式架构,都不好意思跟别人说这是一个完整的项目.这句话虽然有些过激,但是随着人们对效率的要求在提高,以及产品需要提升用户体验.只有 ...
- CLR查找和加载程序集的方式
C#开发者在开发WinForm程序.Asp.Net Web(MVC)程序等,不可避免的在项目中引用许多第三方的DLL程序集, 编译后引用的dll都放在根目录下.以我个人作品 AutoProject S ...
- CLR查找和加载程序集的方式(一)
C#开发者在开发WinForm程序.Asp.Net Web(MVC)程序等,不可避免的在项目中引用许多第三方的DLL程序集, 编译后引用的dll都放在根目录下.以我个人作品 AutoProject S ...
随机推荐
- 选择列表中除了第一个li的其他元素
//选择div中除了第一个li的其他所以li元素 div li:not(:first-child){ }
- R语言:常用函数【转】
数据结构 一.数据管理vector:向量 numeric:数值型向量 logical:逻辑型向量 character:字符型向量list:列表 data.frame:数据框 c:连接为向量或列表len ...
- PostgreSQL配置优化
硬件和系统配置 操作系统 Ubuntu13.04 系统位数 64 CPU Intel(R) Core(TM)2 Duo CPU 内存 4G 硬盘 Seagate ST2000DM001-1CH164 ...
- 8天掌握EF的Code First开发之Entity Framework介绍
返回<8天掌握EF的Code First开发>总目录 本篇目录 Entity Framework概要 什么是ORM Entity Framework简史 Entity Framework具 ...
- 轻量级通信引擎StriveEngine —— C/S通信demo(2) —— 使用二进制协议 (附源码)
在网络上,交互的双方基于TCP或UDP进行通信,通信协议的格式通常分为两类:文本消息.二进制消息. 文本协议相对简单,通常使用一个特殊的标记符作为一个消息的结束. 二进制协议,通常是由消息头(Head ...
- MySQL 复制表结构
200 ? "200px" : this.width)!important;} --> 介绍 有时候我们需要原封不动的复制一张表的表结构来生成一张新表,MYSQL提供了两种便 ...
- 让Chrome 接管邮件连接,收发邮件更方便了
页面中除了传统的超链接外,还可以将邮箱地址写入<a>标签,意思不表自明,当然是用户点击后就会打开相应的邮件客户端向这个连接指向的邮件地址发邮件. <a href="mail ...
- 顶级的JavaScript框架、库、工具及其使用
几乎每隔一个星期,就有一个新的 JavaScript 库席卷网络社区!Web 社区日益活跃.多样,并在多个领域快速成长.想要研究每一个重要的 JavaScript 框架和库,是个不可能完成的任务.接下 ...
- MA均线组合
MA5.MA13.MA21.MA34.MA55.MA90.MA120.MA250
- solve the problem of 'java web project cannot display verification code'
my java code of the function: package com.util; import java.awt.Color; import java.awt.Font; import ...