`
阅读更多

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

【概念阐述】
@将源代码编译为托管模块
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并输入以下实验代码

///////////////////////////////////////////////////////////////////////////
using System;
// 自定义类型 App。
public class App{
 private Int32 afield;
 static public void Main(System.String[] args){
  // 引用类型 System.Console。
  System.Console.WriteLine("Hi");
 }

 // 构造函数
 public App(Int32 fi){
  afield = fi;
 }
 
 // 析构函数
 ~App(){}

 // 重载操作符
 public static Boolean operator == (App a1, App a2){  
  return true;
 }

 public static Boolean operator !=  (App a1, App a2){
  return false;
 }

 public static App operator + (App a1, App a2){
  return null;
 }

 // 属性
 public Int32 Aproperty{
  get{ return afield;}
  set{ afield = value;}
 }

 // 索引器
 public String this[Int32 x]{
  get{ return null; }
  set{}
 }

 event EventHandler AnEvent;
}
/**////////////////////////////////////////////////////////////////////////////

 

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)建立三个源代码文件

//==========================
//D:\mytest\ModuleDemo.cs
using System;
public class ModuleDemo{
 public static void Main(string[] args){
  ModuleA a = new ModuleA();
  ModuleB b = new ModuleB();
  Console.WriteLine(a.ToString() + "\n" + b.ToString());
 }
}

//D:\mytest\ModuleA.cs
public class ModuleA{
}

//D:\mytest\ModuleB.cs
public class ModuleB{
 private ModuleA moduleA;
}
//==========================

 

将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/

分享到:
评论

相关推荐

    Flash .net程序集

    Flash .NET程序集是开发基于.NET Framework的Flash播放器应用时不可或缺的一部分。这些程序集主要包含与Adobe Flash Player交互所需的类库,使得开发者能够在.NET环境中创建、控制和嵌入Flash内容。以下是关于这个...

    .Net程序集信息提取器(含源码)

    《.Net程序集信息提取器:深入理解与应用》 .NET程序集是.NET框架的核心组成部分,它是.NET应用程序的构建块,包含了代码、元数据以及资源等。本文将围绕标题为".Net程序集信息提取器(含源码)"的项目,探讨如何使用...

    反编译工具 反编译.net程序集

    .NET程序集是微软.NET框架中的一种二进制文件格式,它包含了可执行代码、元数据以及资源等。这些文件通常有.dll或.exe扩展名。在软件开发过程中,有时需要查看程序集内部的源代码,这就要用到反编译工具。本文将详细...

    详解.NET程序集的加载规则

    .NET程序集的加载规则是.NET框架中至关重要的概念,它涉及到程序运行时如何找到和加载所需的组件。程序集是.NET中的基本单元,包含了代码、元数据以及资源,用于构成应用程序。本文将深入探讨.NET程序集的加载机制,...

    .Net程序集信息提取器

    《.Net程序集信息提取器:深入理解与应用》 .Net程序集是.NET框架的核心组成部分,它是.NET应用程序的构建块,包含了代码、元数据以及执行.NET应用程序所需的所有资源。本文将详细探讨.Net程序集信息提取器的工作...

    VS中.NET开发添加程序集引用

    本文将深入探讨如何在VS中添加.NET程序集引用,并详细讲解其工作原理。 程序集是.NET Framework的基本模块,它包含了执行.NET应用程序所需的所有元数据和IL(Intermediate Language)代码。程序集可以是DLL(动态...

    .NET 程序集浏览器和反编译器

    ILSpy是一款开源的.NET程序集浏览器和反编译器,它能够解码并显示编译后的.NET程序集的源代码。ILSpy的最新版本支持Visual Studio 2022,这意味着它与.NET框架的最新进展保持同步,为开发者提供了便捷的接口来理解和...

    .net 程序集合并

    本文将深入探讨.NET程序集合并的原理、方法以及可能的影响。 首先,我们要理解.NET程序集的基本概念。在.NET框架中,程序集是代码的物理单元,包含元数据、类型和资源,可以是DLL或EXE文件。当多个DLL需要一起工作...

    .Net程序集反编译工具 ILSpy2.1.0.1603(中文版)

    《.NET程序集反编译工具ILSpy2.1.0.1603:深入理解与应用》 ILSpy是一款强大的.NET程序集反编译工具,它以其免费、开源和用户友好的特性,在开发社区中广受欢迎。ILSpy2.1.0.1603版本特别为中国用户提供了中文界面...

    Sandcastle(.net程序集CHM帮助文件生成器)

    《Sandcastle:.NET程序集CHM帮助文件生成器详解》 在软件开发过程中,文档是不可或缺的一部分,它为开发者提供了清晰的指引和参考。在.NET框架中,Sandcastle工具正是用于生成高质量、详尽的程序集文档,尤其是CHM...

    asp.net程序集扩展文件

    asp.net 验证控件必要文件,将此文件添加bin目录即可;需要的可以下载,本人经常使用此文件

    使用.net 程序集 对windows api 的封装(源码)

    在.NET框架中,开发者通常利用.NET程序集来构建应用程序,但有时需要调用Windows API以实现更底层的功能,这是.NET框架未涵盖的部分。本主题主要围绕如何使用.NET程序集对Windows API进行封装,特别是对`kernel32`和...

    使用.net 程序集 对windows api 的封装

    使用.net 程序集 对windows api 的封装,包含 user32,kernel32 等核心api,便于.net开发者使用 windowsAPI

    Reflexil:.NET程序集编辑器

    反光 .NET程序集编辑器Reflexil是一个程序集编辑器,可作为Red Gate的Reflector , ILSpy和Telerik的JustDecompile的插件运行。 Reflexil使用的是由Jb Evain编写的Mono.Cecil,并且能够操纵IL代码并将修改后的程序集...

    调用.net程序集中的方法

    Biztalk Server虽然自身提供丰富的功能,但有时需要自定义或扩展功能时,就需要借助于.NET程序集。 首先,我们要理解如何创建和发布.NET程序集。在示例中,创建了一个名为"MyFunction"的项目,包含一个名为"class1...

    RefExplorer:显示 .NET 程序集之间依赖关系的工具

    .NET Reference Explorer(简称 RefExplorer)是一个 Windows 软件,用于显示 .NET 程序集之间的依赖关系。 您可以使用它来验证您的软件架构或查找错误。 结果是一个有向图,显示了引用的程序集以及它们是否可以...

    RunDLL.Net:使用Rundll32.exe执行.Net程序集

    运行DLL 使用Rundll32.exe执行.Net程序集用法: rundll32 Rundll.Net.dll,main &lt;assembly&gt; &lt;class&gt; &lt;method&gt; [(type)][arg1] [(type)][arg2]...rundll32 Rundll.Net.dll,main C:\Program.dll MyProgram.Program Do...

    .NET程序集反编译器Reflector

    Reflector 显示所有托管程序集的类型、方法、属性和事件。Reflector 不只是一个简单的类浏览器界面,它还可以分析其他方法和属性调用特殊类型、方法或属性的方式,以及与其他方法和属性之间的依赖关系。此外, ...

    .NET程序反编译工具 - ILSpy ver 1.0.0.1000

    ILSpy的全称是Intermediate Language Spy,其核心功能是对.NET程序集进行反编译,将已编译的.NET程序集(.dll或.exe文件)转换回源代码形式,便于开发者查看和理解代码逻辑。它不仅能展示元数据信息,还可以解析程序...

Global site tag (gtag.js) - Google Analytics