Exportar todos los símbolos al crear una DLL

Con VS2005, quiero crear una DLL y exportar automáticamente todos los símbolos sin agregar __declspec (dllexport) en todas partes y sin crear manualmente archivos .def. ¿Hay alguna manera de hacer esto?

Se puede hacer…

La forma en que lo hacemos aquí es usar la opción / DEF del enlazador para pasar un “archivo de definición de módulo” que contiene una lista de nuestras exportaciones. Veo por su pregunta que usted sabe acerca de estos archivos. Sin embargo, no lo hacemos a mano. La lista de exportaciones en sí misma se crea mediante el comando dumpbin / LINKERMEMBER y manipula la salida mediante un script simple al formato de un archivo de definición de módulo.

Es mucho trabajo de configuración, pero nos permite comstackr código creado sin declaraciones de dllexport para Unix en Windows.

Respuesta corta

Puede hacerlo con la ayuda de la nueva versión de CMake (cualquier versión cmake-3.3.20150721-g9cd2f-win32-x86.exe o superior).

Actualmente está en la twig de desarrollo. Más tarde, la función se agregará en la versión de lanzamiento de cmake-3.4.

Enlace al desarrollador cmake:

cmake_dev

Enlace a un artículo que describe la técnica:

Crea dlls en Windows sin declspec () usando la nueva característica CMake export all

Enlace a un proyecto de ejemplo:

cmake_windows_export_all_symbols


Respuesta larga

Precaución: toda la información a continuación está relacionada con el comstackdor de MSVC o Visual Studio.

Si usa otros comstackdores como gcc en Linux o el comstackdor MinGW gcc en Windows, no tiene errores de enlace debido a símbolos no exportados, porque el comstackdor gcc exporta todos los símbolos en una biblioteca dinámica (dll) de forma predeterminada en lugar de comstackdores MSVC o Intel. .

En Windows, debe exportar explícitamente el símbolo desde un dll.

Más información sobre esto es proporcionada por enlaces:

Exportar desde una DLL

HowTo: Exportar clases de C ++ desde una DLL

Entonces, si desea exportar todos los símbolos de dll con MSVC (comstackdor de Visual Studio), tiene dos opciones:

  • Use la palabra clave __declspec (dllexport) en la definición de la clase / función.
  • Cree un archivo de definición de módulo (.def) y use el archivo .def al comstackr el archivo DLL.

1. Use la palabra clave __declspec (dllexport) en la definición de la clase / función


1.1. Agregue las macros “__declspec (dllexport) / __declspec (dllimport)” a una clase o método que desee utilizar. Entonces, si quieres exportar todas las clases, debes agregar estas macros a todas ellas

Más información sobre esto es provisto por el enlace:

Exportar desde una DLL usando __declspec (dllexport)

Ejemplo de uso (reemplace “Proyecto” por nombre real del proyecto):

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

A continuación, agregue “PROJECTAPI” a todas las clases. Defina “USEPROJECTLIBRARY” solo si desea exportar / importar símbolos de dll. Defina “PROJECTLIBRARY_EXPORTS” para el dll.

Ejemplo de exportación de clase:

 #include "ProjectExport.h" namespace hello { class PROJECTAPI Hello {} } 

Ejemplo de exportación de funciones:

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

Precaución: no olvide incluir el archivo “ProjectExport.h”.


1.2. Exportar como funciones de C Si usa el comstackdor de C ++ para comstackr el código escrito en C, podría agregar extern “C” al frente de una función para eliminar el nombre

Más información acerca de C ++ name mangling es proporcionada por el enlace:

Nombre Decoración

Ejemplo de uso:

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

Más información sobre esto es provisto por el enlace:

Exportación de funciones de C ++ para su uso en ejecutables en lenguaje C


2. Cree un archivo de definición de módulo (.def) y use el archivo .def al comstackr el archivo DLL

Más información sobre esto es provisto por el enlace:

Exportar desde una DLL usando archivos DEF

Además, describo tres enfoques sobre cómo crear un archivo .def.


2.1. Exportar funciones C

En este caso, podría simplemente agregar declaraciones de funciones en el archivo .def a mano.

Ejemplo de uso:

 extern "C" void HelloWorld(); 

Ejemplo de archivo .def (convención de denominación __cdecl):

 EXPORTS _HelloWorld 

2.2. Exportar símbolos de la biblioteca estática

Intenté el enfoque sugerido por “user72260”.

Él dijo:

  • En primer lugar, podría crear una biblioteca estática.
  • Luego use “dumpbin / LINKERMEMBER” para exportar todos los símbolos de la biblioteca estática.
  • Analizar la salida.
  • Pon todos los resultados en un archivo .def.
  • Crea dll con el archivo .def.

Usé este enfoque, pero no es muy conveniente crear siempre dos comstackciones (una como estática y otra como biblioteca dinámica). Sin embargo, debo admitir que este enfoque realmente funciona.


2.3. Exportar símbolos de archivos .obj o con la ayuda del CMake


2.3.1. Con el uso de CMake

Aviso importante: ¡no necesita ninguna macro de exportación para una clase o función!

Aviso importante: ¡ No puede usar / GL ( Whole Program Optimization ) cuando usa este enfoque!

  • Cree un proyecto CMake basado en el archivo “CMakeLists.txt”.
  • Agregue la siguiente línea al archivo “CMakeLists.txt”: set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
  • A continuación, cree el proyecto de Visual Studio con la ayuda de “CMake (cmake-gui)”.
  • Comstack el proyecto.

Ejemplo de uso:

Carpeta raíz

CMakeLists.txt (carpeta raíz)

 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 (carpeta raíz)

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

Carpeta Foo (carpeta raíz / carpeta Foo)

CMakeLists.txt (carpeta Foo)

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

foo.h (carpeta Foo)

 void HelloWorld(); 

foo.cpp (carpeta Foo)

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

Enlace al proyecto de ejemplo nuevamente:

cmake_windows_export_all_symbols

CMake utiliza el enfoque diferente de "2.2. Exportar símbolos de la biblioteca estática".

Hace lo siguiente:

1) Cree el archivo "objects.txt" en el directorio de comstackción con la información de los archivos .obj que se usan en un dll.

2) Compile el dll, es decir, cree archivos .obj.

3) Basado en la información de archivo "objects.txt", extrae todos los símbolos del archivo .obj.

Ejemplo de uso:

 DUMPBIN /SYMBOLS example.obj > log.txt 

Más información sobre esto es provisto por el enlace:

/ SÍMBOLOS

4) Parse extraído de la información del archivo .obj.

En mi opinión, usaría convección de llamada, por ejemplo, "__cdecl / __ fastcall", campo de símbolos "SECTx / UNDEF" (la tercera columna), campo de símbolos "Externo / Estático" (la quinta columna), "??", "? " información para analizar archivos .obj.

No sé exactamente cómo CMake analiza un archivo .obj. Sin embargo, CMake es de código abierto, por lo que puede averiguar si le interesa.

Enlace al proyecto CMake:

CMake_github

5) Coloque todos los símbolos exportados en un archivo .def.

6) Enlace un dll con el uso de un archivo creado .def.

Pasos 4) -5), es decir, analizar los archivos .obj y crear un archivo .def antes de vincular y usar el archivo .def que CMake hace con la ayuda del "evento de prevínculo". Mientras se desencadena el "evento de pre-enlace", puede llamar a cualquier progtwig que desee. Entonces, en caso de "CMake use" "Pre-Link event", llame al CMake con la siguiente información sobre dónde colocar el archivo .def y dónde está el archivo "objects.txt" y con el argumento "-E __create_def". Puede verificar esta información creando el proyecto CMake Visusal Studio con "set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)" y luego revise el archivo de proyecto ".vcxproj" para dll.

Si intenta comstackr un proyecto sin "establecer (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)" o con "set (CMAKE_WINDOWS_EXPORT_ALL_SAMBOLS OFF)" obtendrá errores de enlace, debido al hecho de que los símbolos no se exportan desde un dll.

Más información sobre esto es provisto por el enlace:

Comprender los pasos de comstackción personalizados y generar eventos


2.3.2. Sin el uso de CMake

Puede crear un pequeño progtwig para analizar el archivo .obj por usted mismo sin CMake usege. Hovewer, tengo que admitir que CMake es un progtwig muy útil especialmente para el desarrollo multiplataforma.

He escrito un pequeño progtwig para analizar la salida de “dumpbin / linkermember” en el archivo .lib. Tengo más de 8,000 referencias de funciones para exportar desde una DLL.

El problema al hacerlo en un archivo DLL es que debe vincular el archivo DLL sin las definiciones exportadas una vez para crear el archivo .lib, luego generar el .def, lo que significa que ahora tiene que volver a vincular el archivo DLL con el archivo .def para realmente tener las referencias exportadas

Trabajar con bibliotecas estáticas es más fácil. Reúna todas sus fonts en libs estáticas, ejecute dumbin, genere un .def con su pequeño progtwig y luego vincule las bibliotecas en una DLL ahora que los nombres de las exportaciones están disponibles.

Lamentablemente, mi empresa no me permitirá mostrarle la fuente. El trabajo involucrado es reconocer qué “símbolos públicos” en la salida de volcado no son necesarios en su archivo def. Tienes que descartar muchas de esas referencias, NULL_IMPORT_DESCRIPTOR, NULL_THUNK_DATA, __imp *, etc.

Gracias @Maks por la respuesta detallada .

A continuación se muestra un ejemplo de lo que utilicé en el evento Pre-Link para generar un archivo def desde obj. Espero que sea útil para alguien.

 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 

Básicamente acabo de tomar uno de los objetos (mdb.obj) y grepped mdb_ * funciones. Luego analizamos la salida para mantener solo los nombres teniendo en cuenta la cantidad de espacios para la sangría (uno después de dividir en tokens y otro en echo. No sé si es cuestión de peso).

El guión del mundo real probablemente sea un poco más complejo.

No, necesitará una macro que resuelva __declspec(dllexport) cuando esté incluida en el archivo .cpp que implementa las funciones exportadas, y se resuelva a __declspec(dllimport) contrario.