使用 boost::signals2 和卸载 DLL 时访问冲突

Posted

技术标签:

【中文标题】使用 boost::signals2 和卸载 DLL 时访问冲突【英文标题】:Access violation when using boost::signals2 and unloading DLLs 【发布时间】:2015-02-24 18:26:13 【问题描述】:

我正在尝试解决这个问题。

假设,您有一个使用boost::signals2 在对象之间进行通信的代码。让我们称它们为“色标”。这些色阶的代码通常与使用它们的代码位于同一个 DLL 中。我们就叫它main.dll

但有时来自其他 DLL 的代码需要使用这些对象,这就是问题的根源。

基本上,应用程序非常大,大多数 DLL 被加载以完成一些工作,然后被卸载。对于包含色阶代码的 DLL,情况并非如此,它在应用程序正常运行时必须卸载。

因此,当其中一个 DLL 被加载(我们称之为tools.dll)并且一些代码运行时,它可能想要使用这些色阶对象并与它们通信,所以我连接到这些对象提供的信号。

问题是boost 非常懒惰而且很聪明,当你使用disconnect() 插槽时,它实际上并没有删除connection 和与之相关的东西(比如boost::bind 对象等) .它只是设置了一个标志,表明此connection 现在已断开连接并稍后将其清理(实际上,当您连接新插槽时,它会清理其中的 2 个对象,而当您从版本 1.57 开始调用信号时,它会清理其中的 1 个)。您可能已经看到了它的发展方向。

因此,当您不需要更多工具时,您可以断开这些信号,然后应用程序卸载 tools.dll

然后在稍后阶段,一些代码从main.dll 执行,这会导致调用色标信号之一。 boost::signals2 去调用它,但在它试图清理一个断开连接的插槽之前。这是发生访问冲突的地方,因为内部连接有一个 shared_state 对象或类似的东西,它试图以线程安全的方式清理自己。但它面临的问题是,它试图调用的代码已经不存在,因为 DLL 被卸载,所以抛出了 Access Violation 异常。

我尝试通过在卸载 DLL 之前使用一些虚拟参数调用信号以及连接然后断开更多插槽来解决此问题(这是一个愚蠢的想法,因为它不能解决问题,而只是将其相乘) 一些预定义的次数(比插槽数量多 2 或 3 倍)。

它起作用了,或者我是这么认为的,因为现在它不会立即崩溃,而是在您下次加载相同的 tools.dll 时崩溃。我仍然需要弄清楚它在哪里以及为什么会崩溃,但它在 boost 内部的其他地方。

所以,我想问一下,我有哪些修复方法? 我的想法是

以更简单的方式实现我自己的连接 提供一种更简单的通信方式,例如回调等 为boost 如此懒惰和聪明寻找解决方法。

【问题讨论】:

你有没有想过不通过 dll 传递 boost 对象?重新发明地狱(子弹头)是一个坏主意。找到解决方法将帮助您了解更多,但可能需要无限时间。我会选择更简单的方法。 嗯,这就是问题的重点。如果没有解决方法,它就无法工作。问题是:我应该选择哪种解决方法 【参考方案1】:

嗯,看来修复后我已经找到崩溃的原因了。

所以,基本上,当您使用上述解决方法(多次调用带有虚拟参数的信号)时会发生什么,它的作用是替换从boost 代码从main.dll 创建的_shared_state 对象由另一个 _shared_state 对象创建,该对象是从 boost 代码从 tools.dll 创建的。该对象在内部维护指向引用计数器(类型派生自boost::detail::sp_counter_base)的指针。

然后tools.dll 卸载并且对象仍然存在,但它的虚拟表指向不再存在的代码。让我们看一下引用计数器的虚拟表以了解发生了什么。

    [0] 0x000007fed8a42fe5 tools.dll!boost::detail::sp_counted_impl_p<...>::`vector deleting destructor'(unsigned int)
    [1] 0x000007fed8a4181b tools.dll!boost::detail::sp_counted_impl_p<...>::dispose(void)
    [2] 0x000007fed8a4458e tools.dll!boost::detail::sp_counted_base::destroy(void)
    [3] 0x000007fed8a43c42 SegyTools.dll!boost::detail::sp_counted_impl_p<...>::get_deleter(class type_info const &)
    [4] 0x000007fed8a42da6 tools.dll!boost::detail::sp_counted_impl_p<...>::get_untyped_deleter(void)

如您所见,所有这些方法都与引用计数器的处理有关,因此在您第二次尝试执行相同的技巧之前不会出现问题。因此,断开所有信号以尝试删除来自tools.dll 的所有代码的技巧并没有按预期工作,下次您尝试执行该技巧时,Access Violation 会发生。

【讨论】:

很高兴您正在取得进展。您找到解决方法了吗? 我认为如果我将执行此“技巧”以擦除断开连接的插槽的代码移动到main.dll,由于上述原因,它实际上会起作用。 或者我可以使用其他语言工具进行通信(例如项目中也使用的 Qt),因为main.dll 之外的代码不需要完整范围的信号信息。跨度>

以上是关于使用 boost::signals2 和卸载 DLL 时访问冲突的主要内容,如果未能解决你的问题,请参考以下文章

使用 boost::signals2 的编译时间非常慢

从 boost::signals2 安全断开

boost--signal

如何比较 boost::signals2 中的插槽类型

boost::signals2::signal 输出错误?

在地图中存储 boost::signals2 信号?