创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)
。