WaveLab 10 plugin unloading

Can anyone explain the behaviour of WaveLab 10(on windows) when unloading plugins? The behaviour I am seeing is that it calls InitModule() on loading correctly but on unloading DeinitModule() never gets called, nor does the exit code for the dll (ie. no destructors for objects of global scope called). As a consequence the dll is never unloaded from memory correctly and results in crashes on WaveLab 10 exit. Trying the same with SONAR cakewalk and I am seeing the correct behaviour and no crashes so I am perplexed as to what is going on. I can imagine this behaviour might happen if the plugin code causes an exception and WaveLab traps it but I’m not seeing it. Does it produce any exception logs at all?

As mentioned there:
https://developer.steinberg.help/display/VST/How+the+host+will+load+a+VST-MA+based+Plug-in

The host must call InitDll / ExitDll (and not InitModule / DeinitModule as you mention)
And I have just verified that InitDll and ExitDll are called by WaveLab.

This being said, and independently from WaveLab, I would recommend relying on reference counting of the resources used by your plugin instances, rather than on global resources managed by init/deinit calls.
And above all, don’t use thread locals.

As mentioned there:
https://developer.steinberg.help/display/VST/How+the+host+will+load+a+VST-MA+based+Plug-in

The host must call InitDll / ExitDll (and not InitModule / DeinitModule as you mention)
And I have just verified that InitDll and ExitDll are called by WaveLab.

This being said, and independently from WaveLab, I would recommend relying on reference counting of the resources used by your plugin instances, rather than on global resources managed by init/deinit calls.
And above all, don’t use thread locals.

Well yes, I suspected that it would be calling both on normal circumstances but it appears that there is a way (through memory corruption presumably) for it to not happen. Like I said, I presume what is happening is that an exception handle traps an exception during shutdown but before the exit processing for the dll is called and then is never called and possibly not unloaded post exception. This leads to WaveLab 10 itself crashing. Whilst it looks like my plugin has a problem, it also looks like WaveLab 10’s exception handling isn’t handled well because the whole point of the exception handler wrapper is to prevent it from being corrupted and crashing. I have mixed views about exception handler protections. It would be nice to be able to turn them off as they have a tendency of masking issues when you are trying to debug issues in your code.

This is looking very much like a problem in WaveLab as I have pinpointed the source of the problem. See this extract of code for the initialize method of the processor part of my plugin:

tresult PLUGIN_API HBDynamicsProcessor::initialize(FUnknown* context)
{
tresult result = AudioEffect::initialize(context);
IHostApplication* pHost = (IHostApplication*)context;

String128 HostName = {0};
ConstString AU_WrapperName(“VST3-AU Wrapper”);

pHost->getName(HostName);

IsAU = (AU_WrapperName.compare(ConstString(HostName)) == 0);
.
.
.

To explain the code, my plugin supports side chaining but I couldn’t figure out a way of having a workable AU plugin using the AU wrapper as anything I tried would fail the auval test. The only solution I found was to not support sidechaining for AU,but I still want to support it in VST3 so I call the getName() method on the parent host to figure out if I am in an AU wrapper or something else. If I run my plugin in the debugging with a breakpoint on the call to getName() and then put the instruction point on the next line so that call never takes place everything works fine and WaveLab or my plugin does not crash. If I allow it to execute the getName() method HostName remains unitialised (empty string) the code runs fine, and WaveLab10 crashes when shutting down. The documentation states that “The context parameter passed to Steinberg::IPluginBase::initialize is Steinberg::Vst::IHostApplication” so why can I not call the getName() method on that parameter without having WaveLab10 corrupt itself?

For the sake of anyone else who comes across this issue, I have the solution. If you use queryInterface to get IHostApplication it works correctly. I guess someone should update the documentation for the initialize() method because the pointer passed in by Steinberg WaveLab 10 is clearly no a pointer to IHostApplication. This is what the code now looks like:

tresult PLUGIN_API HBDynamicsProcessor::initialize(FUnknown* context)
{
tresult result = AudioEffect::initialize(context);
IHostApplication* pHost = 0;

if (context->queryInterface(Vst::IHostApplication::iid, (void**)&pHost) == kResultTrue)
{
String128 HostName = {0};
ConstString AU_WrapperName(“VST3-AU Wrapper”);

pHost->getName(HostName);
pHost->release();

IsAU = (AU_WrapperName.compare(ConstString(HostName)) == 0);
}
.
.
.

In case the plugin is running as an AU wrapped instance, “context” isn’t passed by Wavelab or any other host implementation itself but the AU-Wrapper, which is the host your VST3 implementation sees. You can also simply check whether context implements the AUWrapper interface in order to detect whether your plugin is running as AudioUnit, e.g.:

tresult PLUGIN_API HBDynamicsProcessor::initialize(FUnknown* context)
{
  tresult result = AudioEffect::initialize(context);
  
  void *au = nullptr;
  context->queryInterface(IVst3ToAUWrapper::iid, &au);
  IsAU = au != nullptr;
  .
  .
  .

Using a C-style cast on context is a bad idea anyway, because the host implementation sits in a different library/executable (at least in case of VST3 or AU, but not AAX and VST2) which is what the COM style interface of the SDK is provided for.

never try to use a C-cast for interface!!!
you could use like you did with queryInterface or:

FUnknownPtrVst::IHostApplication hostApp (context);
if (hostApp) { …