首页 > 技术文章 > 生成、打包应用程序及类型

wenJiaQi 2017-08-08 22:40 原文

将类型生成到模块中


  本节讨论如何将源代码文件转换成可以部署的文件。先看下面这个简单的示例:

  1. 创建文本文件Program.txt,打开记事本键入如下代码

    public class Program
    {
        static void Main()
        {
            System.Console.WriteLine("Hello");
            System.Console.ReadLine(); 
        }
    }
  2. 将文件的后缀名修改为.cs
  3. 在命令行执行以下命令:

    C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /out:Program.exe /t:exe /r:MSCorLib.dll Program.cs

    执行结果如下:

    命令说明:

    MSCorLib.dll是特殊文件,它包含所有核心类型。由于这些类型使用频繁,以至于C#编译器会自动引用MSCorLib.dll程序集,也就是说,上述命令中的/r:MSCorLib.dll可以省略。又因为/out:Program.exe和/t:exe开关是C#编译器的默认设定,所以上述命名可以继续简化为C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe Program.cs

  4. 步骤3中的命令执行完毕后,会在相应路径下生成可执行文件Program.exe,双击该可执行文件,结果如下:

响应文件

  响应文件是包含一组编译器命令行开关的文本文件,执行csc.exe时,编译器打开响应文件,使用其中包含的所有开关。.NET Framework安装时会在%SystemRoot%\Microsoft.NET\Framework(64)\vX.X.X目录中安装默认全局CSC.rsp文件(X.X.X是安装的.NET Framework版本号),全局CSC.rsp文件引用了列出的所有程序集,所以不必使用C#编译器的/reference开关显示引用这些程序集。



元数据概述


  元数据是由几个表构成的二进制数据块。分别是定义表、引用表和清单表。编译器在编译源代码时,代码定义的任何项都会在定义表中创建一个记录。常用的元数据定义表如下:

元数据定义表名称说明
ModuleDef 包含对模块进行标识的一个记录项。该记录项包含模块文件名和扩展名,以及模块版本ID
TypeDef 记录模块中定义的类型在。包含类型的名称、基类型、一些标志和索引,这些索引指向MethodDef、FieldDef、PropertyDef、EventDef中的成员
MethodDef 记录模块中定义的方法
FieldDef 记录模块中定义的字段
ParamDef 记录模块中定义的参数
PropertyDef 记录模块中定义的属性
EventDef 记录模块中定义的事件

此外,编译器还会检测源代码引用的类型、字段、方法、属性和事件,并创建相应的引用表,它们记录了引用的内容。

引用表名称说明
AssemblyRef 记录模块引用的每个程序集
ModuleRef 记录该模块所引用的类型的每个PE模块
TypeRef 记录模块引用的每个类型
MemberRef 记录模块引用的每个成员

要查看元数据的相关内容,可以使用微软提供的IL反汇编工具ILDasm.exe检查托管PE文件中的元数据,所在目录:

C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools

使用ILDasm.exe打开上一节中的Program.exe程序集,选择视图|元信息|显示!,随后会显示如下信息:

从元信息文件中我们大致可以看出Program.exe中包含名为Program的TypeDef,这是一个公共类,从System.Object类派生,Program类型还定义了两个方法:Main和.ctor(构造器)。

将模块合并成程序集


  程序集是一个或多个类型定义文件及资源文件的集合。程序集中包含一个清单文件,主要包含作为程序集组成部分的文件名称、程序集版本、语言文化等描述性内容。CLR操作程序集,首先加载包含清单元数据表的文件,在根据清单来获取程序集中的其他文件的名称。

为什么要引入程序集的概念?

因为使用程序集,可重用类型的逻辑表示与物理表示可以分开。

使用多文件程序集的理由:

  • 不同的类型用不同的文件,使文件能以“增量”方式下载(将常用类型和不常用类型分开)。
  • 可在程序集中添加资源或数据文件。
  • 程序集包含的各个类型可以用不同的编程语言来实现。

清单元数据表简介:

清单元数据表名称说明
AssemblyRef 主要包含程序集名称、版本、语言文化等内容
FileDef 记录程序集中每个PE文件和资源文件,若程序集只包含模块本身,不包含其他模块和资源文件,FileDef表将无数据
ManifestResourceDef 记录程序集中包含的每个资源的名称、标志以及FileDef表中的一个索引
ExportedTpyesDef 记录从程序集的所有PE模块中导出的每个public类型

下面,我们将通过一个示例来说明如何生成多文件程序集。

  1. 首先准备两个源代码文件

    • CommonTypes.cs

      public class CommonTypes
      {
          public static void Main()
          {
              System.Console.WriteLine("Hello,This is CommonTypes");
              System.Console.ReadLine();
          }
      }
    • UncommonTypes.cs

      public class UncommonTypes
      {
          public static void Main()
          {
              System.Console.WriteLine("Hello,This is UncommonTypes");
              System.Console.ReadLine();
          }
      }
  2. 将UncommonTypes编译到一个单独的模块中,生成文件UncommonTypes.netmodule

    C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /t:module UncommonTypes.cs

  3. 将CommonTypes编译到另一个模块,该模块将成为程序集清单的宿主,因为这些类型会经常使用。生成文件MultiFileLibrary.dll

    C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /out:MultiFileLibrary.dll /t:library /addmodule:UncommonTypes.netmodule CommonTypes.cs

  4. 使用ILDasm.exe工具检查元数据清单表,如下图可知程序集文件确实包含了对UncommonTypes.netmodule文件类型的引用。

当有项目引用MultiFileLibrary.dll时,编译器会在搜索外部类型时加载MultiFileLibrary.dll以及FileDef表中列出的所有文件,也就是说,如果删除UncommonTypes.netmodule文件,编译时会报错



程序集版本资源信息


  CSC.exe生成PE文件程序集时,会在PE文件中嵌入标准的Win32版本资源。可查看文件属性来检查该资源。

版本号格式说明

版本号格式major(主版本号)minor(次版本号)build(内部版本号)revision(修订号)
示例 2 5 719 2
 

推荐阅读