diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index b3855c097..a9aa5bc78 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -1475,24 +1475,36 @@ namespace cppwinrt else if (type_name == "Windows.Foundation.IAsyncAction") { w.write(R"( auto get() const; + // Synchronously waits without asserting that the calling thread is not an STA. + // Use only when the STA is not presenting UI and blocking is known to be safe. + auto get_unchecked() const; auto wait_for(Windows::Foundation::TimeSpan const& timeout) const; )"); } else if (type_name == "Windows.Foundation.IAsyncOperation`1") { w.write(R"( auto get() const; + // Synchronously waits without asserting that the calling thread is not an STA. + // Use only when the STA is not presenting UI and blocking is known to be safe. + auto get_unchecked() const; auto wait_for(Windows::Foundation::TimeSpan const& timeout) const; )"); } else if (type_name == "Windows.Foundation.IAsyncActionWithProgress`1") { w.write(R"( auto get() const; + // Synchronously waits without asserting that the calling thread is not an STA. + // Use only when the STA is not presenting UI and blocking is known to be safe. + auto get_unchecked() const; auto wait_for(Windows::Foundation::TimeSpan const& timeout) const; )"); } else if (type_name == "Windows.Foundation.IAsyncOperationWithProgress`2") { w.write(R"( auto get() const; + // Synchronously waits without asserting that the calling thread is not an STA. + // Use only when the STA is not presenting UI and blocking is known to be safe. + auto get_unchecked() const; auto wait_for(Windows::Foundation::TimeSpan const& timeout) const; )"); } diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 5cefad836..82582ee64 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -99,6 +99,19 @@ WINRT_EXPORT namespace winrt::impl return async.GetResults(); } + template + auto wait_get_bypass_sta_check(Async const& async) + { + auto status = async.Status(); + if (status == Windows::Foundation::AsyncStatus::Started) + { + status = wait_for_completed(async, 0xFFFFFFFF); // INFINITE + } + check_status_canceled(status); + + return async.GetResults(); + } + #ifdef WINRT_IMPL_COROUTINES struct ignore_apartment_context {}; @@ -220,6 +233,11 @@ WINRT_EXPORT namespace winrt::impl impl::wait_get(static_cast(static_cast(*this))); } template + auto consume_Windows_Foundation_IAsyncAction::get_unchecked() const + { + impl::wait_get_bypass_sta_check(static_cast(static_cast(*this))); + } + template auto consume_Windows_Foundation_IAsyncAction::wait_for(Windows::Foundation::TimeSpan const& timeout) const { return impl::wait_for(static_cast(static_cast(*this)), timeout); @@ -231,6 +249,11 @@ WINRT_EXPORT namespace winrt::impl return impl::wait_get(static_cast const&>(static_cast(*this))); } template + auto consume_Windows_Foundation_IAsyncOperation::get_unchecked() const + { + return impl::wait_get_bypass_sta_check(static_cast const&>(static_cast(*this))); + } + template auto consume_Windows_Foundation_IAsyncOperation::wait_for(Windows::Foundation::TimeSpan const& timeout) const { return impl::wait_for(static_cast const&>(static_cast(*this)), timeout); @@ -242,6 +265,11 @@ WINRT_EXPORT namespace winrt::impl impl::wait_get(static_cast const&>(static_cast(*this))); } template + auto consume_Windows_Foundation_IAsyncActionWithProgress::get_unchecked() const + { + impl::wait_get_bypass_sta_check(static_cast const&>(static_cast(*this))); + } + template auto consume_Windows_Foundation_IAsyncActionWithProgress::wait_for(Windows::Foundation::TimeSpan const& timeout) const { return impl::wait_for(static_cast const&>(static_cast(*this)), timeout); @@ -253,6 +281,11 @@ WINRT_EXPORT namespace winrt::impl return impl::wait_get(static_cast const&>(static_cast(*this))); } template + auto consume_Windows_Foundation_IAsyncOperationWithProgress::get_unchecked() const + { + return impl::wait_get_bypass_sta_check(static_cast const&>(static_cast(*this))); + } + template auto consume_Windows_Foundation_IAsyncOperationWithProgress::wait_for(Windows::Foundation::TimeSpan const& timeout) const { return impl::wait_for(static_cast const&>(static_cast(*this)), timeout); diff --git a/test/test_nocoro/get.cpp b/test/test_nocoro/get.cpp index 11339e10b..4fd85fe3e 100644 --- a/test/test_nocoro/get.cpp +++ b/test/test_nocoro/get.cpp @@ -2,6 +2,7 @@ using namespace winrt; using namespace Windows::Foundation; +using namespace Windows::Storage; template struct async_completion_source : implements, IAsyncOperation, IAsyncInfo> @@ -72,3 +73,42 @@ TEST_CASE("get") REQUIRE(acs.as>().get() == 0xDEADBEEF); } + +TEST_CASE("get_unchecked") +{ + // Call a real WinRT async operation from an STA thread. + // This is the scenario the new API is designed for: an STA that is not + // presenting UI, where a synchronous blocking wait is safe. + std::exception_ptr failure{}; + bool content_available = false; + std::thread sta_thread([&failure, &content_available] + { + try + { + winrt::init_apartment(winrt::apartment_type::single_threaded); + struct apartment_guard + { + ~apartment_guard() + { + winrt::uninit_apartment(); + } + } guard; + + auto content = PathIO::ReadTextAsync(L"C:\\Windows\\win.ini").get_unchecked(); + content_available = content.size() > 0; + } + catch (...) + { + failure = std::current_exception(); + } + }); + + sta_thread.join(); + + if (failure) + { + std::rethrow_exception(failure); + } + + REQUIRE(content_available); +} diff --git a/test/test_nocoro/pch.h b/test/test_nocoro/pch.h index 7ff48a37c..30a6d3079 100644 --- a/test/test_nocoro/pch.h +++ b/test/test_nocoro/pch.h @@ -2,5 +2,6 @@ #include "catch.hpp" #include "winrt/Windows.Foundation.h" +#include "winrt/Windows.Storage.h" using namespace std::literals;