xoofx / NPlug

Develop VST3 audio native plugins with .NET
Other
92 stars 5 forks source link

NPlug vst3 causes crash when calling PClassInfo2 from host #7

Open cuikp opened 2 weeks ago

cuikp commented 2 weeks ago

Hi, in the meantime I've been attempting a P/Invoke approach for hosting, but when accessing an NPlug vst3 (specifically NPlug.SimpleDelay.vst3) I get an error (C++ crash) when attempting to obtain PClassInfo2. All other vst3s I've tried report their PClassInfo2 properties without crashing. Any idea why NPlug might result in crashing when trying to access this info?
Briefly, here's the series of methods I call:

In C#:

public string GetInfoList(int index)
{
   int result = 0;
   PClassInfo2 pClassInfo2;
   result = NativeMethods.GetClassInfo2(PluginFactoryPtr, index, out pClassInfo2); // PluginFactoryPtr is definitely valid
  ...

where the mirrored C# struct layout for PClassInfo2 is:

   [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 16)]
public unsafe partial struct PClassInfo2
{
    public Guid cid;
    public int cardinality;
    public fixed byte category[32];
    public fixed byte name[64];
    public uint classFlags;
    public fixed byte subCategories[128];
    public fixed byte vendor[64];
    public fixed byte version[64];
    public fixed byte sdkVersion[64];
}

the C# Native method called is:

[DllImport("Vst3Bridge", CallingConvention = CallingConvention.Cdecl)]
internal static extern int GetClassInfo2(IntPtr pluginFactory, int index, out PClassInfo2 pinfo2);

And at the C++ end :

EXPORTED_FUNCTION int GetClassInfo2(IPluginFactory2* pluginFactory, int index, PClassInfo2& classInfo2) {
//EXPORTED_FUNCTION int GetClassInfo2(IPluginFactory* pluginFactory, int index, PClassInfo& classInfo) {

    redirect();
    int result = -1;
    if (!pluginFactory) { std::cerr << "Plugin factory is null." << std::endl; return -1; }
try {
         result = pluginFactory->getClassInfo2(index, &classInfo2);  //CRASH OCCURS HERE, WITHOUT BEING CAUGHT
    //result = pluginFactory->getClassInfo(index, &classInfo);   //CRASH DOESN'T OCCUR, INFO OBTAINED
}
catch(...){ std::cerr << "unable to obtain classInfo2" << std::endl; }

return result;

The thrown exception is:

Exception thrown: read access violation. Steinberg::IPluginFactory2::getClassInfo2[virtual] was 0xFFFFFFFFFFFFFFFF.

As commented above, if I iuse: EXPORTED_FUNCTION int GetClassInfo2(IPluginFactory* pluginFactory, int index, PClassInfo& classInfo) {

instead, then NPlug properly returns a PClassInfo object and properties without problem, but unfortunately PClassInfo provides less information compared to PClassInfo2.... (Most notably, subCategories is only in PClassInfo2).

Any idea what the problem is?

cuikp commented 2 weeks ago

Never mind, figured out the proper way to navigate the different PluginFactory types without encountering crashing errors. Something like this:

auto f3 = Steinberg::FUnknownPtr<Steinberg::IPluginFactory3>(pluginFactory);
auto f2 = Steinberg::FUnknownPtr<Steinberg::IPluginFactory2>(pluginFactory);

PClassInfo ci;
PClassInfo2 ci2;
PClassInfoW ci3;

    if (f3 && f3->getClassInfoUnicode(index, &ci3) == Steinberg::kResultTrue) {
        std::cerr << "PluginFactory3" << std::endl;
                //deal with PClassInfoW
    }
    else if (f2 && f2->getClassInfo2(index, &ci2) == Steinberg::kResultTrue) {
        std::cerr << "PluginFactory2" << std::endl;
        //deal with PClassInfo2
    }
    else if (pluginFactory->getClassInfo(index, &ci) == Steinberg::kResultTrue) {
      std::cerr << "PluginFactory base" << std::endl;
      //deal with PClassInfo;
      }