Load 2 versions of the same DLL in the same proces

2020-06-03 07:46发布

I want to do exactly what it is described here, but the accepted solution does not work for me. I suppose the reason is explained here :

If a DLL with dependencies is loaded by specifying a full path, the system searches for the DLL's dependent DLLs as if they were loaded with just their module names.

If a DLL with the same module name is already loaded in memory, the system checks only for redirection and a manifest before resolving to the loaded DLL, no matter which directory it is in. The system does not search for the DLL.

I wish to have my application in the following structure.

c:\Exe
 |
 |----- c:\DLL\DLL.dll, c:\DLL\common.dll 
 |
 |----- c:\DLL2\DLL2.dll, c:\DLL2\common.dll

My EXE will load the DLLs through

LoadLibrary("c:\\DLL\\DLL.dll");
LoadLibraryEx("c:\\DLL2\\DLL2.dll");

common is implicitly loaded in both cases.

I tried the SetDllDirectory option, but there is always only one common.dll loaded.

I added version information in common.dll. c:\DLL\common.dll has version 2.0.1.0 while c:\DLL2\DLL2.dll has version 4.0.1.0

I embedded the following manifest with the corresponding version info, but it did not help.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="common" version="4.0.1.0" processorArchitecture="x86"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>

Is there a solution to this problem ?

1条回答
啃猪蹄的小仙女
2楼-- · 2020-06-03 08:22

Where have you embedded the manifest? The EXE or the DLLs?

You have two basic ways of doing this, both involve turning "common" into a private SxS assembly by creating a manifest for it.

Then:

  1. If DLL and DLL2 contain manifests listing dependent assemblies, then you need to add a dependentAssembly to their manifests specifying "acme.common" (for example) as a dependent assembly. As dependent assemblies are always searched for, by default, in the loading modules folder, each DLL will load its own local copy of common.

  2. If you are just relying on the applications default activation context to do most of the heavy lifting, then you can try using the ActivationContext API. Call CreateActCtx twice, specifying two the two different folders as the base folder for the resulting context.

In pseudo code:

HACTCTX h1 = CreateActCtx( ... for DLL ... );
HACTCTX h2 = CreateActCtx( ... for DLL2 ...);

ActivateActCtx(h1,...);
LoadLibrary("C:\\DLL\\DLL1.DLL");
DeactivateActCtx();
ActivateActCtx(h2,...);
LoadLibrary("C:\\DLL2\\DLL2.DLL");
DeactivateActCtx...

If the dlls already contain their own manifests the system will use those. If not, this will let you specify a search directory for private assemblies without modifying the dll's themselves.


To implement Option 1: First, I don't recommend trying to use the dll name as the assembly name. So, create a manifest that looks like this in each folder:

<!-- acme.common.manifest -->
<assembly manifestVersion="1.0">
    <assemblyIdentity type="Win32" name="acme.common" version="1.0.0.0" processorArchitecture="x86"/>
    <file name="common.dll"/>
</assembly>

You can fix the version number to match common.dll's version in each folder, but thats not important.

Then, either the manifest you list, or a directive like this if you are using Visual Studio

#pragma comment(linker, "/manifestdependency:\"acme.common'"\
                   " processorArchitecture='*' version='1.0.0.0' type='win32'\"")

Just make sure the dependent assembly versions match the versions of the corresponding 'acme.common' assembly.

查看更多
登录 后发表回答