代码之家  ›  专栏  ›  技术社区  ›  Makogan

默认情况下,能否在windows上导出所有符号?

  •  0
  • Makogan  · 技术社区  · 3 年前

    使用VS2005,我想创建一个DLL并自动导出所有符号,而无需添加 __declspec(dllexport) 到处都是,不用手工制作 .def 文件夹。有办法做到这一点吗?

    0 回复  |  直到 3 年前
        1
  •  0
  •   Alexander Samoylov    5 年前

    简短回答

    您可以在新版CMake(任何版本的CMake-3.3.20150721-g9cd2f-win32-x86.exe或更高版本)的帮助下完成此操作。

    目前它在开发部门。 稍后,该功能将添加到cmake-3.4的发行版本中。

    链接到cmake开发:

    cmake_dev

    链接到描述该技术的文章:

    Create dlls on Windows without declspec() using new CMake export all feature

    链接到一个示例项目:

    cmake_windows_export_all_symbols


    长话短说

    注意: 以下所有信息都与MSVC编译器或Visual Studio有关。

    如果在Linux上使用gcc或Windows上使用MinGW gcc编译器等其他编译器,则不会因未导出符号而出现链接错误,因为默认情况下,gcc编译器会导出动态库(dll)中的所有符号,而不是MSVC或英特尔Windows编译器。

    在windows中,必须从dll显式导出符号。

    链接提供了更多信息:

    Exporting from a DLL

    HowTo: Export C++ classes from a DLL

    因此,如果要使用MSVC(Visual Studio编译器)从dll导出所有符号,有两个选项:

    • 在类/函数的定义中使用关键字_declspec(dllexport)。
    • 创建模块定义(.def)文件并使用。在构建DLL时使用def文件。

    1.在类/函数的定义中使用关键字_declspec(dllexport)


    1.1. 将“_declspec(dllexport)/_declspec(dllimport)”宏添加到要使用的类或方法中。所以如果你想导出所有的类,你应该把这些宏添加到所有的类中

    链接提供了更多信息:

    Exporting from a DLL Using __declspec(dllexport)

    用法示例(将“项目”替换为真实的项目名称):

    // ProjectExport.h
    
    #ifndef __PROJECT_EXPORT_H
    #define __PROJECT_EXPORT_H
    
    #ifdef USEPROJECTLIBRARY
    #ifdef  PROJECTLIBRARY_EXPORTS 
    #define PROJECTAPI __declspec(dllexport)
    #else
    #define PROJECTAPI __declspec(dllimport)
    #endif
    #else
    #define PROJECTAPI
    #endif
    
    #endif
    

    然后将“PROJECTAPI”添加到所有类中。 仅当需要从dll导出/导入符号时,才定义“USEPROJECTLIBRARY”。 为dll定义“PROJECTLIBRARY_EXPORTS”。

    类导出的示例:

    #include "ProjectExport.h"
    
    namespace hello {
        class PROJECTAPI Hello {}   
    }
    

    函数导出示例:

    #include "ProjectExport.h"
    
    PROJECTAPI void HelloWorld();
    

    注意: 不要忘记包含“ProjectExport.h”文件。


    1.2. 导出为C函数。 如果使用C++编译器编译代码,则可以在函数前添加外部“C”,以消除名称的错误。

    有关C++名称的更多信息由链接提供:

    Name Decoration

    用法示例:

    extern "C" __declspec(dllexport) void HelloWorld();
    

    链接提供了更多信息:

    Exporting C++ Functions for Use in C-Language Executables


    2.创建模块定义(.def)文件并使用。构建DLL时的def文件

    链接提供了更多信息:

    Exporting from a DLL Using DEF Files

    此外,我还描述了三种关于如何创建的方法。def文件。


    2.1. 导出C函数

    在这种情况下,您可以在中添加简单的函数声明。def手动归档。

    用法示例:

    extern "C" void HelloWorld();
    

    例如。def文件(_cdecl命名约定):

    EXPORTS 
    _HelloWorld
    

    2.2. 从静态库导出符号

    我尝试了“user72260”建议的方法。

    他说:

    • 首先,可以创建静态库。
    • 然后使用“dumpbin/LINKERMEMBER”从静态库导出所有符号。
    • 解析输出。
    • 把所有结果放在一个表格中。def文件。
    • 使用。def文件。

    我使用了这种方法,但总是创建两个构建(一个作为静态库,另一个作为动态库)不是很方便。然而,我不得不承认,这种方法确实有效。


    2.3. 从导出符号。obj文件或在CMake的帮助下


    2.3.1. 使用CMake

    重要提示: 您不需要将任何宏导出到类或函数!

    重要提示: 您不能使用/GL( Whole Program Optimization )当使用这种方法时!

    • 基于“CMakeLists.txt”文件创建CMake项目。
    • 将以下行添加到“CMakeLists.txt”文件中: 设置(CMAKE_WINDOWS_导出_所有符号打开)
    • 然后在“CMake(CMake gui)”的帮助下创建Visual Studio项目。
    • 编译项目。

    用法示例:

    根文件夹

    克马克主义者。txt(根文件夹)

    cmake_minimum_required(VERSION 2.6)
    project(cmake_export_all)
    
    set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
    
    set(dir ${CMAKE_CURRENT_SOURCE_DIR})
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${dir}/bin")
    
    set(SOURCE_EXE main.cpp)
    
    include_directories(foo)
    
    add_executable(main ${SOURCE_EXE})
    
    add_subdirectory(foo)
    
    target_link_libraries(main foo)
    

    主要的cpp(根文件夹)

    #include "foo.h"
    
    int main() {
        HelloWorld();
    
        return 0;
    }
    

    Foo文件夹(根文件夹/Foo文件夹)

    克马克主义者。txt(Foo文件夹)

    project(foo)
    
    set(SOURCE_LIB foo.cpp)
    
    add_library(foo SHARED ${SOURCE_LIB})
    

    福。h(Foo文件夹)

    void HelloWorld();
    

    福。cpp(Foo文件夹)

    #include <iostream>
    
    void HelloWorld() {
        std::cout << "Hello World!" << std::endl;
    }
    

    再次链接到示例项目:

    cmake_windows_导出_所有符号

    CMake使用不同于“2.2.从静态库导出符号”的方法。

    它的功能如下:

    1) 在build目录中创建“objects.txt”文件,其中包含以下信息:。obj文件在dll中使用。

    2) 编译dll,即创建。obj文件。

    3) 根据“objects.txt”文件信息从中提取所有符号。obj文件。

    用法示例:

    DUMPBIN /SYMBOLS example.obj > log.txt
    

    链接提供了更多信息:

    /SYMBOLS

    4) 解析从中提取。obj文件信息。

    在我看来,我会使用调用对流,例如“_cdecl/_fastcall”、“SECTx/unde”符号字段(第三列)、“外部/静态”符号字段(第五列)、“??”,"?" 用于分析一个实例的信息。obj文件。

    我不知道如何准确地解析一个。obj文件。 然而,CMake是开源的,所以你可以看看它是否对你感兴趣。

    链接到CMake项目:

    CMake_github

    5) 将所有导出的符号放在一个文件夹中。def文件。

    6) 链接一个dll和一个。def创建了一个文件。

    步骤4)-5),即解析。obj文件并创建一个。在链接和使用之前,请查看def文件。def文件CMake在“链接前事件”的帮助下完成。 当“预链接事件”启动时,你可以调用任何你想要的程序。 因此,如果是“CMake使用情况”,“预链接事件”调用CMake,并提供以下信息,说明在何处放置文件。def文件和其中的“objects.txt”文件以及参数“-E__create_def”。 您可以通过创建带有“set(CMake_WINDOWS_EXPORT_ALL_SYMBOLS ON)”的CMake Visusal Studio项目来检查此信息,然后检查“.vcxproj”项目文件中的dll。

    如果您试图编译一个没有“设置(CMAKE_WINDOWS_导出_所有_符号打开)”或“设置(CMAKE_WINDOWS_导出_所有_符号关闭)”的项目,您将得到链接错误,因为符号不是从dll导出的。

    链接提供了更多信息:

    Understanding Custom Build Steps and Build Events


    2.3.2. 不使用CMake

    你可以简单地创建一个用于解析的小程序。obj文件由youself提供,无需使用CMake。Hovewer,我不得不承认CMake是一个非常有用的程序,尤其是对于跨平台开发。