Yuw*_* Xu 28 c++ vst visual-c++
我正在尝试使用 SDK 中包含的 VST 托管实用程序来加载插件。代码如图:
#include "vst/v3/Vst3CommonIncludes.h"
int main()
{
std::string vst3_module_path = R"(C:\Program Files\Common Files\VST3\Kontakt.vst3)";
std::string error;
std::shared_ptr<Module> vst_module = Module::create(vst3_module_path, error);
std::vector<ClassInfo> class_infos = vst_module->getFactory().classInfos();;
assert(error.empty());
assert(class_infos.size());
ClassInfo plugin_info = class_infos[0]; //Crash
//... load the plugin and do more things
return 0;
}
Run Code Online (Sandbox Code Playgroud)
其中仅包含来自和
8 的Vst3CommonIncludes.h所有 VST SDK 标头pluginterfaces/vstpublic.sdk/source/vst
就我而言,SDK 包含源文件和 cmake 文件,可将它们构建到静态库中。所以我的代码和SDK代码共享同一个编译器。
我的调查显示PluginFactory::classInfos()返回了损坏的数据,尝试从它们中分配原因,std::bad_alloc因为 的大小std::string无效。
VST SDK中PluginFactory::classInfos()的定义:
PluginFactory::ClassInfos PluginFactory::classInfos () const noexcept
{
auto count = classCount ();
Optional<FactoryInfo> factoryInfo;
ClassInfos classes;
classes.reserve (count);
auto f3 = Steinberg::FUnknownPtr<Steinberg::IPluginFactory3> (factory);
auto f2 = Steinberg::FUnknownPtr<Steinberg::IPluginFactory2> (factory);
Steinberg::PClassInfo ci;
Steinberg::PClassInfo2 ci2;
Steinberg::PClassInfoW ci3;
for (uint32_t i = 0; i < count; ++i)
{
if (f3 && f3->getClassInfoUnicode (i, &ci3) == Steinberg::kResultTrue)
//------------Unexpected behaviour here--------------------
classes.emplace_back (ci3); //--
//---------------------------------------------------------
else if (f2 && f2->getClassInfo2 (i, &ci2) == Steinberg::kResultTrue)
classes.emplace_back (ci2);
else if (factory->getClassInfo (i, &ci) == Steinberg::kResultTrue)
classes.emplace_back (ci);
auto& classInfo = classes.back ();
if (classInfo.vendor ().empty ())
{
if (!factoryInfo)
factoryInfo = Optional<FactoryInfo> (info ());
classInfo.get ().vendor = factoryInfo->vendor ();
}
}
return classes;
}
Run Code Online (Sandbox Code Playgroud)
在就地构造新的 ClassInfo 元素后,ClassInfo::data::category其他std::string成员(名称、供应商等)将<NULL>在调试器中读取。
进入 的构造函数std::string,我发现this构造期间的指针data.category不等于&data.category,并且偏移了 4 个字节。
&data.category = 0x 0000 009b 546f ed14
this (std::string constructor scope) = 0x 0000 009b 546f ed18
//Actual address varies but the offset remains the same
Run Code Online (Sandbox Code Playgroud)
因此,字符串对象被损坏,随后导致程序崩溃。
另外,在尝试ClassInfo及其字符串成员时,我遇到了这个:
ClassInfo ci;
ci.get().category = "testCategory"; //OK
const_cast<string&>(ci.category()) = "testCategory"; //Crash, Access violation at 0xFFFFFFFFFFFFFFFF
Run Code Online (Sandbox Code Playgroud)
我认为这与问题高度相关,但我无法给出解释。
我还添加了
#if __cplusplus != 201703L
#error
#endif
Run Code Online (Sandbox Code Playgroud)
到每个相关文件,所以我确信它们共享相同的STL实现,问题仍然会发生。
我希望重新创建一个不包含 VST SDK 的最小场景,并用我自己的场景在某些方面MimicClassInfo类似于原始结构ClassInfo。问题不会发生。
MSVC 14.37.32822 ,使用 C++17 标准。VST SDK 3.7.8 版本 34 (2023-05-15)
我不太确定以下是否是您遇到问题的原因,但我相信也值得检查一下。谁知道。
几年前我发现的问题是 VS 中成员函数指针的大小根据上下文而不同:
因此,在 x86 汇编编译器中,使用无效的偏移量来读取/写入使用成员函数指针的类中的数据成员的数据。因此,有时读取例如std::string field1_;编译器使用偏移量 8,有时使用 32。这样我的字段已在地址 8 处初始化并从地址 32 读取(这是另一个字段的数据)。要弄清楚这一点,就必须阅读 x86 汇编。
以下是重现该问题的代码的最小示例。在vs2022上仍然有效。
/// @file 2.cpp
class X ;
size_t foo() noexcept { return sizeof(void (X::*)()); }
Run Code Online (Sandbox Code Playgroud)
/// @file 1.cpp
// cl 1.cpp 2.cpp
class X {};
size_t foo() noexcept;
size_t bar() noexcept { return sizeof(void (X::*)()); }
#include <iostream>
int main() {
std::cout << foo() << "\n";
std::cout << bar() << "\n";
}
Run Code Online (Sandbox Code Playgroud)
如果编译并运行这个应用程序,您将得到 24 和 8。
它如何影响您的应用程序。假设您有struct Xandstruct Y封装了指向 的成员函数的指针struct X。
/// @file X.h
#pragma once
struct X{};
Run Code Online (Sandbox Code Playgroud)
/// @file Y.h
#pragma once
#include <string>
struct X;
struct Y {
void (X:: * field1_)();
std::string field2_;
};
Run Code Online (Sandbox Code Playgroud)
你有两个翻译单元 main.cpp 和 2.cpp
/// @file 2.cpp
#include "X.h"
#include "Y.h"
#include <iostream>
void foo(Y&y) noexcept {
std::cout << y.field2_ << "\n";
}
Run Code Online (Sandbox Code Playgroud)
/// @file main.cpp
#include "Y.h"
void foo(Y& y);
int main()
{
auto y = Y{};
y.field2_ = "Hello world";
foo(y);
}
Run Code Online (Sandbox Code Playgroud)
现在如果你编译并运行这个应用程序,它就会崩溃。