当前位置: 首页 > news >正文

生成、打包、部署和管理应用程序及类型(3):将模块合并成程序集

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

CLR操作的是程序集。也就是说,CLR总是首先加载包含“清单”元数据表的文件,再根据“清单”获取程序集中的其他文件的没名称。程序集的特点如下:

●定义了可重用的类型

●用一个版本号标记

●可以关联安全信息

这些特点是包含清单的元数据表的文件才具备的。

使用程序集,可重用类型的逻辑表示和物理表示就可以分开。例如,程序集可能包含多个类型,可以将常用类型放到一个文件中,不常用类型放到另一个文件中。如果程序集要从Internet下载并且部署,那么对于含有不常用类型的文件,加入客户端永远不适用那些类型,该文件就永远不会下载到客户端。

总结一下使用多文件程序集的理由:

●不同的类型用不同的文件,使文件以“增量”下载。另外,将类型划分到不同文件中,可以对购买和安装的应用程序部分或分批打包、部署。

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

●程序集包含的各个类型可以用不同的编程语言来实现。编译不同语言类型的时候会生成不同的模块,然后可以用工具将所有模块合并成单个程序集。

生成程序集要么选择现有的PE文件作为“清单”的宿主,要么创建单独的PE文件并只在其中包含清单。由于有了清单的存在,程序集的用户不必关心程序集的划分细节。此外,清单也使程序集具有了自描述性。另外,在包含清单的文件中,一些元数据信息描述了哪些文件是程序集的一部分。但是,那些文件本身并不包含元数据来指出他们是程序集的一部分。

 可以通过很多方式将模块添加到程序集。这里使用/addmodule开关。现在有两个源代码:

Program.cs:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 
 8 namespace Project_1
 9 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             System.Console.WriteLine("HaHa");
15             Class1 C1 = new Class1();
16             C1.printHaha();
17         }
18     }
19 }

 

Class1.cs:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Project_1
 8 {
 9     public class Class1
10     {
11         public void printHaha (){
12             System.Console.WriteLine("Class1 Haha");
13         }
14     }
15 }

先将不常用的类型都编译到一个模块:

 

此时生成一个Class1.netmodule文件。这是一个PE文件,但是不能被CLR加载。

 接着将Program.cs编译到另一个模块。该模块将成为程序集清单的宿主。将该模块命名为MultiFileLibrary.dll:

上述命令行指示C#编译器编译Program.cs来生成MultiFileLibrary.dll。由于制定了/t:library开关,所以生成的是含有清单元数据表的DLL PE文件。/addmodule:Class1.netmodule 告诉编译器将文件添加到FIleD二分清单源数据表,并将Class1.netmodule的公开导出类型添加到ExportedTypes Def清单元数据表。

Class1.netmodule文件包含编译Class1.cs所生成的IL代码。该文件还包含一些定义源数据表,描述了Class1.cs定义的类型、方法、字段、属性、事件等。还包含一些引用元数据表,描述了Class1.cs引用的类型、方法等。

MultiFileLibrary.dll是一个单独的文件。和Class1.netmodule相似,MultiFileLibrary.dll包含编译Program.cs所生成的IL代码以及类似的定义与引用元数据表。此外,MultiFileLibrary.dll还包含额外的清单元数据表,这使得MultiFileLibrary.dll成为程序集。清单元数据表描述了程序集的所有文件(MultiFileLibrary.dll本身和Class1.netmodule)。清单元数据表还包含从Class1.netmodule和MultiFileLibrary.dll导出的所有公共类型。

可用ILDasm检查元数据清单表(摘取部分):

File #1 (26000001)
-------------------------------------------------------
    Token: 0x26000001
    Name : Class1.netmodule
    HashValue Blob : 06 f0 35 e1 e5 70 9e a4  6c 9d 4c dc 6b 40 6f c0  9e b5 09 56 
    Flags : [ContainsMetaData]  (00000000)


ExportedType #1 (27000001)
-------------------------------------------------------
    Token: 0x27000001
    Name: Project_1.Class1
    Implementation token: 0x26000001
    TypeDef token: 0x02000002
    Flags     : [Public] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit]  (00100001)


User Strings
-------------------------------------------------------

从中可以看出,Class1.netmodule文件已经被看成是程序集的一部分。token是0x26000001。在ExportedType 可以看到一个公开导出的类型(只有public会显示在这个数据表中)

客户端执行代码时会调用方法。一个方法首次被调用时,CLR检测作为参数、返回值或者局部变量而被方法引用的类型。然后,CLR尝试加载所引用程序及中含有的清单文件。如果要访问的类型恰好出现在这个文件中,CLR会执行内部登记工作,允许使用该类型。如果清淡指出被引用的类型在不同文件中,CLR会尝试加载需要的文件,同样执行内部登记,并允许使用该类型。只有在调用方法确实引用了未加载程序集中的类型时,才会加载程序集。

使用VS IDE将程序集添加到项目中

这个路径要与生成MultiFileLibrary.dll的路径一致。

使用程序集链接器

除了使用C#编译器,还可以使用“程序集链接器”实用程序AL.exe来创建程序集。如果程序集要包含由不同编译器生成的模块,或者生成时不清楚程序集的打包要求,程序集链接器就显得相当有用。还可以使用AL.exe生成只含资源的程序集,也就是附属程序集,通常用于本地化。

首先,有三个源代码:

Program.cs:

 

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Project_1
 8 {
 9     public class Program
10     {
11         public static void Main(string[] args)
12         {
13             System.Console.WriteLine("HaHa");
14             Class1 C1 = new Class1();
15             C1.printHaha();
16             Class2 C2 = new Class2();
17             C2.printHaha();
18         }
19     }
20 }

 

Class1.cs:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Project_1
 8 {
 9     public class Class1
10     {
11         public void printHaha (){
12             System.Console.WriteLine("Class1 Haha");
13         }
14     }
15 }

Class2.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Project_1
{
    public class Class2
    {
        public void printHaha()
        {
            System.Console.WriteLine("Class2 Haha");
        }
    }
}

 

为了理解AL.exe的工作原理,先改变MultiFileLibrary.dll的生成方式:

 生成MultiFileLibrary.dll:

指定入口并得到EXE PE文件:

(这里在操作的时候,出现了一个问题:指定main函数的时候报错“找不到Program.main”,后来才发现原因有两个:第一个是没有加命名空间Project_1,以及代码中main函数没有写错public,当然现在都加上了。需要指定作用域是因为程序集有可能有多个作用域,所以要指定是哪个作用域下的。但是public这个到现在都没有搞清楚具体原因,不知道有没有懂的大佬解释一下,我只知道和.exe的执行过程有关,但是main是在哪里发生了“外部调用”这个具体的过程还有些模糊)

可以执行Program.exe:

为程序集添加资源文件:

AL.exe创建程序集时,可以用/embed将文件作为资源添加到程序集。该开关获取任意文件,并将文件内容嵌入最终的PE文件。清单中的ManifestResourceDef表会更新以反映资源的存在。

与AL.exe相似,C#编译器CSC.exe也允许将资源合并到编译器生成的程序集中。/resource开关将指定的资源文件嵌入最终生成的程序集PE中,并更新ManifestResourceDef表。/linkresource开关ManifestResourceDef和FileDef清单表中添加记录项来引用独立存在的资源文件。

转载于:https://www.cnblogs.com/renzhoushan/p/10366289.html

相关文章:

  • windows下使用nginx调试简介
  • Ajax 知识
  • 什么软件可以提取视频中的音频制作成手机铃声
  • TypeScript(ES6) 的一些使用的小技巧
  • git远程分支回退
  • 开源SQL-on-Hadoop系统一览
  • Terraform入门 - 3. 变更基础设施
  • 【刷算法】LeetCode-26.删除排序数组中的重复项
  • SpiderData 2019年2月16日 DApp数据排行榜
  • matlab-基础 矩阵 同时修改多个元素
  • micropython esp8266 烧录
  • SOFAMosn配置模型
  • GPU编程(五): 利用好shared memory
  • Systemd曝3漏洞,大部分Linux将受到***
  • VM虚拟机中fedora28 无法使用中文输入法问题
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • angular学习第一篇-----环境搭建
  • ES6 ...操作符
  • golang 发送GET和POST示例
  • IndexedDB
  • JavaScript服务器推送技术之 WebSocket
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • laravel5.5 视图共享数据
  • PHP 的 SAPI 是个什么东西
  • Spring核心 Bean的高级装配
  • tensorflow学习笔记3——MNIST应用篇
  • 理解在java “”i=i++;”所发生的事情
  • 聊聊hikari连接池的leakDetectionThreshold
  • 每天一个设计模式之命令模式
  • 如何学习JavaEE,项目又该如何做?
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 新手搭建网站的主要流程
  • 原生 js 实现移动端 Touch 滑动反弹
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • 数据可视化之下发图实践
  • 智能情侣枕Pillow Talk,倾听彼此的心跳
  • "无招胜有招"nbsp;史上最全的互…
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (¥1011)-(一千零一拾一元整)输出
  • (2015)JS ES6 必知的十个 特性
  • (2020)Java后端开发----(面试题和笔试题)
  • (libusb) usb口自动刷新
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (附源码)ssm捐赠救助系统 毕业设计 060945
  • (三)Honghu Cloud云架构一定时调度平台
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (四)Android布局类型(线性布局LinearLayout)
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (转) 深度模型优化性能 调参
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .Net Remoting常用部署结构
  • .NET 依赖注入和配置系统
  • .NET 中让 Task 支持带超时的异步等待