【CLR】程序集查找与GAC

1. 各种各样的版本号

在这里插入图片描述

如图:

  • AssemblyVersion程序集版本号:存储在AssemblyDef清单元数据表中,虽然在文件属性面板中不显示,但是这个版本号对于CLR来说很重要,绑定强明明程序集时会用到这个,它唯一地标识了程序集。当A程序集引用了B程序集时,会将B程序集的AssemblyVersion嵌入到自己的AssemblyDef清单中,这样当CLR加载B的时候,就能准确的知道是程序集B的哪个版本。
  • AssemblyFileVersion文件版本号:即属性面板中的文件版本,存储在Win32版本资源中,这个号仅供参考,CLR既不会检查,也不关心这个版本号。
  • AssemblyInformationVersion:属性面单中的产品版本,存储在Win32版本资源中,当Assembley.cs中未设置此项时,属性中的产品版本将于AssemblyFileVersion保持一致。也是仅供参考,CLR不检查不适用。没什么用。

版本号的格式:major主版本号.minor次版本号.build内部版本号.reversion修订号。
前两个版本号构成了公众对版本的理解。
如果公司每天都编译这个程序集,那build号每天都需要递增。如果一天内需要多次编译程序集,则保持同一个build一致的情况下,增加修订号。

2. 程序集的查找

很多开发者在编写代码过程都有这么一个需求,喜欢把引用第三方的一些dll放到根目录单独的一个文件夹中,使看起来更有条例一点。
比如我们有个控制台程序ConsoleTest,引用一大堆第三方dll,想把这些dll都放到与ConsoleTest.exe同级的一个文件夹subLib1和subLib2里,大致结构可能如下:

  • 根目录
    • ConsoleTest.exe
    • ConsoleTest.exe.config
    • subLib1
      • a.dll
      • b.dll
    • subLib2
      • c.dll
      • d.dll

此时运行程序的话,因为查找不到引用的dll,会报FileNotFoundException。所以需要在ConsoleTest.exe.config文件中做如下配置(必须是在应用程序的主程序集文件名的config文件中配置)。

<configuration>
	<runtime>
		<assemblyBinding xmlns:"urn:schemas-microsoft-com:asm.v1">
			<probing privatePath="subLib1;subLib2" />
		</assemblyBinding>
	</runtime>
</configuration>

probing就制定了要查找的字目录,privatePath只能是根目录下的相对目录,不能指向根目录意外的目录。

2.1 CLR查找程序集顺序

这里假如CLR要查找一个AsmName.dll的程序集,而且程序的根目录叫AppDir,subLib1和subLib2已经在privatePath中配置过。查找顺序如下:

  • AppDir\AsmName.dll
  • AppDir\AsmName\AsmName.dll(上一步没找到,就往AsmName文件夹内找AsmName.dll文件)
  • AppDir\subLib1\AsmName.dll
  • AppDir\subLib1\AsmName\AsmName.dll
  • AppDir\subLib2\AsmName.dll
  • AppDir\subLib2\AsmName\AsmName.dll
  • 如果再以上各个目录中都没找到目标程序集,则会用exe后缀替换dll后缀继续查找
  • AppDir\AsmName.exe
  • AppDir\AsmName\AsmName.exe
  • AppDir\subLib1\AsmName.exe
  • AppDir\subLib1\AsmName\AsmName.exe
  • AppDir\subLib2\AsmName.exe
  • AppDir\subLib2\AsmName\AsmName.exe

以上查找是基于中性语言文化的查找,如果向AsmName.dll应用了"en-US"语言文化,那么就会这么查找

  • AppDir\en-US\AsmName.dll
  • AppDir\en-US\AsmName\AsmName.dll
  • AppDir\en-US\subLib1\AsmName.dll
  • AppDir\en-US\subLib1\AsmName\AsmName.dll
  • AppDir\en-US\AsmName.exe
  • AppDir\en-US\AsmName\AsmName.exe
  • AppDir\en-US\subLib1\AsmName.exe
  • AppDir\en-US\subLib1\AsmName\AsmName.exe
  • 如果没en-US文件夹或者文件夹内找不到,则会继续找en文件夹
  • AppDir\en\AsmName.dll
  • AppDir\en\AsmName\AsmName.dll
  • AppDir\en\subLib1\AsmName.dll
  • AppDir\en\subLib1\AsmName\AsmName.dll
  • AppDir\en\AsmName.exe
  • AppDir\en\AsmName\AsmName.exe
  • AppDir\en\subLib1\AsmName.exe
  • AppDir\en\subLib1\AsmName\AsmName.exe

3. 强命名程序集与弱命名程序集

二者的结构完全相同,都有PE头、CLR头、元数据、清单表、IL。不同的是强程序集使用发布者的公钥/私钥进行了签名,对程序集进行了唯一性的标识。
弱程序集只能私有部署,即部署到应用程序的根目录或者字目录。强程序集可以私有部署或全局部署,即也可以部署到一些公有位置,CLR默认会检查这些位置。

3.1 Dll hell问题

全局部署会引起dll hell(dll 地狱)问题:两个公司可能会生成同名的dll,然后这两个强程序集都复制到相同的公有位置,最后一个复制的就是“老大”,造成所有正在使用原程序集的软件无法工作。
所以只根据文件名来区分程序集是不够的,CLR必须支持对程序集进行唯一性标识的机制,这就是强命名程序集。强程序集根据以下四个特征进行唯一性标识:

  1. 文件名(不含扩展名)
  2. 版本号(AssemblyVersion)
  3. 语言文化
  4. 公钥标记(public key token):从公钥派生出的小哈希值

如下就是两个程序集:

ConsoleFrame, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b77a5csdfgsdfg3434dsqfg
ConsoleFrame, Version=1.0.0.0, Culture="en-US", PublicKeyToken=b77a5csdfgsdfg3434dsqfg

弱程序集的public key token为null

ConsoleFrame, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

3.1.1 对弱程序集签名,使其变成强程序集

右键项目-属性,选择Signing-Sign the assembly即可。
在这里插入图片描述
然后在另一个项目中,引用ConsoFrame程序集,可以看到,已经生成了PublicKeyToken。
在这里插入图片描述
在这里我们新建另一个程序集ConsoleFrame2,且还用同一个AA.pfx公钥进行签名,可以看到ConsoleFrame与ConsoleFram2的PublicKeyToken完全一致:
在这里插入图片描述
即:生成强程序集时,公钥也会被嵌到dll里。

3.1.2 全局程序集缓存GAC

之前有讲过强程序集可以放到一个公共的位置,供所有的程序访问,这个位置就是GAC(Global Assembly Cache)。这个位置不是固定的,不同的CLR版本可以有不同的位置,但一般在以下目录都能发现:
%SystemRoot%\Microsoft.NET\Assembly
GAC目录下有很多子目录,子目录的名称是用算法生成的,不要自己手动把dll复制到某个目录里去,没用。应该使用GACUtil.exe工具复制,一般电脑里都有这个,使用Everything工具全局搜即可。

至此,dll hell问题得到解决。

3.1.3 在你的项目中如何引用强程序集?

  1. 你知道要引用dll的路径,直接项目上右键-添加引用即可。
  2. 你不知道引用dll的路径,那编译器CSC.exe会从以下位置开始查找:
    • 工作目录
    • CSC.exe所在的目录,目录中包含CLR的各种dll文件
    • 编译时使用/lib参数指定的目录
    • 使用LIB环境变量指定的目录

注意:编译查找dll进行编译的位置可不是程序运行时加载dll的位置,这是两码事。
到这里你应该理解到了.NET为什么会有.NET SDK.NET Runtime两种包可供下载安装了,如果只是运行.net程序而不进行开发那么只下载runtime就行。安装SDK时会安装同一程序集的两套拷贝,一份放到编译器的目录,另一份放到GAC里,而安装runtime只需要放到GAC就行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JimCarter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值