2010-03-29 23 views
13

¿Cuál es la forma más fácil y segura de invocar una función desde una biblioteca/dll compartida? Estoy más que interesado en hacer esto en Linux, pero sería mejor si hubiera una plataforma independiente.¿Cómo llamar a una función desde una biblioteca compartida?

¿Podría alguien proporcionar un código de ejemplo para mostrar cómo hacer el siguiente trabajo, donde el usuario ha compilado su propia versión de foo en una biblioteca compartida?

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

Por cierto, sé cómo compilar la librería compartida (foo.so), sólo necesito saber una manera fácil de cargar en tiempo de ejecución.

Respuesta

25

NOTA: Está pasando objetos C++ (en este caso, cadenas STL) en las llamadas a la biblioteca. No hay estándar C++ ABI en este nivel, así que trate de evitar pasar objetos C++, o asegúrese de que su biblioteca y su programa se hayan compilado con el mismo compilador (idealmente el mismo compilador en la misma máquina, para evitar cualquier sutil sorpresas relacionadas con la configuración.)

No olvide declare sus métodos exportados extern "C" dentro del código de su biblioteca.

Lo anterior Dicho esto, aquí es algún código implementación de lo que ha dicho que quiere lograr:

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 

Puede el extracto esta más:

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

Si usted ha mencionado el cual encabezado para incluir en Unix/Linux ... –

+0

Hecho en el segundo bloque de código. – vladr

+0

¿Qué hay del nombre de la función que se transforma en C++? ¿No complicaría eso las cosas? Además, las ventanas mal escritas aquí '#include ' – sbk

0

En Linux necesita usar dlsym. Vea un ejemplo al final de la página. En ventana: GetProcAddress.

1

LoadLibrary es una función de Windows para cargar archivos DLL. Puede verificar la existencia del símbolo con GetProcAddress. En Linux/Unix, quiere dlopen/dlsym. Para hacer esto en varias plataformas, se podría escribir una función que llama a cualquiera de estos métodos que utilizan pre-procesador, así, algo así como:

int loadlibrary(char* library) 
{ 
#ifdef _WIN32 
    /* do windows code */ 

#endif 
#ifdef _LINUX 
    /* do linux code */ 

#endif 
} 

Ésta es una forma de lograr este tipo de cosas. También podría hacerlo incluyendo un encabezado diferente en su propio árbol fuente para implementaciones de funciones de plataformas específicas. Esta es probablemente una mejor manera. En cualquier caso, la idea es abstraerse de la API subyacente.

Cuestiones relacionadas