Pregunta

He estado leyendo muchos tutoriales / artículos sobre DLL no administradas en C ++. Para mi vida, sin embargo, parece que no puedo entender el concepto. Me confunde fácilmente el aparente desacuerdo sobre si necesita un archivo de encabezado, cómo exportarlo, si necesito un archivo .lib y qué tienes.

Entonces, asumamos que tengo una función como esta:

public int calculateSquare(int num)
{
    return num*num;
}

Ignorando el código real, ¿qué necesito para convertir esta simple función, en sí misma, en una DLL a la que luego puedo llamar? ¿Acabo de agregar __dllexport o lo que sea a la primera línea o necesito un encabezado? Estoy perplejo por todo esto.

¿Fue útil?

Solución

No puedo enfatizar esto lo suficiente, el compilador de C ++ no ve los archivos de encabezado, después de que el preprocesador está listo, solo hay un gran archivo fuente (también llamado unidad de compilación). Por lo tanto, estrictamente no necesita un encabezado para exportar esta función desde un dll. Lo que sí necesita es algún tipo de compilación condicional para exportar la función en la dll que está compilando e importarla en el código del cliente.

Normalmente, esto se hace con una combinación de macros y archivos de encabezado. Crea una macro llamada MYIMPORTEXPORT y, mediante el uso de declaraciones condicionales de macro, hace que funcione como __declspec (dllexport) en el dll y __declspec (dllimport) en el código del cliente.

en el archivo MYIMPORTEXPORT.h

#ifdef SOME_CONDITION
#define MYIMPORTEXPORT __declspec( dllexport )
#else
#define MYIMPORTEXPORT __declspec( dllimport )
#endif

en el archivo MyHeader.h

#include <MyImportExport.h>

MYIMPORTEXPORT public int calculateSquare(int num)
{
    return num*num;
}

en el archivo dc .cpp

#define SOME_CONDITION

#include <MyHeader.h>

en el archivo de código de cliente .cpp

#include <MyHeader.h>

Por supuesto, también debe indicar al vinculador que está construyendo una dll con / DLL option .

El proceso de compilación también creará un archivo .lib, esta es una biblioteca estática, llamada stub en este caso, que el código del cliente debe vincular como si estuviera vinculando a una biblioteca estática real. Automáticamente, el dll se cargará cuando se ejecute el código del cliente. Por supuesto, el sistema operativo debe encontrar la dll a través de su mecanismo de búsqueda, lo que significa que no puede colocar la dll en cualquier lugar, sino en una ubicación específica. Aquí es más sobre eso.

Una herramienta muy útil para ver si exportó la función correcta de la dll y si el código del cliente se está importando correctamente es dumpbin . Ejecútelo con / EXPORTS y / IMPORTS respectivamente.

Otros consejos

La respuesta de QBziZ es lo suficientemente correcta. Consulte DLL no administrados en C ++

Para completarlo: En C ++, si necesita usar un símbolo, debe decirle al compilador que existe y, a menudo, su prototipo .

En otros idiomas, el compilador simplemente explorará la biblioteca por su cuenta y encontrará el símbolo, et voil & # 224; .

En C ++, debe decirle al compilador.

Ver un encabezado C / C ++ como una tabla de contenido del libro

La mejor manera es poner en algún lugar común el código necesario. La " interfaz " ;, si lo desea. Esto generalmente se hace en un archivo de encabezado, llamado encabezado porque generalmente no es un archivo fuente independiente. El encabezado es solo un archivo cuyo objetivo es incluir (es decir, copiar / pegar por el preprocesador) en los archivos de origen verdadero.

En esencia, parece que tiene que declarar dos veces un símbolo (función, clase, lo que sea). Lo cual es casi una herejía en comparación con otros idiomas.

Debería verlo como un libro, con una tabla de resumen o un índice. En la tabla, tienes todos los capítulos. En el texto, tiene los capítulos y su contenido.

Y a veces, estás contento de tener la lista de capítulos.

En C ++, este es el encabezado.

¿Qué pasa con la DLL?

Entonces, volviendo al problema de la DLL: el objetivo de una DLL es exportar los símbolos que usará su código.

Entonces, de manera C ++, ambos deben exportar el código en la compilación (es decir, en Windows, usar el __declspec, por ejemplo) y "publicar". una tabla de lo que se exporta (es decir, tiene " encabezados públicos " que contienen las declaraciones exportadas).

Lista de verificación para exportar funciones:

  • ¿La convención de llamada es adecuada para la persona que llama? (esto determina cómo se pasan los parámetros y resultados, y quién es responsable de limpiar la pila). Debe indicar su convención de llamadas explícitamente.
  • ¿Bajo qué nombre se exportará el símbolo? C ++ generalmente necesita decorar (" mangle ") los nombres de los símbolos, por ejemplo. para distinguir entre diferentes sobrecargas.
  • Indique al vinculador que haga visible la función como Exportación de DLL

En MSVC:

  • __stdcall (que es la convención de llamadas de pascal) es la convención de llamadas típica para los símbolos exportados, admitida por la mayoría de los clientes, supongo.
  • externo '' C '' le permite exportar el símbolo de estilo C sin cambiar el nombre
  • use __declspec (dllexport) para marcar un símbolo a exportar, o vincule un archivo .def separado donde se enumeran los símbolos a exportar. Con un archivo .def también puede exportar solo por ordinales (no por nombre) y cambiar el nombre del símbolo que se exporta.

Debe exportar la función utilizando __declspec (dllexport) o agregando la función a un archivo de definición de módulo (.def). Luego compile el proyecto como una DLL.

En el lado del cliente, tiene dos opciones. Utilice una biblioteca de importación (.lib) que se genera al compilar la DLL. Simplemente enlazando con su proyecto de cliente con esta biblioteca le dará acceso a las funciones exportadas desde la DLL. Y necesita un archivo de encabezado, porque el compilador necesita saber la firma de su función, que devuelve int y toma un int. Para recapitular, debe vincular con una biblioteca de importación (.lib) y un archivo de encabezado que contiene el encabezado de su función.

Otra forma es cargar la DLL dinámicamente usando WinAPI llamar a LoadLibrary y luego GetProcAddress para obtener un puntero a la función. El puntero para funcionar debe tener el tipo correcto, de modo que el compilador pueda darle los parámetros correctos y se use la convención de llamada correcta.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top