diff --git a/cppwinrt.sln b/cppwinrt.sln index 5964f976b..1c1a45f8a 100644 --- a/cppwinrt.sln +++ b/cppwinrt.sln @@ -109,8 +109,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3C7EA5F8-6 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_cpp20", "test\test_cpp20\test_cpp20.vcxproj", "{5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}" ProjectSection(ProjectDependencies) = postProject + {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8} = {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8} {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} + {F1C915B3-2C64-4992-AFB7-7F035B1A7607} = {F1C915B3-2C64-4992-AFB7-7F035B1A7607} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_cpp20_no_sourcelocation", "test\test_cpp20_no_sourcelocation\test_cpp20_no_sourcelocation.vcxproj", "{D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}" diff --git a/strings/base_activation.h b/strings/base_activation.h index be24c6ad9..5daaaf1e6 100644 --- a/strings/base_activation.h +++ b/strings/base_activation.h @@ -18,8 +18,11 @@ namespace winrt::impl using library_handle = handle_type; - template - WINRT_IMPL_NOINLINE hresult get_runtime_activation_factory_impl(param::hstring const& name, winrt::guid const& guid, void** result) noexcept + // This function pointer will be null unless one or more translation units in a binary define WINRT_REG_FREE before + // including winrt/base.h. If that is defined then the overall binary will support regfree COM activation. + inline hresult(*reg_free_factory_getter)(param::hstring const&, guid const&, void**) = nullptr; + + WINRT_IMPL_NOINLINE inline hresult get_runtime_activation_factory_impl(param::hstring const& name, winrt::guid const& guid, void** result) noexcept { if (winrt_activation_handler) { @@ -30,15 +33,8 @@ namespace winrt::impl if (hr == impl::error_not_initialized) { - auto usage = reinterpret_cast(WINRT_IMPL_GetProcAddress(load_library(L"combase.dll"), "CoIncrementMTAUsage")); - - if (!usage) - { - return hr; - } - void* cookie; - usage(&cookie); + WINRT_IMPL_CoIncrementMTAUsage(&cookie); hr = WINRT_IMPL_RoGetActivationFactory(*(void**)(&name), guid, result); } @@ -47,6 +43,21 @@ namespace winrt::impl return 0; } + // If the class is not registered, and regfree support is enabled, give that a chance to make the activation succeed. + // We should not attempt to fallback to regfree for error codes besides class not registered. If the activation is out + // of process then we could have RPC error codes. It would be bad if a class that should only ever be used out of proc + // is erroneously activated inproc. + if ((hr == error_class_not_registered) && reg_free_factory_getter) + { + return reg_free_factory_getter(name, guid, result); + } + + return hr; + } + +#ifdef WINRT_REG_FREE + WINRT_IMPL_NOINLINE inline hresult reg_free_get_activation_factory(param::hstring const& name, winrt::guid const& guid, void** result) noexcept + { com_ptr error_info; WINRT_IMPL_GetErrorInfo(0, error_info.put_void()); @@ -79,13 +90,7 @@ namespace winrt::impl continue; } - if constexpr (isSameInterfaceAsIActivationFactory) - { - *result = library_factory.detach(); - library.detach(); - return 0; - } - else if (0 == library_factory.as(guid, result)) + if (0 == library_factory.as(guid, result)) { library.detach(); return 0; @@ -93,13 +98,31 @@ namespace winrt::impl } WINRT_IMPL_SetErrorInfo(0, error_info.get()); - return hr; + return error_class_not_registered; } + // This file has been compiled by a translation unit with WINRT_REG_FREE defined. Fill in the reg_free_factory_getter function + // pointer so that regfree behavior is activated. + inline unsigned int reg_free_init = []() { + reg_free_factory_getter = reg_free_get_activation_factory; + return 0U; + }(); + + // This pragma is used to detect if the translation unit containing regfree usage is being linked with another translation + // unit that declared WINRT_NEVER_REG_FREE. +#pragma detect_mismatch("WINRT_REG_FREE", "enabled") +#endif // WINRT_REG_FREE + +#ifdef WINRT_NEVER_REG_FREE + // This pragma is used to ensure that a binary will never have winrt regfree logic enabled. Defining WINRT_NEVER_REG_FREE will + // cause this pragma to break linkage if any translation units in the same binary defined WINRT_REG_FREE. +#pragma detect_mismatch("WINRT_REG_FREE", "never") +#endif // WINRT_NEVER_REG_FREE + template hresult get_runtime_activation_factory(param::hstring const& name, void** result) noexcept { - return get_runtime_activation_factory_impl>(name, guid_of(), result); + return get_runtime_activation_factory_impl(name, guid_of(), result); } } diff --git a/strings/base_agile_ref.h b/strings/base_agile_ref.h index b85cb7e61..0dc8bfcb3 100644 --- a/strings/base_agile_ref.h +++ b/strings/base_agile_ref.h @@ -71,10 +71,12 @@ namespace winrt::impl using update_module_lock = module_lock_updater; +#ifdef WINRT_REG_FREE inline void* load_library(wchar_t const* library) noexcept { return WINRT_IMPL_LoadLibraryExW(library, nullptr, 0x00001000 /* LOAD_LIBRARY_SEARCH_DEFAULT_DIRS */); } +#endif // WINRT_REG_FREE inline hresult get_agile_reference(winrt::guid const& iid, void* object, void** reference) noexcept { diff --git a/strings/base_extern.h b/strings/base_extern.h index 2412f9f2c..0670d8125 100644 --- a/strings/base_extern.h +++ b/strings/base_extern.h @@ -33,7 +33,9 @@ extern "C" void __stdcall WINRT_IMPL_RoFailFastWithErrorContext(int32_t) noexcept WINRT_IMPL_LINK(RoFailFastWithErrorContext, 4); int32_t __stdcall WINRT_IMPL_RoTransformError(int32_t, int32_t, void*) noexcept WINRT_IMPL_LINK(RoTransformError, 12); +#ifdef WINRT_REG_FREE void* __stdcall WINRT_IMPL_LoadLibraryExW(wchar_t const* name, void* unused, uint32_t flags) noexcept WINRT_IMPL_LINK(LoadLibraryExW, 12); +#endif // WINRT_REG_FREE int32_t __stdcall WINRT_IMPL_FreeLibrary(void* library) noexcept WINRT_IMPL_LINK(FreeLibrary, 4); void* __stdcall WINRT_IMPL_GetProcAddress(void* library, char const* name) noexcept WINRT_IMPL_LINK(GetProcAddress, 8); @@ -41,6 +43,7 @@ extern "C" int32_t __stdcall WINRT_IMPL_GetErrorInfo(uint32_t reserved, void** info) noexcept WINRT_IMPL_LINK(GetErrorInfo, 8); int32_t __stdcall WINRT_IMPL_CoInitializeEx(void*, uint32_t type) noexcept WINRT_IMPL_LINK(CoInitializeEx, 8); void __stdcall WINRT_IMPL_CoUninitialize() noexcept WINRT_IMPL_LINK(CoUninitialize, 0); + int32_t __stdcall WINRT_IMPL_CoIncrementMTAUsage(void** cookie) noexcept WINRT_IMPL_LINK(CoIncrementMTAUsage, 4); int32_t __stdcall WINRT_IMPL_CoCreateFreeThreadedMarshaler(void* outer, void** marshaler) noexcept WINRT_IMPL_LINK(CoCreateFreeThreadedMarshaler, 8); int32_t __stdcall WINRT_IMPL_CoCreateInstance(winrt::guid const& clsid, void* outer, uint32_t context, winrt::guid const& iid, void** object) noexcept WINRT_IMPL_LINK(CoCreateInstance, 20); diff --git a/test/old_tests/UnitTests/pch.h b/test/old_tests/UnitTests/pch.h index 4b940b552..6b146a3bc 100644 --- a/test/old_tests/UnitTests/pch.h +++ b/test/old_tests/UnitTests/pch.h @@ -1,6 +1,7 @@ #pragma once -#define WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#define WINRT_REG_FREE #define WINRT_NATVIS #define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING diff --git a/test/test/pch.h b/test/test/pch.h index 24d65c225..b2392e14a 100644 --- a/test/test/pch.h +++ b/test/test/pch.h @@ -5,6 +5,7 @@ #include "mingw_com_support.h" #define WINRT_LEAN_AND_MEAN +#define WINRT_REG_FREE #include #include "winrt/Windows.Foundation.Collections.h" #include "winrt/Windows.Foundation.Numerics.h" diff --git a/test/test_cpp20/activation_without_regfree.cpp b/test/test_cpp20/activation_without_regfree.cpp new file mode 100644 index 000000000..b54a3471e --- /dev/null +++ b/test/test_cpp20/activation_without_regfree.cpp @@ -0,0 +1,43 @@ +#include "pch.h" +#include "winrt/test_component.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace test_component; + +TEST_CASE("activation_withoug_regfree_system_type") +{ + REQUIRE_NOTHROW(Uri(L"https://bing.com")); +} + +TEST_CASE("activation_manifested_avoiding_regfree") +{ + bool fusionRegistrationWorked{ false }; + try + { + // app.manifest has registration for the Simple runtimeclass so activation should succeed. + Simple s{}; + fusionRegistrationWorked = true; + } + catch (...) + { + } + REQUIRE(fusionRegistrationWorked); +} + +TEST_CASE("activation_withoug_regfree_fails") +{ + bool threwException{ false }; + try + { + Class c; + REQUIRE(false); + } + catch (winrt::hresult_class_not_registered& e) + { + threwException = true; + REQUIRE(e.code() == winrt::impl::error_class_not_registered); + } + + REQUIRE(threwException); +} \ No newline at end of file diff --git a/test/test_cpp20/app.manifest b/test/test_cpp20/app.manifest new file mode 100644 index 000000000..c88b6cffa --- /dev/null +++ b/test/test_cpp20/app.manifest @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/test/test_cpp20/pch.h b/test/test_cpp20/pch.h index c1a8f5ff3..dffca9d27 100644 --- a/test/test_cpp20/pch.h +++ b/test/test_cpp20/pch.h @@ -3,6 +3,7 @@ #pragma warning(4: 4458) // ensure we compile clean with this warning enabled #define WINRT_LEAN_AND_MEAN +#define WINRT_NEVER_REG_FREE #include #include "winrt/Windows.Data.Json.h" #include "winrt/Windows.Foundation.h" diff --git a/test/test_cpp20/test_cpp20.vcxproj b/test/test_cpp20/test_cpp20.vcxproj index 832297f7f..fdbbd7f65 100644 --- a/test/test_cpp20/test_cpp20.vcxproj +++ b/test/test_cpp20/test_cpp20.vcxproj @@ -100,8 +100,7 @@ true - - + $(CppWinRTDir)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi @@ -119,8 +118,7 @@ Console - - + $(CppWinRTDir)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi @@ -138,8 +136,7 @@ Console - - + $(CppWinRTDir)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi @@ -157,8 +154,7 @@ Console - - + $(CppWinRTDir)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi @@ -180,8 +176,7 @@ true - - + $(CppWinRTDir)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi @@ -203,8 +198,7 @@ true - - + $(CppWinRTDir)cppwinrt -in $(OutputPath)test_component.winmd $(OutputPath)test_component_no_pch.winmd -out "$(ProjectDir)Generated Files" -ref sdk -verbose -fastabi @@ -215,6 +209,7 @@ + @@ -228,6 +223,9 @@ + + + diff --git a/test/test_fast/pch.h b/test/test_fast/pch.h index 97769b358..11dbff990 100644 --- a/test/test_fast/pch.h +++ b/test/test_fast/pch.h @@ -1,5 +1,6 @@ #pragma once +#define WINRT_REG_FREE #include "catch.hpp" #include "winrt/Windows.Foundation.Collections.h" diff --git a/test/test_slow/pch.h b/test/test_slow/pch.h index 97769b358..11dbff990 100644 --- a/test/test_slow/pch.h +++ b/test/test_slow/pch.h @@ -1,5 +1,6 @@ #pragma once +#define WINRT_REG_FREE #include "catch.hpp" #include "winrt/Windows.Foundation.Collections.h"