From c1fa18bc87eacf606a2597cf8da174eaaa74ec88 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 8 Mar 2019 10:45:29 -0800 Subject: [PATCH 01/10] First pass at implementing a WinRT host. --- src/corehost/Windows/gen-buildsys-win.bat | 4 +- src/corehost/build.proj | 3 + src/corehost/cli/CMakeLists.txt | 1 + src/corehost/cli/comhost/CMakeLists.txt | 1 + src/corehost/cli/comhost/comhost.cpp | 113 ++++++------------- src/corehost/cli/fxr/fx_muxer.h | 3 +- src/corehost/cli/fxr/hostfxr.cpp | 2 + src/corehost/cli/fxr_resolver.h | 51 +++++++++ src/corehost/cli/hostfxr.h | 3 +- src/corehost/cli/hostpolicy.cpp | 7 ++ src/corehost/cli/ijwhost/ijwhost.cpp | 53 +++------ src/corehost/cli/redirected_error_writer.cpp | 30 +++++ src/corehost/cli/redirected_error_writer.h | 15 +++ src/corehost/cli/winrthost/CMakeLists.txt | 48 ++++++++ src/corehost/cli/winrthost/Exports.def | 3 + src/corehost/cli/winrthost/winrthost.cpp | 80 +++++++++++++ 16 files changed, 296 insertions(+), 121 deletions(-) create mode 100644 src/corehost/cli/redirected_error_writer.cpp create mode 100644 src/corehost/cli/redirected_error_writer.h create mode 100644 src/corehost/cli/winrthost/CMakeLists.txt create mode 100644 src/corehost/cli/winrthost/Exports.def create mode 100644 src/corehost/cli/winrthost/winrthost.cpp diff --git a/src/corehost/Windows/gen-buildsys-win.bat b/src/corehost/Windows/gen-buildsys-win.bat index ff97131b58..543c9b7c3a 100644 --- a/src/corehost/Windows/gen-buildsys-win.bat +++ b/src/corehost/Windows/gen-buildsys-win.bat @@ -41,8 +41,8 @@ for /f "delims=" %%a in ('powershell -NoProfile -ExecutionPolicy ByPass "& .\Win popd :DoGen -echo "%CMakePath%" %__sourceDir% %__SDKVersion% "-DCLI_CMAKE_RUNTIME_ID:STRING=%cm_BaseRid%" "-DCLI_CMAKE_HOST_VER:STRING=%__HostVersion%" "-DCLI_CMAKE_APPHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_COMHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_IJWHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_HOST_FXR_VER:STRING=%__HostFxrVersion%" "-DCLI_CMAKE_HOST_POLICY_VER:STRING=%__HostPolicyVersion%" "-DCLI_CMAKE_PKG_RID:STRING=%cm_BaseRid%" "-DCLI_CMAKE_COMMIT_HASH:STRING=%__LatestCommit%" "-DCLI_CMAKE_PLATFORM_ARCH_%cm_Arch%=1" "-DCMAKE_INSTALL_PREFIX=%__CMakeBinDir%" "-DCLI_CMAKE_RESOURCE_DIR:STRING=%__ResourcesDir%" -G "Visual Studio %__VSString%" %__ExtraCmakeParams% -"%CMakePath%" %__sourceDir% %__SDKVersion% "-DCLI_CMAKE_RUNTIME_ID:STRING=%cm_BaseRid%" "-DCLI_CMAKE_HOST_VER:STRING=%__HostVersion%" "-DCLI_CMAKE_APPHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_COMHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_IJWHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_HOST_FXR_VER:STRING=%__HostFxrVersion%" "-DCLI_CMAKE_HOST_POLICY_VER:STRING=%__HostPolicyVersion%" "-DCLI_CMAKE_PKG_RID:STRING=%cm_BaseRid%" "-DCLI_CMAKE_COMMIT_HASH:STRING=%__LatestCommit%" "-DCLI_CMAKE_PLATFORM_ARCH_%cm_Arch%=1" "-DCMAKE_INSTALL_PREFIX=%__CMakeBinDir%" "-DCLI_CMAKE_RESOURCE_DIR:STRING=%__ResourcesDir%" -G "Visual Studio %__VSString%" %__ExtraCmakeParams% +echo "%CMakePath%" %__sourceDir% %__SDKVersion% "-DCLI_CMAKE_RUNTIME_ID:STRING=%cm_BaseRid%" "-DCLI_CMAKE_HOST_VER:STRING=%__HostVersion%" "-DCLI_CMAKE_APPHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_COMHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_WINRTHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_IJWHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_HOST_FXR_VER:STRING=%__HostFxrVersion%" "-DCLI_CMAKE_HOST_POLICY_VER:STRING=%__HostPolicyVersion%" "-DCLI_CMAKE_PKG_RID:STRING=%cm_BaseRid%" "-DCLI_CMAKE_COMMIT_HASH:STRING=%__LatestCommit%" "-DCLI_CMAKE_PLATFORM_ARCH_%cm_Arch%=1" "-DCMAKE_INSTALL_PREFIX=%__CMakeBinDir%" "-DCLI_CMAKE_RESOURCE_DIR:STRING=%__ResourcesDir%" -G "Visual Studio %__VSString%" %__ExtraCmakeParams% +"%CMakePath%" %__sourceDir% %__SDKVersion% "-DCLI_CMAKE_RUNTIME_ID:STRING=%cm_BaseRid%" "-DCLI_CMAKE_HOST_VER:STRING=%__HostVersion%" "-DCLI_CMAKE_APPHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_COMHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_WINRTHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_IJWHOST_VER:STRING=%__AppHostVersion%" "-DCLI_CMAKE_HOST_FXR_VER:STRING=%__HostFxrVersion%" "-DCLI_CMAKE_HOST_POLICY_VER:STRING=%__HostPolicyVersion%" "-DCLI_CMAKE_PKG_RID:STRING=%cm_BaseRid%" "-DCLI_CMAKE_COMMIT_HASH:STRING=%__LatestCommit%" "-DCLI_CMAKE_PLATFORM_ARCH_%cm_Arch%=1" "-DCMAKE_INSTALL_PREFIX=%__CMakeBinDir%" "-DCLI_CMAKE_RESOURCE_DIR:STRING=%__ResourcesDir%" -G "Visual Studio %__VSString%" %__ExtraCmakeParams% endlocal GOTO :DONE diff --git a/src/corehost/build.proj b/src/corehost/build.proj index 51793842be..a689fa140b 100644 --- a/src/corehost/build.proj +++ b/src/corehost/build.proj @@ -51,6 +51,9 @@ .NET Core IJW Host + + .NET Core WinRT Host + 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(); + reset_redirected_error_writer(); - error_writer_scope_t writer_scope(comhost_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 e0293e6f7b..5c146dc05f 100644 --- a/src/corehost/cli/fxr/fx_muxer.h +++ b/src/corehost/cli/fxr/fx_muxer.h @@ -18,7 +18,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 de8dd7fff6..cdac378962 100644 --- a/src/corehost/cli/fxr_resolver.h +++ b/src/corehost/cli/fxr_resolver.h @@ -6,7 +6,58 @@ #define _COREHOST_CLI_FXR_RESOLVER_H_ #include +#include "hostfxr.h" +#include "trace.h" +#include "utils.h" bool resolve_fxr_path(const pal::string_t& host_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 (!resolve_fxr_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.cpp b/src/corehost/cli/hostpolicy.cpp index 6cdb483dc0..baf0f96d91 100644 --- a/src/corehost/cli/hostpolicy.cpp +++ b/src/corehost/cli/hostpolicy.cpp @@ -621,6 +621,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.WinRTActivator", + "GetActivationFactory", + delegate + ); default: return StatusCode::LibHostInvalidArgs; } diff --git a/src/corehost/cli/ijwhost/ijwhost.cpp b/src/corehost/cli/ijwhost/ijwhost.cpp index a4337eaf1e..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 (!resolve_fxr_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..70ababa373 --- /dev/null +++ b/src/corehost/cli/redirected_error_writer.cpp @@ -0,0 +1,30 @@ +// 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. + +#include "pal.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..2a07edbc59 --- /dev/null +++ b/src/corehost/cli/redirected_error_writer.h @@ -0,0 +1,15 @@ +// 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. + +#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..12dbf39a85 --- /dev/null +++ b/src/corehost/cli/winrthost/CMakeLists.txt @@ -0,0 +1,48 @@ +# 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) + +# Move to setup.cmake if WinRT host is ever built on non-Windows +if("${CLI_CMAKE_WINRTHOST_VER}" STREQUAL "") + message(FATAL_ERROR "winrthost version is not specified") +else() + add_definitions(-DLIBHOST_PKG_VER="${CLI_CMAKE_WINRTHOST_VER}") +endif() + +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..bb1fcdadfa --- /dev/null +++ b/src/corehost/cli/winrthost/winrthost.cpp @@ -0,0 +1,80 @@ +// 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. + +#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*)(HSTRING activatableClassId, IActivationFactory** factory); + +namespace +{ + int get_winrt_activation_delegate(winrt_activation_fn *delegate) + { + 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 }; + + // 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 + ); + } +} + + +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(&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(activatableClassId, factory); +} + +WINRT_API HRESULT STDMETHODCALLTYPE DllCanUnloadNow(void) +{ + return S_FALSE; +} From c65a45667a684dea8f048731bb07a286a14764a8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 18 Mar 2019 17:04:31 -0700 Subject: [PATCH 02/10] Sign winrthost. --- signing/sign.proj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/signing/sign.proj b/signing/sign.proj index ef80e07e33..b749d7cd44 100644 --- a/signing/sign.proj +++ b/signing/sign.proj @@ -40,6 +40,9 @@ $(CertificateId) + + $(CertificateId) + From ba578d233944982054a1fb34f1885e14ee36b096 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 19 Mar 2019 11:27:42 -0700 Subject: [PATCH 03/10] Point WinRT host to the correct entrypoint in S.P.CL. --- src/corehost/cli/hostpolicy.cpp | 2 +- src/corehost/cli/winrthost/winrthost.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/corehost/cli/hostpolicy.cpp b/src/corehost/cli/hostpolicy.cpp index baf0f96d91..833ddd2ada 100644 --- a/src/corehost/cli/hostpolicy.cpp +++ b/src/corehost/cli/hostpolicy.cpp @@ -624,7 +624,7 @@ SHARED_API int corehost_get_coreclr_delegate(coreclr_delegate_type type, void** case coreclr_delegate_type::winrt_activation: return coreclr->create_delegate( "System.Private.CoreLib", - "Internal.Runtime.InteropServices.WinRTActivator", + "Internal.Runtime.InteropServices.WindowsRuntime.ActivationFactoryLoader", "GetActivationFactory", delegate ); diff --git a/src/corehost/cli/winrthost/winrthost.cpp b/src/corehost/cli/winrthost/winrthost.cpp index bb1fcdadfa..fd4026e825 100644 --- a/src/corehost/cli/winrthost/winrthost.cpp +++ b/src/corehost/cli/winrthost/winrthost.cpp @@ -33,7 +33,7 @@ namespace int get_winrt_activation_delegate(winrt_activation_fn *delegate) { return load_fxr_and_get_delegate( - hostfxr_delegate_type::com_activation, + 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 }; From 22c08aafde1ee14394a635c982c123a130ddfe15 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 19 Mar 2019 16:12:38 -0700 Subject: [PATCH 04/10] Pass the app path to coreclr (to help with resolving the deps.json) --- src/corehost/cli/winrthost/winrthost.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/corehost/cli/winrthost/winrthost.cpp b/src/corehost/cli/winrthost/winrthost.cpp index fd4026e825..ecbf973345 100644 --- a/src/corehost/cli/winrthost/winrthost.cpp +++ b/src/corehost/cli/winrthost/winrthost.cpp @@ -26,11 +26,11 @@ #endif // _WIN32 -using winrt_activation_fn = pal::hresult_t(STDMETHODCALLTYPE*)(HSTRING activatableClassId, IActivationFactory** factory); +using winrt_activation_fn = pal::hresult_t(STDMETHODCALLTYPE*)(const pal::char_t* appPath, HSTRING activatableClassId, IActivationFactory** factory); namespace { - int get_winrt_activation_delegate(winrt_activation_fn *delegate) + 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, @@ -47,7 +47,8 @@ namespace return StatusCode::Success; }, - delegate + delegate, + app_path ); } } @@ -62,8 +63,8 @@ WINRT_API HRESULT STDMETHODCALLTYPE DllGetActivationFactory(_In_ HSTRING activat trace::setup(); reset_redirected_error_writer(); error_writer_scope_t writer_scope(redirected_error_writer); - - int ec = get_winrt_activation_delegate(&activator); + + 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()); @@ -71,7 +72,7 @@ WINRT_API HRESULT STDMETHODCALLTYPE DllGetActivationFactory(_In_ HSTRING activat } } - return activator(activatableClassId, factory); + return activator(app_path.c_str(), activatableClassId, factory); } WINRT_API HRESULT STDMETHODCALLTYPE DllCanUnloadNow(void) From 7caaacb94066c8df5211f6a446097d6e5eb8030c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Mar 2019 11:39:29 -0700 Subject: [PATCH 05/10] First pass at writing a design doc for WinRT activation. --- Documentation/design-docs/WinRT-activation.md | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/design-docs/WinRT-activation.md diff --git a/Documentation/design-docs/WinRT-activation.md b/Documentation/design-docs/WinRT-activation.md new file mode 100644 index 0000000000..7f29a9d696 --- /dev/null +++ b/Documentation/design-docs/WinRT-activation.md @@ -0,0 +1,51 @@ +# 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`. + +## 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 assembly from memory and get the entry-point. + * 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 static int GetActivationFactory( + IntPtr 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. From 5243c961d2c0832ff9549c66a6048e9c7edb1e85 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 21 Mar 2019 17:28:46 -0700 Subject: [PATCH 06/10] Make sure error_codes.h is included in fxr_resolver.h --- src/corehost/cli/fxr_resolver.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/corehost/cli/fxr_resolver.h b/src/corehost/cli/fxr_resolver.h index cdac378962..dfde2496c9 100644 --- a/src/corehost/cli/fxr_resolver.h +++ b/src/corehost/cli/fxr_resolver.h @@ -9,6 +9,7 @@ #include "hostfxr.h" #include "trace.h" #include "utils.h" +#include "error_codes.h" bool resolve_fxr_path(const pal::string_t& host_path, pal::string_t* out_dotnet_root, pal::string_t* out_fxr_path); From 9c5893b2d0a8da738996b1a134f939190c8897f0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 22 Mar 2019 09:59:41 -0700 Subject: [PATCH 07/10] Fix include. --- src/corehost/cli/redirected_error_writer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corehost/cli/redirected_error_writer.cpp b/src/corehost/cli/redirected_error_writer.cpp index 70ababa373..445ae52806 100644 --- a/src/corehost/cli/redirected_error_writer.cpp +++ b/src/corehost/cli/redirected_error_writer.cpp @@ -1,7 +1,7 @@ // 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. -#include "pal.h" +#include "redirected_error_writer.h" namespace { From 812d00c6fc2f0d79524e8902a75351827b4598d7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 25 Mar 2019 15:43:22 -0700 Subject: [PATCH 08/10] PR feedback. --- Documentation/design-docs/WinRT-activation.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Documentation/design-docs/WinRT-activation.md b/Documentation/design-docs/WinRT-activation.md index 7f29a9d696..66068036f8 100644 --- a/Documentation/design-docs/WinRT-activation.md +++ b/Documentation/design-docs/WinRT-activation.md @@ -21,7 +21,8 @@ In the managed (.NET) world, we put all of the code that implements the componen * 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 assembly from memory and get the entry-point. +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 @@ -29,8 +30,8 @@ namespace Internal.Runtime.InteropServices.WindowsRuntime { public static class ActivationFactoryLoader { - public static int GetActivationFactory( - IntPtr componentPath, + public unsafe static int GetActivationFactory( + char* componentPath, [MarshalAs(UnmanagedType.HString)] string typeName, [MarshalAs(UnmanagedType.Interface)] out IActivationFactory activationFactory); } @@ -49,3 +50,4 @@ If the runtime activation fails, we want to ensure that the user can diagnose th * 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. From 913dc1ce45bd9b62a627ca0f5e20c758e65a76d3 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 25 Mar 2019 15:44:32 -0700 Subject: [PATCH 09/10] Fixup license headers. --- src/corehost/cli/redirected_error_writer.cpp | 5 +++-- src/corehost/cli/redirected_error_writer.h | 5 +++-- src/corehost/cli/winrthost/winrthost.cpp | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/corehost/cli/redirected_error_writer.cpp b/src/corehost/cli/redirected_error_writer.cpp index 445ae52806..aa8fda4a38 100644 --- a/src/corehost/cli/redirected_error_writer.cpp +++ b/src/corehost/cli/redirected_error_writer.cpp @@ -1,5 +1,6 @@ -// 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. +// 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" diff --git a/src/corehost/cli/redirected_error_writer.h b/src/corehost/cli/redirected_error_writer.h index 2a07edbc59..7b330bd368 100644 --- a/src/corehost/cli/redirected_error_writer.h +++ b/src/corehost/cli/redirected_error_writer.h @@ -1,5 +1,6 @@ -// 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. +// 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_ diff --git a/src/corehost/cli/winrthost/winrthost.cpp b/src/corehost/cli/winrthost/winrthost.cpp index ecbf973345..d57f04dfb6 100644 --- a/src/corehost/cli/winrthost/winrthost.cpp +++ b/src/corehost/cli/winrthost/winrthost.cpp @@ -1,5 +1,6 @@ -// 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. +// 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" From 2db06b6f2f66ff9cc18f4a6637170403007fa239 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 25 Mar 2019 15:49:20 -0700 Subject: [PATCH 10/10] Add some justification on why the WinRT host and the COM host are separate. --- Documentation/design-docs/WinRT-activation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/design-docs/WinRT-activation.md b/Documentation/design-docs/WinRT-activation.md index 66068036f8..e8a72d5dd9 100644 --- a/Documentation/design-docs/WinRT-activation.md +++ b/Documentation/design-docs/WinRT-activation.md @@ -1,6 +1,6 @@ # 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`. +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