diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index 342668146a873..adf2908c581ab 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -117,7 +117,7 @@
-
+
@@ -285,10 +285,12 @@
+
+
Common\Interop\Windows\OleAut32\Interop.VariantClear.cs
diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml
index 1b4e3a1bd2acd..e3682b253e3a7 100644
--- a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml
+++ b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml
@@ -21,13 +21,6 @@
-
-
-
-
-
diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml
index 52ab4af9bef74..7ddddc6f48af8 100644
--- a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml
+++ b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Windows.xml
@@ -10,5 +10,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs
new file mode 100644
index 0000000000000..3e89bca82e0f2
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.CoreCLR.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Internal.Runtime.InteropServices
+{
+ public static partial class ComponentActivator
+ {
+ // This hook for when GetFunctionPointer is called when the feature is disabled allows us to
+ // provide error messages for known hosting scenarios such as C++/CLI.
+ private static void OnDisabledGetFunctionPointerCall(IntPtr typeNameNative, IntPtr methodNameNative)
+ {
+ if (!OperatingSystem.IsWindows())
+ return;
+
+ // Check for the exact type and method name used by ijwhost - see src/native/corehost/ijwhost/ijwhost.cpp
+ if (Marshal.PtrToStringUni(methodNameNative) == "LoadInMemoryAssemblyInContext"
+ && Marshal.PtrToStringUni(typeNameNative) == $"Internal.Runtime.InteropServices.{nameof(InMemoryAssemblyLoader)}, {CoreLib.Name}")
+ {
+ throw new NotSupportedException(SR.NotSupported_CppCli);
+ }
+ }
+ }
+}
diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.PlatformNotSupported.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.PlatformNotSupported.cs
new file mode 100644
index 0000000000000..1a0881cd414fe
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.PlatformNotSupported.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+
+namespace Internal.Runtime.InteropServices
+{
+ ///
+ /// This class enables the .NET IJW host to load an in-memory module as a .NET assembly
+ ///
+ public static class InMemoryAssemblyLoader
+ {
+ ///
+ /// Loads into an isolated AssemblyLoadContext an assembly that has already been loaded into memory by the OS loader as a native module.
+ ///
+ /// The native module handle for the assembly.
+ /// The path to the assembly (as a pointer to a UTF-16 C string).
+ public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assemblyPath)
+ => throw new PlatformNotSupportedException();
+ }
+}
diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs
index 4456ce1f9ae14..fa7414da21d53 100644
--- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs
+++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs
@@ -2,48 +2,87 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
+using System.Runtime.Versioning;
namespace Internal.Runtime.InteropServices
{
///
/// This class enables the .NET IJW host to load an in-memory module as a .NET assembly
///
+ [SupportedOSPlatform("windows")]
public static class InMemoryAssemblyLoader
{
-#if TARGET_WINDOWS
private static bool IsSupported { get; } = InitializeIsSupported();
-
private static bool InitializeIsSupported() => AppContext.TryGetSwitch("System.Runtime.InteropServices.EnableCppCLIHostActivation", out bool isSupported) ? isSupported : true;
-#endif
///
- /// Loads into an isolated AssemblyLoadContext an assembly that has already been loaded into memory by the OS loader as a native module.
+ /// Loads an assembly that has already been loaded into memory by the OS loader as a native module
+ /// into an isolated AssemblyLoadContext.
///
/// The native module handle for the assembly.
/// The path to the assembly (as a pointer to a UTF-16 C string).
public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assemblyPath)
{
-#if TARGET_WINDOWS
if (!IsSupported)
- throw new NotSupportedException("This API is not enabled in trimmed scenarios. see https://aka.ms/dotnet-illink/nativehost for more details");
+ throw new NotSupportedException(SR.NotSupported_CppCli);
+
+#pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
+ LoadInMemoryAssemblyInContextImpl(moduleHandle, assemblyPath);
+#pragma warning restore IL2026
+ }
+
+ ///
+ /// Loads into an assembly that has already been loaded into memory by the OS loader as a native module
+ /// into the specified load context.
+ ///
+ /// The native module handle for the assembly.
+ /// The path to the assembly (as a pointer to a UTF-16 C string).
+ /// Load context (currently must be IntPtr.Zero)
+ [UnmanagedCallersOnly]
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
+ Justification = "The same C++/CLI feature switch applies to LoadInMemoryAssembly and this function. We rely on the warning from LoadInMemoryAssembly.")]
+ public static unsafe void LoadInMemoryAssemblyInContext(IntPtr moduleHandle, IntPtr assemblyPath, IntPtr loadContext)
+ {
+ if (!IsSupported)
+ throw new NotSupportedException(SR.NotSupported_CppCli);
+
+ if (loadContext != IntPtr.Zero)
+ throw new ArgumentOutOfRangeException(nameof(loadContext));
+ LoadInMemoryAssemblyInContextImpl(moduleHandle, assemblyPath, AssemblyLoadContext.Default);
+ }
+
+ [RequiresUnreferencedCode("C++/CLI is not trim-compatible", Url = "https://aka.ms/dotnet-illink/nativehost")]
+ private static void LoadInMemoryAssemblyInContextImpl(IntPtr moduleHandle, IntPtr assemblyPath, AssemblyLoadContext? alc = null)
+ {
string? assemblyPathString = Marshal.PtrToStringUni(assemblyPath);
if (assemblyPathString == null)
- {
throw new ArgumentOutOfRangeException(nameof(assemblyPath));
- }
- // We don't cache the ALCs here since each IJW assembly will call this method at most once
+ // We don't cache the ALCs or resolvers here since each IJW assembly will call this method at most once
// (the load process rewrites the stubs that call here to call the actual methods they're supposed to)
-#pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
- AssemblyLoadContext context = new IsolatedComponentLoadContext(assemblyPathString);
-#pragma warning restore IL2026
- context.LoadFromInMemoryModule(moduleHandle);
-#else
- throw new PlatformNotSupportedException();
-#endif
+ if (alc is null)
+ {
+ alc = new IsolatedComponentLoadContext(assemblyPathString);
+ }
+ else if (alc == AssemblyLoadContext.Default)
+ {
+ var resolver = new AssemblyDependencyResolver(assemblyPathString);
+ AssemblyLoadContext.Default.Resolving +=
+ [RequiresUnreferencedCode("C++/CLI is not trim-compatible", Url = "https://aka.ms/dotnet-illink/nativehost")]
+ (context, assemblyName) =>
+ {
+ string? assemblyPath = resolver.ResolveAssemblyToPath(assemblyName);
+ return assemblyPath != null
+ ? context.LoadFromAssemblyPath(assemblyPath)
+ : null;
+ };
+ }
+
+ alc.LoadFromInMemoryModule(moduleHandle);
}
}
}
diff --git a/src/installer/tests/HostActivation.Tests/NativeHosting/Ijwhost.cs b/src/installer/tests/HostActivation.Tests/NativeHosting/Ijwhost.cs
index 46eb6c6585a3d..99b22c870eba0 100644
--- a/src/installer/tests/HostActivation.Tests/NativeHosting/Ijwhost.cs
+++ b/src/installer/tests/HostActivation.Tests/NativeHosting/Ijwhost.cs
@@ -35,7 +35,7 @@ public void LoadLibrary()
result.Should().Pass()
.And.HaveStdOutContaining("[C++/CLI] NativeEntryPoint: calling managed class")
- .And.HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"IsolatedComponentLoadContext");
+ .And.HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"Default\" System.Runtime.Loader.DefaultAssemblyLoadContext");
}
[Theory]
@@ -57,7 +57,7 @@ public void ManagedHost(bool selfContained)
result.Should().Pass()
.And.HaveStdOutContaining("[C++/CLI] NativeEntryPoint: calling managed class")
- .And.HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"IsolatedComponentLoadContext")
+ .And.HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"Default\" System.Runtime.Loader.DefaultAssemblyLoadContext")
.And.HaveStdErrContaining($"Executing as a {(selfContained ? "self-contained" : "framework-dependent")} app");
}
diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml
index 3def281c2bcab..27d64e4c5bf8f 100644
--- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml
+++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.Shared.xml
@@ -16,10 +16,16 @@
-->
+
+
+
+
+
+
-
+
@@ -69,7 +75,7 @@
-
+
diff --git a/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs b/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs
index 97f8c64008371..118afb963cfa6 100644
--- a/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs
+++ b/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs
@@ -11,7 +11,7 @@
namespace Internal.Runtime.InteropServices
{
- public static class ComponentActivator
+ public static partial class ComponentActivator
{
private const string TrimIncompatibleWarningMessage = "Native hosting is not trim compatible and this warning will be seen if trimming is enabled.";
private const string NativeAOTIncompatibleWarningMessage = "The native code for the method requested might not be available at runtime.";
@@ -115,7 +115,24 @@ public static unsafe int GetFunctionPointer(IntPtr typeNameNative,
IntPtr functionHandle)
{
if (!IsSupported)
+ {
+#if CORECLR
+ try
+ {
+ OnDisabledGetFunctionPointerCall(typeNameNative, methodNameNative);
+ }
+ catch (Exception e)
+ {
+ // The callback can intentionally throw NotSupportedException to provide errors to consumers,
+ // so we let that one through. Any other exceptions must not be leaked out.
+ if (e is NotSupportedException)
+ throw;
+
+ return e.HResult;
+ }
+#endif
return HostFeatureDisabled;
+ }
try
{
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index 0896ffcf6cc99..d3505fea8c957 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -3727,6 +3727,9 @@
Built-in COM has been disabled via a feature switch. See https://aka.ms/dotnet-illink/com for more information.
+
+ C++/CLI activation has been disabled via a feature switch. See https://aka.ms/dotnet-illink/nativehost for more information.
+
Queue empty.
diff --git a/src/native/corehost/ijwhost/ijwhost.cpp b/src/native/corehost/ijwhost/ijwhost.cpp
index 85020baba02db..ab4f047ee8123 100644
--- a/src/native/corehost/ijwhost/ijwhost.cpp
+++ b/src/native/corehost/ijwhost/ijwhost.cpp
@@ -2,15 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license.
#include "ijwhost.h"
-#include "hostfxr.h"
-#include "fxr_resolver.h"
-#include "pal.h"
-#include "trace.h"
-#include "error_codes.h"
-#include "utils.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#include "bootstrap_thunk.h"
-
#if defined(_WIN32)
// IJW entry points are defined without the __declspec(dllexport) attribute.
// The issue here is that the MSVC compiler links to the exact name _CorDllMain instead of their stdcall-managled names.
@@ -22,8 +22,9 @@
pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_memory_assembly_fn* delegate)
{
- return load_fxr_and_get_delegate(
- hostfxr_delegate_type::hdt_load_in_memory_assembly,
+ get_function_pointer_fn get_function_pointer;
+ int status = load_fxr_and_get_delegate(
+ hostfxr_delegate_type::hdt_get_function_pointer,
[handle](const pal::string_t& host_path, pal::string_t* config_path_out)
{
pal::string_t mod_path;
@@ -39,8 +40,18 @@ pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_m
return StatusCode::Success;
},
- delegate
+ &get_function_pointer
);
+ if (status != StatusCode::Success)
+ return status;
+
+ return get_function_pointer(
+ _X("Internal.Runtime.InteropServices.InMemoryAssemblyLoader, System.Private.CoreLib"),
+ _X("LoadInMemoryAssemblyInContext"),
+ UNMANAGEDCALLERSONLY_METHOD,
+ nullptr, // load context
+ nullptr, // reserved
+ (void**)delegate);
}
IJW_API BOOL STDMETHODCALLTYPE _CorDllMain(HINSTANCE hInst,
diff --git a/src/native/corehost/ijwhost/ijwhost.h b/src/native/corehost/ijwhost/ijwhost.h
index 78a242f0dbd42..c3698a4d4acb6 100644
--- a/src/native/corehost/ijwhost/ijwhost.h
+++ b/src/native/corehost/ijwhost/ijwhost.h
@@ -13,7 +13,7 @@ bool patch_vtable_entries(PEDecoder& decoder);
void release_bootstrap_thunks(PEDecoder& decoder);
bool are_thunks_installed_for_module(HMODULE instance);
-using load_in_memory_assembly_fn = void(STDMETHODCALLTYPE*)(pal::dll_t handle, const pal::char_t* path);
+using load_in_memory_assembly_fn = void(STDMETHODCALLTYPE*)(pal::dll_t handle, const pal::char_t* path, void* load_context);
pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_memory_assembly_fn* delegate);
diff --git a/src/native/corehost/ijwhost/ijwthunk.cpp b/src/native/corehost/ijwhost/ijwthunk.cpp
index 91a462dfb102e..eeddf10fd931c 100644
--- a/src/native/corehost/ijwhost/ijwthunk.cpp
+++ b/src/native/corehost/ijwhost/ijwthunk.cpp
@@ -34,7 +34,7 @@ namespace
HANDLE g_heapHandle;
bool patch_vtable_entries(PEDecoder& pe)
-{
+{
size_t numFixupRecords;
IMAGE_COR_VTABLEFIXUP* pFixupTable = pe.GetVTableFixups(&numFixupRecords);
@@ -68,7 +68,7 @@ bool patch_vtable_entries(PEDecoder& pe)
}
trace::setup();
-
+
error_writer_scope_t writer_scope(swallow_trace);
size_t currentThunk = 0;
@@ -115,7 +115,7 @@ extern "C" std::uintptr_t __stdcall start_runtime_and_get_target_address(std::ui
{
trace::setup();
error_writer_scope_t writer_scope(swallow_trace);
-
+
bootstrap_thunk *pThunk = bootstrap_thunk::get_thunk_from_cookie(cookie);
load_in_memory_assembly_fn loadInMemoryAssembly;
pal::dll_t moduleHandle = pThunk->get_dll_handle();
@@ -128,7 +128,7 @@ extern "C" std::uintptr_t __stdcall start_runtime_and_get_target_address(std::ui
// As we were taken here via an entry point with arbitrary signature,
// there's no way of returning the error code so we just throw it.
- trace::error(_X("Failed to start the .NET runtime. Error code %d"), status);
+ trace::error(_X("Failed to start the .NET runtime. Error code: %#x"), status);
#pragma warning (push)
#pragma warning (disable: 4297)
@@ -145,7 +145,7 @@ extern "C" std::uintptr_t __stdcall start_runtime_and_get_target_address(std::ui
#pragma warning (pop)
}
- loadInMemoryAssembly(moduleHandle, app_path.c_str());
+ loadInMemoryAssembly(moduleHandle, app_path.c_str(), nullptr);
std::uintptr_t thunkAddress = *(pThunk->get_slot_address());