强命名程序集的缘由:
目前Windows中出现的DLL Hell问题(两个不同的公司可能开发处具有相同名称的程序
集,如果将相同名称的程序 集放置到同一个目录下,则会出现程序集覆盖现象,最后安装的
程序集会覆盖前面的程序集,从而可能导致应用序不能正常运行)。
由此看来,仅靠名称来区分程序集是不足够的。CLR采取了强命名程序集的方式来唯一的
表示程序集。强命名程序集包含四个标识:名称,版本号,语言文化标识和一个共有/私有密钥对。
两种程序集/两种部署方式:.Net支持两种程序集:弱命名程序集和强命名程序集(注:.Net框架中没有弱命名程序集,只是为了和强命名程序集相对应而已)。
弱命名程序集和强命名程序集在结构上是相同的。他们都采用PE文件格式,包含PE表头,CLR表头,元数据和清单表。
区别在于:强命名程序集拥有一个发布者的公钥/私钥签名对,他们用于唯一的标识程序集的发布者。通过公钥/私钥对,我们可以对程序集进行唯一的标识,安全策略和版本策略。
强名称主要用处:就是防止dll被随意引用,另外控制版本,标识唯一性。
如何创建强命名程序集
1. 在Visual Studio中的class library工程上点右键, 选择properties.
2. 选择左边的Signing选项卡.
3. 勾选Sign the assembly复选框. 在下拉列表中选择<New...>.
4. 在弹出的对话框中给snk文件起一个名字. 按OK.
(也可以通过sn.exe生成key)
5.在Signing选项中选中强名称key文件,保存生成
(AssemblyInfo.cs主要用来设定生成的有关程序集的常规信息dll文件的一些参数)
签名防止程序集的伪造,签名的具体过程是:
(1)、在编译后签名前对程序集的所有非主模块计算出其散列值,并保存在主模块清单中的FileDef中;
(2)、通过一定的方式对存在于主模块清单FileDef中的散列值和主模块一起计算出其对应的散列值,并将散列算法的引用保存在主模块清单中的AssemblyDef中;
(3)、对上面算出的主模块的散列值通过密钥(私钥)进行加密,并将加密后的值作为数字签名保存在主模块的CLR文件头中,并将公钥保存在主模块清单的AssemblyDef中。
这样就通过这种公钥/密钥对可以互相加密解密的特性,可以保证如果通过公钥解密的数字签名和当前主模块计算出散列值相同则为真正的程序集,否则则为伪造。
这里如果在程序集的开发过程中,有大量的人介入,每个负责编译强名称程序集的人都需要访问包含公钥/密钥对文件,这样就很难保证密钥不会被泄露出去,为了尽量防止这种情况的发生,便诞生了一种叫做延迟签名(delayed signature)的机制,这种机制的运作方式是:
(1)、首先通过sn -p命令产生一个只包含公钥信息的.snk文件,例如:sn -p MyKey.snk MyPubKey.snk,
后面的这个MyPubKey.snk便是一个只包含公钥信息的.snk文件;
(2)、然后呢通过用true来初始化AssemblyDelaySignAttribute来激活delayed signature,这样开发人员直接使用MyPubKey.snk来代替MyKey.snk来进行开发,编译器不会对主模块进行签名,但是会保留出主模块中用来签名的空间,并且还会将MyPubKey.snk中的公钥信息插入到主模块清单中的AssemblyDef中,并且计算各非主模块的散列值插入到主模块清单的FileDef中。
(3)、最后当开发完毕准备打包部署时,只要使用sn.exe工具的-R(注意大写)选项就可以完成签名,例如:sn.exe -R Assname.exe MyKey.snk。
可以通过sn -T(注意大写)来查看程序集的公钥记号(Public Key Token)。如果程序集的公钥记号是b03f5f7f11d50a3a那么为微软公司提供的,如果是b77a5c561934e089那么是ECMA组织提供的,除非他们的密钥被破解,或者改变,否者这是永久成立的。
区域设置(Culture)是为了程序能够让不同的国家、不同语言的用户来使用而产生的一种机制,他需要为程序支持的每一种区域设置提供一组应用程序呈现给用户的内容(字符串、图片、动画、声音、日期和数字格式等),着被称为应用程序的全球化(globalization)或国际化(internationalization)或本地化(localization)。
区域设置是国家和语言的组合体,比如“en-US”就是表示英语(美国)“fr-CA”表示法语(加拿大),区域设置通过CultureInfoAttribute来实现。
区域设置作为程序集强名成的一部分,但是任何包含代码的程序集必须采用区域无关的设置即提供给CultureInfoAttribute的字符串为空,如果过要用区域设置作为资源的索引就要创建一种仅包含资源的程序集--卫星程序集(satellite assembly)。
GAC的作用
全称是Global Assembly Cache作用是可以存放一些有很多程序都要用到的公共Assembly,例如System.Data、System.Windows.Forms等等。这样,很多程序就可以从GAC里面取得Assembly,而不需要再把所有要用到的Assembly都拷贝到应用程序的执行目录下面。举例而言,如果没有GAC,那么势必每个WinForm程序的目录下就都要从C:\WINDOWS\Microsoft.NET\Framework\vX下面拷贝一份System.Windows.Forms.dll,这样显然不如都从GAC里面取用方便,也有利于Assembly的升级和版本控制。
主要的优点包括:
单一部署
由于同一台机器上的所有应用程序都从GAC加载共享程序集,所以不需要为每个应用程序都部署程序集。这有别于.NET框架默认的“私有程序集”,它必须随同每个应用程序来部署。
绕过验证
如前所述,强名称允许CLR的类加载器验证程序集自编译后便没有被篡改过。将程序集放到GAC中,只有在程序集首次放入GAC时才会执行验证,而不是应用程序每次加载它时都执行验证,这有助于提升性能。
减少工作量
如果多个应用程序引用同一个共享程序集,所有应用程序都从相同的位置加载程序集。因此,操作系统在所有应用程序中共享程序集的代码页,这减少了内存占用。
集中更新
程序集部署到GAC后,就可集中部署修正内容。虽然应用程序默认使用程序当初编译时的版本,而且GAC允许相同程序集的多个版本并存,但只需在 machine.config文件中添加一个版本策略,即可强迫机器上的所有应用程序使用新版本,而不是使用旧版本。除此之外,一旦程序集包含强名称,其他代码就可指定:只有来自具有特定强名称的一个程序集中的代码才能调用自己。
安装问题
具有强名称的程序集通常放在GAC中,假如应用程序必须将程序集安装到GAC中,安装过程就必然会复杂一些。幸好,用Visual Studio .NET创建的Windows Installer项目可将程序集自动安装到GAC中。此外,开发者还可使用命令行实用程序Gacutil.exe。
在VS的“添加引用”对话框中的.NET选项卡中显示GAC 程序集
.NET的GAC程序集默认是放在目录C:\WINDOWS\assembly\(不同的vs版本与环境对应的位置不同)下的,但是你会发现就算将GAC程序集放在该目录下,在VS的添加引用的.NET选项卡列表里还是看不到该程序集
这里介绍两个方法来解决该问题
方案一:在“添加引用”对话框中显示程序集
即使已将程序集安装到全局程序集缓存 (GAC) 中,“添加引用”对话框也不会自动显示每个程序集。“添加引用”对话框基于路径并要求添加注册表项,该项指定要显示的程序集的位置。
在“添加引用”对话框中显示程序集
需要添加以下注册表项之一,其中 <AssemblyLocation> 是要在“添加引用”对话框中显示的程序集目录,例如 C:\\MyAssemblies。
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\.NETFramework\<version>\AssemblyFoldersEx\MyAssemblies]@="<AssemblyLocation>"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\<version>\AssemblyFoldersEx\MyAssemblies]@="<AssemblyLocation>"
注意
在 HKEY_LOCAL_MACHINE 配置单元下创建注册表项以后,所有用户都可以在“添加引用”对话框的指定位置看到程序集。在 HKEY_CURRENT_USER 配置单元下创建注册表项只会影响当前用户的设置。
重新启动 Visual Studio。
方案二:可以把你的DLL复制到.NET Framework目录(%WINDIR%\Microsoft.NET\Framework\v2.x.yyyy)中,这样的话.可以达到你的目的. 但是,MS建议我们不应该将自己的assembly拷贝到Framework目录中。一个更好的方法就是把它们放在一个单独的目录中,然后给Windows Registry添加一个key,告诉VS.NET在哪里可以找到这些assembly。除了核心的Framework assembly外,VS.NET也可以显示在以下这些Registry key中列出的目录中的任何assembly:HKEY_CURRENT_USER\Software\Microsoft\.NETFramework\AssemblyFolders
HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\AssemblyFolders
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\7.1\AssemblyFolders
HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\AssemblyFolders