use super::{ModuleLoader, VstModule};
use crate::error::{Error, Result};
use std::path::Path;
use vst3::Steinberg::IPluginFactory;
#[cfg(target_os = "windows")]
use libloading::{Library, Symbol};
type InitDllFunc = unsafe extern "C" fn() -> bool;
type ExitDllFunc = unsafe extern "C" fn() -> bool;
type GetPluginFactoryFunc = unsafe extern "C" fn() -> *mut IPluginFactory;
pub struct WindowsModule {
#[allow(dead_code)]
library: Library,
path: std::path::PathBuf,
exit_dll: Option<Symbol<'static, ExitDllFunc>>,
get_factory_fn: Symbol<'static, GetPluginFactoryFunc>,
}
fn arch_mismatch_detail(binary: &Path) -> Option<String> {
use super::arch;
let data = std::fs::read(binary).ok()?;
let machine = arch::detect_pe_machine(&data)?;
if machine == arch::PE_MACHINE_ARM64 && runs_on_arm64_windows() {
return None;
}
arch::mismatch_detail("DLL", arch::pe_machine_name(machine))
}
pub(crate) fn runs_on_arm64_windows() -> bool {
type IsWow64Process2 =
unsafe extern "system" fn(*mut std::ffi::c_void, *mut u16, *mut u16) -> i32;
let Ok(kernel32) = libloading::os::windows::Library::open_already_loaded("kernel32.dll") else {
return false;
};
unsafe {
let Ok(is_wow64_process2) = kernel32.get::<IsWow64Process2>(b"IsWow64Process2\0") else {
return false;
};
let (mut process_machine, mut native_machine) = (0, 0);
is_wow64_process2(
winapi::um::processthreadsapi::GetCurrentProcess().cast(),
&mut process_machine,
&mut native_machine,
) != 0
&& native_machine == super::arch::PE_MACHINE_ARM64
}
}
impl WindowsModule {
fn load_internal(path: &Path) -> Result<Self> {
unsafe {
log::info!("=== Windows VST3 MODULE LOADING START ===");
log::info!("Loading VST3 DLL: {}", path.display());
let binary = if path.is_dir() {
crate::discovery::get_vst3_binary_path(path)?
} else {
path.to_path_buf()
};
log::debug!("Step 1: Loading DLL: {}", binary.display());
let library = Library::new(&binary).map_err(|e| {
let detail = arch_mismatch_detail(&binary)
.unwrap_or_else(|| format!("Failed to load DLL: {}", e));
Error::PluginLoadFailed(detail)
})?;
log::debug!("DLL loaded successfully");
log::debug!("Step 2: Looking for InitDll function...");
let init_dll_result = library.get::<InitDllFunc>(b"InitDll");
match init_dll_result {
Ok(init_dll) => {
log::debug!("InitDll function found, calling it...");
let init_result = init_dll();
if init_result {
log::debug!("InitDll called successfully");
} else {
log::warn!("InitDll returned false");
return Err(Error::PluginLoadFailed(
"InitDll function returned false".to_string(),
));
}
}
Err(_) => {
log::debug!(
"InitDll function not found (this is OK, it's optional on Windows)"
);
}
}
log::debug!("Step 3: Looking for ExitDll function...");
let exit_dll = match library.get::<ExitDllFunc>(b"ExitDll") {
Ok(func) => {
log::debug!("ExitDll function found (will be called on cleanup)");
Some(std::mem::transmute(func))
}
Err(_) => {
log::debug!(
"ExitDll function not found (this is OK, it's optional on Windows)"
);
None
}
};
log::debug!("Step 4: Getting GetPluginFactory function...");
let get_factory_fn = library
.get::<GetPluginFactoryFunc>(b"GetPluginFactory")
.map_err(|e| {
Error::PluginLoadFailed(format!("Failed to find GetPluginFactory: {}", e))
})?;
log::debug!("GetPluginFactory function found");
let get_factory_fn: Symbol<'static, GetPluginFactoryFunc> =
std::mem::transmute(get_factory_fn);
log::info!("=== Windows VST3 MODULE LOADING COMPLETE ===");
log::info!("DLL loaded successfully: {}", path.display());
Ok(WindowsModule {
library,
path: path.to_path_buf(),
exit_dll,
get_factory_fn,
})
}
}
}
impl VstModule for WindowsModule {
fn get_factory(&self) -> Result<*mut IPluginFactory> {
let factory = unsafe { (self.get_factory_fn)() };
if factory.is_null() {
Err(Error::PluginLoadFailed(
"GetPluginFactory returned null".to_string(),
))
} else {
Ok(factory)
}
}
fn path(&self) -> &Path {
&self.path
}
}
impl Drop for WindowsModule {
fn drop(&mut self) {
unsafe {
log::debug!("=== Windows VST3 MODULE CLEANUP START ===");
if let Some(exit_dll) = self.exit_dll.take() {
log::debug!("Calling ExitDll...");
let exit_result = exit_dll();
if exit_result {
log::debug!("ExitDll called successfully");
} else {
log::warn!("ExitDll returned false");
}
}
log::debug!("Unloading DLL...");
log::debug!("DLL unloaded");
log::debug!("=== Windows VST3 MODULE CLEANUP COMPLETE ===");
}
}
}
pub struct WindowsModuleLoader;
impl ModuleLoader for WindowsModuleLoader {
fn load(path: &Path) -> Result<Box<dyn VstModule>> {
let module = WindowsModule::load_internal(path)?;
Ok(Box::new(module))
}
}