Exportação de funções de uma DLL com dllexport
-
22-08-2019 - |
Pergunta
Eu gostaria de um exemplo simples de exportar uma função de um Windows DLL C ++.
Eu gostaria de ver o cabeçalho, o arquivo cpp eo arquivo def (se for absolutamente necessário).
Eu gostaria que o nome exportado para ser undecorated . Eu gostaria de usar a convenção mais de chamada padrão (__stdcall?). Eu gostaria o uso __ declspec (dllexport) e não ter que usar um arquivo DEF.
Por exemplo:
//header
extern "C"
{
__declspec(dllexport) int __stdcall foo(long bar);
}
//cpp
int __stdcall foo(long bar)
{
return 0;
}
Eu estou tentando evitar os sublinhados e / ou números de vinculador adicionado (contagens de bytes?) Para o nome.
Eu estou bem com não apoiar dllimport e dllexport usando o mesmo cabeçalho. Eu não quero qualquer informação sobre a exportação de métodos de classe C ++, apenas c-estilo funções globais.
Atualização
Não incluindo a convenção de chamada (e usando extern "C") dá-me os nomes de exportação, como eu gosto, mas o que isso significa? É tudo o padrão convenção de chamada Eu estou conseguindo o que PInvoke (NET), declare (VB6) e GetProcAddress esperaria? (Eu acho que para GetProcAddress dependeria o ponteiro de função o chamador criado).
Eu quero que este DLL para ser usado sem um arquivo de cabeçalho, então eu realmente não precisa a um monte de # define fantasia para fazer a utilizável cabeçalho por um chamador.
Estou OK com uma resposta é que eu tenho que usar um arquivo DEF.
Solução
Se você quiser exportações C lisas, use um projeto C não C ++. DLLs C ++ contar com nome-desconfiguração para todos os ismos do C ++ (namespaces etc ...). Você pode compilar o código como C, indo para as configurações do projeto em C / C ++ -> Avançado, não é uma opção "Compile Como", que cooresponds para o compilador muda / TP e / TC
.Como exportar / importar DLL liberais no VC ++
O que você realmente quer fazer é definir uma macro condicional em um cabeçalho que será incluída em todos os arquivos de origem no seu projeto DLL:
#ifdef LIBRARY_EXPORTS
# define LIBRARY_API __declspec(dllexport)
#else
# define LIBRARY_API __declspec(dllimport)
#endif
Então, em uma função que você quer ser exportados você usa LIBRARY_API
:
LIBRARY_API int GetCoolInteger();
Em seu projeto de construção da biblioteca criar uma definição LIBRARY_EXPORTS
isso fará com que as suas funções a serem exportadas para a sua construção DLL.
Desde LIBRARY_EXPORTS
não será definido em um projeto consumindo a DLL, quando o projeto inclui o arquivo de cabeçalho da sua biblioteca de todas as funções serão importados vez.
Se a sua biblioteca é para ser multi-plataforma pode definir LIBRARY_API como nada quando não estiver no Windows:
#ifdef _WIN32
# ifdef LIBRARY_EXPORTS
# define LIBRARY_API __declspec(dllexport)
# else
# define LIBRARY_API __declspec(dllimport)
# endif
#elif
# define LIBRARY_API
#endif
Ao usar dllexport / dllimport você não precisa de arquivos DEF uso, se você usar arquivos DEF você não precisa usar dllexport / dllimport. Os dois métodos de realizar a mesma tarefa maneiras diferentes, acredito que dllexport / dllimport é o método recomendado para fora dos dois.
exportar as funções de um unmangled ++ DLL C durante LoadLibrary / PInvoke
Se você precisa disso para usar LoadLibrary e GetProcAddress, ou talvez fazendo PInvoke do .net você pode usar extern "C"
em linha com o seu dllexport. E já que estamos usando GetProcAddress em vez de dllimport não precisamos fazer a dança ifdef de cima, apenas uma dllexport simples:
O Código:
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
EXTERN_DLL_EXPORT int getEngineVersion() {
return 1;
}
EXTERN_DLL_EXPORT void registerPlugin(Kernel &K) {
K.getGraphicsServer().addGraphicsDriver(
auto_ptr<GraphicsServer::GraphicsDriver>(new OpenGLGraphicsDriver())
);
}
E aqui está o que as exportações parecido com DUMPBIN / exportações:
Dump of file opengl_plugin.dll
File Type: DLL
Section contains the following exports for opengl_plugin.dll
00000000 characteristics
49866068 time date stamp Sun Feb 01 19:54:32 2009
0.00 version
1 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
1 0 0001110E getEngineVersion = @ILT+265(_getEngineVersion)
2 1 00011028 registerPlugin = @ILT+35(_registerPlugin)
Assim, este código funciona bem:
m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");
m_pfnGetEngineVersion = reinterpret_cast<fnGetEngineVersion *>(
::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
::GetProcAddress(m_hDLL, "registerPlugin")
);
Outras dicas
Para C ++:
Eu só enfrentou o mesmo problema e eu acho que vale a pena mencionar um problema surge quando se usar tanto __stdcall
(ou WINAPI
) e extern "C"
:
Como você sabe extern "C"
remove a decoração de modo que em vez de:
__declspec(dllexport) int Test(void) --> dumpbin : ?Test@@YaHXZ
-lo a obter um nome de símbolo undecorated:
extern "C" __declspec(dllexport) int Test(void) --> dumpbin : Test
No entanto, o _stdcall
(= WINAPI macro, que altera a convenção de chamada) também decora nomes para que se usarmos tanto obtemos:
extern "C" __declspec(dllexport) int WINAPI Test(void) --> dumpbin : _Test@0
eo benefício da extern "C"
está perdido, porque o símbolo está decorado (com _ @bytes)
Note que esta única ocorre para arquitetura x86, porque a convenção
__stdcall
é ignorado em x64 ( MSDN : em x64 arquiteturas, por convenção, os argumentos são passados ??nos registradores quando possível, e os argumentos subsequentes são passados ??na pilha .).
Isto é particularmente complicado se você tiver como alvo ambas as plataformas x86 e x64.
Duas soluções
-
Use um arquivo de definição. Mas Isso força você a manter o estado do arquivo def.
-
a maneira mais simples: definir o macro (ver MSDN ):
comentário #define EXPORTAÇÃO (linker, "/ EXPORT:" __FUNCTION__ "=" __FUNCDNAME __)
e, em seguida, incluir o seguinte pragma no corpo da função:
#pragma EXPORT
Exemplo completa:
int WINAPI Test(void)
{
#pragma EXPORT
return 1;
}
Isto irá exportar a função undecorated para ambos os alvos x86 e x64, preservando a convenção __stdcall
para x86. O __declspec(dllexport)
não é necessária neste caso.
Eu tinha exatamente o mesmo problema, a minha solução foi usar arquivo de definição de módulo (.DEF) em vez de __declspec(dllexport)
para definir exportações ( http://msdn.microsoft.com/en-us/library/d91k01sh.aspx ). Eu não tenho idéia por que isso funciona, mas ele faz
Eu acho _naked pode obter o que deseja, mas também impede o compilador de gerar o código de gerenciamento de pilha para a função. extern "C" faz com que nome de estilo C decoração. Retire isso e que deve se livrar de seus _ de. O vinculador não adicionar os sublinhados, o compilador faz. stdcall faz com que o tamanho da pilha argumento para ser anexado.
Para saber mais, consulte: http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx
A grande questão é por que você quer fazer isso? O que há de errado com os nomes mutilados?