当前位置:  开发笔记 > 编程语言 > 正文

创建DLL时导出所有符号

如何解决《创建DLL时导出所有符号》经验,为你挑选了3个好方法。

使用VS2005,我想创建一个DLL并自动导出所有符号,而无需在任何地方添加__declspec(dllexport),也无需手工创建.def文件.这是一种方法吗?



1> Andrew Stein..:

可以办到...

我们这样做的方法是使用链接器的/ DEF选项传递包含导出列表的"模块定义文件".我从你的问题中看到你知道这些文件.但是,我们不是手工完成的.导出列表本身由dumpbin/LINKERMEMBER命令创建,并通过简单脚本将输出操作为模块定义文件的格式.

设置需要做很多工作,但它允许我们编译在Windows上没有针对Unix的dllexport声明创建的代码.


OP不想在任何地方写`__declspec(dllexport)`.在任何地方添加一些其他导出宏也同样困难.
通常最好添加导出宏,在Windows上扩展为"__declspec(dllexport)",在gcc上扩展为`__attribute __((dllexport))`,在其他编译器上为空.然后在gcc上传递`-fvisibility = hidden`.您将获得一个更小,更清晰的符号表,您将捕获在Linux上进行测试时破坏Windows构建的错误.

2> Maks..:

简短的回答

你可以借助新版本的CMake(任何版本的cmake-3.3.20150721-g9cd2f-win32-x86.exe或更高版本)来实现.

目前它在dev分支中.稍后,该功能将添加到cmake-3.4的发行版中.

链接到cmake dev:

cmake_dev

链接到描述技术的文章:

使用新的CMake export all功能在没有declspec()的情况下在Windows上创建dll

链接到示例项目:

cmake_windows_export_all_symbols


答案很长

警告: 以下所有信息都与MSVC编译器或Visual Studio有关.

如果您在Linux上使用其他编译器(如Linux上的gcc或Windows上的MinGW gcc编译器),则由于未导出符号而导致链接错误,因为gcc编译器默认导出动态库(dll)中的所有符号而不是MSVC或Intel Windows编译器.

在Windows中,您必须从dll显式导出符号.

有关这个的更多信息,请参见链接:

从DLL导出

HowTo:从DLL导出C++类

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

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

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


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


1.1.将"__declspec(dllexport)/ __declspec(dllimport)"宏添加到要使用的类或方法中.因此,如果要导出所有类,则应将此宏添加到所有类中

有关此内容的更多信息,请访问:

使用__declspec(dllexport)从DLL导出

用法示例(用实际项目名称替换"Project"):

// 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语言编写的,你可以在函数前添加extern"C"以消除名称错误

有关C++ name mangling的更多信息,请链接:

名称装饰

用法示例:

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

有关此内容的更多信息,请访问:

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


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

有关此内容的更多信息,请访问:

使用DEF文件从DLL导出

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


2.1.导出C函数

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

用法示例:

extern "C" void HelloWorld();

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

EXPORTS 
_HelloWorld

2.2.从静态库导出符号

我尝试了"user72260"建议的方法.

他说:

首先,您可以创建静态库.

然后使用"dumpbin/LINKERMEMBER"从静态库中导出所有符号.

解析输出.

将所有结果放在.def文件中.

使用.def文件创建dll.

我使用这种方法,但总是创建两个构建(一个作为静态,另一个作为动态库)并不是很方便.但是,我必须承认,这种方法确实有效.


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


2.3.1.使用CMake

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

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

基于"CMakeLists.txt"文件创建CMake项目.

将以下行添加到"CMakeLists.txt"文件中:set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

然后在"CMake(cmake-gui)"的帮助下创建Visual 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文件夹(Root文件夹/ 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 

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

再次链接到示例项目:

cmake_windows_export_all_symbols

CMake使用与"2.2.从静态库导出符号"方法不同的方法.

它执行以下操作:

1)在构建目录中创建"objects.txt"文件,并在dll中使用.obj文件的信息.

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

3)基于"objects.txt"文件信息从.obj文件中提取所有符号.

用法示例:

DUMPBIN /SYMBOLS example.obj > log.txt

有关此内容的更多信息,请访问:

/符号

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

在我看来,我会用打电话的对流,例如"__cdecl/__ FASTCALL","SECTx /民主基金"符号字段(第三列),"外部/静"符号字段(第五列)," - ","? " 用于解析.obj文件的信息.

我不知道CMake究竟是如何解析.obj文件的.但是,CMake是开源的,所以你可以找出它是否对你感兴趣.

链接到CMake项目:

CMake_github

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

6)使用.def创建的文件链接dll.

步骤4)-5),即解析.obj文件并在链接和使用.def文件之前创建.def文件.CMake在"Pre-Link事件"的帮助下完成.当"Pre-Link事件"触发时,您可以调用任何您想要的程序.因此,在"CMake usage""Pre-Link事件"的情况下,使用以下信息调用CMake,其中包含.def文件的放置位置以及"objects.txt"文件和参数"-E __create_def"的位置.您可以通过使用"set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)"创建CMake Visusal Studio项目来检查此信息,然后检查dll的".vcxproj"项目文件.

如果您尝试编译没有"set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)"或"set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)"的项目,您将得到链接错误,因为符号不是从dll导出的.

有关此内容的更多信息,请访问:

了解自定义构建步骤和构建事件


2.3.2.没有CMake使用

你可以简单地创建一个小程序来自己解析.obj文件而不需要使用CMake usege.Hovewer,我不得不承认CMake是非常有用的程序,特别是对于跨平台开发.



3> user72260..:

我编写了一个小程序来解析.lib文件中"dumpbin/linkermember"的输出.我有超过8,000个函数引用从一个DLL导出.

在DLL上执行此操作的问题是,您必须链接DLL而不导出定义一次以创建.lib文件,然后生成.def,这意味着您现在必须使用.def文件再次重新链接DLL将引用导出.

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

不幸的是,我的公司不允许我向您展示来源.所涉及的工作是识别def文件中不需要转储输出中的"公共符号".你必须扔掉很多这些引用,NULL_IMPORT_DESCRIPTOR,NULL_THUNK_DATA,__ imp*等.

推荐阅读
地之南_816
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有