3
votes

I have some code which is trying to load a Dll.

I have encountered an odd 'error' with this. When trying to load the dll from an absolute path, I get a Non-Null HMODULE that gives no windows error codes on a GetLastError call (i.e. GetLastError returns '0' or success according to msdn). When calling into a function in this dll, I get incorrect values.

This behavior is peculiar, because if, instead, I switch the current directory to be that of my current dll using SetCurrentDirectory and use a relative path call to LoadLibrary, I get correct values.

Here is a snippet describing the situation:

Using absolute path:

std::string libLoc = get_dll_location(); // Get the directory of this dll
HMODULE myDLL = LoadLibraryA(libLoc.c_str()); // Non-null value
DWORD lastError = GetLastError(); // returns 0

MyObj * value = UseDLL(myDLL); // bad value

Using relative path:

SetCurrentDirectory("c:\\path\\containing\\dll\\"); // hard coded path to dll's folder
HMODULE myDLL = LoadLibrary("myDll.dll");  // Non-null value
MyObj * value = UseDLL(myDLL);  // Good value

I'd really like to avoid having to use SetCurrentDirectory, because the application using this Dll may be multi-threaded and require that the directory stays the same.

Any insight on this issue would be much appreciated. Hopefully, this is just a minor bug on my part.

Update: Using LoadLibraryEx seems to be out of the question, as the LOAD_LIBRARY_SEARCH_* flags don't appear to be available to me (I've already tried installing the KB2533623 update).

2
LoadLibraryEx with alternate search path is likely your best bet. - WhozCraig
More likely something else going on that's not obvious. Is the 'UseDLL' impacted by the directory change? What happens if you use a 'LoadLibrary' with a hard-coded relative path (but not changing current directory)? - mark
Only slightly related, but did you intend to include the DLL name in your SetCurrentDirectory() path call ? - WhozCraig
@WhozCraig This looks to be a solid function and I'll see if it works, but the LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is only available for windows 7 and above - this may introduce backwards compatability issues. - funseiki
If that is the case, relying on the search path via LoadLibrary is going to be your only bet, and I would suggest you read David's answer several times over below. I hate relying on the system search path for anything besides OS libs only because it can be such a security hole if properly exploited. It sounds like you have a better idea of whats going on though, which is good. - WhozCraig

2 Answers

7
votes

Most likely the problem is that MyDll.dll has dependencies on other DLLs that reside in the same folder as MyDll.dll. When your application lives in a different folder from MyDll.dll, those dependencies will not be resolved from the folder that contains MyDll.dll. Instead they are resolved by the system DLL search order.

Your use SetCurrentDirectory influences the system DLL search order. And it means that the dependencies of MyDll.dll are resolved from the directory that contains MyDll.dll.

You can test this hypothesis by using, for example, Dependency Walker in profile mode. That will tell you how the dependencies of MyDll.dll are resolved at runtime.

You are right to dislike the use of SetCurrentDirectory. The best solution would be to put all the DLLs in the same directory as the application.

Of course, the other possibility is that the call to UseDLL has a dependency on the working directory. You can rule that out by changing the working directory back to its original value after the call to LoadLibrary.

5
votes

Try setting SetDllDirectory or AddDllDirectory instead of SetCurrentDirectory, and use LoadLibraryEx with flag LOAD_LIBRARY_SEARCH_USER_DIRS instead of LoadLibrary. Useful information can be found on MSDN here.

Update: Example of AddDllDirectory and associated calls on Windows 7 (requires KB2533623)

#include "stdafx.h"
#include <iostream>
#include <Windows.h>

typedef DLL_DIRECTORY_COOKIE (WINAPI *ADD_DLL_PROC)(PCWSTR);
typedef BOOL (WINAPI *REMOVE_DLL_PROC)(DLL_DIRECTORY_COOKIE);

#ifndef LOAD_LIBRARY_SEARCH_USER_DIRS
#define LOAD_LIBRARY_SEARCH_USER_DIRS       0x00000400
#endif

int main()
{

    ADD_DLL_PROC lpfnAdllDllDirectory = (ADD_DLL_PROC)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "AddDllDirectory");
    REMOVE_DLL_PROC lpfnRemoveDllDirectory = (REMOVE_DLL_PROC)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "RemoveDllDirectory");
    if(lpfnAdllDllDirectory && lpfnRemoveDllDirectory)
    {

        DLL_DIRECTORY_COOKIE cookie = ((ADD_DLL_PROC)lpfnAdllDllDirectory)(L"c:\\windows\\system32\\");
        std::cout << cookie << std::endl;
        HMODULE module = LoadLibraryEx(L"cabview.dll", NULL, LOAD_LIBRARY_SEARCH_USER_DIRS);
        if(module)
        {
            std::cout << "Locked and loaded" << std::endl;
            FreeLibrary(module);
        }
        if(cookie && ((REMOVE_DLL_PROC)(cookie)))
        {
            std::cout << "Added and removed cookie" << std::endl;
        }


    }
    std::cin.get();
    return 0;
}