From 297365e712f11a0e4f3891ebf79544952a1e06fd Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 8 Jun 2020 08:18:46 -0700 Subject: [PATCH 001/387] Can clear a specific factory from static lifetime store (#654) --- strings/base_abi.h | 2 +- strings/base_implements.h | 23 +++++++++++++--- test/old_tests/Component/Events.cpp | 42 +++++++++++++++++++++++------ 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/strings/base_abi.h b/strings/base_abi.h index 1ad5e827e..6a88266b9 100644 --- a/strings/base_abi.h +++ b/strings/base_abi.h @@ -70,8 +70,8 @@ namespace winrt::impl virtual int32_t __stdcall unused2() noexcept = 0; virtual int32_t __stdcall unused3() noexcept = 0; virtual int32_t __stdcall Insert(void*, void*, bool*) noexcept = 0; + virtual int32_t __stdcall Remove(void*) noexcept = 0; virtual int32_t __stdcall unused4() noexcept = 0; - virtual int32_t __stdcall unused5() noexcept = 0; }; struct __declspec(novtable) IWeakReference : unknown_abi diff --git a/strings/base_implements.h b/strings/base_implements.h index 2457b80b4..0722aec5f 100644 --- a/strings/base_implements.h +++ b/strings/base_implements.h @@ -1198,6 +1198,14 @@ namespace winrt::impl }; #endif + inline com_ptr get_static_lifetime_map() + { + auto const lifetime_factory = get_activation_factory(L"Windows.ApplicationModel.Core.CoreApplication"); + Windows::Foundation::IUnknown collection; + check_hresult(lifetime_factory->GetCollection(put_abi(collection))); + return collection.as(); + } + template auto make_factory() -> typename impl::implements_default_interface::type { @@ -1209,10 +1217,7 @@ namespace winrt::impl } else { - auto const lifetime_factory = get_activation_factory(L"Windows.ApplicationModel.Core.CoreApplication"); - Windows::Foundation::IUnknown collection; - check_hresult(lifetime_factory->GetCollection(put_abi(collection))); - auto const map = collection.as(); + auto const map = get_static_lifetime_map(); param::hstring const name{ name_of() }; void* result{}; map->Lookup(get_abi(name), &result); @@ -1304,6 +1309,16 @@ WINRT_EXPORT namespace winrt } } + template + inline void clear_factory_static_lifetime() + { + auto unregister = [map = impl::get_static_lifetime_map()](param::hstring name) + { + map->Remove(get_abi(name)); + }; + ((unregister(name_of())), ...); + } + template struct implements : impl::producers, impl::base_implements::type { diff --git a/test/old_tests/Component/Events.cpp b/test/old_tests/Component/Events.cpp index 732569531..842f23556 100644 --- a/test/old_tests/Component/Events.cpp +++ b/test/old_tests/Component/Events.cpp @@ -74,25 +74,51 @@ namespace winrt::Component::factory_implementation bool Events::TestStaticLifetime() { + auto GetReferenceCount = [this]() + { + AddRef(); + return Release(); + }; + // Capture current reference count. - AddRef(); - auto refcount = Release(); + auto refcount = GetReferenceCount(); // Reset constructor count. s_constructorCount = 0; - auto self = make_self(); - if (self.get() != this) + // make_self should return a reference to ourselves + // since we are static_lifetime. + if (make_self().get() != this) { return false; } - self = nullptr; // Refcount should be unchanged. + if (refcount != GetReferenceCount()) + { + return false; + } + // Should not have been constructed spuriously. - AddRef(); - auto new_refcount = Release(); + if (s_constructorCount != 0) + { + return false; + } + + // Clear the static lifetime. That should drop the reference count. + clear_factory_static_lifetime(); + if (refcount == GetReferenceCount()) + { + return false; + } + + // Making a new object should put a different instance into + // the static lifetime. + if (make_self().get() == this) + { + return false; + } - return refcount == new_refcount && s_constructorCount == 0; + return true; } } From f816245db2eea456753979f13965b042e573fe45 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 8 Jun 2020 08:19:10 -0700 Subject: [PATCH 002/387] Fix unbox_value_or with explicit type or implicit hstring (#656) --- strings/base_reference_produce.h | 2 +- test/old_tests/UnitTests/Boxing2.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/strings/base_reference_produce.h b/strings/base_reference_produce.h index 31477b8a0..4ad39c5b9 100644 --- a/strings/base_reference_produce.h +++ b/strings/base_reference_produce.h @@ -318,7 +318,7 @@ WINRT_EXPORT namespace winrt } } - template + template , int> = 0> hstring unbox_value_or(Windows::Foundation::IInspectable const& value, param::hstring const& default_value) { if (value) diff --git a/test/old_tests/UnitTests/Boxing2.cpp b/test/old_tests/UnitTests/Boxing2.cpp index 1c43d0402..3b7c85d6b 100644 --- a/test/old_tests/UnitTests/Boxing2.cpp +++ b/test/old_tests/UnitTests/Boxing2.cpp @@ -214,4 +214,10 @@ TEST_CASE("Boxing") REQUIRE(unbox_value_or(box_value(static_cast(UnsignedEnum::Second)), UnsignedEnum::First) == UnsignedEnum::First); REQUIRE(unbox_value_or(box_value(static_cast(UnsignedEnum::Second)), UnsignedEnum::First) == UnsignedEnum::First); } + + { + // Test some cases where the compiler has to choose between multiple overloads. + REQUIRE(unbox_value_or(nullptr, {}) == IInspectable{}); + REQUIRE(unbox_value_or(nullptr, hstring{}) == hstring{}); + } } From 87436b4b5d683c6fdea292a708c816c153d3206c Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 8 Jun 2020 09:10:17 -0700 Subject: [PATCH 003/387] Fix ABI conformance of IMap::Remove, add TryRemove (#655) * Fix ABI conformance of IMap::Remove, add TryRemove `single_threaded_map()`'s IMap::Remove did not throw `hresult_out_of_range` on attempts to remove a nonexistent key. Now it throws. Failure to remove a nonexistent key does not invalidate iterators, because nothing actually changed. This brings the map implementation in line with the implementations in other projections. Note that this is a breaking change. Code that assumed nonexistent objects could be harmlessly removed will encounter exceptions when run against C++/WinRT implementations. This was, however, a pre-existing bug, because implementations from other projections (C#, C++/CX) always threw under those conditions. Added a TryRemove() method for people who wanted the nonthrowing version. Note that fixing the ABI conformance is required in order for TryRemove to work, because TryRemove relies on the call to Remove failing if the key doesn't exist. (JavaScript doesn't project objects as maps, so there is nothing to validate there.) Tightened the behavior of TryLookup and TryRemove so they propagate RPC failures. Because the inability to remove the item could be due to the server being unavailable, and that's not the same as the item not existing in the collection. Previous code treated loss of server the same as "The item doesn't exist", which is not true: The item could exist, we just were unable to contact the server to find out. * TryLookup and TryRemove should not be noexcept because they can throw on other errors. Added unit test to verify that errors other than "key not found" are propagated. Co-authored-by: Kenny Kerr --- cppwinrt/code_writers.h | 17 ++++++--- strings/base_collections_base.h | 9 ++++- strings/base_error.h | 12 ++++++ test/old_tests/UnitTests/TryLookup.cpp | 38 +++++++++++++++++++ test/old_tests/UnitTests/produce_map.cpp | 5 ++- .../UnitTests/single_threaded_map.cpp | 6 ++- 6 files changed, 75 insertions(+), 12 deletions(-) diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index ee082f717..c7f19d335 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -1305,12 +1305,12 @@ namespace cppwinrt else if (type_name == "Windows.Foundation.Collections.IMapView`2") { w.write(R"( - auto TryLookup(param_type const& key) const noexcept + auto TryLookup(param_type const& key) const { if constexpr (std::is_base_of_v) { V result{ nullptr }; - WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(result)); + impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(result))); return result; } else @@ -1318,7 +1318,7 @@ namespace cppwinrt std::optional result; V value{ empty_value() }; - if (0 == WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(value))) + if (0 == impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(value)))) { result = std::move(value); } @@ -1331,12 +1331,12 @@ namespace cppwinrt else if (type_name == "Windows.Foundation.Collections.IMap`2") { w.write(R"( - auto TryLookup(param_type const& key) const noexcept + auto TryLookup(param_type const& key) const { if constexpr (std::is_base_of_v) { V result{ nullptr }; - WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(result)); + impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(result))); return result; } else @@ -1344,7 +1344,7 @@ namespace cppwinrt std::optional result; V value{ empty_value() }; - if (0 == WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(value))) + if (0 == impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(value)))) { result = std::move(value); } @@ -1352,6 +1352,11 @@ namespace cppwinrt return result; } } + + auto TryRemove(param_type const& key) const + { + return 0 == impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Remove(get_abi(key))); + } )"); } else if (type_name == "Windows.Foundation.IAsyncAction") diff --git a/strings/base_collections_base.h b/strings/base_collections_base.h index 641d4aadb..d798df991 100644 --- a/strings/base_collections_base.h +++ b/strings/base_collections_base.h @@ -1,4 +1,3 @@ - WINRT_EXPORT namespace winrt { template @@ -415,8 +414,14 @@ WINRT_EXPORT namespace winrt void Remove(K const& key) { + auto& container = static_cast(*this).get_container(); + auto found = container.find(static_cast(*this).wrap_value(key)); + if (found == container.end()) + { + throw hresult_out_of_bounds(); + } this->increment_version(); - static_cast(*this).get_container().erase(static_cast(*this).wrap_value(key)); + container.erase(found); } void Clear() noexcept diff --git a/strings/base_error.h b/strings/base_error.h index 20a13a6e1..630e1dd33 100644 --- a/strings/base_error.h +++ b/strings/base_error.h @@ -579,3 +579,15 @@ WINRT_EXPORT namespace winrt abort(); } } + +namespace winrt::impl +{ + inline hresult check_hresult_allow_bounds(hresult const result) + { + if (result != impl::error_out_of_bounds) + { + check_hresult(result); + } + return result; + } +} \ No newline at end of file diff --git a/test/old_tests/UnitTests/TryLookup.cpp b/test/old_tests/UnitTests/TryLookup.cpp index c03cf1104..47d2b5536 100644 --- a/test/old_tests/UnitTests/TryLookup.cpp +++ b/test/old_tests/UnitTests/TryLookup.cpp @@ -90,3 +90,41 @@ TEST_CASE("TryLookup") REQUIRE(map.TryLookup(123).value() == 456); } } + +TEST_CASE("TryRemove") +{ + auto map = single_threaded_map(std::map{ + { 123, nullptr }, + { 124, make(L"remove") }, + { 125, make(L"keep") }, + }); + + REQUIRE(map.TryRemove(122) == false); + REQUIRE(map.TryRemove(123) == true); + REQUIRE(map.TryRemove(124) == true); + + // Should still have one item left. + REQUIRE(map.Size() == 1); + REQUIRE(map.Lookup(125).ToString() == L"keep"); +} + +TEST_CASE("TryLookup TryRemove error") +{ + // Simulate a non-agile map that is being accessed from the wrong thread. + // "Try" operations should throw rather than erroneously report "not found". + // Because they didn't even try. The operation never got off the ground. + struct incorrectly_used_non_agile_map : implements> + { + int Lookup(int) { throw hresult_wrong_thread(); } + int32_t Size() { throw hresult_wrong_thread(); } + bool HasKey(int) { throw hresult_wrong_thread(); } + IMapView GetView() { throw hresult_wrong_thread(); } + bool Insert(int, int) { throw hresult_wrong_thread(); } + void Remove(int) { throw hresult_wrong_thread(); } + void Clear() { throw hresult_wrong_thread(); } + }; + + auto map = make(); + REQUIRE_THROWS_AS(map.TryLookup(123), hresult_wrong_thread); + REQUIRE_THROWS_AS(map.TryRemove(123), hresult_wrong_thread); +} \ No newline at end of file diff --git a/test/old_tests/UnitTests/produce_map.cpp b/test/old_tests/UnitTests/produce_map.cpp index f81267294..b456082a4 100644 --- a/test/old_tests/UnitTests/produce_map.cpp +++ b/test/old_tests/UnitTests/produce_map.cpp @@ -93,7 +93,7 @@ TEST_CASE("produce_IMap_int32_t_hstring") REQUIRE(m.Size() == 2); m.Remove(1); // existing REQUIRE(m.Size() == 1); - m.Remove(3); // not existing + REQUIRE_THROWS_AS(m.Remove(3), hresult_out_of_bounds); // not existing REQUIRE(m.Size() == 1); m.Clear(); @@ -177,7 +177,8 @@ TEST_CASE("produce_IMap_hstring_int32_t") REQUIRE(m.Size() == 2); m.Remove(L"one"); // existing REQUIRE(m.Size() == 1); - m.Remove(L"three"); // not existing + REQUIRE_THROWS_AS(m.Remove(L"three"), hresult_out_of_bounds); // not existing + REQUIRE(!m.TryRemove(L"three")); // not existing REQUIRE(m.Size() == 1); m.Clear(); diff --git a/test/old_tests/UnitTests/single_threaded_map.cpp b/test/old_tests/UnitTests/single_threaded_map.cpp index 44be94c08..ac58c9fa8 100644 --- a/test/old_tests/UnitTests/single_threaded_map.cpp +++ b/test/old_tests/UnitTests/single_threaded_map.cpp @@ -28,6 +28,7 @@ namespace values.Insert(2,20); values.Insert(3,30); IIterator> first = values.First(); + REQUIRE(!values.TryRemove(999)); // failed removal does not invalidate REQUIRE(first.HasCurrent()); [[maybe_unused]] auto pair = first.Current(); REQUIRE(first.MoveNext()); @@ -52,7 +53,8 @@ namespace REQUIRE(!values.Insert(2, 20)); compare(values, { { 1,100 }, {2,20} }); - values.Remove(3); + REQUIRE_THROWS_AS(values.Remove(3), hresult_out_of_bounds); + REQUIRE(!values.TryRemove(3)); compare(values, { { 1,100 },{ 2,20 } }); values.Remove(2); compare(values, { { 1,100 } }); @@ -65,7 +67,7 @@ namespace compare(values, {}); test_invalidation(values, [&] { values.Clear(); }); - test_invalidation(values, [&] { values.Remove(10); }); + test_invalidation(values, [&] { values.Remove(1); }); test_invalidation(values, [&] { values.Insert(1,10); }); } } From 8455a2a8cf6417f0b013a0485eb67ab863a34e08 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Tue, 9 Jun 2020 07:27:27 -0700 Subject: [PATCH 004/387] build --- natvis/pch.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/natvis/pch.h b/natvis/pch.h index 1a33d1b2b..d9f1813b1 100644 --- a/natvis/pch.h +++ b/natvis/pch.h @@ -20,11 +20,15 @@ #include "base_com_ptr.h" #include "base_string.h" #include "base_string_input.h" +#include "base_string_operators.h" #include "base_array.h" #include "base_weak_ref.h" #include "base_agile_ref.h" #include "base_error.h" #include "base_marshaler.h" +#include "base_delegate.h" +#include "base_events.h" +#include "base_activation.h" #include "base_implements.h" #include #include From 0a53d3c9873fe7460a3725d43f469dfde87be97c Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Thu, 11 Jun 2020 13:31:15 -0700 Subject: [PATCH 005/387] Set better default for references and project references to keep MdMerge happy. (#612) --- nuget/Microsoft.Windows.CppWinRT.props | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/nuget/Microsoft.Windows.CppWinRT.props b/nuget/Microsoft.Windows.CppWinRT.props index 6e1344f40..94392e36d 100644 --- a/nuget/Microsoft.Windows.CppWinRT.props +++ b/nuget/Microsoft.Windows.CppWinRT.props @@ -43,6 +43,20 @@ Copyright (C) Microsoft Corporation. All rights reserved. nul + + + false + true + + + + false + true + From 26f12959434319d870e20ca8e115c46d3ed4fc36 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Mon, 15 Jun 2020 10:17:53 -0700 Subject: [PATCH 006/387] Make disconnect_aware_handler ctor and move ctor noexcept. (#661) --- strings/base_coroutine_foundation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 8b22caa1f..fe4975b96 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -100,10 +100,10 @@ namespace winrt::impl struct disconnect_aware_handler { - disconnect_aware_handler(std::experimental::coroutine_handle<> handle) + disconnect_aware_handler(std::experimental::coroutine_handle<> handle) noexcept : m_handle(handle) { } - disconnect_aware_handler(disconnect_aware_handler&& other) + disconnect_aware_handler(disconnect_aware_handler&& other) noexcept : m_context(std::move(other.m_context)) , m_handle(std::exchange(other.m_handle, {})) { } From 8c0832fadd643f159a39e790a00a65d1bdef6331 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 15 Jun 2020 13:09:30 -0700 Subject: [PATCH 007/387] try_capture and try_create_instance (#663) --- strings/base_activation.h | 6 +++++ strings/base_com_ptr.h | 27 ++++++++++++++++++++ test/old_tests/UnitTests/capture.cpp | 24 +++++++++++++++++ test/old_tests/UnitTests/create_instance.cpp | 9 +++++++ 4 files changed, 66 insertions(+) diff --git a/strings/base_activation.h b/strings/base_activation.h index 7f35e1a79..685d9f5c0 100644 --- a/strings/base_activation.h +++ b/strings/base_activation.h @@ -487,6 +487,12 @@ WINRT_EXPORT namespace winrt impl::get_factory_cache().clear(); } + template + auto try_create_instance(guid const& clsid, uint32_t context = 0x1 /*CLSCTX_INPROC_SERVER*/, void* outer = nullptr) + { + return try_capture(WINRT_IMPL_CoCreateInstance, clsid, outer, context); + } + template auto create_instance(guid const& clsid, uint32_t context = 0x1 /*CLSCTX_INPROC_SERVER*/, void* outer = nullptr) { diff --git a/strings/base_com_ptr.h b/strings/base_com_ptr.h index 46867a73a..3ae9d88cd 100644 --- a/strings/base_com_ptr.h +++ b/strings/base_com_ptr.h @@ -153,6 +153,18 @@ WINRT_EXPORT namespace winrt *other = m_ptr; } + template + bool try_capture(F function, Args&&...args) + { + return function(args..., guid_of(), put_void()) >= 0; + } + + template + bool try_capture(com_ptr const& object, M method, Args&&...args) + { + return (object.get()->*(method))(args..., guid_of(), put_void()) >= 0; + } + template void capture(F function, Args&&...args) { @@ -204,6 +216,21 @@ WINRT_EXPORT namespace winrt type* m_ptr{}; }; + template + impl::com_ref try_capture(F function, Args&& ...args) + { + void* result{}; + function(args..., guid_of(), &result); + return { result, take_ownership_from_abi }; + } + + template + impl::com_ref try_capture(com_ptr const& object, M method, Args&& ...args) + { + void* result{}; + (object.get()->*(method))(args..., guid_of(), &result); + return { result, take_ownership_from_abi }; + } template impl::com_ref capture(F function, Args&& ...args) { diff --git a/test/old_tests/UnitTests/capture.cpp b/test/old_tests/UnitTests/capture.cpp index e88a29056..d1c4e41de 100644 --- a/test/old_tests/UnitTests/capture.cpp +++ b/test/old_tests/UnitTests/capture.cpp @@ -58,3 +58,27 @@ TEST_CASE("capture") REQUIRE_THROWS_AS(capture(a, &ICapture::CreateMemberCapture, 0), hresult_no_interface); REQUIRE_THROWS_AS(d.capture(a, &ICapture::CreateMemberCapture, 0), hresult_no_interface); } + +TEST_CASE("try_capture") +{ + // Identical to the "capture" test above, just with different + // error handling. + com_ptr a = try_capture(CreateCapture, 10); + REQUIRE(a->GetValue() == 10); + a = nullptr; + REQUIRE(a.try_capture(CreateCapture, 20)); + REQUIRE(a->GetValue() == 20); + + auto b = try_capture(a, &ICapture::CreateMemberCapture, 30); + REQUIRE(b->GetValue() == 30); + b = nullptr; + REQUIRE(b.try_capture(a, &ICapture::CreateMemberCapture, 40)); + REQUIRE(b->GetValue() == 40); + + com_ptr d; + + REQUIRE(!try_capture(CreateCapture, 0)); + REQUIRE(!d.try_capture(CreateCapture, 0)); + REQUIRE(!try_capture(a, &ICapture::CreateMemberCapture, 0)); + REQUIRE(!d.try_capture(a, &ICapture::CreateMemberCapture, 0)); +} diff --git a/test/old_tests/UnitTests/create_instance.cpp b/test/old_tests/UnitTests/create_instance.cpp index 38f1bf031..fa1c2ee53 100644 --- a/test/old_tests/UnitTests/create_instance.cpp +++ b/test/old_tests/UnitTests/create_instance.cpp @@ -9,3 +9,12 @@ TEST_CASE("create_instance") com_ptr dialog = create_instance(guid_of()); REQUIRE(dialog); } + +TEST_CASE("try_create_instance") +{ + com_ptr dialog = try_create_instance(guid_of()); + REQUIRE(dialog); + + dialog = try_create_instance(CLSID_NULL); + REQUIRE(!dialog); +} From a0b18895b0b93ee0449acbf1073bf942d61ceadf Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 15 Jun 2020 23:24:17 -0700 Subject: [PATCH 008/387] Resuming neutral context from STA should force background thread (#662) --- strings/base_coroutine_foundation.h | 4 +- strings/base_coroutine_threadpool.h | 99 ++++++++++++++----- .../old_tests/UnitTests/apartment_context.cpp | 49 +++++++++ test/test/await_adapter.cpp | 22 +++-- 4 files changed, 138 insertions(+), 36 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index fe4975b96..311522a6c 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -35,7 +35,7 @@ namespace winrt::impl { // Note: A blocking wait on the UI thread for an asynchronous operation can cause a deadlock. // See https://docs.microsoft.com/windows/uwp/cpp-and-winrt-apis/concurrency#block-the-calling-thread - WINRT_ASSERT(!is_sta()); + WINRT_ASSERT(!is_sta_thread()); } template @@ -119,7 +119,7 @@ namespace winrt::impl private: std::experimental::coroutine_handle<> m_handle; - com_ptr m_context = apartment_context(); + resume_apartment_context m_context; void Complete() { diff --git a/strings/base_coroutine_threadpool.h b/strings/base_coroutine_threadpool.h index 254840dc2..ae26677ef 100644 --- a/strings/base_coroutine_threadpool.h +++ b/strings/base_coroutine_threadpool.h @@ -1,6 +1,14 @@ namespace winrt::impl { + inline auto submit_threadpool_callback(void(__stdcall* callback)(void*, void* context), void* context) + { + if (!WINRT_IMPL_TrySubmitThreadpoolCallback(callback, context, nullptr)) + { + throw_last_error(); + } + } + inline void __stdcall resume_background_callback(void*, void* context) noexcept { std::experimental::coroutine_handle<>::from_address(context)(); @@ -8,30 +16,43 @@ namespace winrt::impl inline auto resume_background(std::experimental::coroutine_handle<> handle) { - if (!WINRT_IMPL_TrySubmitThreadpoolCallback(resume_background_callback, handle.address(), nullptr)) - { - throw_last_error(); - } + submit_threadpool_callback(resume_background_callback, handle.address()); } - inline bool is_sta() noexcept + inline std::pair get_apartment_type() noexcept { int32_t aptType; int32_t aptTypeQualifier; - return (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) && ((aptType == 0 /*APTTYPE_STA*/) || (aptType == 3 /*APTTYPE_MAINSTA*/)); + if (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) + { + return { aptType, aptTypeQualifier }; + } + else + { + return { 1 /* APTTYPE_MTA */, 1 /* APTTYPEQUALIFIER_IMPLICIT_MTA */ }; + } } - inline bool requires_apartment_context() noexcept + inline bool is_sta_thread() noexcept { - int32_t aptType; - int32_t aptTypeQualifier; - return (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) && ((aptType == 0 /*APTTYPE_STA*/) || (aptType == 2 /*APTTYPE_NA*/) || (aptType == 3 /*APTTYPE_MAINSTA*/)); + auto type = get_apartment_type(); + switch (type.first) + { + case 0: /* APTTYPE_STA */ + case 3: /* APTTYPE_MAINSTA */ + return true; + case 2: /* APTTYPE_NA */ + return type.second == 3 /* APTTYPEQUALIFIER_NA_ON_STA */ || + type.second == 5 /* APTTYPEQUALIFIER_NA_ON_MAINSTA */; + } + return false; } - inline auto apartment_context() + struct resume_apartment_context { - return requires_apartment_context() ? capture(WINRT_IMPL_CoGetObjectContext) : nullptr; - } + com_ptr m_context = try_capture(WINRT_IMPL_CoGetObjectContext); + int32_t m_context_type = get_apartment_type().first; + }; inline int32_t __stdcall resume_apartment_callback(com_callback_args* args) noexcept { @@ -39,25 +60,49 @@ namespace winrt::impl return 0; }; - inline auto resume_apartment(com_ptr const& context, std::experimental::coroutine_handle<> handle) + inline void resume_apartment_sync(com_ptr const& context, std::experimental::coroutine_handle<> handle) + { + com_callback_args args{}; + args.data = handle.address(); + + check_hresult(context->ContextCallback(resume_apartment_callback, &args, guid_of(), 5, nullptr)); + } + + inline void resume_apartment_on_threadpool(com_ptr const& context, std::experimental::coroutine_handle<> handle) { - if (context) + struct threadpool_resume { - com_callback_args args{}; - args.data = handle.address(); + threadpool_resume(com_ptr const& context, std::experimental::coroutine_handle<> handle) : + m_context(context), m_handle(handle) { } + com_ptr m_context; + std::experimental::coroutine_handle<> m_handle; + }; + auto state = std::make_unique(context, handle); + submit_threadpool_callback([](void*, void* p) + { + std::unique_ptr state{ static_cast(p) }; + resume_apartment_sync(state->m_context, state->m_handle); + }, state.get()); + state.release(); + } - check_hresult(context->ContextCallback(resume_apartment_callback, &args, guid_of(), 5, nullptr)); + inline auto resume_apartment(resume_apartment_context const& context, std::experimental::coroutine_handle<> handle) + { + if ((context.m_context == nullptr) || (context.m_context == try_capture(WINRT_IMPL_CoGetObjectContext))) + { + handle(); + } + else if (context.m_context_type == 1 /* APTTYPE_MTA */) + { + resume_background(handle); + } + else if ((context.m_context_type == 2 /* APTTYPE_NTA */) && is_sta_thread()) + { + resume_apartment_on_threadpool(context.m_context, handle); } else { - if (requires_apartment_context()) - { - resume_background(handle); - } - else - { - handle(); - } + resume_apartment_sync(context.m_context, handle); } } @@ -294,7 +339,7 @@ WINRT_EXPORT namespace winrt impl::resume_apartment(context, handle); } - com_ptr context = impl::apartment_context(); + impl::resume_apartment_context context; }; [[nodiscard]] inline auto resume_after(Windows::Foundation::TimeSpan duration) noexcept diff --git a/test/old_tests/UnitTests/apartment_context.cpp b/test/old_tests/UnitTests/apartment_context.cpp index eab146973..ae3511698 100644 --- a/test/old_tests/UnitTests/apartment_context.cpp +++ b/test/old_tests/UnitTests/apartment_context.cpp @@ -1,8 +1,10 @@ #include "pch.h" #include "catch.hpp" +#include using namespace winrt; using namespace Windows::Foundation; +using namespace Windows::System; namespace { @@ -12,9 +14,56 @@ namespace co_await context; } + + template + void InvokeInContext(IContextCallback* context, TLambda&& lambda) + { + ComCallData data; + data.pUserDefined = λ + check_hresult(context->ContextCallback([](ComCallData* data) -> HRESULT + { + auto& lambda = *reinterpret_cast(data->pUserDefined); + lambda(); + return S_OK; + }, &data, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr)); + } + + auto get_winrt_apartment_context_for_com_context(com_ptr<::IContextCallback> const& com_context) + { + std::optional context; + InvokeInContext(com_context.get(), [&] { + context = apartment_context(); + }); + return context.value(); + } + + bool is_nta_on_mta() + { + APTTYPE type; + APTTYPEQUALIFIER qualifier; + check_hresult(CoGetApartmentType(&type, &qualifier)); + return (type == APTTYPE_NA) && (qualifier == APTTYPEQUALIFIER_NA_ON_MTA || qualifier == APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA); + } + + IAsyncAction TestNeutralApartmentContext() + { + auto controller = DispatcherQueueController::CreateOnDedicatedThread(); + co_await resume_foreground(controller.DispatcherQueue()); + + // Entering neutral apartment from STA should resume on explicit background thread. + auto nta = get_winrt_apartment_context_for_com_context(capture<::IContextCallback>(CoGetDefaultContext, APTTYPE_NA)); + co_await nta; + + REQUIRE(is_nta_on_mta()); + } } TEST_CASE("apartment_context coverage") { Async().get(); } + +TEST_CASE("apartment_context nta") +{ + TestNeutralApartmentContext().get(); +} diff --git a/test/test/await_adapter.cpp b/test/test/await_adapter.cpp index 65018d33b..16575699b 100644 --- a/test/test/await_adapter.cpp +++ b/test/test/await_adapter.cpp @@ -8,6 +8,14 @@ using namespace Windows::System; namespace { + bool is_sta() + { + APTTYPE type; + APTTYPEQUALIFIER qualifier; + check_hresult(CoGetApartmentType(&type, &qualifier)); + return (type == APTTYPE_STA) || (type == APTTYPE_MAINSTA); + } + static handle signal{ CreateEventW(nullptr, false, false, nullptr) }; IAsyncAction OtherForegroundAsync() @@ -29,9 +37,9 @@ namespace IAsyncAction ForegroundAsync(DispatcherQueue dispatcher) { - REQUIRE(!impl::is_sta()); + REQUIRE(!is_sta()); co_await resume_foreground(dispatcher); - REQUIRE(impl::is_sta()); + REQUIRE(is_sta()); // This exercises one STA thread waiting on another thus one context callback // completing on another. @@ -48,9 +56,9 @@ namespace fire_and_forget SignalFromForeground(DispatcherQueue dispatcher) { - REQUIRE(!impl::is_sta()); + REQUIRE(!is_sta()); co_await resume_foreground(dispatcher); - REQUIRE(impl::is_sta()); + REQUIRE(is_sta()); // Previously, this signal was never raised because the foreground thread // was always blocked waiting for ContextCallback to return. @@ -61,19 +69,19 @@ namespace { // Switch to a background (MTA) thread. co_await resume_background(); - REQUIRE(!impl::is_sta()); + REQUIRE(!is_sta()); // This exercises one MTA thread waiting on another and just completing // directly without the overhead of a context switch. co_await OtherBackgroundAsync(); - REQUIRE(!impl::is_sta()); + REQUIRE(!is_sta()); // Wait for a coroutine that completes on a foreground (STA) thread. co_await ForegroundAsync(dispatcher); // Resumption should automatically switch to a background (MTA) thread // without blocking the Completed handler (which would in turn block the foreground thread). - REQUIRE(!impl::is_sta()); + REQUIRE(!is_sta()); // Attempt to signal from the foreground thread under the assumption // that the foreground thread is not blocked. From 9836238bfd7b1362242bf55e143b1992538bf2a0 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Tue, 16 Jun 2020 22:59:07 -0700 Subject: [PATCH 009/387] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ca9f70ac..09a03c0c8 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,6 @@ C++/WinRT is an entirely standard C++ language projection for Windows Runtime (W * Visual Studio extension: http://aka.ms/cppwinrt/vsix * Wikipedia: https://en.wikipedia.org/wiki/C++/WinRT -C++/WinRT is part of the [xlang](https://github.com/microsoft/xlang) family of projects that help developers create APIs that can run on multiple platforms and be used with a variety of languages. - # Building C++/WinRT Don't build C++/WinRT yourself - just download the latest version here: https://aka.ms/cppwinrt/nuget @@ -36,3 +34,9 @@ provided by the bot. You will only need to do this once across all repos using o This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +# Project Reunion + +Project Reunion is a set of libraries, frameworks, components, and tools that you can use to access powerful Windows platform functionality from all kinds of apps on many versions of Windows. Project Reunion combines the power of Win32 native applications alongside modern APIs, so your apps light up everywhere your users are. + +Other Project Reunion components include [WinUI](https://github.com/microsoft/microsoft-ui-xaml), WebView2, MSIX, [Rust/WinRT](https://github.com/microsoft/winrt-rs), and [C#/WinRT](https://github.com/microsoft/cswinrt). If you'd like to learn more, contribute to Project Reunion, or have app model questions, visit [Project Reunion on GitHub](https://github.com/microsoft/ProjectReunion). From 276b2f56941213ac3362167d1c4a92ff68bc3469 Mon Sep 17 00:00:00 2001 From: David Fields Date: Fri, 19 Jun 2020 16:21:21 -0700 Subject: [PATCH 010/387] Make array_view(pointer, size) constructor public (#666) --- strings/base_array.h | 10 +++++----- test/old_tests/UnitTests/array.cpp | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/strings/base_array.h b/strings/base_array.h index eb3fa540b..5f0904efe 100644 --- a/strings/base_array.h +++ b/strings/base_array.h @@ -17,6 +17,11 @@ WINRT_EXPORT namespace winrt array_view() noexcept = default; + array_view(pointer data, size_type size) noexcept : + m_data(data), + m_size(size) + {} + array_view(pointer first, pointer last) noexcept : m_data(first), m_size(static_cast(last - first)) @@ -192,11 +197,6 @@ WINRT_EXPORT namespace winrt protected: - array_view(pointer data, size_type size) noexcept : - m_data(data), - m_size(size) - {} - pointer m_data{ nullptr }; size_type m_size{ 0 }; diff --git a/test/old_tests/UnitTests/array.cpp b/test/old_tests/UnitTests/array.cpp index aa13b8018..95a387822 100644 --- a/test/old_tests/UnitTests/array.cpp +++ b/test/old_tests/UnitTests/array.cpp @@ -126,6 +126,24 @@ TEST_CASE("custom,DataReader") REQUIRE(3 == a[2]); } +// +// This test illustrates an array_view (non-const) bound to a raw buffer +// +TEST_CASE("buffer,DataReader") +{ + auto reader = CreateDataReader({ 1, 2, 3 }).get(); + + std::array a; + byte* ptr = a.data(); + auto size = a.size(); + reader.ReadBytes({ ptr, static_cast(size) }); + + REQUIRE(3 == a.size()); + REQUIRE(1 == a[0]); + REQUIRE(2 == a[1]); + REQUIRE(3 == a[2]); +} + // // This test illustrates receiving an IVector and calling GetMany to fill an array. // @@ -1259,6 +1277,7 @@ TEST_CASE("array_view,ctad") uint8_t a[3]{}; REQUIRE_DEDUCED_AS(uint8_t, &a[0], &a[0]); + REQUIRE_DEDUCED_AS(uint8_t, &a[0], 3); REQUIRE_DEDUCED_AS(uint8_t, a); std::array ar{}; From 4fa0e403f55eaf9d4c6182f1496d41cce585cd6e Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Thu, 25 Jun 2020 09:13:43 -0700 Subject: [PATCH 011/387] clang10 (#669) --- strings/base_coroutine_foundation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 311522a6c..43a8b16bd 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -118,8 +118,8 @@ namespace winrt::impl } private: - std::experimental::coroutine_handle<> m_handle; resume_apartment_context m_context; + std::experimental::coroutine_handle<> m_handle; void Complete() { @@ -743,7 +743,7 @@ WINRT_EXPORT namespace winrt auto [delegate, shared] = impl::make_delegate_with_shared_state>(shared_type{}); - auto completed = [&](T const& async) + auto completed = [delegate = std::move(delegate)](T const& async) { async.Completed(delegate); }; From f67e55ac609450c5e65558c6975b9aa89b0a07c8 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Sun, 28 Jun 2020 08:33:04 -0700 Subject: [PATCH 012/387] Don't destroy the IAsyncInfo from inside Completed handler (#671) --- strings/base_coroutine_foundation.h | 1 + test/test/async_completed.cpp | 66 +++++++++++++++++++++++++++++ test/test/test.vcxproj | 1 + 3 files changed, 68 insertions(+) create mode 100644 test/test/async_completed.cpp diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 43a8b16bd..797070737 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -140,6 +140,7 @@ namespace winrt::impl void await_suspend(std::experimental::coroutine_handle<> handle) { + auto extend_lifetime = async; async.Completed([this, handler = disconnect_aware_handler{ handle }](auto&&, auto operation_status) mutable { status = operation_status; diff --git a/test/test/async_completed.cpp b/test/test/async_completed.cpp new file mode 100644 index 000000000..6b784e451 --- /dev/null +++ b/test/test/async_completed.cpp @@ -0,0 +1,66 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that awaiting an already-completed async operation + // does not destroy the operation from within the Completed handler. + // The Completed handler may run synchronously, and destroying the + // operation from within the Completed handler pulls the rug out + // from under the operation! + // + struct already_completed : implements + { + void Completed(AsyncActionCompletedHandler const& complete) + { + auto self = get_weak(); + complete(*this, AsyncStatus::Completed); + REQUIRE(self.get() != nullptr); + } + + auto Completed() const noexcept + { + return nullptr; + } + + uint32_t Id() const noexcept + { + return 1; + } + + AsyncStatus Status() const noexcept + { + return AsyncStatus::Completed; + } + + hresult ErrorCode() const noexcept + { + return 0; + } + + void GetResults() const noexcept + { + } + + void Cancel() const noexcept + { + } + + void Close() const noexcept + { + } + }; + + IAsyncAction TestCompleted() + { + co_await make(); + } +} + +TEST_CASE("async_completed") +{ + TestCompleted().get(); +} diff --git a/test/test/test.vcxproj b/test/test/test.vcxproj index de9f484ff..1b4cc6cc5 100644 --- a/test/test/test.vcxproj +++ b/test/test/test.vcxproj @@ -293,6 +293,7 @@ + From f48d7a664f48e29bdd9d76a247c69c8f3c8f9d4c Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 29 Jun 2020 14:58:39 -0700 Subject: [PATCH 013/387] make progress_token::operator() const so it can be captured by lambdas (#673) --- strings/base_coroutine_foundation.h | 2 +- test/test/async_progress.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 797070737..84fe8bc81 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -305,7 +305,7 @@ namespace winrt::impl return *this; } - void operator()(Progress const& result) + void operator()(Progress const& result) const { m_promise->set_progress(result); } diff --git a/test/test/async_progress.cpp b/test/test/async_progress.cpp index 0a2790f50..a1ef121c8 100644 --- a/test/test/async_progress.cpp +++ b/test/test/async_progress.cpp @@ -19,8 +19,12 @@ namespace IAsyncOperationWithProgress Operation(HANDLE event) { co_await resume_on_signal(event); - auto progress = co_await get_progress_token(); - progress(123); + + // Invoke from a lambda to ensure that operator() is const. + [progress = co_await get_progress_token()]() + { + progress(123); + }(); co_return 1; } From fba255d96db18096bbefd7e1a8b1133561056617 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 29 Jun 2020 15:35:02 -0700 Subject: [PATCH 014/387] Cancellation token improvements (#674) --- strings/base_coroutine_foundation.h | 7 +++++-- test/test/async_auto_cancel.cpp | 14 ++++++++++++++ test/test/async_cancel_callback.cpp | 17 ++++++++++------- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 84fe8bc81..a70b4d0c5 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -273,7 +273,7 @@ namespace winrt::impl return m_promise->Status() == Windows::Foundation::AsyncStatus::Canceled; } - void callback(winrt::delegate<>&& cancel) noexcept + void callback(winrt::delegate<>&& cancel) const noexcept { m_promise->cancellation_callback(std::move(cancel)); } @@ -559,7 +559,10 @@ namespace winrt::impl } } - cancel(); + if (cancel) + { + cancel(); + } } #if defined(_DEBUG) && !defined(WINRT_NO_MAKE_DETECTION) diff --git a/test/test/async_auto_cancel.cpp b/test/test/async_auto_cancel.cpp index 45ff30fcf..2b1fdc728 100644 --- a/test/test/async_auto_cancel.cpp +++ b/test/test/async_auto_cancel.cpp @@ -39,6 +39,19 @@ namespace co_return 1; } + IAsyncAction ActionForceAutoCancel(HANDLE event) + { + co_await resume_on_signal(event); + + // Null out the callback to indicate that we want to cancel + // any existing cancellation callback and rely on auto-cancel. + auto cancel = co_await get_cancellation_token(); + cancel.callback(nullptr); + + co_await std::experimental::suspend_never(); + REQUIRE(false); + } + template void Check(F make) { @@ -70,4 +83,5 @@ TEST_CASE("async_auto_cancel") Check(ActionWithProgress); Check(Operation); Check(OperationWithProgress); + Check(ActionForceAutoCancel); } diff --git a/test/test/async_cancel_callback.cpp b/test/test/async_cancel_callback.cpp index 0ab5b6bb1..a69575a88 100644 --- a/test/test/async_cancel_callback.cpp +++ b/test/test/async_cancel_callback.cpp @@ -11,13 +11,16 @@ namespace IAsyncAction Action(HANDLE event, bool& canceled) { - auto cancel = co_await get_cancellation_token(); - - cancel.callback([&] - { - REQUIRE(!canceled); - canceled = true; - }); + // Put the cancellation token into a lambda just to make + // sure it's possible. + [cancel = co_await get_cancellation_token(), &canceled] + { + cancel.callback([&] + { + REQUIRE(!canceled); + canceled = true; + }); + }(); co_await resume_on_signal(event); co_await std::experimental::suspend_never(); From aeb78bbd6dd3c1c54d0983762e56e61daa87b14f Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Tue, 30 Jun 2020 11:22:18 -0700 Subject: [PATCH 015/387] Fix concat_hstring for 0-length string (#675) --- strings/base_string_operators.h | 7 ++++++- test/old_tests/UnitTests/hstring.cpp | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/strings/base_string_operators.h b/strings/base_string_operators.h index 284cc1118..215186b04 100644 --- a/strings/base_string_operators.h +++ b/strings/base_string_operators.h @@ -98,7 +98,12 @@ namespace winrt::impl { inline hstring concat_hstring(std::wstring_view const& left, std::wstring_view const& right) { - hstring_builder text(static_cast(left.size() + right.size())); + auto size = static_cast(left.size() + right.size()); + if (size == 0) + { + return{}; + } + hstring_builder text(size); memcpy_s(text.data(), left.size() * sizeof(wchar_t), left.data(), left.size() * sizeof(wchar_t)); memcpy_s(text.data() + left.size(), right.size() * sizeof(wchar_t), right.data(), right.size() * sizeof(wchar_t)); return text.to_hstring(); diff --git a/test/old_tests/UnitTests/hstring.cpp b/test/old_tests/UnitTests/hstring.cpp index 38ef1e3ae..1369fd229 100644 --- a/test/old_tests/UnitTests/hstring.cpp +++ b/test/old_tests/UnitTests/hstring.cpp @@ -570,4 +570,7 @@ TEST_CASE("hstring, concat") REQUIRE(hstring() + s == L"abc"); REQUIRE(s + L"" == L"abc"); REQUIRE(L"" + s == L"abc"); + + REQUIRE(hstring() + hstring() == L""); + REQUIRE(get_abi(hstring() + hstring()) == nullptr); } From 61a55bc774cfd50692c6b8bed4305ee53b04fa7a Mon Sep 17 00:00:00 2001 From: Pedro Miguel Justo <40605312+pmsjt@users.noreply.github.com> Date: Fri, 3 Jul 2020 16:23:55 -0700 Subject: [PATCH 016/387] Fix CFG helper calling convention and simplify flow of call target address. (#678) --- fast_fwd/arm64/thunks.asm | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/fast_fwd/arm64/thunks.asm b/fast_fwd/arm64/thunks.asm index be128ecac..0fe5252a0 100644 --- a/fast_fwd/arm64/thunks.asm +++ b/fast_fwd/arm64/thunks.asm @@ -11,10 +11,9 @@ NESTED_ENTRY InvokeForwarder ; Save enregistered args - PROLOG_SAVE_REG_PAIR fp, lr, #-64! - PROLOG_SAVE_REG_PAIR x19, x20, #16 - PROLOG_NOP stp x0, x1, [sp, #32] - PROLOG_NOP stp x2, x3, [sp, #48] + PROLOG_SAVE_REG_PAIR fp, lr, #-48! + PROLOG_NOP stp x0, x1, [sp, #16] + PROLOG_NOP stp x2, x3, [sp, #32] ; Replace forwarder abi with owner abi ldr x1, [x0, #8] @@ -26,23 +25,20 @@ ; Get method address from owner abi vtable ldr x0, [x1] - ldr x19, [x0, x12, lsl #3] - mov x0, x19 + ldr x15, [x0, x12, lsl #3] ; Verify indirect call target adrp x12, __guard_check_icall_fptr ldr x12, [x12, __guard_check_icall_fptr] blr x12 - ; Restore method address, return address, and args - mov x12, x19 - EPILOG_NOP ldp x2, x3, [sp, #48] - EPILOG_NOP ldp x0, x1, [sp, #32] - EPILOG_RESTORE_REG_PAIR x19, x20, #16 - EPILOG_RESTORE_REG_PAIR fp, lr, #64! + ; Restore return address, and args + EPILOG_NOP ldp x2, x3, [sp, #32] + EPILOG_NOP ldp x0, x1, [sp, #16] + EPILOG_RESTORE_REG_PAIR fp, lr, #48! ; Jump to method - EPILOG_NOP br x12 + EPILOG_NOP br x15 NESTED_END InvokeForwarder From 3689d858826ea3b5fad7fe15ff9bdcb962436f6e Mon Sep 17 00:00:00 2001 From: Jon Wiswall Date: Fri, 3 Jul 2020 16:24:24 -0700 Subject: [PATCH 017/387] Add SDKReference-sourced WinMDs when building (#679) --- build_nuget.cmd | 2 +- nuget/Microsoft.Windows.CppWinRT.nuspec | 2 +- nuget/Microsoft.Windows.CppWinRT.targets | 3 ++- nuget/readme.md | 11 +++++++++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/build_nuget.cmd b/build_nuget.cmd index 068e1dfd0..5c5c60c6c 100644 --- a/build_nuget.cmd +++ b/build_nuget.cmd @@ -10,4 +10,4 @@ call msbuild /m /p:Configuration=Release,Platform=arm64,CppWinRTBuildVersion=%ta call msbuild /m /p:Configuration=Release,Platform=x86,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:cppwinrt -nuget pack nuget\Microsoft.Windows.CppWinRT.nuspec -Properties cppwinrt_exe=%cd%\_build\x86\Release\cppwinrt.exe;cppwinrt_fast_fwd_x86=%cd%\_build\x86\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%cd%\_build\x64\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm=%cd%\_build\arm\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%cd%\_build\arm64\Release\cppwinrt_fast_forwarder.lib +nuget pack nuget\Microsoft.Windows.CppWinRT.nuspec -Properties target_version=%target_version%;cppwinrt_exe=%cd%\_build\x86\Release\cppwinrt.exe;cppwinrt_fast_fwd_x86=%cd%\_build\x86\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%cd%\_build\x64\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm=%cd%\_build\arm\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%cd%\_build\arm64\Release\cppwinrt_fast_forwarder.lib diff --git a/nuget/Microsoft.Windows.CppWinRT.nuspec b/nuget/Microsoft.Windows.CppWinRT.nuspec index a49f1eb8f..882931734 100644 --- a/nuget/Microsoft.Windows.CppWinRT.nuspec +++ b/nuget/Microsoft.Windows.CppWinRT.nuspec @@ -2,7 +2,7 @@ Microsoft.Windows.CppWinRT - 1.0.0.0 + $target_version$ C++/WinRT Build Support Microsoft Microsoft diff --git a/nuget/Microsoft.Windows.CppWinRT.targets b/nuget/Microsoft.Windows.CppWinRT.targets index a04e96a87..f5ee7929c 100644 --- a/nuget/Microsoft.Windows.CppWinRT.targets +++ b/nuget/Microsoft.Windows.CppWinRT.targets @@ -248,11 +248,12 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_CppWinRTDirectWinMDReferences Remove="@(_CppWinRTDirectWinMDReferences)" /> <_CppWinRTDirectWinMDReferences Include="@(ReferencePath)" Condition="'%(ReferencePath.IsSystemReference)' != 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ResolveAssemblyReference'" /> + <_CppWinRTDirectWinMDReferences Include="@(ReferencePath)" Condition="'%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ExpandSDKReference'" /> %(FullPath) diff --git a/nuget/readme.md b/nuget/readme.md index 696bf1b76..8dbb9931f 100644 --- a/nuget/readme.md +++ b/nuget/readme.md @@ -94,3 +94,14 @@ Example: For more complex analysis of build errors, the [MSBuild Binary and Structured Log Viewer](http://msbuildlog.com/) is highly recommended. +## Building, Testing + +Be sure to get the latest nuget.exe from [nuget.org](https://www.nuget.org/downloads) and place it in your path. + +Build the package by running [build_nuget.cmd](../build_nuget.cmd) from a developer environment command line. For testing pass a version number that is much higher than your currently installed, like: + +``` +c:\repos\cppwinrt> .\build_nuget.cmd 5.0.0.0 +``` + +Add the cppwinrt repo directory as a nuget source location and update your projects' references to point at it, update project references, then rebuild a test/sample project. From 8f7fab26e0bb6a5f7189865c657ec5e0c385131a Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Fri, 3 Jul 2020 18:34:57 -0700 Subject: [PATCH 018/387] Fix build --- build_nuget.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_nuget.cmd b/build_nuget.cmd index 5c5c60c6c..068e1dfd0 100644 --- a/build_nuget.cmd +++ b/build_nuget.cmd @@ -10,4 +10,4 @@ call msbuild /m /p:Configuration=Release,Platform=arm64,CppWinRTBuildVersion=%ta call msbuild /m /p:Configuration=Release,Platform=x86,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:cppwinrt -nuget pack nuget\Microsoft.Windows.CppWinRT.nuspec -Properties target_version=%target_version%;cppwinrt_exe=%cd%\_build\x86\Release\cppwinrt.exe;cppwinrt_fast_fwd_x86=%cd%\_build\x86\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%cd%\_build\x64\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm=%cd%\_build\arm\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%cd%\_build\arm64\Release\cppwinrt_fast_forwarder.lib +nuget pack nuget\Microsoft.Windows.CppWinRT.nuspec -Properties cppwinrt_exe=%cd%\_build\x86\Release\cppwinrt.exe;cppwinrt_fast_fwd_x86=%cd%\_build\x86\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%cd%\_build\x64\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm=%cd%\_build\arm\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%cd%\_build\arm64\Release\cppwinrt_fast_forwarder.lib From 11abc6e7f4ebdde63acab937ab8382b7bef94815 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Fri, 3 Jul 2020 18:35:31 -0700 Subject: [PATCH 019/387] Fix build --- nuget/Microsoft.Windows.CppWinRT.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nuget/Microsoft.Windows.CppWinRT.nuspec b/nuget/Microsoft.Windows.CppWinRT.nuspec index 882931734..a49f1eb8f 100644 --- a/nuget/Microsoft.Windows.CppWinRT.nuspec +++ b/nuget/Microsoft.Windows.CppWinRT.nuspec @@ -2,7 +2,7 @@ Microsoft.Windows.CppWinRT - $target_version$ + 1.0.0.0 C++/WinRT Build Support Microsoft Microsoft From 070820f0e05266618ab64102417748ea774b5c77 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 6 Jul 2020 09:32:04 -0700 Subject: [PATCH 020/387] Support coroutines returning references (#682) --- strings/base_coroutine_threadpool.h | 6 +-- test/test/async_ref_result.cpp | 65 +++++++++++++++++++++++++++++ test/test/test.vcxproj | 1 + 3 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 test/test/async_ref_result.cpp diff --git a/strings/base_coroutine_threadpool.h b/strings/base_coroutine_threadpool.h index ae26677ef..acb03624f 100644 --- a/strings/base_coroutine_threadpool.h +++ b/strings/base_coroutine_threadpool.h @@ -166,7 +166,7 @@ namespace winrt::impl return free_await_adapter_impl{ static_cast(awaitable) }.suspend(handle); } - auto await_resume() + decltype(auto) await_resume() { return free_await_adapter_impl{ static_cast(awaitable) }.resume(); } @@ -188,7 +188,7 @@ namespace winrt::impl return awaitable.await_suspend(handle); } - auto await_resume() + decltype(auto) await_resume() { return awaitable.await_resume(); } @@ -243,7 +243,7 @@ namespace winrt::impl return awaitable.await_suspend(handle); } - auto await_resume() + decltype(auto) await_resume() { if (winrt_resume_handler) { diff --git a/test/test/async_ref_result.cpp b/test/test/async_ref_result.cpp new file mode 100644 index 000000000..015bb7b31 --- /dev/null +++ b/test/test/async_ref_result.cpp @@ -0,0 +1,65 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that references returned by awaitables + // are not accidentally decayed. + // + // This test "runs" at compile time via static_assert. + + template + struct awaitable : std::experimental::suspend_never + { + std::decay_t value; + T await_resume() { return static_cast(value); } + }; + + template + struct awaitable_member_awaiter : std::experimental::suspend_never + { + decltype(auto) get_awaiter() { return *this; } + std::decay_t value; + T await_resume() { return static_cast(value); } + }; + + template + struct awaitable_free_awaiter : std::experimental::suspend_never + { + std::decay_t value; + T await_resume() { return static_cast(value); } + }; + template + decltype(auto) get_awaiter(awaitable_free_awaiter&& value) { return std::move(value); } + + template typename A, typename T> + IAsyncAction Check() + { + decltype(auto) value = co_await A(); + static_assert(std::is_same_v); + } + + template typename A> + IAsyncAction Check() + { + co_await Check(); + co_await Check(); + co_await Check(); + co_await Check(); + } + + IAsyncAction Test() + { + co_await Check(); + co_await Check(); + co_await Check(); + } +} + +TEST_CASE("async_ref_result") +{ + Test().get(); +} diff --git a/test/test/test.vcxproj b/test/test/test.vcxproj index 1b4cc6cc5..f29835de6 100644 --- a/test/test/test.vcxproj +++ b/test/test/test.vcxproj @@ -294,6 +294,7 @@ + From 2e05e58d1a5bbed8f1e71e25f427b5f7242f011f Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Thu, 9 Jul 2020 07:37:03 -0700 Subject: [PATCH 021/387] Improve detection of mismatched header versions (#683) --- cppwinrt/code_writers.h | 3 ++- cppwinrt/cppwinrt.vcxproj | 1 + cppwinrt/cppwinrt.vcxproj.filters | 3 +++ cppwinrt/file_writers.h | 3 ++- strings/base_version.h | 6 ++++-- strings/base_version_odr.h | 2 ++ 6 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 strings/base_version_odr.h diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index c7f19d335..b44516d60 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -25,8 +25,9 @@ namespace cppwinrt { w.write_root_include("base"); auto format = R"(static_assert(winrt::check_version(CPPWINRT_VERSION, "%"), "Mismatched C++/WinRT headers."); +#define CPPWINRT_VERSION "%" )"; - w.write(format, CPPWINRT_VERSION_STRING); + w.write(format, CPPWINRT_VERSION_STRING, CPPWINRT_VERSION_STRING); } static void write_include_guard(writer& w) diff --git a/cppwinrt/cppwinrt.vcxproj b/cppwinrt/cppwinrt.vcxproj index 871316366..6055d3101 100644 --- a/cppwinrt/cppwinrt.vcxproj +++ b/cppwinrt/cppwinrt.vcxproj @@ -81,6 +81,7 @@ + diff --git a/cppwinrt/cppwinrt.vcxproj.filters b/cppwinrt/cppwinrt.vcxproj.filters index 069061a3b..21fabf0f2 100644 --- a/cppwinrt/cppwinrt.vcxproj.filters +++ b/cppwinrt/cppwinrt.vcxproj.filters @@ -136,6 +136,9 @@ strings + + strings + strings diff --git a/cppwinrt/file_writers.h b/cppwinrt/file_writers.h index 42fb86556..db81fddf9 100644 --- a/cppwinrt/file_writers.h +++ b/cppwinrt/file_writers.h @@ -6,6 +6,7 @@ namespace cppwinrt { writer w; write_preamble(w); + w.write(strings::base_version_odr, CPPWINRT_VERSION_STRING); write_open_file_guard(w, "BASE"); w.write(strings::base_includes); @@ -38,7 +39,7 @@ namespace cppwinrt w.write(strings::base_std_hash); w.write(strings::base_coroutine_threadpool); w.write(strings::base_natvis); - w.write(strings::base_version, CPPWINRT_VERSION_STRING); + w.write(strings::base_version); write_endif(w); w.flush_to_file(settings.output_folder + "winrt/base.h"); diff --git a/strings/base_version.h b/strings/base_version.h index 865792b82..227f10402 100644 --- a/strings/base_version.h +++ b/strings/base_version.h @@ -1,6 +1,4 @@ -#define CPPWINRT_VERSION "%" - // WINRT_version is used by Microsoft to analyze C++/WinRT library adoption and inform future product decisions. extern "C" __declspec(selectany) @@ -12,6 +10,10 @@ char const * const WINRT_version = "C++/WinRT version:" CPPWINRT_VERSION; #pragma comment(linker, "/include:WINRT_version") #endif +#if defined(_MSC_VER) +#pragma detect_mismatch("C++/WinRT version", CPPWINRT_VERSION) +#endif + WINRT_EXPORT namespace winrt { template diff --git a/strings/base_version_odr.h b/strings/base_version_odr.h new file mode 100644 index 000000000..03963dded --- /dev/null +++ b/strings/base_version_odr.h @@ -0,0 +1,2 @@ +#define CPPWINRT_VERSION "%" + From 5a5e33ce6b613ad209a10931684c753a3267324a Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Fri, 10 Jul 2020 08:24:02 -0700 Subject: [PATCH 022/387] Disable warning 4458 in delegates (#686) --- strings/base_delegate.h | 9 +++++++++ test/test/pch.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/strings/base_delegate.h b/strings/base_delegate.h index e3995dd11..fa1484dd2 100644 --- a/strings/base_delegate.h +++ b/strings/base_delegate.h @@ -1,6 +1,11 @@ namespace winrt::impl { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4458) // declaration hides class member (okay because we do not use named members of base class) +#endif + template struct implements_delegate : abi_t, H, update_module_lock { @@ -187,6 +192,10 @@ namespace winrt::impl return { static_cast(new variadic_delegate(std::forward(handler))), take_ownership_from_abi }; } }; + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif } WINRT_EXPORT namespace winrt diff --git a/test/test/pch.h b/test/test/pch.h index f027fb732..ea74230cc 100644 --- a/test/test/pch.h +++ b/test/test/pch.h @@ -1,5 +1,7 @@ #pragma once +#pragma warning(4: 4458) // ensure we compile clean with this warning enabled + #define WINRT_LEAN_AND_MEAN #include #include "winrt/Windows.Foundation.Collections.h" From 048c5c6a0f95328fcc882d215386b4632f57f89f Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Fri, 10 Jul 2020 08:24:31 -0700 Subject: [PATCH 023/387] Accommodate other "key not found" errors (#687) --- strings/base_error.h | 2 +- strings/base_types.h | 1 + test/old_tests/UnitTests/TryLookup.cpp | 40 ++++++++++++++++++-------- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/strings/base_error.h b/strings/base_error.h index 630e1dd33..f22214718 100644 --- a/strings/base_error.h +++ b/strings/base_error.h @@ -584,7 +584,7 @@ namespace winrt::impl { inline hresult check_hresult_allow_bounds(hresult const result) { - if (result != impl::error_out_of_bounds) + if (result != impl::error_out_of_bounds && result != impl::error_fail && result != impl::error_file_not_found) { check_hresult(result); } diff --git a/strings/base_types.h b/strings/base_types.h index 0eadc03cc..e6ca11fb0 100644 --- a/strings/base_types.h +++ b/strings/base_types.h @@ -147,4 +147,5 @@ namespace winrt::impl constexpr hresult error_canceled{ static_cast(0x800704C7) }; // HRESULT_FROM_WIN32(ERROR_CANCELLED) constexpr hresult error_bad_alloc{ static_cast(0x8007000E) }; // E_OUTOFMEMORY constexpr hresult error_not_initialized{ static_cast(0x800401F0) }; // CO_E_NOTINITIALIZED + constexpr hresult error_file_not_found{ static_cast(0x80070002) }; // HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) } diff --git a/test/old_tests/UnitTests/TryLookup.cpp b/test/old_tests/UnitTests/TryLookup.cpp index 47d2b5536..3c6c54580 100644 --- a/test/old_tests/UnitTests/TryLookup.cpp +++ b/test/old_tests/UnitTests/TryLookup.cpp @@ -110,21 +110,37 @@ TEST_CASE("TryRemove") TEST_CASE("TryLookup TryRemove error") { - // Simulate a non-agile map that is being accessed from the wrong thread. - // "Try" operations should throw rather than erroneously report "not found". - // Because they didn't even try. The operation never got off the ground. - struct incorrectly_used_non_agile_map : implements> + // A map that throws a specific error, used to verify various edge cases. + struct error_map : implements> { - int Lookup(int) { throw hresult_wrong_thread(); } - int32_t Size() { throw hresult_wrong_thread(); } - bool HasKey(int) { throw hresult_wrong_thread(); } - IMapView GetView() { throw hresult_wrong_thread(); } - bool Insert(int, int) { throw hresult_wrong_thread(); } - void Remove(int) { throw hresult_wrong_thread(); } - void Clear() { throw hresult_wrong_thread(); } + hresult code; + int Lookup(int) { throw_hresult(code); } + int32_t Size() { throw_hresult(E_UNEXPECTED); } // shouldn't be called by the test + bool HasKey(int) { throw_hresult(E_UNEXPECTED); } // shouldn't be called by the test + IMapView GetView() { throw_hresult(E_UNEXPECTED); } // shouldn't be called by the test + bool Insert(int, int) { throw_hresult(E_UNEXPECTED); } // shouldn't be called by the test + void Remove(int) { throw_hresult(code); } + void Clear() { throw_hresult(E_UNEXPECTED); } // shouldn't be called by the test }; - auto map = make(); + auto self = make_self(); + IMap map = *self; + + // Simulate a non-agile map that is being accessed from the wrong thread. + // "Try" operations should throw rather than erroneously report "not found". + // Because they didn't even try. The operation never got off the ground. + self->code = RPC_E_WRONG_THREAD; REQUIRE_THROWS_AS(map.TryLookup(123), hresult_wrong_thread); REQUIRE_THROWS_AS(map.TryRemove(123), hresult_wrong_thread); + + // Some implementations return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) + // or E_FAIL when the key is not present. + self->code = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + REQUIRE(!map.TryLookup(123)); + REQUIRE(!map.TryRemove(123)); + + self->code = E_FAIL; + REQUIRE(!map.TryLookup(123)); + REQUIRE(!map.TryRemove(123)); + } \ No newline at end of file From 5869973d47f77ea0da9b5005af757b39a4c7df8b Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 13 Jul 2020 09:06:00 -0700 Subject: [PATCH 024/387] diagnose throw_hresult(success_code) better (#689) --- strings/base_error.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/strings/base_error.h b/strings/base_error.h index f22214718..2b4eda9d8 100644 --- a/strings/base_error.h +++ b/strings/base_error.h @@ -183,17 +183,17 @@ WINRT_EXPORT namespace winrt return *this; } - explicit hresult_error(hresult const code) noexcept : m_code(code) + explicit hresult_error(hresult const code) noexcept : m_code(verify_error(code)) { originate(code, nullptr); } - hresult_error(hresult const code, param::hstring const& message) noexcept : m_code(code) + hresult_error(hresult const code, param::hstring const& message) noexcept : m_code(verify_error(code)) { originate(code, get_abi(message)); } - hresult_error(hresult const code, take_ownership_from_abi_t) noexcept : m_code(code) + hresult_error(hresult const code, take_ownership_from_abi_t) noexcept : m_code(verify_error(code)) { com_ptr info; WINRT_IMPL_GetErrorInfo(0, info.put_void()); @@ -306,6 +306,13 @@ WINRT_EXPORT namespace winrt WINRT_VERIFY(info.try_as(m_info)); } + static hresult verify_error(hresult const code) noexcept + { + WINRT_ASSERT(code < 0); + return code; + } + + #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-private-field" From 9ddc0bb4e767f9fc0c7d7cc2aafbdb1f909193e8 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Tue, 14 Jul 2020 10:39:23 -0700 Subject: [PATCH 025/387] Don't free the apartment_context while we are still using it (#691) --- strings/base_coroutine_threadpool.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/strings/base_coroutine_threadpool.h b/strings/base_coroutine_threadpool.h index acb03624f..2409aecf6 100644 --- a/strings/base_coroutine_threadpool.h +++ b/strings/base_coroutine_threadpool.h @@ -336,7 +336,8 @@ WINRT_EXPORT namespace winrt void await_suspend(std::experimental::coroutine_handle<> handle) const { - impl::resume_apartment(context, handle); + auto copy = context; // resuming may destruct *this, so use a copy + impl::resume_apartment(copy, handle); } impl::resume_apartment_context context; From aed79775b8ef0eb0d146f0856dc3ba88010845ea Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Tue, 21 Jul 2020 07:56:04 -0700 Subject: [PATCH 026/387] Add value type support to as() and try_as() (#695) --- strings/base_com_ptr.h | 13 +- strings/base_meta.h | 9 - strings/base_reference_produce.h | 142 ++++++++---- strings/base_windows.h | 36 ++- test/old_tests/UnitTests/Boxing2.cpp | 335 ++++++++++++--------------- test/old_tests/UnitTests/boxing.cpp | 129 ++++------- test/old_tests/UnitTests/com_ptr.cpp | 50 ++++ 7 files changed, 371 insertions(+), 343 deletions(-) diff --git a/strings/base_com_ptr.h b/strings/base_com_ptr.h index 3ae9d88cd..546910b82 100644 --- a/strings/base_com_ptr.h +++ b/strings/base_com_ptr.h @@ -133,8 +133,17 @@ WINRT_EXPORT namespace winrt template bool try_as(To& to) const noexcept { - to = try_as>(); - return static_cast(to); + if constexpr (impl::is_com_interface_v || !std::is_same_v>) + { + to = try_as>(); + return static_cast(to); + } + else + { + auto result = try_as(); + to = result.has_value() ? result.value() : impl::empty_value(); + return result.has_value(); + } } hresult as(guid const& id, void** result) const noexcept diff --git a/strings/base_meta.h b/strings/base_meta.h index 01e102b5b..d2b386442 100644 --- a/strings/base_meta.h +++ b/strings/base_meta.h @@ -226,15 +226,6 @@ namespace winrt::impl template using wrapped_type_t = typename wrapped_type::type; - template