windows 一种从中央存储库加载 DLL 的方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1969360/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 13:39:33  来源:igfitidea点击:

A way to load DLL from central repository

c++cwindowsdll

提问by Kartlee

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.

我们有很多产品,每个产品的应用程序都有一些通用的 DLL。现在我们将每个公共 DLL 复制到每个产品的 bin 目录中,并将它们视为私有程序集。这不必要地增加了每个产品的 msi 大小,并且当 DLL 中出现问题时,我们必须构建每个产品的包含 DLL 的 msi 并部署它。

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 ]

有没有指示产品应用程序使用一个通用的私有目录来加载 DLL [使用清单方案..]?[注意:将私有目录添加到 PATH env 不会提供解决方案,就好像 SYSTEM 目录中存在同名 DLL 一样,这将获得我们私有目录的特权]

-Kartlee

-卡特利

回答by Chris Becke

You don't specify if your environment is .NET or straight Win32.

您没有指定您的环境是 .NET 还是直接的 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.

我假设它的 Win32 是因为如果它是 .NET,那么就全局程序集缓存之类的东西而言,执行此操作的技术都更接近手。

In terms of Win32 it is possible to load Dlls from a shared location in one of two ways:

在 Win32 方面,可以通过以下两种方式之一从共享位置加载 Dll:

  • 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

  • 将 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中描述了基本过程



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

在Windows版本6.1及以上(在Windows Server 2008和讽刺的是名为Windows 7)应用程序配置文件现在要做的支持探测元素应用程序配置文件

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.

这意味着您应该能够提供一个路径(相对于您的应用程序)到包含要加载的 dll 程序集的文件夹。



I've done some testing on Windows 7, and this works:

我在 Windows 7 上做了一些测试,这有效:

Assuming you have an application app1.exe installed in \Program Files\App1that depends on some common dll "thedll.dll"

假设您安装了一个应用程序 app1.exe,\Program Files\App1它依赖于一些常见的 dll “thedll.dll”

In the application folder (\Program Files\App1) create a file App1.exe.config and give it the following contents :

在应用程序文件夹 ( \Program Files\App1) 中创建一个文件 App1.exe.config 并为其提供以下内容:

<configuration>   
   <windows>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <probing privatePath="..\AcmeCommon"/>
    </assemblyBinding>
  </windows>
</configuration>

Now, create a folder called \Program Files\AcmeCommon, and in it a folder acme.thedll, and copy thedll.dll into \Program Files\AcmeCommon\acme.thedll

现在,创建一个名为 的文件夹\Program Files\AcmeCommon,在其中创建一个文件夹 acme.thedll,并将 thedll.dll 复制到\Program Files\AcmeCommon\acme.thedll

Also create a file in AcmeCommon\acme.thedll called acme.thedll.manifest - this will be the assembly manifest describing the assembly called 'acme.thedll'

同时在 AcmeCommon\acme.thedll 中创建一个名为 acme.thedll.manifest 的文件 - 这将是描述名为“acme.thedll”的程序集的程序集清单

The contents of acme.thedll.manifest will be:-

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>

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.

现在,我们在一个公共位置拥有了公共 dll 作为本机 sxs 程序集。我们有一个带有配置文件的应用程序,它会在 Windows 7 和 2008 服务器(及更高版本)上告诉它在公共位置搜索程序集。但是该应用程序仍在尝试将 dll 作为 dll 链接到 dll,而不是通过程序集。

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 :-

为了让应用程序加载程序集,我们需要向应用程序添加一个清单文件。如果您使用的是 Visual Studio,您的应用程序可能已经配置为通过链接器和清单工具项目设置创建和嵌入清单。在这种情况下,告诉应用程序有关程序集的最简单方法是在将以下代码添加到项目中的至少一个头文件或 c/cpp 文件后重建它:-

#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:

如果您使用的是手工制作清单的较旧构建环境,则需要将以下 xml 与 App1 文件夹中的 app1.exe.manifest 合并:

<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.

这应该结束循环:当应用程序加载时,win32 加载器将加载应用程序清单(app1.exe.manifest 或作为 RT_MANIFEST 资源嵌入)并了解“acme.thedll”程序集。它还将加载应用程序配置文件 (app1.exe.config) 并了解搜索程序集的私有路径。然后它将加载并将“acme.thedll.manifest”添加到应用程序的“激活上下文”中。然后,当加载器尝试加载“thedll.dll”时,它将搜索激活上下文数据库,发现它在 acme.thedll 程序集中,并从程序集位置加载它。

回答by r590

I am following Chris's answer. Make sure the case is correct on the manifests and configs. Otherwise they will fail. I was able to get the assembly to be loaded, but the DLL wouldn't be chosen. In my case a Windows DLL in system32 is being chosen instead of my own with the same name. In Dependency Walker, my DLL is loaded, but at runtime, with Process Explorer, Windows' copy is loaded. Any ideas?

我正在关注克里斯的回答。确保清单和配置上的案例正确。否则他们会失败。我能够加载程序集,但不会选择 DLL。在我的情况下,系统 32 中的 Windows DLL 被选择而不是我自己的同名。在 Dependency Walker 中,加载了我的 DLL,但在运行时,使用 Process Explorer,加载了 Windows 的副本。有任何想法吗?

回答by Rubens Farias

If you're talking about .NET, you can:

如果您在谈论 .NET,您可以:

  • Load your DLL directly from a database by using Assembly.Load(byte[])
  • By using Assembly.TypeResolveevent
  • By using TypeProviderclass
  • By defining a probe directory in your config file
  • 通过使用直接从数据库加载您的 DLL Assembly.Load(byte[])
  • 通过使用Assembly.TypeResolve事件
  • 通过使用TypeProvider
  • 通过在配置文件中定义探针目录

Like:

喜欢:

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

回答by Scott Vercuski

I'm not sure if this is what you're looking for but where I work now we use a common UNC path for all of our DLL's.

我不确定这是否是您要查找的内容,但我现在工作的地方我们对所有 DLL 使用通用的 UNC 路径。

We have something akin to ...

我们有类似...

\\server01\productionLibrary for production read DLLs, each in it's own directory.

\\server01\productionLibrary 用于生产读取 DLL,每个 DLL 都在它自己的目录中。

and

\\server01\developmentLibrary which mirrors the production library and this is what the developers use as they develop.

\\server01\developmentLibrary 反映了生产库,这是开发人员在开发时使用的。

When we merge code after testing has been completed we deploy to the production library. All projects reference the production library when they are built into MSI files for deployment. Our automated system builds the projects into MSIs and it verifies that all DLLs are pointing to the production library, so there is no chance that it will use a development copy accidentally.

当我们在测试完成后合并代码时,我们将部署到生产库。所有项目在构建到 MSI 文件中进行部署时都会引用生产库。我们的自动化系统将项目构建到 MSI 中,并验证所有 DLL 都指向生产库,因此不会意外使用开发副本。

Hope this helps.

希望这可以帮助。

回答by Kel

I'm not sure I understand the question correctly, but if you're in .Net, there's a Global Assembly Cache (GAC): http://en.wikipedia.org/wiki/Global_Assembly_Cache

我不确定我是否正确理解了这个问题,但是如果您在 .Net 中,则有一个全局程序集缓存 (GAC):http: //en.wikipedia.org/wiki/Global_Assembly_Cache

This caches the assemblies and permits the reuse of them by applications (like with .net framework). You simply will need to register the assembly to GAC on the installation, for example provide a "your framework install" with all common assemblies, and this can be deployed only once.

这会缓存程序集并允许应用程序重用它们(如使用 .net 框架)。您只需要在安装时将程序集注册到 GAC,例如提供包含所有常见程序集的“您的框架安装”,并且这只能部署一次。

回答by Len Holgate

This may not help you, but... you CAN load dlls from arbitrary directories AND still rely on normal dynamic linking to them as long as you can control when the dlls are loaded via dynamic linking and ensure that you've already loaded the dll explicitly using a full path before it's loaded dynamically.

这可能对您没有帮助,但是...您可以从任意目录加载 dll 并且仍然依赖正常的动态链接到它们,只要您可以控制何时通过动态链接加载 dll 并确保您已经加载了 dll在动态加载之前明确使用完整路径。

This is only likely to be of help if you're writing a plugin system where your plugins are dynamically linked to dlls which you wish to keep in a non standard directory. If you then know all about the dlls they're linked to you can explicitly load these dlls directly using their full path before you load the dlls (plugins) that depend on them dynamically. Since the dll is already in memory when your plugin needs to locate it it will use the version in memory. You can the unload the explicit load that you did before loading the plugin and you're good to go.

如果您正在编写一个插件系统,其中您的插件动态链接到您希望保留在非标准目录中的 dll,这可能会有所帮助。如果您随后了解了它们链接到的 dll 的所有信息,则可以在加载动态依赖于它们的 dll(插件)之前直接使用它们的完整路径显式加载这些 dll。由于 dll 已经在内存中,当您的插件需要定位它时,它将使用内存中的版本。您可以卸载在加载插件之前所做的显式加载,一切顺利。

Unfortunately this wont work if your main exe needs to load dlls from arbitrary places as you cant get in ahead of the normal dll load process.

不幸的是,如果您的主 exe 需要从任意位置加载 dll,这将不起作用,因为您无法在正常的 dll 加载过程之前进入。