diff --git a/src/common/logging/vst3.cpp b/src/common/logging/vst3.cpp index b4dad673..259a27a6 100644 --- a/src/common/logging/vst3.cpp +++ b/src/common/logging/vst3.cpp @@ -306,8 +306,13 @@ bool Vst3Logger::log_request( const YaEditController::SetComponentHandler& request) { return log_request_base(is_host_vst, [&](auto& message) { message << request.instance_id - << ": IEditController::setComponentHandler(handler = " - ")"; + << ": IEditController::setComponentHandler(handler = "; + if (request.component_handler_proxy_args) { + message << ""; + } else { + message << ""; + } + message << ")"; }); } diff --git a/src/common/serialization/vst3/plugin/edit-controller.h b/src/common/serialization/vst3/plugin/edit-controller.h index c83c3090..0bc0ee46 100644 --- a/src/common/serialization/vst3/plugin/edit-controller.h +++ b/src/common/serialization/vst3/plugin/edit-controller.h @@ -344,12 +344,19 @@ class YaEditController : public Steinberg::Vst::IEditController { native_size_t instance_id; - Vst3ComponentHandlerProxy::ConstructArgs component_handler_proxy_args; + /** + * Some hosts will pass a null pointer to explicitly free the component + * handler passed before releasing the object instance. This also + * happens in the SDK, so this seems like valid behaviour we should + * support. + */ + std::optional + component_handler_proxy_args; template void serialize(S& s) { s.value8b(instance_id); - s.object(component_handler_proxy_args); + s.ext(component_handler_proxy_args, bitsery::ext::StdOptional{}); } }; diff --git a/src/plugin/bridges/vst3-impls/plugin-proxy.cpp b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp index 5e9ae518..3688a8e3 100644 --- a/src/plugin/bridges/vst3-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp @@ -452,7 +452,8 @@ Vst3PluginProxyImpl::setParamNormalized(Steinberg::Vst::ParamID id, tresult PLUGIN_API Vst3PluginProxyImpl::setComponentHandler( Steinberg::Vst::IComponentHandler* handler) { - // TODO: Null pointers are valid here, should we pass them through? + // Null pointers are valid here going from the reference implementations in + // the SDK if (handler) { // We'll store the pointer for when the plugin later makes a callback to // this component handler @@ -473,10 +474,18 @@ tresult PLUGIN_API Vst3PluginProxyImpl::setComponentHandler( Vst3ComponentHandlerProxy::ConstructArgs(component_handler, instance_id())}); } else { - bridge.logger.log( - "WARNING: Null pointer passed to " - "'IEditController::setComponentHandler()'"); - return Steinberg::kInvalidArgument; + component_handler = nullptr; + + component_handler_2 = nullptr; + component_handler_3 = nullptr; + component_handler_bus_activation = nullptr; + progress = nullptr; + unit_handler = nullptr; + unit_handler_2 = nullptr; + + return bridge.send_message(YaEditController::SetComponentHandler{ + .instance_id = instance_id(), + .component_handler_proxy_args = std::nullopt}); } } diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index e612d7c5..dc4e8420 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -364,14 +364,18 @@ void Vst3Bridge::run() { }, [&](YaEditController::SetComponentHandler& request) -> YaEditController::SetComponentHandler::Response { - // We'll create a proxy object for the component handler and - // pass that to the initialize function. The lifetime of this - // object is tied to that of the actual plugin object we're - // proxying for. + // If the host passed a valid component handler, then we'll + // create a proxy object for the component handler and pass that + // to the initialize function. The lifetime of this object is + // tied to that of the actual plugin object we're proxying for. + // Otherwise we'll also pass a null pointer. This often happens + // just before the host terminates the plugin. object_instances[request.instance_id].component_handler_proxy = - Steinberg::owned(new Vst3ComponentHandlerProxyImpl( - *this, - std::move(request.component_handler_proxy_args))); + request.component_handler_proxy_args + ? Steinberg::owned(new Vst3ComponentHandlerProxyImpl( + *this, + std::move(*request.component_handler_proxy_args))) + : nullptr; return object_instances[request.instance_id] .edit_controller->setComponentHandler(