programas - Exportación de C++ DLL: nombres decorados/destrozados
lenguaje c (10)
Creado básico C ++ DLL y exportado nombres utilizando el archivo de definición del módulo (MyDLL.def). Después de la compilación, dumpbin.exe
nombres de las funciones exportadas usando dumpbin.exe
que espero ver:
SomeFunction
pero veo esto en su lugar:
SomeFunction = SomeFunction@@@23mangledstuff#@@@@
¿Por qué?
La función exportada parece no decorada (especialmente si se compara con no usar el archivo Module Def), pero ¿qué pasa con las otras cosas?
Si uso dumpbin.exe
contra una DLL desde cualquier aplicación comercial, obtienes la limpieza:
SomeFunction
y nada más...
También traté de eliminar la definición del módulo y exportar los nombres usando el estilo de exportación "C", a saber:
extern "C" void __declspec(dllexport) SomeFunction();
(Simplemente usando "extern" C "no creó una función exportada)
Sin embargo, esto todavía crea el mismo resultado, a saber:
SomeFunction = SomeFunction@@@23mangledstuff#@@@@
También probé la #define dllexport __declspec(dllexport)
y creé un LIB sin ningún problema. Sin embargo, no quiero tener que proporcionar un archivo LIB a las personas que usan la DLL en su aplicación C #.
Es un simple DLL C ++ vainilla (código no administrado), compilado con C ++ nada más que un simple encabezado y código. Sin Module Def obtengo funciones modificadas exportadas (puedo crear una biblioteca estática y utilizar el LIB no hay problema. Estoy tratando de evitar eso). Si utilizo extern "C" __declspec(dllexport)
O una definición de módulo, obtengo lo que parece ser un nombre de función sin decorar ... el único problema es que es seguido por un "=" y lo que parece una versión decorada del función. Quiero deshacerme de las cosas después de "=" - o al menos entender por qué está allí.
Tal como están las cosas, estoy bastante seguro de que puedo llamar a la función desde C # usando una P / Invoke ... Solo quiero evitar esa basura al final de "=".
Estoy abierto a sugerencias sobre cómo cambiar la configuración del proyecto / compilador, pero acabo de utilizar la plantilla DLL de Visual Studio estándar, nada especial.
Básicamente, cuando utiliza funciones en C ++, algunas partes de sus nombres ahora incluyen su firma y demás, para facilitar las características del lenguaje, como la sobrecarga.
Si escribe una DLL usando __declspec (dllexport), entonces también debería producir una lib. Enlace a esa lib, y se le vinculará automáticamente y las funciones registradas por la CRT en el momento de la puesta en marcha (si se acordó de cambiar todas sus importaciones a las exportaciones). No necesita saber sobre el cambio de nombre si usa este sistema.
Debes declarar las funciones como extern "C"
si no quieres que se rompan sus nombres.
En caso de que no fuera claro a partir de los cientos de líneas de waffles sobre el tema de las exportaciones mutiladas. Aquí está mi valor de 2c :)
Después de crear un proyecto llamado Win32Project2 utilizando VS 2012 y eligiendo exportar todos los símbolos en el asistente. Deberías tener 2 archivos llamados Win32Project2.cpp y Win32project2.h
Ambos harán referencia a una variable exportable de ejemplo y a una función exportada de ejemplo.
En Win32Project2.h tendrá lo siguiente:
#ifdef WIN32PROJECT2_EXPORTS
#define WIN32PROJECT2_API __declspec(dllexport)
#else
#define WIN32PROJECT2_API __declspec(dllimport)
#endif
extern WIN32PROJECT2_API int nWin32Project2;
WIN32PROJECT2_API int fnWin32Project2(void);
Para desmantelar, CAMBIE las últimas dos líneas para externar las declaraciones "C" a:
extern "C" WIN32PROJECT2_API int nWin32Project2;
extern "C" WIN32PROJECT2_API int fnWin32Project2(void);
En Win32Project2.cpp también tendrá las siguientes definiciones predeterminadas:
// This is an example of an exported variable
WIN32PROJECT2_API int nWin32Project2=0;
// This is an example of an exported function.
WIN32PROJECT2_API int fnWin32Project2(void)
{
return 42;
}
Para desconectar, CAMBIE ESTOS A:
// This is an example of an exported variable
extern "C" WIN32PROJECT2_API int nWin32Project2=0;
// This is an example of an exported function.
extern "C" WIN32PROJECT2_API int fnWin32Project2(void)
{
return 42;
}
Básicamente, debe usar el prefijo externo "C" delante de las declaraciones para forzar al enlazador a producir nombres similares a C no modificados.
Si prefiere usar nombres mutilados para ese poco de ofuscación adicional (en caso de que la información de creación de trama sea útil para alguien de alguna manera) use "dumpbin / exports Win32Project2.dll" desde una línea de comando de VC para buscar los nombres de referencia reales. Tendrá la forma "? FnWind32Project2 @ [param bytes] @ [otra información]. También hay otras herramientas de visualización de DLL alrededor si ejecutar un shell de comandos de VC no hace flotar su bote.
Exactamente por qué MS no se queda por defecto con esta convención es un misterio. La información real sobre la creación de mallas significa algo (como el tamaño de los parámetros en bytes y más) que podría ser útil para la validación y la depuración, pero que de otro modo sería un guff.
Para importar la función DLL arriba en el proyecto C # (en este caso una aplicación básica de Windows C # con un formulario que contiene el botón "button1") aquí hay un código de muestra:
using System.Runtime.InteropServices;
namespace AudioRecApp
{
public partial class Form1 : Form
{
[ DllImport("c://Projects/test/Debug/Win32Projects2.dll")]
public static extern int fnWin32Project2();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int value;
value = fnWin32Project2();
}
}
}
En lugar de usar el archivo .def simplemente inserta el pragma comment
como este
#pragma comment(linker, "/EXPORT:SomeFunction=_SomeFunction@@@23mangledstuff#@@@@")
Editar: O incluso más fácil: dentro del cuerpo de la función use
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
. . . si tiene problemas para encontrar el nombre de la función decorada. Este último pragma se puede reducir aún más con una simple definición de macro.
Incluso sin el cambio, el nombre de construcciones de 32 bits y de 64 bits se exporta de forma diferente, incluso con la "C" externa. Compruébalo con DEPENDS.EXE.
Esto puede significar un GRAN problema para cualquier cliente que haga LoadLibrary + GetProcAdress para acceder a su función.
Entonces, además de todo, los demás usan un Archivo de Definición de Módulo de la siguiente manera:
LIBRARY MYDLL
EXPORTS
myFunction=myFunction
Sí, es un poco difícil de mantener, pero ¿cuántas funciones exportadas escribes al día?
Además, generalmente cambio las macros como se muestra a continuación, ya que mis DLL exportan funciones no clases de C ++ y quiero que puedan ser activadas por la mayoría de los entornos de programación:
#ifdef WTS_EXPORTS
#define WTS_API(ReturnType) extern "C" __declspec(dllexport) ReturnType WINAPI
#else
#define WTS_API(ReturnType) extern "C" __declspec(dllimport) ReturnType WINAPI
#endif
WTS_API(int) fnWTS(void);
La última línea utilizada para confundir a VisualAssistX hace un par de años, no sé si ahora la digiere correctamente :-)
Perdón por responder a un hilo antiguo, pero lo que se ha marcado como la respuesta no funcionó para mí.
Como lo han señalado varias personas, la decoración externa "C" es importante. Cambiar la configuración de "Proyecto / Propiedades / Enlazador / Depuración / Generar información de depuración" no modificó por completo los nombres destrozados que se generan para mí en el modo de compilación Debug o Release.
Configuración: VS2005 que compila un proyecto de Biblioteca de clases de Visual C ++. Estaba comprobando la salida .dll compilada con la herramienta Dependency Walker de Microsoft.
Aquí hay una receta de ejemplo que funcionó para mí ...
En project.h:
#define DllExport extern "C" __declspec( dllexport )
DllExport bool API_Init();
DllExport bool API_Shutdown();
En project.cpp:
#include "project.h"
bool API_Init()
{
return true;
}
bool API_Shutdown()
{
return true;
}
Luego ser llamado desde el código administrado C #, class.cs:
using System.Runtime.Interopservices;
namespace Foo
{
public class Project
{
[DllImport("project.dll")]
public static extern bool API_Init();
[DllImport("project.dll")]
public static extern bool API_Shutdown();
}
}
Hacer lo anterior evitó los nombres destrozados en los modos Depurar y Liberar, independientemente de la configuración Generar información de depuración. Buena suerte.
Por experiencia, tenga cuidado si usa __stdcall
en la firma de su función. Con __stdcall
, el nombre permanecerá mutilado hasta cierto punto (lo averiguarás lo suficientemente rápido). Aparentemente, hay dos niveles de manipulación, uno que el extern "C"
trata en el nivel de C ++, pero no trata con otro nivel de __stdcall
de nombres causado por __stdcall
. El cambio adicional es aparentemente relevante para la sobrecarga, pero no estoy seguro de eso.
Puede obtener lo que desea desactivando la generación de información de depuración. Project + Properties, Linker, Debugging, Generate Debug Info = No.
Naturalmente, solo desea hacer esto para la compilación Release. Donde la opción ya está establecida de esa manera.
Sé cuántas veces he intentado forzar nombres de funciones usando código y # pragma. Y siempre termino exactamente con lo mismo, usando el archivo de definición de módulo (* .def) al final. Y esta es la razón:
//---------------------------------------------------------------------------------------------------
// Test cases built using VC2010 - Win32 - Debug / Release << doesn''t matter
//---------------------------------------------------------------------------------------------------
// SET: Project > Properties > Linker > Debugging > Generate Debug Info = Yes (/DEBUG)
// || (or, also doesn''t matter)
// SET: Project > Properties > Linker > Debugging > Generate Debug Info = No + delete PDB file!
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback
extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> _SetCallback@4
__declspec(dllexport) void SetCallback(LPCALLBACK function);
> ?SetCallback@@YAXP6AXHPADPAX@Z@Z
__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> ?SetCallback@@YGXP6GXHPADPAX@Z@Z
//---------------------------------------------------------------------------------------------------
// this also big is nonsense cause as soon you change your calling convention or add / remove
// extern "C" code won''t link anymore.
// doesn''t work on other cases
#pragma comment(linker, "/EXPORT:SetCallback")
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
// doesn''t work on other cases
#pragma comment(linker, "/EXPORT:SetCallback=SetCallback")
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
// doesn''t work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=_SetCallback@4")
extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
// doesn''t work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=?SetCallback@@YAXP6AXHPADPAX@Z@Z")
__declspec(dllexport) void SetCallback(LPCALLBACK function);
// doesn''t work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=?SetCallback@@YGXP6GXHPADPAX@Z@Z")
__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
//---------------------------------------------------------------------------------------------------
// So far only repetable case is using Module-Definition File (*.def) in all possible cases:
EXPORTS
SetCallback
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback
extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> SetCallback
__declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback
__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> SetCallback
// And by far this is most acceptable as it will reproduce exactly same exported function name
// using most common compilers. Header is dictating calling convention so not much trouble for
// other sw/ppl trying to build Interop or similar.
Me pregunto por qué nadie hizo esto, me llevó solo 10 minutos probar todos los casos.
SomeFunction @@@ 23mangledstuff # @@@@ está destrozado para dar los tipos y clases de la función C ++. Las exportaciones simples son funciones que se pueden llamar desde C, es decir, se escriben en C o se declaran externas "C" en el código C ++. Si desea una interfaz simple, debe hacer que las funciones que exporta sean solo C y conviértalas funciones no miembro en el espacio de nombres global.