diff --git a/nuget/readme.md b/nuget/readme.md index 16eb5d2bb..ba9e1a598 100644 --- a/nuget/readme.md +++ b/nuget/readme.md @@ -115,6 +115,8 @@ void MyComponent::InitializeComponent() } ``` +InitializeComponent can also have parameters, the arguments passed to the constructor will also be attempted for use in calling InitializeComponent. If no such overload exists, it will fall back to calling InitializeComponent without arguments. + ***[Windows|Microsoft]::UI::Xaml::Markup::ComponentConnectorT*** A consequence of calling InitializeComponent outside construction is that Xaml runtime callbacks to IComponentConnector::Connect and IComponentConnector2::GetBindingConnector are now dispatched to the most derived implementations. Previously, these calls were dispatched directly to the class under construction, as the vtable had yet to be initialized. For objects with markup that derive from composable base classes with markup, this is a breaking change. Derived classes must now implement IComponentConnector::Connect and IComponentConnector2::GetBindingConnector by explicitly calling into the base class. The ComponentConnectorT template provides a correct implementation for these interfaces: diff --git a/strings/base_implements.h b/strings/base_implements.h index 0eb8db0bd..430276565 100644 --- a/strings/base_implements.h +++ b/strings/base_implements.h @@ -1320,12 +1320,26 @@ WINRT_EXPORT namespace winrt::impl static constexpr bool value = get_value(0); }; + template + class has_initializer_with_args + { + template ().InitializeComponent(std::declval()...))> static constexpr bool get_value(int) { return true; } + template static constexpr bool get_value(...) { return false; } + + public: + static constexpr bool value = get_value(0); + }; + template T* create_and_initialize(Args&&... args) { com_ptr instance{ new heap_implements(std::forward(args)...), take_ownership_from_abi }; - - if constexpr (has_initializer::value) + + if constexpr ((sizeof...(Args) != 0) && has_initializer_with_args::value) + { + instance->InitializeComponent(std::forward(args)...); + } + else if constexpr (has_initializer::value) { instance->InitializeComponent(); } diff --git a/test/test/initialize.cpp b/test/test/initialize.cpp index e5532fe2d..a449e6125 100644 --- a/test/test/initialize.cpp +++ b/test/test/initialize.cpp @@ -65,6 +65,56 @@ namespace m_initialize_called = true; } }; + + template + struct InitializeTWithArgs : implements + { + InitializeTWithArgs([[maybe_unused]] bool& initialize_called) + { + } + + ~InitializeTWithArgs() + { + } + + void InitializeComponent(bool& initialize_called) + { + initialize_called = true; + throw some_exception(); + } + + hstring ToString() + { + return {}; + } + }; + + struct InitializeWithArgs : InitializeTWithArgs + { + InitializeWithArgs(bool& initialize_called) : InitializeTWithArgs(initialize_called) + { + } + }; + + struct ThrowingDerivedWithArgs : InitializeTWithArgs + { + ThrowingDerivedWithArgs(bool& initialize_called) : InitializeTWithArgs(initialize_called) + { + throw some_exception(); + } + }; + + struct OverriddenInitializeWithArgs : InitializeTWithArgs + { + OverriddenInitializeWithArgs(bool& initialize_called) : InitializeTWithArgs(initialize_called) + { + } + + void InitializeComponent(bool& initialize_called) + { + initialize_called = true; + } + }; } TEST_CASE("initialize") @@ -116,4 +166,52 @@ TEST_CASE("initialize") REQUIRE(initialize_called); REQUIRE(!exception_caught); } + + // Ensure that failure to initialize is failure to instantiate, with no side effects + { + bool initialize_called{}; + bool exception_caught{}; + try + { + make(initialize_called); + } + catch (some_exception const&) + { + exception_caught = true; + } + REQUIRE(initialize_called); + REQUIRE(exception_caught); + } + + // Ensure that base is never initialized if exception thrown from derived/base constructor + { + bool initialize_called{}; + bool exception_caught{}; + try + { + make(initialize_called); + } + catch (some_exception const&) + { + exception_caught = true; + } + REQUIRE(!initialize_called); + REQUIRE(exception_caught); + } + + // Support for overriding initialization for post-processing (e.g., accessing Xaml properties) + { + bool initialize_called{}; + bool exception_caught{}; + try + { + make(initialize_called); + } + catch (some_exception const&) + { + exception_caught = true; + } + REQUIRE(initialize_called); + REQUIRE(!exception_caught); + } }