From 97b1d88f644eeddca5ab0e2c0c38e4b8a040b1eb Mon Sep 17 00:00:00 2001 From: Jason Ginchereau Date: Thu, 13 Apr 2023 22:03:38 -1000 Subject: [PATCH 1/2] Allow host module re-initialization --- src/NodeApi/DotNetHost/NativeHost.cs | 36 ++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/NodeApi/DotNetHost/NativeHost.cs b/src/NodeApi/DotNetHost/NativeHost.cs index cfa606f3..40ba6fcf 100644 --- a/src/NodeApi/DotNetHost/NativeHost.cs +++ b/src/NodeApi/DotNetHost/NativeHost.cs @@ -22,8 +22,11 @@ internal unsafe partial class NativeHost : IDisposable private static readonly string s_managedHostTypeName = typeof(NativeHost).Namespace + ".ManagedHost"; + private string? _targetFramework; + private string? _managedHostPath; private ICLRRuntimeHost* _runtimeHost; private hostfxr_handle _hostContextHandle; + private JSReference? _exports; public static bool IsTracingEnabled { get; } = Environment.GetEnvironmentVariable("TRACE_NODE_API_HOST") == "1"; @@ -81,20 +84,36 @@ public NativeHost() /// JS exports value from the managed host. private JSValue InitializeManagedHost(JSCallbackArgs args) { + string targetFramework = (string)args[0]; + string managedHostPath = (string)args[1]; + if (_hostContextHandle != default || _runtimeHost is not null) { - throw new NotSupportedException( - ".NET is already initialized in the current process. " + - "Initializing multiple .NET versions is not supported."); + // .NET is already loaded for this host. + if (targetFramework == _targetFramework && managedHostPath == _managedHostPath && + _exports is not null) + { + // The same version of .NET and same managed host were requested again. + // Just return the same exports object that was initialized the first time. + // Normally this shouldn't happen because the host package initialization + // script would only be loaded once by require(). But certain situations like + // drive letter or path casing inconsistencies can cause it to be loaded twice. + return _exports.GetValue()!.Value; + } + else + { + throw new NotSupportedException( + $".NET ({_targetFramework}) is already initialized in the current process. " + + "Initializing multiple .NET versions is not supported."); + } } - string targetFramework = (string)args[0]; - string managedHostPath = (string)args[1]; JSValue require = args[2]; Trace($"> NativeHost.InitializeManagedHost({targetFramework}, {managedHostPath})"); try { + JSValue exports; if (!targetFramework.Contains('.') && targetFramework.StartsWith("net") && targetFramework.Length >= 5) { @@ -104,7 +123,7 @@ private JSValue InitializeManagedHost(JSCallbackArgs args) int.Parse(targetFramework.Substring(4, 1)), targetFramework.Length == 5 ? 0 : int.Parse(targetFramework.Substring(5, 1))); - return InitializeFrameworkHost(frameworkVersion, managedHostPath, require); + exports = InitializeFrameworkHost(frameworkVersion, managedHostPath, require); } else { @@ -114,8 +133,11 @@ private JSValue InitializeManagedHost(JSCallbackArgs args) #else Version dotnetVersion = Version.Parse(targetFramework.AsSpan(3)); #endif - return InitializeDotNetHost(dotnetVersion, managedHostPath, require); + exports = InitializeDotNetHost(dotnetVersion, managedHostPath, require); } + + _exports = new JSReference(exports); + return exports; } catch (Exception ex) { From c2934f24f58a445abafcb5cf18c6432ebf37d133 Mon Sep 17 00:00:00 2001 From: Jason Ginchereau Date: Thu, 13 Apr 2023 22:16:30 -1000 Subject: [PATCH 2/2] Save init parameters --- src/NodeApi/DotNetHost/NativeHost.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/NodeApi/DotNetHost/NativeHost.cs b/src/NodeApi/DotNetHost/NativeHost.cs index 40ba6fcf..fc720152 100644 --- a/src/NodeApi/DotNetHost/NativeHost.cs +++ b/src/NodeApi/DotNetHost/NativeHost.cs @@ -136,6 +136,9 @@ private JSValue InitializeManagedHost(JSCallbackArgs args) exports = InitializeDotNetHost(dotnetVersion, managedHostPath, require); } + // Save init parameters and result in case of re-init. + _targetFramework = targetFramework; + _managedHostPath = managedHostPath; _exports = new JSReference(exports); return exports; }