使用cmake构建可执行文件和共享库,runtimelinker找不到dll

问题描述 投票:6回答:4

我正在使用gcc(cygwin),gnu make,windows 7和cmake。

我的cmake testprojekt具有以下结构

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

ROOTDIR /应用/ 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.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 /应用/的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) 

现在,当我运行cmake和make时,一切都将生成并构建,没有错误,但是当我尝试执行二进制文件时,它将失败,因为找不到生成的库。

但是:当我将lib dll复制到app app这样的目录中时,它会被执行!

另外:如果我将库配置为静态,它也将执行。

如何告诉运行时链接器在哪里查找我的DLL?

更新:

解决方案根据用户Vorren提出的方法:

我打开了注册表编辑器,导航到以下密钥:

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

,在这里我创建了一个名为applikation的新密钥:

在这种情况下:TestProjectExecutable.exe

之后,(默认)值设置为TestProjectExecutable.exe的完整路径,包括文件名和扩展名。然后我创建了另一个名为“Path”的字符串值,并将值设置为dll所在的文件夹:

c++ windows dll makefile cmake
4个回答
10
投票

您的问题不在于链接器或编译器,而在于Windows搜索DLL的方式。

操作系统将使用以下算法来定位所需的DLL:

顺便拜访:

  1. 特定于应用程序的Path注册表项中列出的目录;
  2. 当前进程的可执行模块所在的目录;
  3. 当前目录;
  4. Windows系统目录;
  5. Windows目录;
  6. PATH环境变量中列出的目录;

因此,如果您不想使用特定于应用程序的dll来混淆操作系统目录,那么您有两个合理的选择:

  1. 创建一个特定于应用程序的Path注册表项(我会使用此选项);
  2. 将您的DLL放在与EXE相同的文件夹中;
  3. 修改PATH变量(但是为什么要这样做,如果你可以使用选项1?);

9
投票

我不喜欢的解决方案是将您的shared-libs构建到与可执行文件相同的目录中。这往往是一个更简单的解决方案。

使用cmake执行此操作的一种方法是

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $ {CMAKE_BINARY_DIR} / bin)

或者您也可以根据构建风格设置输出目录。

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


1
投票

我尝试了接受答案的选项1(由pdeschain)。我甚至创建了一个cmake挂钩来自动注册链接库的路径

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

可以用作xtarget_link_libraries(test lib1 lib2)。该应用程序将能够在其绝对路径上找到动态库。

但是,这有一个很大的问题,即App Paths机制https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx#appPaths

不允许有'Debug / test.exe'和'Release / test.exe'的不同条目。所以对我来说这是一个糟糕的选择。

您可以添加以下行来填充Default键作为帖子中建议的程序路径。

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 )

现在你可以享受从系统中的任何地方运行test.exe ...我想我的下一次尝试将是选项

  1. 使用cmake创建dll的符号链接。

1
投票

我发现(我相信是)这是一个很好的处理方式。它遵循将.dll添加到与.exe相同的目录的方法。你可以在CMake中这样做:

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

其中app-target是您正在构建的应用程序或库的名称(通过add_executableadd_library创建),lib-target是使用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()
© www.soinside.com 2019 - 2024. All rights reserved.