Me gustaría un ejemplo simple de cómo exportar una función desde una DLL de Windows C ++.
Me gustaría ver el encabezado, el .cpp
archivo y el .def
archivo (si es absolutamente necesario).
Me gustaría que el nombre exportado no estuviera decorado . Me gustaría usar la convención de llamadas más estándar ( __stdcall
?). Me gustaría el uso __declspec(dllexport)
y no tengo que usar un .def
archivo.
Por ejemplo:
//header
extern "C"
{
__declspec(dllexport) int __stdcall foo(long bar);
}
//cpp
int __stdcall foo(long bar)
{
return 0;
}
Estoy tratando de evitar que el enlazador agregue guiones bajos y / o números (¿recuentos de bytes?) Al nombre.
Estoy de acuerdo con no admitir dllimport
y dllexport
usar el mismo encabezado. No quiero ninguna información sobre la exportación de métodos de clase C ++, solo funciones globales de estilo C.
ACTUALIZAR
Sin incluir la convención de llamada (y el uso extern "C"
) me da los nombres de exportación que me gustan, pero ¿qué significa eso? ¿Es cualquier convención de llamada predeterminada que obtengo lo que pinvoke (.NET), declare (vb6) y GetProcAddress
esperaría? (Supongo GetProcAddress
que dependería del puntero de función que creó la persona que llama).
Quiero que esta DLL se use sin un archivo de encabezado, por lo que realmente no necesito tanta fantasía #defines
para hacer que el encabezado pueda ser utilizado por una persona que llama.
Estoy de acuerdo con una respuesta que es que tengo que usar un *.def
archivo.
fuente
extern C
eliminará la decoración que describe los tipos de parámetros de la función, pero no la decoración que describe la convención de llamada de la función; b) para eliminar toda la decoración, debe especificar el nombre (sin decoración) en un archivo DEF.Respuestas:
Si desea exportaciones simples de C, use un proyecto de C, no C ++. Las DLL de C ++ se basan en la manipulación de nombres para todos los ismos de C ++ (espacios de nombres, etc.). Puede compilar su código como C yendo a la configuración de su proyecto en C / C ++ -> Avanzado, hay una opción "Compilar como" que corresponde a los modificadores del compilador / TP y / TC.
Si aún desea usar C ++ para escribir los componentes internos de su lib pero exportar algunas funciones sin alterar para usarlas fuera de C ++, consulte la segunda sección a continuación.
Exportación / importación de bibliotecas DLL en VC ++
Lo que realmente quiere hacer es definir una macro condicional en un encabezado que se incluirá en todos los archivos fuente en su proyecto DLL:
Luego, en una función que desea exportar, usa
LIBRARY_API
:En el proyecto de construcción de su biblioteca, cree una definición,
LIBRARY_EXPORTS
esto hará que sus funciones se exporten para su construcción DLL.Dado
LIBRARY_EXPORTS
que no se definirá en un proyecto que consume la DLL, cuando ese proyecto incluya el archivo de encabezado de su biblioteca, todas las funciones se importarán en su lugar.Si su biblioteca va a ser multiplataforma, puede definir LIBRARY_API como nada cuando no está en Windows:
Cuando utilice dllexport / dllimport, no es necesario utilizar archivos DEF, si utiliza archivos DEF, no es necesario utilizar dllexport / dllimport. Los dos métodos realizan la misma tarea de diferentes maneras, creo que dllexport / dllimport es el método recomendado de los dos.
Exportación de funciones no manipuladas desde una DLL de C ++ para LoadLibrary / PInvoke
Si necesita esto para usar LoadLibrary y GetProcAddress, o tal vez para importar desde otro idioma (es decir, PInvoke desde .NET, o FFI en Python / R, etc.) puede usar en
extern "C"
línea con su dllexport para decirle al compilador C ++ que no altere los nombres. Y dado que estamos usando GetProcAddress en lugar de dllimport, no necesitamos hacer el baile ifdef desde arriba, solo un simple dllexport:El código:
Y así es como se ven las exportaciones con Dumpbin / export:
Entonces este código funciona bien:
fuente
Para C ++:
Acabo de enfrentar el mismo problema y creo que vale la pena mencionar que surge un problema cuando uno usa tanto
__stdcall
(oWINAPI
) comoextern "C"
:Como sabéis
extern "C"
quita la decoración para que en lugar de:obtienes un nombre de símbolo sin decorar:
Sin embargo la
_stdcall
(= macro WINAPI, que cambia la convención de llamada) también decora nombres de modo que si usamos ambos obtenemos:y
extern "C"
se pierde el beneficio de porque el símbolo está decorado (con _ @bytes)Esto es particularmente complicado si su objetivo es plataformas x86 y x64.
Dos soluciones
Utilice un archivo de definición. Pero esto te obliga a mantener el estado del archivo def.
la forma más sencilla: definir la macro (ver msdn ):
y luego incluir el siguiente pragma en el cuerpo de la función:
Ejemplo completo:
Esto exportará la función sin decorar para los destinos x86 y x64 mientras se conserva la
__stdcall
convención para x86. En este caso__declspec(dllexport)
no es necesario.fuente
__FUNCTION__
macro solo funciona en funciones.Tuve exactamente el mismo problema, mi solución fue usar el archivo de definición del módulo (.def) en lugar de
__declspec(dllexport)
definir las exportaciones ( http://msdn.microsoft.com/en-us/library/d91k01sh.aspx ). No tengo idea de por qué esto funciona, pero lo hacefuente
.def
módulo exporta archivo hace el trabajo, pero a expensas de ser capaz de suministrarextern
las definiciones en el archivo de cabecera para, por ejemplo los datos en globales cuyo caso, usted tiene que suministrar laextern
definición manualmente en internos usos de esos datos. (Sí, hay ocasiones en las que lo necesita). Es mejor, tanto en general como especialmente para el código multiplataforma, simplemente usarlo__declspec()
con una macro para que pueda distribuir los datos normalmente.__stdcall
, entonces__declspec(dllexport)
será no quitar las decoraciones..def
Sin embargo, agregar la función a un testamento.Creo que _naked podría obtener lo que desea, pero también evita que el compilador genere el código de administración de pila para la función. extern "C" provoca la decoración del nombre del estilo C. Elimine eso y eso debería deshacerse de su _. El enlazador no agrega los guiones bajos, el compilador lo hace. stdcall hace que se agregue el tamaño de la pila de argumentos.
Para obtener más información, consulte: http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx
La pregunta más importante es ¿por qué quieres hacer eso? ¿Qué pasa con los nombres destrozados?
fuente