Friday, February 14, 2014

RPATH on Windows

On Linux, we can specify shared library search paths for our executable/libraries using RPATH. On Windows, however, there is no such easy way to do that. A possible solution is to use /DELAYLOAD and SetDllDirectory.

Ways to Load a DLL

Basically speaking, there are three ways to load a DLL on windows.
- Implicitly loading. In this way, we link our application against the .lib file of the DLL. The DLL will be loaded once the application was invoked.
- Explicitly loading. That is the way we call LoadLibrary Win32 API to load a DLL. The search path can be changed by calling SetDllDirectory function.
- Delayed loading. Similar to Implicit loading, but the DLL won’t be loaded until once of functions in it got called.

Delay loading

/DELAYLOAD is a linker option of MSVC compiler (version 6 or later) that can tell the linker which DLL we want to delay load. So, to simulate the functionality of RPATH, we can specify the DLL should be delay loaded via /DELAYLOAD at link time, and call SetDllDirectory function to set the search path. After that, there are three ways to force load the library.

  • We can simply call a function that is exported in that library to force loading the library.
  • Call LoadLibrary to explicitly load the library.
  • Call __HrLoadAllImportsForDll to force load the library.

Listed below is an example to use the above method to delay load a DLL. Please note that to make the code work, delayimp.lib should be linked against.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <delayimp.h>
#include <string>

bool loadTheLibraryNow(const char* dir) {
    const char* baseDllName = "library-to-delay-load.dll";

    if (!dir)
        return false;

    // backup current dll directory
    char previousDllDirectory[MAX_PATH] = { '\0' };
    if (!::GetDllDirectoryA(MAX_PATH, previousDllDirectory)) {
        previousDllDirectory[0] = '\0';
    }

    // set new current dll directory to
    if (!::SetDllDirectoryA(dir))
        return false;

    // force load the dll
    bool result = SUCCEEDED(__HrLoadAllImportsForDll(baseDllName));

    // restore the dll directory to old value
    ::SetDllDirectoryA(previousDllDirectory);

    return result;
}

Please See here for more details.

No comments:

Post a Comment