353 lines
13 KiB
C++
353 lines
13 KiB
C++
//*********************************************************
|
|
//
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
// This code is licensed under the MIT License.
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
//
|
|
//*********************************************************
|
|
|
|
#ifndef __WIL_CPPWINRT_HELPERS_DEFINED
|
|
#define __WIL_CPPWINRT_HELPERS_DEFINED
|
|
|
|
/// @cond
|
|
namespace wil::details
|
|
{
|
|
struct dispatcher_RunAsync
|
|
{
|
|
template<typename Dispatcher, typename... Args>
|
|
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
|
|
{
|
|
dispatcher.RunAsync(std::forward<Args>(args)...);
|
|
}
|
|
};
|
|
|
|
struct dispatcher_TryEnqueue
|
|
{
|
|
template<typename Dispatcher, typename... Args>
|
|
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
|
|
{
|
|
dispatcher.TryEnqueue(std::forward<Args>(args)...);
|
|
}
|
|
};
|
|
|
|
template<typename Dispatcher> struct dispatcher_traits;
|
|
}
|
|
|
|
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
|
|
#include <experimental/coroutine>
|
|
namespace wil::details
|
|
{
|
|
template<typename T = void> using coroutine_handle = std::experimental::coroutine_handle<T>;
|
|
}
|
|
#elif defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)
|
|
#include <coroutine>
|
|
namespace wil::details
|
|
{
|
|
template<typename T = void> using coroutine_handle = std::coroutine_handle<T>;
|
|
}
|
|
#endif
|
|
/// @endcond
|
|
|
|
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) || (defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L))
|
|
/// @cond
|
|
namespace wil::details
|
|
{
|
|
struct dispatched_handler_state
|
|
{
|
|
details::coroutine_handle<> handle{};
|
|
bool orphaned = false;
|
|
};
|
|
|
|
struct dispatcher_handler
|
|
{
|
|
dispatcher_handler(dispatched_handler_state* state) : m_state(state) { }
|
|
dispatcher_handler(dispatcher_handler&& other) noexcept : m_state(std::exchange(other.m_state, {})) {}
|
|
|
|
~dispatcher_handler()
|
|
{
|
|
if (m_state && m_state->handle)
|
|
{
|
|
m_state->orphaned = true;
|
|
Complete();
|
|
}
|
|
}
|
|
void operator()()
|
|
{
|
|
Complete();
|
|
}
|
|
|
|
void Complete()
|
|
{
|
|
auto state = std::exchange(m_state, nullptr);
|
|
std::exchange(state->handle, {}).resume();
|
|
}
|
|
|
|
dispatched_handler_state* m_state;
|
|
};
|
|
}
|
|
/// @endcond
|
|
|
|
namespace wil
|
|
{
|
|
//! Resumes coroutine execution on the thread associated with the dispatcher, or throws
|
|
//! an exception (from an arbitrary thread) if unable. Supported dispatchers are
|
|
//! Windows.System.DispatcherQueue, Microsoft.System.DispatcherQueue,
|
|
//! Microsoft.UI.Dispatching.DispatcherQueue, and Windows.UI.Core.CoreDispatcher,
|
|
//! but you must include the corresponding <winrt/Namespace.h> header before including
|
|
//! wil\cppwinrt_helpers.h. It is okay to include wil\cppwinrt_helpers.h multiple times:
|
|
//! support will be enabled for any winrt/Namespace.h headers that were included since
|
|
//! the previous inclusion of wil\cppwinrt_headers.h.
|
|
template<typename Dispatcher>
|
|
[[nodiscard]] auto resume_foreground(Dispatcher const& dispatcher,
|
|
typename details::dispatcher_traits<Dispatcher>::Priority priority = details::dispatcher_traits<Dispatcher>::Priority::Normal)
|
|
{
|
|
using Traits = details::dispatcher_traits<Dispatcher>;
|
|
using Priority = typename Traits::Priority;
|
|
using Handler = typename Traits::Handler;
|
|
|
|
struct awaitable
|
|
{
|
|
awaitable(Dispatcher const& dispatcher, Priority priority) noexcept :
|
|
m_dispatcher(dispatcher),
|
|
m_priority(priority)
|
|
{
|
|
}
|
|
bool await_ready() const noexcept { return false; }
|
|
|
|
void await_suspend(details::coroutine_handle<> handle)
|
|
{
|
|
m_state.handle = handle;
|
|
Handler handler{ details::dispatcher_handler(&m_state) };
|
|
try
|
|
{
|
|
// The return value of Schedule is not reliable. Use the dispatcher_handler destructor
|
|
// to detect whether the work item failed to run.
|
|
Traits::Scheduler::Schedule(m_dispatcher, m_priority, handler);
|
|
}
|
|
catch (...)
|
|
{
|
|
m_state.handle = nullptr; // the exception will resume the coroutine, so the handler shouldn't do it
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void await_resume() const
|
|
{
|
|
if (m_state.orphaned)
|
|
{
|
|
throw winrt::hresult_error(static_cast<winrt::hresult>(0x800701ab)); // HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE)
|
|
}
|
|
}
|
|
|
|
private:
|
|
Dispatcher const& m_dispatcher;
|
|
Priority const m_priority;
|
|
details::dispatched_handler_state m_state;
|
|
};
|
|
return awaitable{ dispatcher, priority };
|
|
}
|
|
}
|
|
#endif // Coroutines are supported
|
|
|
|
#endif // __WIL_CPPWINRT_HELPERS_DEFINED
|
|
|
|
/// @cond
|
|
#if defined(WINRT_Windows_UI_Core_H) && !defined(__WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS)
|
|
#define __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS
|
|
namespace wil::details
|
|
{
|
|
template<>
|
|
struct dispatcher_traits<winrt::Windows::UI::Core::CoreDispatcher>
|
|
{
|
|
using Priority = winrt::Windows::UI::Core::CoreDispatcherPriority;
|
|
using Handler = winrt::Windows::UI::Core::DispatchedHandler;
|
|
using Scheduler = dispatcher_RunAsync;
|
|
};
|
|
}
|
|
#endif // __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS
|
|
|
|
#if defined(WINRT_Windows_System_H) && !defined(__WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS)
|
|
#define __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS
|
|
namespace wil::details
|
|
{
|
|
template<>
|
|
struct dispatcher_traits<winrt::Windows::System::DispatcherQueue>
|
|
{
|
|
using Priority = winrt::Windows::System::DispatcherQueuePriority;
|
|
using Handler = winrt::Windows::System::DispatcherQueueHandler;
|
|
using Scheduler = dispatcher_TryEnqueue;
|
|
};
|
|
}
|
|
#endif // __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS
|
|
|
|
#if defined(WINRT_Microsoft_System_H) && !defined(__WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS)
|
|
#define __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS
|
|
namespace wil::details
|
|
{
|
|
template<>
|
|
struct dispatcher_traits<winrt::Microsoft::System::DispatcherQueue>
|
|
{
|
|
using Priority = winrt::Microsoft::System::DispatcherQueuePriority;
|
|
using Handler = winrt::Microsoft::System::DispatcherQueueHandler;
|
|
using Scheduler = dispatcher_TryEnqueue;
|
|
};
|
|
}
|
|
#endif // __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS
|
|
|
|
#if defined(WINRT_Microsoft_UI_Dispatching_H) && !defined(__WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS)
|
|
#define __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
|
|
namespace wil::details
|
|
{
|
|
template<>
|
|
struct dispatcher_traits<winrt::Microsoft::UI::Dispatching::DispatcherQueue>
|
|
{
|
|
using Priority = winrt::Microsoft::UI::Dispatching::DispatcherQueuePriority;
|
|
using Handler = winrt::Microsoft::UI::Dispatching::DispatcherQueueHandler;
|
|
using Scheduler = dispatcher_TryEnqueue;
|
|
};
|
|
}
|
|
#endif // __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
|
|
/// @endcond
|
|
|
|
#if defined(WINRT_Windows_Foundation_Collections_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS)
|
|
#define __WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS
|
|
namespace wil
|
|
{
|
|
/// @cond
|
|
namespace details
|
|
{
|
|
template<typename T> struct is_winrt_vector_like {
|
|
private:
|
|
template <typename U,
|
|
typename = decltype(std::declval<U>().GetMany(std::declval<U>().Size(),
|
|
winrt::array_view<decltype(std::declval<U>().GetAt(0))>{}))>
|
|
static constexpr bool get_value(int) { return true; }
|
|
template <typename> static constexpr bool get_value(...) { return false; }
|
|
public:
|
|
static constexpr bool value = get_value<T>(0);
|
|
};
|
|
|
|
template<typename T> struct is_winrt_iterator_like {
|
|
private:
|
|
template <typename U,
|
|
typename = decltype(std::declval<U>().GetMany(winrt::array_view<decltype(std::declval<U>().Current())>{}))>
|
|
static constexpr bool get_value(int) { return true; }
|
|
template <typename> static constexpr bool get_value(...) { return false; }
|
|
public:
|
|
static constexpr bool value = get_value<T>(0);
|
|
};
|
|
|
|
template<typename T> constexpr T empty() noexcept
|
|
{
|
|
if constexpr (std::is_base_of_v<winrt::Windows::Foundation::IUnknown, T>)
|
|
{
|
|
return nullptr;
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
/// @endcond
|
|
|
|
/** Converts C++ / WinRT vectors, iterators, and iterables to std::vector by requesting the
|
|
collection's data in bulk. This can be more efficient in terms of IPC cost than iteratively
|
|
processing the collection.
|
|
~~~
|
|
winrt::IVector<winrt::hstring> collection = GetCollection();
|
|
std::vector<winrt::hstring> allData = wil::to_vector(collection); // read all data from collection
|
|
for (winrt::hstring const& item : allData)
|
|
{
|
|
// use item
|
|
}
|
|
~~~
|
|
Can be used for IVector<T>, IVectorView<T>, IIterable<T>, IIterator<T>, and any type or
|
|
interface that C++/WinRT projects those interfaces for (PropertySet, IMap<T,K>, etc.)
|
|
Iterable-only types fetch content in units of 64. When used with an iterator, the returned
|
|
vector contains the iterator's current position and any others after it.
|
|
*/
|
|
template<typename TSrc> auto to_vector(TSrc const& src)
|
|
{
|
|
if constexpr (details::is_winrt_vector_like<TSrc>::value)
|
|
{
|
|
using T = decltype(src.GetAt(0));
|
|
std::vector<T> result;
|
|
if (auto expected = src.Size())
|
|
{
|
|
result.resize(expected + 1, details::empty<T>());
|
|
auto actual = src.GetMany(0, result);
|
|
if (actual > expected)
|
|
{
|
|
throw winrt::hresult_changed_state();
|
|
}
|
|
result.resize(actual);
|
|
}
|
|
return result;
|
|
}
|
|
else if constexpr (details::is_winrt_iterator_like<TSrc>::value)
|
|
{
|
|
using T = decltype(src.Current());
|
|
std::vector<T> result;
|
|
constexpr uint32_t chunkSize = 64;
|
|
while (true)
|
|
{
|
|
auto const lastSize = result.size();
|
|
result.resize(lastSize + chunkSize, details::empty<T>());
|
|
auto fetched = src.GetMany({result.data() + lastSize, result.data() + lastSize + chunkSize });
|
|
if (fetched < chunkSize)
|
|
{
|
|
result.resize(lastSize + fetched);
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return to_vector(src.First());
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(WINRT_Windows_UI_H) && defined(_WINDOWS_UI_INTEROP_H_) && !defined(__WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS)
|
|
#define __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS
|
|
#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
|
|
#pragma push_macro("ABI")
|
|
#undef ABI
|
|
#define ABI
|
|
#endif
|
|
|
|
namespace wil
|
|
{
|
|
#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)
|
|
//! The following methods require that you include both <winrt/Windows.UI.h>
|
|
//! <Windows.UI.Interop.h> before including wil/cppwinrt_helpers.h, and that NTDDI_VERSION
|
|
//! is at least NTDDI_WIN10_CU. It is okay to include wil\cppwinrt_helpers.h multiple times:
|
|
//! support will be enabled for any headers that were included since the previous inclusion
|
|
//! of wil\cppwinrt_headers.h.
|
|
inline winrt::Windows::UI::WindowId GetWindowIdFromWindow(HWND hwnd)
|
|
{
|
|
ABI::Windows::UI::WindowId abiWindowId;
|
|
winrt::check_hresult(::GetWindowIdFromWindow(hwnd, &abiWindowId));
|
|
return winrt::Windows::UI::WindowId{ abiWindowId.Value };
|
|
}
|
|
|
|
inline HWND GetWindowFromWindowId(winrt::Windows::UI::WindowId windowId)
|
|
{
|
|
HWND hwnd;
|
|
winrt::check_hresult(::GetWindowFromWindowId({ windowId.Value }, &hwnd));
|
|
return hwnd;
|
|
}
|
|
#endif /*defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)*/
|
|
}
|
|
|
|
#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
|
|
#pragma pop_macro("ABI")
|
|
#endif
|
|
#endif // __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS
|