33

我正在调用一个静态链接的 .dll,我看到了这个错误:

在此处输入图像描述

我编写了 .dll 和调用代码。不应发生此错误。我想知道是否有人以前遇到过它?.dll 仅包含大约 10 行代码,它只是一个测试 .dll,以了解 dll 的一般工作方式。当我将 std::string 从 .dll 中传回时,它会爆炸。

我正在使用 Visual Studio 2012 和 C++。

接下来我会尝试什么

调试断言... _pFirstBlock == pHead

如果在多线程模块中使用单线程库,则可能会出现此问题。

明天,我将尝试在多线程模式下重新编译 Boost 静态库(我的 .dll 设置为多线程静态模式)。

接下来我会尝试什么

请参阅在从 DLL 导出的对象中使用字符串会导致运行时错误

你需要做两件事之一

  1. 使 DLL 和使用它的客户端都链接到 CRT 的DLL 版本(例如,不是静态的)。
  2. 或者您需要确保不会跨 DLL 边界传递动态分配的内存(例如包含在字符串对象中)。换句话说,不要有返回字符串对象的 DLL 导出函数。

这似乎与正在发生的事情相匹配,它在我将字符串传回 .dll 边界的精确点处爆炸。仅当所有内容都以静态模式链接时才会出现此问题。现在可以解决了。

请参阅通过 dll 边界传递对 STL 向量的引用

接下来我会尝试什么

请参阅无法通过 DLL 传递 std::wstring

解决方案

我有一个很好的解决方案,请参阅下面的答案。

4

4 回答 4

41

在这种情况下,问题在于我正在通过std::string.dll 边界传递一个回传。

运行时库配置

  • 如果 MSVCRuntime library设置为Multi-threaded Debug DLL (/MDd),那么这没问题(它工作正常)。

  • 如果 MSVCRuntime library设置为Multi-threaded Debug (/MTd),则会抛出此错误,可以通过以下说明修复。

在内存管理器 A 中分配的内存并在内存管理器 B 中释放...

问题是在 .dll 端分配内存,然后在应用程序端释放相同的内存。这意味着内存管理器 A 正在分配内存,而内存管理器 B 正在释放相同的内存,这会产生错误。

解决方案是确保所有传回的内存都没有在 DLL 中分配。换句话说,内存总是在应用程序端分配,在应用程序端释放。

当然,DLL 可以在内部分配/释放内存 - 但它不能分配稍后由应用程序释放的内存。

例子

这将不起作用

// Memory is allocated on the .dll side, and freed on the app side, which throws error.
DLL std::string GetString(); 

这将起作用:

// Memory is allocated/freed on the application side, and never allocated in the .dll.
DLL int GetString(std::string& text); 

然而,这还不够。

在应用程序端,必须预先分配字符串:

std::string text("");
text.reserve(1024);     // Reserves 1024 bytes in the string "text".

在 .dll 端,必须将文本复制到原始缓冲区中(而不是用在 .dll 端分配的内存覆盖):

text.assign("hello");

有时,C++ 无论如何都会坚持分配内存。仔细检查预分配是否仍然与原来相同:

if (text.capacity < 1024)
{
   cout << "Memory was allocated on the .dll side. This will eventually throw an error.";
}

另一种可行的方法是使用std::shared_ptr<std::string>,因此即使在 .dll 中分配了内存,它也会由 .dll (而不是应用程序端)释放。

还有一种方法是接受一个char *和一个长度,它表示预分配的内存量。如果我们要传回的文本长于预分配内存的长度,则返回错误。

于 2013-09-19T21:07:54.390 回答
3

这就是当它的表达式参数计算为假时assert()的样子。此断言存在于 C 运行时库的调试版本中,旨在检查分配问题。您的情况下的 free() 函数。Debug 构建添加了额外的检查,以确保您正确编写代码。并在检测到问题时告诉您。就像在已经释放的分配上调用 free() 一样,简单的情况。或者调用 free() 传递错误的指针值,更棘手的情况。或者当堆被早期代码破坏时调用 free() ,这是更困难的情况。

这只是他们所能接受的,他们实际上并不知道为什么你的代码出错了。例如,他们无法在损坏堆的代码上放置一个大红色箭头。Debug + Windows + Call Stack 调试器窗口涵盖了简单的情况,它会将您带到程序中调用 free() 的代码。或者std::operator delete对于 C++ 程序。更难的情况确实非常非常难,堆损坏通常是一个Heisenbug。使断言可重复,以便您可以在报告的地址上设置数据断点是核心策略。为简单的案例交叉手指,祝你好运!


编辑后:是的,像 std::string 这样的 C++ 类存在跨模块问题肯定是它可以解决的问题之一。不是Heisenbug,很好的问题。两个基本问题:

  • 每个模块可能都有自己的 CRT 副本,由一个 CRT 副本分配的对象不能由另一个 CRT 副本释放。他们每个人都有自己分配的堆。在 VS2012 中解决了一个问题,CRT 现在从进程全局堆中分配。
  • 模块可能不会使用相同的 std::string 实现。对象布局不匹配。使用不同的 C++ 库版本编译模块很容易诱发,尤其是 C++11 更改的问题。或者不同的构建设置,_HAS_ITERATOR_DEBUGGING 宏是非常臭名昭著的。

解决该问题的唯一方法是确保使用完全相同的编译器版本使用完全相同的构建设置构建程序中的所有模块。使用 /MD 是强制性的,它确保 CRT 是共享的,因此程序中只有一个。

于 2013-09-18T22:18:10.177 回答
2

可能的原因:绑定到错误版本的 Qt DLL,尤其是在将项目从 VS2010 移动到 VS2012 时。

这是由于标准库的不同版本和相关的动态分配问题。

于 2014-10-15T08:33:25.103 回答
1

重新安装 Windows 后,我遇到了同样的问题。我的运行时库构建是多线程调试 DLL ( /MDd)。

我的解决方案是删除*.userVisual Studio 项目的文件,删除调试文件夹并重建项目。

于 2016-10-12T09:24:55.037 回答