LoadLibrary with Absolute Path returns Incorrect HMODULE with No Error

12,026

Solution 1

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.

Solution 2

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;
}
Share:
12,026

Related videos on Youtube

funseiki
Author by

funseiki

Python wrestler, by day. High-fiber eating, app-developing, video game playing being, by night.

Updated on June 29, 2022

Comments

  • funseiki
    funseiki almost 2 years

    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).

    • WhozCraig
      WhozCraig over 11 years
      LoadLibraryEx with alternate search path is likely your best bet.
    • WhozCraig
      WhozCraig
      Only slightly related, but did you intend to include the DLL name in your SetCurrentDirectory() path call ?
  • funseiki
    funseiki over 11 years
    I think you're right, should have realized that. Now I need to figure out how to get myDll.dll's dependencies to load from the new directory - LoadLibraryEx looks promising.
  • funseiki
    funseiki over 11 years
    Hmm, I have kernel32.lib referenced, but AddDllDirectory and many of the flags for LoadLibraryEx are missing. Are there other dependencies required to use these?
  • Anthill
    Anthill over 11 years
    What's your development environment (for instance Visual Studio) and version?
  • Anthill
    Anthill over 11 years
    Try adding #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0502 #endif to your stdafx.h file. I take it you have included Windows.h?
  • Anthill
    Anthill over 11 years
    Hmm it's the Windows version. Use GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "AddlDllDirectory"); The function looks like DLL_DIRECTORY_COOKIE WINAPI AddDllDirectory( In PCWSTR NewDirectory ); Alternatively, SetDllDirectory should do without the hoops (AddDllDirectory is useful when you have several search paths to add). Does that work?
  • funseiki
    funseiki over 11 years
    I believe AddDllDirectory is only available on Windows 8 according to the msdn
  • Anthill
    Anthill over 11 years
  • David Heffernan
    David Heffernan over 11 years
    Is SetDllDirectory process wide, or per thread? If the former then its use could lead to problems.
  • Anthill
    Anthill over 11 years
    SetDllDirectory and AddDllDirectory are process-wide as far as I am aware, so yes, it is associated with quite a few potential pitfalls.
  • funseiki
    funseiki over 11 years
    @Anthill I was able to get it to work - a non-dll file was being loaded from a local directory, so I needed to change that. As far as using AddDllDirectory, GetProcAddress was returning a NULL handle. I believe the error I received was 127 - procedure not found :/.