Windows dll 插件加载时崩溃

crash in Windows dll plugin on load

在加载 dll 时寻找有关在 Windows 中导致此类崩溃的原因的一些建议。

我正在用 C++ 编写 Windows VST3 .dll 插件,但在加载我的 .dll 时遇到启动崩溃。当 运行 主机应用程序和插件通过 Visual Studio 2019 调试器时,我得到上面显示的 Access violation executing location ... 对话框,调用堆栈是空的,让我觉得内存已完全损坏。

这可能是由于 link我在构建 .dll 时需要调整选项造成的?

以下是一些事实:

不幸的是,将整个空白的 VST3 插件放在一起对于 Whosebug 问题中的 post 来说代码太多了。但是,如果我注释掉对第 3 方库的所有调用,我可以让插件工作,我不认为代码本身是问题,我认为 dll (.vst3) 文件的构建方式是问题。

构建 .vst3 dll 文件的 CMakeLists.txt 文件的关键部分如下所示:

FIND_LIBRARY ( SuperpoweredSDK NAMES SuperpoweredWin141_Debug_MD_x64.lib PATHS ${SUPERPOWERED_DIR}/libWindows )
SET ( TEST_EXTERNAL_DEPS PRIVATE Threads::Threads ${SuperpoweredSDK} )

FILE ( GLOB JUCE_SOURCE ${CMAKE_HOME_DIRECTORY}/JuceLibraryCode/*.cpp )
LIST ( FILTER JUCE_SOURCE EXCLUDE REGEX ".+/JuceLibraryCode/include_juce_audio_plugin_client_[^u].+" )

FILE ( GLOB PLUGIN_SOURCE *.cpp )

ADD_LIBRARY ( TestVST3 SHARED ${JUCE_SOURCE} ${PLUGIN_SOURCE} ${CMAKE_HOME_DIRECTORY}/JuceLibraryCode/include_juce_audio_plugin_client_VST3.cpp )
SET_TARGET_PROPERTIES ( TestVST3 PROPERTIES OUTPUT_NAME "Test" SUFFIX ".vst3" )
TARGET_LINK_LIBRARIES ( TestVST3 PUBLIC ${TEST_EXTERNAL_DEPS} )

what can cause a crash of this type in Windows when loading a dll

典型的原因是某些全局变量的初始值设定项。加载 DLL 时会崩溃的示例代码:

class C
{
public:
    C() { __debugbreak(); } // Compiles into `int 3` interrupt, will definitely crash
};
static C g_c;

当您 link 一个外部库但不调用它时,linker 的死代码消除功能通常会删除完整的 .obj 文件,这些文件是 link 编辑但未使用的。这也会将变量连同它们的构造函数一起放到那里。

一种调试方法,在 DllMain 上设置断点。但不是您代码中的 DllMain,而是另一个实际导出的代码。在 VC++ 2017 CRT 中,一个叫做 dllmain_dispatch,它的源文件在 VC++ 运行时,crt\src\vcruntime\dll_dllmain.cpp。 dllmain_dispatch 在调用你的 DllMain 之前调用了几个函数,其中一个 dllmain_crt_process_attach 调用了 _initterm 并且那个函数实际上调用了所有全局内容的构造函数。在你的情况下,至少有一个失败了。

P.S。像这样的崩溃的一个可能原因是,您的第三方库正在尝试使用 COM,而调用 LoadLibrary 的线程从未调用 CoInitialize()。

我们最终确实找到了这次崩溃的原因。对于 VST3 文件,DAW 加载每个 .dll(或 .vst3),查询功能,然后立即再次卸载 dll。然后它移动到下一个文件,整个过程重新开始,直到所有插件都被查询过。

在这种情况下,我使用的第 3 方库之一 (SuperpoweredSDK) 启动了一个后台线程,以根据某处的 Web 服务器验证许可证。这没有记录在案。随着后台线程的启动,DAW 不知不觉地继续卸载 DLL,使线程挂在那里 运行 不再存在的代码!因此,没有任何调用堆栈的崩溃。

几种可能的解决方案:

  • 将 SuperpoweredSDK 初始化代码移到以后真正需要的时候。
  • 在未来尚未发布的 SuperpoweredSDK 版本中(当前版本为 2.0.1),他们显然添加了一个 API 调用来关闭 Superpowered,并且该关闭调用将等到所有后台线程已完成 运行。确保将此命名为新的 API.