【深入理解CLR 三】生成、打包、部署和管理应用程序及类型(上)

简介: 【深入理解CLR 三】生成、打包、部署和管理应用程序及类型(上)

上一篇博文https://blog.csdn.net/sinat_33087001/article/details/80185199讲了CLR的执行模型,从整体流程把控方面介绍了CLR的执行过程,对于一些细节没有进行说明,例如:类怎么被编译为模块文件的、模块文件又是怎么被合并为程序集等问题,还有诸如元数据是如何工作的、程序集的版本资源信息怎么看等操作。本篇博文可以说是对上一篇细节的补充说明吧。

这里扯个题外话,从上两篇博文的阅读量来看,再和之前我写的java相关博文阅读量比起来,简直惨不忍睹,看来大家对于.Net的热情确实消退了不少,感觉微软因为开源和高收费失去了中国市场的十年吧,希望.Net Core的出现,和微软云编译,模糊化PC的战略能打赢下一个十年,毕竟感觉微软是非常伟大的一家公司。对照最近5.15的TNT和中兴的休克,真的能感受到科技PPT和科技创新之间真正的差距,我们还有很长的路要走啊。

闲言少叙,书归正传,依照惯例,本篇博文的最佳食用方式如下:本篇博文主线是1、类如何编译为托管模块-----------2、托管模块如何集成为程序集,实际上也就是上篇博文讲到的第一部分操作,将源代码编译为面向CLR的程序集的全部细节过程,附带将详细讨论元数据有哪些内容,另外,你将会了解到一些原理性知识,

  • 为何现在删程序好删多了,不用管注册表什么的,而之前特别麻烦。
  • CLR如何解析和定位要执行的应用程序集,即使你把qq软件里的远程连接dll扔到微信文件夹下,只要通过配置依然安然无恙。
  • 软件为何可以具备那么多语言,怎么加载的
  • 程序集的版本资源信息又是如何生成的

等等知识,数不胜数,接下来通过主线来串联本篇博文涉及到的知识.

#将类型生成到模块中

按照上一篇博客的理解,我们得现将源代码编译成托管模块,这个过程实质上就是把类型生成到托管模块中去依照Jeffrey举的一个小小例子来分析:

namespace TML
{
    class Program
    {
        static void Main(string[] args)          //成员方法入口
        {
            Console.WriteLine("Hello World!");   //引入的外部类型Console
        }
    }
}

以上所示也就是源代码啦,编写完是一个Program.cs这样子的源代码文件。

##生成基本流程

拿到源代码文件Program.cs之后做如下处理用来生成Program.exe(可执行文件,换言之可被CLR执行文件):

执行如下命令 csc.exe /out:Program.exe /t:exe /r:MScorLib.dll Program.cs

下面解释这几个输出参数:

  • /out         输出文件名,这里文件名为Program.exe
  • /t:exe       生成控制台可执行文件 (/target:exe的缩写: )
  • /r         从指定程序集文件引用元数据 (/reference:<文件列表> 的 缩写:) ,这里引用的程序集文件是MScorLib.dll

该篇文章有详细配置和参数说明 https://blog.csdn.net/cy88310/article/details/4792118

由于C#编译器默认执行命令 /out:Program.exe /t:exe,所以以上命令可以简写为

csc.exe /r:MScorLib.dll Program.cs

这里为什么要引用MScorLib.dll 程序集呢?因为这里引用了外部类型System.Console的WriteLine方法,而该类型来源于此文件集,MScorLib.dll 是核心类型,所以C#编译器会自动引用该程序集。所以以上命令又可以简写为 csc.exe Program.cs 当然如果不想默认引用该程序集,使用如下命令:

/nostdlib[+|-]  不引用标准库(mscorlib.dll) ,这个时候就会报错,因为没有引用标准库:csc.exe /out:Program.exe /t:exe /nostdlib Program.cs 该文件使用的System.Console类是在标准库中定义的。

以上这一大段的核心内容就是:编译器将cs文件编译为exe文件。cs文件里引用的一些外部类型要想使用必须引用外部程序集,那么就需要通过一些命令来引,/r,那么问题来了,如果我这个类文件要引100个程序集,得在命令行敲100个么?这就需要接下来介绍的神器响应文件来帮忙了

##辅助神器–响应文件

为了避免输入命令的繁琐,不再使用/r开关显式的引用这些程序集,C#编译器支持响应文件,这货长这样:

这是我本机的csc.rsp文件,这个文件是全局性质的响应文件。那么在执行命令的时候,读取流程是什么呢?例如以下这行命令:

csc.exe @MyProject.rsp  Program.cs

CSC在运行时经过如下过程:

  1. 首先检查全局响应文件csc.rsp(在csc.exe所在目录),若有加载
  2. 其次检查本地引入的响应文件(这里就是Myproject.rsp)若有加载
  3. 最后读取命令行手动显示指定的设置

汇总三方面命令得出最后的总的命令,若命令行与全局和本地文件冲突,以命令行为准,若本地和全局发生冲突,以本地为准,另外,指定/noconfig 命令行开关,编译器将忽略本地和全局csc.rsp

##构建联系–元数据(定义表与引用表)

元数据是由几个表构成的二进制数据块,和JVM的class文件表类似,只是class文件在一张表(表里的一些项也是表)里聚合了名称和数量并且使用常量池来读取值,而元数据则分成3张表,元数据定义表描述了自身的信息,元数据引用表描述了引用信息,元数据清单表应用于程序集,托管模块没有该表。

###元数据定义表

元数据定义表包含7张表,模块、类型、(方法、字段、参数、属性、事件)

  1. ModeleDef:每一个记录项包括模块文件名和扩展名、模块版本ID
  2. TypeDef每一个记录项包括类型的名称、基类型、一些标志、一些索引,这些标志标志了类的可访问性等信息,这些索引则指明了类的组成元素来自哪张表里的哪些项
  3. MethodDef每一个记录项包括方法的名称、一些标志、签名、方法的IL代码在模块中的偏移量、一个索引,这个索引指向了ParamDef表中的一个记录项,指明方法参数的信息
  4. FieldDef每一个记录项包括字段的名称、字段类型、一些标志,标志了字段的可访问性等
  5. ParamDef每一个记录项包括参数的名称、参数类型、标志
  6. PropertyDef每一个记录项包括属性的名称、属性类型、标志
  7. EventDef每一个记录项包括事件的名称、标志

这里科普下:方法签名由方法名称和一个参数列表(方法的参数个数、顺序和类型)组成

通过以上信息可以看出,这些元数据定义表基本就清晰的描绘出了整个模块里各个类的一些细节,但仍然有一个问题,如果我一个类引用了另一个类,引用信息该怎么描述呢?我如果想查看被引用类属于哪个程序集,哪个模块该怎么办,这些就要用到元数据引用表。

###元数据引用表

元数据应用表主要包括4张表:引用的程序集表、引用的模块表、引用的类型表、引用的成员表

  • AssemblyRef每个记录项都包含:程序集的名称、版本号、语言文化、公钥token生成hash值(标识该程序集发布者)、标志、校验和hash值

  • ModuleRef每个记录项都包含:所引用托管模块的文件名和扩展名
  • TypeRef每个记录项都包含:所引用类型的名称和类型位置
  • MemberRef每个记录项都包含:所引用的每个成员的名称、签名、指向对成员定义的那个类型的TypeRef记录项

###反编译对照说明

上边说了这么多表,太混乱,这里还是用开篇代码Program.exe来描述下具体的元数据是怎样的,为方便显示,将定义表里对应的一些引用直接放到该项目下边说明。

  • 首先是定义表:

    这里就不讨论托管模块表,可以从类的定义表里看到如下信息,类名、基型、一些标志位、索引(这里直接整合到下边,不呈现MethodDef了)也就是方法的记录项,包括方法名称、一些标志、偏移量(RVA目前还不知道干啥的)、方法的签名返回类型。构造器方法里包括一个指向调用方法时构造对象的内存,这也解释了为什么可以通过this访问自己。
  • 其次是引用表

    程序集引用表,可以看出引用自程序集mscorlib也就是标准库

    类型引用,引用了System.Console类型的方法WriteLine。这里有个困惑留下来,我本身用到的类型如何对应到引用的类型,难道仅凭类型名就可以么?这也太草率了吧。

除此之外ILDasm.exe还提供统计信息。

#将模块合并为程序集

上一篇博文讲到了程序集,这里添加一些程序集的特点

  • 程序集定义了可重用的类型
  • 程序集用一个版本号标记
  • 程序集可以关联安全信息
相关文章
|
2月前
|
Prometheus 监控 Cloud Native
应用程序部署
应用程序部署
51 3
|
监控 安全 虚拟化
DLL注入的环境构建
DLL注入的环境构建
|
开发框架 缓存 自然语言处理
【深入理解CLR 三】生成、打包、部署和管理应用程序及类型(下)
【深入理解CLR 三】生成、打包、部署和管理应用程序及类型(下)
166 0
|
JavaScript 前端开发 缓存
SharePoint Framework 在web部件中使用已存在的JavaScript库 - 捆绑打包和外部引用
在构建SPFx客户端web部件时,你可以使用公网已有的JavaScript库来构建强大的解决方案。但是在使用的时候你需要考虑你引用的东西没有影响SharePoint页面的性能。
5489 0