Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElinor Fung <47805090+elinor-fung@users.noreply.github.com>2019-05-02 21:36:50 +0300
committerGitHub <noreply@github.com>2019-05-02 21:36:50 +0300
commitc6f5b9098002f91dc8ee28c653ddfc8b4c9ae03d (patch)
tree3c57f7de54802a1811c9a885307c792168c7d49f /src/installer/corehost/cli/fxr/hostfxr.cpp
parent864191b29b8fa27ad238d3c251a128d450a2a313 (diff)
Add host context-based entry points for native hosting (dotnet/core-setup#5859)
- Track existing hostpolicy_context in hostpolicy - Add host_context to hostfxr to represent active (first) and secondary contexts - Switch com/ijw/winrt hosts to use host context-based APIs - Update nativehost test executable to exercise new APIs - Make non-context-based entry points check for existing context and create an empty context - Basic automated tests for context-based entry points Commit migrated from https://github.com/dotnet/core-setup/commit/4f7fefe81748bcf7fe65ad3492ed2ca22a2b834a
Diffstat (limited to 'src/installer/corehost/cli/fxr/hostfxr.cpp')
-rw-r--r--src/installer/corehost/cli/fxr/hostfxr.cpp517
1 files changed, 456 insertions, 61 deletions
diff --git a/src/installer/corehost/cli/fxr/hostfxr.cpp b/src/installer/corehost/cli/fxr/hostfxr.cpp
index d836eb50f75..a42ed43b239 100644
--- a/src/installer/corehost/cli/fxr/hostfxr.cpp
+++ b/src/installer/corehost/cli/fxr/hostfxr.cpp
@@ -13,12 +13,20 @@
#include "sdk_info.h"
#include "sdk_resolver.h"
#include "hostfxr.h"
+#include "host_context.h"
-SHARED_API int hostfxr_main_startupinfo(const int argc, const pal::char_t* argv[], const pal::char_t* host_path, const pal::char_t* dotnet_root, const pal::char_t* app_path)
+namespace
{
- trace::setup();
+ void trace_hostfxr_entry_point(const pal::char_t *entry_point)
+ {
+ trace::setup();
+ trace::info(_X("--- Invoked %s [commit hash: %s]"), entry_point, _STRINGIFY(REPO_COMMIT_HASH));
+ }
+}
- trace::info(_X("--- Invoked hostfxr v2 [commit hash: %s] main"), _STRINGIFY(REPO_COMMIT_HASH));
+SHARED_API int hostfxr_main_startupinfo(const int argc, const pal::char_t* argv[], const pal::char_t* host_path, const pal::char_t* dotnet_root, const pal::char_t* app_path)
+{
+ trace_hostfxr_entry_point(_X("hostfxr_main_startupinfo"));
host_startup_info_t startup_info(host_path, dotnet_root, app_path);
@@ -27,9 +35,7 @@ SHARED_API int hostfxr_main_startupinfo(const int argc, const pal::char_t* argv[
SHARED_API int hostfxr_main(const int argc, const pal::char_t* argv[])
{
- trace::setup();
-
- trace::info(_X("--- Invoked hostfxr [commit hash: %s] main"), _STRINGIFY(REPO_COMMIT_HASH));
+ trace_hostfxr_entry_point(_X("hostfxr_main"));
host_startup_info_t startup_info;
startup_info.parse(argc, argv);
@@ -51,13 +57,13 @@ SHARED_API int hostfxr_main(const int argc, const pal::char_t* argv[])
// sub-folders. Pass the directory of a dotnet executable to
// mimic how that executable would search in its own directory.
// It is also valid to pass nullptr or empty, in which case
-// multi-level lookup can still search other locations if
+// multi-level lookup can still search other locations if
// it has not been disabled by the user's environment.
//
// working_dir
// The directory where the search for global.json (which can
// control the resolved SDK version) starts and proceeds
-// upwards.
+// upwards.
//
// buffer
// The buffer where the resolved SDK path will be written.
@@ -86,9 +92,7 @@ SHARED_API int32_t hostfxr_resolve_sdk(
pal::char_t buffer[],
int32_t buffer_size)
{
- trace::setup();
-
- trace::info(_X("--- Invoked hostfxr [commit hash: %s] hostfxr_resolve_sdk"), _STRINGIFY(REPO_COMMIT_HASH));
+ trace_hostfxr_entry_point(_X("hostfxr_resolve_sdk"));
if (buffer_size < 0 || (buffer_size > 0 && buffer == nullptr))
{
@@ -157,18 +161,18 @@ typedef void (*hostfxr_resolve_sdk2_result_fn)(
// sub-folders. Pass the directory of a dotnet executable to
// mimic how that executable would search in its own directory.
// It is also valid to pass nullptr or empty, in which case
-// multi-level lookup can still search other locations if
+// multi-level lookup can still search other locations if
// it has not been disabled by the user's environment.
//
// working_dir
// The directory where the search for global.json (which can
// control the resolved SDK version) starts and proceeds
-// upwards.
+// upwards.
//
// flags
// Bitwise flags that influence resolution.
// disallow_prerelease (0x1)
-// do not allow resolution to return a prerelease SDK version
+// do not allow resolution to return a prerelease SDK version
// unless prerelease version was specified via global.json.
//
// result
@@ -191,7 +195,7 @@ typedef void (*hostfxr_resolve_sdk2_result_fn)(
// Return value:
// 0 on success, otherwise failure
// 0x8000809b - SDK could not be resolved (SdkResolverResolveFailure)
-//
+//
// String encoding:
// Windows - UTF-16 (pal::char_t is 2 byte wchar_t)
// Unix - UTF-8 (pal::char_t is 1 byte char)
@@ -202,9 +206,7 @@ SHARED_API int32_t hostfxr_resolve_sdk2(
int32_t flags,
hostfxr_resolve_sdk2_result_fn result)
{
- trace::setup();
-
- trace::info(_X("--- Invoked hostfxr [commit hash: %s] hostfxr_resolve_sdk2"), _STRINGIFY(REPO_COMMIT_HASH));
+ trace_hostfxr_entry_point(_X("hostfxr_resolve_sdk2"));
if (exe_dir == nullptr)
{
@@ -220,7 +222,7 @@ SHARED_API int32_t hostfxr_resolve_sdk2(
pal::string_t global_json_path;
bool success = sdk_resolver_t::resolve_sdk_dotnet_path(
- exe_dir,
+ exe_dir,
working_dir,
&resolved_sdk_dir,
(flags & hostfxr_resolve_sdk2_flags_t::disallow_prerelease) != 0,
@@ -241,7 +243,7 @@ SHARED_API int32_t hostfxr_resolve_sdk2(
}
return success
- ? StatusCode::Success
+ ? StatusCode::Success
: StatusCode::SdkResolverResolveFailure;
}
@@ -276,9 +278,7 @@ SHARED_API int32_t hostfxr_get_available_sdks(
const pal::char_t* exe_dir,
hostfxr_get_available_sdks_result_fn result)
{
- trace::setup();
-
- trace::info(_X("--- Invoked hostfxr [commit hash: %s] hostfxr_get_available_sdks"), _STRINGIFY(REPO_COMMIT_HASH));
+ trace_hostfxr_entry_point(_X("hostfxr_get_available_sdks"));
if (exe_dir == nullptr)
{
@@ -304,7 +304,7 @@ SHARED_API int32_t hostfxr_get_available_sdks(
result(sdk_dirs.size(), &sdk_dirs[0]);
}
-
+
return StatusCode::Success;
}
@@ -350,9 +350,7 @@ SHARED_API int32_t hostfxr_get_available_sdks(
//
SHARED_API int32_t hostfxr_get_native_search_directories(const int argc, const pal::char_t* argv[], pal::char_t buffer[], int32_t buffer_size, int32_t* required_buffer_size)
{
- trace::setup();
-
- trace::info(_X("--- Invoked hostfxr_get_native_search_directories [commit hash: %s] main"), _STRINGIFY(REPO_COMMIT_HASH));
+ trace_hostfxr_entry_point(_X("hostfxr_get_native_search_directories"));
if (buffer_size < 0 || (buffer_size > 0 && buffer == nullptr) || required_buffer_size == nullptr)
{
@@ -373,18 +371,18 @@ typedef void(*hostfxr_error_writer_fn)(const pal::char_t* message);
// Sets a callback which is to be used to write errors to.
//
// Parameters:
-// error_writer
+// error_writer
// A callback function which will be invoked every time an error is to be reported.
// Or nullptr to unregister previously registered callback and return to the default behavior.
// Return value:
// The previously registered callback (which is now unregistered), or nullptr if no previous callback
// was registered
-//
+//
// The error writer is registered per-thread, so the registration is thread-local. On each thread
// only one callback can be registered. Subsequent registrations overwrite the previous ones.
-//
+//
// By default no callback is registered in which case the errors are written to stderr.
-//
+//
// Each call to the error writer is sort of like writing a single line (the EOL character is omitted).
// Multiple calls to the error writer may occure for one failure.
//
@@ -397,55 +395,452 @@ SHARED_API hostfxr_error_writer_fn hostfxr_set_error_writer(hostfxr_error_writer
return trace::set_error_writer(error_writer);
}
-coreclr_delegate_type hostfxr_delegate_to_coreclr_delegate(hostfxr_delegate_type type)
+namespace
{
- switch (type)
+ int populate_startup_info(const hostfxr_initialize_parameters *parameters, host_startup_info_t &startup_info)
{
- case hostfxr_delegate_type::com_activation:
- return coreclr_delegate_type::com_activation;
- case hostfxr_delegate_type::load_in_memory_assembly:
- return coreclr_delegate_type::load_in_memory_assembly;
- case hostfxr_delegate_type::winrt_activation:
- return coreclr_delegate_type::winrt_activation;
+ if (parameters != nullptr)
+ {
+ if (parameters->host_path != nullptr)
+ startup_info.host_path = parameters->host_path;
+
+ if (parameters->dotnet_root != nullptr)
+ startup_info.dotnet_root = parameters->dotnet_root;
+ }
+
+ if (startup_info.host_path.empty())
+ {
+ if (!pal::get_own_executable_path(&startup_info.host_path) || !pal::realpath(&startup_info.host_path))
+ {
+ trace::error(_X("Failed to resolve full path of the current host [%s]"), startup_info.host_path.c_str());
+ return StatusCode::CoreHostCurHostFindFailure;
+ }
+ }
+
+ if (startup_info.dotnet_root.empty())
+ {
+ pal::string_t mod_path;
+ if (!pal::get_own_module_path(&mod_path))
+ return StatusCode::CoreHostCurHostFindFailure;
+
+ startup_info.dotnet_root = get_dotnet_root_from_fxr_path(mod_path);
+ if (!pal::realpath(&startup_info.dotnet_root))
+ {
+ trace::error(_X("Failed to resolve full path of dotnet root [%s]"), startup_info.dotnet_root.c_str());
+ return StatusCode::CoreHostCurHostFindFailure;
+ }
+ }
+
+ return StatusCode::Success;
+ }
+}
+
+//
+// Initializes the hosting components for running an application
+//
+// Parameters:
+// argc
+// Number of argv arguments
+// argv
+// Arguments for the application. If argc is 0, this is ignored.
+// app_path
+// Path to the managed application. If this is nullptr, the first argument in argv is assumed to be
+// the application path.
+// parameters
+// Optional. Additional parameters for initialization
+// host_context_handle
+// On success, this will be populated with an opaque value representing the initalized host context
+//
+// Return value:
+// Success - Hosting components were successfully initialized
+// HostInvalidState - Hosting components are already initialized
+//
+// The app_path will be used to find the corresponding .runtimeconfig.json and .deps.json with which to
+// resolve frameworks and dependencies and prepare everything needed to load the runtime.
+//
+// This function does not load the runtime.
+//
+SHARED_API int32_t __cdecl hostfxr_initialize_for_app(
+ int argc,
+ const pal::char_t *argv[],
+ const pal::char_t *app_path,
+ const hostfxr_initialize_parameters * parameters,
+ /*out*/ hostfxr_handle * host_context_handle)
+{
+ trace_hostfxr_entry_point(_X("hostfxr_initialize_for_app"));
+
+ if (host_context_handle == nullptr || (argv == nullptr && argc != 0) || (app_path == nullptr && argc == 0))
+ return StatusCode::InvalidArgFailure;
+
+ *host_context_handle = nullptr;
+
+ host_startup_info_t startup_info{};
+ int new_argc;
+ const pal::char_t **new_argv;
+ if (app_path != nullptr)
+ {
+ startup_info.app_path = app_path;
+ new_argc = argc;
+ new_argv = argv;
+ }
+ else
+ {
+ // Take the first argument as the app path
+ startup_info.app_path = argv[0];
+ new_argc = argc-1;
+ new_argv = argc > 0 ? &argv[1] : nullptr;
+ }
+
+ int rc = populate_startup_info(parameters, startup_info);
+ if (rc != StatusCode::Success)
+ return rc;
+
+ return fx_muxer_t::initialize_for_app(
+ startup_info,
+ new_argc,
+ new_argv,
+ host_context_handle);
+}
+
+//
+// Initializes the hosting components using a .runtimeconfig.json file
+//
+// Parameters:
+// runtime_config_path
+// Path to the .runtimeconfig.json file
+// parameters
+// Optional. Additional parameters for initialization
+// host_context_handle
+// On success, this will be populated with an opaque value representing the initalized host context
+//
+// Return value:
+// Success - Hosting components were successfully initialized
+// CoreHostAlreadyInitialized - Config is compatible with already initialized hosting components
+// [TODO]
+// CoreHostIncompatibleConfig - Config is incompatible with already initialized hosting components
+// CoreHostDifferentProperties - Config has runtime properties that differ from already initialized hosting components
+//
+// This function will process the .runtimeconfig.json to resolve frameworks and prepare everything needed
+// to load the runtime. It will only process the .deps.json from frameworks (not any app/component that
+// may be next to the .runtimeconfig.json).
+//
+// This function does not load the runtime.
+//
+// If called when the runtime has already been loaded, this function will check if the specified runtime
+// config is compatible with the existing runtime.
+//
+SHARED_API int32_t __cdecl hostfxr_initialize_for_runtime_config(
+ const pal::char_t *runtime_config_path,
+ const hostfxr_initialize_parameters *parameters,
+ /*out*/ hostfxr_handle *host_context_handle)
+{
+ trace_hostfxr_entry_point(_X("hostfxr_initialize_for_runtime_config"));
+
+ if (runtime_config_path == nullptr || host_context_handle == nullptr)
+ return StatusCode::InvalidArgFailure;
+
+ *host_context_handle = nullptr;
+
+ host_startup_info_t startup_info{};
+ int rc = populate_startup_info(parameters, startup_info);
+ if (rc != StatusCode::Success)
+ return rc;
+
+ return fx_muxer_t::initialize_for_runtime_config(
+ startup_info,
+ runtime_config_path,
+ host_context_handle);
+}
+
+//
+// Load CoreCLR and run the application for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+//
+// Return value:
+// If the app was successfully run, the exit code of the application. Otherwise, the error code result.
+//
+// The host_context_handle must have been initialized using hostfxr_initialize_for_app.
+//
+// This function will not return until the managed application exits.
+//
+SHARED_API int32_t __cdecl hostfxr_run_app(const hostfxr_handle host_context_handle)
+{
+ trace_hostfxr_entry_point(_X("hostfxr_run_app"));
+
+ host_context_t *context = host_context_t::from_handle(host_context_handle);
+ if (context == nullptr)
+ return StatusCode::InvalidArgFailure;
+
+ return fx_muxer_t::run_app(context);
+}
+
+namespace
+{
+ coreclr_delegate_type hostfxr_delegate_to_coreclr_delegate(hostfxr_delegate_type type)
+ {
+ switch (type)
+ {
+ case hostfxr_delegate_type::com_activation:
+ return coreclr_delegate_type::com_activation;
+ case hostfxr_delegate_type::load_in_memory_assembly:
+ return coreclr_delegate_type::load_in_memory_assembly;
+ case hostfxr_delegate_type::winrt_activation:
+ return coreclr_delegate_type::winrt_activation;
+ }
+ return coreclr_delegate_type::invalid;
}
- return coreclr_delegate_type::invalid;
}
//
// Gets a typed delegate from the currently loaded CoreCLR or from a newly created one.
//
// Parameters:
-// libhost_path
-// Absolute path of the entry hosting library
-// dotnet_root
-// app_path
+// host_context_handle
+// Handle to the initialized host context
+// type
+// Type of runtime delegate requested
// delegate
-// An out parameter that will be assigned the delegate.
+// An out parameter that will be assigned the delegate.
+//
// Return value:
// The error code result.
//
-// A new CoreCLR instance will be created or reused if the existing instance can satisfy the configuration
-// requirements supplied by the runtimeconfig.json file.
+// The host_context_handle must have been initialized using hostfxr_initialize_for_runtime_config.
//
-SHARED_API int32_t hostfxr_get_runtime_delegate(
- const pal::char_t* host_path,
- const pal::char_t* dotnet_root,
- const pal::char_t* app_path,
+SHARED_API int32_t __cdecl hostfxr_get_runtime_delegate(
+ const hostfxr_handle host_context_handle,
hostfxr_delegate_type type,
- void** delegate)
+ /*out*/ void **delegate)
{
- if (host_path == nullptr || dotnet_root == nullptr || delegate == nullptr)
+ trace_hostfxr_entry_point(_X("hostfxr_get_runtime_delegate"));
+
+ if (delegate == nullptr)
return StatusCode::InvalidArgFailure;
- trace::setup();
+ *delegate = nullptr;
- trace::info(_X("--- Invoked hostfxr_get_runtime_delegate [commit hash: %s]"), _STRINGIFY(REPO_COMMIT_HASH));
+ host_context_t *context = host_context_t::from_handle(host_context_handle);
+ if (context == nullptr)
+ return StatusCode::InvalidArgFailure;
- host_startup_info_t startup_info{ host_path, dotnet_root, app_path };
+ return fx_muxer_t::get_runtime_delegate(context, hostfxr_delegate_to_coreclr_delegate(type), delegate);
+}
- return fx_muxer_t::load_runtime_and_get_delegate(
- startup_info,
- host_mode_t::libhost,
- hostfxr_delegate_to_coreclr_delegate(type),
- delegate);
+//
+// Gets the runtime property value for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// name
+// Runtime property name
+// value
+// Out parameter. Pointer to a buffer with the property value.
+//
+// Return value:
+// The error code result.
+//
+// The buffer pointed to by value is owned by the host context. The lifetime of the buffer is only
+// guaranteed until any of the below occur:
+// - a 'run' method is called for the host context
+// - properties are changed via hostfxr_set_runtime_property_value
+// - the host context is closed via 'hostfxr_close'
+//
+// If host_context_handle is nullptr and an active host context exists, this function will get the
+// property value for the active host context.
+//
+SHARED_API int32_t __cdecl hostfxr_get_runtime_property_value(
+ const hostfxr_handle host_context_handle,
+ const pal::char_t *name,
+ /*out*/ const pal::char_t **value)
+{
+ trace_hostfxr_entry_point(_X("hostfxr_get_runtime_property_value"));
+
+ if (name == nullptr || value == nullptr)
+ return StatusCode::InvalidArgFailure;
+
+ const host_context_t *context;
+ if (host_context_handle == nullptr)
+ {
+ const host_context_t *context_maybe = fx_muxer_t::get_active_host_context();
+ if (context_maybe == nullptr || context_maybe->type != host_context_type::active)
+ {
+ trace::error(_X("Hosting components context has not been initialized. Cannot get runtime properties."));
+ return StatusCode::HostInvalidState;
+ }
+
+ context = context_maybe;
+ }
+ else
+ {
+ context = host_context_t::from_handle(host_context_handle);
+ if (context == nullptr)
+ return StatusCode::InvalidArgFailure;
+ }
+
+
+ if (context->type == host_context_type::secondary)
+ {
+ const std::unordered_map<pal::string_t, pal::string_t> &properties = context->config_properties;
+ auto iter = properties.find(name);
+ if (iter == properties.cend())
+ return StatusCode::HostPropertyNotFound;
+
+ *value = (*iter).second.c_str();
+ return StatusCode::Success;
+ }
+
+ assert(context->type == host_context_type::initialized || context->type == host_context_type::active);
+ const corehost_context_contract &contract = context->hostpolicy_context_contract;
+ return contract.get_property_value(name, value);
+}
+
+//
+// Sets the value of a runtime property for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// name
+// Runtime property name
+// value
+// Value to set
+//
+// Return value:
+// The error code result.
+//
+// Setting properties is only supported for the first host context, before the runtime has been loaded.
+//
+// If the property already exists in the host context, it will be overwritten. If value is nullptr, the
+// property will be removed.
+//
+SHARED_API int32_t __cdecl hostfxr_set_runtime_property_value(
+ const hostfxr_handle host_context_handle,
+ const pal::char_t *name,
+ const pal::char_t *value)
+{
+ trace_hostfxr_entry_point(_X("hostfxr_set_runtime_property_value"));
+
+ if (name == nullptr)
+ return StatusCode::InvalidArgFailure;
+
+ host_context_t *context = host_context_t::from_handle(host_context_handle);
+ if (context == nullptr)
+ return StatusCode::InvalidArgFailure;
+
+ if (context->type != host_context_type::initialized)
+ {
+ trace::error(_X("Setting properties is not allowed once runtime has been loaded."));
+ return StatusCode::InvalidArgFailure;
+ }
+
+ const corehost_context_contract &contract = context->hostpolicy_context_contract;
+ return contract.set_property_value(name, value);
+}
+
+//
+// Gets all the runtime properties for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// count
+// [in] Size of the keys and values buffers
+// [out] Number of properties returned (size of keys/values buffers used). If the input value is too
+// small or keys/values is nullptr, this is populated with the number of available properties
+// keys
+// Array of pointers to buffers with runtime property keys
+// values
+// Array of pointers to buffers with runtime property values
+//
+// Return value:
+// The error code result.
+//
+// The buffers pointed to by keys and values are owned by the host context. The lifetime of the buffers is only
+// guaranteed until any of the below occur:
+// - a 'run' method is called for the host context
+// - properties are changed via hostfxr_set_runtime_property_value
+// - the host context is closed via 'hostfxr_close'
+//
+// If host_context_handle is nullptr and an active host context exists, this function will get the
+// properties for the active host context.
+//
+SHARED_API int32_t __cdecl hostfxr_get_runtime_properties(
+ const hostfxr_handle host_context_handle,
+ /*inout*/ size_t * count,
+ /*out*/ const pal::char_t **keys,
+ /*out*/ const pal::char_t **values)
+{
+ trace_hostfxr_entry_point(_X("hostfxr_get_runtime_properties"));
+
+ if (count == nullptr)
+ return StatusCode::InvalidArgFailure;
+
+ const host_context_t *context;
+ if (host_context_handle == nullptr)
+ {
+ const host_context_t *context_maybe = fx_muxer_t::get_active_host_context();
+ if (context_maybe == nullptr || context_maybe->type != host_context_type::active)
+ {
+ trace::error(_X("Hosting components context has not been initialized. Cannot get runtime properties."));
+ return StatusCode::HostInvalidState;
+ }
+
+ context = context_maybe;
+ }
+ else
+ {
+ context = host_context_t::from_handle(host_context_handle);
+ if (context == nullptr)
+ return StatusCode::InvalidArgFailure;
+ }
+
+ if (context->type == host_context_type::secondary)
+ {
+ const std::unordered_map<pal::string_t, pal::string_t> &properties = context->config_properties;
+ size_t actualCount = properties.size();
+ size_t input_count = *count;
+ *count = actualCount;
+ if (input_count < actualCount || keys == nullptr || values == nullptr)
+ return StatusCode::HostApiBufferTooSmall;
+
+ int i = 0;
+ for (const auto& kv : properties)
+ {
+ keys[i] = kv.first.data();
+ values[i] = kv.second.data();
+ ++i;
+ }
+
+ return StatusCode::Success;
+ }
+
+ assert(context->type == host_context_type::initialized || context->type == host_context_type::active);
+ const corehost_context_contract &contract = context->hostpolicy_context_contract;
+ return contract.get_properties(count, keys, values);
}
+
+//
+// Closes an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+//
+// Return value:
+// The error code result.
+//
+SHARED_API int32_t __cdecl hostfxr_close(const hostfxr_handle host_context_handle)
+{
+ trace_hostfxr_entry_point(_X("hostfxr_close"));
+
+ // Allow contexts with a type of invalid as we still need to clean them up
+ host_context_t *context = host_context_t::from_handle(host_context_handle, /*allow_invalid_type*/ true);
+ if (context == nullptr)
+ return StatusCode::InvalidArgFailure;
+
+ return fx_muxer_t::close_host_context(context);
+} \ No newline at end of file