Pregunta

Creó C ++ DLL y nombres exportados utilizando el archivo de definición del módulo (mydll.def). Después de la compilación, verifique los nombres de las funciones exportadas usando dumpbin.exeEspero ver:

SomeFunction

Pero veo esto en su lugar:

SomeFunction = SomeFunction@@@23mangledstuff#@@@@

¿Por qué?

La función exportada parece no decorada (especialmente en comparación con no usar el archivo DEF del módulo), pero ¿qué pasa con las otras cosas?

Si uso dumpbin.exe Contra una DLL de cualquier aplicación comercial, obtienes la limpieza:

SomeFunction

y nada más...

También intenté eliminar la definición del módulo y exportar los nombres utilizando el estilo de exportación "C", a saber:

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

(Simplemente usar "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é el #define dllexport __declspec(dllexport) opción y creó una lib sin problemas. Sin embargo, no quiero tener que proporcionar un archivo LIB a las personas que usan la DLL en su aplicación C#.

Es una DLL C ++ de vainilla simple (código no administrado), compilado con C ++ nada más que un encabezado y código simples. Sin el módulo Def, recibo funciones exportadas destrozadas (puedo crear una biblioteca estática y usar el LIB No hay problema. Estoy tratando de evitar eso). Si uso extern "C" __declspec(dllexport) O Una definición del módulo obtengo lo que parece ser un nombre de función no decorado ... el único problema es que es seguido por un "=" y lo que parece una versión decorada de la función. Quiero deshacerme de las cosas después del "=", o al menos entender por qué está allí.

Tal como está, estoy bastante seguro de que puedo llamar a la función de C# usando un P/Invoke ... Solo quiero evitar esa basura al final del "=".

Estoy abierto a sugerencias sobre cómo cambiar la configuración del proyecto/compilador, pero acabo de utilizar la plantilla estándar de Visual Studio DLL, nada especial.

¿Fue útil?

Solución

Puede obtener lo que desea desactivando la generación de información de depuración. Project + Propiedades, enlazador, depuración, generar información de depuración = No.

Naturalmente, solo quieres hacer esto para la construcción de lanzamiento. Donde la opción ya está configurada de esa manera.

Otros consejos

En lugar de usar el archivo .def, simplemente inserte pragma comment como esto

#pragma comment(linker, "/EXPORT:SomeFunction=_SomeFunction@@@23mangledstuff#@@@@")

Editar: o incluso más fácil: dentro del cuerpo de la función uso

#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 definición macro simple.

Tienes que declarar las funciones como extern "C" Si no quieres que se destrozaran sus nombres.

Por experiencia, tenga cuidado si usa __stdcall En su firma de función. Con __stdcall, el nombre permanecerá destrozado en cierta medida (lo descubrirá lo suficientemente rápido). Aparentemente, hay dos niveles de destrozamiento, uno el extern "C" se ocupa del nivel de C ++, pero no trata con otro nivel de nombramiento causado por __stdcall. El destrozamiento adicional es aparentemente relevante para la sobrecarga, pero no estoy seguro de eso.

Perdón por responder a un hilo viejo, pero lo que se ha marcado como la respuesta no funcionó para mí.

Como han señalado varias personas, la decoración "C" externa es importante. Cambiar la configuración "Proyecto / Propiedades / Linker / Depurging / Generar Información de depuración" no hizo absolutamente ninguna diferencia en los nombres destrozados que se generaban para mí en el modo de construcción de depuración o de lanzamiento.

Configuración: VS2005 compilando un proyecto de biblioteca de clases C ++ Visual. Estaba revisando la salida de .dll compilada con la herramienta Walker de dependencia 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 se llama 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 tanto en modo de depuración como de liberación, independientemente de la configuración de información de depuración generar. Buena suerte.

Incluso sin la gestión, el nombre de 32 bits y 64 bits se exporta de manera diferente, incluso con "C" extern. Compruébalo con dependss.exe.

Esto puede significar grandes problemas para cualquier cliente que haga un LoadLibrary+GetProcadress para acceder a su función.

Entonces, además de todos los demás, use un archivo de definición del módulo de la siguiente manera:

LIBRARY MYDLL
EXPORTS
myFunction=myFunction

Sí, es un poco doloroso 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 funciones de exportación DLLS no son clases de C ++ y quiero que sean llamables 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 solía confundir VisualSistx hace un par de años, no sé si lo digiere correctamente ahora :-)

Sé cuántas veces he intentado forzar los nombres de las funciones usando Code y #Pragma's. Y siempre termino exactamente con la misma cosa, usando el archivo de definición de módulo (*.def) al final. Y aquí está 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.

El SomeFunction @@@ 23Mangledstuff#@@@@@ está destrozado para dar los tipos y la clase de la función C ++. Las exportaciones simples son funciones que se pueden llamar desde c ie se escriben en c o de lo contrario se declaran extern "c 'en código C ++. Si desea una interfaz simple, debe hacer que las funciones que exportan se use solo t tipos y hacerlas Funciones no miembros en el espacio de nombres global.

Básicamente, cuando usa funciones en C ++, partes de sus nombres ahora incluyen su firma y similares, para facilitar las características del lenguaje como la sobrecarga.

Si escribe una DLL usando __DeclSpec (DLEXPORT), entonces también debe producir una lib. Enlace a esa lib, y se vinculará automáticamente y las funciones registradas por el CRT en la hora de inicio (si recordó cambiar todas sus importaciones a exportaciones). No necesita saber acerca de la gestión de nombres si usa este sistema.

En caso de que no estuviera claro a partir de los cientos de líneas de waffle sobre el tema de las exportaciones destrozadas. Aquí está mi valor de 2c :)

Después de crear un proyecto llamado Win32Project2 usando VS 2012 y elegir exportar todos los símbolos en el asistente. Debe tener 2 archivos llamados win32project2.cpp y win32project2.h

Ambos harán referencia a una variable exportable de ejemplo y 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);

Desenrosar el cambio de las últimas dos líneas a las declaraciones de "C" externas 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;
}

Desenrosar cambiarlos 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;
}

Esencialmente, debe usar el prefijo "C" externo frente a las declaraciones para obligar al enlazador a producir nombres similares a C sin resolver.

Si prefiere usar los nombres destrozados para ese poco de ofuscación adicional (en caso de que la información de la gestión sea útil para alguien de alguna manera) use "Dumpbin /Exports win32project2.dll" de una línea de comandos VC para buscar los nombres de referencia reales. Tendrá el formulario "? Fnwind32project2@[param bytes]@[otra información]. También hay otras herramientas de visualización de DLL si se ejecuta un shell de comando VC no flota su barco.

Exactamente por qué MS no es un misterio por defecto en esta convención. La información de gestión real 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 es GFF.

Para importar la función DLL arriba en el proyecto C# (en este caso, una aplicación básica de C# Windows con un formulario que contiene el botón "Botón1") Aquí hay algún 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();
        }
      }
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top