如何解决本地DLL 的PublicKey Token与项目需要的DLL的PublicKey Token不一致的问题

来源:互联网 发布:c++游戏编程 编辑:程序博客网 时间:2024/04/28 17:51

这个过程是这样的。有一个遗留的项目需要做一些修改,那么我就从TFS上把这个项目下载下来,用VisualStudio打开。编译时没有发现错误,直接运行。可是运行时就抛出了异常。

Could not load file or assembly 'log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)":"log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821"}

我的第一反应是这个Log4Net.dll找不到,觉得那么在reference列表中这个dll应该是黄色才对。右键查看项目引用的dll,发现Log4Net.dll正常引用,并没有黄色叹号,说明找到了这个dll.(后来想想也对,如果有黄色叹号标记的dll,那么说明找不到这个dll,那么编译的时候就应该报出异常,说找不到这个dll,所以由此总结,带黄色叹号的都是项目要用到的,但是没有在本地找到的,编译不会通过。如果没有黄色叹号抛出上边的异常,那么就是这个dll找到了,但是可能版本不一致。)

双击reference列表中的Log4Net.dll,可以看到其详细的信息,可以看出,这个dll指向的路径是GAC中,那么我就去GAC中找,确实在我本机找到了。


这个程序集存在于GAC中。那么我们查看一下这个Log4Net的dll信息。





果然,这里的publicKey Token与所需要的PublicKeyToken=1b44e1d426115821不一致。那么怎么解决这个问题?以前记得解决DLLversin不一样问题的时候,可以使用web.config或者app.config中的runtime节点进行redirect映射,那么publicTokenKey 可以吗?我找了找,没有找到相关配置。怎么办? 那么就去找指定publicKey token的dll吧。去哪里找?去Log4Net的官网找。确实,官网说明了它的dll变过publicKey token.



不过一般来讲,一个公司发布产品类型的dll,本不应该变换publickey Token签名的,否则会给用户带来像本文提到的这种问题,不同的人机器人可能安装了不同publickeytoken的签名版本,造成混乱。但是不知道为什么Log4Net却中途变了这个签名,有newkey与oldkey版本。

我下载了new key的版本,觉得这个应该是项目需要的,但是怎么确定一下这个下载来的dll的publickkeytoken呢?这里就可以用VS自带的工具。打开VS命令行,输入sn命令就可以。



由此可以看到这个publickey token不是。所以继续下载了oldkey的dll,使用同样的方法,最终证实确实是需要oldkey的版本。所以,我下载下来这个Log4Net.dll,然后在项目中建立一个Lib目录,把dll放进去,让项目对其添加引用。publickeyToken对了,此时我们需要看一下,那么版本对吗?右键查看这个新引用的dll的property,发现版本还真不对。项目需要Version=1.2.10.0,而这个新下载的是1.2.13.0,那么怎么办?这时就可以用版本映射来解决了。打开配置问题件,在节点中增加这些信息,意思就是需要1.2.10.0的时候,如果遇到了1.2.13.0,那么就可以认为找到了对应的dll。

 

至此,编译运行,发现问题解决,没有再报任何的异常。


一些题外话:

以上的问题,都是由于项目开发时候的不规范造成的。像Log4Net这样的非微软集成到GAC中的dll,就应该在项目中建立一个Lib目录存储下来,以后当有人下载代码的时候,能够连带dll一起下载到本地,不用再大费周章的去找对应版本了。现在有时候是用Nuget就很好,那么额外的dll全会放到package中,并且include到项目中。

 

还有关于dll version的映射问题,这个不是什么时候都OK的。

比如说,现在我用到了一个第三方的dll,叫做SuperLog.dll,这个dll是一个公司的产品,这个公司是引用EntrepriseLogging.dll,引用的是5.0的版本,并且在这个基础之上增加功能。我们在一个项目中用到了SuperLog.dll,我们加入这个SuperLog.dll,编译时告诉我们需要引入EntrepriseLogging.dll,但是我们手头只有EntrepriseLogging.dll6.0的版本。或许我们可以引入6.0的版本,但是重新映射到5.0版本。但是,我们这样做了,编译的时候还是遇到了问题。异常好像是说,EntrepriseLogging没有某个方法之类的。所以,这个意思就是说,以前5.0的时候,可能有一个方法A(),正好被SuperLog调用了。但是在6.0版本的时候,EntrepriseLogging决定不要这个方法了。所以在我们项目中,虽然没有抛出版本不一样的异常,但是当把6.0的版本当5.0的用,对这个6.0版本掉用A方法,但是没有,就抛出了异常。


一般来讲,第三方DLL发布新的版本,基本上不会更新对外暴露的方法,只是会更新方法中的内容。所以,不管哪个版本我们用到的都是同一个方法。但是如果真的对外的方法都变了,增加了活减少了什么的,那么就会遇到上边的问题。所以,不是所有的版本不同问题都能通过配置映射来解决。

 

为什么要有PublickKey Token?

其中一个很重要的原因就是安全角度考虑。这样说吧,某公司开发了一个桌面程序A.exe,然后安装包供下载。你下载完成,并且安装。那么,在你的ProgramFile里边,就有A.exe与一些供其调用的dll,比如说B.dll,当你运行A的时候,会调用B.dll,完成相应操作。现在,如果有某个非法程序偷偷的跑到A的目录中,找到B.dll,然后把自己写的同名的dll替换原来的。这时运行A的时候,调用B,就可能会有一些恶意非法操作。PublicKey的作用之一就是用来验证B是否是完全一致的B。验证机制比较复杂,大家可以进一步探索。


0 0