Pergunta

Criou DLL C++ básica e exportou nomes usando o arquivo de definição de módulo (MyDLL.def).Após a compilação, verifico os nomes das funções exportadas usando dumpbin.exeEspero ver:

SomeFunction

mas vejo isso em vez disso:

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

Por que?

A função exportada parece sem decoração (especialmente em comparação com o não uso do arquivo Module Def), mas o que há com as outras coisas?

Se eu usar dumpbin.exe contra uma DLL de qualquer aplicativo comercial, você obtém a limpeza:

SomeFunction

e nada mais...

Também tentei remover a Definição do Módulo e exportar os nomes usando o estilo de exportação "C", a saber:

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

(Simplesmente usar "extern "C" não criou uma função exportada)

No entanto, isso ainda cria a mesma saída, a saber:

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

Eu também tentei o #define dllexport __declspec(dllexport) opção e criei uma LIB sem problemas.No entanto, não quero fornecer um arquivo LIB para pessoas que usam a DLL em seu aplicativo C#.

É uma DLL C++ simples (código não gerenciado), compilada com C++ nada além de um cabeçalho e código simples.Sem o Módulo Def, recebo funções exportadas mutiladas (posso criar uma biblioteca estática e usar o LIB sem problemas.Estou tentando evitar isso).Se eu usar extern "C" __declspec(dllexport) OU uma definição de módulo, recebo o que parece ser um nome de função não decorado ...o único problema é que ele é seguido por um "=" e pelo que parece ser uma versão decorada da função.Quero me livrar das coisas depois do "=" - ou pelo menos entender por que elas estão lá.

Do jeito que está, tenho certeza de que posso chamar a função de C# usando um P/Invoke...Eu só quero evitar aquele lixo no final do "=".

Estou aberto a sugestões sobre como alterar as configurações do projeto/compilador, mas usei apenas o modelo DLL padrão do Visual Studio - nada de especial.

Foi útil?

Solução

Você pode obter o que deseja desligar a geração de informações de depuração. Projeto + Propriedades, Linker, Debugging, Gere Informações de depuração = Não.

Naturalmente, você só quer fazer isso para a versão de lançamento. Onde a opção já está definida dessa maneira.

Outras dicas

Em vez de usar o arquivo .def apenas inserir pragma comment assim

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

Editar: ou até mais fácil: dentro do corpo da função, use

#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)

. . . Se você tiver problemas para encontrar o nome da função decorada. Este último Pragma pode ser reduzido ainda mais com uma simples definição de macro.

Você tem que declarar as funções como extern "C" Se você não deseja que os nomes deles sejam mutilados.

Por experiência, tenha cuidado se você usar __stdcall na sua assinatura de função. Com __stdcall, o nome permanecerá mutilado até certo ponto (você descobrirá com rapidez suficiente). Aparentemente, existem dois níveis de alcance, um extern "C" lida com o nível de C ++, mas não lida com outro nível de nome de nome causado por __stdcall. O magrando extra é aparentemente relevante para a sobrecarga - mas não tenho certeza disso.

Desculpe por responder a um tópico antigo, mas o que foi marcado como a resposta não funcionou para mim.

Como várias pessoas apontaram, a decoração externa "C" é importante. Alterar a configuração "Project / Properties / Linker / Debugging / Generate Info Info" não fez absolutamente nenhuma diferença para os nomes mutilados que estão sendo gerados para mim no modo de depuração ou versão de liberação.

Configuração: VS2005 Compilando um projeto de biblioteca de classe C ++ visual. Eu estava verificando a saída .dll compilada com a ferramenta Walker de dependência da Microsoft.

Aqui está uma receita de exemplo que funcionou para mim ...

Em Project.h:

#define DllExport extern "C" __declspec( dllexport )

DllExport bool API_Init();
DllExport bool API_Shutdown();

Em Project.cpp:

#include "project.h"

bool API_Init()
{
  return true;
}

bool API_Shutdown()
{
  return true;
}

Em seguida, sendo chamado do código gerenciado C#, classe.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();
    }
}

Fazer o exposto impediu os nomes mutilados no modo de depuração e liberação, independentemente da configuração de informações de depuração. Boa sorte.

Mesmo sem a confusão, o nome das compilações de 32 e 64 bits é exportado de maneira diferente, mesmo com "C" externo.Confira com DEPENDS.EXE.

Isso pode significar um GRANDE problema para qualquer cliente que faça um LoadLibrary+GetProcAdress para acessar sua função.

Portanto, além de todos os outros, use um arquivo de definição de módulo da seguinte forma:

LIBRARY MYDLL
EXPORTS
myFunction=myFunction

Sim, é um pouco difícil de manter, mas quantas funções exportadas você escreve por dia?

Além disso, costumo alterar as macros como mostrado abaixo, já que minhas DLLs exportam funções e não classes C++ e quero que elas possam ser chamadas pela maioria dos ambientes de programação:

#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);

A última linha usada para confundir o VisualAssistX alguns anos atrás, não sei se ela é digerida corretamente agora :-)

Sei quantas vezes eu tentei forçar nomes de funções usando o código e #Pragma. E eu sempre termino com exatamente a mesma coisa, usando o arquivo de definição de módulo (*.def) no final. E aqui está o motivo:

//---------------------------------------------------------------------------------------------------
// 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.

Eu me pergunto por que ninguém fez isso, levei apenas 10 minutos para testar todos os casos.

o SomeFunction@@@23mangledstuff#@@@@ é mutilado para fornecer os tipos e a classe da função C++.As exportações simples são funções que podem ser chamadas de C, ou seja,são escritos em C ou então são declarados extern "C' em código C++.Se você deseja uma interface simples, você deve fazer com que as funções exportadas usem apenas tipos C e torná-las funções não membros no namespace global.

Basicamente, quando você usa funções no C ++, as partes de seus nomes agora incluem sua assinatura e tal semelhante, a fim de facilitar os recursos de linguagem como sobrecarga.

Se você escrever uma DLL usando __declspec (dllexport), ela também deve produzir um lib. Link para esse LIB, e você estará automaticamente vinculado e as funções registradas pelo CRT no horário de inicialização (se você se lembrava de alterar todas as suas importações para exportações). Você não precisa saber sobre o nome do nome se usar este sistema.

Caso não estivesse claro nas centenas de linhas de waffle sobre o assunto de exportações mutiladas. Aqui está o meu 2C vale :)

Depois de criar um projeto chamado Win32Project2, usando o VS 2012 e a escolha de todos os símbolos no assistente. Você deve ter 2 arquivos chamados win32Project2.cpp e win32Project2.h

Ambos farão referência a um exemplo de variável exportável e um exemplo de função exportada.

Em Win32Project2.h você terá o seguinte:

#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 desmembrar, altere as duas últimas linhas para as declarações externas "C" para:

extern "C" WIN32PROJECT2_API int nWin32Project2;
extern "C" WIN32PROJECT2_API int fnWin32Project2(void);

Em Win32Project2.cpp, você também terá as seguintes definições padrão:

// 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 desmembrar, mude estes para:

// 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;
}

Essencialmente, você deve usar o prefixo externo "C" na frente das declarações para forçar o vinculador a produzir nomes C não consecutivos.

Se você preferir usar nomes mutilados para esse pouco de ofuscação extra (caso as informações de alcance sejam úteis para alguém de alguma forma), use "DumpBin /Exports Win32Project2.dll" de uma linha de comando VC para procurar os nomes referentes de referência. Ele terá o formulário "? FNWind32Project2@[param bytes]@[outras informações]. Também existem outras ferramentas de visualização de DLL se a execução de um shell de comando VC não flutue seu barco.

Exatamente por que a MS não padrão para esta convenção é um mistério. As informações reais de alcance significa algo (como tamanho de parâmetro em bytes e muito mais) que podem ser úteis para validação e depuração, mas de outra forma são garff.

Para importar a função DLL acima para o projeto C# (neste caso, um aplicativo C# Windows básico com um formulário que contém o botão "Button1") Aqui está algum código de amostra:

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top