8

我有一个原型函数,如下所示:

void function(std::string str);

在加载和使用该 dll 的另一个程序中的主函数中调用此函数。

function("some string value here");

从这个函数返回时,我得到堆损坏错误:

Windows 在 program.exe 中触发了一个断点。

这可能是由于堆损坏,这表明 program.exe 或其已加载的任何 DLL 中存在错误。

这也可能是由于用户在 program.exe 获得焦点时按 F12。

输出窗口可能有更多诊断信息。

玩弄我的代码,我注意到一些奇怪的观察结果:
1. 当传入的字符串长度小于 11 个字符时,我没有收到任何错误,只要我添加更多字符,就会出现错误。
2. 将参数类型从 更改为 时std::stringstd::string&错误消失。传递引用的想法来自这里
3. 我已经注释掉了函数的主体。那里的操作与产生的异常无关。
4. 将参数类型从 更改std::stringchar*也可以解决问题。
什么可能导致此错误?我该如何解决?

4

3 回答 3

9

最有可能的是,由于在 Windows 中 DLL 有自己的私有堆这一事实,您会看到崩溃。

当你编译你的函数时,编译器为std::string的析构函数生成了一些代码,以清理它的参数。此代码释放 DLL 堆上分配的内存。但是,应用程序 EXE 也会为 的构造函数生成自己的代码std::string,该构造函数在程序堆上分配代码。当您在一个堆上分配并在另一个堆上释放时,会发生未定义的行为,并且您会崩溃。

至于为什么小字符串不会触发错误 - 许多std::string实现将小字符串内联到结构本身,以避免堆开销。当您的字符串足够小以适应时,不需要进行内存分配,因此它似乎可以工作......只要您对 EXE 和 DLL 使用相同的 STL 版本,并且内联的阈值永远不会改变。

为避免此问题,不要将对象按值传递给 DLL(除非它们是POD 对象),并且不要在与创建对象不同的 DLL 或 EXE 中释放对象。避免将 STL 或 C++ 库对象传递为好吧,因为它们的实现可能在不同版本的 C++ 编译器之间有所不同。传递 POD 对象或 C 原始类型,例如const char *

于 2012-12-03T08:02:42.567 回答
7

导出 DLL 函数时,最好只接受整数数据类型,即 int 或指针(不确定 float 和 double)。

当您需要传递字符串时,将其作为 传递const char *,当您需要 DLL 函数返回字符串时,将char *指向预分配缓冲区的指针传递给 DLL,DLL 将在其中写入字符串。

永远不要在 DLL 自己的函数之外使用由 DLL 分配的内存,并且永远不要通过具有自己的构造函数/析构函数的值结构传递。

于 2012-12-03T07:59:24.027 回答
5

可能您已经与静态版本的 C 运行时链接,创建与静态版本的 C 运行时链接的 DLL 绝不是一个好主意。这可能会导致很多问题,例如在您的程序中,您的 EXE 从与其链接的静态 C 运行时的私有堆分配内存,然后在您的 DLL 中您想要删除该堆并创建一个新堆(因为您想要添加一些数据到输入字符串,它需要增加它的缓冲区),所以它会导致错误。最简单的方法是将程序的所有部分(EXE 和 DLL)与 C 运行时的 DLL 版本链接,因此它们都共享来自 MSVCRTXX.dll 的相同堆

于 2012-12-03T08:05:58.607 回答