16

从共享库/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),我只需要知道一种在运行时加载它的简单方法。

4

3 回答 3

27

注意:您在库调用周围传递 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);
于 2010-03-29T13:33:54.690 回答
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 中抽象出来。

于 2010-03-29T13:27:18.593 回答
0

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

于 2010-03-29T13:24:22.957 回答