转载自:http://blog.csdn.net/byxdaz/article/details/6730702
一、程序集概念、特性
程序集构成了基于 .NET 的应用程序的部署、版本控制、重用、激活范围和安全权限的基本单元。程序集以可执行 (.exe) 文件或动态链接库 (.dll) 文件的形式出现,是 .NET Framework 的生成块。它们向公共语言运行时提供了解类型实现所需要的信息。可以将程序集看成是构成逻辑功能单元并为一起工作而生成的类型和资源的集合。
程序集可以包含一个或多个模块。例如,计划较大的项目时,可以让几个各个开发人员负责单独的模块,并通过组合所有这些模块来创建单个程序集。
程序集具有以下特点:
程序集作为 .exe 或 .dll 文件实现。
通过将程序集放在全局程序集缓存中,可在多个应用程序之间共享程序集。在将程序集包含在全局程序集缓存之前,必须对程序集进行强命名。有关更多信息,请参见具有强名称的程序集。
程序集仅在需要时才加载到内存中。如果不使用程序集,则不会加载。这意味着程序集可能是在大型项目中管理资源的有效途径。
可以使用反射以编程方式获取关于程序集的信息。有关更多信息,请参见反射。
如果加载程序集的目的只是对其进行检查,应使用诸如ReflectionOnlyLoadFrom的方法。
程序集清单
每个程序集中都有一个“程序集清单”。类似于目录,程序集清单包含以下内容:
1、程序集的标识(其名称和版本)。
2、文件表,描述组成程序集的所有其他文件,例如包括创建的.exe 或 .dll 文件所依赖的任何其他程序集,甚至包括位图文件或自述文件。
3、“程序集引用列表”是所有外部依赖项的一个列表,这些外部依赖项就是一些 .dll 或已由其他人创建而您的应用程序所需的其他文件。程序集引用同时包含对全局对象和私有对象的引用。全局对象驻留在全局程序集缓存中(可供其他应用程序使用的区域,有些类似于 System32 目录)。私有对象必须在应用程序安装目录的同一级(或下一级)目录中。
由于程序集包含有关内容、版本控制和依赖项的信息,因此用 Visual Basic 和 C# 创建的应用程序不依赖注册表值就可正常运行。程序集减少了 .dll 冲突,使应用程序更加可靠和更易于部署。许多情况下,只需将基于 .NET 应用程序的文件复制到目标计算机就可以安装该应用程序。
二、程序集分类
1、私有程序集
私有程序集是最简单的一种程序集类型。私有程序集一般附带在某些软件上,且只能用于该软件中。附带私有程序集的常见情况是,以可执行文件或许多库的方式提供应用程序,这些库包含的代码只能用于该应用程序。
系统可以保证私有程序集不被其他软件使用,因为应用程序只能加载位于主执行文件所在文件夹或其子文件夹中的程序集。
用户一般会希望把商用软件安装在它自己的目录下,这样软件包没有覆盖、修改或加载另一个软件包的私有程序集的风险。私有程序集只能用于自己的软件包,这样,用户对什么软件使用它们就有了更多的控制。因此,不需要采取安全措施,因为这没有其他商用软件用某个新版本的程序集覆盖原来的私有程序集的风险(但软件是专门执行怀有恶意的损害性操作的情况除外)。名称也不会有冲突。如果私有程序集中的类正巧与另一个人的私有程序集中的类同名,是不会有问题的,因为给定的应用程序只能使用私有程序集的名称。
因为私有程序集完全是自含式的,所以安装它的过程就很简单。只需把相应的文件放在文件系统的对应文件夹中即可(不需要注册表项),这个过程称为“0影响(xcopy)安装”。
2、共享程序集
共享程序集是其他应用程序可以使用的公共库。因为其他软件可以访问共享程序集,所以需要采取一定的保护措施来防止以下风险:
● 名称冲突,另一个公司的共享程序集执行的类型与自己的共享程序集中的类型同名。因为客户机代码理论上可以同时访问这些程序集,所以这是一个严重的问题。
● 程序集被同一个程序集的不同版本覆盖——新版本与某些已有的客户机代码不兼容。
这些问题的解决方法是把共享程序集放在文件系统的一个特定的子目录树中,称为全局程序集高速缓存(GAC)。与私有程序集不同,不能简单地把共享程序集复制到对应的文件夹中,而需要专门安装到高速缓存中,这个过程可以用许多.NET工具来完成,其中包含对程序集的检查、在程序集高速缓存中设置一个小的文件夹层次结构,以确保程序集的完整性。
为了避免名称冲突,共享程序集应根据私有密钥加密法指定一个名称(私有程序集只需要指定与其主文件名相同的名称即可)。该名称称为强名(strongname),并保证其惟一性,它必须由要引用共享程序集的应用程序来引用。
与覆盖程序集相关的问题,可以通过在程序集清单中指定版本信息来解决,也可以通过同时安装来解决。
一个强命名程序集包含四个唯一标志程序集的特性:文件名(没有扩展名),版本号,语言文化信息(如果有的话),公有密钥。
这些信息存储在程序集的清单(manifest)中。清单包含了程序集的元数据,并嵌入在程序集的某个文件中。
下面的字符串标识了四个不同的程序集文件:
“MyType, Version=1.0.1.0,
Culture=neutral, PublicKeyToken=bf5779af662fc055”
“MyType, Version=1.0.1.0,
Culture=en-us, PublicKeyToken=bf5779af662fc055”
“MyType, Version=1.0.2.0,
Culture=neturl, PublicKeyToken=bf5779af662fc055”
“MyType, Version=1.0.2.0,
Culture=neutral, PublicKeyToken=dbe4120289f9fd8a
创建和使用共享程序集有一些特殊的步骤,
1. 使用sn.exe创建加密文件,sn.exe -k test.snk
2. 在公共程序集的源代码中加入属性,[assembly:AssemblyKeyFileAttribute("test.snk")],习惯放在所有using的后面。或者通过projectproperties->Signing->Sign the assembly来指定加密文件
3. 使用gacutil -i test.dll安装共享程序集,安装后在C:/WINDOWS/assembly目录下将看到test文件
三、友元程序集
“友元程序集”是一种能够访问其他程序集的Friend(Visual Basic) 或 internal (C#) 类型和成员的程序集。如果将程序集指定为友元程序集,则不再需要将类型和成员标记为公共,以使其他程序集可以访问它们。在以下情况下,使用友元程序集尤其方便:
· 在单元测试中,测试代码在另一个程序集中运行,但需要访问正在测试的标记为Friend (Visual Basic) 或 internal (C#) 的程序集中的成员。
· 您正在开发类库,库的附加部分包含在单独的程序集中,但需要访问标记为Friend (Visual Basic) 或 internal (C#) 的现有程序集中的成员。
如何:创建未签名友元程序集
在 VisualStudio 中创建程序集和友元程序集
1. 打开 VisualStudio 命令提示。
2. 创建一个名为 friend_signed_A. 的 Visual Basic 或 C# 文件,其中包含以下代码。该代码使用 InternalsVisibleToAttribute特性将 friend_signed_B 声明为友元程序集。
// friend_unsigned_A.cs
// Compile with:
// csc /target:library friend_unsigned_A.cs
using System.Runtime.CompilerServices;
using System;
[assembly: InternalsVisibleTo("friend_unsigned_B")]
// Type is internal by default.
class Class1
{
public void Test()
{
Console.WriteLine("Class1.Test");
}
}
// Public type with internal member.
public class Class2
{
internal void Test()
{
Console.WriteLine("Class2.Test");
}
}
3. 使用以下命令编译friend_signed_A 并为其签名。
csc /target:library friend_unsigned_A.cs
4. 创建一个名为 friend_unsigned_B 的 Visual Basic 或 C# 文件,其中包含以下代码。由于 friend_unsigned_A 将 friend_unsigned_B 指定为友元程序集,因此friend_unsigned_B 中的代码可以访问 friend_unsigned_A 中的 Friend (Visual Basic) 或 internal (C#) 类型和成员。
// friend_unsigned_B.cs
// Compile with:
// csc /r:friend_unsigned_A.dll /out:friend_unsigned_B.exe friend_unsigned_B.cs
public class Program
{
static void Main()
{
// Access an internal type.
Class1 inst1 = new Class1();
inst1.Test();
Class2 inst2 = new Class2();
// Access an internal member of a public type.
inst2.Test();
System.Console.ReadLine();
}
}
5. 使用以下命令编译friend_signed_B。
csc /r:friend_unsigned_A.dll /out:friend_unsigned_B.exe friend_unsigned_B.cs
编译器生成的程序集的名称必须与传递给 InternalsVisibleToAttribute特性的友元程序集名称匹配。可以使用 /out 编译器选项显式设置该程序集。
在 C# 中,您必须使用/out 编译器选项显式指定输出程序集(.exe 或 .dll)的名称。在 Visual Basic 中,这是可选的。
6. 运行 friend_signed_B.exe文件。
程序将打印两个字符串:“Class1.Test”和“Class2.Test”。
安全性
InternalsVisibleToAttribute特性和 StrongNameIdentityPermission类之间存在相似性。主要的差异在于:StrongNameIdentityPermission可以要求具有安全权限才能运行特定的一段代码,而 InternalsVisibleToAttribute特性则控制 Friend (Visual Basic) 或 internal (C#) 类型和成员的可见性。
如何:创建签名的友元程序集
在 VisualStudio 中创建签名的程序集和友元程序集
1. 打开 VisualStudio 命令提示。
2. 使用强名称工具,通过以下命令序列生成 keyfile 并显示其公钥。有关更多信息,请参见Sn.exe(强名称工具)。
1. 生成此示例的强名称密钥,并将其存储在 FriendAssemblies.snk 文件中:
sn -kFriendAssemblies.snk
2. 从FriendAssemblies.snk 文件中提取公钥,将其放入FriendAssemblies.publickey 中:
sn -pFriendAssemblies.snk FriendAssemblies.publickey
3. 显示存储在FriendAssemblies.publickey 文件中的公钥:
sn -tpFriendAssemblies.publickey
3. 创建一个名为 friend_signed_A 的 Visual Basic 或 C#文件,其中包含以下代码。该代码使用 InternalsVisibleToAttribute特性将 friend_signed_B 声明为友元程序集。
强名称工具在每次运行时生成新的公钥。因此,必须将以下代码中的公钥替换为刚生成的公钥,如下例所示。
// friend_signed_A.cs
// Compile with:
// csc /target:library /keyfile:FriendAssemblies.snk friend_signed_A.cs
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("friend_signed_B, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e3aedce99b7e10823920206f8e46cd5558b4ec7345bd1a5b201ffe71660625dcb8f9a08687d881c8f65a0dcf042f81475d2e88f3e3e273c8311ee40f952db306c02fbfc5d8bc6ee1e924e6ec8fe8c01932e0648a0d3e5695134af3bb7fab370d3012d083fa6b83179dd3d031053f72fc1f7da8459140b0af5afc4d2804deccb6")]
class Class1
{
public void Test()
{
System.Console.WriteLine("Class1.Test");
System.Console.ReadLine();
}
}
4. 使用以下命令编译friend_signed_A 并为其签名。
csc /target:library /keyfile:FriendAssemblies.snk friend_signed_A.cs
5. 创建一个名为 friend_signed_B 的 Visual Basic 或 C#文件,并包含以下代码。由于 friend_signed_A 将friend_signed_B 指定为友元程序集,因此 friend_signed_B 中的代码可以访问 friend_signed_A 中的 Friend (VisualBasic) 或 internal (C#) 类型和成员。文件包含以下代码。
// friend_signed_B.cs
// Compile with:
// csc /keyfile:FriendAssemblies.snk /r:friend_signed_A.dll /out:friend_signed_B.exe friend_signed_B.cs
public class Program
{
static void Main()
{
Class1 inst = new Class1();
inst.Test();
}
}
6. 使用以下命令编译friend_signed_B 并为其签名。
不支持该语言或没有可用的代码示例。
csc /keyfile:FriendAssemblies.snk /r:friend_signed_A.dll /out:friend_signed_B.exe friend_signed_B.cs
编译器生成的程序集的名称必须与传递给 InternalsVisibleToAttribute特性的元程序集的名称匹配。可以使用/out 编译器选项显式设置该程序集。
在 C# 中,您必须使用/out 编译器选项显式指定输出程序集(.exe 或 .dll)的名称。在 Visual Basic 中,这是可选的。
7. 运行friend_signed_B.exe 文件。
程序将打印字符串“Class1.Test”。
四、如何把共享程序集添加到引用
1. 如何创建强名程序集
可以用VS来产生公钥密钥对文件, Project Properties à Signing
2. 如何查看public key token
可以用命令 sn–T [assembly]
3. 安装共享程序集
只有强名程序集才可以安装到Gac中。可以用命令
gacutil /i[assembly]
4. 卸载共享程序集
可以在C:/Windows/assembly目录下,找到要卸载的程序集,右键单击,然后选择Uninstall.
5. 如何对共享程序集在VS中添加引用
首先运行Start > Run >"C:/windows/assembly ".
选择你要找的程序集的文件夹,然后进入该文件夹,再选择正确的版本的文件夹。
找到你要的程序集后,可先将其临时拷贝到一个位置,再把这个拷贝程序集添加到引用中,然后便可以删除这个临时程序集拷贝,因为VS会自动在GAC中搜索该程序集。
五、如何:确定文件是否为程序集
如何手动确定一个文件是否为程序集
1、启动Ildasm.exe(MSIL 反汇编程序)。
加载希望测试的文件。
如果 ILDASM 报告该文件不是可迁移的可执行 (PE) 文件,则它不是程序集。有关更多信息,请参见主题 如何:查看程序集内容。
2、如何以编程方式确定一个文件是否为程序集
调用 GetAssemblyName方法,并向其传递正在测试的文件的完整文件路径和名称。
如果引发 BadImageFormatException异常,则该文件不是程序集。