.NET程序集

【主要内容】
@将源代码编译为托管模块
@将模块组合为程序集
@共享程序集(强签名)

【概念阐述】
@将源代码编译为托管模块
1、在.NET框架里,我们可以用任何支持CLR(Common Language Runtime)的编程语言来创建源代码文件,然后用相应的编译器来做
    语法检查和源代码分析,不管使用的是何种编程语言及编译器,最后生成的结果都是一个托管模块(Managed Module)。
 
    高级语言代码 --> 托管模块(包含IL和元数据的PE文件)--> CPU指令(运行时从IL(Intermediary Language中间语言)编译产生)

2、托管模块是一个需要CLR才能执行的标准PE(可移植可执行Portable Executable)文件。
     PE文件是一个非常重要的概念,它由PE表头、CLR表头、元数据和IL(中间语言)代码四部分组成。

     PE表头:主要指出了文件的类型,GUI(图形用户)、CUI(控制台)或是DLL(在.NET中DLL特指程序集文件的一种形式,不同于以前的动态链接库文件)。
                 另外该表头还包括一个时间标记用于表示文件创建的时间。对于仅包含IL代码的模块该表头的大部分信息会被忽略。对于包含有本地CPU代码的模块,
                 该表头还会包含有关本地CPU代码的一些信息。
     CLR表头:包含标识托管模块的一些信息(可以被CLR或者一些使用工具解析)。这些信息包括托管模块所需要的CLR版本号,一些标记,托管模块入口点
                   方法(MethodDef)元数据标记,以及有关托管模块的元数据、资源、强命名标记和其他一些意义不是太大的信息的位置和尺寸。
     【元数据】(表):该表用于描述代码中用到的类型和成员,描述的类型和成员有两类,一是源代码中定义,一是引用的。元数据总是和包含IL代码的文件相关联,实际上元数据总是和这些代码一起被嵌入到同一个exe/dll文件中,编译器总是同时产生元数据和IL代码,并且总是同时将它们嵌入到生产的托管模块中,两者始终保持同步。
                 元数据的用处:
                 @元数据省去了源代码编译时对头文件和库文件的需求。这是因为在含有实现类型和成员的IL代码文件中,已经包含了所有被引用的类型和成员的信息,
                    编译器可以直接从托管模块中读取元数据来获得这些信息。
                 @Visual Studio.NET的智能感知(Intellisense)功能是通过分析元数据来实现的。
                 @CLR代码验证过程可以利用元数据来确保代码仅执行“安全”的操作。
                 @利用元数据,可以将一个对象的字段序列化后发送到远程机器,然后在远程机器上执行反序列化,从而重新创建对象和它的状态。
                 @利用元数据,垃圾收集器可以追踪对象的生存期。对于任何对象,垃圾收集器都能通过元数据来确定该对象的类型,并且可以获知该对象的哪些字段引
                    用了其他对象。
     IL代码:编译器在编译源代码是产生的指令,CLR运行时将它们编译成本地CPU指令。
3、【程序集】
      程序集(assembly)是一个抽象的概念,它是一个或多个托管模块,以及一些源文件的逻辑组合。
      可以通过Assembly Linker(【AL.exe】)将这些文件组合到一个程序集中。

4、IL(Intermediary Language中间语言)
     高级语言编译成的IL代码,可以用微软提供的【ILDasm.exe】进行反编译。可以直接用IL来编写代码,微软也提供了一个汇编器【ILAsm.exe】供使用。
     高级语言提供的都只是CLR全部功能的一个子集,IL汇编语言包含CLR全部功能,当我们选择的语言没有提供我们需要的CLR的某些功能时,则可以选择IL
     汇编语言,或另一个提供了该功能的高级语言来编写这部分程序。

【实验步骤】
=========
@查看元数据
=========
1)用notepad新建文件D:\mytest\App.cs并输入以下实验代码

定义类

2)打开【SDK命令提示】,输入命令:

D:\Program Files\Microsoft Visual Studio 8\SDK\v2.0>cd d:\mytest
D:\mytest>【csc.exe 】/out:App.exe /t:exe /r:MScorLib.dll App.cs

3)使用 ILDAsm.exe 来查看元数据
D:\mytest>ildasm.exe App.exe
【视图】--> 【元信息】--> 【显示】
 或使用快捷键:Ctrl + M

=========================
@将源代码编译为模块并加入到程序集中
=========================
1)建立三个源代码文件

Code

将ModuleA.cs和ModuleB.cs编译为模块
D:\mytest>csc /t:module ModuleA.cs
D:\mytest>csc /t:module ModuleB.cs
ModuleB.cs(2,10): error CS0246: 找不到类型或命名空间名称“ModuleA”(是否缺少 using 指令或程序集引用?)
D:\mytest>csc /t:module /addmodule:ModuleA.netmodule ModuleB.cs

将ModuleDemo.cs编译为程序集
D:\mytest>csc /t:exe  ModuleDemo.cs.
ModuleDemo.cs(3,3): error CS0246: 找不到类型或命名空间名称“ModuleA”(是否缺少 using 指令或程序集引用?)
ModuleDemo.cs(3,19): error CS0246: 找不到类型或命名空间名称“ModuleA”(是否缺少 using 指令或程序集引用?)
ModuleDemo.cs(4,3): error CS0246: 找不到类型或命名空间名称“ModuleB”(是否缺少 using 指令或程序集引用?)
ModuleDemo.cs(4,19): error CS0246: 找不到类型或命名空间名称“ModuleB”(是否缺少 using 指令或程序集引用?)
ModuleDemo.cs(5,3): error CS0103: 当前上下文中不存在名称“Console”

虽然B中添加了ModuleA.netmodule,同时ModuleDemo.cs编译时加入了ModuleB.cs,但仍需要添加ModuleA.netmodule。
D:\mytest>csc /t:exe  /r:MSCorLib.dll /addmodule:ModuleB.netmodule ModuleDemo.cs
ModuleDemo.cs(4,3): error CS0246: 找不到类型或命名空间名称“ModuleA”(是否缺少 using 指令或程序集引用?)
ModuleDemo.cs(4,19): error CS0246: 找不到类型或命名空间名称“ModuleA”(是否缺少 using 指令或程序集引用?)

D:\mytest>csc /t:exe  /r:MSCorLib.dll /addmodule:ModuleA.netmodule,ModuleB.netmodule ModuleDemo.cs

执行
D:\mytest>ModuleDemo.exe
ModuleA
ModuleB

将ModuleA.netmodule和ModuleB.netmodule移动到新建文件夹D:\mytest\mymodule下
D:\mytest>ModuleDemo.exe
未处理的异常:  System.IO.FileNotFoundException: 未能加载文件或程序集“ModuleA.netmodule”或它的某一个依赖项。系统找不到指定的文件。
文件名:“ModuleA.netmodule” ---> System.IO.FileNotFoundException: 系统找不到指定的文件。 (异常来自 HRESULT:0x80070002)在 ModuleDemo.Main(String[] args)

建立配置文件
D:\mytest\ModuleDemo.exe.config

<?xml version="1.0"?>
<configuration>
  
<runtime>
    
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      
<probing privatePath="mymodule"/>
    
</assemblyBinding>
  
</runtime>
</configuration>

错误同上。该配置文件用来查找程序集的,模块必须依赖于程序集。参考【程序集与模块管理】。

将ModuleA.cs和ModuleB.cs分别编译为dll文件。
D:\mytest>csc /t:library ModuleA.cs
D:\mytest>csc /t:library /r:ModuleA.dll ModuleB.cs

新建文件夹:D:\mytest\mydll
将ModuleA.dll移动到D:\mytest\mydll
D:\mytest>csc /t:library /r:ModuleA.dll ModuleB.cs
error CS0006: 未能找到元数据文件“ModuleA.dll”

修改刚才建立的配置文件
<probing privatePath="mydll"/>
D:\mytest>csc /t:library /r:ModuleA.dll ModuleB.cs
error CS0006: 未能找到元数据文件“ModuleA.dll”

说明该配置文件对编译器不起作用。用如下命令才可以。
D:\mytest>csc /t:library /r:mydll\ModuleA.dll ModuleB.cs

将生成的ModuleB.dll也移动到mydll文件夹下。
D:\mytest>csc /t:exe  /r:MSCorLib.dll,ModuleA.dll,ModuleB.dll ModuleDemo.cs
error CS0006: 未能找到元数据文件“ModuleA.dll”
error CS0006: 未能找到元数据文件“ModuleB.dll”
要使用如下命令编译。
D:\mytest>csc /t:exe  /r:MSCorLib.dll,mytest\ModuleA.dll,mytest\ModuleB.dll ModuleDemo.cs

执行
D:\mytest>ModuleDemo.exe
ModuleA
ModuleB

修改配置文件
<probing privatePath="myxxx"/>

D:\mytest>ModuleDemo
未处理的异常:  System.IO.FileNotFoundException: 未能加载文件或程序集“ModuleA, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null”或它的某一个依赖项。
系统找不到指定的文件。文件名:“ModuleA, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null”在 ModuleDemo.Main(String[] args)

警告: 程序集绑定日志记录被关闭。
要启用程序集绑定失败日志记录,请将注册表值 [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD)设置为 1。
注意: 会有一些与程序集绑定失败日志记录关联的性能损失。
要关闭此功能,请移除注册表值 [HKLM\Software\Microsoft\Fusion!EnableLog]。

修改配置文件
<probing privatePath="mydll"/>

执行
D:\mytest>ModuleDemo.exe
ModuleA
ModuleB

删除mydll下的ModuleA.dll和ModuleB.dll,将mymodule下的ModuleA.netmodule和ModuleB.netmodule移动到mydll下,然后执行命令:
D:\mytest>al.exe /out:mydll\ModuleAB.dll /t:library mydll\ModuleA.netmodule mydll\ModuleB.netmodule
D:\mytest>csc /t:exe  /r:MSCorLib.dll,mydll\ModuleAB.dll ModuleDemo.cs
D:\mytest>ModuleDemo.exe
ModuleA
ModuleB

删除ModuleA.netmodule后再执行命令:
D:\mytest>ModuleDemo.exe
未处理的异常:  System.IO.FileNotFoundException: 未能加载文件或程序集“ModuleB.netmodule”或它的某一个依赖项。系统找不到指定的文件。
文件名:“ModuleB.netmodule” ---> System.IO.FileNotFoundException: 系统找不到指定的文件。 (异常来自 HRESULT:0x80070002) 在 ModuleDemo.Main(String[] args)

撤销删除。

=====================
@将程序集安装到全局程序集缓存
=====================
新建源代码文件 NamedDemo.cs

public class NamedDemo{
}

编译为dll文件。
D:\mytest>csc /t:library NamedDemo.cs

D:\mytest>【gacutil】 /i NamedDemo.dll
将程序集添加到缓存失败: 试图安装没有强名称的程序集

获取密钥文件。
创建一个名为MyCompany.keys(名称自定)的文件,生成一对以二进制而是存储的共有密钥和私有密钥。
D:\mytest>【SN】 -k MyCompany.keys
密钥对被写入 MyCompany.keys

从密钥对中提取公钥,并将其复制到一个单独的文件MyCompany.Publickey(名称自定)中。
D:\mytest>SN -p MyCompany.keys MyCompany.Publickey
公钥被写入 MyCompany.Publickey

生成强签名的程序集。
修改源代码。

using System.Reflection;
[assembly:AssemblyKeyFile(
"MyCompany.keys")]
public class NamedDemo{
}

D:\mytest>csc /t:library NamedDemo.cs
NamedDemo.cs(2,11): warning CS1699: 使用命令行选项“/keyfile”或适当的项目设置代替“AssemblyKeyFile”

根据提示,修改源代码

public class NamedDemo{
}

使用私钥进行程序集签名,执行命令:
D:\mytest>csc /t:library /keyfile:MyCompany.keys NamedDemo.cs

D:\mytest>gacutil /i NamedDemo.dll
程序集已成功添加到缓存中

D:\mytest>gacutil /u NamedDemo.dll
找不到与以下内容匹配的程序集: NamedDemo.dll
卸载的程序集数 = 0
失败次数 = 0

D:\mytest>gacutil /u NamedDemo
程序集: NamedDemo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=faeeaad7fd870aaa, processorArchitecture=MSIL
已卸载: NamedDemo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=faeeaad7fd870aaa, processorArchitecture=MSIL
卸载的程序集数 = 1
失败次数 = 0

查看公钥
D:\mytest>sn -tp MyCompany.Publickey
公钥为...
公钥标记为 faeeaad7fd870aaa
注意此处显示的公钥标记与卸载程序集时显示的PublickeyToken是一样的。

不能查看私钥。
D:\mytest>sn -tp MyCompany.keys
未能将密钥转换为标记 -- 程序集“(null)”的公钥无效。

不能使用公钥加密。
D:\mytest>csc /t:library /keyfile:MyCompany.Publickey NamedDemo.cs
error CS1548: 对程序集“d:\mytest\NamedDemo.dll”签名时加密失败
        --“密钥文件“d:\mytest\MyCompany.Publickey”缺少签名所需的私钥”

【参考资源】

SDK命令提示 http://msdn.microsoft.com/zh-cn/library/ms229859(VS.80).aspx   
csc.exe  http://msdn.microsoft.com/zh-cn/library/2fdbz5xd(VS.80).aspx
程序集  http://msdn.microsoft.com/zh-cn/library/hk5f40ct.aspx  
ILAsm.exe http://msdn.microsoft.com/zh-cn/library/496e4ekx.aspx
ILDasm.exe http://msdn.microsoft.com/zh-cn/library/f7dy01k1.aspx
元数据  http://msdn.microsoft.com/zh-cn/library/4y7k7c6k.aspx
AL.exe  http://msdn.microsoft.com/zh-cn/library/c405shex(VS.80).aspx
定位程序集 http://msdn.microsoft.com/zh-cn/library/yx7xezcf.aspx
程序集与托管模块 http://www.cnblogs.com/JoeDZ/archive/2008/06/25/1229662.html
gacutil.exe http://msdn.microsoft.com/zh-cn/library/ex0ss12c.aspx
sn.exe  http://msdn.microsoft.com/zh-cn/library/k5b5tt23.aspx
《Microsoft.NET框架程序设计》
《C#高级编程》(第四版)
学习笔记《Microsoft.NET框架程序设计》(Blog):http://blog.csdn.net/difall/

 

 

 

 

 

posted @ 2008-06-27 08:05  NanKe Sir's Blog  阅读(2345)  评论(0编辑  收藏