c++ - ucrtbased - simbolos visual studio
Exportar todos los símbolos al crear una DLL (5)
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?
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 rama de desarrollo. Más tarde, la función se agregará en la versión de lanzamiento de cmake-3.4.
Enlace al desarrollador cmake:
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 compilador de MSVC o Visual Studio.
Si usa otros compiladores como gcc en Linux o el compilador MinGW gcc en Windows, no tiene errores de enlace debido a símbolos no exportados, porque el compilador gcc exporta todos los símbolos en una biblioteca dinámica (dll) de forma predeterminada en lugar de compiladores 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:
HowTo: Exportar clases de C ++ desde una DLL
Entonces, si desea exportar todos los símbolos de dll con MSVC (compilador 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 compilar 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 compilador de C ++ para compilar 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:
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 compilar 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 compilaciones (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)".
- Compila 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 <iostream>
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 compilació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:
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:
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 programa 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 compilar 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 compilación personalizados y generar eventos
2.3.2. Sin el uso de CMake
Puede crear un pequeño programa para analizar el archivo .obj por usted mismo sin CMake usege. Hovewer, tengo que admitir que CMake es un programa muy útil especialmente para el desarrollo multiplataforma.
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.
He escrito un pequeño programa 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 fuentes en libs estáticas, ejecute dumbin, genere un .def con su pequeño programa 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.
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.
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 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 compilar código creado sin declaraciones de dllexport para Unix en Windows.