【问题标题】:Debug Assertion Failed! Expression: _pFirstBlock == pHead调试断言失败!表达式:_pFirstBlock == pHead
【发布时间】:2013-09-18 21:35:32
【问题描述】:

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

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

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

接下来我会尝试什么

来自Debug assertion... _pFirstBlock == pHead

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

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

接下来我会尝试什么

Using strings in an object exported from a DLL causes runtime error:

你需要做两件事之一

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

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

Passing reference to STL vector over dll boundary

接下来我会尝试什么

Unable to pass std::wstring across DLL

解决方案

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

【问题讨论】:

  • 您的具体问题是什么?
  • 这表明你有堆损坏。什么会导致堆损坏?至少有一百万种不同的东西。如果您发布一些代码,也许有人可以提供帮助。
  • 但这显然正在发生。您是否声称自己无法编写破坏堆的代码?
  • 即使你认为它不应该发生,它显然是。所以你的代码有 99.9% 的可能有问题...
  • 刚刚在搜索引擎中输入了错误。一些有趣的热门歌曲,例如casablanca.codeplex.com/discussions/442262,“那个堆栈跟踪对我来说太熟悉了——标准字符串的布局在不同配置的运行时二进制文件之间有所不同。如果我是一个赌徒,我敢打赌你有一个混合的发布和调试二进制文件在你的执行文件夹中。”还有stackoverflow.com/questions/15524591/…。从本质上看,您的 exe 和 dll 的构建方式可能不匹配。

标签: c++ visual-studio-2012


【解决方案1】:

在这种情况下,问题在于我将 std::string 传递回了 .dll 边界。

运行时库配置

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

  • 如果将 MSVC Runtime 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&lt;std::string&gt;,因此即使在 .dll 中分配内存,它也会由 .dll(而不是应用程序端)释放。

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

【讨论】:

  • 感谢您跟进解决方案。对我帮助很大。
  • 谢谢,有这个我无法弄清楚的错误(可以忽略没有问题)。我正在链接到使用 MTd 而不是 MDd 构建的多个热插拔 DLL
  • @Melbourne 不客气!这个特别的问题在 7 年前彻底吓坏了我,以至于我在大约两周后放弃了 C++,转而使用 C#。我应该坚持下去,我现在有 3 年的 C++ 经验,希望我有更多。从那以后我遇到了一些问题,但没有一个比这更可怕。
  • 切换到 /MDd 解决了我们的调试稳定性问题。我们没有发布构建稳定性问题,但切换到 /MD 将启动时间缩短了 30 秒,这完全出乎意料。我们有多个进程,并且怀疑 30 秒是为每个进程加载静态链接的运行时库与仅加载一次运行时 DLL 相比的额外开销。
【解决方案2】:

这就是assert() 在其 expression 参数计算结果为 false 时的样子。此断言存在于 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 是共享的,因此程序中只有一个。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 这到底是什么意思?多线程/单线程,调试/发布?还是 Qt 版本号?
    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-11
      • 1970-01-01
      • 1970-01-01
      • 2013-06-06
      • 2018-03-23
      相关资源
      最近更新 更多