2010-03-29 49 views
13

从共享库/ DLL调用函数最简单和最安全的方法是什么?我最感兴趣的是在Linux上这样做,但如果有一种平台无关的方式会更好。如何从共享库调用函数?

有人可以提供示例代码来演示如何进行以下工作,其中用户已将自己的foo版本编译到共享库中?

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

顺便说一句,我知道如何编译共享库(foo.so),我只需要知道一个简单的方法在运行时加载它。

回答

25

注意:您正在库调用周围传递C++对象(在本例中为STL字符串)。有没有标准的C++ ABI在这个级别,所以要么尝试避免传递C++对象,要么确保你的库和你的程序都是用同一个编译器构建的(理想情况是同一台机器上的同一个编译器,为了避免任何微妙的配置相关的惊喜)

不要忘了声明您的库代码中的导出方法extern "C"

上面已经说,这里是一些代码实现,你说你想达到什么

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 

您可以摘要进一步

#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

如果您提及的头文件包含在Unix/Linux上...... – 2010-03-29 13:36:04

+0

在第二个代码块中完成。 – vladr 2010-03-29 13:55:47

+0

C++中的函数名称改变如何,不会使事情复杂化?另外,你拼错这里的窗口'#include ' – sbk 2010-03-29 13:59:45

0

在Linux上,您需要使用dlsym。查看页面末尾的示例。 在窗口:GetProcAddress

1

LoadLibrary是一个用于加载DLL的Windows函数。您可以使用GetProcAddress检查符号是否存在。在Linux/Unix上,您需要dlopen/dlsym。要在跨平台做到这一点,你可以编写调用或者使用预处理器这些方法的功能,所以,这样的:

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

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

#endif 
} 

这是实现这样的事情的一种方式。你也可以通过在你自己的源码树中包含一个不同的头文件来实现函数的特定平台实现。这可能是更好的方法。无论哪种情况,这个想法都是从底层API中抽象出来的。

相关问题