Bug in juce_VST3Common.h

(macOS 12.0.1)

I created console application (cmake → juce_add_console_app)
I scan plugins in the main thread.

Many of the plugins I scan successfully.
But when I start to scan Lindell Audio 50 Series my application crashes without any exception. Even if I surround pluginDirectoryScanner.scanNextFile with try-catch, it does not reach catch block.

I have debugged juce code to fine out where exactly it crashes, and found that it happens in loadFrom method on factory->createInstance.

line 480 in file juce_VST3Common.h

 bool loadFrom (Steinberg::IPluginFactory* factory, const Steinberg::TUID& uuid)
    {
        jassert (factory != nullptr);
        *this = nullptr;
        return factory->createInstance (uuid, ObjectType::iid, (void**) &source) == Steinberg::kResultOk;
    }

And here is my code, just in case:

    PluginScanner::PluginScanner(){
        _init();
        _scan();
    }

    PluginScanner::~PluginScanner(){}

    void PluginScanner::_save(){}
    void PluginScanner::_load(){}
    void PluginScanner::_init(){
        String workingPath = File::getCurrentWorkingDirectory().getFullPathName();
        plugins_file.reset(new File(workingPath + "/" + PLUGIN_DATA_FILE));
        if (!plugins_file->exists())
        {
            plugins_file->create();
        }
    }

    void PluginScanner::_scan(){
        FileSearchPath searchPath (VST3_PLUGINPATH);
        PluginDirectoryScanner pluginDirectoryScanner (knownPluginList,
                                                   pluginFormat,
                                                   searchPath,
                                                   false /*is reccursive*/,
                                                   File(),
                                                   false /*is asyncronous*/);

        String plugname;
        int index = 0;
        while (true)
        {
            String nextname = pluginDirectoryScanner.getNextPluginFileThatWillBeScanned();
            std::cout << "scanning " << nextname << std::endl;

            // here I just catch a breakpoint when I found the plugin
            if (nextname == "/Library/Audio/Plug-Ins/VST3/Lindell 50 Channel.vst3")
            {
                auto a = 0;
            }
            try {
                // at this point the application silently crashes without any exception
                if (pluginDirectoryScanner.scanNextFile(true, plugname)==false)
                    {
                        std::cout << "stopped at " << plugname << std::endl;
                        break;
                    }
                }
            catch(std::exception &e){
                // and we never go here after silent crash.
                std::cout << "failed: " << e.what() << " " << nextname << std::endl;
            }
        }

        plugins_file->appendText(knownPluginList.createXml()->toString());
    }


int main(){
    PluginScanner();
    return 0;
}

Please help!

Are you running in debug or release?
Some plugins crash on purpose when running debug to prevent reverse engineering.

That being said some plugins can still crash in release. A try/catch won’t help since they are crashing, not cleanly throwing an exception.

Also, plugins can crash if scanned along with others, but not when scanned alone, as some corrupt memory.

Finally, plugins need to be loaded on the message thread.

That is why plugin scanning should be done:

  • out of process
  • on the message thread
  • one plugin at a time
  • in release mode only

The JUCE Audio plugin host now implements out of process scanning, if you need to see how it’s done.

1 Like

It looks like you don’t have a message thread running, but some plugins expect to be able to post messages during startup. You might consider basing your project off the GUI app template (but without necessarily showing a GUI window) rather than the console app example, so that the message loop is set up to run during the plugin scanning.

2 Likes

Thank you.

I went thru example of juce plugin host, but did not find a place where it actually calls scanNextFile. I think it is somewhere in constructor of CustomPluginListComponent. But I cannot figure out how to provide extra plugin directory.

Maybe you have a hint?

The AudioPluginHost uses a PluginListComponent to display the list of plugins and start the scanning process. This component uses a PluginDirectoryScanner internally to actually do the scanning, and the PluginDirectoryScanner takes a constructor argument that specifies which paths should be scanned.

You can look at the implementation of the PluginListComponent to see how this works. In Scanner::startScan, the scanner creates a new PluginDirectoryScanner and passes it the paths configured by the user in the pathList window.

1 Like

@reuk @dimbouche Thank you guys!

I am successfully doing all plugins scan while my UI is not frozen.
It took some time to study example code from JUCE host app.

Do I get it right now? : I still can run scanning in a thread, but I have to provide a CustomScanner to KnownPluginList object, where CustomScanner is using Child/Main process system to run the scanning as a process. And when a scanned plugin checks if it is a main thread, it does not know that Child process was run from gui app and thinks it is a main one?

Correct. Each process owns its threads.
So from the main process (main app) you run the scan on a detached thread, which triggers a secondary process (plugin scan app) which scans plugins on the message thread as expected.

1 Like