Como chamar uma função de uma biblioteca compartilhada?
-
22-09-2019 - |
Pergunta
Qual é a maneira mais fácil e segura de chamar uma função de uma biblioteca / DLL compartilhada? Estou principalmente interessado em fazer isso no Linux, mas seria melhor se houvesse uma maneira independente da plataforma.
Alguém poderia fornecer código de exemplo para mostrar como fazer o seguinte trabalho, onde o usuário compilou sua própria versão de foo
em uma biblioteca compartilhada?
// function prototype, implementation loaded at runtime:
std::string foo(const std::string);
int main(int argc, char** argv) {
LoadLibrary(argv[1]); // loads library implementing foo
std::cout << "Result: " << foo("test");
return 0;
}
Btw, eu sei como compilar a lib compartilhada (foo.so
), Eu só preciso conhecer uma maneira fácil de carregá -lo em tempo de execução.
Solução
NOTA: Você está passando por objetos C ++ (neste caso STL Strings) em torno das chamadas da biblioteca. Há Sem C ++ padrão Abi Nesse nível, Portanto, tente evitar passar objetos C ++ por perto ou garantir que sua biblioteca e seu programa tenham sido construídos com o mesmo compilador (idealmente o mesmo compilador na mesma máquina, para evitar surpresas sutis relacionadas à configuração.)
Não se esqueça de Declare seus métodos exportados extern "C"
dentro do seu código da biblioteca.
O acima foi dito, aqui está algum código implementando o que você disse que deseja alcançar:
typedef std::string (*foo_t)(const std::string);
foo_t foo = NULL;
...
# ifdef _WIN32
HMODULE hDLL = ::LoadLibrary(szMyLib);
if (!hDll) { /*error*/ }
foo = (foo_t)::GetProcAddress(hDLL, "foo");
# else
void *pLib = ::dlopen(szMyLib, RTLD_LAZY);
if (!pLib) { /*error*/ }
foo = (foo_t)::dlsym(pLib, "foo");
# endif
if (!foo) { /*error*/ }
...
foo("bar");
...
# ifdef _WIN32
::FreeLibrary(hDLL);
# else
::dlclose(pLib);
# endif
Você pode Resumo mais isso:
#ifdef _WIN32
#include <windows.h>
typedef HANDLE my_lib_t;
#else
#include <dlfcn.h>
typedef void* my_lib_t;
#endif
my_lib_t MyLoadLib(const char* szMyLib) {
# ifdef _WIN32
return ::LoadLibraryA(szMyLib);
# else //_WIN32
return ::dlopen(szMyLib, RTLD_LAZY);
# endif //_WIN32
}
void MyUnloadLib(my_lib_t hMyLib) {
# ifdef _WIN32
return ::FreeLibrary(hMyLib);
# else //_WIN32
return ::dlclose(hMyLib);
# endif //_WIN32
}
void* MyLoadProc(my_lib_t hMyLib, const char* szMyProc) {
# ifdef _WIN32
return ::GetProcAddress(hMyLib, szMyProc);
# else //_WIN32
return ::dlsym(hMyLib, szMyProc);
# endif //_WIN32
}
typedef std::string (*foo_t)(const std::string);
typedef int (*bar_t)(int);
my_lib_t hMyLib = NULL;
foo_t foo = NULL;
bar_t bar = NULL;
...
if (!(hMyLib = ::MyLoadLib(szMyLib)) { /*error*/ }
if (!(foo = (foo_t)::MyLoadProc(hMyLib, "foo")) { /*error*/ }
if (!(bar = (bar_t)::MyLoadProc(hMyLib, "bar")) { /*error*/ }
...
foo("bar");
bar(7);
...
::MyUnloadLib(hMyLib);
Outras dicas
Loadlibrary é uma função do Windows para carregar DLLs. Você pode verificar a existência de símbolos com GetProcaddress. No Linux/Unix, você quer Dlopen/dlsym. Para fazer isso na plataforma cruzada, você pode escrever uma função que chama qualquer um desses métodos usando o pré-processador, então, algo como:
int loadlibrary(char* library)
{
#ifdef _WIN32
/* do windows code */
#endif
#ifdef _LINUX
/* do linux code */
#endif
}
Esta é uma maneira de alcançar esse tipo de coisa. Você também pode fazer isso, incluindo um cabeçalho diferente em sua própria árvore de origem para implementações específicas de plataforma de funções. Provavelmente é uma maneira melhor. Em ambos os casos, a idéia é abstrair da API subjacente.
No Linux, você precisa usar dlsym. Veja um exemplo no final da página. Na janela: GetProcaddress.