Jaz*_*azz 65 c++ windows visual-studio-2005 visual-studio
使用VS2005,我想创建一个DLL并自动导出所有符号,而无需在任何地方添加__declspec(dllexport),也无需手工创建.def文件.这是一种方法吗?
And*_*ein 39
可以办到...
我们这样做的方法是使用链接器的/ DEF选项传递包含导出列表的"模块定义文件".我从你的问题中看到你知道这些文件.但是,我们不是手工完成的.导出列表本身由dumpbin/LINKERMEMBER命令创建,并通过简单脚本将输出操作为模块定义文件的格式.
设置需要做很多工作,但它允许我们编译在Windows上没有针对Unix的dllexport声明创建的代码.
Mak*_*aks 31
你可以借助新版本的CMake(任何版本的cmake-3.3.20150721-g9cd2f-win32-x86.exe或更高版本)来实现.
目前它在dev分支中.稍后,该功能将添加到cmake-3.4的发行版中.
链接到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显式导出符号.
有关这个的更多信息,请参见链接:
因此,如果要使用MSVC(Visual Studio编译器)从dll导出所有符号,则有两个选项:
1.在类/函数的定义中使用关键字__declspec(dllexport)
1.1.将"__declspec(dllexport)/ __declspec(dllimport)"宏添加到要使用的类或方法中.因此,如果要导出所有类,则应将此宏添加到所有类中
有关此内容的更多信息,请访问:
用法示例(用实际项目名称替换"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
Run Code Online (Sandbox Code Playgroud)
然后将"PROJECTAPI"添加到所有类.仅当您需要来自dll的导出/导入符号时才定义"USEPROJECTLIBRARY".为dll定义"PROJECTLIBRARY_EXPORTS".
类导出示例:
#include "ProjectExport.h"
namespace hello {
class PROJECTAPI Hello {}
}
Run Code Online (Sandbox Code Playgroud)
功能导出示例:
#include "ProjectExport.h"
PROJECTAPI void HelloWorld();
Run Code Online (Sandbox Code Playgroud)
警告:不要忘记包含"ProjectExport.h"文件.
1.2.导出为C函数.如果使用C++编译器编译代码是用C语言编写的,你可以在函数前添加extern"C"以消除名称错误
有关C++ name mangling的更多信息,请链接:
用法示例:
extern "C" __declspec(dllexport) void HelloWorld();
Run Code Online (Sandbox Code Playgroud)
有关此内容的更多信息,请访问:
2.创建模块定义(.def)文件,并在构建DLL时使用.def文件
有关此内容的更多信息,请访问:
此外,我描述了有关如何创建.def文件的三种方法.
2.1.导出C函数
在这种情况下,您可以手动在.def文件中简单地添加函数声明.
用法示例:
extern "C" void HelloWorld();
Run Code Online (Sandbox Code Playgroud)
.def文件的示例(__cdecl命名约定):
EXPORTS
_HelloWorld
Run Code Online (Sandbox Code Playgroud)
2.2.从静态库导出符号
我尝试了"user72260"建议的方法.
他说:
我使用这种方法,但总是创建两个构建(一个作为静态,另一个作为动态库)并不是很方便.但是,我必须承认,这种方法确实有效.
2.3.从.obj文件中导出符号或在CMake的帮助下导出符号
2.3.1.使用CMake
重要提示:您不需要任何类或函数的导出宏!
重要提示:使用此方法时,不能使用/ GL(整个程序优化)!
用法示例:
根文件夹
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)
Run Code Online (Sandbox Code Playgroud)
main.cpp(根文件夹)
#include "foo.h"
int main() {
HelloWorld();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Foo文件夹(Root文件夹/ Foo文件夹)
CMakeLists.txt(Foo文件夹)
project(foo)
set(SOURCE_LIB foo.cpp)
add_library(foo SHARED ${SOURCE_LIB})
Run Code Online (Sandbox Code Playgroud)
foo.h(Foo文件夹)
void HelloWorld();
Run Code Online (Sandbox Code Playgroud)
foo.cpp(Foo文件夹)
#include <iostream>
void HelloWorld() {
std::cout << "Hello World!" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
再次链接到示例项目:
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
Run Code Online (Sandbox Code Playgroud)
有关此内容的更多信息,请访问:
4)从.obj文件信息中提取的解析.
在我看来,我会用打电话的对流,例如"__cdecl/__ FASTCALL","SECTx /民主基金"符号字段(第三列),"外部/静"符号字段(第五列)," - ","? " 用于解析.obj文件的信息.
我不知道CMake究竟是如何解析.obj文件的.但是,CMake是开源的,所以你可以找出它是否对你感兴趣.
链接到CMake项目:
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是非常有用的程序,特别是对于跨平台开发.
jww*_*jww 10
我想创建一个 DLL 并自动导出所有符号,而无需在任何地方添加 __declspec(dllexport) 并且无需手动创建 .def 文件。有没有办法做到这一点?
这是一个迟到的答案,但它在第 (2) 节中提供了 Maks 答案的详细信息。它还避免使用脚本并使用名为dump2def
. 源代码dump2def
如下。
最后,以下步骤假设您正在使用 Visual Studio Developer Prompt,这是一个vcvarsall.bat
已运行的 Windows 终端。您需要确保这样的构建工具cl.exe
,lib.exe
,link.exe
和nmake.exe
是在路径上。
链接提供了有关此的更多信息:
下面的指令使用:
static.lib
- 静态库存档(Linux 上的 *.a 文件)dynamic.dll
- 动态库(Linux 上的 *.so 文件)import.lib
- 动态库(Windows 上的导入库)另请注意,尽管您从 DLL 中导出所有内容,但客户端仍必须declspec(dllimport)
在它们使用的所有符号(类、函数和数据)上使用。另见 MSDN。
首先,获取您的对象并创建一个静态存档:
AR = lib.exe
ARFLAGS = /nologo
CXX_SRCS = a.cpp b.cpp c.cpp ...
LIB_OBJS = a.obj b.obj c.obj ...
static.lib: $(LIB_OBJS)
$(AR) $(ARFLAGS) $(LIB_OBJS) /out:$@
Run Code Online (Sandbox Code Playgroud)
其次,dumpbin.exe /LINKERMEMEBER
在存档上运行以创建*.dump
文件:
dynamic.dump:
dumpbin /LINKERMEMBER static.lib > dynamic.dump
Run Code Online (Sandbox Code Playgroud)
第三,dump2def.exe
在*.dump
文件上运行以生成*.def
文件。源代码dump2def.exe
如下。
dynamic.def: static.lib dynamic.dump
dump2def.exe dynamic.dump dynamic.def
Run Code Online (Sandbox Code Playgroud)
四、构建DLL:
LD = link.exe
LDFLAGS = /OPT:REF /MACHINE:X64
LDLIBS = kernel32.lib
dynamic.dll: $(LIB_OBJS) dynamic.def
$(LD) $(LDFLAGS) /DLL /DEF:dynamic.def /IGNORE:4102 $(LIB_OBJS) $(LDLIBS) /out:$@
Run Code Online (Sandbox Code Playgroud)
/IGNORE:4102
用于避免此警告。在这种情况下预计:
dynamic.def : warning LNK4102: export of deleting destructor 'public: virtual v
oid * __ptr64 __cdecl std::exception::`scalar deleting destructor'(unsigned int)
__ptr64'; image may not run correctly
Run Code Online (Sandbox Code Playgroud)
当dynamic.dll
配方被调用时,它也会创建一个dynamic.lib
导入文件和dynamic.exp
文件:
> cls && nmake /f test.nmake dynamic.dll
...
Creating library dynamic.lib and object dynamic.exp
Run Code Online (Sandbox Code Playgroud)
和:
C:\Users\Test\testdll>dir *.lib *.dll *.def *.exp
Volume in drive C is Windows
Volume Serial Number is CC36-23BE
Directory of C:\Users\Test\testdll
01/06/2019 08:33 PM 71,501,578 static.lib
01/06/2019 08:33 PM 11,532,052 dynamic.lib
Directory of C:\Users\Test\testdll
01/06/2019 08:35 PM 5,143,552 dynamic.dll
Directory of C:\Users\Test\testdll
01/06/2019 08:33 PM 1,923,070 dynamic.def
Directory of C:\Users\Test\testdll
01/06/2019 08:35 PM 6,937,789 dynamic.exp
5 File(s) 97,038,041 bytes
0 Dir(s) 139,871,186,944 bytes free
Run Code Online (Sandbox Code Playgroud)
在这里将它粘合在一起就是 Nmake 生成文件的样子。它是真正的 Nmake 文件的一部分:
all: test.exe
test.exe: pch.pch static.lib $(TEST_OBJS)
$(LD) $(LDFLAGS) $(TEST_OBJS) static.lib $(LDLIBS) /out:$@
static.lib: $(LIB_OBJS)
$(AR) $(ARFLAGS) $(LIB_OBJS) /out:$@
dynamic.map:
$(LD) $(LDFLAGS) /DLL /MAP /MAPINFO:EXPORTS $(LIB_OBJS) $(LDLIBS) /out:dynamic.dll
dynamic.dump:
dumpbin.exe /LINKERMEMBER static.lib /OUT:dynamic.dump
dynamic.def: static.lib dynamic.dump
dump2def.exe dynamic.dump
dynamic.dll: $(LIB_OBJS) dynamic.def
$(LD) $(LDFLAGS) /DLL /DEF:dynamic.def /IGNORE:4102 $(LIB_OBJS) $(LDLIBS) /out:$@
clean:
$(RM) /F /Q pch.pch $(LIB_OBJS) pch.obj static.lib $(TEST_OBJS) test.exe *.pdb
Run Code Online (Sandbox Code Playgroud)
这是源代码dump2def.exe
:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <set>
typedef std::set<std::string> SymbolMap;
void PrintHelpAndExit(int code)
{
std::cout << "dump2def - create a module definitions file from a dumpbin file" << std::endl;
std::cout << " Written and placed in public domain by Jeffrey Walton" << std::endl;
std::cout << std::endl;
std::cout << "Usage: " << std::endl;
std::cout << " dump2def <infile>" << std::endl;
std::cout << " - Create a def file from <infile> and write it to a file with" << std::endl;
std::cout << " the same name as <infile> but using the .def extension" << std::endl;
std::cout << " dump2def <infile> <outfile>" << std::endl;
std::cout << " - Create a def file from <infile> and write it to <outfile>" << std::endl;
std::exit(code);
}
int main(int argc, char* argv[])
{
// ******************** Handle Options ******************** //
// Convenience item
std::vector<std::string> opts;
for (size_t i=0; i<argc; ++i)
opts.push_back(argv[i]);
// Look for help
std::string opt = opts.size() < 3 ? "" : opts[1].substr(0,2);
if (opt == "/h" || opt == "-h" || opt == "/?" || opt == "-?")
PrintHelpAndExit(0);
// Add <outfile> as needed
if (opts.size() == 2)
{
std::string outfile = opts[1];
std::string::size_type pos = outfile.length() < 5 ? std::string::npos : outfile.length() - 5;
if (pos == std::string::npos || outfile.substr(pos) != ".dump")
PrintHelpAndExit(1);
outfile.replace(pos, 5, ".def");
opts.push_back(outfile);
}
// Check or exit
if (opts.size() != 3)
PrintHelpAndExit(1);
// ******************** Read MAP file ******************** //
SymbolMap symbols;
try
{
std::ifstream infile(opts[1].c_str());
std::string::size_type pos;
std::string line;
// Find start of the symbol table
while (std::getline(infile, line))
{
pos = line.find("public symbols");
if (pos == std::string::npos) { continue; }
// Eat the whitespace after the table heading
infile >> std::ws;
break;
}
while (std::getline(infile, line))
{
// End of table
if (line.empty()) { break; }
std::istringstream iss(line);
std::string address, symbol;
iss >> address >> symbol;
symbols.insert(symbol);
}
}
catch (const std::exception& ex)
{
std::cerr << "Unexpected exception:" << std::endl;
std::cerr << ex.what() << std::endl;
std::cerr << std::endl;
PrintHelpAndExit(1);
}
// ******************** Write DEF file ******************** //
try
{
std::ofstream outfile(opts[2].c_str());
// Library name, cryptopp.dll
std::string name = opts[2];
std::string::size_type pos = name.find_last_of(".");
if (pos != std::string::npos)
name.erase(pos);
outfile << "LIBRARY " << name << std::endl;
outfile << "DESCRIPTION \"Crypto++ Library\"" << std::endl;
outfile << "EXPORTS" << std::endl;
outfile << std::endl;
outfile << "\t;; " << symbols.size() << " symbols" << std::endl;
// Symbols from our object files
SymbolMap::const_iterator it = symbols.begin();
for ( ; it != symbols.end(); ++it)
outfile << "\t" << *it << std::endl;
}
catch (const std::exception& ex)
{
std::cerr << "Unexpected exception:" << std::endl;
std::cerr << ex.what() << std::endl;
std::cerr << std::endl;
PrintHelpAndExit(1);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我编写了一个小程序来解析.lib文件中"dumpbin/linkermember"的输出.我有超过8,000个函数引用从一个DLL导出.
在DLL上执行此操作的问题是,您必须链接DLL而不导出定义一次以创建.lib文件,然后生成.def,这意味着您现在必须使用.def文件再次重新链接DLL将引用导出.
使用静态库更容易.将所有源代码编译为静态库,运行dumbin,使用您的小程序生成.def,然后将libs链接到DLL中,因为导出名称可用.
不幸的是,我的公司不允许我向您展示来源.所涉及的工作是识别def文件中不需要转储输出中的"公共符号".你必须扔掉很多这些引用,NULL_IMPORT_DESCRIPTOR,NULL_THUNK_DATA,__ imp*等.
感谢@Maks 的详细回答。
下面是我在 Pre-Link 事件中使用的从 obj 生成 def 文件的示例。我希望这对某人有帮助。
dumpbin /SYMBOLS $(Platform)\$(Configuration)\mdb.obj | findstr /R "().*External.*mdb_.*" > $(Platform)\$(Configuration)\mdb_symbols
(echo EXPORTS & for /F "usebackq tokens=2 delims==|" %%E in (`type $(Platform)\$(Configuration)\mdb_symbols`) do @echo %%E) > $(Platform)\$(Configuration)\lmdb.def
Run Code Online (Sandbox Code Playgroud)
基本上我只是采用了对象之一(mdb.obj)并 grep mdb_* 函数。然后解析输出以仅保留名称,同时考虑到缩进的空格数量(一个在拆分为标记后,另一个在回显中。但我不知道这是否重要)。
不过,现实世界的脚本可能会更复杂。
归档时间: |
|
查看次数: |
55569 次 |
最近记录: |