Building of executable and shared library with cmake, runtimelinker does not find dll

17,994

Solution 1

Your problem lies not with linker or compiler, but with the way Windows searches for DLL's.

The OS will use the following algorithm to locate the required DLL's:

Look in:

  1. The directories listed in the Application-specific Path registry key;
  2. The directory where the executable module for the current process is located;
  3. The current directory;
  4. The Windows system directory;
  5. The Windows directory;
  6. The directories listed in the PATH environment variable;

Thus you have two reasonable options if you don't want to clutter the OS directories with your app-specific dll:

  1. Create an app-specific Path registry entry (I would go with this option);
  2. Put your DLL in the same folder as your EXE;
  3. Modify the PATH variable (but why would you do that, if you can go with option 1?);

Solution 2

A solution I prefer that hasn't really been mentioned, is build your shared-libs into the same directory as your executables. This tends to be a much simpler solution.

One way to do this with cmake is

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

Or you can also set output directories based on build flavours.

See how do I make cmake output into a 'bin' dir?

Solution 3

I discovered (what I believe to be) quite a nice way of handling this. It follows the approach of adding the .dll to the same directory as the .exe. You can do it in CMake like so:

if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
    TARGET <app-target> POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    $<TARGET_FILE_DIR:<lib-target>>
    $<TARGET_FILE_DIR:<app-target>)
endif()

where app-target is the name of the application or library you're building (created through add_executable or add_library) and lib-target is the imported library brought in with find_package.

# full example
cmake_minimum_required(VERSION 3.14)

project(my-app-project VERSION 0.0.1 LANGUAGES CXX)

find_package(useful-library REQUIRED)

add_executable(my-application main.cpp)

target_link_libraries(my-application PUBLIC useful-library::useful-library)

if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
    TARGET my-application POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    $<TARGET_FILE_DIR:useful-library::useful-library>
    $<TARGET_FILE_DIR:my-application>)
endif()

Solution 4

I tried the option 1 from accepted answer (by pdeschain). I even created a cmake hook to register paths of linked libraries automatically

function (xtarget_link_libraries target libs) # same as target_link_libraries but with additional improvements to allow windows find the library at runtime
LIST(REMOVE_AT ARGV 0)
SET(LIBS ${ARGV}) # this is to pass list into this function
target_link_libraries(${target} ${LIBS}) # call standard routine

if(WIN32)
    set(TFILE ".")
    get_property(slibs TARGET ${target} PROPERTY all_libs) # recall libs linked before
    set(LIBS ${slibs};${LIBS})
    set_property(TARGET ${target} PROPERTY all_libs ${LIBS}) # save all libs
    FOREACH(lib ${LIBS}) # compose a list of paths
        set(TFILE "${TFILE};$<TARGET_LINKER_FILE_DIR:${lib}>")
    ENDFOREACH()
    #add reg key
    add_custom_command(TARGET ${target} POST_BUILD COMMAND  reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /v "Path" /d "${TFILE}" /f )
endif()
endfunction()

Can be used as xtarget_link_libraries(test lib1 lib2). The application will be able to find dynamic libraries at their absolute paths.

BUT, there is a big problem with this, that the App Paths mechanism https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx#appPaths

does not allow to have different entries for say 'Debug/test.exe' and 'Release/test.exe'. So to me this is a poor option.

You may add the following line to fill the Default key as path to the program as suggested in the post.

add_custom_command(TARGET ${target} POST_BUILD COMMAND reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /ve /d "$<TARGET_FILE:${target}>" /f )

Now you can enjoy running test.exe from anywhere in the system... I guess my next try will be option

  1. Create symbolic links to dlls with cmake.
Share:
17,994
Daniel Jawna
Author by

Daniel Jawna

Updated on June 09, 2022

Comments

  • Daniel Jawna
    Daniel Jawna almost 2 years

    I am working with gcc(cygwin), gnu make, windows 7 and cmake.

    my cmake testprojekt has the following structure

    rootdir
    |-- App
    |   |-- app.cpp
    |   +-- CMakeLists.txt
    |-- Lib
    |   |-- lib.cpp
    |   |-- CMakeLists.txt
    |-- MakeFileProject
    + CMakeLists.txt
    

    rootdir/App/app.cpp:

    #include<string>
    void printThemMessageToScreen(std::string input);//prototype
    int main(int argc,char **argv){
     printThemMessageToScreen("this will be displayed by our lib");
     return 0;
    }
    

    rootdir/Lib/lib.cpp:

    #include<iostream>
    #include<string>
    
    void printThemMessageToScreen(std::string input){
     std::cout<<input;
    }
    

    rootdir/CMakeLists.txt:

    cmake_minimum_required(VERSION 2.6)
    project(TestProject)
    
    add_subdirectory(App)
    add_subdirectory(Lib)
    

    rootdir/Lib/CMakeLists.txt:

    add_library(Lib SHARED lib.cpp)
    

    rootdir/App/CMakeLists.txt:

    # Make sure the compiler can find include files from our Lib library. 
    include_directories (${LIB_SOURCE_DIR}/Lib) 
    
    # Make sure the linker can find the Lib library once it is built. 
    link_directories (${LIB_BINARY_DIR}/Lib) 
    
    # Add executable called "TestProjectExecutable" that is built from the source files 
    add_executable (TestProjectExecutable app.cpp) 
    
    # Link the executable to the lib library. 
    target_link_libraries (TestProjectExecutable Lib) 
    

    Now, when i run cmake and make, everything will get generated & built with no errors, but when i try to execute the binary, it will fail because the library which was generated could not be found.

    BUT: when i copy the lib dll into the same directory like the app exe, it will get executed!

    also: if i configure the library to be static, it will also execute.

    how to tell the runtime linker where to look for my dll?

    UPDATE:

    Solution according to the Method proposed by User Vorren:

    I opened up the registry editor, and navigated to the following Key:

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
    

    , here i created a new key with the name of my Applikation:

    in this case : TestProjectExecutable.exe

    after that, the (default) value was set to the full path of TestProjectExecutable.exe including the filename and extension. Then i created another String Value called "Path" and set the value to the folder where the dll was located:

    enter image description here

    • drescherjm
      drescherjm about 10 years
      Is the dll in the same folder as the executable or in your path?
    • Daniel Jawna
      Daniel Jawna about 10 years
      well no, i want the dll to be in a different directory, but if i copy the dll into the same directory as the exe, the programm will execute just fine.
    • drescherjm
      drescherjm about 10 years
      Then you most likely have to add the folder containing the dll to your PATH variable. This really is not a cmake or cygwin specific problem but a windows problem. See here for how windows finds dlls: msdn.microsoft.com/en-us/library/7d83bc18.aspx
    • Daniel Jawna
      Daniel Jawna about 10 years
      @drescherjm yes adding the dll directory to the PATH variable will do the trick, but i wonder if this is the "right" way of solving this poblem...
    • drescherjm
      drescherjm about 10 years
      The easiest way is to put the dll in the same folder as the exe. The link I gave you showed the 5 possible ways to do this and all are considered fine with Microsoft. Although with UAC touching the system folders will require elevation and obviously rights.
    • drescherjm
      drescherjm about 10 years
      BTW with cmake you can easily set the output folders such that the .dll is put in the same folder as the exe: stackoverflow.com/questions/6594796/… I use this method on all of my cmake based projects.
    • Knitschi
      Knitschi over 9 years
      I have the same problem. My exe is under "../out/bin", my dll is under "../out/bin/plugins". In the production code the dll is linked explicitly so it is no problem that it is not in the exe folder. But now I want to create a test exe in "../out/bin", that links implicitly with the dll. I tried to import the library with the commands found here "cmake.org/Wiki/CMake/Tutorials/Exporting_and_Importing_Targ‌​ets", but the exe can still not find the dll.
    • Knitschi
      Knitschi over 9 years
      For now I "solved" the problem by copying the dll to the bin folder with the exe, but this is somewhat ugly.
  • Daniel Jawna
    Daniel Jawna over 9 years
    So when i want to use option 1, the shipable application would need an installer like wix or that nullsoft installer, to create the Registry entries for my dlls right?
  • pdeschain
    pdeschain over 9 years
    Either that, or you'd need to figure out how to modify registry from your application's code. Check out this link: msdn.microsoft.com/en-us/library/windows/desktop/…
  • Daniel Jawna
    Daniel Jawna over 9 years
    ok i will look into it and try your first option, right after im done with my work for today.
  • Daniel Jawna
    Daniel Jawna over 9 years
    Yes, your method works like a charm! i will elaborate a little on my question, on how to get it to work with the registry app-path
  • pdeschain
    pdeschain over 9 years
    Second option in my answer And was mentioned in the comments to the question ;)
  • Knitschi
    Knitschi over 9 years
    The problem occurred at work. If I want to change the output directory of the dll I need to convince someone else that he needs to change the output directory for the dlls. I was hoping to avoid that by just changing somthing in my local CMakeLists file for my test exe.
  • Knitschi
    Knitschi over 9 years
    This answer helps solving the problem. I guess I will try to temporarily add my dll folder to the PATH while executing the test exe.
  • StarShine
    StarShine almost 9 years
    For me this is the most sensible option as well.
  • Chris
    Chris over 8 years
    Is there a way to have cmake edit the app-specific path registries?
  • Liviu
    Liviu over 7 years
    @Knitschi You don't have to convince them since you can make your own CMakeLists.txt (Visual Studio solution) that sets this wonderful variable and includes their CMakeLists files
  • Knitschi
    Knitschi over 7 years
    @Liviu I think the Problem was that our project had an extra folder for some plugin dlls and another folder for the executables. The production code manually loaded the plugins, using the correct path. When I wrote tests for the plugin code, the executables ended up in the exe folder and I wanted to avoid loading the plugin dlls manually in the tests. But I am in another company now, so no worries ;-)