mirror of https://github.com/PCSX2/pcsx2.git
2521 lines
93 KiB
C++
2521 lines
93 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.
|
|
//
|
|
//*********************************************************
|
|
//! @file
|
|
//! Various types and functions for working with the Windows Runtime
|
|
#ifndef __WIL_WINRT_INCLUDED
|
|
#define __WIL_WINRT_INCLUDED
|
|
|
|
#include <hstring.h>
|
|
#include <wrl/client.h>
|
|
#include <wrl/implements.h>
|
|
#include <wrl/async.h>
|
|
#include <wrl/wrappers/corewrappers.h>
|
|
#include "result.h"
|
|
#include "com.h"
|
|
#include "resource.h"
|
|
#include <windows.foundation.h>
|
|
#include <windows.foundation.collections.h>
|
|
|
|
#ifdef __cplusplus_winrt
|
|
#include <collection.h> // bring in the CRT iterator for support for C++ CX code
|
|
#endif
|
|
|
|
/// @cond
|
|
#if defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WI_HAS_STD_LESS)
|
|
#ifdef __has_include
|
|
#if __has_include(<functional>)
|
|
#define __WI_HAS_STD_LESS 1
|
|
#include <functional>
|
|
#endif // Otherwise, not using STL; don't specialize std::less
|
|
#else
|
|
// Fall back to the old way of forward declaring std::less
|
|
#define __WI_HAS_STD_LESS 1
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4643) // Forward declaring '...' in namespace std is not permitted by the C++ Standard.
|
|
namespace std
|
|
{
|
|
template <class _Ty>
|
|
struct less;
|
|
}
|
|
#pragma warning(pop)
|
|
#endif
|
|
#endif
|
|
#if defined(WIL_ENABLE_EXCEPTIONS) && defined(__has_include)
|
|
#if __has_include(<vector>)
|
|
#define __WI_HAS_STD_VECTOR 1
|
|
#include <vector>
|
|
#endif
|
|
#endif
|
|
/// @endcond
|
|
|
|
// This enables this code to be used in code that uses the ABI prefix or not.
|
|
// Code using the public SDK and C++ CX code has the ABI prefix, windows internal
|
|
// is built in a way that does not.
|
|
/// @cond
|
|
#if !defined(MIDL_NS_PREFIX) && !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__)
|
|
// Internal .idl files use the namespace without the ABI prefix. Macro out ABI for that case
|
|
#pragma push_macro("ABI")
|
|
#undef ABI
|
|
#define ABI
|
|
#endif
|
|
/// @endcond
|
|
|
|
namespace wil
|
|
{
|
|
// time_t is the number of 1 - second intervals since January 1, 1970.
|
|
constexpr long long SecondsToStartOf1970 = 0x2b6109100;
|
|
constexpr long long HundredNanoSecondsInSecond = 10000000LL;
|
|
|
|
inline __time64_t DateTime_to_time_t(ABI::Windows::Foundation::DateTime dateTime)
|
|
{
|
|
// DateTime is the number of 100 - nanosecond intervals since January 1, 1601.
|
|
return (dateTime.UniversalTime / HundredNanoSecondsInSecond - SecondsToStartOf1970);
|
|
}
|
|
|
|
inline ABI::Windows::Foundation::DateTime time_t_to_DateTime(__time64_t timeT)
|
|
{
|
|
ABI::Windows::Foundation::DateTime dateTime;
|
|
dateTime.UniversalTime = (timeT + SecondsToStartOf1970) * HundredNanoSecondsInSecond;
|
|
return dateTime;
|
|
}
|
|
|
|
#pragma region HSTRING Helpers
|
|
/// @cond
|
|
namespace details
|
|
{
|
|
// hstring_compare is used to assist in HSTRING comparison of two potentially non-similar string types. E.g.
|
|
// comparing a raw HSTRING with WRL's HString/HStringReference/etc. The consumer can optionally inhibit the
|
|
// deduction of array sizes by providing 'true' for the 'InhibitStringArrays' template argument. This is
|
|
// generally done in scenarios where the consumer cannot guarantee that the input argument types are perfectly
|
|
// preserved from end-to-end. E.g. if a single function in the execution path captures an array as const T&,
|
|
// then it is impossible to differentiate const arrays (where we generally do want to deduce length) from
|
|
// non-const arrays (where we generally do not want to deduce length). The consumer can also optionally choose
|
|
// to perform case-insensitive comparison by providing 'true' for the 'IgnoreCase' template argument.
|
|
template <bool InhibitStringArrays, bool IgnoreCase>
|
|
struct hstring_compare
|
|
{
|
|
// get_buffer returns the string buffer and length for the supported string types
|
|
static const wchar_t* get_buffer(HSTRING hstr, UINT32* length) WI_NOEXCEPT
|
|
{
|
|
return ::WindowsGetStringRawBuffer(hstr, length);
|
|
}
|
|
|
|
static const wchar_t* get_buffer(const Microsoft::WRL::Wrappers::HString& hstr, UINT32* length) WI_NOEXCEPT
|
|
{
|
|
return hstr.GetRawBuffer(length);
|
|
}
|
|
|
|
static const wchar_t* get_buffer(const Microsoft::WRL::Wrappers::HStringReference& hstr, UINT32* length) WI_NOEXCEPT
|
|
{
|
|
return hstr.GetRawBuffer(length);
|
|
}
|
|
|
|
static const wchar_t* get_buffer(const unique_hstring& str, UINT32* length) WI_NOEXCEPT
|
|
{
|
|
return ::WindowsGetStringRawBuffer(str.get(), length);
|
|
}
|
|
|
|
template <bool..., bool Enable = InhibitStringArrays>
|
|
static wistd::enable_if_t<Enable, const wchar_t*> get_buffer(const wchar_t* str, UINT32* length) WI_NOEXCEPT
|
|
{
|
|
str = (str != nullptr) ? str : L"";
|
|
*length = static_cast<UINT32>(wcslen(str));
|
|
return str;
|
|
}
|
|
|
|
template <typename StringT, bool..., bool Enable = !InhibitStringArrays>
|
|
static wistd::enable_if_t<
|
|
wistd::conjunction<wistd::is_pointer<StringT>, wistd::is_same<wistd::decay_t<wistd::remove_pointer_t<StringT>>, wchar_t>, wistd::bool_constant<Enable>>::value,
|
|
const wchar_t*>
|
|
get_buffer(StringT str, UINT32* length) WI_NOEXCEPT
|
|
{
|
|
str = (str != nullptr) ? str : L"";
|
|
*length = static_cast<UINT32>(wcslen(str));
|
|
return str;
|
|
}
|
|
|
|
template <size_t Size, bool..., bool Enable = !InhibitStringArrays>
|
|
static wistd::enable_if_t<Enable, const wchar_t*> get_buffer(const wchar_t (&str)[Size], UINT32* length) WI_NOEXCEPT
|
|
{
|
|
*length = Size - 1;
|
|
return str;
|
|
}
|
|
|
|
template <size_t Size, bool..., bool Enable = !InhibitStringArrays>
|
|
static wistd::enable_if_t<Enable, const wchar_t*> get_buffer(wchar_t (&str)[Size], UINT32* length) WI_NOEXCEPT
|
|
{
|
|
*length = static_cast<UINT32>(wcslen(str));
|
|
return str;
|
|
}
|
|
|
|
// Overload for std::wstring, or at least things that behave like std::wstring, without adding a dependency
|
|
// on STL headers
|
|
template <typename StringT>
|
|
static wistd::enable_if_t<
|
|
wistd::conjunction_v<
|
|
wistd::is_constructible<StringT, wchar_t*>,
|
|
wistd::is_convertible<decltype(wistd::declval<StringT>().data()), const wchar_t*>,
|
|
wistd::is_same<typename StringT::size_type, decltype(wistd::declval<StringT>().size())>>,
|
|
const wchar_t*>
|
|
get_buffer(const StringT& str, UINT32* length) WI_NOEXCEPT
|
|
{
|
|
*length = static_cast<UINT32>(str.size());
|
|
const wchar_t* ret = str.data();
|
|
return ret ? ret : L"";
|
|
}
|
|
|
|
template <typename LhsT, typename RhsT>
|
|
static auto compare(LhsT&& lhs, RhsT&& rhs)
|
|
-> decltype(get_buffer(lhs, wistd::declval<UINT32*>()), get_buffer(rhs, wistd::declval<UINT32*>()), int())
|
|
{
|
|
UINT32 lhsLength;
|
|
UINT32 rhsLength;
|
|
auto lhsBuffer = get_buffer(wistd::forward<LhsT>(lhs), &lhsLength);
|
|
auto rhsBuffer = get_buffer(wistd::forward<RhsT>(rhs), &rhsLength);
|
|
|
|
const auto result = ::CompareStringOrdinal(lhsBuffer, lhsLength, rhsBuffer, rhsLength, IgnoreCase ? TRUE : FALSE);
|
|
WI_ASSERT(result != 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename LhsT, typename RhsT>
|
|
static auto equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT->decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
|
{
|
|
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_EQUAL;
|
|
}
|
|
|
|
template <typename LhsT, typename RhsT>
|
|
static auto not_equals(LhsT&& lhs, RhsT&& rhs)
|
|
WI_NOEXCEPT->decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
|
{
|
|
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_EQUAL;
|
|
}
|
|
|
|
template <typename LhsT, typename RhsT>
|
|
static auto less(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT->decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
|
{
|
|
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_LESS_THAN;
|
|
}
|
|
|
|
template <typename LhsT, typename RhsT>
|
|
static auto less_equals(LhsT&& lhs, RhsT&& rhs)
|
|
WI_NOEXCEPT->decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
|
{
|
|
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_GREATER_THAN;
|
|
}
|
|
|
|
template <typename LhsT, typename RhsT>
|
|
static auto greater(LhsT&& lhs, RhsT&& rhs)
|
|
WI_NOEXCEPT->decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
|
{
|
|
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_GREATER_THAN;
|
|
}
|
|
|
|
template <typename LhsT, typename RhsT>
|
|
static auto greater_equals(LhsT&& lhs, RhsT&& rhs)
|
|
WI_NOEXCEPT->decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
|
{
|
|
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_LESS_THAN;
|
|
}
|
|
};
|
|
} // namespace details
|
|
/// @endcond
|
|
|
|
//! Detects if one or more embedded null is present in an HSTRING.
|
|
inline bool HasEmbeddedNull(_In_opt_ HSTRING value)
|
|
{
|
|
BOOL hasEmbeddedNull = FALSE;
|
|
(void)WindowsStringHasEmbeddedNull(value, &hasEmbeddedNull);
|
|
return hasEmbeddedNull != FALSE;
|
|
}
|
|
|
|
/** TwoPhaseHStringConstructor help using the 2 phase constructor pattern for HSTRINGs.
|
|
@code
|
|
auto stringConstructor = wil::TwoPhaseHStringConstructor::Preallocate(size);
|
|
RETURN_IF_NULL_ALLOC(stringConstructor.Get());
|
|
|
|
RETURN_IF_FAILED(stream->Read(stringConstructor.Get(), stringConstructor.ByteSize(), &bytesRead));
|
|
|
|
// Validate stream contents, sizes must match, string must be null terminated.
|
|
RETURN_IF_FAILED(stringConstructor.Validate(bytesRead));
|
|
|
|
wil::unique_hstring string { stringConstructor.Promote() };
|
|
@endcode
|
|
|
|
See also wil::unique_hstring_buffer.
|
|
*/
|
|
struct TwoPhaseHStringConstructor
|
|
{
|
|
TwoPhaseHStringConstructor() = delete;
|
|
TwoPhaseHStringConstructor(const TwoPhaseHStringConstructor&) = delete;
|
|
void operator=(const TwoPhaseHStringConstructor&) = delete;
|
|
|
|
TwoPhaseHStringConstructor(TwoPhaseHStringConstructor&& other) WI_NOEXCEPT
|
|
{
|
|
m_characterLength = other.m_characterLength;
|
|
other.m_characterLength = 0;
|
|
m_maker = wistd::move(other.m_maker);
|
|
}
|
|
|
|
static TwoPhaseHStringConstructor Preallocate(UINT32 characterLength)
|
|
{
|
|
return TwoPhaseHStringConstructor{characterLength};
|
|
}
|
|
|
|
//! Returns the HSTRING after it has been populated like Detatch() or release(); be sure to put this in a RAII type to manage
|
|
//! its lifetime.
|
|
HSTRING Promote()
|
|
{
|
|
m_characterLength = 0;
|
|
return m_maker.release().release();
|
|
}
|
|
|
|
~TwoPhaseHStringConstructor() = default;
|
|
|
|
WI_NODISCARD explicit operator PCWSTR() const
|
|
{
|
|
// This is set by WindowsPromoteStringBuffer() which must be called to
|
|
// construct this object via the static method Preallocate().
|
|
return m_maker.buffer();
|
|
}
|
|
|
|
//! Returns a pointer for the buffer so it can be populated
|
|
WI_NODISCARD wchar_t* Get() const
|
|
{
|
|
return const_cast<wchar_t*>(m_maker.buffer());
|
|
}
|
|
//! Used to validate range of buffer when populating.
|
|
WI_NODISCARD ULONG ByteSize() const
|
|
{
|
|
return m_characterLength * sizeof(wchar_t);
|
|
}
|
|
|
|
/** Ensure that the size of the data provided is consistent with the pre-allocated buffer.
|
|
It seems that WindowsPreallocateStringBuffer() provides the null terminator in the buffer
|
|
(based on testing) so this can be called before populating the buffer.
|
|
*/
|
|
WI_NODISCARD HRESULT Validate(ULONG bytesRead) const
|
|
{
|
|
// Null termination is required for the buffer before calling WindowsPromoteStringBuffer().
|
|
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), (bytesRead != ByteSize()) || (Get()[m_characterLength] != L'\0'));
|
|
return S_OK;
|
|
}
|
|
|
|
private:
|
|
TwoPhaseHStringConstructor(UINT32 characterLength) : m_characterLength(characterLength)
|
|
{
|
|
(void)m_maker.make(nullptr, characterLength);
|
|
}
|
|
|
|
UINT32 m_characterLength;
|
|
details::string_maker<unique_hstring> m_maker;
|
|
};
|
|
|
|
//! A transparent less-than comparison function object that enables comparison of various string types intended for
|
|
//! use with associative containers (such as `std::set`, `std::map`, etc.) that use
|
|
//! `Microsoft::WRL::Wrappers::HString` as the key type. This removes the need for the consumer to explicitly
|
|
//! create an `HString` object when using lookup functions such as `find`, `lower_bound`, etc. For example, the
|
|
//! following scenarios would all work exactly as you would expect them to:
|
|
//! ~~~
|
|
//! std::map<HString, int, wil::hstring_less> map;
|
|
//! const wchar_t constArray[] = L"foo";
|
|
//! wchar_t nonConstArray[MAX_PATH] = L"foo";
|
|
//!
|
|
//! HString key;
|
|
//! THROW_IF_FAILED(key.Set(constArray));
|
|
//! map.emplace(std::move(key), 42);
|
|
//!
|
|
//! HString str;
|
|
//! wil::unique_hstring uniqueStr;
|
|
//! THROW_IF_FAILED(str.Set(L"foo"));
|
|
//! THROW_IF_FAILED(str.CopyTo(&uniqueStr));
|
|
//!
|
|
//! // All of the following return an iterator to the pair { L"foo", 42 }
|
|
//! map.find(str);
|
|
//! map.find(str.Get());
|
|
//! map.find(HStringReference(constArray));
|
|
//! map.find(uniqueStr);
|
|
//! map.find(std::wstring(constArray));
|
|
//! map.find(constArray);
|
|
//! map.find(nonConstArray);
|
|
//! map.find(static_cast<const wchar_t*>(constArray));
|
|
//! ~~~
|
|
//! The first four calls in the example above use `WindowsGetStringRawBuffer` (or equivalent) to get the string
|
|
//! buffer and length for the comparison. The fifth example uses `std::wstring::c_str` and `std::wstring::length`
|
|
//! for getting these two values. The remaining three examples use only the string buffer and call `wcslen` for the
|
|
//! length. That is, the length is *not* deduced for either array. This is because argument types are not always
|
|
//! perfectly preserved by container functions and in fact are often captured as const references making it
|
|
//! impossible to differentiate const arrays - where we can safely deduce length - from non const arrays - where we
|
|
//! cannot safely deduce length since the buffer may be larger than actually needed (e.g. creating a
|
|
//! `char[MAX_PATH]` array, but only filling it with 10 characters). The implications of this behavior is that
|
|
//! string literals that contain embedded null characters will only include the part of the buffer up to the first
|
|
//! null character. For example, the following example will result in all calls to `find` returning an end
|
|
//! iterator.
|
|
//! ~~~
|
|
//! std::map<HString, int, wil::hstring_less> map;
|
|
//! const wchar_t constArray[] = L"foo\0bar";
|
|
//! wchar_t nonConstArray[MAX_PATH] = L"foo\0bar";
|
|
//!
|
|
//! // Create the key with the embedded null character
|
|
//! HString key;
|
|
//! THROW_IF_FAILED(key.Set(constArray));
|
|
//! map.emplace(std::move(key), 42);
|
|
//!
|
|
//! // All of the following return map.end() since they look for the string "foo"
|
|
//! map.find(constArray);
|
|
//! map.find(nonConstArray);
|
|
//! map.find(static_cast<const wchar_t*>(constArray));
|
|
//! ~~~
|
|
//! In order to search using a string literal that contains embedded null characters, a simple alternative is to
|
|
//! first create an `HStringReference` and use that for the function call:
|
|
//! ~~~
|
|
//! // HStringReference's constructor *will* deduce the length of const arrays
|
|
//! map.find(HStringReference(constArray));
|
|
//! ~~~
|
|
struct hstring_less
|
|
{
|
|
using is_transparent = void;
|
|
|
|
template <typename LhsT, typename RhsT>
|
|
WI_NODISCARD auto operator()(const LhsT& lhs, const RhsT& rhs) const
|
|
WI_NOEXCEPT->decltype(details::hstring_compare<true, false>::less(lhs, rhs))
|
|
{
|
|
return details::hstring_compare<true, false>::less(lhs, rhs);
|
|
}
|
|
};
|
|
|
|
//! A transparent less-than comparison function object whose behavior is equivalent to that of @ref hstring_less
|
|
//! with the one difference that comparisons are case-insensitive. That is, the following example will correctly
|
|
//! find the inserted value:
|
|
//! ~~~
|
|
//! std::map<HString, int, wil::hstring_insensitive_less> map;
|
|
//!
|
|
//! HString key;
|
|
//! THROW_IF_FAILED(key.Set(L"foo"));
|
|
//! map.emplace(std::move(key), 42);
|
|
//!
|
|
//! // All of the following return an iterator to the pair { L"foo", 42 }
|
|
//! map.find(L"FOo");
|
|
//! map.find(HStringReference(L"fOo"));
|
|
//! map.find(HStringReference(L"fOO").Get());
|
|
//! ~~~
|
|
struct hstring_insensitive_less
|
|
{
|
|
using is_transparent = void;
|
|
|
|
template <typename LhsT, typename RhsT>
|
|
WI_NODISCARD auto operator()(const LhsT& lhs, const RhsT& rhs) const
|
|
WI_NOEXCEPT->decltype(details::hstring_compare<true, true>::less(lhs, rhs))
|
|
{
|
|
return details::hstring_compare<true, true>::less(lhs, rhs);
|
|
}
|
|
};
|
|
|
|
#pragma endregion
|
|
|
|
/// @cond
|
|
namespace details
|
|
{
|
|
// MapToSmartType<T>::type is used to map a raw type into an RAII expression
|
|
// of it. This is needed when lifetime management of the type is needed, for example
|
|
// when holding them as a value produced in an iterator.
|
|
// This type has a common set of methods used to abstract the access to the value
|
|
// that is similar to ComPtr<> and the WRL Wrappers: Get(), GetAddressOf() and other operators.
|
|
// Clients of the smart type must use those to access the value.
|
|
|
|
// TODO: Having the base definition defined will result in creating leaks if a type
|
|
// that needs resource management (e.g. PROPVARIANT) that has not specialized is used.
|
|
//
|
|
// One fix is to use std::is_enum to cover that case and leave the base definition undefined.
|
|
// That base should use static_assert to inform clients how to fix the lack of specialization.
|
|
template <typename T, typename Enable = void>
|
|
struct MapToSmartType
|
|
{
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4702) // https://github.com/Microsoft/wil/issues/2
|
|
struct type // T holder
|
|
{
|
|
type() = default;
|
|
type(T&& value) : m_value(wistd::forward<T>(value))
|
|
{
|
|
}
|
|
WI_NODISCARD operator T() const
|
|
{
|
|
return m_value;
|
|
}
|
|
type& operator=(T&& value)
|
|
{
|
|
m_value = wistd::forward<T>(value);
|
|
return *this;
|
|
}
|
|
WI_NODISCARD T Get() const
|
|
{
|
|
return m_value;
|
|
}
|
|
|
|
// Returning T&& to support move only types
|
|
// In case of absence of T::operator=(T&&) a call to T::operator=(const T&) will happen
|
|
T&& Get()
|
|
{
|
|
return wistd::move(m_value);
|
|
}
|
|
|
|
WI_NODISCARD HRESULT CopyTo(T* result) const
|
|
{
|
|
*result = m_value;
|
|
return S_OK;
|
|
}
|
|
T* GetAddressOf()
|
|
{
|
|
return &m_value;
|
|
}
|
|
T* ReleaseAndGetAddressOf()
|
|
{
|
|
return &m_value;
|
|
}
|
|
T* operator&()
|
|
{
|
|
return &m_value;
|
|
}
|
|
T m_value{};
|
|
};
|
|
#pragma warning(pop)
|
|
};
|
|
|
|
// IUnknown * derived -> Microsoft::WRL::ComPtr<>
|
|
template <typename T>
|
|
struct MapToSmartType<T, typename wistd::enable_if<wistd::is_base_of<IUnknown, typename wistd::remove_pointer<T>::type>::value>::type>
|
|
{
|
|
typedef Microsoft::WRL::ComPtr<typename wistd::remove_pointer<T>::type> type;
|
|
};
|
|
|
|
// HSTRING -> Microsoft::WRL::Wrappers::HString
|
|
template <>
|
|
struct MapToSmartType<HSTRING, void>
|
|
{
|
|
class HStringWithRelease : public Microsoft::WRL::Wrappers::HString
|
|
{
|
|
public:
|
|
// Unlike all other WRL types HString does not have ReleaseAndGetAddressOf and
|
|
// GetAddressOf() has non-standard behavior, calling Release().
|
|
HSTRING* ReleaseAndGetAddressOf() WI_NOEXCEPT
|
|
{
|
|
Release();
|
|
return &hstr_;
|
|
}
|
|
};
|
|
typedef HStringWithRelease type;
|
|
};
|
|
|
|
// WinRT interfaces like IVector<>, IAsyncOperation<> and IIterable<> can be templated
|
|
// on a runtime class (instead of an interface or primitive type). In these cases the objects
|
|
// produced by those interfaces implement an interface defined by the runtime class default interface.
|
|
//
|
|
// These templates deduce the type of the produced interface or pass through
|
|
// the type unmodified in the non runtime class case.
|
|
//
|
|
// for example:
|
|
// IAsyncOperation<StorageFile*> -> IAsyncOperation<IStorageFile*>
|
|
|
|
// For IVector<T>, IVectorView<T>.
|
|
template <typename VectorType>
|
|
struct MapVectorResultType
|
|
{
|
|
template <typename TVector, typename TResult>
|
|
static TResult PeekGetAtType(HRESULT (STDMETHODCALLTYPE TVector::*)(unsigned, TResult*));
|
|
typedef decltype(PeekGetAtType(&VectorType::GetAt)) type;
|
|
};
|
|
|
|
// For IIterator<T>.
|
|
template <typename T>
|
|
struct MapIteratorResultType
|
|
{
|
|
template <typename TIterable, typename TResult>
|
|
static TResult PeekCurrentType(HRESULT (STDMETHODCALLTYPE TIterable::*)(TResult*));
|
|
typedef decltype(PeekCurrentType(&ABI::Windows::Foundation::Collections::IIterator<T>::get_Current)) type;
|
|
};
|
|
|
|
// For IAsyncOperation<T>.
|
|
template <typename T>
|
|
struct MapAsyncOpResultType
|
|
{
|
|
template <typename TAsyncOperation, typename TResult>
|
|
static TResult PeekGetResultsType(HRESULT (STDMETHODCALLTYPE TAsyncOperation::*)(TResult*));
|
|
typedef decltype(PeekGetResultsType(&ABI::Windows::Foundation::IAsyncOperation<T>::GetResults)) type;
|
|
};
|
|
|
|
// For IAsyncOperationWithProgress<T, P>.
|
|
template <typename T, typename P>
|
|
struct MapAsyncOpProgressResultType
|
|
{
|
|
template <typename TAsyncOperation, typename TResult>
|
|
static TResult PeekGetResultsType(HRESULT (STDMETHODCALLTYPE TAsyncOperation::*)(TResult*));
|
|
typedef decltype(PeekGetResultsType(&ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>::GetResults)) type;
|
|
};
|
|
|
|
// No support for IAsyncActionWithProgress<P> none of these (currently) use
|
|
// a runtime class for the progress type.
|
|
} // namespace details
|
|
/// @endcond
|
|
#pragma region C++ iterators for WinRT collections for use with range based for and STL algorithms
|
|
|
|
/** Range base for and STL algorithms support for WinRT ABI collection types, IVector<T>, IVectorView<T>, IIterable<T>
|
|
similar to support provided by <collection.h> for C++ CX. Three error handling policies are supported.
|
|
@code
|
|
ComPtr<CollectionType> collection = GetCollection(); // can be IVector<HSTRING>, IVectorView<HSTRING> or IIterable<HSTRING>
|
|
|
|
for (auto const& element : wil::get_range(collection.Get())) // exceptions
|
|
for (auto const& element : wil::get_range_nothrow(collection.Get(), &hr)) // error code
|
|
for (auto const& element : wil::get_range_failfast(collection.Get())) // fail fast
|
|
{
|
|
// use element
|
|
}
|
|
@endcode
|
|
Standard algorithm example:
|
|
@code
|
|
ComPtr<IVectorView<StorageFile*>> files = GetFiles();
|
|
auto fileRange = wil::get_range_nothrow(files.Get());
|
|
auto itFound = std::find_if(fileRange.begin(), fileRange.end(), [](ComPtr<IStorageFile> file) -> bool
|
|
{
|
|
return true; // first element in range
|
|
});
|
|
@endcode
|
|
*/
|
|
#pragma region exception and fail fast based IVector<>/IVectorView<>
|
|
|
|
template <typename VectorType, typename err_policy = err_exception_policy>
|
|
class vector_range
|
|
{
|
|
public:
|
|
typedef typename details::MapVectorResultType<VectorType>::type TResult;
|
|
typedef typename details::MapToSmartType<TResult>::type TSmart;
|
|
|
|
vector_range() = delete;
|
|
|
|
explicit vector_range(_In_ VectorType* vector) : m_v(vector)
|
|
{
|
|
}
|
|
|
|
class vector_iterator
|
|
{
|
|
public:
|
|
#if defined(_XUTILITY_) || defined(WIL_DOXYGEN)
|
|
// could be random_access_iterator_tag but missing some features
|
|
typedef ::std::bidirectional_iterator_tag iterator_category;
|
|
#endif
|
|
typedef TSmart value_type;
|
|
typedef ptrdiff_t difference_type;
|
|
typedef const TSmart* pointer;
|
|
typedef const TSmart& reference;
|
|
|
|
// for begin()
|
|
vector_iterator(VectorType* v, unsigned int pos) : m_v(v), m_i(pos)
|
|
{
|
|
}
|
|
|
|
// for end()
|
|
vector_iterator() : m_v(nullptr), m_i(-1)
|
|
{
|
|
}
|
|
|
|
vector_iterator(const vector_iterator& other)
|
|
{
|
|
m_v = other.m_v;
|
|
m_i = other.m_i;
|
|
err_policy::HResult(other.m_element.CopyTo(m_element.GetAddressOf()));
|
|
}
|
|
|
|
vector_iterator& operator=(const vector_iterator& other)
|
|
{
|
|
if (this != wistd::addressof(other))
|
|
{
|
|
m_v = other.m_v;
|
|
m_i = other.m_i;
|
|
err_policy::HResult(other.m_element.CopyTo(m_element.ReleaseAndGetAddressOf()));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
reference operator*()
|
|
{
|
|
err_policy::HResult(m_v->GetAt(m_i, m_element.ReleaseAndGetAddressOf()));
|
|
return m_element;
|
|
}
|
|
|
|
pointer operator->()
|
|
{
|
|
err_policy::HResult(m_v->GetAt(m_i, m_element.ReleaseAndGetAddressOf()));
|
|
return wistd::addressof(m_element);
|
|
}
|
|
|
|
vector_iterator& operator++()
|
|
{
|
|
++m_i;
|
|
return *this;
|
|
}
|
|
|
|
vector_iterator& operator--()
|
|
{
|
|
--m_i;
|
|
return *this;
|
|
}
|
|
|
|
vector_iterator operator++(int)
|
|
{
|
|
vector_iterator old(*this);
|
|
++*this;
|
|
return old;
|
|
}
|
|
|
|
vector_iterator operator--(int)
|
|
{
|
|
vector_iterator old(*this);
|
|
--*this;
|
|
return old;
|
|
}
|
|
|
|
vector_iterator& operator+=(int n)
|
|
{
|
|
m_i += n;
|
|
return *this;
|
|
}
|
|
|
|
vector_iterator& operator-=(int n)
|
|
{
|
|
m_i -= n;
|
|
return *this;
|
|
}
|
|
|
|
WI_NODISCARD vector_iterator operator+(int n) const
|
|
{
|
|
vector_iterator ret(*this);
|
|
ret += n;
|
|
return ret;
|
|
}
|
|
|
|
WI_NODISCARD vector_iterator operator-(int n) const
|
|
{
|
|
vector_iterator ret(*this);
|
|
ret -= n;
|
|
return ret;
|
|
}
|
|
|
|
WI_NODISCARD ptrdiff_t operator-(const vector_iterator& other) const
|
|
{
|
|
return m_i - other.m_i;
|
|
}
|
|
|
|
WI_NODISCARD bool operator==(const vector_iterator& other) const
|
|
{
|
|
return m_i == other.m_i;
|
|
}
|
|
|
|
WI_NODISCARD bool operator!=(const vector_iterator& other) const
|
|
{
|
|
return m_i != other.m_i;
|
|
}
|
|
|
|
WI_NODISCARD bool operator<(const vector_iterator& other) const
|
|
{
|
|
return m_i < other.m_i;
|
|
}
|
|
|
|
WI_NODISCARD bool operator>(const vector_iterator& other) const
|
|
{
|
|
return m_i > other.m_i;
|
|
}
|
|
|
|
WI_NODISCARD bool operator<=(const vector_iterator& other) const
|
|
{
|
|
return m_i <= other.m_i;
|
|
}
|
|
|
|
WI_NODISCARD bool operator>=(const vector_iterator& other) const
|
|
{
|
|
return m_i >= other.m_i;
|
|
}
|
|
|
|
private:
|
|
VectorType* m_v; // weak, collection must outlive iterators.
|
|
unsigned int m_i;
|
|
TSmart m_element;
|
|
};
|
|
|
|
vector_iterator begin()
|
|
{
|
|
return vector_iterator(m_v, 0);
|
|
}
|
|
|
|
vector_iterator end()
|
|
{
|
|
unsigned int size;
|
|
err_policy::HResult(m_v->get_Size(&size));
|
|
return vector_iterator(m_v, size);
|
|
}
|
|
|
|
private:
|
|
VectorType* m_v; // weak, collection must outlive iterators.
|
|
};
|
|
#pragma endregion
|
|
|
|
#pragma region error code based IVector<>/IVectorView<>
|
|
|
|
__WI_ITR_NAMESPACE_BEGIN
|
|
|
|
template <typename VectorType>
|
|
class vector_range_nothrow
|
|
{
|
|
public:
|
|
typedef typename details::MapVectorResultType<VectorType>::type TResult;
|
|
typedef typename details::MapToSmartType<TResult>::type TSmart;
|
|
|
|
vector_range_nothrow() = delete;
|
|
vector_range_nothrow(const vector_range_nothrow&) = delete;
|
|
vector_range_nothrow& operator=(const vector_range_nothrow&) = delete;
|
|
|
|
vector_range_nothrow(vector_range_nothrow&& other) WI_NOEXCEPT : m_v(other.m_v),
|
|
m_size(other.m_size),
|
|
m_result(other.m_result),
|
|
m_resultStorage(other.m_resultStorage),
|
|
m_currentElement(wistd::move(other.m_currentElement))
|
|
{
|
|
}
|
|
|
|
vector_range_nothrow(_In_ VectorType* vector, HRESULT* result = nullptr) :
|
|
m_v(vector), m_result(result ? result : &m_resultStorage)
|
|
{
|
|
*m_result = m_v->get_Size(&m_size);
|
|
}
|
|
|
|
class vector_iterator_nothrow
|
|
{
|
|
public:
|
|
#if defined(_XUTILITY_) || defined(WIL_DOXYGEN)
|
|
// must be input_iterator_tag as use (via ++, --, etc.) of one invalidates the other.
|
|
typedef ::std::input_iterator_tag iterator_category;
|
|
#endif
|
|
typedef TSmart value_type;
|
|
typedef ptrdiff_t difference_type;
|
|
typedef const TSmart* pointer;
|
|
typedef const TSmart& reference;
|
|
|
|
vector_iterator_nothrow() = delete;
|
|
vector_iterator_nothrow(vector_range_nothrow<VectorType>* range, unsigned int pos) :
|
|
m_range(range),
|
|
m_i(pos)
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
,
|
|
m_version(range->m_version)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
WI_NODISCARD reference operator*() const
|
|
{
|
|
return *this->operator->();
|
|
}
|
|
|
|
WI_NODISCARD pointer operator->() const
|
|
{
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
WI_FAIL_FAST_ASSERT_MSG(m_version == m_range->m_version, "Dereferencing an out-of-date vector_iterator_nothrow");
|
|
WI_FAIL_FAST_ASSERT_MSG(SUCCEEDED(*m_range->m_result), "Dereferencing a vector_iterator_nothrow in a failed state");
|
|
WI_FAIL_FAST_ASSERT_MSG(m_i < m_range->m_size, "Dereferencing an 'end' iterator");
|
|
#endif
|
|
return wistd::addressof(m_range->m_currentElement);
|
|
}
|
|
|
|
vector_iterator_nothrow& operator++()
|
|
{
|
|
return *this += 1;
|
|
}
|
|
|
|
vector_iterator_nothrow& operator--()
|
|
{
|
|
return *this += -1;
|
|
}
|
|
|
|
vector_iterator_nothrow operator++(int)
|
|
{
|
|
vector_iterator_nothrow old(*this);
|
|
++*this;
|
|
return old;
|
|
}
|
|
|
|
vector_iterator_nothrow operator--(int)
|
|
{
|
|
vector_iterator_nothrow old(*this);
|
|
--*this;
|
|
return old;
|
|
}
|
|
|
|
vector_iterator_nothrow& operator+=(int n)
|
|
{
|
|
#if WIL_ITERATOR_DEBUG_LEVEL == 2
|
|
// This is _technically_ safe because we are not reading the out-of-date cached value, however having two active
|
|
// copies of iterators is generally a sign of problematic code with a bug waiting to happen
|
|
WI_FAIL_FAST_ASSERT_MSG(
|
|
m_version == m_range->m_version, "Incrementing/decrementing an out-of-date copy of a vector_iterator_nothrow");
|
|
#endif
|
|
m_i += n;
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
// NOTE: 'm_i' is unsigned, hence the single check for increment/decrement
|
|
WI_FAIL_FAST_ASSERT_MSG(m_i <= m_range->m_size, "Incrementing/decrementing a vector_iterator_nothrow out of range");
|
|
#endif
|
|
m_range->get_at_current(m_i);
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
m_version = m_range->m_version;
|
|
#endif
|
|
return *this;
|
|
}
|
|
|
|
vector_iterator_nothrow& operator-=(int n)
|
|
{
|
|
return *this += -n;
|
|
}
|
|
|
|
WI_NODISCARD bool operator==(vector_iterator_nothrow const& other) const
|
|
{
|
|
return FAILED(*m_range->m_result) || (m_i == other.m_i);
|
|
}
|
|
|
|
WI_NODISCARD bool operator!=(vector_iterator_nothrow const& other) const
|
|
{
|
|
return !operator==(other);
|
|
}
|
|
|
|
private:
|
|
vector_range_nothrow<VectorType>* m_range;
|
|
unsigned int m_i = 0;
|
|
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
// For checked iterator support; must match the version in 'm_range'
|
|
int m_version = 0;
|
|
#endif
|
|
};
|
|
|
|
vector_iterator_nothrow begin()
|
|
{
|
|
// NOTE: Calling 'begin()' more than once is explicitly allowed as a way to reset the range, however because state is
|
|
// shared between all iterators, this invalidates previously created iterators
|
|
get_at_current(0);
|
|
return vector_iterator_nothrow(this, 0);
|
|
}
|
|
|
|
vector_iterator_nothrow end()
|
|
{
|
|
return vector_iterator_nothrow(this, m_size);
|
|
}
|
|
|
|
// Note, the error code is observed in operator!= and operator==, it always
|
|
// returns "equal" in the failed state to force the compare to the end
|
|
// iterator to return false and stop the loop.
|
|
//
|
|
// Is this ok for the general case?
|
|
void get_at_current(unsigned int i)
|
|
{
|
|
if (SUCCEEDED(*m_result) && (i < m_size))
|
|
{
|
|
*m_result = m_v->GetAt(i, m_currentElement.ReleaseAndGetAddressOf());
|
|
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
++m_version;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private:
|
|
VectorType* m_v; // weak, collection must outlive iterators.
|
|
unsigned int m_size;
|
|
|
|
// This state is shared by vector_iterator_nothrow instances. this means
|
|
// use of one iterator invalidates the other.
|
|
HRESULT* m_result;
|
|
HRESULT m_resultStorage = S_OK; // for the case where the caller does not provide the location to store the result
|
|
TSmart m_currentElement;
|
|
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
// For checked iterator support
|
|
int m_version = 0;
|
|
#endif
|
|
};
|
|
|
|
__WI_ITR_NAMESPACE_END
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region exception and fail fast based IIterable<>
|
|
|
|
template <typename T, typename err_policy = err_exception_policy>
|
|
class iterable_range
|
|
{
|
|
public:
|
|
typedef typename details::MapIteratorResultType<T>::type TResult;
|
|
typedef typename details::MapToSmartType<TResult>::type TSmart;
|
|
|
|
explicit iterable_range(_In_ ABI::Windows::Foundation::Collections::IIterable<T>* iterable) : m_iterable(iterable)
|
|
{
|
|
}
|
|
|
|
class iterable_iterator
|
|
{
|
|
public:
|
|
#if defined(_XUTILITY_) || defined(WIL_DOXYGEN)
|
|
typedef ::std::forward_iterator_tag iterator_category;
|
|
#endif
|
|
typedef TSmart value_type;
|
|
typedef ptrdiff_t difference_type;
|
|
typedef const TSmart* pointer;
|
|
typedef const TSmart& reference;
|
|
|
|
iterable_iterator() : m_i(-1)
|
|
{
|
|
}
|
|
|
|
// for begin()
|
|
explicit iterable_iterator(_In_ ABI::Windows::Foundation::Collections::IIterable<T>* iterable)
|
|
{
|
|
err_policy::HResult(iterable->First(&m_iterator));
|
|
boolean hasCurrent;
|
|
err_policy::HResult(m_iterator->get_HasCurrent(&hasCurrent));
|
|
m_i = hasCurrent ? 0 : -1;
|
|
}
|
|
|
|
// for end()
|
|
iterable_iterator(int /*currentIndex*/) : m_i(-1)
|
|
{
|
|
}
|
|
|
|
iterable_iterator(const iterable_iterator& other)
|
|
{
|
|
m_iterator = other.m_iterator;
|
|
m_i = other.m_i;
|
|
err_policy::HResult(other.m_element.CopyTo(m_element.GetAddressOf()));
|
|
}
|
|
|
|
iterable_iterator& operator=(const iterable_iterator& other)
|
|
{
|
|
if (this != wistd::addressof(other))
|
|
{
|
|
m_iterator = other.m_iterator;
|
|
m_i = other.m_i;
|
|
err_policy::HResult(other.m_element.CopyTo(m_element.ReleaseAndGetAddressOf()));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
WI_NODISCARD bool operator==(iterable_iterator const& other) const
|
|
{
|
|
return m_i == other.m_i;
|
|
}
|
|
|
|
WI_NODISCARD bool operator!=(iterable_iterator const& other) const
|
|
{
|
|
return !operator==(other);
|
|
}
|
|
|
|
reference operator*()
|
|
{
|
|
err_policy::HResult(m_iterator->get_Current(m_element.ReleaseAndGetAddressOf()));
|
|
return m_element;
|
|
}
|
|
|
|
pointer operator->()
|
|
{
|
|
err_policy::HResult(m_iterator->get_Current(m_element.ReleaseAndGetAddressOf()));
|
|
return wistd::addressof(m_element);
|
|
}
|
|
|
|
iterable_iterator& operator++()
|
|
{
|
|
boolean hasCurrent;
|
|
err_policy::HResult(m_iterator->MoveNext(&hasCurrent));
|
|
if (hasCurrent)
|
|
{
|
|
m_i++;
|
|
}
|
|
else
|
|
{
|
|
m_i = -1;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
iterable_iterator operator++(int)
|
|
{
|
|
iterable_iterator old(*this);
|
|
++*this;
|
|
return old;
|
|
}
|
|
|
|
private:
|
|
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IIterator<T>> m_iterator;
|
|
int m_i;
|
|
TSmart m_element;
|
|
};
|
|
|
|
iterable_iterator begin()
|
|
{
|
|
return iterable_iterator(m_iterable);
|
|
}
|
|
|
|
iterable_iterator end()
|
|
{
|
|
return iterable_iterator();
|
|
}
|
|
|
|
private:
|
|
// weak, collection must outlive iterators.
|
|
ABI::Windows::Foundation::Collections::IIterable<T>* m_iterable;
|
|
};
|
|
#pragma endregion
|
|
|
|
#if defined(__WI_HAS_STD_VECTOR) || defined(WIL_DOXYGEN)
|
|
/** Converts WinRT vectors to std::vector by requesting the collection's data in a single
|
|
operation. This can be more efficient in terms of IPC cost than iteratively processing it.
|
|
@code
|
|
ComPtr<IVector<IPropertyValue*>> values = GetValues();
|
|
std::vector<ComPtr<IPropertyValue>> allData = wil::to_vector(values);
|
|
for (ComPtr<IPropertyValue> const& item : allData)
|
|
{
|
|
// use item
|
|
}
|
|
@endcode
|
|
Can be used for ABI::Windows::Foundation::Collections::IVector<T> and
|
|
ABI::Windows::Foundation::Collections::IVectorView<T>
|
|
*/
|
|
template <typename VectorType>
|
|
auto to_vector(VectorType* src)
|
|
{
|
|
using TResult = typename details::MapVectorResultType<VectorType>::type;
|
|
using TSmart = typename details::MapToSmartType<TResult>::type;
|
|
static_assert(sizeof(TResult) == sizeof(TSmart), "result and smart sizes are different");
|
|
std::vector<TSmart> output;
|
|
UINT32 expected = 0;
|
|
THROW_IF_FAILED(src->get_Size(&expected));
|
|
if (expected > 0)
|
|
{
|
|
output.resize(expected + 1);
|
|
UINT32 fetched = 0;
|
|
THROW_IF_FAILED(src->GetMany(0, static_cast<UINT32>(output.size()), reinterpret_cast<TResult*>(output.data()), &fetched));
|
|
THROW_HR_IF(E_CHANGED_STATE, fetched > expected);
|
|
output.resize(fetched);
|
|
}
|
|
return output;
|
|
}
|
|
#endif
|
|
|
|
#pragma region error code base IIterable<>
|
|
|
|
__WI_ITR_NAMESPACE_BEGIN
|
|
|
|
template <typename T>
|
|
class iterable_range_nothrow
|
|
{
|
|
public:
|
|
typedef typename details::MapIteratorResultType<T>::type TResult;
|
|
typedef typename details::MapToSmartType<TResult>::type TSmart;
|
|
|
|
iterable_range_nothrow() = delete;
|
|
iterable_range_nothrow(const iterable_range_nothrow&) = delete;
|
|
iterable_range_nothrow& operator=(const iterable_range_nothrow&) = delete;
|
|
iterable_range_nothrow& operator=(iterable_range_nothrow&&) = delete;
|
|
|
|
iterable_range_nothrow(iterable_range_nothrow&& other) WI_NOEXCEPT : m_iterator(wistd::move(other.m_iterator)),
|
|
m_element(wistd::move(other.m_element)),
|
|
m_resultStorage(other.m_resultStorage)
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
,
|
|
m_index(other.m_index)
|
|
#endif
|
|
{
|
|
if (other.m_result == &other.m_resultStorage)
|
|
{
|
|
m_result = &m_resultStorage;
|
|
}
|
|
else
|
|
{
|
|
m_result = other.m_result;
|
|
}
|
|
}
|
|
|
|
iterable_range_nothrow(_In_ ABI::Windows::Foundation::Collections::IIterable<T>* iterable, HRESULT* result = nullptr) :
|
|
m_result(result ? result : &m_resultStorage)
|
|
{
|
|
*m_result = iterable->First(&m_iterator);
|
|
if (SUCCEEDED(*m_result))
|
|
{
|
|
boolean hasCurrent;
|
|
*m_result = m_iterator->get_HasCurrent(&hasCurrent);
|
|
if (SUCCEEDED(*m_result) && hasCurrent)
|
|
{
|
|
*m_result = m_iterator->get_Current(m_element.ReleaseAndGetAddressOf());
|
|
if (FAILED(*m_result))
|
|
{
|
|
m_iterator = nullptr; // release the iterator if no elements are found
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_iterator = nullptr; // release the iterator if no elements are found
|
|
}
|
|
}
|
|
}
|
|
|
|
class iterable_iterator_nothrow
|
|
{
|
|
public:
|
|
#if defined(_XUTILITY_) || defined(WIL_DOXYGEN)
|
|
// muse be input_iterator_tag as use of one instance invalidates the other.
|
|
typedef ::std::input_iterator_tag iterator_category;
|
|
#endif
|
|
typedef TSmart value_type;
|
|
typedef ptrdiff_t difference_type;
|
|
typedef const TSmart* pointer;
|
|
typedef const TSmart& reference;
|
|
|
|
iterable_iterator_nothrow(_In_ iterable_range_nothrow* range, int currentIndex) : m_range(range), m_i(currentIndex)
|
|
{
|
|
}
|
|
|
|
WI_NODISCARD bool operator==(iterable_iterator_nothrow const& other) const
|
|
{
|
|
return FAILED(*m_range->m_result) || (m_i == other.m_i);
|
|
}
|
|
|
|
WI_NODISCARD bool operator!=(iterable_iterator_nothrow const& other) const
|
|
{
|
|
return !operator==(other);
|
|
}
|
|
|
|
WI_NODISCARD reference operator*() const WI_NOEXCEPT
|
|
{
|
|
return *this->operator->();
|
|
}
|
|
|
|
WI_NODISCARD pointer operator->() const WI_NOEXCEPT
|
|
{
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
WI_FAIL_FAST_ASSERT_MSG(SUCCEEDED(*m_range->m_result), "Dereferencing an iterable_iterator_nothrow in a failed state");
|
|
WI_FAIL_FAST_ASSERT_MSG(m_i >= 0, "Dereferencing an 'end' iterator");
|
|
WI_FAIL_FAST_ASSERT_MSG(m_i == m_range->m_index, "Dereferencing an out-of-date iterable_iterator_nothrow");
|
|
#endif
|
|
return wistd::addressof(m_range->m_element);
|
|
}
|
|
|
|
iterable_iterator_nothrow& operator++()
|
|
{
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
// Failing this check is always bad because the iterator object we hold always advances forward
|
|
WI_FAIL_FAST_ASSERT_MSG(m_i >= 0, "Incrementing an end iterator");
|
|
WI_FAIL_FAST_ASSERT_MSG(m_i == m_range->m_index, "Incrementing an out-of-date copy of an iterable_iterator_nothrow");
|
|
#endif
|
|
boolean hasCurrent;
|
|
*m_range->m_result = m_range->m_iterator->MoveNext(&hasCurrent);
|
|
if (SUCCEEDED(*m_range->m_result) && hasCurrent)
|
|
{
|
|
*m_range->m_result = m_range->m_iterator->get_Current(m_range->m_element.ReleaseAndGetAddressOf());
|
|
if (SUCCEEDED(*m_range->m_result))
|
|
{
|
|
m_i++;
|
|
}
|
|
else
|
|
{
|
|
m_i = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_i = -1;
|
|
}
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
m_range->m_index = m_i;
|
|
#endif
|
|
return *this;
|
|
}
|
|
|
|
iterable_iterator_nothrow operator++(int)
|
|
{
|
|
iterable_iterator_nothrow old(*this);
|
|
++*this;
|
|
return old;
|
|
}
|
|
|
|
private:
|
|
iterable_range_nothrow* m_range;
|
|
int m_i;
|
|
};
|
|
|
|
iterable_iterator_nothrow begin()
|
|
{
|
|
#if WIL_ITERATOR_DEBUG_LEVEL == 2
|
|
// The IIterator we hold only advances forward; we can't reset it back to the beginning. Calling this method more than
|
|
// once will very likely lead to unexpected behavior.
|
|
WI_FAIL_FAST_ASSERT_MSG(m_index == 0, "Calling begin() on an already advanced iterable_range_nothrow");
|
|
#endif
|
|
return iterable_iterator_nothrow(this, this->m_iterator ? 0 : -1);
|
|
}
|
|
|
|
iterable_iterator_nothrow end()
|
|
{
|
|
return iterable_iterator_nothrow(this, -1);
|
|
}
|
|
|
|
private:
|
|
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IIterator<T>> m_iterator;
|
|
// This state is shared by all iterator instances
|
|
// so use of one iterator can invalidate another's ability to dereference
|
|
// that is allowed for input iterators.
|
|
TSmart m_element;
|
|
HRESULT* m_result;
|
|
HRESULT m_resultStorage = S_OK;
|
|
|
|
#if WIL_ITERATOR_DEBUG_LEVEL > 0
|
|
// For checked iterator support
|
|
int m_index = 0;
|
|
#endif
|
|
};
|
|
|
|
__WI_ITR_NAMESPACE_END
|
|
|
|
#pragma endregion
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
template <typename T>
|
|
vector_range<ABI::Windows::Foundation::Collections::IVector<T>> get_range(ABI::Windows::Foundation::Collections::IVector<T>* v)
|
|
{
|
|
return vector_range<ABI::Windows::Foundation::Collections::IVector<T>>(v);
|
|
}
|
|
|
|
template <typename T>
|
|
vector_range<ABI::Windows::Foundation::Collections::IVectorView<T>> get_range(ABI::Windows::Foundation::Collections::IVectorView<T>* v)
|
|
{
|
|
return vector_range<ABI::Windows::Foundation::Collections::IVectorView<T>>(v);
|
|
}
|
|
#endif // WIL_ENABLE_EXCEPTIONS
|
|
|
|
template <typename T>
|
|
vector_range<ABI::Windows::Foundation::Collections::IVector<T>, err_failfast_policy> get_range_failfast(
|
|
ABI::Windows::Foundation::Collections::IVector<T>* v)
|
|
{
|
|
return vector_range<ABI::Windows::Foundation::Collections::IVector<T>, err_failfast_policy>(v);
|
|
}
|
|
|
|
template <typename T>
|
|
vector_range<ABI::Windows::Foundation::Collections::IVectorView<T>, err_failfast_policy> get_range_failfast(
|
|
ABI::Windows::Foundation::Collections::IVectorView<T>* v)
|
|
{
|
|
return vector_range<ABI::Windows::Foundation::Collections::IVectorView<T>, err_failfast_policy>(v);
|
|
}
|
|
|
|
template <typename T>
|
|
vector_range_nothrow<ABI::Windows::Foundation::Collections::IVector<T>> get_range_nothrow(
|
|
ABI::Windows::Foundation::Collections::IVector<T>* v, HRESULT* result = nullptr)
|
|
{
|
|
return vector_range_nothrow<ABI::Windows::Foundation::Collections::IVector<T>>(v, result);
|
|
}
|
|
|
|
template <typename T>
|
|
vector_range_nothrow<ABI::Windows::Foundation::Collections::IVectorView<T>> get_range_nothrow(
|
|
ABI::Windows::Foundation::Collections::IVectorView<T>* v, HRESULT* result = nullptr)
|
|
{
|
|
return vector_range_nothrow<ABI::Windows::Foundation::Collections::IVectorView<T>>(v, result);
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
template <typename T>
|
|
iterable_range<T> get_range(ABI::Windows::Foundation::Collections::IIterable<T>* v)
|
|
{
|
|
return iterable_range<T>(v);
|
|
}
|
|
#endif // WIL_ENABLE_EXCEPTIONS
|
|
|
|
template <typename T>
|
|
iterable_range<T, err_failfast_policy> get_range_failfast(ABI::Windows::Foundation::Collections::IIterable<T>* v)
|
|
{
|
|
return iterable_range<T, err_failfast_policy>(v);
|
|
}
|
|
|
|
template <typename T>
|
|
iterable_range_nothrow<T> get_range_nothrow(ABI::Windows::Foundation::Collections::IIterable<T>* v, HRESULT* result = nullptr)
|
|
{
|
|
return iterable_range_nothrow<T>(v, result);
|
|
}
|
|
#pragma endregion
|
|
} // namespace wil
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
|
|
#pragma region Global operator functions
|
|
#if defined(MIDL_NS_PREFIX) || defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__)
|
|
namespace ABI
|
|
{
|
|
#endif
|
|
namespace Windows
|
|
{
|
|
namespace Foundation
|
|
{
|
|
namespace Collections
|
|
{
|
|
template <typename X>
|
|
typename wil::vector_range<IVector<X>>::vector_iterator begin(IVector<X>* v)
|
|
{
|
|
return typename wil::vector_range<IVector<X>>::vector_iterator(v, 0);
|
|
}
|
|
|
|
template <typename X>
|
|
typename wil::vector_range<IVector<X>>::vector_iterator end(IVector<X>* v)
|
|
{
|
|
unsigned int size;
|
|
THROW_IF_FAILED(v->get_Size(&size));
|
|
return typename wil::vector_range<IVector<X>>::vector_iterator(v, size);
|
|
}
|
|
|
|
template <typename X>
|
|
typename wil::vector_range<IVectorView<X>>::vector_iterator begin(IVectorView<X>* v)
|
|
{
|
|
return typename wil::vector_range<IVectorView<X>>::vector_iterator(v, 0);
|
|
}
|
|
|
|
template <typename X>
|
|
typename wil::vector_range<IVectorView<X>>::vector_iterator end(IVectorView<X>* v)
|
|
{
|
|
unsigned int size;
|
|
THROW_IF_FAILED(v->get_Size(&size));
|
|
return typename wil::vector_range<IVectorView<X>>::vector_iterator(v, size);
|
|
}
|
|
|
|
template <typename X>
|
|
typename wil::iterable_range<X>::iterable_iterator begin(IIterable<X>* i)
|
|
{
|
|
return typename wil::iterable_range<X>::iterable_iterator(i);
|
|
}
|
|
|
|
template <typename X>
|
|
typename wil::iterable_range<X>::iterable_iterator end(IIterable<X>*)
|
|
{
|
|
return typename wil::iterable_range<X>::iterable_iterator();
|
|
}
|
|
} // namespace Collections
|
|
} // namespace Foundation
|
|
} // namespace Windows
|
|
#if defined(MIDL_NS_PREFIX) || defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__)
|
|
} // namespace ABI
|
|
#endif
|
|
#pragma endregion
|
|
|
|
#endif // WIL_ENABLE_EXCEPTIONS
|
|
|
|
namespace wil
|
|
{
|
|
#pragma region WinRT Async API helpers
|
|
|
|
/// @cond
|
|
namespace details
|
|
{
|
|
template <typename TResult, typename TFunc, typename... Args, typename wistd::enable_if<wistd::is_same<HRESULT, TResult>::value, int>::type = 0>
|
|
HRESULT CallAndHandleErrorsWithReturnType(TFunc&& func, Args&&... args)
|
|
{
|
|
return wistd::forward<TFunc>(func)(wistd::forward<Args>(args)...);
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
template <typename TResult, typename TFunc, typename... Args, typename wistd::enable_if<wistd::is_same<void, TResult>::value, int>::type = 0>
|
|
HRESULT CallAndHandleErrorsWithReturnType(TFunc&& func, Args&&... args)
|
|
{
|
|
try
|
|
{
|
|
wistd::forward<TFunc>(func)(wistd::forward<Args>(args)...);
|
|
}
|
|
CATCH_RETURN();
|
|
return S_OK;
|
|
}
|
|
#endif
|
|
|
|
template <typename TFunc, typename... Args>
|
|
HRESULT CallAndHandleErrors(TFunc&& func, Args&&... args)
|
|
{
|
|
return CallAndHandleErrorsWithReturnType<decltype(wistd::forward<TFunc>(func)(wistd::forward<Args>(args)...))>(
|
|
wistd::forward<TFunc>(func), wistd::forward<Args>(args)...);
|
|
}
|
|
|
|
// Get the last type of a template parameter pack.
|
|
// usage:
|
|
// LastType<int, bool>::type boolValue;
|
|
template <typename... Ts>
|
|
struct LastType
|
|
{
|
|
template <typename T, typename... OtherTs>
|
|
struct LastTypeOfTs
|
|
{
|
|
typedef typename LastTypeOfTs<OtherTs...>::type type;
|
|
};
|
|
|
|
template <typename T>
|
|
struct LastTypeOfTs<T>
|
|
{
|
|
typedef T type;
|
|
};
|
|
|
|
template <typename... OtherTs>
|
|
static typename LastTypeOfTs<OtherTs...>::type LastTypeOfTsFunc()
|
|
{
|
|
}
|
|
typedef decltype(LastTypeOfTsFunc<Ts...>()) type;
|
|
};
|
|
|
|
// Takes a member function that has an out param like F(..., IAsyncAction**) or F(..., IAsyncOperation<bool>**)
|
|
// and returns IAsyncAction* or IAsyncOperation<bool>*.
|
|
template <typename I, typename... P>
|
|
typename wistd::remove_pointer<typename LastType<P...>::type>::type GetReturnParamPointerType(HRESULT (STDMETHODCALLTYPE I::*)(P...));
|
|
|
|
// Use to determine the result type of the async action/operation interfaces or example
|
|
// decltype(GetAsyncResultType(action.get())) returns void
|
|
void GetAsyncResultType(ABI::Windows::Foundation::IAsyncAction*);
|
|
template <typename P>
|
|
void GetAsyncResultType(ABI::Windows::Foundation::IAsyncActionWithProgress<P>*);
|
|
template <typename T>
|
|
typename wil::details::MapAsyncOpResultType<T>::type GetAsyncResultType(ABI::Windows::Foundation::IAsyncOperation<T>*);
|
|
template <typename T, typename P>
|
|
typename wil::details::MapAsyncOpProgressResultType<T, P>::type GetAsyncResultType(
|
|
ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>*);
|
|
|
|
// Use to determine the result type of the async action/operation interfaces or example
|
|
// decltype(GetAsyncDelegateType(action.get())) returns void
|
|
ABI::Windows::Foundation::IAsyncActionCompletedHandler* GetAsyncDelegateType(ABI::Windows::Foundation::IAsyncAction*);
|
|
template <typename P>
|
|
ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler<P>* GetAsyncDelegateType(
|
|
ABI::Windows::Foundation::IAsyncActionWithProgress<P>*);
|
|
template <typename T>
|
|
ABI::Windows::Foundation::IAsyncOperationCompletedHandler<T>* GetAsyncDelegateType(ABI::Windows::Foundation::IAsyncOperation<T>*);
|
|
template <typename T, typename P>
|
|
ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<T, P>* GetAsyncDelegateType(
|
|
ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>*);
|
|
|
|
template <typename TBaseAgility, typename TIOperation, typename TFunction>
|
|
HRESULT RunWhenCompleteAction(_In_ TIOperation operation, TFunction&& func) WI_NOEXCEPT
|
|
{
|
|
using namespace Microsoft::WRL;
|
|
typedef wistd::remove_pointer_t<decltype(GetAsyncDelegateType(operation))> TIDelegate;
|
|
|
|
auto callback = Callback<Implements<RuntimeClassFlags<ClassicCom>, TIDelegate, TBaseAgility>>(
|
|
[func = wistd::forward<TFunction>(func)](TIOperation operation, ABI::Windows::Foundation::AsyncStatus status) mutable -> HRESULT {
|
|
HRESULT hr = S_OK;
|
|
if (status != ABI::Windows::Foundation::AsyncStatus::Completed) // avoid a potentially costly marshaled QI / call if we completed successfully
|
|
{
|
|
// QI to the IAsyncInfo interface. While all operations implement this, it is
|
|
// possible that the stub has disconnected, causing the QI to fail.
|
|
ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
|
|
hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Save the error code result in a temporary variable to allow us
|
|
// to also retrieve the result of the COM call. If the stub has
|
|
// disconnected, this call may fail.
|
|
HRESULT errorCode = E_UNEXPECTED;
|
|
hr = asyncInfo->get_ErrorCode(&errorCode);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Return the operations error code to the caller.
|
|
hr = errorCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CallAndHandleErrors(func, hr);
|
|
});
|
|
RETURN_IF_NULL_ALLOC(callback);
|
|
return operation->put_Completed(callback.Get());
|
|
}
|
|
|
|
template <typename TBaseAgility, typename TIOperation, typename TFunction>
|
|
HRESULT RunWhenComplete(_In_ TIOperation operation, TFunction&& func) WI_NOEXCEPT
|
|
{
|
|
using namespace Microsoft::WRL;
|
|
using namespace ABI::Windows::Foundation::Internal;
|
|
|
|
typedef wistd::remove_pointer_t<decltype(GetAsyncDelegateType(operation))> TIDelegate;
|
|
|
|
auto callback = Callback<Implements<RuntimeClassFlags<ClassicCom>, TIDelegate, TBaseAgility>>(
|
|
[func = wistd::forward<TFunction>(func)](TIOperation operation, ABI::Windows::Foundation::AsyncStatus status) mutable -> HRESULT {
|
|
typename details::MapToSmartType<typename GetAbiType<typename wistd::remove_pointer<TIOperation>::type::TResult_complex>::type>::type result;
|
|
|
|
HRESULT hr = S_OK;
|
|
// avoid a potentially costly marshaled QI / call if we completed successfully
|
|
if (status == ABI::Windows::Foundation::AsyncStatus::Completed)
|
|
{
|
|
hr = operation->GetResults(result.GetAddressOf());
|
|
}
|
|
else
|
|
{
|
|
// QI to the IAsyncInfo interface. While all operations implement this, it is
|
|
// possible that the stub has disconnected, causing the QI to fail.
|
|
ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
|
|
hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Save the error code result in a temporary variable to allow us
|
|
// to also retrieve the result of the COM call. If the stub has
|
|
// disconnected, this call may fail.
|
|
HRESULT errorCode = E_UNEXPECTED;
|
|
hr = asyncInfo->get_ErrorCode(&errorCode);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Return the operations error code to the caller.
|
|
hr = errorCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CallAndHandleErrors(func, hr, result.Get());
|
|
});
|
|
RETURN_IF_NULL_ALLOC(callback);
|
|
return operation->put_Completed(callback.Get());
|
|
}
|
|
|
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
|
template <typename TIOperation>
|
|
HRESULT WaitForCompletion(_In_ TIOperation operation, COWAIT_FLAGS flags, DWORD timeoutValue, _Out_opt_ bool* timedOut) WI_NOEXCEPT
|
|
{
|
|
typedef wistd::remove_pointer_t<decltype(GetAsyncDelegateType(operation))> TIDelegate;
|
|
|
|
class CompletionDelegate
|
|
: public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::Delegate>, TIDelegate, Microsoft::WRL::FtmBase>
|
|
{
|
|
public:
|
|
HRESULT RuntimeClassInitialize()
|
|
{
|
|
RETURN_HR(m_completedEventHandle.create());
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE Invoke(_In_ TIOperation, ABI::Windows::Foundation::AsyncStatus status) override
|
|
{
|
|
m_status = status;
|
|
m_completedEventHandle.SetEvent();
|
|
return S_OK;
|
|
}
|
|
|
|
WI_NODISCARD HANDLE GetEvent() const
|
|
{
|
|
return m_completedEventHandle.get();
|
|
}
|
|
|
|
WI_NODISCARD ABI::Windows::Foundation::AsyncStatus GetStatus() const
|
|
{
|
|
return m_status;
|
|
}
|
|
|
|
private:
|
|
volatile ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started;
|
|
wil::unique_event_nothrow m_completedEventHandle;
|
|
};
|
|
|
|
WI_ASSERT(timedOut || (timeoutValue == INFINITE));
|
|
assign_to_opt_param(timedOut, false);
|
|
|
|
Microsoft::WRL::ComPtr<CompletionDelegate> completedDelegate;
|
|
RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize<CompletionDelegate>(&completedDelegate));
|
|
RETURN_IF_FAILED(operation->put_Completed(completedDelegate.Get()));
|
|
|
|
HANDLE handles[] = {completedDelegate->GetEvent()};
|
|
DWORD dwHandleIndex;
|
|
HRESULT hr = CoWaitForMultipleHandles(flags, timeoutValue, ARRAYSIZE(handles), handles, &dwHandleIndex);
|
|
|
|
// If the caller is listening for timedOut, and we actually timed out, set the bool and return S_OK. Otherwise, fail.
|
|
if (timedOut && (hr == RPC_S_CALLPENDING))
|
|
{
|
|
*timedOut = true;
|
|
return S_OK;
|
|
}
|
|
RETURN_IF_FAILED(hr);
|
|
|
|
if (completedDelegate->GetStatus() != ABI::Windows::Foundation::AsyncStatus::Completed)
|
|
{
|
|
// QI to the IAsyncInfo interface. While all operations implement this, it is
|
|
// possible that the stub has disconnected, causing the QI to fail.
|
|
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
|
|
hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Save the error code result in a temporary variable to allow us
|
|
// to also retrieve the result of the COM call. If the stub has
|
|
// disconnected, this call may fail.
|
|
HRESULT errorCode = E_UNEXPECTED;
|
|
hr = asyncInfo->get_ErrorCode(&errorCode);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Return the operations error code to the caller.
|
|
hr = errorCode;
|
|
}
|
|
}
|
|
return hr; // leave it to the caller to log failures.
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
template <typename TIOperation, typename TIResults>
|
|
HRESULT WaitForCompletion(_In_ TIOperation operation, _Out_ TIResults result, COWAIT_FLAGS flags, DWORD timeoutValue, _Out_opt_ bool* timedOut) WI_NOEXCEPT
|
|
{
|
|
RETURN_IF_FAILED_EXPECTED(details::WaitForCompletion(operation, flags, timeoutValue, timedOut));
|
|
return operation->GetResults(result);
|
|
}
|
|
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
|
} // namespace details
|
|
/// @endcond
|
|
|
|
/** Set the completion callback for an async operation to run a caller provided function.
|
|
Once complete the function is called with the error code result of the operation
|
|
and the async operation result (if applicable).
|
|
The function parameter list must be (HRESULT hr) for actions,
|
|
(HRESULT hr, IResultInterface* object) for operations that produce interfaces,
|
|
and (HRESULT hr, TResult value) for operations that produce value types.
|
|
~~~
|
|
run_when_complete(getFileOp.Get(), [](HRESULT hr, IStorageFile* file) -> void
|
|
{
|
|
|
|
});
|
|
~~~
|
|
for an agile callback use Microsoft::WRL::FtmBase
|
|
~~~
|
|
run_when_complete<FtmBase>(getFileOp.Get(), [](HRESULT hr, IStorageFile* file) -> void
|
|
{
|
|
|
|
});
|
|
~~~
|
|
Using the non throwing form:
|
|
~~~
|
|
hr = run_when_complete_nothrow<StorageFile*>(getFileOp.Get(), [](HRESULT hr, IStorageFile* file) -> HRESULT
|
|
{
|
|
|
|
});
|
|
~~~
|
|
*/
|
|
|
|
//! Run a fuction when an async operation completes. Use Microsoft::WRL::FtmBase for TAgility to make the completion handler agile
|
|
//! and run on the async thread.
|
|
template <typename TAgility = IUnknown, typename TFunc>
|
|
HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncAction* operation, TFunc&& func) WI_NOEXCEPT
|
|
{
|
|
return details::RunWhenCompleteAction<TAgility>(operation, wistd::forward<TFunc>(func));
|
|
}
|
|
|
|
template <typename TAgility = IUnknown, typename TResult, typename TFunc, typename TAsyncResult = typename wil::details::MapAsyncOpResultType<TResult>::type>
|
|
HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation, TFunc&& func) WI_NOEXCEPT
|
|
{
|
|
return details::RunWhenComplete<TAgility>(operation, wistd::forward<TFunc>(func));
|
|
}
|
|
|
|
template <typename TAgility = IUnknown, typename TResult, typename TProgress, typename TFunc, typename TAsyncResult = typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type>
|
|
HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation, TFunc&& func) WI_NOEXCEPT
|
|
{
|
|
return details::RunWhenComplete<TAgility>(operation, wistd::forward<TFunc>(func));
|
|
}
|
|
|
|
template <typename TAgility = IUnknown, typename TProgress, typename TFunc>
|
|
HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncActionWithProgress<TProgress>* operation, TFunc&& func) WI_NOEXCEPT
|
|
{
|
|
return details::RunWhenCompleteAction<TAgility>(operation, wistd::forward<TFunc>(func));
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Run a fuction when an async operation completes. Use Microsoft::WRL::FtmBase for TAgility to make the completion handler agile
|
|
//! and run on the async thread.
|
|
template <typename TAgility = IUnknown, typename TFunc>
|
|
void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncAction* operation, TFunc&& func)
|
|
{
|
|
THROW_IF_FAILED((details::RunWhenCompleteAction<TAgility>(operation, wistd::forward<TFunc>(func))));
|
|
}
|
|
|
|
template <typename TAgility = IUnknown, typename TResult, typename TFunc, typename TAsyncResult = typename wil::details::MapAsyncOpResultType<TResult>::type>
|
|
void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation, TFunc&& func)
|
|
{
|
|
THROW_IF_FAILED((details::RunWhenComplete<TAgility>(operation, wistd::forward<TFunc>(func))));
|
|
}
|
|
|
|
template <typename TAgility = IUnknown, typename TResult, typename TProgress, typename TFunc, typename TAsyncResult = typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type>
|
|
void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation, TFunc&& func)
|
|
{
|
|
THROW_IF_FAILED((details::RunWhenComplete<TAgility>(operation, wistd::forward<TFunc>(func))));
|
|
}
|
|
|
|
template <typename TAgility = IUnknown, typename TProgress, typename TFunc>
|
|
void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncActionWithProgress<TProgress>* operation, TFunc&& func)
|
|
{
|
|
THROW_IF_FAILED((details::RunWhenCompleteAction<TAgility>(operation, wistd::forward<TFunc>(func))));
|
|
}
|
|
#endif
|
|
|
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
|
/** Wait for an asynchronous operation to complete (or be canceled).
|
|
Use to synchronously wait on async operations on background threads.
|
|
Do not call from UI threads or STA threads as reentrancy will result.
|
|
~~~
|
|
ComPtr<IAsyncOperation<StorageFile*>> op;
|
|
THROW_IF_FAILED(storageFileStatics->GetFileFromPathAsync(HStringReference(path).Get(), &op));
|
|
auto file = wil::wait_for_completion(op.Get());
|
|
~~~
|
|
*/
|
|
template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
|
|
inline HRESULT wait_for_completion_nothrow(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT
|
|
{
|
|
return details::WaitForCompletion(operation, flags, INFINITE, nullptr);
|
|
}
|
|
|
|
// These forms return the result from the async operation
|
|
|
|
template <typename TResult>
|
|
HRESULT wait_for_completion_nothrow(
|
|
_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation,
|
|
_Out_ typename wil::details::MapAsyncOpResultType<TResult>::type* result,
|
|
COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT
|
|
{
|
|
return details::WaitForCompletion(operation, result, flags, INFINITE, nullptr);
|
|
}
|
|
|
|
template <typename TResult, typename TProgress>
|
|
HRESULT wait_for_completion_nothrow(
|
|
_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation,
|
|
_Out_ typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type* result,
|
|
COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT
|
|
{
|
|
return details::WaitForCompletion(operation, result, flags, INFINITE, nullptr);
|
|
}
|
|
|
|
// Same as above, but allows caller to specify a timeout value.
|
|
// On timeout, S_OK is returned, with timedOut set to true.
|
|
|
|
template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
|
|
inline HRESULT wait_for_completion_or_timeout_nothrow(
|
|
_In_ TAsync* operation, DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT
|
|
{
|
|
return details::WaitForCompletion(operation, flags, timeoutValue, timedOut);
|
|
}
|
|
|
|
template <typename TResult>
|
|
HRESULT wait_for_completion_or_timeout_nothrow(
|
|
_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation,
|
|
_Out_ typename wil::details::MapAsyncOpResultType<TResult>::type* result,
|
|
DWORD timeoutValue,
|
|
_Out_ bool* timedOut,
|
|
COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT
|
|
{
|
|
return details::WaitForCompletion(operation, result, flags, timeoutValue, timedOut);
|
|
}
|
|
|
|
template <typename TResult, typename TProgress>
|
|
HRESULT wait_for_completion_or_timeout_nothrow(
|
|
_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation,
|
|
_Out_ typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type* result,
|
|
DWORD timeoutValue,
|
|
_Out_ bool* timedOut,
|
|
COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT
|
|
{
|
|
return details::WaitForCompletion(operation, result, flags, timeoutValue, timedOut);
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Wait for an asynchronous operation to complete (or be canceled).
|
|
template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
|
|
inline void wait_for_completion(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS)
|
|
{
|
|
THROW_IF_FAILED(details::WaitForCompletion(operation, flags, INFINITE, nullptr));
|
|
}
|
|
|
|
template <typename TResult, typename TReturn = typename wil::details::MapToSmartType<typename wil::details::MapAsyncOpResultType<TResult>::type>::type>
|
|
TReturn wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS)
|
|
{
|
|
TReturn result;
|
|
THROW_IF_FAILED(details::WaitForCompletion(operation, result.GetAddressOf(), flags, INFINITE, nullptr));
|
|
return result;
|
|
}
|
|
|
|
template <
|
|
typename TResult,
|
|
typename TProgress,
|
|
typename TReturn = typename wil::details::MapToSmartType<typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type>::type>
|
|
TReturn wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS)
|
|
{
|
|
TReturn result;
|
|
THROW_IF_FAILED(details::WaitForCompletion(operation, result.GetAddressOf(), flags, INFINITE, nullptr));
|
|
return result;
|
|
}
|
|
|
|
/** Similar to WaitForCompletion but this function encapsulates the creation of the async operation
|
|
making usage simpler.
|
|
~~~
|
|
ComPtr<ILauncherStatics> launcher; // inited somewhere
|
|
auto result = call_and_wait_for_completion(launcher.Get(), &ILauncherStatics::LaunchUriAsync, uri.Get());
|
|
~~~
|
|
*/
|
|
template <typename I, typename... P, typename... Args>
|
|
auto call_and_wait_for_completion(I* object, HRESULT (STDMETHODCALLTYPE I::*func)(P...), Args&&... args)
|
|
{
|
|
Microsoft::WRL::ComPtr<typename wistd::remove_pointer<typename wistd::remove_pointer<typename details::LastType<P...>::type>::type>::type> op;
|
|
THROW_IF_FAILED((object->*func)(wistd::forward<Args>(args)..., &op));
|
|
return wil::wait_for_completion(op.Get());
|
|
}
|
|
#endif
|
|
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region WinRT object construction
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Get a WinRT activation factory object, usually using a IXXXStatics interface.
|
|
template <typename TInterface>
|
|
com_ptr<TInterface> GetActivationFactory(PCWSTR runtimeClass)
|
|
{
|
|
com_ptr<TInterface> result;
|
|
THROW_IF_FAILED(RoGetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(runtimeClass).Get(), IID_PPV_ARGS(&result)));
|
|
return result;
|
|
}
|
|
|
|
//! Get a WinRT object.
|
|
template <typename TInterface>
|
|
com_ptr<TInterface> ActivateInstance(PCWSTR runtimeClass)
|
|
{
|
|
com_ptr<IInspectable> result;
|
|
THROW_IF_FAILED(RoActivateInstance(Microsoft::WRL::Wrappers::HStringReference(runtimeClass).Get(), &result));
|
|
return result.query<TInterface>();
|
|
}
|
|
#endif
|
|
#pragma endregion
|
|
|
|
#pragma region Async production helpers
|
|
|
|
/// @cond
|
|
namespace details
|
|
{
|
|
template <typename TResult>
|
|
class SyncAsyncOp WrlFinal : public Microsoft::WRL::RuntimeClass<
|
|
ABI::Windows::Foundation::IAsyncOperation<TResult>,
|
|
Microsoft::WRL::AsyncBase<ABI::Windows::Foundation::IAsyncOperationCompletedHandler<TResult>>>
|
|
{
|
|
// typedef typename MapToSmartType<TResult>::type TSmart;
|
|
using RuntimeClassT = typename SyncAsyncOp::RuntimeClassT;
|
|
InspectableClass(__super::z_get_rc_name_impl(), TrustLevel::BaseTrust);
|
|
|
|
public:
|
|
HRESULT RuntimeClassInitialize(const TResult& op)
|
|
{
|
|
m_result = op;
|
|
return S_OK;
|
|
}
|
|
|
|
IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncOperationCompletedHandler<TResult>* competed) override
|
|
{
|
|
competed->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed);
|
|
return S_OK;
|
|
}
|
|
|
|
IFACEMETHODIMP get_Completed(ABI::Windows::Foundation::IAsyncOperationCompletedHandler<TResult>** competed) override
|
|
{
|
|
*competed = nullptr;
|
|
return S_OK;
|
|
}
|
|
|
|
IFACEMETHODIMP GetResults(TResult* result) override
|
|
{
|
|
*result = m_result;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OnStart() override
|
|
{
|
|
return S_OK;
|
|
}
|
|
void OnClose() override
|
|
{
|
|
}
|
|
void OnCancel() override
|
|
{
|
|
}
|
|
|
|
private:
|
|
// needs to be MapToSmartType<TResult>::type to hold non trial types
|
|
TResult m_result{};
|
|
};
|
|
|
|
extern const __declspec(selectany) wchar_t SyncAsyncActionName[] = L"SyncActionAction";
|
|
|
|
class SyncAsyncActionOp WrlFinal : public Microsoft::WRL::RuntimeClass<
|
|
ABI::Windows::Foundation::IAsyncAction,
|
|
Microsoft::WRL::AsyncBase<
|
|
ABI::Windows::Foundation::IAsyncActionCompletedHandler,
|
|
Microsoft::WRL::Details::Nil,
|
|
Microsoft::WRL::AsyncResultType::SingleResult
|
|
#ifndef _WRL_DISABLE_CAUSALITY_
|
|
,
|
|
Microsoft::WRL::AsyncCausalityOptions<SyncAsyncActionName>
|
|
#endif
|
|
>>
|
|
{
|
|
InspectableClass(InterfaceName_Windows_Foundation_IAsyncAction, TrustLevel::BaseTrust);
|
|
|
|
public:
|
|
IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncActionCompletedHandler* competed) override
|
|
{
|
|
competed->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed);
|
|
return S_OK;
|
|
}
|
|
|
|
IFACEMETHODIMP get_Completed(ABI::Windows::Foundation::IAsyncActionCompletedHandler** competed) override
|
|
{
|
|
*competed = nullptr;
|
|
return S_OK;
|
|
}
|
|
|
|
IFACEMETHODIMP GetResults() override
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT OnStart() override
|
|
{
|
|
return S_OK;
|
|
}
|
|
void OnClose() override
|
|
{
|
|
}
|
|
void OnCancel() override
|
|
{
|
|
}
|
|
};
|
|
} // namespace details
|
|
/// @endcond
|
|
|
|
//! Creates a WinRT async operation object that implements IAsyncOperation<TResult>. Use mostly for testing and for mocking APIs.
|
|
template <typename TResult>
|
|
HRESULT make_synchronous_async_operation_nothrow(ABI::Windows::Foundation::IAsyncOperation<TResult>** result, const TResult& value)
|
|
{
|
|
return Microsoft::WRL::MakeAndInitialize<details::SyncAsyncOp<TResult>>(result, value);
|
|
}
|
|
|
|
//! Creates a WinRT async operation object that implements IAsyncAction. Use mostly for testing and for mocking APIs.
|
|
inline HRESULT make_synchronous_async_action_nothrow(ABI::Windows::Foundation::IAsyncAction** result)
|
|
{
|
|
return Microsoft::WRL::MakeAndInitialize<details::SyncAsyncActionOp>(result);
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Creates a WinRT async operation object that implements IAsyncOperation<TResult>. Use mostly for testing and for mocking APIs.
|
|
// TODO: map TRealResult and TSmartResult into SyncAsyncOp.
|
|
template <typename TResult, typename TRealResult = typename details::MapAsyncOpResultType<TResult>::type, typename TSmartResult = typename details::MapToSmartType<TRealResult>::type>
|
|
void make_synchronous_async_operation(ABI::Windows::Foundation::IAsyncOperation<TResult>** result, const TResult& value)
|
|
{
|
|
THROW_IF_FAILED((Microsoft::WRL::MakeAndInitialize<details::SyncAsyncOp<TResult>>(result, value)));
|
|
}
|
|
|
|
//! Creates a WinRT async operation object that implements IAsyncAction. Use mostly for testing and for mocking APIs.
|
|
inline void make_synchronous_async_action(ABI::Windows::Foundation::IAsyncAction** result)
|
|
{
|
|
THROW_IF_FAILED((Microsoft::WRL::MakeAndInitialize<details::SyncAsyncActionOp>(result)));
|
|
}
|
|
#endif
|
|
#pragma endregion
|
|
|
|
#pragma region EventRegistrationToken RAII wrapper
|
|
|
|
// unique_winrt_event_token[_cx] is an RAII wrapper around EventRegistrationToken. When the unique_winrt_event_token[_cx] is
|
|
// destroyed, the event is automatically unregistered. Declare a wil::unique_winrt_event_token[_cx]<T> at the scope the event
|
|
// should be registered for (often they are tied to object lifetime), where T is the type of the event sender
|
|
// wil::unique_winrt_event_token_cx<Windows::UI::Xaml::Controls::Button> m_token;
|
|
//
|
|
// Macros have been defined to register for handling the event and then returning an unique_winrt_event_token[_cx]. These
|
|
// macros simply hide the function references for adding and removing the event.
|
|
// C++/CX m_token = WI_MakeUniqueWinRtEventTokenCx(ExampleEventName, sender, handler);
|
|
// ABI m_token = WI_MakeUniqueWinRtEventToken(ExampleEventName, sender, handler, &m_token); // Exception and failfast
|
|
// ABI RETURN_IF_FAILED(WI_MakeUniqueWinRtEventTokenNoThrow(ExampleEventName, sender, handler, &m_token)); // No throw variant
|
|
//
|
|
// When the wrapper is destroyed, the handler will be unregistered. You can explicitly unregister the handler prior.
|
|
// m_token.reset();
|
|
//
|
|
// You can release the EventRegistrationToken from being managed by the wrapper by calling .release()
|
|
// m_token.release(); // DANGER: no longer being managed
|
|
//
|
|
// If you just need the value of the EventRegistrationToken you can call .get()
|
|
// m_token.get();
|
|
//
|
|
// See "onecore\shell\tests\wil\UniqueWinRTEventTokenTests.cpp" for more examples of usage in ABI and C++/CX.
|
|
|
|
// clang-format off
|
|
#ifdef __cplusplus_winrt
|
|
namespace details
|
|
{
|
|
template <typename T>
|
|
struct remove_reference
|
|
{
|
|
typedef T type;
|
|
};
|
|
template <typename T>
|
|
struct remove_reference<T^>
|
|
{
|
|
typedef T type;
|
|
};
|
|
} // namespace details
|
|
|
|
template <typename T>
|
|
class unique_winrt_event_token_cx
|
|
{
|
|
using removal_func = void (T::*)(Windows::Foundation::EventRegistrationToken);
|
|
using static_removal_func = void(__cdecl*)(Windows::Foundation::EventRegistrationToken);
|
|
|
|
public:
|
|
unique_winrt_event_token_cx() = default;
|
|
|
|
unique_winrt_event_token_cx(Windows::Foundation::EventRegistrationToken token, T^ sender, removal_func removalFunction) WI_NOEXCEPT
|
|
: m_token(token),
|
|
m_weakSender(sender),
|
|
m_removalFunction(removalFunction)
|
|
{
|
|
}
|
|
|
|
unique_winrt_event_token_cx(Windows::Foundation::EventRegistrationToken token, static_removal_func removalFunction) WI_NOEXCEPT
|
|
: m_token(token),
|
|
m_staticRemovalFunction(removalFunction)
|
|
{
|
|
}
|
|
|
|
unique_winrt_event_token_cx(const unique_winrt_event_token_cx&) = delete;
|
|
unique_winrt_event_token_cx& operator=(const unique_winrt_event_token_cx&) = delete;
|
|
|
|
unique_winrt_event_token_cx(unique_winrt_event_token_cx&& other) WI_NOEXCEPT
|
|
: m_token(other.m_token),
|
|
m_weakSender(wistd::move(other.m_weakSender)),
|
|
m_removalFunction(other.m_removalFunction),
|
|
m_staticRemovalFunction(other.m_staticRemovalFunction)
|
|
{
|
|
other.m_token = {};
|
|
other.m_weakSender = nullptr;
|
|
other.m_removalFunction = nullptr;
|
|
other.m_staticRemovalFunction = nullptr;
|
|
}
|
|
|
|
unique_winrt_event_token_cx& operator=(unique_winrt_event_token_cx&& other) WI_NOEXCEPT
|
|
{
|
|
if (this != wistd::addressof(other))
|
|
{
|
|
reset();
|
|
|
|
wistd::swap_wil(m_token, other.m_token);
|
|
wistd::swap_wil(m_weakSender, other.m_weakSender);
|
|
wistd::swap_wil(m_removalFunction, other.m_removalFunction);
|
|
wistd::swap_wil(m_staticRemovalFunction, other.m_staticRemovalFunction);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
~unique_winrt_event_token_cx() WI_NOEXCEPT
|
|
{
|
|
reset();
|
|
}
|
|
|
|
WI_NODISCARD explicit operator bool() const WI_NOEXCEPT
|
|
{
|
|
return (m_token.Value != 0);
|
|
}
|
|
|
|
WI_NODISCARD Windows::Foundation::EventRegistrationToken get() const WI_NOEXCEPT
|
|
{
|
|
return m_token;
|
|
}
|
|
|
|
void reset() noexcept
|
|
{
|
|
if (m_token.Value != 0)
|
|
{
|
|
if (m_staticRemovalFunction)
|
|
{
|
|
(*m_staticRemovalFunction)(m_token);
|
|
}
|
|
else
|
|
{
|
|
auto resolvedSender = [&]() {
|
|
try
|
|
{
|
|
return m_weakSender.Resolve<T>();
|
|
}
|
|
catch (...)
|
|
{
|
|
// Ignore RPC or other failures that are unavoidable in some cases
|
|
// matching wil::unique_winrt_event_token and winrt::event_revoker
|
|
return static_cast<T^>(nullptr);
|
|
}
|
|
}();
|
|
if (resolvedSender)
|
|
{
|
|
(resolvedSender->*m_removalFunction)(m_token);
|
|
}
|
|
}
|
|
release();
|
|
}
|
|
}
|
|
|
|
// Stops the wrapper from managing resource and returns the EventRegistrationToken.
|
|
Windows::Foundation::EventRegistrationToken release() WI_NOEXCEPT
|
|
{
|
|
auto token = m_token;
|
|
m_token = {};
|
|
m_weakSender = nullptr;
|
|
m_removalFunction = nullptr;
|
|
m_staticRemovalFunction = nullptr;
|
|
return token;
|
|
}
|
|
|
|
private:
|
|
Windows::Foundation::EventRegistrationToken m_token = {};
|
|
Platform::WeakReference m_weakSender;
|
|
removal_func m_removalFunction = nullptr;
|
|
static_removal_func m_staticRemovalFunction = nullptr;
|
|
};
|
|
|
|
#endif
|
|
// clang-format on
|
|
|
|
template <typename T>
|
|
class unique_winrt_event_token
|
|
{
|
|
using removal_func = HRESULT (__stdcall T::*)(::EventRegistrationToken);
|
|
|
|
public:
|
|
unique_winrt_event_token() = default;
|
|
|
|
unique_winrt_event_token(::EventRegistrationToken token, T* sender, removal_func removalFunction) WI_NOEXCEPT
|
|
: m_token(token),
|
|
m_removalFunction(removalFunction)
|
|
{
|
|
m_weakSender = wil::com_weak_query_failfast(sender);
|
|
}
|
|
|
|
unique_winrt_event_token(const unique_winrt_event_token&) = delete;
|
|
unique_winrt_event_token& operator=(const unique_winrt_event_token&) = delete;
|
|
|
|
unique_winrt_event_token(unique_winrt_event_token&& other) WI_NOEXCEPT : m_token(other.m_token),
|
|
m_weakSender(wistd::move(other.m_weakSender)),
|
|
m_removalFunction(other.m_removalFunction)
|
|
{
|
|
other.m_token = {};
|
|
other.m_removalFunction = nullptr;
|
|
}
|
|
|
|
unique_winrt_event_token& operator=(unique_winrt_event_token&& other) WI_NOEXCEPT
|
|
{
|
|
if (this != wistd::addressof(other))
|
|
{
|
|
reset();
|
|
|
|
wistd::swap_wil(m_token, other.m_token);
|
|
wistd::swap_wil(m_weakSender, other.m_weakSender);
|
|
wistd::swap_wil(m_removalFunction, other.m_removalFunction);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
~unique_winrt_event_token() WI_NOEXCEPT
|
|
{
|
|
reset();
|
|
}
|
|
|
|
WI_NODISCARD explicit operator bool() const WI_NOEXCEPT
|
|
{
|
|
return (m_token.value != 0);
|
|
}
|
|
|
|
WI_NODISCARD ::EventRegistrationToken get() const WI_NOEXCEPT
|
|
{
|
|
return m_token;
|
|
}
|
|
|
|
void reset() WI_NOEXCEPT
|
|
{
|
|
if (m_token.value != 0)
|
|
{
|
|
// If T cannot be QI'ed from the weak object then T is not a COM interface.
|
|
auto resolvedSender = m_weakSender.try_query<T>();
|
|
if (resolvedSender)
|
|
{
|
|
FAIL_FAST_IF_FAILED((resolvedSender.get()->*m_removalFunction)(m_token));
|
|
}
|
|
release();
|
|
}
|
|
}
|
|
|
|
// Stops the wrapper from managing resource and returns the EventRegistrationToken.
|
|
::EventRegistrationToken release() WI_NOEXCEPT
|
|
{
|
|
auto token = m_token;
|
|
m_token = {};
|
|
m_weakSender = nullptr;
|
|
m_removalFunction = nullptr;
|
|
return token;
|
|
}
|
|
|
|
private:
|
|
::EventRegistrationToken m_token = {};
|
|
wil::com_weak_ref_failfast m_weakSender;
|
|
removal_func m_removalFunction = nullptr;
|
|
};
|
|
|
|
/// @cond
|
|
namespace details
|
|
{
|
|
// clang-format off
|
|
#ifdef __cplusplus_winrt
|
|
|
|
// Handles registration of the event handler to the subscribing object and then wraps the EventRegistrationToken in unique_winrt_event_token.
|
|
// Not intended to be directly called. Use the WI_MakeUniqueWinRtEventTokenCx macro to abstract away specifying the functions that handle addition and removal.
|
|
template <typename T, typename addition_func, typename removal_func, typename handler>
|
|
inline wil::unique_winrt_event_token_cx<T> make_unique_winrt_event_token_cx(
|
|
T^ sender, addition_func additionFunc, removal_func removalFunc, handler^ h)
|
|
{
|
|
auto rawToken = (sender->*additionFunc)(h);
|
|
wil::unique_winrt_event_token_cx<T> temp(rawToken, sender, removalFunc);
|
|
return temp;
|
|
}
|
|
|
|
template <typename T, typename addition_func, typename removal_func, typename handler>
|
|
inline wil::unique_winrt_event_token_cx<T> make_unique_winrt_static_event_token_cx(
|
|
addition_func additionFunc, removal_func removalFunc, handler^ h)
|
|
{
|
|
auto rawToken = (*additionFunc)(h);
|
|
wil::unique_winrt_event_token_cx<T> temp(rawToken, removalFunc);
|
|
return temp;
|
|
}
|
|
|
|
#endif // __cplusplus_winrt
|
|
// clang-format on
|
|
|
|
// Handles registration of the event handler to the subscribing object and then wraps the EventRegistrationToken in unique_winrt_event_token.
|
|
// Not intended to be directly called. Use the WI_MakeUniqueWinRtEventToken macro to abstract away specifying the functions that handle addition and removal.
|
|
template <typename err_policy = wil::err_returncode_policy, typename T, typename addition_func, typename removal_func, typename handler>
|
|
inline auto make_unique_winrt_event_token(
|
|
T* sender, addition_func additionFunc, removal_func removalFunc, handler h, wil::unique_winrt_event_token<T>* token_reference)
|
|
{
|
|
::EventRegistrationToken rawToken;
|
|
err_policy::HResult((sender->*additionFunc)(h, &rawToken));
|
|
*token_reference = wil::unique_winrt_event_token<T>(rawToken, sender, removalFunc);
|
|
return err_policy::OK();
|
|
}
|
|
|
|
// Overload make function to allow for returning the constructed object when not using HRESULT based code.
|
|
template <typename err_policy = wil::err_returncode_policy, typename T, typename addition_func, typename removal_func, typename handler>
|
|
inline typename wistd::enable_if<!wistd::is_same<err_policy, wil::err_returncode_policy>::value, wil::unique_winrt_event_token<T>>::type make_unique_winrt_event_token(
|
|
T* sender, addition_func additionFunc, removal_func removalFunc, handler h)
|
|
{
|
|
::EventRegistrationToken rawToken;
|
|
err_policy::HResult((sender->*additionFunc)(h, &rawToken));
|
|
return wil::unique_winrt_event_token<T>(rawToken, sender, removalFunc);
|
|
}
|
|
|
|
} // namespace details
|
|
/// @endcond
|
|
|
|
// Helper macros to abstract function names for event addition and removal.
|
|
#ifdef __cplusplus_winrt
|
|
|
|
#define WI_MakeUniqueWinRtEventTokenCx(_event, _object, _handler) \
|
|
wil::details::make_unique_winrt_event_token_cx( \
|
|
_object, \
|
|
&wil::details::remove_reference<decltype(_object)>::type::##_event## ::add, \
|
|
&wil::details::remove_reference<decltype(_object)>::type::##_event## ::remove, \
|
|
_handler)
|
|
|
|
#define WI_MakeUniqueWinRtStaticEventTokenCx(_event, _baseType, _handler) \
|
|
wil::details::make_unique_winrt_static_event_token_cx<_baseType>( \
|
|
&##_baseType## ::##_event## ::add, &##_baseType## ::##_event## ::remove, _handler)
|
|
|
|
#endif // __cplusplus_winrt
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
|
|
#define WI_MakeUniqueWinRtEventToken(_event, _object, _handler) \
|
|
wil::details::make_unique_winrt_event_token<wil::err_exception_policy>( \
|
|
_object, \
|
|
&wistd::remove_pointer<decltype(_object)>::type::add_##_event, \
|
|
&wistd::remove_pointer<decltype(_object)>::type::remove_##_event, \
|
|
_handler)
|
|
|
|
#endif // WIL_ENABLE_EXCEPTIONS
|
|
|
|
#define WI_MakeUniqueWinRtEventTokenNoThrow(_event, _object, _handler, _token_reference) \
|
|
wil::details::make_unique_winrt_event_token( \
|
|
_object, \
|
|
&wistd::remove_pointer<decltype(_object)>::type::add_##_event, \
|
|
&wistd::remove_pointer<decltype(_object)>::type::remove_##_event, \
|
|
_handler, \
|
|
_token_reference)
|
|
|
|
#define WI_MakeUniqueWinRtEventTokenFailFast(_event, _object, _handler) \
|
|
wil::details::make_unique_winrt_event_token<wil::err_failfast_policy>( \
|
|
_object, \
|
|
&wistd::remove_pointer<decltype(_object)>::type::add_##_event, \
|
|
&wistd::remove_pointer<decltype(_object)>::type::remove_##_event, \
|
|
_handler)
|
|
|
|
#pragma endregion // EventRegistrationToken RAII wrapper
|
|
|
|
} // namespace wil
|
|
|
|
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
|
|
/// @cond
|
|
template <>
|
|
struct ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Foundation::IAsyncAction*>
|
|
: ABI::Windows::Foundation::IAsyncOperation_impl<ABI::Windows::Foundation::IAsyncAction*>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperation<IAsyncAction*>";
|
|
}
|
|
};
|
|
|
|
template <typename P>
|
|
struct ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Foundation::IAsyncAction*, P>
|
|
: ABI::Windows::Foundation::IAsyncOperationWithProgress_impl<ABI::Windows::Foundation::IAsyncAction*, P>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperationWithProgress<IAsyncAction*,P>";
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Foundation::IAsyncOperation<T>*>
|
|
: ABI::Windows::Foundation::IAsyncOperation_impl<ABI::Windows::Foundation::IAsyncOperation<T>*>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperation<IAsyncOperation<T>*>";
|
|
}
|
|
};
|
|
|
|
template <typename T, typename P>
|
|
struct ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Foundation::IAsyncOperation<T>*, P>
|
|
: ABI::Windows::Foundation::IAsyncOperationWithProgress_impl<ABI::Windows::Foundation::IAsyncOperation<T>*, P>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperationWithProgress<IAsyncOperation<T>*,P>";
|
|
}
|
|
};
|
|
|
|
template <typename T, typename P>
|
|
struct ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>*>
|
|
: ABI::Windows::Foundation::IAsyncOperation_impl<ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>*>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperation<IAsyncOperationWithProgress<T,P>*>";
|
|
}
|
|
};
|
|
|
|
template <typename T, typename P, typename Z>
|
|
struct ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>*, Z>
|
|
: ABI::Windows::Foundation::IAsyncOperationWithProgress_impl<ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>*, Z>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperationWithProgress<IAsyncOperationWithProgress<T,P>*,Z>";
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct ABI::Windows::Foundation::IAsyncOperationCompletedHandler<ABI::Windows::Foundation::IAsyncAction*>
|
|
: ABI::Windows::Foundation::IAsyncOperationCompletedHandler_impl<ABI::Windows::Foundation::IAsyncAction*>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperationCompletedHandler<IAsyncAction*>";
|
|
}
|
|
};
|
|
|
|
template <typename P>
|
|
struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<ABI::Windows::Foundation::IAsyncAction*, P>
|
|
: ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler_impl<ABI::Windows::Foundation::IAsyncAction*, P>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperationWithProgressCompletedHandler<IAsyncAction*,P>";
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct ABI::Windows::Foundation::IAsyncOperationCompletedHandler<ABI::Windows::Foundation::IAsyncOperation<T>*>
|
|
: ABI::Windows::Foundation::IAsyncOperationCompletedHandler_impl<ABI::Windows::Foundation::IAsyncOperation<T>*>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperationCompletedHandler<IAsyncOperation<T>*>";
|
|
}
|
|
};
|
|
|
|
template <typename T, typename P>
|
|
struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<ABI::Windows::Foundation::IAsyncOperation<T>*, P>
|
|
: ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler_impl<ABI::Windows::Foundation::IAsyncOperation<T>*, P>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperationWithProgressCompletedHandler<IAsyncOperation<T>*,P>";
|
|
}
|
|
};
|
|
|
|
template <typename T, typename P>
|
|
struct ABI::Windows::Foundation::IAsyncOperationCompletedHandler<ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>*>
|
|
: ABI::Windows::Foundation::IAsyncOperationCompletedHandler_impl<ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>*>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperationCompletedHandler<IAsyncOperationWithProgress<T>*>";
|
|
}
|
|
};
|
|
|
|
template <typename T, typename P, typename Z>
|
|
struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>*, Z>
|
|
: ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler_impl<ABI::Windows::Foundation::IAsyncOperationWithProgress<T, P>*, Z>
|
|
{
|
|
static const wchar_t* z_get_rc_name_impl()
|
|
{
|
|
return L"IAsyncOperationWithProgressCompletedHandler<IAsyncOperationWithProgress<T,P>*,Z>";
|
|
}
|
|
};
|
|
/// @endcond
|
|
#endif // NTDDI_VERSION >= NTDDI_WINBLUE
|
|
|
|
#if !defined(MIDL_NS_PREFIX) && !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__)
|
|
// Internal .idl files use the namespace without the ABI prefix. Macro out ABI for that case
|
|
#pragma pop_macro("ABI")
|
|
#endif
|
|
|
|
#if __WI_HAS_STD_LESS
|
|
|
|
namespace std
|
|
{
|
|
//! Specialization of `std::less` for `Microsoft::WRL::Wrappers::HString` that uses `hstring_less` for the
|
|
//! comparison function object.
|
|
template <>
|
|
struct less<Microsoft::WRL::Wrappers::HString> : public wil::hstring_less
|
|
{
|
|
};
|
|
|
|
//! Specialization of `std::less` for `wil::unique_hstring` that uses `hstring_less` for the comparison function
|
|
//! object.
|
|
template <>
|
|
struct less<wil::unique_hstring> : public wil::hstring_less
|
|
{
|
|
};
|
|
} // namespace std
|
|
|
|
#endif
|
|
|
|
#endif // __WIL_WINRT_INCLUDED
|