我们有很多产品,每个产品的应用程序都有一些常见的DLL。现在我们将每个常见的DLL复制到每个产品的bin目录中,并将它们视为私有程序集。这会不必要地增加每个产品的msi大小,当DLL中出现问题时,我们必须构建包含DLL的每个产品的msi并进行部署。
是否有指示产品应用程序使用公共私有目录来加载DLL [使用manifest scheme ..]? [注意:将私有目录添加到PATH env将不会提供解决方案,就好像SYSTEM目录中存在具有相同名称的DLL一样,这将获取对我们的私人目录的特权]
-Kartlee
答案 0 :(得分:8)
您没有指定您的环境是.NET还是Win32。
我假设它的Win32,因为如果它的.NET这样做的技术在诸如全局程序集缓存之类的东西方面更接近。
就Win32而言,可以通过以下两种方式之一从共享位置加载Dll:
将LoadLibrary与显式完整路径一起使用。这意味着您不能使用静态链接 - 所有产品中使用的所有DLL函数都必须通过GetProcAddress访问。您无法从通过LoadLibrary加载的dll导入c ++类 - 它们必须静态链接到工作,因此这种方法可能或可能不可行。编写伪装成dll接口的shim头文件并根据需要为每次调用执行一次dll加载和GetProcAddress非常难。
另一种选择是将dll转换为所谓的“并排程序集”并将它们安装到WinSxS存储中。不要被大名吓到。 “并排组装”是指“带有版本信息的Dll文件加清单文件”。 然后,各种应用程序中的每一个都会将“强名称”(包括版本信息)放入其使用的每个dll的应用程序清单中,Win32 Dll加载程序将使用它从WinSxS存储中选择正常dll的正确实例。 MSDN文章Guidelines for Creating Side-by-side Assemblies
在Windows 6.1及更高版本(Windows Server 2008和具有讽刺意义的Windows 7)应用程序配置文件中,现在支持Application Configuration Files中的探测元素
这意味着您应该能够提供一个路径(相对于您的应用程序)到包含您要加载的dll程序集的文件夹。
我已经在Windows 7上做了一些测试,这很有效:
假设您在\Program Files\App1
中安装了一个应用程序app1.exe,它取决于一些常见的dll“thedll.dll”
在应用程序文件夹(\Program Files\App1
)中创建一个文件App1.exe.config并为其提供以下内容:
<configuration>
<windows>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="..\AcmeCommon"/>
</assemblyBinding>
</windows>
</configuration>
现在,创建一个名为\Program Files\AcmeCommon
的文件夹,并在其中创建一个文件夹acme.thedll,并将该DLL文件复制到\Program Files\AcmeCommon\acme.thedll
还在AcmeCommon \ acme.thedll中创建一个名为acme.thedll.manifest的文件 - 这将是描述名为'acme.thedll'的程序集的程序集清单
acme.thedll.manifest的内容将是: -
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="acme.thedll" version="1.2.3.4" processorArchitecture="x86" type="win32"/>
<file name="thedll.dll"/>
</assembly>
现在我们在公共位置使用公共dll作为本机sxs程序集。我们有一个应用程序,配置文件将在Windows 7和2008服务器(及以上)告诉它在公共位置搜索程序集。但该应用程序仍在尝试将dll链接为dll,而不是通过程序集链接。
要让应用加载程序集,我们需要向应用程序添加清单文件。如果您使用的是visual studio,您的应用程序可能已经配置为通过链接器和清单工具项目设置创建和嵌入清单。在这种情况下,告诉应用程序有关程序集的最简单方法是在将以下代码添加到项目中的至少一个头文件或c / cpp文件后重建它: -
#pragma comment(linker,"/manifestdependency:\"type='win32' name='acme.thedll' version='1.2.3.4' processorArchitecture='x86' language='*'\"")
如果您使用的是较旧的构建环境,其中清单是手工制作的,则需要将以下xml与App1文件夹中的app1.exe.manifest合并:
<dependency>
<dependentassembly>
<assemblyIdentity type="win32" name="acme.thedll" version="1.2.3.4" processorArchitecture="x86" language="*"/>
</dependentassembly>
</dependency>
这应关闭圆圈:当应用加载win32加载器时,将加载应用程序清单(app1.exe.manifest或嵌入为RT_MANIFEST资源)并了解“acme.thedll”程序集。它还将加载应用程序配置文件(app1.exe.config)并了解搜索程序集的专用路径。然后它将加载并将“acme.thedll.manifest”添加到应用程序“激活上下文”。然后,当加载程序尝试加载“thedll.dll”时,它将搜索激活上下文数据库,发现它位于acme.thedll程序集中,并从程序集位置加载它。
答案 1 :(得分:1)
如果你在谈论.NET,你可以:
Assembly.Load(byte[])
Assembly.TypeResolve
事件TypeProvider
class 像:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin"/>
</assemblyBinding>
</runtime>
</configuration>
答案 2 :(得分:1)
我正在追随克里斯的回答。确保清单和配置上的案例是正确的。否则他们会失败。 我能够加载程序集,但不会选择DLL。在我的情况下,正在选择system32中的Windows DLL而不是我自己的同名。在Dependency Walker中,我的DLL已加载,但在运行时,使用Process Explorer,将加载Windows的副本。有什么想法吗?
答案 3 :(得分:0)
我不确定这是否是您正在寻找的,但我现在在哪里工作,我们为所有DLL使用通用的UNC路径。
我们有类似的东西......
\\ server01 \ productionLibrary用于生产读取DLL,每个DLL都在自己的目录中。
和
\\ server01 \ developmentLibrary镜像生产库,这是开发人员在开发时使用的。
当我们在测试完成后合并代码时,我们将部署到生产库。所有项目在将生成库构建到MSI文件中进行部署时都会引用它们。我们的自动化系统将项目构建到MSI中,并验证所有DLL都指向生产库,因此不会意外地使用开发副本。
希望这有帮助。
答案 4 :(得分:0)
我不确定我是否正确理解了这个问题,但是如果你在.Net中,那就是全局程序集缓存(GAC):http://en.wikipedia.org/wiki/Global_Assembly_Cache
这会缓存程序集并允许应用程序重用它们(例如.net框架)。您只需要在安装时将程序集注册到GAC,例如提供包含所有常用程序集的“框架安装”,并且只能部署一次。
答案 5 :(得分:0)
这可能对您没有帮助,但是......您可以从任意目录加载dll并依赖于它们的正常动态链接,只要您可以控制通过动态链接加载dll的时间并确保您已经在动态加载之前使用完整路径显式加载dll。
如果你正在编写一个插件系统,你的插件动态链接到你希望保存在非标准目录中的dll,这只会有所帮助。如果你知道它们链接到的dll的所有内容,你可以在加载动态依赖它们的dll(插件)之前直接使用它们的完整路径显式加载这些dll。由于当你的插件需要找到它时,dll已经在内存中,它将使用内存中的版本。你可以在加载插件之前卸载你做的显式加载,然后你就可以了。
不幸的是,如果您的主要exe需要从任意位置加载dll,因为您无法在正常的dll加载过程之前进入,这将无法工作。