C ++包装器DLL到静态LIB

时间:2011-08-30 17:37:38

标签: c++ windows visual-studio

我有一些静态编译的库(.lib),我在我的项目中使用它,它是用C ++编写的,并在Windows和Linux上构建。在我项目的这些库的入口点,我只使用静态库套件中“main”库中的一个或两个函数(但我确信这些函数会调用套件中其他库中的许多其他函数)

理想情况下,我希望有一套动态链接库(DLL)包装静态lib套件中的每个lib;我已经读过/听说过在Windows上执行此操作的方法(例如,Visual Studio 2005/2008/2010)是“创建一个包装器DLL”,其中一些公开的函数调用底层的静态库函数。如果有人可以在MS Visual Studio 2005/2008/2010中详细介绍如何进行一些片段,我将非常感谢。我相信你们中的一些人可能已经在日常工作中这样做了;非常感谢您的体验。

编辑:

为了我这样的其他人的利益,我发布了我发现的第一个“有用”链接: http://tom-shelton.net/index.php/2008/12/11/creating-a-managed-wrapper-for-a-lib-file/

2 个答案:

答案 0 :(得分:6)

“将库转换为另一种库类型”似乎很容易,但事实并非如此。没有直接的逐步方法来执行此操作,因为C ++和DLL根本不能很好地协同工作,并且您的代码需要适应以支持DLL接口。

描述问题的简明方法是:

  • .lib的界面是C ++
  • .dll的界面是C

因此,DLL的界面根本不支持C ++,你需要聪明才能使它工作 - 这就是现有模糊答案的原因。

一种标准方法是通过COM,这意味着为库构建一个完整的COM包装器,包括类工厂,接口,对象,并使用BSTR而不是std::string。我猜这是不切实际的。

另一个解决方案是为您的C ++库创建一个DLL安全的C接口。这意味着基本上创建一个winapi风格的界面,这可能是不切实际或根本没有使用你的库的目的。这就是@David Heffernan建议的。但他没有解决的问题是如何将代码更改为与DLL兼容。

一个重要但微妙的问题是你不能跨越DLL边界传递任何模板化的C ++对象。这意味着将std::string传入或传出DLL函数被认为是不安全的。每个二进制文件都有自己的std::string代码副本,并且无法保证它们相互之间可以很好地协作。每个二进制文件(可能)也获得它自己的CRT副本,你将通过操纵另一个模块的对象来搞乱一个模块的内部状态。

修改:您可以使用__declspec(dllexport)在MSVC中导出C ++对象,然后使用__declspec(dllimport)导入它们。但是这个和细微之处有很多限制会导致问题。基本上这是让编译器为导出的类或函数创建廉价C风格接口的快捷方式。问题是它没有警告你发生了多少不安全的事情。重申:

  1. 如果有任何模板化符号越过DLL边界,则不安全(例如std::*)。
  2. 任何具有CRT管理状态的对象都不应跨越DLL边界(例如FILE*)。

答案 1 :(得分:1)

对于tenfour的回复,这个评论有点大......

如果您在使用DLL包装器时仍想维护C ++ API,则可以在头文件中将C ++转换为C转换函数。这可确保只有C兼容的数据类型才能跨越DLL边界。

作为一个例子

//MyDLL.h

class MyDLL {
 public:
  ...
  int Add2ToValues(std::vector<int>& someValues) {
   int* cValues = new int[someValues.size()];
   memcpy(cValues, &someValues[0], someValues.size() * sizeof(int));
   int retVal = Add2ToValues_Internal(cValues, someValues.size());
   someValues.assign(std::begin(cValues), std::end(cValues));
   delete [] cValues;
   return retVal;
  }

private:
  int Add2ToValues_Internal(int* valuesOut, const int numValues);
};

//MyDLL.cpp

 int MyDLL::Add2ToValues_Internal(int* values, const int numValues)
 {
   for(int i = 0; i < numValues; ++i) {
     values[i] += 2;
   }

   return 0;
 }

我在执行这些包装时遇到的一个问题是您必须在头文件中分配和释放任何内存。由于头文件将由使用您的库的应用程序编译,因此它将使用CRT用于构建应用程序的任何编译器。与DLL的所有交互都使用C,因此您不会遇到任何运行时不匹配,并且所有内存都完全在DLL内或完全在应用程序内分配和释放,因此您也没有任何交叉DLL内存管理问题。在示例中,我在标头中分配和取消分配。如果需要在_Internal函数中分配数据,则还需要添加一个允许您在DLL中释放该内存的函数。一旦进入_Internal函数,就可以随意使用尽可能多的C ++。