diff --git a/Documentation/design-docs/WinRT-activation.md b/Documentation/design-docs/WinRT-activation.md new file mode 100644 index 0000000000..e8a72d5dd9 --- /dev/null +++ b/Documentation/design-docs/WinRT-activation.md @@ -0,0 +1,53 @@ +# Managed WinRT Activation of .NET Core components + +As part of supporting a complete story for XAML Islands on .NET Core, we should provide a mechanism of activating .NET Core WinRT components. To do so, we will follow the path of the COM and IJW activations and provide a new host for activating these components in a manner similar to native WinRT components, which we will call the `winrthost`. We are creating a separate host instead of combining with the COM host because the COM and WinRT hosts (although generally similar in design in Windows) have very different activation and resolution paths. As a result, we would not be able to reuse much code at all. Additionally, the WinRT host does not require a `clsidmap` or similar functionality to function. If we were to combine the two hosts, we would have to emit an empty clsidmap when creating the host and modify it even though the WinRT portion has no dependencies on any embedded resources. + +## Requirements + +* Discover all installed versions of .NET Core. +* Load the appropriate version of .NET Core for the class if a .NET Core instance is not running, or validate the currently existing .NET Core instance can satisfy the class requirement. +* Return an [`IActivationFactory`](https://docs.microsoft.com/windows/desktop/api/activation/nn-activation-iactivationfactory) implementation that will construct an instance of the .NET WinRT class. + +## Native WinRT Activation + +In the native (C++/CX, C++/WRL, or C++/WinRT) world, building a WinRT Component named `MyComponent` produces files named `MyComponent.dll`, `MyComponent.winmd`. In this world, the `winmd` contains the metadata describing the types provided by the component, and the code implementing said types is compiled into the `dll`. When an application wants to activate a component, it will call [`RoGetActivationFactory`](https://docs.microsoft.com/windows/desktop/api/roapi/nf-roapi-rogetactivationfactory). The operating system will then search through various areas in the filesystem to find the correct component for the class name. Starting in Windows 10 19H1, there is now support for declaring that specific classes come from components that live side-by-side with the application, similar to Reg-Free COM. After finding the correct component, the OS will load the component via `CoLoadLibrary` and then call the `DllGetActivationFactory` entrypoint, which gets an activation factory for the class by name. + +## Proposed Managed WinRT Activation + +In the managed (.NET) world, we put all of the code that implements the component into the `winmd`. So, when running in an AppContainer/`.appx`, a .NET component only needs one file. However, when outside of an AppContainer, there needs to be some sort of host to activate runtime. We will supply a host that implements the [`DllGetActivationFactory`](https://docs.microsoft.com/previous-versions//br205771(v=vs.85)) entrypoint. When `DllGetActivationFactory` is called, the following will occur: + +1) If a [`.runtimeconfig.json`](https://github.com/dotnet/cli/blob/master/Documentation/specs/runtime-configuration-file.md) file exists adjacent to the shim assembly (`.runtimeconfig.json`), that file will be used to describe CLR configuration details. The documentation for the `.runtimeconfig.json` format defines under what circumstances this file may be optional. +2) Using the existing `hostfxr` library, attempt to discover the desired CLR and target [framework](https://docs.microsoft.com/en-us/dotnet/core/packages#frameworks). + * If a CLR is active with the process, the requested CLR version will be validated against that CLR. If version satisfiability fails, activation will fail. + * If a CLR is **not** active with the process, an attempt will be made to create a satisfying CLR instance. + * Failure to create an instance will result in activation failure. +3) A request to the CLR will be made to load the corresponding WinRT type for the given type name into the runtime. + * This request will use the runtime's current support for resolving WinRT types to correctly resolve them. + * The ability to load an assembly from memory will require exposing a new function that can be called from `hostfxr`, as well as a new API in `System.Private.CoreLib` on a new class in `Internal.Runtime.InteropServices`: + +```csharp +namespace Internal.Runtime.InteropServices.WindowsRuntime +{ + public static class ActivationFactoryLoader + { + public unsafe static int GetActivationFactory( + char* componentPath, + [MarshalAs(UnmanagedType.HString)] string typeName, + [MarshalAs(UnmanagedType.Interface)] out IActivationFactory activationFactory); + } +} +``` + +Note this API would not be exposed outside of `System.Private.CoreLib` unless we decide to do so. The runtime loads WinRT components via a different binder than .NET assemblies, so the WinRT component assemblies themselves will be loaded by the WinRT binder, but all assemblies that the WinRT component depends on will be loaded into an isolated `AssemblyLoadContext`. + +To match the user-visible architecture of native WinRT Activation, when building a Windows Metadata component (`winmdobj`), we will copy the `winrthost` to the user's output directory and rename it to be the same name as the `.winmd` but with a `.dll` extension. For example, if the user creates a project `MyComponent` and is building a Windows Metadata component, we will copy the `winrthost` to the output directory for `MyComponent` and rename it to be `MyComponent.dll`. From a user's perspective, they will activate WinRT objects via `MyComponent.dll`, the same as if the component was a native WinRT component. + +## Error reporting + +If the runtime activation fails, we want to ensure that the user can diagnose the failure. However, we don't want to write directly to the `stderr` of a process that we don't own. So, we will redirect the trace stream to point to a local stream. In the case of failure to activate the runtime, we will report the error code via [`RoOriginateErrorW`](https://docs.microsoft.com/windows/desktop/api/roerrorapi/nf-roerrorapi-rooriginateerrorw), which will present the user with the error according to the error reporting flags they have set via [`RoSetErrorReportingFlags`](https://docs.microsoft.com/windows/desktop/api/roerrorapi/nf-roerrorapi-roseterrorreportingflags). + +## Open issues + +* The tool that converts the `.winmdobj` to a `.winmd`, WinMDExp, is only available in Desktop MSBuild and is not available in the .NET Core MSBuild distribution. Additionally, WinMDExp only supports full PDBs and will fail if given portable PDBs. +* To build a `.winmdobj`, the user will need to reference a contract assembly that includes the `Windows.Foundation` namespace. There are new packages that expose these contract assemblies, but we should ensure that these contract assemblies will be released by the time that we finalize our Managed WinRT Component support. +* Writing unit tests for the WinRT host without modifying system-global state requires the new Reg-Free WinRT support, which is Windows 10 19H1 only. diff --git a/signing/sign.proj b/signing/sign.proj index 170fd30c31..495a9a8cc9 100644 --- a/signing/sign.proj +++ b/signing/sign.proj @@ -40,6 +40,9 @@ $(CertificateId) + + $(CertificateId) + $(CertificateId) diff --git a/src/corehost/build.proj b/src/corehost/build.proj index 260a712711..b3c59da37e 100644 --- a/src/corehost/build.proj +++ b/src/corehost/build.proj @@ -51,6 +51,9 @@ .NET Core IJW Host + + .NET Core WinRT Host + .NET Core Component Host diff --git a/src/corehost/cli/CMakeLists.txt b/src/corehost/cli/CMakeLists.txt index 4d307fc080..0ed5b8ab32 100644 --- a/src/corehost/cli/CMakeLists.txt +++ b/src/corehost/cli/CMakeLists.txt @@ -10,4 +10,5 @@ add_subdirectory(test) if(WIN32) add_subdirectory(comhost) add_subdirectory(ijwhost) + add_subdirectory(winrthost) endif() diff --git a/src/corehost/cli/comhost/CMakeLists.txt b/src/corehost/cli/comhost/CMakeLists.txt index c80db1e2e6..44c84a6a16 100644 --- a/src/corehost/cli/comhost/CMakeLists.txt +++ b/src/corehost/cli/comhost/CMakeLists.txt @@ -16,6 +16,7 @@ set(SOURCES comhost.cpp ../fxr_resolver.cpp clsidmap.cpp + ../redirected_error_writer.cpp ../fxr/fx_ver.cpp ../json/casablanca/src/json/json.cpp ../json/casablanca/src/json/json_parsing.cpp diff --git a/src/corehost/cli/comhost/comhost.cpp b/src/corehost/cli/comhost/comhost.cpp index 0798b18ddf..6ec2342921 100644 --- a/src/corehost/cli/comhost/comhost.cpp +++ b/src/corehost/cli/comhost/comhost.cpp @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. #include "comhost.h" +#include "redirected_error_writer.h" #include "hostfxr.h" #include "fxr_resolver.h" #include "pal.h" @@ -45,69 +46,48 @@ using com_activation_fn = int(*)(com_activation_context*); namespace { - pal::stringstream_t & get_comhost_error_stream() + int get_com_activation_delegate(pal::string_t *app_path, com_activation_fn *delegate) { - thread_local static pal::stringstream_t comhost_errors; + return load_fxr_and_get_delegate( + hostfxr_delegate_type::com_activation, + [](const pal::string_t& host_path, pal::string_t* app_path_out) + { + pal::string_t app_path_local{ host_path }; - return comhost_errors; - } + // Strip the comhost suffix to get the 'app' + size_t idx = app_path_local.rfind(_X(".comhost.dll")); + assert(idx != pal::string_t::npos); + app_path_local.replace(app_path_local.begin() + idx, app_path_local.end(), _X(".dll")); - void reset_comhost_error_stream() - { - pal::stringstream_t newstream; - get_comhost_error_stream().swap(newstream); - } + *app_path_out = std::move(app_path_local); - void comhost_error_writer(const pal::char_t* msg) - { - get_comhost_error_stream() << msg; + return StatusCode::Success; + }, + delegate, + app_path + ); } - - int get_com_activation_delegate(pal::string_t *app_path, com_activation_fn *delegate) + + void report_com_error_info(const GUID& guid, pal::string_t errs) { - pal::string_t host_path; - if (!pal::get_own_module_path(&host_path) || !pal::realpath(&host_path)) - { - trace::error(_X("Failed to resolve full path of the current host module [%s]"), host_path.c_str()); - return StatusCode::CoreHostCurHostFindFailure; - } - - pal::string_t dotnet_root; - pal::string_t fxr_path; - if (!fxr_resolver::try_get_path(get_directory(host_path), &dotnet_root, &fxr_path)) + ICreateErrorInfo *cei; + if (!errs.empty() && SUCCEEDED(::CreateErrorInfo(&cei))) { - return StatusCode::CoreHostLibMissingFailure; - } + if (SUCCEEDED(cei->SetGUID(guid))) + { + if (SUCCEEDED(cei->SetDescription((LPOLESTR)errs.c_str()))) + { + IErrorInfo *ei; + if (SUCCEEDED(cei->QueryInterface(__uuidof(ei), (void**)&ei))) + { + ::SetErrorInfo(0, ei); + ei->Release(); + } + } + } - // Load library - pal::dll_t fxr; - if (!pal::load_library(&fxr_path, &fxr)) - { - trace::error(_X("The library %s was found, but loading it from %s failed"), LIBFXR_NAME, fxr_path.c_str()); - trace::error(_X(" - Installing .NET Core prerequisites might help resolve this problem.")); - trace::error(_X(" %s"), DOTNET_CORE_INSTALL_PREREQUISITES_URL); - return StatusCode::CoreHostLibLoadFailure; + cei->Release(); } - - // Leak fxr - - auto get_runtime_delegate = (hostfxr_get_delegate_fn)pal::get_symbol(fxr, "hostfxr_get_runtime_delegate"); - if (get_runtime_delegate == nullptr) - return StatusCode::CoreHostEntryPointFailure; - - pal::string_t app_path_local{ host_path }; - - // Strip the comhost suffix to get the 'app' - size_t idx = app_path_local.rfind(_X(".comhost.dll")); - assert(idx != pal::string_t::npos); - app_path_local.replace(app_path_local.begin() + idx, app_path_local.end(), _X(".dll")); - - *app_path = std::move(app_path_local); - - auto set_error_writer_fn = (hostfxr_set_error_writer_fn)pal::get_symbol(fxr, "hostfxr_set_error_writer"); - propagate_error_writer_t propagate_error_writer_to_hostfxr(set_error_writer_fn); - - return get_runtime_delegate(host_path.c_str(), dotnet_root.c_str(), app_path->c_str(), hostfxr_delegate_type::com_activation, (void**)delegate); } } @@ -129,35 +109,14 @@ COM_API HRESULT STDMETHODCALLTYPE DllGetClassObject( com_activation_fn act; { trace::setup(); - reset_comhost_error_stream(); - - error_writer_scope_t writer_scope(comhost_error_writer); + reset_redirected_error_writer(); + + error_writer_scope_t writer_scope(redirected_error_writer); int ec = get_com_activation_delegate(&app_path, &act); if (ec != StatusCode::Success) { - // Create an IErrorInfo instance with the failure data. - pal::string_t errs = get_comhost_error_stream().str(); - - ICreateErrorInfo *cei; - if (!errs.empty() && SUCCEEDED(::CreateErrorInfo(&cei))) - { - if (SUCCEEDED(cei->SetGUID(rclsid))) - { - if (SUCCEEDED(cei->SetDescription((LPOLESTR)errs.c_str()))) - { - IErrorInfo *ei; - if (SUCCEEDED(cei->QueryInterface(__uuidof(ei), (void**)&ei))) - { - ::SetErrorInfo(0, ei); - ei->Release(); - } - } - } - - cei->Release(); - } - + report_com_error_info(rclsid, std::move(get_redirected_error_string())); return __HRESULT_FROM_WIN32(ec); } } diff --git a/src/corehost/cli/fxr/fx_muxer.h b/src/corehost/cli/fxr/fx_muxer.h index 37f1825c6a..a2a52c09d6 100644 --- a/src/corehost/cli/fxr/fx_muxer.h +++ b/src/corehost/cli/fxr/fx_muxer.h @@ -17,7 +17,8 @@ enum class coreclr_delegate_type { invalid, com_activation, - load_in_memory_assembly + load_in_memory_assembly, + winrt_activation }; class fx_muxer_t diff --git a/src/corehost/cli/fxr/hostfxr.cpp b/src/corehost/cli/fxr/hostfxr.cpp index c807304588..3b54d6bbe5 100644 --- a/src/corehost/cli/fxr/hostfxr.cpp +++ b/src/corehost/cli/fxr/hostfxr.cpp @@ -404,6 +404,8 @@ coreclr_delegate_type hostfxr_delegate_to_coreclr_delegate(hostfxr_delegate_type 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; } diff --git a/src/corehost/cli/fxr_resolver.h b/src/corehost/cli/fxr_resolver.h index 30dfc29f85..3fd4ce18b8 100644 --- a/src/corehost/cli/fxr_resolver.h +++ b/src/corehost/cli/fxr_resolver.h @@ -6,10 +6,62 @@ #define _COREHOST_CLI_FXR_RESOLVER_H_ #include +#include "hostfxr.h" +#include "trace.h" +#include "utils.h" +#include "error_codes.h" namespace fxr_resolver { bool try_get_path(const pal::string_t& root_path, pal::string_t* out_dotnet_root, pal::string_t* out_fxr_path); } +template +int load_fxr_and_get_delegate(hostfxr_delegate_type type, THostNameToAppNameCallback host_path_to_app_path, TDelegate* delegate, pal::string_t* out_app_path = nullptr) +{ + pal::dll_t fxr; + + pal::string_t host_path; + if (!pal::get_own_module_path(&host_path) || !pal::realpath(&host_path)) + { + trace::error(_X("Failed to resolve full path of the current host module [%s]"), host_path.c_str()); + return StatusCode::CoreHostCurHostFindFailure; + } + + pal::string_t dotnet_root; + pal::string_t fxr_path; + if (!fxr_resolver::try_get_path(get_directory(host_path), &dotnet_root, &fxr_path)) + { + return StatusCode::CoreHostLibMissingFailure; + } + + // Load library + if (!pal::load_library(&fxr_path, &fxr)) + { + trace::error(_X("The library %s was found, but loading it from %s failed"), LIBFXR_NAME, fxr_path.c_str()); + trace::error(_X(" - Installing .NET Core prerequisites might help resolve this problem.")); + trace::error(_X(" %s"), DOTNET_CORE_INSTALL_PREREQUISITES_URL); + return StatusCode::CoreHostLibLoadFailure; + } + + // Leak fxr + + auto get_delegate_from_hostfxr = (hostfxr_get_delegate_fn)pal::get_symbol(fxr, "hostfxr_get_runtime_delegate"); + if (get_delegate_from_hostfxr == nullptr) + return StatusCode::CoreHostEntryPointFailure; + + pal::string_t app_path; + + pal::string_t* app_path_to_use = out_app_path != nullptr ? out_app_path : &app_path; + + pal::hresult_t status = host_path_to_app_path(host_path, app_path_to_use); + + if (status != StatusCode::Success) + { + return status; + } + + return get_delegate_from_hostfxr(host_path.c_str(), dotnet_root.c_str(), app_path_to_use->c_str(), type, (void**)delegate); +} + #endif //_COREHOST_CLI_FXR_RESOLVER_H_ diff --git a/src/corehost/cli/hostfxr.h b/src/corehost/cli/hostfxr.h index aa57d50a5e..759a322898 100644 --- a/src/corehost/cli/hostfxr.h +++ b/src/corehost/cli/hostfxr.h @@ -19,7 +19,8 @@ using hostfxr_main_startupinfo_fn = int32_t(*)( enum class hostfxr_delegate_type { com_activation, - load_in_memory_assembly + load_in_memory_assembly, + winrt_activation }; using hostfxr_get_delegate_fn = int32_t(*)( diff --git a/src/corehost/cli/hostpolicy/hostpolicy.cpp b/src/corehost/cli/hostpolicy/hostpolicy.cpp index 5990c694ef..5c138d8798 100644 --- a/src/corehost/cli/hostpolicy/hostpolicy.cpp +++ b/src/corehost/cli/hostpolicy/hostpolicy.cpp @@ -587,6 +587,13 @@ SHARED_API int corehost_get_coreclr_delegate(coreclr_delegate_type type, void** "Internal.Runtime.InteropServices.InMemoryAssemblyLoader", "LoadInMemoryAssembly", delegate); + case coreclr_delegate_type::winrt_activation: + return coreclr->create_delegate( + "System.Private.CoreLib", + "Internal.Runtime.InteropServices.WindowsRuntime.ActivationFactoryLoader", + "GetActivationFactory", + delegate + ); default: return StatusCode::LibHostInvalidArgs; } diff --git a/src/corehost/cli/ijwhost/ijwhost.cpp b/src/corehost/cli/ijwhost/ijwhost.cpp index 18943424d1..d2a5728959 100644 --- a/src/corehost/cli/ijwhost/ijwhost.cpp +++ b/src/corehost/cli/ijwhost/ijwhost.cpp @@ -23,46 +23,19 @@ pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_memory_assembly_fn* delegate) { - pal::dll_t fxr; - - pal::string_t host_path; - if (!pal::get_own_module_path(&host_path) || !pal::realpath(&host_path)) - { - trace::error(_X("Failed to resolve full path of the current host module [%s]"), host_path.c_str()); - return StatusCode::CoreHostCurHostFindFailure; - } - - pal::string_t dotnet_root; - pal::string_t fxr_path; - if (!fxr_resolver::try_get_path(get_directory(host_path), &dotnet_root, &fxr_path)) - { - return StatusCode::CoreHostLibMissingFailure; - } - - // Load library - if (!pal::load_library(&fxr_path, &fxr)) - { - trace::error(_X("The library %s was found, but loading it from %s failed"), LIBFXR_NAME, fxr_path.c_str()); - trace::error(_X(" - Installing .NET Core prerequisites might help resolve this problem.")); - trace::error(_X(" %s"), DOTNET_CORE_INSTALL_PREREQUISITES_URL); - return StatusCode::CoreHostLibLoadFailure; - } - - // Leak fxr - - auto get_delegate_from_hostfxr = (hostfxr_get_delegate_fn)pal::get_symbol(fxr, "hostfxr_get_runtime_delegate"); - if (get_delegate_from_hostfxr == nullptr) - return StatusCode::CoreHostEntryPointFailure; - - pal::string_t app_path; - - if (!pal::get_module_path(handle, &app_path)) - { - trace::error(_X("Failed to resolve full path of the current mixed-mode module [%s]"), host_path.c_str()); - return StatusCode::LibHostCurExeFindFailure; - } - - return get_delegate_from_hostfxr(host_path.c_str(), dotnet_root.c_str(), app_path.c_str(), hostfxr_delegate_type::load_in_memory_assembly, (void**)delegate); + return load_fxr_and_get_delegate( + hostfxr_delegate_type::load_in_memory_assembly, + [handle](const pal::string_t& host_path, pal::string_t* app_path_out) + { + if (!pal::get_module_path(handle, app_path_out)) + { + trace::error(_X("Failed to resolve full path of the current mixed-mode module [%s]"), host_path.c_str()); + return StatusCode::LibHostCurExeFindFailure; + } + return StatusCode::Success; + }, + delegate + ); } IJW_API BOOL STDMETHODCALLTYPE _CorDllMain(HINSTANCE hInst, diff --git a/src/corehost/cli/redirected_error_writer.cpp b/src/corehost/cli/redirected_error_writer.cpp new file mode 100644 index 0000000000..aa8fda4a38 --- /dev/null +++ b/src/corehost/cli/redirected_error_writer.cpp @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "redirected_error_writer.h" + +namespace +{ + pal::stringstream_t & get_redirected_error_stream() + { + thread_local static pal::stringstream_t comhost_errors; + + return comhost_errors; + } +} + +void reset_redirected_error_writer() +{ + pal::stringstream_t newstream; + get_redirected_error_stream().swap(newstream); +} + +void redirected_error_writer(const pal::char_t* msg) +{ + get_redirected_error_stream() << msg; +} + +pal::string_t get_redirected_error_string() +{ + return get_redirected_error_stream().str(); +} diff --git a/src/corehost/cli/redirected_error_writer.h b/src/corehost/cli/redirected_error_writer.h new file mode 100644 index 0000000000..7b330bd368 --- /dev/null +++ b/src/corehost/cli/redirected_error_writer.h @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef _COREHOST_CLI_REDIRECTED_ERROR_WRITER_H_ +#define _COREHOST_CLI_REDIRECTED_ERROR_WRITER_H_ + +#include + +void reset_redirected_error_writer(); + +void redirected_error_writer(const pal::char_t* msg); + +pal::string_t get_redirected_error_string(); + +#endif /* _COREHOST_CLI_REDIRECTED_ERROR_WRITER_H_ */ \ No newline at end of file diff --git a/src/corehost/cli/winrthost/CMakeLists.txt b/src/corehost/cli/winrthost/CMakeLists.txt new file mode 100644 index 0000000000..98b43fc233 --- /dev/null +++ b/src/corehost/cli/winrthost/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. + +cmake_minimum_required (VERSION 2.6) +project(winrthost) + +set(DOTNET_PROJECT_NAME "winrthost") + +# Include directories +include_directories(../fxr) +include_directories(../json/casablanca/include) + +# CMake does not recommend using globbing since it messes with the freshness checks + +set (SOURCES + winrthost.cpp + ../redirected_error_writer.cpp + ../fxr_resolver.cpp + ../../common/trace.cpp + ../../common/utils.cpp + ../fxr/fx_ver.cpp +) + +if(WIN32) + list(APPEND SOURCES + Exports.def) +endif() + +include(../lib.cmake) + +add_definitions(-DFEATURE_LIBHOST=1) + +# Specify non-default Windows libs to be used for Arm/Arm64 builds +if (WIN32 AND (CLI_CMAKE_PLATFORM_ARCH_ARM OR CLI_CMAKE_PLATFORM_ARCH_ARM64)) + target_link_libraries(winrthost Advapi32.lib Ole32.lib OleAut32.lib) +endif() + +target_link_libraries(winrthost RuntimeObject.lib) + +install(TARGETS winrthost DESTINATION corehost) +install_symbols(winrthost corehost) \ No newline at end of file diff --git a/src/corehost/cli/winrthost/Exports.def b/src/corehost/cli/winrthost/Exports.def new file mode 100644 index 0000000000..f82e6dbd04 --- /dev/null +++ b/src/corehost/cli/winrthost/Exports.def @@ -0,0 +1,3 @@ +EXPORTS + DllGetActivationFactory PRIVATE + DllCanUnloadNow PRIVATE diff --git a/src/corehost/cli/winrthost/winrthost.cpp b/src/corehost/cli/winrthost/winrthost.cpp new file mode 100644 index 0000000000..d57f04dfb6 --- /dev/null +++ b/src/corehost/cli/winrthost/winrthost.cpp @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "redirected_error_writer.h" +#include "hostfxr.h" +#include "fxr_resolver.h" +#include "pal.h" +#include "trace.h" +#include "error_codes.h" +#include "utils.h" +#include +#include +#include + + +#if defined(_WIN32) + +// WinRT entry points are defined without the __declspec(dllexport) attribute. +// The issue here is that the compiler will throw an error regarding linkage +// redefinion. The solution here is to the use a .def file on Windows. +#define WINRT_API extern "C" + +#else + +#define WINRT_API SHARED_API + +#endif // _WIN32 + +using winrt_activation_fn = pal::hresult_t(STDMETHODCALLTYPE*)(const pal::char_t* appPath, HSTRING activatableClassId, IActivationFactory** factory); + +namespace +{ + int get_winrt_activation_delegate(pal::string_t* app_path, winrt_activation_fn *delegate) + { + return load_fxr_and_get_delegate( + hostfxr_delegate_type::winrt_activation, + [](const pal::string_t& host_path, pal::string_t* app_path_out) + { + pal::string_t app_path_local{ host_path }; + + // Change the extension to get the 'app' + size_t idx = app_path_local.rfind(_X(".dll")); + assert(idx != pal::string_t::npos); + app_path_local.replace(app_path_local.begin() + idx, app_path_local.end(), _X(".winmd")); + + *app_path_out = std::move(app_path_local); + + return StatusCode::Success; + }, + delegate, + app_path + ); + } +} + + +WINRT_API HRESULT STDMETHODCALLTYPE DllGetActivationFactory(_In_ HSTRING activatableClassId, _Out_ IActivationFactory** factory) +{ + HRESULT hr; + pal::string_t app_path; + winrt_activation_fn activator; + { + trace::setup(); + reset_redirected_error_writer(); + error_writer_scope_t writer_scope(redirected_error_writer); + + int ec = get_winrt_activation_delegate(&app_path, &activator); + if (ec != StatusCode::Success) + { + RoOriginateErrorW(__HRESULT_FROM_WIN32(ec), 0 /* message is null-terminated */, get_redirected_error_string().c_str()); + return __HRESULT_FROM_WIN32(ec); + } + } + + return activator(app_path.c_str(), activatableClassId, factory); +} + +WINRT_API HRESULT STDMETHODCALLTYPE DllCanUnloadNow(void) +{ + return S_FALSE; +}