在编写.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. 如何改善win10录屏时声音降噪(消除杂音)

    此文章是针对win10系统中安装Realtek声卡的麦克风出现杂音的设置办法 1. 打开win10的控制面板,找到"硬件和声音选项" 2. 进入"硬件和声音"选 ...

  2. Hyperledger Fabric源码分析之Gossip

    Gossip算法正如它的名字,小道消息只需要有人传播一次,那么过一段时间,所有人都会知道,就像新冠病毒一样,所以Gossip算法也有其它的别名"传染病扩散算法"."谣言传 ...

  3. Unsupervised Feature Learning via Non-Parametric Instance Discrimination

    目录 概 主要内容 Wu Z., Xiong Y., Yu S. & Lin D. Unsupervised Feature Learning via Non-Parametric Insta ...

  4. 『动善时』JMeter基础 — 60、固定吞吐量测试

    目录 1.定时器介绍 2.固定吞吐量定时器介绍 3.固定吞吐量定时器界面说明 4.固定吞吐量定时器的使用 (1)测试计划内包含的元件 (2)登陆请求内容 (3)固定吞吐量定时器内容 (4)线程组元件内 ...

  5. CS5211|DP转LVDS |低成本DP to LVDS Conversion 方案设计

    目前市面上DP转LVDS转换--DP to LVDS Conversion 方案设计有以下: 龙迅LT8911 LT7211.普瑞PS8625.昆泰CH7511等方案,DP换LVDS转换主要是用在一些 ...

  6. Eclipse 常用快捷键大全

    15 个 Eclipse 常用开发快捷键使用技巧 1.alt+? 或 alt+/:自动补全代码或者提示代码 2.ctrl+o:快速outline视图 3.ctrl+shift+r:打开资源列表 4.c ...

  7. Swoole 中使用 Lock 实现进程间锁

    注意:不要在 lock 和 unlock 操作中间使用可能引起协程切换的 API. $lock = new Swoole\Lock(SWOOLE_MUTEX); echo "[Master] ...

  8. PyCharm - 关联mysql失败 - Server returns invalid timezone. Go to 'Advanced' tab and set 'serverTimezone' property manually.

    时区错误,MySQL默认的时区是UTC时区,比北京时间晚8个小时. 所以要修改mysql的时长 在mysql的命令模式下,输入: set global time_zone='+8:00'; 再次连接成 ...

  9. tomcat 服务器的几个重要监听 方法 与 使用

    1. 总结一下tomcat 服务器里的三种监听 ServletContextListener HttpSessionListener ServletRequestListener 这是我要做的三个自定 ...

  10. 第10组 Beta冲刺 总结(组长)

    1.基本情况 组长博客链接:https://www.cnblogs.com/cpandbb/p/14050808.html 答辩总结: ·因为alpha阶段的产品做得偏离了方向,所以beta冲刺大家非 ...