这篇文章也不错
A common issue in previous versions of Windows was that users frequently suffered from , where more than one version of the same (DLL) was installed on the computer. As software relies on DLLs, using the wrong version could result in non-functional applications, or worse. Windows XP solved this problem by introducing side-by-side assemblies.
从上述描述中可以知道从xp开始,微软开始引入winsxs目录通过版本化管理dll,来部分解决dllhell问题,这种策略在.net的私有部署和全局gac部署中也能看到影子。
下面是我进行的一些验证
1. 使用manifest 文件.
使用visual studio 2005 创建一个com dll,然后写一个测试程序创建它。结果发现确实优先定为了winsxs目录下的信息,不过这儿有一点需要注意,就是winsxs目录必须正确配置了指定版本的信息,这个在manifest 目录下有. 因此如果光删除winsxs下的指定版本的dll是不能认为本机就没有了这个版本的dll.
2. 验证dll的搜索路径
将winsxs下指定的ATL80信息删除,运行测试程序发现,果然位于app同目录下的dll被load了。
我准备了一个测试程序,这个测试程序创建了一个COM对象,而这个COM对象依赖于ATL80.dll , 测试中在exe目录下准备了 comclient.exe.manifest
准备了Microsoft.VC80.ATL.manifest
注意测试程序和com对象都静态link MFC,仅仅测试atl80.dll,
经过验证果然优先加载本地的版本,而忽略winsxs.
注意com对象需要删除manifest信息,否则创建也会失败。个人感觉是com对象删除了manifest信息后,windows在创建无manifest信息的dll时候倒是到app目录下寻找atl80.dll.
3. atl80.dll 可以在一个进程中出现多次,因为windows认为它加载了不同的版本,而这个是程序所需要的.
举例来说我采用了2所说的方式加载一个本地dll,同时我又创建了一个普通的嵌入了标准信息的com对象,而这个对象则依然会按照windows的搜索方式寻找atl80.dll而正好winsxs下有,所以出现了两个。我认为这个现象仅仅会在windows xp or later上存在。
4. 一个问题的分析
问题时这样的,有个程序client.exe 用到了ATL80.dll ,且准备了标准的client.exe.manifest,以及标准的Microsoft.VC80.ATL.manifest , 然后client.exe 运行过程中创建了一个不在同一目录的com对象comobj.dll , 而这个comobj.dll 同样内置了manifest (当然开发者忽略了这一点).然后又非常碰巧,用户的机器上没有这个版本的atl80.dll, 这样发现这个com对象是中创建不起来。
现在我知道了为什么?因为windowsXP发现了com对象,又检测到了内嵌的manifest信息,刚才说了碰巧用户的windowsXP机器上没有这个版本的atl80.dll, 但是让我费解的是msdn 这个地址说明加载顺序
- Side-by-side searches the WinSxS folder.
- \\<appdir>\<assemblyname>.DLL
- \\<appdir>\<assemblyname>.manifest
- \\<appdir>\<assemblyname>\<assemblyname>.DLL
- \\<appdir>\<assemblyname>\<assemblyname>.manifest
而实际上我在client.exe 目录下已经有了atl80.dll , comobj.dll 下也有,但是依然不行。然后我尝试将正确版本的atl80.dll 放到system32 目录下,结果依然不行。
因此我做出修改,将Microsoft.VC80.ATL 放到comobj.dll 同目录,这下ok了,可以创建了,且进程中分别出现了在我本地私有部署的atl80.dll. 看来我上面的疑问解决是 comobj.dll 仅仅内嵌了对atl80.dll 的引用manifest说明,但是还需要具体dll的manifest,原因不明.
2008-08-27 在出问题的客户机器上,将helloworld的manifest信息删除,结果ok,也验证了我本地的想法。
5. 最后再作一个试验,用vs2005 编译不带有manifest信息的client.exe , comobj.dll 。
貌似只要2005编出来就需要使用winsxs 下的??
据我观察,如果人工放置的话,即使版本一致,也会弹出版本不一致无法加载而导致失败。
实际上运行库类的,在vs2005编译出来强制要求使用manifest
6. 加载的appdir 到底是什么,来看今天碰到的一个问题。
xuming在vista下安装了两个阿里旺旺。 先用administrtor运行A旺旺,这样导致wwsdkcomd.ll在注册表zohng1是A;然后用普通用户运行B,因为wwsdkcom.dll无法写CLASSROOT,因此导致失败,然后B加载了A的wwsdkcom.dll,由于windows 的加载规则因素,wwsdkcom.dll 所依赖的c/c++/atl运行库,在A的wwsdkcom.dll目录下进行查找(因为vista的winsxs版本太旧了),另外由于B也按照自己的manifest原则加载了运行库,因此出现了两份运行库,这样就非常容易导致内存的crash。
而这个crash是由于wwsdkcom.dll / wwsdk.dll 直接的内存交互导致的,如果仅仅是com接口交互是没有问题的。
解决方案,思前想后,
去掉 wwsdkcom.dll 中的manifiest信息,这样就会加载exe下的dll。
最后还遗留一个问题就是,目前这种方式无法解决一个运行,另外一个安装的问题。
7, 另外我发现了manifest的一个问题,就是说如果模块自己产生的manifest那么必须内嵌,否则无效,而它引用的模块的manifest才需要外置的。
8. 一般的ATL COM模块的免注册方式.
如果是C#那么直接添加已经注册的referece,然后设置isloated,那么将生成manifest文件.
9. side by side 这种模式目前我发现仅仅能够处理进程内com,遇到进程外com就无能为力了。
我做过如下尝试。
mysrevice -Embedding , 让进程先起来。
然后client去CoGetClassObject获取前者注册的类厂,也失败了。
另外从visual studio 2005 的isolated com中发现 component file: dll/ 提示,貌似也
是仅仅支持dll.
另外尝试在C#中引用一个com exe server ,然后选择isolated , 结果编译时候直接报错。
下面这个link描述了.net中实现进程外com的方法。
阅读(2138) | 评论(1) | 转发(0) |