创buildDLL时导出所有符号

使用VS2005,我想创build一个DLL并自动导出所有符号,而无需在每个地方添加__declspec(dllexport),而无需手动创build.def文件。 是否有办法做到这一点?

可以办到…

我们这里做的方式是使用链接器的/ DEF选项来传递一个包含我们导出列表的“模块定义文件” 。 我从你的问题中看到你知道这些文件。 但是,我们不是亲手做的。 导出列表本身由dumpbin / LINKERMEMBER命令创build,并通过简单脚本将输出操作为模块定义文件的格式。

这需要很多工作,但是它允许我们在Windows上编译没有dllexport声明的代码。

简短的回答

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

目前它在开发分支。 稍后,该function将被添加到cmake-3.4的发行版中。

链接到cmake dev:

cmake_dev

链接到描述技术的文章:

在没有使用新的CMake导出所有function的declspec()的Windows上创buildDLL

链接到一个示例项目:

cmake_windows_export_all_symbols


长答案

警告:下面的所有信息都与MSVC编译器或Visual Studio相关。

如果您使用其他编译器(如Linux上的gcc或Windows上的MinGW gcc编译器),则由于未导出符号而没有链接错误,因为默认情况下,gcc编译器会导出dynamic库(dll)中的所有符号,而不是MSVC或Intel Windows编译器。

在Windows中,你必须显式地从一个DLL中导出符号。

有关这方面的更多信息由链接提供:

从DLL导出

如何:从DLL导出C ++类

所以,如果你想用MSVC(Visual Studio编译器)从DLL导出所有的符号,你有两个select:

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

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


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

更多关于这方面的信息由链接提供:

从DLL导出使用__declspec(dllexport)

使用示例(用实际项目名称replace“项目”):

 // 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 {} } 

function导出示例:

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

警告:不要忘记包含“ProjectExport.h”文件。


1.2。 导出为C函数。 如果使用C ++编译器进行编译,则在C上编写代码时,可以在函数前添加extern“C”以消除名称重组

有关C ++名称mangling的更多信息,请参阅链接:

名称装饰

使用示例:

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

更多关于这方面的信息由链接提供:

导出用于C语言可执行文件的C ++函数


2.创build模块定义(.def)文件并在构buildDLL时使用.def文件

更多关于这方面的信息由链接提供:

使用DEF文件从DLL导出

接下来我将介绍如何创build.def文件的三种方法。


2.1。 导出C函数

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

使用示例:

 extern "C" void HelloWorld(); 

.def文件的示例(__cdecl命名约定):

 EXPORTS _HelloWorld 

2.2。 从静态库中导出符号

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

他说:

  • 首先,你可以创build静态库。
  • 然后使用“dumpbin / LINKERMEMBER”导出静态库中的所有符号。
  • parsing输出。
  • 把所有的结果放在一个.def文件中。
  • 使用.def文件创builddll。

我使用这种方法,但总是创build两个构build(一个是静态的,另一个是dynamic库)并不是很方便。 不过,我不得不承认,这种方法确实有效。


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


2.3.1。 使用CMake

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

重要提示:使用此方法时,不能使用/ GL( 整体程序优化 )!

  • 基于“CMakeLists.txt”文件创buildCMake项目。
  • 将以下行添加到“CMakeLists.txt”文件中:set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
  • 然后在“CMake(cmake-gui)”的帮助下创buildVisual Studio项目。
  • 编译该项目。

使用示例:

根文件夹

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

main.cpp(根文件夹)

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

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

CMakeLists.txt(Foo文件夹)

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

foo.h(Foo文件夹)

 void HelloWorld(); 

foo.cpp(Foo文件夹)

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

再次链接到示例项目:

cmake_windows_export_all_symbols

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

它执行以下操作:

1)在build目录下创build“objects.txt”文件,在.dll文件中使用.obj文件的信息。

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

3)基于“objects.txt”文件信息提取.obj文件中的所有符号。

使用示例:

 DUMPBIN /SYMBOLS example.obj > log.txt 

更多关于这方面的信息由链接提供:

/符号

4)parsing从.obj文件中提取的信息。

在我看来,我将使用呼叫对stream,例如“__cdecl / __ fastcall”,“SECTx / UNDEF”符号字段(第三列),“外部/静态”符号字段(第五列),“??”,“ “ parsing.obj文件的信息。

我不知道CMake如何parsing一个.obj文件。 但是,CMake是开源的,所以你可以看看它是否对你感兴趣。

链接到CMake项目:

CMake_github

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

6)使用.def创build的文件链接一个dll。

步骤4)-5),即parsing.obj文件,并在链接和使用.def文件之前创build.def文件。CMake在“Pre-Link事件”的帮助下完成。 当“Pre-Link事件”发生时,您可以调用您想要的任何程序。 因此,在“CMake使用情况”,“Pre-Link事件”的情况下,使用以下有关放置.def文件的位置以及“objects.txt”文件和参数“-E __create_def”的信息调用CMake。 您可以通过创build“设置(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)”的CMake Viscous Studio项目来检查此信息,然后检查“.vcxproj”项目文件中的dll。

如果你尝试编译一个没有“set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)”或者“set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)”的项目,你会得到链接错误,因为符号不是从dll导出的。

更多关于这方面的信息由链接提供:

了解自定义生成步骤和生成事件


2.3.2。 没有CMake的使用

你简单的可以创build一个小程序来自己parsing.obj文件而不需要CMake usege。 Hovewer,我不得不承认,CMake是非常有用的程序,尤其是跨平台开发。

我写了一个小程序来parsing.lib文件中的“dumpbin / linkermember”的输出。 我有超过8000个函数引用从一个DLL导出。

在DLL上执行的问题是,必须链接DLL,而不导出定义一次以创build.lib文件,然后生成.def,这意味着您现在必须再次使用.def文件重新链接DLL以实际有参考出口。

使用静态库更容易。 编译所有的源代码到静态库中,运行dumbin,用你的小程序生成一个.def,然后将这些库链接到一个DLL中,然后导出名称可用。

不幸的是,我的公司不会允许我向您显示来源。 涉及的工作是识别def文件中不需要转储输出中的哪些“公共符号”。 你必须扔掉很多那些引用,NULL_IMPORT_DESCRIPTOR,NULL_THUNK_DATA,__imp *等

不,你将需要一个当它被实现导出函数的.cpp文件包含的时候parsing为__declspec(dllexport)的macros,否则就会parsing为__declspec(dllimport)