With VS2005, I want to create a DLL and automatically export all symbols without adding __declspec(dllexport) everywhere, and without hand-creating .def files. Is there a way to do this?
No, you will need a macro that resolves to __declspec(dllexport) when it's included by the .cpp file that implements the exported functions, and resolves to __declspec(dllimport) otherwise.
The way we do it here is to use the /DEF option of the linker to pass a "module definition file" containing a list of our exports. I see from your question that you know about these files. However, we do not do it by hand. The list of exports itself is created by the dumpbin /LINKERMEMBER command, and manipulating the output via a simple script to the format of a module definition file.
It is a lot of work to setup, but it allows us to compile code created without dllexport declarations for Unix on Windows.
I've written a small program to parse the output of "dumpbin /linkermember" on the .lib file. I have upwards of 8,000 function references to export from one DLL.
The problem with doing it on a DLL is that you have to link the DLL without the exported definitions once to create the .lib file, then generate the .def which means you now have to relink the DLL again with the .def file to actually have the references exported.
Working with static libraries is easier. Compile all your sources into static libs, run dumbin, generate a .def with your little program, then link the libs together into a DLL now that the export names are available.
Unfortunately my company won't allow me to show you the source. The work involved is recognizing which "public symbols" in the dump output are not needed in your def file. You have to throw away a lot of those references, NULL_IMPORT_DESCRIPTOR, NULL_THUNK_DATA, __imp*, etc.
Caution:
All information below is related to the MSVC compiler or Visual Studio.
If you use other compilers like gcc on Linux or MinGW gcc compiler on Windows you don't have linking errors due to not exported symbols, because gcc compiler export all symbols in a dynamic library (dll) by default instead of MSVC or Intel windows compilers.
In windows you have to explicitly export symbol from a dll.
So if you want to export all symbols from dll with MSVC (Visual Studio compiler) you have two options:
Use the keyword __declspec(dllexport) in the class/function's definition.
Create a module definition (.def) file and use the .def file when building the DLL.
1. Use the keyword __declspec(dllexport) in the class/function's definition
1.1. Add "__declspec(dllexport) / __declspec(dllimport)" macros to a class or method you want to use. So if you want to export all classes you should add this macros to all of them
Then add "PROJECTAPI" to all classes.
Define "USEPROJECTLIBRARY" only if you want export/import symbols from dll.
Define "PROJECTLIBRARY_EXPORTS" for the dll.
Example of class export:
#include "ProjectExport.h"
namespace hello {
class PROJECTAPI Hello {}
}
Caution: don't forget to include "ProjectExport.h" file.
1.2. Export as C functions.
If you use C++ compiler for compilation code is written on C, you could add extern "C" in front of a function to eliminate name mangling
More info about C++ name mangling is provided by link:
Further I describe three approach about how to create .def file.
2.1. Export C functions
In this case you could simple add function declarations in the .def file by hand.
Example of usage:
extern "C" void HelloWorld();
Example of .def file (__cdecl naming convention):
EXPORTS
_HelloWorld
2.2. Export symbols from static library
I tried approach suggested by "user72260".
He said:
Firstly, you could create static library.
Then use "dumpbin /LINKERMEMBER" to export all symbols from static library.
Parse the output.
Put all results in a .def file.
Create dll with the .def file.
I used this approach, but it's not very convinient to always create two builds (one as a static and the other as a dynamic library). However, I have to admit, this approach really works.
2.3. Export symbols from .obj files or with help of the CMake
2.3.1. With CMake usage
Important notice: You don't need any export macros to a classes or functions!
In my opinion I would use calling convection, for example "__cdecl/__fastcall", "SECTx/UNDEF" symbol field (the third column), "External/Static" symbol field (the fifth column), "??", "?" information for parsing an .obj files.
I don't know how exactly CMake parse an .obj file.
However, CMake is open source, so you could find out if it's interested for you.
Steps 4)-5), that is parse .obj files and create a .def file before linking and using the .def file CMake does with help of "Pre-Link event".
While "Pre-Link event" fires you could call any program you want.
So in case of "CMake usage" "Pre-Link event" call the CMake with the following information about where to put the .def file and where the "objects.txt" file and with argument "-E __create_def".
You could check this information by creating CMake Visusal Studio project with "set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)" and then check the ".vcxproj" project file for dll.
If you try to compile a project without "set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)" or with "set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)" you will get linking errors, due to the fact that symbols are not exported from a dll.
You simple could create a small program for parsing .obj file by youself without CMake usege. Hovewer, I have to admit that CMake is very usefull program especially for cross-platform development.
Below is an example of what I used in Pre-Link event to generate def file from obj. I hope it will be helpful for someone.
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
Basically I just took one of objects (mdb.obj) and grepped mdb_* functions. Then parsed output to keep just names taking into account amount of spaces for indentation (one after splitting into tokens and another in echo. I don't know if it's matter though).
Real world script probably will kind of more complex though.
I want to create a DLL and automatically export all symbols without adding __declspec(dllexport) everywhere and without hand-creating .def files. Is threre a way to do this?
This is a late answer, but it provides the details for Maks's answer in Section (2). It also avoids scripts and uses a C++ program called dump2def. The source code for dump2def is below.
Finally, the steps below assume you are working from a Visual Studio Developer Prompt, which is a Windows Terminal where vcvarsall.bat has been run. You need to ensure the build tools like cl.exe, lib.exe, link.exe and nmake.exe are on-path.
static.lib - static library archive (*.a file on Linux)
dynamic.dll - dynamic library (*.so file on Linux)
import.lib - dynamic library (import library on Windows)
Also note that though you are exporting everything from the DLL, clients still must use declspec(dllimport) on all symbols (classes, functions and data) that they use. Also see on MSDN.
First, take your objects and create a static archive:
/IGNORE:4102 is used to avoid this warning. It is expected in this case:
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
When the dynamic.dll recipe is invoked, it creates a dynamic.lib import file and dynamic.exp file, too:
Perhaps somebody finds useful my Python script for converting .dump to .def.
import sys, os
functions = []
startPoint = False
# Exclude standard API like sprintf to avoid multiple definition link error
excluded_functions = [ 'sprintf', 'snprintf', 'sscanf', 'fprintf' ]
if len(sys.argv) < 2:
print('Usage: %s <Input .dump file> <Output .def file>.' % sys.argv[0])
print('Example: %s myStaticLib.dump exports.def' % sys.argv[0])
sys.exit(1)
print('%s: Processing %s to %s' % (sys.argv[0], sys.argv[1], sys.argv[2]))
fin = open(sys.argv[1], 'r')
lines = fin.readlines()
fin.close()
# Reading
for l in lines:
l_str = l.strip()
if (startPoint == True) and (l_str == 'Summary'): # end point
break
if (startPoint == False) and ("public symbols" in l_str):
startPoint = True
continue
if (startPoint == True) and l_str is not '':
funcName = l_str.split(' ')[-1]
if funcName not in excluded_functions:
functions.append(" " + funcName)
# Writing
fout = open(sys.argv[2], 'w')
fout.write('EXPORTS\n')
for f in functions:
fout.write('%s\n' % f)
fout.close()
With this script you can get the .def file for your .lib in two steps: