在编写.NET程序的时候,如果需要对一个程序集文件进行分析,我们可以使用Assembly.LoadFile()来加载这个程序集,然后对LoadFile()方法返回的Assembly对象进行进一步的分析。但是Assembly.LoadFile()方法会以执行为目的把程序集加载到程序中,因此它对于被加载的程序集文件有严格的要求,比如,如果被程序集所依赖的程序集不存在,那么LoadFile()会抛出异常,再比如,在.NET Core中加载.NET Framework的程序集,LoadFile()也会抛出异常。如果我们只想分析程序集,但是并不需要执行程序集,那么我们就需要一种单纯地分析程序集文件的方式。

.NET Framework提供了Assembly.ReflectionOnlyLoad()来实现类似的效果,但是这个方法由于依赖于AppDomain,因此在.NET Core中不被支持。微软曾经在实验室项目中提出过一个在.NET Core中实现这个功能的System.Reflection.TypeLoader,但不知道什么原因,没有在.NET Core的正式版中提供这个类。

我们知道,.NET程序集是PE格式的文件,.NET中提供了用来分析PE文件的类PEReader(位于System.Reflection.Metadata这个NuGet包中),因此我们可以用PEReader来分析程序集文件。

在PEReader中,我们可以通过TypeDefinitions获取到程序集中的所有类,我们可以用GetMethods()获取某个类中定义的所有方法。为了提升效率,TypeDefinitions、GetMethods()等成员获得到的对象都是TypeDefinitionHandle、MethodDefinitionHandle等句柄类型的,这些对象只包含地址信息,并不包含类型的名字、方法的名字、方法的参数等详细信息,要获取这些信息,我们需要调用MetadataReader的GetTypeDefinition()、GetMethodDefinition()等方法来获取。如下的代码用来加载一个程序集,并且输出程序集中所有的类型信息以及类型中定义的方法:

//Install-Package System.Reflection.Metadata
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable; string file = @"E:\Microsoft.AspNetCore.Components.Web.dll";
using FileStream fileStream = File.OpenRead(file);
using PEReader peReader = new PEReader(fileStream);
if(!peReader.HasMetadata)
{
Console.WriteLine($"{file} doesn't contain CLI metadata.");
return;
}
var mdReader = peReader.GetMetadataReader();
if (!mdReader.IsAssembly)
{
Console.WriteLine($"{file} is not an assembly.");
return;
}
foreach (var typeHandler in mdReader.TypeDefinitions)
{
var typeDef = mdReader.GetTypeDefinition(typeHandler);
string name = mdReader.GetString(typeDef.Name);
string nameSpace = mdReader.GetString(typeDef.Namespace);
Console.WriteLine($"***********{nameSpace}.{name}***********");
foreach (var methodHandler in typeDef.GetMethods())
{
var methodDef = mdReader.GetMethodDefinition(methodHandler);
Console.WriteLine(mdReader.GetString(methodDef.Name));
}
}

使用PEReader的时候,我们需要先获得XXXHandler,然后再调用MetadataReader获取句柄的详细信息,这样做尽管性能比较高,但是代码比较繁琐,而且在实现某些高级操作的时候比较麻烦。比如,如果我们要获取一个程序集的CustomAttribute信息,PEReader并没有提供比较简单的方法,需要我们对PE格式非常精通,才能编写出来对应的代码。

我们可以使用AsmResolver.DotNet这个第三方Nuget包来简化程序集文件的读取分析,它是对PEReader的一个高级封装。如下的代码用来加载一个程序集,输出程序集的公司信息,并且输出程序集中所有的类型信息以及类型中定义的方法:

string file = @"E:\Microsoft.AspNetCore.Components.Web.dll";
var moduleDef = AsmResolver.DotNet.ModuleDefinition.FromFile(file);//用的不是System.Reflection.Metadata命名空间下的ModuleDefinition类
var asmCompanyAttr = moduleDef.Assembly.CustomAttributes.FirstOrDefault(c => c.Constructor.DeclaringType.FullName == "System.Reflection.AssemblyCompanyAttribute");
var utf8Value = (Utf8String?)asmCompanyAttr.Signature.FixedArguments[0].Element;
var strValue = (string?)utf8Value;
Console.WriteLine($"company name:{strValue}");
foreach(var typeDef in moduleDef.GetAllTypes())
{
string name = typeDef.Name;
string nameSpace = typeDef.Namespace;
Console.WriteLine($"***********{nameSpace}.{name}***********");
foreach (var methodDef in typeDef.Methods)
{
Console.WriteLine(methodDef.Name);
}
}

总之,如果我们需要分析一个程序集并且要运行其中的代码,我们可以使用Assembly.LoadFile();如果我们不需要运行程序集,只是想分析程序集,那么使用PEReader是更好的选择,当然我们也可以选择对PEReader进行封装的AsmResolver.DotNet这个NuGet包。本文作者杨中科在Zack.Commons这个开源项目中实现“判断一个程序集是否是微软开发的”这个功能的时候就用到了AsmResolver.DotNet,大家可以查看这个项目的GitHub代码仓库来查看源代码。

.NET Core分析程序集最优美的方法,不用Assembly.LoadFile(),超越ReflectionOnlyLoad的更多相关文章

  1. Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的区别!

    参考: http://www.cnblogs.com/benwu/archive/2009/10/24/1589096.html http://www.cnblogs.com/xuefeng1982/ ...

  2. external-provisioner源码分析(2)-main方法与Leader选举分析

    更多ceph-csi其他源码分析,请查看下面这篇博文:kubernetes ceph-csi分析目录导航 external-provisioner源码分析(2)-main方法与Leader选举分析 本 ...

  3. C#动态创建和动态使用程序集、类、方法、字段等

    C#动态创建和动态使用程序集.类.方法.字段等 分类:技术交流 (3204)  (3)   首先需要知道动态创建这些类型是使用的一些什么技术呢?其实只要相关动态加载程序集呀,类呀,都是使用反射,那么动 ...

  4. dubbo源码分析2-reference bean发起服务方法调用

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  5. Linux性能分析工具与图形化方法

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~. 作者:赵坤|腾讯魔王工作室后台开发工程师 在项目开发中,经常会遇到程序启动时间过长.CPU使用率过高等问题,这个时候需要依靠性能分析工具来 ...

  6. 解决vs2019中暂时无法为.net core WinForms使用 Designer 的临时方法

    目录 解决vs2019中暂时无法为.net core WinForms使用 Designer 的临时方法 安装 vs 2019 professional/enterprise版本 在vs的设置里,勾选 ...

  7. dubbo源码分析9——ServiceBean的afterPropertiesSet方法分析

    ServiceBean的afterPropertiesSet方法是实现了InitializingBean,还是准备先做宏观分析,然后再做细致分析.下面先宏观分析:  public void after ...

  8. HashMap底层原理分析(put、get方法)

    1.HashMap底层原理分析(put.get方法) HashMap底层是通过数组加链表的结构来实现的.HashMap通过计算key的hashCode来计算hash值,只要hashCode一样,那ha ...

  9. .NET Core 获取数据库上下文实例的方法和配置连接字符串

    目录 .NET Core 获取数据库上下文实例的方法和配置连接字符串 ASP.NET Core 注入 .NET Core 注入 无签名上下文 OnConfigure 配置 有签名上下文构造函数和自己n ...

随机推荐

  1. Chapter 8 Selection Bias

    目录 8.1 The structure of selection bias 8.2 Examples of selection bias 8.3 Selection bias and confoun ...

  2. PlatformIO+Jlink进行调试

    PlatformIO自带调试功能具体配置如下 https://docs.platformio.org/en/latest/plus/debug-tools/jlink.html     我是用的是直接 ...

  3. 对vector和map容器的删除元素操作

    /** * 删除头部元素 * 切割map到指定的个数 * @param map * @param i * @return */ map<int, Rect> PublicCardFrame ...

  4. gojs 如何实现虚线(蚂蚁线)动画?

    在绘制 dag 图时,通过节点和来箭头的连线来表示节点彼此之间的关系.而节点常常又带有状态,为了更好的表示节点之间的流程关系,loading 状态的节点,与后续节点之间,需要用 动画着的虚线 表示,表 ...

  5. Ubuntu复习笔记-认识Linux

    本次复习基于\(Ubuntu20.04\)的发行版进行总结,目的是更好记录自己学习的\(Linux\). 认识Linux 学习\(Linux\)之前,需要搞懂几个概念,\(Linux\)桌面操作系统与 ...

  6. 使用 DML语句,对 “锦图网” 数据进行操作,聚合函数练习

    查看本章节 查看作业目录 需求说明: 根据客户 ID 统计订单数.订单总金额.最高订单金额.最低订单金额和每份订单平均金额,并按订单总金额升序显示 根据客户统计订单总订购人次数> 5 的统计信息 ...

  7. go语言生成markdown文档工具

    虽然有go语言的swagger,但是go版本的swagger对代码的侵入比较大,因此抽空实现了基于GO语言生成markdown文档的工具 开源链接地址: https://github.com/w3li ...

  8. navicat 找不到系统路径 【修改了系统路径中文名称引起的】

    这是我还没修改系统路径中文名称时的路径, 怎么办? 关闭当前用户连接 右键,选择连接属性 把那个改了即可

  9. [转]axios请求超时,设置重新请求的完美解决方法

    自从使用Vue2之后,就使用官方推荐的axios的插件来调用API,在使用过程中,如果服务器或者网络不稳定掉包了, 你们该如何处理呢? 下面我给你们分享一下我的经历. 具体原因 最近公司在做一个项目, ...

  10. POJ 2387 Til the Cows Come Home (最短路径 模版题 三种解法)

    原题链接:Til the Cows Come Home 题目大意:有  个点,给出从  点到  点的距离并且  和  是互相可以抵达的,问从  到  的最短距离. 题目分析:这是一道典型的最短路径模版 ...