2010-03-29 82 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中抽象出來的。