diff --git a/src/TeamsISO.Engine.NdiInterop/NdiNative.cs b/src/TeamsISO.Engine.NdiInterop/NdiNative.cs
index f4a47fd..a3d66b2 100644
--- a/src/TeamsISO.Engine.NdiInterop/NdiNative.cs
+++ b/src/TeamsISO.Engine.NdiInterop/NdiNative.cs
@@ -4,13 +4,30 @@ namespace TeamsISO.Engine.NdiInterop;
///
/// P/Invoke declarations for the NewTek/Vizrt NDI SDK 6 native library.
-/// On Windows the import target is Processing.NDI.Lib.x64.dll; the loader resolves it
-/// from the NDI Runtime installation path or from the application directory.
+/// On Windows the import target is Processing.NDI.Lib.x64.dll. Resolution of
+/// the DLL is handled by , which loads it from
+/// the NDI Runtime installation directory exposed by the
+/// NDI_RUNTIME_DIR_V<n> environment variables — the NDI installer sets
+/// these but does not always add the runtime directory to PATH, so a default
+/// loader-based resolution would fail with 0x8007007E on otherwise correctly
+/// installed machines.
///
internal static class NdiNative
{
private const string LibName = "Processing.NDI.Lib.x64";
+ ///
+ /// Registers with the runtime before any
+ /// P/Invoke against fires. The static constructor is
+ /// guaranteed to run before the first access to any member of this type, which
+ /// includes the very first [DllImport] call site, so the resolver is
+ /// always in place when the loader needs it.
+ ///
+ static NdiNative()
+ {
+ NdiNativeLibraryResolver.Register();
+ }
+
// ---- Lifecycle ----
[DllImport(LibName, EntryPoint = "NDIlib_initialize", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)]
diff --git a/src/TeamsISO.Engine.NdiInterop/NdiNativeLibraryResolver.cs b/src/TeamsISO.Engine.NdiInterop/NdiNativeLibraryResolver.cs
new file mode 100644
index 0000000..26ada5c
--- /dev/null
+++ b/src/TeamsISO.Engine.NdiInterop/NdiNativeLibraryResolver.cs
@@ -0,0 +1,66 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace TeamsISO.Engine.NdiInterop;
+
+///
+/// Resolves the NDI native library (Processing.NDI.Lib.x64.dll) from the
+/// NDI Runtime install directory exposed via NDI_RUNTIME_DIR_V<n>
+/// environment variables.
+///
+/// The NDI installer creates these env vars but does not always add the runtime
+/// directory to PATH, so a default [DllImport("Processing.NDI.Lib.x64")]
+/// can fail with 0x8007007E ("the specified module could not be found") on
+/// otherwise correctly installed machines. This resolver patches that gap by
+/// pre-loading the DLL from the install dir before the runtime falls back to its
+/// default search algorithm.
+///
+/// The resolver is registered from the static constructor of ,
+/// which guarantees it runs before the first P/Invoke on that type fires. On
+/// non-Windows the resolver short-circuits; the assembly's P/Invokes are gated by
+/// [SupportedOSPlatform("windows")] and won't fire there in any case.
+///
+internal static class NdiNativeLibraryResolver
+{
+ private const string NdiX64LibraryName = "Processing.NDI.Lib.x64";
+ private const string NdiX64LibraryFile = "Processing.NDI.Lib.x64.dll";
+
+ ///
+ /// NDI runtime install-dir environment variables, in preference order.
+ /// V6 is what TeamsISO is built against; V5/V4 are listed as graceful fallbacks
+ /// for installs that pre-date V6 — the runtime probe will still report a
+ /// version mismatch, but at least the DLL will load and the engine can surface
+ /// a clear alert instead of dying with DllNotFoundException.
+ ///
+ private static readonly string[] EnvVarFallbacks =
+ {
+ "NDI_RUNTIME_DIR_V6",
+ "NDI_RUNTIME_DIR_V5",
+ "NDI_RUNTIME_DIR_V4",
+ };
+
+ internal static void Register()
+ {
+ NativeLibrary.SetDllImportResolver(typeof(NdiNativeLibraryResolver).Assembly, Resolve);
+ }
+
+ private static IntPtr Resolve(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ if (libraryName != NdiX64LibraryName) return IntPtr.Zero;
+ if (!OperatingSystem.IsWindows()) return IntPtr.Zero;
+
+ foreach (var envVar in EnvVarFallbacks)
+ {
+ var dir = Environment.GetEnvironmentVariable(envVar);
+ if (string.IsNullOrEmpty(dir)) continue;
+ var path = Path.Combine(dir, NdiX64LibraryFile);
+ if (!File.Exists(path)) continue;
+ if (NativeLibrary.TryLoad(path, out var handle))
+ return handle;
+ }
+
+ // Fall through to default loader (PATH, app dir, etc.) — preserves the
+ // chance that someone added the NDI dir to PATH manually.
+ return IntPtr.Zero;
+ }
+}