0

I've got some DLLs arranged like this:

  • lib.dll - 3rd-party DLL with an import library that you link to
  • plugin.dll - a DLL that links to lib.dll, a plugin designed to be loaded into a host program with LoadLibrary
  • other\plugin.dll - another plugin DLL that links to lib.dll
  • other\lib.dll - otherplugin's copy of lib.dll

(These are just ordinary DLLs with some exported functions - I'm not using COM or anything.)

In my test setup, if I use LoadLibrary to load plugin.dll, and then use LoadLibrary to load other\plugin.dll, it seems they both share the same loaded copy of lib.dll. But I actually need other\plugin.dll to load other\lib.dll - I don't want the libraries to be shared.

Can I do this? (Ideally without having to recompile lib.dll.)

Chris Becke
  • 34,244
  • 12
  • 79
  • 148
Tom Seddon
  • 2,648
  • 1
  • 19
  • 28

2 Answers2

3

The technology that allows this is called Application Isolation. The way it works is by having the developer arrange their dlls into Side by Side assemblies using manifest files.

Read Isolated Applications on MSDN for the official documentation.


Alternatively, this could work without all that, assuming you are, in fact, loading "lib.dll" explicitly via LoadLibrary.

First, note that when a Dll calls LoadLibrary, the DLLs own folder is NOT searched. If you check the documentation for LoadLibrary on MSDN you will see that the application executables folder is the preferred search location. So the first thing you need to do is call SetDllDirectory before loading Plugin.dll so that it can actually find its own copy of "lib.dll".

Next - note that the LoadLibrary search paths are only used when a relative path is passed.

So, if you FIRST fix the DllDirectory and LoadLibrary "Plugin.dll" THEN call LoadLibrary, passing the fully qualified path to your own copy of Lib.dll, then plugin.dll will load its own version using the search path, and your app will load its own version explicitly.

Chris Becke
  • 34,244
  • 12
  • 79
  • 148
  • `lib.dll` is loaded implicitly via the import library. – Tom Seddon Aug 01 '16 at 12:26
  • Well then, you might be stuck wrapping "plugin.dll" in a private side by side assembly, or using the Activation Context API to wrap the load of the plugin.dll in a different activation context. I have a lot of answers to these kind of questions, but all 5 years old and I right now my knowledge on this topic is so stale they all confuse me :P – Chris Becke Aug 01 '16 at 12:53
  • 1
    Found one: http://stackoverflow.com/questions/3832290/altering-dll-search-path-for-static-linked-dll/3832428#3832428 – Chris Becke Aug 11 '16 at 06:34
1

There's another option: set the DLL to be delay-loaded, and use the delay load DLL import hook to load explicitly the DLL you want.

When it comes to resolving a delay-loaded DLL, usually the system (though I'm not sure which part - presumably the CRT?) will spot that there's a plugin.dll already loaded and hand out another handle to it. But with the delay load DLL hook you can get it to load another DLL with LoadLibrary - in this case the plugin specific copy of the shared DLL. Supply the full path, so there's no chance LoadLibrary will return a handle to the existing DLL.

To do this, store the DLL's HINSTANCE in DllMain.

static HINSTANCE g_hinstDLL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
    if (fdwReason == DLL_PROCESS_ATTACH)
        g_hinstDLL = hinstDLL;

    return TRUE;
}

And have a suitable delay load hook that watches for dliNotePreLoadLibrary.

static FARPROC WINAPI DliHook(unsigned dliNotify, PDelayLoadInfo pdli) {
    if (dliNotify == dliNotePreLoadLibrary) {
        if (strcmp(pdli->szDll, "plugin.dll") == 0) {
            char path[MAX_PATH];
            GetModuleFileNameA(g_hinstDLL, path, sizeof path);
            PathRemoveFileSpecA(path);
            PathAppendA(path, "plugin.dll");

            HMODULE hModule = LoadLibraryA(path);
            return (FARPROC)hModule;
        }
    }

    return 0;
}

const PfnDliHook __pfnDliNotifyHook2 = &DliHook;

(In the end I had to give up on this whole approach - lib.dll seemed to assume it would only ever be loaded once per process, not unreasonably, and conflicted with itself in various ways when loaded multiple times. Probably solvable in principle, but I expect I'd only then have to do the same again for Linux and OS X...)

Tom Seddon
  • 2,648
  • 1
  • 19
  • 28