《CLR via C# 》读书笔记(1) -- .Net程序是如何生成的

说到.NET基础,我觉得应该首先应该要知道一组概念,我觉得作为.NET程序员,这应该算是常识。有可能我目前的理解也不算正确,希望能在以后的积累中逐步完善。

CLR

- CLR (Common Language Runtime)它为.NET Application提供一个运行时的环境。毕竟.NET程序和普通的Windows应用程序(比如说用C++开发的程序)运行方式是不一样的。在

运行时,它有自己的内存管理机制,有自己的执行模型,有自己的异常处理,有自己的线程同步等。而这些都是由CLR提供。相当于一个容器。

CTS

- CTS (Common Type System)  .Net是一个基于类型的平台,所有的事物都必须被一个类型所包含。没有像C++中的全局变量,全局函数这类东西。那么CTS是一系列用来规范

类型的规则。比如说:规定类型可以包含字段,方法,属性,事件,规定类型可以继承,规定类型的可访问类型可以有private, public, internal等。个人理解是一种语法规则。

CLS

- CLS (Common Language Specification) CLR其实提供了很多的功能,而每一种面向CLR的语言(比如说C#,VB.NET)可能不需要暴露CLR所有的功能给开发人员。但是如果每种语言所支持的

CLR功能不一样,相互之间的调用便会出现问题了(目前可以举出一个例子:CLR是支持throw 任何类型,VB.NET是完全支持CLR的,但是C#只支持throw Exception的派生类。如果在C#中调用VB.NET的

代码,那VB.NET中的有些throw肯定无法在C#中catch住。)。为了能够跨语言调用,必须有一个规范来统一各个语言所必须支持CLR中的功能集。这个规范便是CLS。他定义了CLR强大功能中的一个子集合,这是

所有面向CLR的语言必须要支持的。这样能才能实现跨语言的调用。

程序集

- 程序集(Assembly)我觉得用最通俗的语言讲就是一些类的集合。但是这样说应该是比较片面的,至少程序集里面还能嵌入资源文件。

官方说法应该是程序集是由一个或多个模块加上资源文件组成,每个模块由描述该模块所包含/引用类型的元数据和IL代码组成。

 

回到和标题相关的内容,平常我们编写一个.NET程序的过程应该是

1. 创建一个VS Project

2. 编写代码

3. 编译Project,接着VS便给我们产生了一个exe程序

4. 我们双击一下这个exe程序,我们绘制的界面就出现了。

作为.NET程序员,我觉得应该要了解得更多一点。这样才能知其所以然,呵呵。

  • 剖析生成的exe/dll文件
    1. 文件组成结构
        编绎器生成的exe/dll的组成结构为:PE32(+) Header,CLR Header,程序集的元数据以及源代码经过编绎后生成的IL代码。具体表示内容见下表:


        我们可以通过微软提供的工具dumpbin,来查看PE32(+) Header 和 CLR Header. 以下是我通过该工具获取的某dll的部分PE32(+) Header和CLR header
    PE32 Header
    File Type: DLL

    FILE HEADER VALUES
                 14C machine (x86)
                   3 number of sections
            50AA2CD2 time date stamp Mon Nov 19 20:57:54 2012
                   0 file pointer to symbol table
                   0 number of symbols
                  E0 size of optional header
                2102 characteristics
                       Executable
                       32 bit word machine
                       DLL

        CLR Header

File Type: DLL

  clr Header:

              48 cb
            2.05 runtime version
            2098  [      71C ] RVA  [ size ] of MetaData Directory
               1 flags
                   IL Only
               0 entry point token
               0  [        0 ] RVA  [ size ] of Resources Directory
               0  [        0 ] RVA  [ size ] of StrongNameSignature Directory
               0  [        0 ] RVA  [ size ] of CodeManagerTable Directory
               0  [        0 ] RVA  [ size ] of VTableFixups Directory
               0  [        0 ] RVA  [ size ] of ExportAddressTableJumps Directory
               0  [        0 ] RVA  [ size ] of ManagedNativeHeader Directory

        可以通过ILDasm这个工具来查看程序集中的元数据,下表是通过该工具查看到的某dll的元数据。

ExpandedBlockStart.gif View Code
TypeDef #1 (02000002)
-------------------------------------------------------
    TypDefName: Entity.Person  (02000002)
    Flags     :  [ Public ]  [ AutoLayout ]  [ Class ]  [ AnsiClass ]  [ BeforeFieldInit ]  (00100001)
    Extends   : 01000001  [ TypeRef ] System.Object
    Field #1 (04000001)
    -------------------------------------------------------
        Field Name: <Name>k__BackingField (04000001)
        Flags     :  [ Private ]  (00000001)
        CallCnvntn:  [ FIELD ]
        Field type:  String
        CustomAttribute #1 (0c000002)
        -------------------------------------------------------
            CustomAttribute Type: 0a000011
            CustomAttributeName: System.Runtime.CompilerServices.CompilerGeneratedAttribute :: instance void .ctor()
            Length: 4
            Value : 01 00 00 00                                      >                <
            ctor args: ()


    Field #2 (04000002)
    -------------------------------------------------------
        Field Name: <Age>k__BackingField (04000002)
        Flags     :  [ Private ]  (00000001)
        CallCnvntn:  [ FIELD ]
        Field type:  I4
        CustomAttribute #1 (0c000012)
        -------------------------------------------------------
            CustomAttribute Type: 0a000011
            CustomAttributeName: System.Runtime.CompilerServices.CompilerGeneratedAttribute :: instance void .ctor()
            Length: 4
            Value : 01 00 00 00                                      >                <
            ctor args: ()


    Method #1 (06000001) 
    -------------------------------------------------------
        MethodName: get_Name (06000001)
        Flags     :  [ Public ]  [ HideBySig ]  [ ReuseSlot ]  [ SpecialName ]  (00000886)
        RVA       : 0x00002050
        ImplFlags :  [ IL ]  [ Managed ]  (00000000)
        CallCnvntn:  [ DEFAULT ]
        hasThis 
        ReturnType: String
        No arguments.
        CustomAttribute #1 (0c000001)
        -------------------------------------------------------
            CustomAttribute Type: 0a000011
            CustomAttributeName: System.Runtime.CompilerServices.CompilerGeneratedAttribute :: instance void .ctor()
            Length: 4
            Value : 01 00 00 00                                      >                <
            ctor args: ()


    Method #2 (06000002) 
    -------------------------------------------------------
        MethodName: set_Name (06000002)
        Flags     :  [ Public ]  [ HideBySig ]  [ ReuseSlot ]  [ SpecialName ]  (00000886)
        RVA       : 0x00002067
        ImplFlags :  [ IL ]  [ Managed ]  (00000000)
        CallCnvntn:  [ DEFAULT ]
        hasThis 
        ReturnType: Void
        1 Arguments
            Argument #1:  String
        1 Parameters
            (1) ParamToken : (08000001) Name : value flags:  [ none ] (00000000)
        CustomAttribute #1 (0c000011)
        -------------------------------------------------------
            CustomAttribute Type: 0a000011
            CustomAttributeName: System.Runtime.CompilerServices.CompilerGeneratedAttribute :: instance void .ctor()
            Length: 4
            Value : 01 00 00 00                                      >                <
            ctor args: ()


    Method #3 (06000003) 
    -------------------------------------------------------
        MethodName: get_Age (06000003)
        Flags     :  [ Public ]  [ HideBySig ]  [ ReuseSlot ]  [ SpecialName ]  (00000886)
        RVA       : 0x00002070
        ImplFlags :  [ IL ]  [ Managed ]  (00000000)
        CallCnvntn:  [ DEFAULT ]
        hasThis 
        ReturnType: I4
        No arguments.
        CustomAttribute #1 (0c000013)
        -------------------------------------------------------
            CustomAttribute Type: 0a000011
            CustomAttributeName: System.Runtime.CompilerServices.CompilerGeneratedAttribute :: instance void .ctor()
            Length: 4
            Value : 01 00 00 00                                      >                <
            ctor args: ()


    Method #4 (06000004) 
    -------------------------------------------------------
        MethodName: set_Age (06000004)
        Flags     :  [ Public ]  [ HideBySig ]  [ ReuseSlot ]  [ SpecialName ]  (00000886)
        RVA       : 0x00002087
        ImplFlags :  [ IL ]  [ Managed ]  (00000000)
        CallCnvntn:  [ DEFAULT ]
        hasThis 
        ReturnType: Void
        1 Arguments
            Argument #1:  I4
        1 Parameters
            (1) ParamToken : (08000002) Name : value flags:  [ none ] (00000000)
        CustomAttribute #1 (0c000014)
        -------------------------------------------------------
            CustomAttribute Type: 0a000011
            CustomAttributeName: System.Runtime.CompilerServices.CompilerGeneratedAttribute :: instance void .ctor()
            Length: 4
            Value : 01 00 00 00                                      >                <
            ctor args: ()


    Method #5 (06000005) 
    -------------------------------------------------------
        MethodName: PrintName (06000005)
        Flags     :  [ Public ]  [ HideBySig ]  [ ReuseSlot ]  (00000086)
        RVA       : 0x00002090
        ImplFlags :  [ IL ]  [ Managed ]  (00000000)
        CallCnvntn:  [ DEFAULT ]
        hasThis 
        ReturnType: Void
        No arguments.

    Method #6 (06000006) 
    -------------------------------------------------------
        MethodName: .ctor (06000006)
        Flags     :  [ Public ]  [ HideBySig ]  [ ReuseSlot ]  [ SpecialName ]  [ RTSpecialName ]  [ .ctor ]  (00001886)
        RVA       : 0x0000209f
        ImplFlags :  [ IL ]  [ Managed ]  (00000000)
        CallCnvntn:  [ DEFAULT ]
        hasThis 
        ReturnType: Void
        No arguments.

    Property #1 (17000001)
    -------------------------------------------------------
        Prop.Name : Name (17000001)
        Flags     :  [ none ] (00000000)
        CallCnvntn:  [ PROPERTY ]
        hasThis 
        ReturnType: String
        No arguments.
        DefltValue: 
        Setter    : (06000002) set_Name
        Getter    : (06000001) get_Name
        0 Others

    Property #2 (17000002)
    -------------------------------------------------------
        Prop.Name : Age (17000002)
        Flags     :  [ none ] (00000000)
        CallCnvntn:  [ PROPERTY ]
        hasThis 
        ReturnType: I4
        No arguments.
        DefltValue: 
        Setter    : (06000004) set_Age
        Getter    : (06000003) get_Age
        0 Others


TypeRef #1 (01000001)
-------------------------------------------------------
Token:             0x01000001
ResolutionScope:   0x23000001
TypeRefName:       System.Object
    MemberRef #1 (0a000013)
    -------------------------------------------------------
        Member: (0a000013) .ctor: 
        CallCnvntn:  [ DEFAULT ]
        hasThis 
        ReturnType: Void
        No arguments.

.....

TypeRef #20 (01000014)
-------------------------------------------------------
Token:             0x01000014
ResolutionScope:   0x23000001
TypeRefName:       System.Console
    MemberRef #1 (0a000012)
    -------------------------------------------------------
        Member: (0a000012) WriteLine: 
        CallCnvntn:  [ DEFAULT ]
        ReturnType: Void
        1 Arguments
            Argument #1:  String
Assembly
-------------------------------------------------------
    Token: 0x20000001
    Name : Entity
    Public Key    :
    Hash Algorithm : 0x00008004
    Version: 1.0.0.0
    Major Version: 0x00000001
    Minor Version: 0x00000000
    Build Number: 0x00000000
    Revision Number: 0x00000000
    Locale: <null>
    Flags :  [ none ] (00000000)
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)

        2. 元数据
        所谓元数据即用来描述数据的数据,具体到程序集的元数据则是用来描述程序集的数据。其实这些元数据便是.NET反射机制的数据来源。上面通过ILDasm获得的是结构化处理过之后的结果。而真实存储于文件中的是一张张二维的元数据表,下表是常用的元数据定义表和引用元数据表:

      

       

       

      

       这些都是在编绎的时候,编绎器根据源代码进行分析,结合编绎源码时传递的对外部程序集的引用命令,然后总结成上面所描述的元数据表。

  • 多文件程序集
    其实在几年的工作过程中,我还没有碰到过多文件程序集。在程序集的定义中,其实是有一个模块(Module)的概念。一个程序集可以由多个模块组成。
    每一个模块是指一个.netmodule的文件。(貌似通过VS无法生成这种类型的文件,必须要通过手动调用CSC编绎器程序去生成)当然如果程序集只有一个模块,那么这个模块当然也就直接在dll/exe文件中了。每一个模块都会有上面所描述文件结构,也会包含元数据。但是CLR不能加载这种模块文件,CLR加载的最小单元是程序集。模块文件必须被关联到某个程序集中,并且有一个dll/exe文件与这个程序集相对应,该模块文件才能被CLR使用。而这个dll/exe文件充当了程序集所有模块的关联者。如下图:


    这样,CLR可以在真正需要的时候才去加载多模块程序集中的某个特定的模块。可以避免加载一载根本不需要用到的模块。
    那到这里,出现了一个问题,在dll/exe文件中没有用来保存dll/exe与模块文件之前的关系的元素。CLR如何知道该程序集包含哪几个模块呢?因此另一种类型
    的元数据表便诞生了---清单元数据表。 见下表:


    通过清单元数据,编译器将程序集的组成结构保存了下来。
  • 强命名程序集
          .NET提供的这个功能是我一直觉得很神秘的东西,原因是因为一直都没有机会用过。(人有时候很奇怪,总是会觉得没有接触过的东西很神秘,
    即使它其实只是个很普通的玩意。)
          通过强名,CLR通过 AssemblyName,Version,Culture,PublicKey 四个元素来标识一个程序集。从而增强了程序集的可区分性。
          我觉得强名程序集主要解决以下两方面问题:
           1. 通过采用一种比较强的机制标识程序集,从而解决安装程序集到GAC时无法区分不分提供者的同名程序集以及同一程序集的不同版本等问题。(如果是私有部署,问题还是无法解决,因为相同文件名的文件会被替换)
           2. 采用公钥/私钥对进程序集进行加密,然后加载时进行验证从而保证程序集的有效性。
    1. 生成
           讲了半天,还是先来看看如何生成一个强名的程序集。
           主要步骤是:
           1. 先利用.NET提供的工具 SN.exe产生一个私钥/公钥对文件
           
           2. 在编译程序集是提供给编译器,让它根据1中产生的私钥/公钥对文件对程序集进行加密与签名。


    通过这样编译出来的程序集便是用私钥签过名的,并且编译器会将公钥保存在程序集的清单元数据中。
    签名处理的方式是对PE文件中除了强名数据外的其他信息进行Hash,然后将Hash值用私钥进行加密后存放于CLR的Header中。如下图:


    通过签名,当CLR加载该程序集时可以通过对加载的程序集进行Hash验证。则可以知道该程序集是否被篡改。

    让我们用工具来一探虚实:
    1. 通过dumpbin来查看CLR Header

    编译出来的程序集文件的CLR Header里显示了该程序集是Strong Name Signed.

    再来用ILDasm看看清单元数据:
    ExpandedBlockStart.gif View Code
    // Metadata version: v4.0.30319
    .assembly extern mscorlib
    {
      .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
      .ver 4:0:0:0
    }
    .assembly Entity
    {
      .custom instance void [ mscorlib ]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 )
      .custom instance void [ mscorlib ]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 06 45 6E 74 69 74 79 00 00 )                // ...Entity..
      .custom instance void [ mscorlib ]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
      .custom instance void [ mscorlib ]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 1A 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B   // ....NETFramework
                                                                                                            2C 56 65 72 73 69 6F 6E 3D 76 34 2E 30 01 00 54   // ,Version=v4.0..T
                                                                                                            0E 14 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C   // ..FrameworkDispl
                                                                                                            61 79 4E 61 6D 65 10 2E 4E 45 54 20 46 72 61 6D   // ayName..NET Fram
                                                                                                            65 77 6F 72 6B 20 34 )                            // ework 4
      .custom instance void [ mscorlib ]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 06 45 6E 74 69 74 79 00 00 )                // ...Entity..
      .custom instance void [ mscorlib ]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 )
      .custom instance void [ mscorlib ]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 )
      .custom instance void [ mscorlib ]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 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.
      .custom instance void [ mscorlib ]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 12 43 6F 70 79 72 69 67 68 74 20 C2 A9 20   // ...Copyright ..
                                                                                                      20 32 30 31 32 00 00 )                            //  2012..
      .custom instance void [ mscorlib ]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 )
      .custom instance void [ mscorlib ]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 )
      .custom instance void [ mscorlib ]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 31 38 37 31 33 65 65 31 2D 63 61 62 62   // ..$18713ee1-cabb
                                                                                                      2D 34 39 31 61 2D 39 34 35 63 2D 36 31 63 30 63   // -491a-945c-61c0c
                                                                                                      32 62 36 61 39 36 64 00 00 )                      // 2b6a96d..
      .custom instance void [ mscorlib ]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 )             // ...1.0.0.0..

      // --- The following custom attribute is added automatically, do not uncomment -------
      //  .custom instance void [ mscorlib ]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [ mscorlib ]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )

      .publickey = (00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00   // .$..............
                    00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00   // .$..RSA1........
                    A1 C0 32 95 08 03 92 3F 1B 1D BA 20 B0 B4 1B F5   // ..2....?... ....
                    9C 3A E5 C5 88 50 74 7B 66 4D A9 A6 4C CB D5 30   // .:...Pt{fM..L..0
                    80 5B FB 98 B3 4B 55 CE D6 BC 53 BF 80 A7 18 39   // . [ ...KU...S....9
                    DF 9E 3C AD 5F 34 B8 B0 6F AF 87 CD 9B 36 66 F3   // ..<._4..o....6f.
                    27 FF 7E E9 4E 52 5D 17 61 49 F6 89 54 2D B0 AF   // '.~.NR].aI..T-..
                    FD 7E EA 75 D5 A7 47 08 10 FD 18 91 99 4E 99 DA   // .~.u..G......N..
                    78 0A DE BE 1F D7 00 21 A5 A5 99 0B 89 53 43 8E   // x......!.....SC.
                    AB 78 5D F9 A1 C6 16 23 4E C6 65 3D F5 8F D8 DC ) // .x]....#N.e=....
      .hash algorithm 0x00008004
      .ver 1:0:0:0
    }
    .module Entity.dll
    // MVID: {72590280-AC77-4C2F-BA62-BF18ABD776B2}
    .imagebase 0x00400000
    .file alignment 0x00000200
    .stackreserve 0x00100000
    .subsystem 0x0003       // WINDOWS_CUI
    .corflags 0x00000009    //  ILONLY
    // Image base: 0x03760000

        可以看到清单元数据中包含了一个PublicKey元素(即公钥)。
2. 使用

      当加载一个强名的程序集时,必须提供强名标识符
     

AssemblyName assemblyName =  new AssemblyName( " Entity, Version=1.0.0.0, culture=neutral, PublicKeyToken=65fed09757d375fd ");
Assembly entityAssembly = Assembly.Load(assemblyName);

 此处的PublicKeyToken是公钥的64位Hash值。可以使用SN工具来获取私钥/公钥对文件中的公钥以及公钥标记。

此处如果版本不对:

AssemblyName assemblyName =  new AssemblyName( " Entity, Version=1.0.0.1, culture=neutral, PublicKeyToken=65fed09757d375fd ");
Assembly entityAssembly = Assembly.Load(assemblyName);

或公钥标记不对:

AssemblyName assemblyName =  new AssemblyName( " Entity, Version=1.0.0.0, culture=neutral, PublicKeyToken=65fed09757d375fda ");
Assembly entityAssembly = Assembly.Load(assemblyName);

都会导致该程序集加载失败:

终于写完了这一节,感觉写点东西比看东西费劲很多啊。不过在写的过程中还是加深了自己的理解。勉励一下自己,继续坚持。

转载于:https://www.cnblogs.com/Code-life/archive/2012/11/20/2777677.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值