A VST3 plug-in host for .NET on Windows. A native DLL wraps the Steinberg VST3 SDK behind a small, flat C ABI, and a managed C# library puts safe, disposable types on top — so a .NET application can load VST3 bundles, run audio through them, and show their editors without touching COM-style SDK interfaces or C++.
Windows x64 only. .NET 9.
- Load bundles and list their classes — name, vendor, category and version for each audio
processor class in a
.vst3bundle (effects and instruments alike). - Instantiate plug-ins — creates the processor and its edit controller and connects them.
- Process audio — 32-bit float, planar (deinterleaved), mono or stereo, in place if you like. The host negotiates the bus arrangement and never silently substitutes a different channel count.
- Parameters — enumerate them (title, units, default, step count, flags) and get or set normalized values, including from the UI thread while audio is running.
- State — save and restore a plug-in's complete state as an opaque blob.
- Editors — attach a plug-in's own GUI to any window handle (HWND) you supply, query its size
first, and close it again. The host implements
IPlugFrame, so editors that resize themselves work too.
Not included: MIDI or other event input (instruments load and show their editors, but cannot be played), 64-bit float processing, and x86 or non-Windows builds.
- StreamRecorder — applies VST3 effects in its audio chain through a small
Vst3Effect : IAudioEffectadapter that lives in that repository. - PlugBrowser — a browsable catalog of installed plug-ins, which loads each VST3 in a sandboxed process and photographs its editor.
| Path | What it is |
|---|---|
native/include/vst3_host_c.h |
The flat C ABI. The source of truth; the managed side mirrors it one to one. |
native/src/vst3_host.cpp |
Vst3HostNative.dll: module and plug-in lifecycle, processing, parameters, state, editor hosting. |
native/test/smoke.cpp |
Native-only exercise of the ABI. No CLR involved, so a crash is attributable to the host. |
managed/NetPlugHost/ |
NetPlugHost.dll: P/Invoke layer plus Vst3Module and Vst3Plugin. |
tests/NetPlugHost.HeadlessTest/ |
End-to-end test of the whole ABI, plus contract checks. |
tests/fixture/ |
Builds the SDK's again-sample-accurate gain plug-in as a deterministic test target. |
external/vst3sdk |
Steinberg VST3 SDK, as a git submodule. |
NetPlugHost.Native.targets |
MSBuild targets that copy the native DLL into a consuming project's output. |
ABI_SPEC.md |
The pinned contract: every function, status code, struct and threading rule. |
HANDOFF.md |
Integration notes — what is proven, and the traps worth knowing up front. |
Prerequisites: Visual Studio 2022 with the "Desktop development with C++" workload (MSVC x64, Windows SDK and CMake) and the .NET 9 SDK.
git clone --recursive https://github.com/Bezmont/NetPlugHost.git
cd NetPlugHost
.\build.ps1Already cloned without --recursive? Fetch the SDK with git submodule update --init --recursive.
build.ps1 builds the native DLL, the test fixture and the managed assembly in dependency order,
then runs both tests. Options: -Configuration Debug for SDK assertions, -SkipFixture and
-SkipTest to shorten the loop.
To build just the native DLL:
cmake -S native -B native/build -A x64
cmake --build native/build --config ReleaseThe output is native/build/bin/Vst3HostNative.dll. See native/README.md for
building against an SDK checkout elsewhere.
Reference the managed project, target x64, and import the targets file so the native DLL is copied
next to your binaries — without it the first call fails with DllNotFoundException:
<PropertyGroup>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\NetPlugHost\managed\NetPlugHost\NetPlugHost.csproj" />
</ItemGroup>
<Import Project="..\NetPlugHost\NetPlugHost.Native.targets" />Then:
using NetPlugHost;
using var module = Vst3Module.Load(@"C:\Program Files\Common Files\VST3\Some Plugin.vst3");
foreach (var info in module.GetClasses())
Console.WriteLine($"{info.Name} by {info.Vendor} ({info.Category})");
using var plugin = module.CreatePlugin(classIndex: 0);
plugin.Setup(sampleRate: 48000, maxBlockSize: 512, inputChannels: 2, outputChannels: 2);
plugin.SetActive(true);
// On the audio thread: plugin.Process(inputs, outputs, frames) for each block.
plugin.SetActive(false);Rules worth knowing before you start (all detailed in ABI_SPEC.md):
- Dispose plug-ins before their module. Disposing a module with live plug-ins throws rather than unloading code that is still in use.
- Threading:
Processis the only audio-thread call. Everything else, and editor calls in particular, belongs on the UI thread.SetParameteris the one call that is also safe from the UI thread while audio runs. - Latency is reported, not compensated.
LatencySamplesgives the plug-in's delay; aligning the output is the caller's job. - Keep blocks within
maxBlockSize; shorter blocks are fine.
- The headless test passes end to end against the SDK fixture: bundle load, class enumeration, setup, a parameter round trip, in-place planar processing (input RMS 0.351 → 0.088, exactly the 0.25 gain applied), a consistent second block, a state round trip, and 20 load / create / process / destroy cycles with no crash.
- The native smoke test also passes against nine real third-party plug-ins (Native Instruments, Plugin Alliance, Arturia), including editor size queries and a side-chain compressor.
- Editor hosting is exercised at scale by PlugBrowser, which opens and captures the editors of
nearly two hundred commercial plug-ins through
vst3_plugin_open_editor.
Behaviours that each cost a debugging session, and that the host now handles for you:
- The host owns its channel-pointer arrays. Plug-ins may modify the channel pointers they are
given while processing (the SDK's
ProcessDataSliceradvances every pointer per slice), so the host copies the caller's pointers into its own arrays. Passing the same array for inputs and outputs is therefore safe. - A
setBusArrangementsrefusal is not decisive. Several shipping plug-ins returnkResultFalsewhile already being in exactly the arrangement requested. The host treats the queried arrangement as authoritative, and still reportsVST3_ERR_UNSUPPORTED_IOwhen what is actually in effect differs from what was asked for. - Editors need an
IPlugFrame. Many plug-ins (Native Instruments' among them) callIPlugFrame::resizeViewwhile attaching their editor, and crash if no frame was set. The host sets its own frame on every view before attaching it and resizes the parent window on request.
GPLv3 — see LICENSE.
Vst3HostNative.dll links the Steinberg VST3 SDK, which is dual-licensed GPLv3 / Steinberg
proprietary, and this project takes the GPLv3 option. Anything that links it — the managed
NetPlugHost assembly and any application that uses it — inherits GPLv3 obligations when
distributed. native/README.md covers what that means for consumers, and what a
non-GPL distribution would require.
VST is a registered trademark of Steinberg Media Technologies GmbH.