认识程序集:1. 程序集的生成
程序集(Assembly)是.NET程序的最小组成单位。每个程序集都有自己的名称、版本等信息。程序集通常表现为一个文件(.exe或.dll文件),这样的程序集被称为单文件程序集,这种程序集是最常见的。程序集也可以由多个文件组成,每个文件都是一个模块文件或者是一个资源文件,这样的程序集被称为多文件程序集。下面来了解一下如何生成单文件程序集和多文件程序集。
1. 单文件程序集的生成
这是最常见的程序集,使用 Visual Studio 只能生成单文件程序集。使用 csc.exe 能够很轻松地生成单文件程序集:
csc.exe /target:library /resource:Hello.jpg Hello.cs
上述命令执行后,会生成一个名为 Hello.dll 的文件,该文件组成了单文件程序集。这个文件是一个托管模块,存储了类型元数据和中间语言,还嵌入了资源文件 Hello.jpg 的内容(通过使用 /resource 选项)。类型元数据描述了模块中定义的类型及其成员,中间语言是编译后的托管代码。
程序集本身其实是一个逻辑概念,对于自身的描述是通过程序集清单来实现的。程序集清单是一种元数据,描述了程序集的名称、版本、区域性和强名称信息,还包括组成程序集的文件的列表和资源信息,以及其他信息。在单文件程序集中,程序集清单的元数据直接保存在主模块文件中。
在本例中,程序集中的名称是Hello,版本没有提供,默认为0.0.0.0,区域性为语言中立,强名称信息为空,文件列表只有Hello.exe,托管资源为Hello.jpg。
2. 多文件程序集的生成
多文件程序集比较少见,而且除了使用 csc.exe 之外,有时还需要使用 AL.exe。假设有两个源代码文件 A.cs 和 B.cs,我们有两种生成多文件程序集的方法,这两种方法的主要区别在于程序集清单是存储在某个模块中还是单独存储于主模块中。
先看第一种方法,只使用csc.exe就可以了:
csc.exe /target:module A.cs
csc.exe /target:library /addmodule.A.netmodule /out:Hello.dll B.cs
第一个命令将 A.cs 编译为一个模块文件,A.netmodule,该文件仅包含类型元数据和中间语言,不包含程序集清单,是一种“原始”的模块文件,不能独立存在,必须从属于程序集。第二个命令将 B.cs 编译成模块文件,Hello.dll。这个文件除了包含类型元数据和中间语言之外,还存储了程序集清单,因为成为主模块文件。此时,程序集的名称仍然为 Hello,但文件列表的内容变成了两个:A.netmodule 和 Hello.dll。
再来看第二种方法,需要使用 csc.exe 和 al.exe 两个命令:
csc /target:module A.cs
csc /target:module B.cs
al.exe /target:library /out:Hello.dll A.netmodule B.netmodule
前两个命令将 A.cs 和 B.cs 编译为两个“原始”的模块文件,A.netmodule 和 B.netmodule。第三个命令使用了AL.exe(Assembly Linker,程序集链接器),该命令能够将 A.netmodule 和 B.netmodule 组合到程序集中。这样,最终会生成 Hello.dll 这个主模块文件,该文件将仅包含程序集清单。在这个程序集清单中,文件列表的内容变成了三个:A.netmodule,B.netmodule 和 Hello.dll。
多文件程序集不仅生成麻烦,而且应用似乎也不是很广泛,因为对于CLR来说,它只认识程序集,所以部署的时候,都是以程序集作为最小单位。一旦某个“原始”模块发生了改变,整个程序集都必须重新生成。另外,只有程序集有版本,模块作为程序集的组成部分,不能被版本化。
=============================================================
=============================================================
程序集(assembly)
注意可执行代码和库代码使用相同的程序集结构。惟一的区别是可执行的程序集包含一个主程序入口点,而库程序集则不包含。
程序集的一个重要特性是它们包含的元数据描述了对应代码中定义的类型和方法。程序集也包含描述程序集本身的元数据,这种程序集元数据包含在一个称为程序集清单的区域中,可以检查程序集的版本及其完整性。
注意:
ildasm是一个基于Windows的实用程序,可以用于检查程序集的内容,包括程序集清单和元数据。第15章将介绍ildasm。
程序集包含程序的元数据,表示调用给定程序集中的代码的应用程序或其他程序集不需要指定注册表或其他数据源,以便确定如何使用该程序集。这与以前的COM有很大的不同,以前,组件的GUID和接口必须从注册表中获取,在某些情况下,方法和属性的详细信息也需要从类型库中读取。
把数据分散在3个以上的不同位置上,可能会出现信息不同步的情况,从而妨碍其他软件成功地使用该组件。有了程序集后,就不会发生这种情况,因为所有的元数据都与程序的可执行指令存储在一起。注意,即使程序集存储在几个文件中,数据也不会出现不同步的问题。这是因为包含程序集入口的文件也存储了其他文件的细节、散列和内容,如果一个文件被替换,或者被塞满,系统肯定会检测出来,并拒绝加载程序集。
程序集有两种类型:共享程序集和私有程序集。
1.4.1
系统可以保证私有程序集不被其他软件使用,因为应用程序只能加载位于主执行文件所在文件夹或其子文件夹中的程序集。
用户一般会希望把商用软件安装在它自己的目录下,这样软件包没有覆盖、修改或加载另一个软件包的私有程序集的风险。私有程序集只能用于自己的软件包,这样,用户对什么软件使用它们就有了更多的控制。因此,不需要采取安全措施,因为这没有其他商用软件用某个新版本的程序集覆盖原来的私有程序集的风险(但软件是专门执行怀有恶意的损害性操作的情况除外)。名称也不会有冲突。如果私有程序集中的类正巧与另一个人的私有程序集中的类同名,是不会有问题的,因为给定的应用程序只能使用私有程序集的名称。
因为私有程序集完全是自含式的,所以安装它的过程就很简单。只需把相应的文件放在文件系统的对应文件夹中即可(不需要注册表项),这个过程称为“0影响(xcopy)安装”。
1.4.2
●
●
这些问题的解决方法是把共享程序集放在文件系统的一个特定的子目录树中,称为全局程序集高速缓存(GAC)。与私有程序集不同,不能简单地把共享程序集复制到对应的文件夹中,而需要专门安装到高速缓存中,这个过程可以用许多.NET工具来完成,其中包含对程序集的检查、在程序集高速缓存中设置一个小的文件夹层次结构,以确保程序集的完整性。
为了避免名称冲突,共享程序集应根据私有密钥加密法指定一个名称(私有程序集只需要指定与其主文件名相同的名称即可)。该名称称为强名(strong name),并保证其惟一性,它必须由要引用共享程序集的应用程序来引用。
与覆盖程序集相关的问题,可以通过在程序集清单中指定版本信息来解决,也可以通过同时安装来解决。
1.4.3
======================================================
======================================================
C#之程序集
程序集的定义:
程序集和托管模块的关系:
│
├─ko-kr
│
├─it
│
├─fr
│
├─es
│
├─en
│
├─en-us
│
└─de
通过AL工具来改变一个程序集的各种属性:
通过AssemblyInfo.cs文件来改变一个程序集的属性:
将程序集组成各种应用程序,进行程序集的部署:
=============================================================
=============================================================
浅析.Net共享程序集编程
从《为.Net程序集添加资源》和《浅析.Net下的AppDomain编程》两篇文章中我们知道,.Net结构里的程序集Assembly是自我描述的安装单元,它在应用程序域(AppDomain)中运行。您必须首先将程序集加载到应用程序域中,然后才能运行该应用程序,并且,同一程序集可以加载到多个应用程序域中,根据这些应用程序域对该程序集代码使用方式的不同产生了程序集的分类:私有程序集和共享程序集。
1、私有程序集
我们通常用到的就是私有程序集。这种情况下,我们创建本地应用程序项目(或组件),编译后就生成dll或exe类型的私有程序程序集。当我们在其他客户应用程序中使用这类程序集时,只需要添加引用。当这样的程序集被多个应用程序域使用时,每个应用程序域需要复制该程序集,进程中也将存在该程序集的多个副本。
2、共享程序集
与私有程序集相对的是共享程序集(Shared Assembly),它提供多个应用程序域访问同一个程序集的能力,特别地,内存中只存在该程序集的同一份副本,这种非特定于域的代码共享极大节省了内存资源占用。并且,在大多数情况下,共享程序集安装在全局程序集高速缓冲存储器(Global Assembly Cache)中而不存在于应用程序相关目录下,对它的引用不会产生文件复制,自然也不会产生额外的副本。因而,共享程序集不能简单通过XCOPY命令实现部署,而应使用MSI(Microsoft Windows Installer)进行。当组件和主应用程序不由同一个开发商建立,或者一个大应用程序分布在几个小工程中时,常常需要使用共享程序集。
创建名称唯一的共享程序集
与私有程序集不同,使用共享程序集时要遵循许多规则。特别地,共享程序集必须有一个唯一的名称(称为强名StrongName)。这个名称被要求必须是全局唯一的,并且应该能够保护该名称,其他人不能再使用它创建同名程序集。一般地,我们通过嵌套命名空间层次结构来满足这一要求。结合公司名称、项目类别(类似文件夹分类)等命名项目类等对象,可以在一定程度上避免程序集重名。而配合使用公共/私有密钥机制,则可以彻底保证名称的唯一性。(关于密钥机制请参见专门的文章),下面简要说明强名称工具Sn.exe在这方面的应用:
生成公共/私有密钥对文件
.Net结构中的强名称工具Sn.exe有助于使用强名称对程序集进行签名,而通过签名具有强名称的程序集,就可以确保名称的全局唯一性。Sn.exe工具提供用于密钥管理、签名生成和签名验证的选项。其重要的一个应用是生成一个新的密钥对并将其写入指定的文件。引用这个密钥对文件的共享程序集将能够保证唯一的名称。
下面的命令行语句创建一个新的随机密钥对并将其存储在 myKey.snk 文件中。
sn -k myKey.snk
(Sn.exe还有很多参数,请参见Microsoft技术支持)
修改属性为程序集应用强名称
下一步,使用程序集属性将强名称信息引入代码中。属性AssemblyKeyFileAttribute
在代码模块中,添加AssemblyKeyFileAttribute
[assembly:AssemblyKeyFileAttribute
在全局程序集高速缓冲存储器GAC(Global Assembly Cache)中安装共享程序集
在我们使用Dll类型的私有程序集时,需要添加该程序集引用。而当私有程序集是exe类型时,还需要将它显式复制到当前应用程序的可执行目录(通常是当前工程的BinDebug目录)中。其实,对DLL类型私有程序集的引用实质上也隐式进行了复制。添加应用后,在当前应用程序可执行目录或其子目录下,你就能够找到被引用的程序集文件(其实正是因为这样的特性,私有程序集安装很简单,只需复制改程序集中的所有文件,一个xcopy命令就足够了,这就是0压缩安装)。
与此类似,使用共享程序集也需要添加引用。不同的是,引用共享程序集不产生复制,而是在使用共享程序集前将其安装到全局程序集高速缓冲存储器GAC(Global Assembly Cache)中。.Net提供的命令行工具gacutil.exe用于支持这一功能。gacutil.exe可以将具有强名称的程序集添至全局程序集缓存。命令格式为:
gacutil -I <程序集名称>
其中,"程序集名称"是要在全局程序集缓存中安装的程序集的名称。
下面的示例语句将文件名为 myAssembly.dll 的程序集安装到全局程序集缓存:
gacutil -i myAssembly.dll
在客户应用程序中使用共享程序集
在客户应用程序中使用共享程序集的方法与私有程序集一样简单。创建客户应用程序后,以与引用私有程序集相同的方式引用共享程序集,在应用程序代码中包含共享程序集命名空间(using语句),这之后,你就可以象使用本地对象一样使用共享程序集的公共对象了。
综合上述,在.Net中使用VS.NET SDK进行共享程序集编程可分为以下步骤:
1)、生成共享程序集代码文件(组件、类库等)
2)、创建密钥文件并签名共享程序集
3)、在GAC中安装共享程序集
4)、在客户应用程序中使用共享程序集
下面的例子详细说明以上过程。为节省篇幅,示例使用控制台应用程序。Windows Forms应用程序中共享程序集编程与此类似。
1)、生成共享程序集代码文件
这里,创建一个的Windows Class Library 类库工程TestCreateSharedAssembly
public string GetCreateDateTime()
{
}
2)、创建密钥文件并签名共享程序集
首先,使用Sn.exe强名工具生成密钥对文件myKey.snk:
Sn.exe -k myKey.snk;
然后,修改AssemblyInfo.cs文件以签名当前程序集:
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile(@"d:winappmyKey.snk")](请指定myKey.snk文件的正确路径)
[assembly: AssemblyKeyName("")]
这之后,编译项目就将生成共享程序集TestCreateSharedAssembly
3)、在GAC中安装共享程序集
在程序集中包含了密钥对文件后,就可以使用全局程序集高速缓冲存储器gacutil的/I选项把它安装到全局程序集库(Global Assembly Store)中,下面的语句将前一步创建的共享程序集TestCreateSharedAssembly
gacutil /I TestCreateSharedAssembly
4)、在客户应用程序中使用共享程序集
现在,创建一个Windows Console控制台应用程序TestUseSharedAssembly。首先,引用前面创建的共享程序集TestCreateSharedAssembly
在当前应用程序代码文件中,使用using语句包含欲使用共享程序集所在的命名空间TestCreateSharedAssembly
using System;
using TestCreateSharedAssembly
namespace TestUseSharedAssembly
{
class Class1
{
static void Main(string[] args)
{
TestCreateSharedAssembly
string
datetime=tcsa.GetCreateDateTime();//调用所应用程序集的公共方法Console.WriteLine("Shared
Assembly Create Time is:"+datetime);
}
}
}
编译当前工程并在命令行中运行它,就会显示所引用共享程序集的创建日期和时间信息:
Shared Assemby Create Time is:2002年12月10日
==================================================
==================================================
认识程序集:2. 生成强名称程序集 (转载)
通过嵌入公钥并使用私钥签名,可以生成强名称(strong name)的程序集。强名称程序集由4部分进行标识:名称、版本、区域性和公钥。与之相对的,我们可以把没有嵌入公钥和使用私钥签名的程序集称之为弱名称(weak name)程序集(这个术语是Jffery Richter创造的)。强名称程序集与弱名称程序集相比,有以下特点:
强名称程序集可以保证唯一性。公/私密钥对是由发行者自行生成的,是唯一的,保证了程序集的标识不会重复。
强名称程序集可以防篡改。强类型程序集使用私钥对自己进行了签名,这样在被加载时可以检查程序集是否被修改。
强名称程序集可以实施版本策略。对于弱名称程序集,引用它的程序不会关心它的版本,而对于强类型的程序集来说,引用它的程序会被绑定到特定版本的程序集上,如果使用新版本的强名称程序集替换旧版本,会导致程序无法运行。(当然还可以使用配置文件对强名称程序集进行重定向)。
强名称程序集可以部署到GAC中。GAC指全局程序集缓存,这是一个公共目录,放在此处的程序集可以被本机任意一个程序所引用。弱名称程序集无法部署到此处。不同版本的相同程序集还可以同时存在于GAC中。
强名称程序集只能引用强名称程序集。弱名称程序集可以引用强名称程序集,也可以引用弱名称程序集,但强名称程序集只能引用强名称程序集。
强名称程序集支持并行执行。并行(side-by-side)执行是指程序同时引用了多个版本的同名程序集,这样在运行时,会有多个版本的同名程序集被加载和同时执行。通常不建议使用。
下面来研究一下如何生成强名称的程序集。首先,使用SN.exe创建一个密钥文件:
sn.exe -k MyKey.snk
生成的文件包含了公钥和私钥的内容。我们可以查看公钥的内容,私钥是不允许查看的,所以要先将公钥提取出来。仍然是使用SN.exe:
sn -p MyKey.snk MyPublicKey.snk
sn -tp MyPublicKey.snk
前一个命令将密钥文件中的公钥提取出来,放到 MyPublicKey.snk 文件中;后一个命令用于显示该文件中的公钥和公钥标记(Public key token),显示的内容可能如下(每个人生成的都不同):
Microsoft (R) .NET Framework Strong Name Utility
Copyright (c) Microsoft Corporation.
Public key is
002400000480000094000000
4763250746c094e45db0c715
1a39fbea9d0bee001c093b22
7955572b4174125494a593c1
e6812db5
Public key token is 337642649f453c2c
公钥标记是公钥的64位散列值,用于简化对公钥的引用。
第二步是创建强名称程序集。我们可以在源文件中使用AssemblyKeyFileAttribute
csc /t:library /keyfile:MyKey.snk MyType.cs
运行后得到 MyType.dll ,我们可以显示其中包含的公钥标记,看是否和上面的相同:
sn -Tp MyType.dll
显示内容如下:
Microsoft (R) .NET Framework Strong Name Utility
Copyright (c) Microsoft Corporation.
Public key is
002400000480000094000000
4763250746c094e45db0c715
1a39fbea9d0bee001c093b22
7955572b4174125494a593c1
e6812db5
Public key token is 337642649f453c2c
由此可见,公钥的内容确实嵌入到了程序集当中。除此之外,程序集的全部内容经过散列编码后,还使用密钥进行了签名,也嵌入到了程序集中。
这样我们就得到了一个强名称程序集。如果有程序引用了该程序集,会记录由以下内容标识的程序集:
MyType, Version=1.0.3087.28686, Culture=neutral, PublicKeyToken=337642649f453c2c
这些内容唯一的标识了一个强名称程序集,由于公钥太长,这里只引用了公钥标记。当程序运行时,CLR 会根据这些内容去搜寻程序集,只有完全匹配的程序集才会被加载,即便是版本的细微差别都不会忽略。如果没有找到,或者找到的程序集不匹配,都会产生异常。