一种从中央存储库加载 DLL 的方法
我们有很多产品,每个产品的应用程序都有一些通用的 DLL.现在我们将每个公共 DLL 复制到每个产品的 bin 目录中,并将它们视为私有程序集.这会不必要地增加每个产品的 msi 大小,并且当 DLL 中出现问题时,我们必须构建包含 DLL 的每个产品的 msi 并进行部署.
是否有任何指示产品应用程序使用公共私有目录来加载 DLL [使用清单方案..]?[注意:将私有目录添加到 PATH env 不会提供解决方案,就像 SYSTEM 目录中存在同名的 DLL 一样,这会占用我们私有目录的权限 ]
-卡特利
解决方案您没有指定您的环境是 .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中描述了基本过程并行程序集
在 Windows 6.1 及更高版本(Windows Server 2008 和具有讽刺意味的 Windows 7)上,应用程序配置文件现在支持 应用程序配置文件
这意味着您应该能够提供一个路径(相对于您的应用程序)到包含您要加载的 dll 程序集的文件夹.
<小时>我已经在 Windows 7 上进行了一些测试,这很有效:
假设您在 Program FilesApp1
中安装了一个应用程序 app1.exe,它依赖于一些常见的 dll thedll.dll"
在应用程序文件夹 (Program FilesApp1
) 中创建一个文件 App1.exe.config 并为其提供以下内容:
现在,创建一个名为Program FilesAcmeCommon
的文件夹,在其中创建一个文件夹acme.thedll,并将dll.dll复制到Program FilesAcmeCommonacme.thedll代码>
还在 AcmeCommonacme.thedll 中创建一个名为 acme.thedll.manifest 的文件 - 这将是描述名为acme.thedll"的程序集的程序集清单
acme.thedll.manifest 的内容为:-
<assemblyIdentity name="acme.thedll" version="1.2.3.4" processorArchitecture="x86" type="win32"/><文件名=thedll.dll"/></组装>
现在我们在一个公共位置有了公共 dll,作为原生 sxs 程序集.我们有一个带有配置文件的应用程序,它会在 Windows 7 和 2008 服务器(及更高版本)上告诉它在公共位置搜索程序集.但是该应用程序仍在尝试将 dll 作为 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 合并:
<依赖><依赖程序集><assemblyIdentity type="win32" name="acme.thedll" version="1.2.3.4" processorArchitecture="x86" language="*"/></依赖程序集></依赖>
这应该结束循环:当应用程序加载时,win32 加载器将加载应用程序清单(app1.exe.manifest 或作为 RT_MANIFEST 资源嵌入)并了解acme.thedll"程序集.它还将加载应用程序配置文件 (app1.exe.config) 并了解搜索程序集的私有路径.然后它将加载并将acme.thedll.manifest"添加到应用程序的激活上下文"中.然后,当加载器尝试加载thedll.dll"时,它将搜索激活上下文数据库,发现它在 acme.thedll 程序集中,并从程序集位置加载它.
We have lot of products and there are some common DLLs across each product's application. Right now we copy each common DLL into each product's bin directory and treat them as private assembly. This unnecessarily increase the msi size of each product and when a problem occurs in a DLL we have to build each product's msi comprising the DLL and deploy it.
Is there anyway to instruct the product application to use a common private directory to be used for loading DLLs [ using manifest scheme.. ]? [ Note: Adding the private directory to PATH env will not provide a solution as if there is a DLL with same name exist in SYSTEM directory, that would take the privilege over our private directory ]
-Kartlee
解决方案You don't specify if your environment is .NET or straight Win32.
I am assuming its Win32 because if its .NET the technologies to do this are all much closer to hand in terms of things like the Global Assembly Cache.
In terms of Win32 it is possible to load Dlls from a shared location in one of two ways:
Use LoadLibrary with explicit full paths. This means you cannot use static linking - all dll functions used in all products will have to be accessed via GetProcAddress. You cannot import c++ classes from dll's loaded via LoadLibrary - they must be statically linked to work so this approach may or may not be viable. Its not terribly hard to write shim header files that masquerade as the dll's interface and do a just in time dll load and GetProcAddress as needed for each call.
The other option is to turn the dll's into what are called "side by side assemblies" and install them into the WinSxS store. Don't be scared by the big name. "side by side assembly" means "A Dll file plus manifest file with version information". Each of the various applications would then put 'strong name' - which includes version information - into its application manifest for each dll it uses, and the Win32 Dll loader will use this to pick the correct instance of the common dll from the WinSxS store. The basic process is described in the MSDN article Guidelines for Creating Side-by-side Assemblies
On Windows versions 6.1 and up (Windows Server 2008 and the ironically named Windows 7) application configuration files DO NOW support the probing element in Application Configuration Files
This means you should be able to provide a path (relative to your application) to a folder containing containing dll assemblies you want to load.
I've done some testing on Windows 7, and this works:
Assuming you have an application app1.exe installed in Program FilesApp1
that depends on some common dll "thedll.dll"
In the application folder (Program FilesApp1
) create a file App1.exe.config and give it the following contents :
<configuration>
<windows>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="..AcmeCommon"/>
</assemblyBinding>
</windows>
</configuration>
Now, create a folder called Program FilesAcmeCommon
, and in it a folder acme.thedll, and copy thedll.dll into Program FilesAcmeCommonacme.thedll
Also create a file in AcmeCommonacme.thedll called acme.thedll.manifest - this will be the assembly manifest describing the assembly called 'acme.thedll'
The contents of acme.thedll.manifest will be:-
<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>
Now we have the common dll, in a common location, as a native sxs assembly. We have the app, with a config file that will, on Windows 7 and 2008 server (and up) tell it to search for assemblies in the common location. But the app is still trying to link to the dll as a dll, rather than via an assembly.
To get the app to load the assembly, we need to add a manifest file to the application. If you are using visual studio, your applications are probably already configured to create and embed manifests via the linker and manifest tool project settings. In which case the easiest way to tell the app about the assembly is to rebuild it after adding the following code to at least one header or c/cpp file in the project :-
#pragma comment(linker,"/manifestdependency:"type='win32' name='acme.thedll' version='1.2.3.4' processorArchitecture='x86' language='*'"")
If you are using an older build environment where the manifests are hand made you would need to merge the following xml with app1.exe.manifest in the App1 folder:
<dependency>
<dependentassembly>
<assemblyIdentity type="win32" name="acme.thedll" version="1.2.3.4" processorArchitecture="x86" language="*"/>
</dependentassembly>
</dependency>
This should close the circle: When the app loads the win32 loader will load the application manifest (app1.exe.manifest or embedded as a RT_MANIFEST resource) and learn about the "acme.thedll" assembly. It will also load the application config file (app1.exe.config) and learn about the private path to search for assemblies. And it will then load and add "acme.thedll.manifest" to the apps "activation context". Then, when the loader tries to load "thedll.dll" it will search the activation context db, find that it's in the acme.thedll assembly, and load it from the assemblies location.
相关文章