为什么这个程序崩溃:在DLL之间传递std :: string

时间:2010-02-23 22:01:53

标签: c++ dll memory-management visual-c++

我无法弄清楚以下崩溃的原因(MSVC9):

//// the following compiles to A.dll with release runtime linked dynamically
//A.h
class A {
  __declspec(dllexport) std::string getString();
};
//A.cpp
#include "A.h"
std::string A::getString() {
   return "I am a string.";
}

//// the following compiles to main.exe with debug runtime linked dynamically
#include "A.h"
int main() {
   A a;
   std::string s = a.getString();
   return 0;
} // crash on exit

显然(?)这是由于可执行文件和DLL的内存模型不同。可能是字符串A::getString()返回是在A.dll中分配并在main.exe中释放了吗?

如果是这样,为什么 - 以及在DLL(或可执行文件)之间传递字符串的安全方法是什么?不使用像shared_ptr这样的包装器和自定义删除器。

6 个答案:

答案 0 :(得分:50)

这实际上不是由不同的堆实现引起的 - MSVC std :: string实现不为小的字符串使用动态分配的内存(它使用小字符串优化)。 CRT确实需要匹配,但这次不是你的想法。

发生的事情是您通过违反一个定义规则来调用未定义的行为。

发布和调试版本将设置不同的预处理器标志,您会发现std::string在每种情况下都有不同的定义。询问你的编译器sizeof(std::string)是什么 - MSVC10告诉我它在调试版本中是32,在发布版本中是28(这不是填充 - 28和32都是4字节`边界)。

那发生了什么事?使用复制构造函数的调试版本初始化变量s以复制std::string的发行版本。成员变量的偏移量在版本之间是不同的,因此您可以复制垃圾。 MSVC实现有效地存储了开始和结束指针 - 你已经将垃圾复制到它们中;因为它们不再为null,析构函数会尝试释放它们,并且您会获得访问冲突。

即使堆实现是相同的,它也会崩溃,因为你将垃圾指针释放到从未首先分配的内存中。


总结:CRT版本需要匹配,但定义 - 包括标准库中的定义

答案 1 :(得分:3)

  

可能是字符串   A :: getString()返回   在A.dll中分配并释放   MAIN.EXE?

是。

  

如果是这样,为什么 - 以及什么是安全的   在DLL之间传递字符串的方法(或   可执行文件,就此而言)?没有   使用像shared_ptr这样的包装器   自定义删除。

使用shared_ptr听起来对我来说是个明智之举。请记住,根据经验,分配和解除分配应该由同一个模块完成,以避免这些故障。

在dll中导出STL对象充其量只是一个棘手的小马。我建议您先查看this MSDN知识库文章,然后this发布。

答案 2 :(得分:2)

您需要为应用程序中的每个DLL链接到相同的运行时库(DLL一个),无论是调试还是释放,其中内存在一个中分配,另一个在内存中释放。 (使用动态链接的运行时库的原因是,整个进程将有一个堆,而不是每个dll / exe链接到静态堆。)

这包括按值返回std :: string和stl-containers,就像你所做的那样。

原因有两个(更新部分)

  • 这些类具有不同的布局/大小,因此不同编译的代码假定数据位于不同的位置。无论谁先创建它都是正确的,但另一个会迟早会导致崩溃。
  • msvc堆实现在每个runtime-lib中是不同的,这意味着如果你试图释放堆中没有分配它的指针,它将会变得疯狂。 (如果布局相似,即第一种情况比你的情况要长,就会发生这种情况。)

因此,直接获取运行时库,或者停止在不同的dll中释放/分配(即停止按值传递内容)。

答案 3 :(得分:2)

除了上面所述之外,请确保两个项目中的平台工具集(在Properties-> General下)相同。否则,到达方的字符串内容可能是虚假的。

当使用v100工具集版本的控制台应用程序项目使用了设置为v90的库时,发生了这种情况。

答案 4 :(得分:1)

这可能是因为DLL和EXE是使用不同的CRT设置编译的。因此,当您传递字符串时,会发生一些资源冲突。检查DLL和可执行文件的项目设置。

答案 5 :(得分:0)

确保两个项目(App和Dll)都使用“ 多线程DLL ”运行时库之一,而不是静态版本。

属性-> C / C ++ -> 代码生成->( / MD或/ MDd

注意: 如果您在应用中使用任何第三方库,则可能还需要重新编译它们,链接器通常会通过不匹配/重复的运行时错误通知您。