本机 WebRTC 在 webrtc::PeerConnectionInterface::RTCConfiguration 析构函数中崩溃

Native WebRTC crashes in webrtc::PeerConnectionInterface::RTCConfiguration destructor

我正在用 C++ 编写 WebRTC 客户端。它需要跨平台工作。我从 Windows 上的 POC 开始。我可以连接和断开 to/from 示例 peerconnection_server.exe。

根据 Microsoft“入门”教程 (https://docs.microsoft.com/en-us/winrtc/getting-started),我使用的是 WebRTC“M84”版本。

我知道这个类似的 SO post,但这不是同一个问题,所以请不要指出我: 这是 Linux 上的构造函数问题。我的问题是 Windows 上的析构函数...(显然 class 总的来说肯定有缺陷..)

言归正传,我发现我永远无法销毁 webrtc::PeerConnectionInterface::RTCConfiguration 对象。如果我这样做,则会产生一个致命错误,说明应用程序以某种非法方式写入堆。我如何或何时使用 class 没有区别——它总是在破坏时爆炸。所有需要做的测试如下:

webrtc::PeerConnectionInterface::RTCConfiguration *config
    = new webrtc::PeerConnectionInterface::RTCConfiguration();
delete config;

delete 线上 - kaboom!

我找到了使用此配置 class 的各种示例。没有人看起来有任何此类问题,并且似乎没有跳过箍来处理这个问题。通常,这些对象之一是在 PeerConnection 存在时创建的,然后只允许超出本地函数的范围。考虑到它是一个“配置”对象,它应该是相当良性的——但显然不是!

这里有什么秘诀?

查看源代码,默认构造函数和析构函数的定义非常简单。那么,是不是配置中某个成员对象的bug?

PeerConnectionInterface::RTCConfiguration::RTCConfiguration() = default;
...
PeerConnectionInterface::RTCConfiguration::~RTCConfiguration() = default;

事实证明,这不仅特定于 Windows/MSVC,而且特定于 DEBUG 模式。在发布模式下,这不会发生在我身上。显然,这是由于 link 年龄不匹配造成的。如果您使用给定的 /MDd/MT 开关等进行编译,如果您 link 到其他不同意的库或 dll,您将 运行 陷入此类问题那个细节。 (然而,值得注意的是,我还没有在我的构建中看到任何其他此类问题 - 只有这个析构函数行为不端。奇怪!)让 WebRTC 在 Windows 中构建需要各种 linkage 到 Windows SDK和系统库。至少其中一个必须与我不匹配。因此,这是解决方法 - 自动 忽略 错误!

首先,为了方便起见,设置一个#define来标识条件场景。我正在使用 Qt,所以对我来说看起来像这样:

#if defined(QT_DEBUG) && defined(Q_CC_MSVC)
#define MSVC_DEBUG
#endif

然后,包括这个 Windows api header:

#ifdef MSVC_DEBUG
#include <crtdbg.h>
#endif

我正在通过 std::unique_ptr 管理我的配置 object(在此 .cpp 中使用 STL 而不是 Qt,因为 WebRTC 当然不使用 Qt,而且使用“在罗马时……”方法):

std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration> connConfig_;

最后,当我想销毁这个 object 时,我调用了一个类似如下的函数:

// Note: in Windows debug mode this destructor produces a heap assertion
// failure, which would create an ugly popup if not for the explicit
// suppression code surrounding the reset() below
void SomeClass::destroyConnectionConfig()
{
    if( !connConfig_.get() ) return;
#ifdef MSVC_DEBUG
    // Temporarily disabled popup error messages on Windows
    auto warnCrtMode(   _CrtSetReportMode( _CRT_WARN,   _CRTDBG_MODE_FILE   ) );
    auto warnCrtFile(   _CrtSetReportFile( _CRT_WARN,   _CRTDBG_FILE_STDERR ) );
    auto errorCrtMode(  _CrtSetReportMode( _CRT_ERROR,  _CRTDBG_MODE_FILE   ) );
    auto errorCrtFile(  _CrtSetReportFile( _CRT_ERROR,  _CRTDBG_FILE_STDERR ) );
    auto assertCrtMode( _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE   ) );
    auto assertCrtFile( _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR ) );
    auto errorMode( _set_error_mode( _OUT_TO_STDERR ) );
#endif
    connConfig_.reset();
#ifdef MSVC_DEBUG
    // Restore error handling setttings
    _CrtSetReportMode( _CRT_WARN,   warnCrtMode   );
    _CrtSetReportFile( _CRT_WARN,   warnCrtFile   );
    _CrtSetReportMode( _CRT_ERROR,  errorCrtMode  );
    _CrtSetReportFile( _CRT_ERROR,  errorCrtFile  );
    _CrtSetReportMode( _CRT_ASSERT, assertCrtMode );
    _CrtSetReportFile( _CRT_ASSERT, assertCrtFile );
    _set_error_mode( errorMode );
#endif
}