重温CLR(二)生成、部署以及程序集

将类型生成到模块中

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hi");
    }
}

该应用程序定义了program类型,其中有名为Main的public static方法。Main中引用了另一个类型System.Console。System.console是Microsoft实现好的类型,用于实现这个类型的各个方法的IL代码存储在MSCorLib.dll文件中。总之,应用程序定义了一个类型,还使用了其他公司提供的类型。

为了生存这个示例应用程序,请将上述代码放到一个源代码文件中(假定为Program.cs),然后再命令行执行以下命令

csc.exe /out:program.exe /t:exe /r:MSCorLib.dll Program.cs

这个命令行指示C#编译器生成名为Program.exe的可执行文件(/out : Program.exe)。生成的文件是Win32控制台应用程序类型(/t[arget]:exe)。

C#编译器处理源文件时,发现代码引用了System.Console类型的WriteLine方法。此时,编译器要核实该类型确实存在,它确实有WriteLine方法,而且传递的实参与方法形参匹配。由于该类型在c#源代码中没有定义,所以要顺利通过编译,必须向c#编译器提供一组程序集,使它能解析对外部类型的引用。MSCorLib.dll是特殊文件,它包含所有核心类型,包括Byte,Char,String,Int32等等。事实上,由于这些类型使用得如此频繁,以至于C#编译器会自动引用MSCorLib.dll程序集。

此外,/out:program.exe /t:exe开关是C#编译器的默认设定,所以能简化命令为

csc.exe program.exe

C#编译器生成的Program.exe文件,它是标准PE(可移植执行体,Portable Executable)文件

元数据概述

  Program.exe文件中到底有什么?托管PE文件由4部分构成:PE32(+)头、CLR头、元数据以及IL。PE32(+)头是windows要求的标准信息。

  CLR头是一个小的信息块,是需要CLR的模块(托管模块)特有的。这个头包含模块生成时所面向CLR的major(主)和minor(次)版本号;一些标志(flag);一个MethodDef token,该token指定了模块的入口方法(前提是该模块是CUI\GUI或windows store执行体);一个可选的强名称数字签名。最后,clr头还包含模块内部的一些元数据表的大小和偏移量。可以查看CorHdr.h头文件定义的IMAGE_COR20_Header来了解CLR透的具体格式。

  元数据是由几个表构成的二进制数据块。有三中表,分别是定义表(definition table)、引用表(reference table)和清单表(manifest table)。下表总结了模块元数据块中常用的定义表。

编译器编译源代码时,代码定义的任何东西都导致在上表列出的某个表中创建一个记录项。此外,编译器还会检测源代码引用的类型、字段、方法、属性和事件,并创建相应的元数据表记录项。在创建的元数据中包含一组引用表,他们记录了所引用的内容。表2-2总结了常用的引用元数据表。

可以用反编译工具查看托管pe文件中的元数据。

Program.exe包含名为Program的TypeDef。program是公共密封类,从System.Object派生。program类型还定义了两个方法:main和.ctor(构造器)。

Main是公共静态方法,用IL代码实现

将模块合并成程序集

上一节讨论的Program.exe并非只是含有元数据的PE文件,他还是程序集(assembly)。程序集是一个或多个类型定义文件及资源文件的集合。在程序及的所有文件中,有一个文件容纳了清单(manifest)。清单也是一个元数据表集合,表中主要包含作为程序集组成部分的那些文件的名称。此外,还描述了程序集的版本、语言文化、发布者、公共导出的类型以及构成程序集的所有文件。

CLR操作的是程序集。换而言之,CLR总是首先加载包含清单元数据表的文件,再根据清单来获取程序集中的其他文件的名称。下面列出了程序集的重要特点。

1 程序集定义了可重用的类型

2 程序集用一个版本号标记

3 程序集可以管理安全信息

除了包含清单元数据表的文件,程序集其他单独的文件并不具备上述特点。

类型为了顺利地进行打包、版本控制、安全保护以及使用,必须放在座位程序集一部分的模块中。程序集大多数时候只有一个文件,就像前面的Program.exe那样。然后,程序集还可以由多个文件构成:一些事含有元数据的pe文件,另一些是.gif或.jpg这样的资源文件。为便于理解,可将程序集视为一个逻辑EXE或DLL。

Microsoft为什么引入程序集的概念?这是因为使用程序集,可重用类型的逻辑表示与物理表示就可以分开。例如,程序集可能包含多个类型。可以将常用类型放到一个文件中,不常用类型放到另一个文件中。如果程序集要从internet下载并部署,那么对于含有不常用类型的文件,加入客户端不实用那些类型,该文件就永远不会下载到客户端。

使用多文件程序集的额外三个理由

1 不同的类型用不同的文件,使文件能以“增量”方式下载(就像前面在internet下载的例子描述的那样)。另外,将类型划分到不同的文件中,可以对购买和安装的应用程序进行部分或分批打包/部署。

2 可在程序集中添加资源或数据文件。例如,假定一个类型的作用是计算保险信息,需要访问精算表才能完成计算。在这种情况下,不必在自己的源代码中签入精算表。相反,可以使用有一个工具,使数据文件称为程序集的一部分。数据文件可为任意格式——只要应用程序知道如何解析。

3 程序集包含的各个类型可以用不同的编码程序语言来实现。然后 可以用工具将所有模块合并成单个程序集。

总之,程序集是进行重用、版本控制和应用安全性设置的基本单元。它允许将类型和资源文件划分到单独的文件中。这样一来,无论你自己还是用户,都可以决定打包和部署那些文件,一旦CLR加载还有清单的文件,就可确定在程序集的其他文件中。

共享程序集和强命名程序集

  CLR支持两种程序集:弱命名程序集(weakly named assembly)和强命名程序集(strongly named assembly)。两种程序集结构完全相同。也就是说他们都使用前面讨论的PE文件格式头、PE32(+)头、CLR头、元数据、清单表以及IL。生成工具也相同,都是C#编译器或者AL.EXE。两者的区别在于,强命名程序集使用发布者的公钥/私钥进行了签名。这一对密钥允许对程序集进行唯一性的标识、保护和版本控制,并允许程序集部署到用户机器的任何地方,甚至可以部署到internet上。

程序集可采用两种方式部署:私有或全局。私有部署的程序集是指部署到应用程序基目录或者某个子目录的程序集。弱命名程序集只能以私有方式部署。全局部署的程序集是指部署到一些公认公认位置的程序集。CLR在查找程序集时,会检查这些位置。强命名程序集既可私有部署,也可全局部署。

全局程序集缓存

由多个应用程序访问的程序集必须放到公认的目录,而且CLR在检测到对该程序集的引用时,必须知道检查该目录。这个公认位置就是全局程序集缓存(Global Assembly Cache,GAC)。GAC的具体位置是一种实现细节,不同版本会有所变化,一般在

%systemroot%\microsoft.net\assembly

GAC目录是结构化的:其中包含许多子目录,子目录名称用算法生成。永远不要将程序集文件手动复制到GAC目录;相反,要用巩固完成这项任务。工具知道GAC内部结构,并指导如何生成正确的子目录名。

强命名程序集能放篡改

用私钥对程序集进行签名,并将公钥和签名签入程序集,CLR就可验证程序集未被修改或破坏。程序集安装到GAC时,系统对包含清单的那个文件的内容进行哈希处理,将哈希值与PE文件中签入的RSA签名进行比较。此外,系统还对程序集的其他文件的内容进行哈希处理,并将哈希值与清单文件的FileDef表中存储的哈希值进行比较。任何一个哈希值不匹配,表明程序集至少有一个文件呗篡改,程序集将无法安装到GAC。

应用程序需要绑定到程序集时,clr根据被引用程序集的属性(名称、版本、公钥等)在Gac中定位该程序集。找到被引用程序集,就返回包含他的子目录,并加装清单所在的文件。如果被引用程序集不在GAC中,CLR会查找应用程序的基目录,然后查找应用程序配置文件中标注的任何私有路径。然后,如果应用程序有MSI安装,CLR要求MSI定位程序集,如果任何位置都找不到程序集,那么绑定失败,抛出FileNotFoundException。

如果强命名程序集文件从GAC之外的位置加载,CLR会在程序集加载后比较哈希值。也就是说,每次应用程序执行并加载程序集时,都会对文件进行哈希处理,以牺牲性能为代价,保证程序集文件内容没有被篡改。

“运行时”如何解析类型引用

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hi");
    }
}

  编译以上代码并生成程序集(假定名为Program.exe)。运行应用程序,CLR会加重并初始化自身,读取程序集CLR头,查找表示了应用程序入口方法(main)的MethodDefToken,检索MethodDefToken元数据表找到方法的IL代码再文件中的偏移量,将IL代码JIT编译成本机代码,最后执行本机代码。

对这些代码进行JIT编译,CLR会检测所有类型和成员的引用,加载他们的定义程序集(如果尚未加载)。具体地说,IL call指令应用了元数据toekn 0A000003。该token表示memberRef元数据表中的记录项3。CLR检查该memberRef记录项,发现它的字段引用了TypeRef表中的记录项(system.Console类型)。按照

TypeRef记录项,CLR被引导至一个AssemblyRef记录项

这时CLR知道了它需要的是哪个程序集。接着,CLR必须定位并加载该程序集。

高级管理控制

以下是一个示例XML配置文件

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0"/>
    <add key="webpages:Enabled" value="false"/>
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5"/>
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35"/>
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
        <bindingRedirect oldVersion="1.0.0.0-5.2.4.0" newVersion="5.2.4.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs"
        type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.8.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701"/>
    </compilers>
  </system.codedom>
</configuration>

这个XML文件为CLR提供了丰富的信息,具体如下所示。

probing元素

  查找弱命名程序集时,检查应用程序基目录下的AuxFiles和bin\subdir子目录。对于强命名程序集,clr检查GAC或者由codeBase元素指定的url。只有在未指定codeBase元素时,CLR才会在用用程序的私有路径中检查强命名程序集。

第一个dependentAssembly,assemblyIdentity和bindingRedirect元素

查找由控制着公钥标记31bf3856ad364e35的组织发布的、语言文化为中性的SomeClassLibrary程序集的1.0.0.0版本时,改为定位同一个程序集的3.0.0.0版本。

codeBase元素

查找由控制着公钥标记31bf3856ad364e35的组织发布的、语言文化为中性的SomeClassLibrary程序集的2.0.0.0版本时,尝试在标记URL处发现他

编译方法时,CLR判断它引用了哪些类型和成员。根据这些信息,“运行时”检查进行引用的程序集的AssemblyRef表,判断程序集生成时引用了哪些程序集。然后,clr在引用程序配置文件中检查程序集/版本,进行制定的版本号重定向操作。随后,clr查找新的、重定向的程序集/版本。最后,clr在机器的machine.config文件中检查新的程序集、版本并进行制定的版本号重定向操作。

利用这些配置文件,管理员可以实际地控制CLR加载的程序集。

原文地址:https://www.cnblogs.com/qixinbo/p/10197613.html

时间: 2024-10-11 19:23:24

重温CLR(二)生成、部署以及程序集的相关文章

C#程序集系列13,如何让CLR选择不同版本的程序集

本篇主要体验,在存在多个版本程序集的情况下,如何让CLR选择哪个版本程序集运行,以及程序集版本的切换. 分别生成非强名称程序集不同版本 □ 生成某个版本的程序集 →清理F盘as文件夹,剩下如下文件 →查看Cow.cs文件 using System; using System.Reflection; [assembly: AssemblyVersion("3.3.3.3")] public class Cow { public static void Moo() { Console.Wr

如何生成强命名程序集

如何生成强名称的程序集 时间 2016-01-28 14:27:15                                               陈斌彬的技术博客 原文                   http://cnbin.github.io/blog/2016/01/28/ru-he-sheng-cheng-qiang-ming-cheng-de-cheng-xu-ji/ 主题                 Berkeley DB                 .N

2017.2.28 activiti实战--第五章--用户与组及部署管理(二)部署流程资源

学习资料:<Activiti实战> 第五章 用户与组及部署管理(二)部署流程资源 内容概览:讲解流程资源的读取与部署. 5.2 部署流程资源 5.2.1 流程资源 流程资源常用的有以下几种: 1 流程定义文件:拓展名为bpmn20.xml和bpmn 2 流程定义的图片:拓展名为PNG 3 表单文件:拓展名为form 4 规则文件:拓展名为drl 部署流程资源的时候,要注意一点: 引擎会根据不同的拓展名进行不同的处理.bpmn或bpmn20.xml类型的文件,会在ACT_RU_PROCDEF(流

android Ant批打包学习(二)---生成有签名且混淆的apk包

本篇文章预期目标:                 生成有签名apk文件 详细步骤如下: 1  定义文件 两个文件 1.1  名字:local.properties 内容:SDK的路径(例如:sdk.dir=/Users/lincoln/Android/sdk) 1.2 名字:ant.properties 内容: source.dir=src out.dir=bin proguard.config=proguard.cfg   (添加混淆,必须保证proguard.cfg 文件存在) key.s

WIN8+VS2013编写发布WCF、一(编写)、二(部署)、三(调用)

原文://http://www.cnblogs.com/tntboom/p/4348483.html 引言:上学期因为写服务器用WCF,所以连查资料再瞎调试勉强成功了,但是这学期又到了用WCF的时候,而当时的资料零零散散,查找不易,并且此次是在WIN8与VS2013环境下编写的,所以将该入门过程记录下来,一是方便查阅,二是有助于其他新手. 一.新建工程并编写. 1.打开VS2013,新建--WCF----WCF服务应用程序,工程名称请根据爱好自行填写.如图: 2.观察解决方案框中的文件列表,其中

【VMCloud云平台】SCDPM(二)部署

上一篇讲述了如何去规划DPM的部署,这一篇,来讲讲如何部署SCDPM,同样的,我也会根据微软最佳实践来做,本篇涉及的服务器如下图(蓝色为计划中,紫色为实施完成,红色为进行中). 1. 还记得基础架构篇时,创建的SCDPM管理吗?SCDPMADMIN的条件如下: l 必须为DPM Server的本地管理组与SQL本地管理员 l 必须有SQL ServerSysAdmin的权限 l 在安装完成后可以把账户从SQL服务器的本地管理组移除 l 除DPMAdmin之外建议创建DPMDBReader作为DP

【ASP.NET Core快速入门】(二)部署到IIS

原文:[ASP.NET Core快速入门](二)部署到IIS 配置IIS模块 ASP.NET Core Module载地址:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/aspnet-core-module?tabs=aspnetcore2x 安装后再IIS管理器模块页面会出现aspnetcoremodule 新建网站 修改应用池 网站发布 控制台方式 dotnet publish发布到项目文件的bin/deb

SpringBoot(二)CentOS部署SpringBoot项目从0到1

在之前的博文<详解intellij idea搭建SpringBoot>介绍了idea搭建SpringBoot的详细过程, 并在<CentOS安装Tomcat>中介绍了Tomcat的安装,前面几篇文章实际上已经充分准备好了部署Linux的必要条件.那么今天来看看如何在CentOS部署SpringBoot,让你的SpringBoot在服务器上跑起来. vLinux部署springboot 从0到1,5步走,在Linux Tomcat部署springboot: 1. <CentOS

[From 2.7]简单应用程序部署(程序集打包)

简单应用程序部署(程序集打包) windows Store打包 打包成.appx 非windows store应用打包 1.直接复制 2.使用.cab 3.打包成一个MSI(可做到按需安装----首次尝试加载一个程序集时才安装它) 原文地址:https://www.cnblogs.com/TheChenLin/p/10190302.html