在编写.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. 基于React和Node.JS的表单录入系统的设计与实现

    一.写在前面 这是一个真实的项目,项目已经过去好久了,虽然很简单,但还是有很多思考点,跟随着笔者的脚步,一起来看看吧.本文纯属虚构,涉及到的相关信息均已做虚构处理, 二.背景 人活着一定要有信仰,没有 ...

  2. ZOJ 3870:Team Formation(位运算&思维)

    Team Formation Time Limit: 2 Seconds Memory Limit: 131072 KB For an upcoming programming contest, Ed ...

  3. Codeforces 189A:Cut Ribbon(完全背包,DP)

    time limit per test : 1 second memory limit per test : 256 megabytes input : standard input output : ...

  4. 决策树之PEP(悲观剪枝)

    引用这2篇文章 https://blog.csdn.net/taoqick/article/details/72818496 https://www.open-open.com/lib/view/op ...

  5. 【MySQL作业】sum、max 和 min 聚合函数——美和易思聚合函数应用习题

    点击打开所使用到的数据库>>> 1.统计商品最高单价和最低单价. -- 获取所有商品的最高单价和最低单价: select max(unitPrice) 最高单价 , min(unit ...

  6. 编写Java程序,演练静态内部类应用

    返回本章节 返回作业目录 需求说明: 创建一个Person类,在该类中定义一个Home静态内部类,并在这个Home类中定义一个显示Home相关信息的方法. 在Person类中设置一个Home类型属性对 ...

  7. hisql 高级功能数据检测将错误数据拦截在系统外 一

    hisql github源码下载 git clone https://github.com/tansar/HiSql.git 在设计第二范式数据库时经常会把可能重复的数据单独做一种表关联,但是在写入表 ...

  8. Hadoop常用命令及基本概念

    HADOOP 是什么? 分布式计算开源框架,其核心组件为:HDFS.MAPREDUCE.YARN Hadoop各个功能模块的理解 1. HDFS模块 HDFS负责大数据的存储,通过将大文件分块后进行分 ...

  9. JMeter_性能压测报错address already in use:connect

    报错截图如下: 原因分析: 这个问题的原因是windows端口被耗尽了(默认1024-5000),而且操作系统要 2~4分钟才会重新释放这些端口,所以可以增加windows的可用端口来解决.windo ...

  10. Centos7 mysqldump定时任务,对mysql定时备份数据

    vi mysqldump_bak.sh #!/bin/bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/ ...