Determine the loaded path for DLLs

2020-02-10 09:48发布

问题:

I wish to have my application in the following structure.

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

My EXE will load the DLLs through

LoadLibraryEx(_T("DLL\\DLL.dll"), 0, 0);
LoadLibraryEx(_T("DLL2\\DLL2.dll"), 0, 0);

DLL.dll and DLL2.dll project will link against common.dll through lib file. There will be 2 different versions of common.dll though.

However, during execution, Exe expected me to place common.dll same directory as Exe, but not same directory as DLL and DLL2. Is there any way I can resolve this, by able to have the above directory structure. Yet, still use lib to link against DLL/DLL2 with common.

回答1:

  1. You want to load two different DLLs with the same name (common.dll) into the same process.

    That seems like a bad idea to me. Is it really necessary? Could one of them be renamed?

  2. Ensuring the DLLs you load can find other DLLs which aren't in the search path.

    (If you were not dynamically loading DLL.dll and DLL2.dll then I'm not sure what the would be. Luckily, I see you are. :))

    If you are dynamically loading DLL.dll and DLL2.dll (that is, using LoadLibrary at runtime instead of linking to their .lib files at build time), then you can call SetDllDirectory beforehand to explicitly add the DLL or DLL2 directories to the search path. You would want to only have one directory in the path at once to ensure the right common.dll was loaded.

    Note that it is good practice, unless it breaks a poorly-written component, to call SetDllDirectory("") at the start of your program to remove the current-working-directory (not the program's directory, don't worry) from the DLL search path. This mitigates security issues where your code can be tricked into loading DLLs. But also note that if you reset the search path by calling SetDllDirectory(NULL) then you need to call SetDllDirectory("") again afterwards.

So you'd have code like this:

SetDllDirectory(NULL); // Reset.
SetDllDirectory(""); // Plug "binary planting" security hole. `
SetDllDirectory("C:\MyExePath\DLL");
LoadLibrary("C:\MyExePath\DLL\DLL.dll");

SetDllDirectory(NULL); // Reset.
SetDllDirectory(""); // Plug "binary planting" security hole.
SetDllDirectory("C:\MyExePath\DLL2");
LoadLibrary("C:\MyExePath\DLL2\DLL2.dll");

SetDllDirectory(NULL); // Reset.
SetDllDirectory(""); // Plug "binary planting" security hole.

(Untested so apologies for any mistakes or missing arguments. Should give you the idea, though.)

(You should calculate the C:\MyExePath at runtime. Hard-coding it would be bad, obviously.)

(I am assuming that DLL.dll and DLL2.dll load their common.dll implicitly. If they are loading common.dll via a LoadLibrary call then the problem is even easier: Just make them calculate their own paths and then pass LoadLibrary the full path to common.dll.)

Beware: SetDllDirectory affects your entire process. If your process has multiple threads you should ensure the SetDllDirectory calls are isolated from each other as well as anything else that may trigger a LoadLibrary call. e.g. Load the libraries at startup before you spawn any other thread, if possible.



回答2:

It won't work. You can't link "DLL2\DLL2.dll" to "DLL2\common.dll". DLL2.dll will be linked against "DLL\common.dll". There will be a "common.dll" in memory when "DLL2.dll" is loaded, so the imports of DLL2.dll will be resolved against that DLL.

Note that suggestions such as PATH or SetDllDirectory do not work. They affect how LoadLibrary finds "common.dll", but LoadLibrary("common.dll") is called only once, for DLL\DLL.dll.



回答3:

Ok this will really be fun.

The basic thing here is, to load the DLLs from other paths, you either must specify the full path (when using LoadLibrary) or expand the PATH environment variable to include the other folders that contain your DLLs. See setenv for details on how to do that.

A simple solution is to provide the relative path to LoadLibrary. But is seems you are linking the DLLs and thus you can't apply this solution.

The problem is no matter what your project layout is the runtime linker will use the current working directory and the PATH variable. Since you seem to be linking the library there is no chance you can "fix" the PATH variable just after loading the first DLL and before the second DLL. The remaining solution is to rename the common DLL to something like common-1.dll and common-2.dll. This also has the benefit that you can actually put them all into the same directory.

(If you are using LoadLibrary in any case, then just fix the PATH var...)