《CLR via C#》读书笔记 之 生成、打包、部署和管理应用程序及类型
第二章 生成、打包、部署和管理应用程序及类型
2013-02-27
2.2 将类型生成到模块中
2.3 元数据概述
2.4 将模块合并成程序集
2.2 将类型生成到模块中
文件Program.cs源代码:
1 public class Program 2 { 3 public static void Main() 4 { 5 System.Console.WriteLine("Hi"); 6 } 7 }
csc命令
//1.生成programe.exe, MSCorLib.dll包含所有默认核心类型,所以C#编译器会自动引用 csc.exe /out:Program.exe /t:exe /r:MSCorLib.dll Program.cs //2.若不希望自动引用MSCorLib.dll,使用/nostdlib开关 csc.exe /out:Program.exe /t:exe /nostdlib Program.cs //3.使用响应文件,把需要的设置放在响应文件中 csc.exe @MyProject.rsp Program.cs //3.1 MyProject.rsp内容: /out:Program.exe /t:exe /r:MSCorLib.dll
响应文件
C#响应文件允许同时使用多个响应文件。除了命令显示指定的文件,编译器还会自动查找两个名为CSC.rsp的文件:
- 在当前目录;
- 在csc.exe文件所在目录(安装.NET Framework时,会在%SystemRoot%\Microsoft.NET\Framework\v.X.X.X)。
csc.exe所在目录的文件CSC.rsp会引用一些常用程序集,可能对编译器的速度有些影响。但如果源代码没有引用其中任何程序集中的类型或方法,就不会影响最终生成的程序集文件,也就不会影响程序执行性能。
注意:使用/reference编译器开关引用程序集时,可以指定文件完整路径。若不指定路径,编译器会按以下顺序搜索:
- 工作目录
- csc.exe本身所在目录
- 使用/lib编译器开关指定的任何目录
- 使用LIB环境变量指定的任何目录
2.3 元数据概述
表的种类有三种:定义表、引用表和清单(manifest)表
表1 定义表
名称 |
说明 |
ModuleDef |
总是包含一个标识模块的记录项[1]。该记录项包含模块的文件名和扩展名(不包含路径),以及模块版本ID(下面代码中的MVID,编译器创建的GUID)。 |
TypeDef |
模块中的每个类型在TypeDef表中都有一个对应的记录项。每个记录项都包含类型名、基类型、标记(public、private等)以及一些索引,这些索引指向该类型在MethodDef表、FieldDef表、Property表、EventDef表所拥有的成员。 |
MethodDef |
模块中的每个方法在MethodDef表中都有一个对应的记录项。每个记录项都包含方法名、标记(private、public、virtual、abstract、static、final等)、签名以及方法的IL代码在模块中的偏移量(下面代码中的RVA,Relative Virtual Addresses,相对虚地址)。每个记录项还包含一个指向ParamDef表对应记录项的指针,这样可以找到有关方法参数的更多信息。 (另,CallCnvntn即指CallingConventions) |
FieldDef |
模块中的每个字段在FieldDef表中都有一个对应的记录项。每个记录项包含标记(private、public等)、类型和名称。 |
ParamDef |
模块中的每个参数在ParamDef表中都有一个对应的记录项。每个记录项包含标记(in、out、retval等)、类型和名称。 |
PropertyDef |
模块中的每个属性在PropertyDef表中都有一个对应的记录项。每个记录项包含标记、类型和名称。 |
EventDef |
模块中的每个事件在EventDef表中都有一个对应的记录项。每个记录项包含标记和名称。 |
表2 引用表
名称 |
说明 |
AssemblyRef |
模块中的每个引用的程序集在AssemblyRef表中都有一个对应的记录项。每个记录项包含绑定程序集所必要的信息:程序集名称(不包含路径和扩展名)、版本号、语言文化(culture)和公共密钥标记(通常由程序集发布者提供的公共密钥(标识被引用的程序集的发布者)生成的小的散列值。该散列值是被引用的程序集中的位的校验和。CLR完全忽略该散列值,并在以后也将继续如此。 |
ModuleRef |
当前模块引用的类型可能由别的PE模块实现的,所有那些模块在这个表中都有一个对应记录项。每个记录项都包含模块的文件名和扩展名(不含路径)。该表用来绑定那些实现在相同程序集不同模块中的类型。 |
TypeRef |
该模块所引用的每个类型都在TypeRef表中有一个对应的记录项。每个记录项包含了类型的名称和一个引用(指向类型的指针)。如果类型实现在另一个类型内部,那么该指针指向一个TypeRef记录项。如果该类型实现在同一个模块中或者实现在同一程序集的其他模块中,那么该指针指向一个ModuleDef记录项。如果类型实现在不同的程序集中,那么该指针指向一个AssemblyRef记录项 |
MemberRef |
模块中所引用的每一个成员(字段、方法、属性方法和事件方法)都在这个表中有一个对应的记录项。每个记录项都包含成员的名称、签名以及指向成员所在类型的TypeRef表中对应记录项的指针。 |
表3 清单表
名称 |
说明 |
AssemblyDef |
如果模块标识为一个程序集,那么它将在AssemblyDef表中有一个对应的记录项。该记录项包含程序集名称(不包含路径和扩展名)、版本号(主版本号、次版本号、生成版本号和修订版本号)、语言文化、一些标记、散列算法和发布者的公有密钥(可能为null)。 |
FileDef |
程序集中所有PE文件和资源文件都在FileDef表中有一个对应的记录项(除了清单所在的文件,因为它对应的记录项在AssemblyDef表中)。该记录项包含文件名和扩展名(不包含路径)、散列值、一些标记。如果程序集只包含一个文件(即清单所在文件),那么FileDef表中没有任何记录项。 |
ManifestResourceDef |
程序集中所有资源都在ManifestResourceDef表中有一个对应的记录项。该记录项包含资源名称、一些标记(public表示对程序集外部可见,private表示不可见)、和一个资源文件或流所在的文件在FileDef表中的索引。如果资源文件不是一个单独的文件(如jpg、gif),那么所谓资源就是指嵌入在PE文件中的流。对于嵌入的资源,该记录项还包含表示资源流在PE文件中起始位置的偏移量。 |
ExportedTypesDef |
程序集中所有的PE模块导出的所有公有类型(就是程序集中定义的public类型,它们在程序集的外部可见),都在ExportedTypesDef表中有一个对应的记录项。该记录项版含类型名称、到FileDef表的索引(指明哪一个程序集文件实现了该类型)、到TypeDef表的索引。注意:为节省文件空间,从包含清单的文件导出的类型不包含在该表中,因为这些类型信息可以通过元数据的TypeDef表获得。 |
注意: 包含清单的程序集文件还有一个AssemblyRef表。程序集的所有文件所有所引用的所有程序集在这个表中都有一个对应记录项。
2.4 将模块合并成程序集
多文件程序集的三点理由:
- 可用单独文件对类型进行划分,允许文件以“增量”的方式景象下载。将类型划分到不同的文件,还是我们能够对购买和安装的应用程序进行部分或分批打包/部署。
- 可在自己的程序集中添加资源或数据文件。
- 程序集包含的各种类型可以用不同的编程语言来实现。不同的语言写的类型放在不同模块中,然后,可以再合并成单个程序集。
将模块合并到程序集
//将不常用的类型编译到单独的模块中,创建一个名为RUT.netmodule的模块 csc /t:module RUT.cs //将常用类型编译到另一个模块,该模块包含清单。并将RUT.betmodule视为程序集的一部分,RUT.betmodule会添加到FileDef清单元数据表中,它的公开导出的类型添加到ExportedTypesDef清单元数据表中 csc /out:JeffTypes.dll /t:library /addmodule:RUT.netmodule FUT.cs
RUT.cs和FUT.cs内容
//RUT.cs content using System; public sealed class ARarelyUsedType { public ARarelyUsedType() { Console.WriteLine("A rarely used type was constructed."); } } //FUT.cs content using System; public sealed class AFrequentlyUsedType { public AFrequentlyUsedType() { Console.WriteLine("A frequently used type was constructed."); } }
图2-1 包含两个托管模块的一个多文件程序集,清单在其中的一个模块中
注意:清单源数据信息实际并不包含从清单所在的锁在的那个PE文件导出的类型。这是一项优化措施。
可以使用ILDAsm.exe检查元数据的清单表
File #1 (26000001)
-------------------------------------------------------
Token: 0x26000001
Name : RUT.netmodule
HashValue Blob : bc e9 d8 5c 51 06 69 c9 d7 3d d9 db c9 58 54 21 51 fb 8e 79
Flags : [ContainsMetaData] (00000000)
ExportedType #1 (27000001)
-------------------------------------------------------
Token: 0x27000001
Name: ARarelyUsedType
Implementation token: 0x26000001
TypeDef token: 0x02000002
Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] [BeforeFieldInit] (00100101)
可以看出,RUT.netmodule文件已被视为程序集的一部分,它的token是0x26000001。ExprotedTypeDef表中也只有RUT的公开导出类型ARarelyUsedType。
注意:元数据token是一个4字节的值。其中高位字节指明token的类型(0x01=TypeRef, 0x02=TypeDef, 0x23=AssemblyRef, 0x26=FileDef, 0x27=ExportedType)。要取完整列表,请参见.net framework SDK包含的CorHdr.h文件中的CorTokenType枚举类型。token的三个低位字节表明对应的元数据表中的行。例如:0x26000001这个实现引用的是FileDef表的第一行,对于大多数表,行都是从1开始编号。但对于TypeDef表,行号从2开始。
对于引用多文件程序集,编译生成一个新的程序集时,所引用的程序集中所有文件都必须存在;运行程序的时候,并不要求被引用的所有程序集都存在,关键看使用的类或方法在那个文件中。
=========================================================== ScopeName : JeffTypes.dll MVID : {D340DEFC-2B70-4F33-8983-06B327DED21D} =========================================================== Global functions ------------------------------------------------------- Global fields ------------------------------------------------------- Global MemberRefs ------------------------------------------------------- TypeDef #1 (02000002) ------------------------------------------------------- TypDefName: AFrequentlyUsedType (02000002) Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] [BeforeFieldInit] (00100101) Extends : 01000002 [TypeRef] System.Object Method #1 (06000001) ------------------------------------------------------- MethodName: .ctor (06000001) Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886) RVA : 0x00002050 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. TypeRef #1 (01000001) ------------------------------------------------------- Token: 0x01000001 ResolutionScope: 0x23000001 TypeRefName: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute MemberRef #1 (0a000001) ------------------------------------------------------- Member: (0a000001) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. TypeRef #2 (01000002) ------------------------------------------------------- Token: 0x01000002 ResolutionScope: 0x23000001 TypeRefName: System.Object MemberRef #1 (0a000003) ------------------------------------------------------- Member: (0a000003) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. TypeRef #3 (01000003) ------------------------------------------------------- Token: 0x01000003 ResolutionScope: 0x23000001 TypeRefName: System.Runtime.CompilerServices.CompilationRelaxationsAttribute MemberRef #1 (0a000002) ------------------------------------------------------- Member: (0a000002) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: I4 TypeRef #4 (01000004) ------------------------------------------------------- Token: 0x01000004 ResolutionScope: 0x23000001 TypeRefName: System.Console MemberRef #1 (0a000004) ------------------------------------------------------- Member: (0a000004) WriteLine: CallCnvntn: [DEFAULT] ReturnType: Void 1 Arguments Argument #1: String Assembly ------------------------------------------------------- Token: 0x20000001 Name : JeffTypes Public Key : Hash Algorithm : 0x00008004 Version: 0.0.0.0 Major Version: 0x00000000 Minor Version: 0x00000000 Build Number: 0x00000000 Revision Number: 0x00000000 Locale: <null> Flags : [none] (00000000) CustomAttribute #1 (0c000001) ------------------------------------------------------- CustomAttribute Type: 0a000001 CustomAttributeName: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute :: instance void .ctor() Length: 30 Value : 01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78 > T WrapNonEx< : 63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01 >ceptionThrows < ctor args: () CustomAttribute #2 (0c000002) ------------------------------------------------------- CustomAttribute Type: 0a000002 CustomAttributeName: System.Runtime.CompilerServices.CompilationRelaxationsAttribute :: instance void .ctor(int32) Length: 8 Value : 01 00 08 00 00 00 00 00 > < ctor args: (8) AssemblyRef #1 (23000001) ------------------------------------------------------- Token: 0x23000001 Public Key or Token: b7 7a 5c 56 19 34 e0 89 Name: mscorlib Version: 4.0.0.0 Major Version: 0x00000004 Minor Version: 0x00000000 Build Number: 0x00000000 Revision Number: 0x00000000 Locale: <null> HashValue Blob: Flags: [none] (00000000) File #1 (26000001) ------------------------------------------------------- Token: 0x26000001 Name : RUT.netmodule HashValue Blob : 3c 0b 23 bd 40 b6 8f 58 24 c5 da b0 a7 b7 1a 32 21 7f 8f 12 Flags : [ContainsMetaData] (00000000) ExportedType #1 (27000001) ------------------------------------------------------- Token: 0x27000001 Name: ARarelyUsedType Implementation token: 0x26000001 TypeDef token: 0x02000002 Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] [BeforeFieldInit] (00100101) User Strings ------------------------------------------------------- 70000001 : (39) L"A frequently used type was constructed." Coff symbol name overhead: 0 =========================================================== =========================================================== ===========================================================