从 dll 中的函数返回时堆损坏

Heap corruption when returning from function inside a dll

本文关键字:损坏 返回 函数 dll      更新时间:2023-10-16

我有一个带有如下原型的函数:

void function(std::string str);

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

function("some string value here");

从此函数返回时,出现堆损坏错误:

Windows 在程序.exe中触发了断点。

这可能是由于堆损坏,这表明 程序.exe或它已加载的任何 DLL。

这也可能是由于用户在 F12 时按下 F12 程序.exe有重点。

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

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

最有可能的是,您看到崩溃是由于在Windows中DLL有自己的私有堆

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

至于为什么小字符串不会触发错误 - 许多std::string实现将小字符串内联到结构本身中,以避免堆开销。当你的字符串足够小以适合时,不需要进行内存分配,因此它恰好看起来有效......只要对 EXE 和 DLL 使用相同的 STL 版本,并且内联的阈值永远不会更改。

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

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

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

切勿在 DLL 自己的函数之外使用 DLL 分配的内存,也切勿传递具有自己的构造函数/析构函数的值结构。

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