mirror of https://github.com/PCSX2/pcsx2.git
3331 lines
129 KiB
C++
3331 lines
129 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
|
|
//! Smart pointers and other thin usability pattern wrappers over COM patterns.
|
|
#ifndef __WIL_COM_INCLUDED
|
|
#define __WIL_COM_INCLUDED
|
|
|
|
#include <WeakReference.h>
|
|
#include <combaseapi.h>
|
|
#include "result.h"
|
|
#include "resource.h" // last to ensure _COMBASEAPI_H_ protected definitions are available
|
|
|
|
#if __has_include(<tuple>)
|
|
#include <tuple>
|
|
#endif
|
|
#if __has_include(<type_traits>)
|
|
#include <type_traits>
|
|
#endif
|
|
|
|
// Forward declaration within WIL (see https://msdn.microsoft.com/en-us/library/br244983.aspx)
|
|
/// @cond
|
|
namespace Microsoft
|
|
{
|
|
namespace WRL
|
|
{
|
|
template <typename T>
|
|
class ComPtr;
|
|
}
|
|
} // namespace Microsoft
|
|
/// @endcond
|
|
|
|
namespace wil
|
|
{
|
|
/// @cond
|
|
namespace details
|
|
{
|
|
// We can't directly use wistd::is_convertible as it returns TRUE for an ambiguous conversion.
|
|
// Adding is_abstract to the mix, enables us to allow conversion for interfaces, but deny it for
|
|
// classes (where the multiple inheritance causes ambiguity).
|
|
// NOTE: I've reached out to vcsig on this topic and it turns out that __is_convertible_to should NEVER
|
|
// return true for ambiguous conversions. This was a bug in our compiler that has since been fixed.
|
|
// Eventually, once that fix propagates we can move to a more efficient __is_convertible_to without
|
|
// the added complexity.
|
|
template <class TFrom, class TTo>
|
|
struct is_com_convertible
|
|
: wistd::bool_constant<__is_convertible_to(TFrom, TTo) && (__is_abstract(TFrom) || wistd::is_same<TFrom, TTo>::value)>
|
|
{
|
|
};
|
|
|
|
using tag_com_query = wistd::integral_constant<char, 0>;
|
|
using tag_try_com_query = wistd::integral_constant<char, 1>;
|
|
using tag_com_copy = wistd::integral_constant<char, 2>;
|
|
using tag_try_com_copy = wistd::integral_constant<char, 3>;
|
|
|
|
class default_query_policy
|
|
{
|
|
public:
|
|
template <typename T>
|
|
inline static HRESULT query(_In_ T* ptr, REFIID riid, _COM_Outptr_ void** result)
|
|
{
|
|
return ptr->QueryInterface(riid, result);
|
|
}
|
|
|
|
template <typename T, typename TResult>
|
|
inline static HRESULT query(_In_ T* ptr, _COM_Outptr_ TResult** result)
|
|
{
|
|
return query_dispatch(ptr, typename details::is_com_convertible<T*, TResult*>::type(), result);
|
|
}
|
|
|
|
private:
|
|
template <typename T, typename TResult>
|
|
inline static HRESULT query_dispatch(_In_ T* ptr, wistd::true_type, _COM_Outptr_ TResult** result) // convertible
|
|
{
|
|
*result = ptr;
|
|
(*result)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
template <typename T, typename TResult>
|
|
inline static HRESULT query_dispatch(_In_ T* ptr, wistd::false_type, _COM_Outptr_ TResult** result) // not convertible
|
|
{
|
|
auto hr = ptr->QueryInterface(IID_PPV_ARGS(result));
|
|
__analysis_assume(SUCCEEDED(hr) || (*result == nullptr));
|
|
return hr;
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct query_policy_helper
|
|
{
|
|
using type = default_query_policy;
|
|
};
|
|
|
|
class weak_query_policy
|
|
{
|
|
public:
|
|
inline static HRESULT query(_In_ IWeakReference* ptr, REFIID riid, _COM_Outptr_ void** result)
|
|
{
|
|
WI_ASSERT_MSG(riid != __uuidof(IWeakReference), "Cannot resolve a weak reference to IWeakReference");
|
|
*result = nullptr;
|
|
|
|
IInspectable* temp;
|
|
HRESULT hr = ptr->Resolve(__uuidof(IInspectable), &temp);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (temp == nullptr)
|
|
{
|
|
return E_NOT_SET;
|
|
}
|
|
hr = temp->QueryInterface(riid, result);
|
|
__analysis_assume(SUCCEEDED(hr) || (*result == nullptr));
|
|
temp->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
template <typename TResult>
|
|
inline static HRESULT query(_In_ IWeakReference* ptr, _COM_Outptr_ TResult** result)
|
|
{
|
|
static_assert(!wistd::is_same<IWeakReference, TResult>::value, "Cannot resolve a weak reference to IWeakReference");
|
|
return query_dispatch(ptr, wistd::is_base_of<IInspectable, TResult>(), result);
|
|
}
|
|
|
|
private:
|
|
template <typename TResult>
|
|
static HRESULT query_dispatch(_In_ IWeakReference* ptr, wistd::true_type, _COM_Outptr_ TResult** result)
|
|
{
|
|
auto hr = ptr->Resolve(__uuidof(TResult), reinterpret_cast<IInspectable**>(result));
|
|
if (SUCCEEDED(hr) && (*result == nullptr))
|
|
{
|
|
hr = E_NOT_SET;
|
|
}
|
|
__analysis_assume(SUCCEEDED(hr) || (*result == nullptr));
|
|
return hr;
|
|
}
|
|
|
|
template <typename TResult>
|
|
static HRESULT query_dispatch(_In_ IWeakReference* ptr, wistd::false_type, _COM_Outptr_ TResult** result)
|
|
{
|
|
return query(ptr, IID_PPV_ARGS(result));
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct query_policy_helper<IWeakReference>
|
|
{
|
|
using type = weak_query_policy;
|
|
};
|
|
|
|
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
|
|
class agile_query_policy
|
|
{
|
|
public:
|
|
inline static HRESULT query(_In_ IAgileReference* ptr, REFIID riid, _COM_Outptr_ void** result)
|
|
{
|
|
WI_ASSERT_MSG(riid != __uuidof(IAgileReference), "Cannot resolve a agile reference to IAgileReference");
|
|
auto hr = ptr->Resolve(riid, result);
|
|
__analysis_assume(SUCCEEDED(hr) || (*result == nullptr)); // IAgileReference::Resolve not annotated correctly
|
|
return hr;
|
|
}
|
|
|
|
template <typename TResult>
|
|
static HRESULT query(_In_ IAgileReference* ptr, _COM_Outptr_ TResult** result)
|
|
{
|
|
static_assert(!wistd::is_same<IAgileReference, TResult>::value, "Cannot resolve a agile reference to IAgileReference");
|
|
return query(ptr, __uuidof(TResult), reinterpret_cast<void**>(result));
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct query_policy_helper<IAgileReference>
|
|
{
|
|
using type = agile_query_policy;
|
|
};
|
|
#endif
|
|
|
|
template <typename T>
|
|
using query_policy_t = typename query_policy_helper<typename wistd::remove_pointer<T>::type>::type;
|
|
|
|
} // namespace details
|
|
/// @endcond
|
|
|
|
//! Represents the base template type that implements com_ptr, com_weak_ref, and com_agile_ref.
|
|
//! See @ref page_comptr for more background. See @ref page_query for more information on querying with WIL.
|
|
//! @tparam T Represents the type being held by the com_ptr_t.
|
|
//! For com_ptr, this will always be the interface being represented. For com_weak_ref, this will always
|
|
//! be IWeakReference. For com_agile_ref, this will always be IAgileReference.
|
|
//! @tparam err_policy Represents the error policy for the class (error codes, exceptions, or fail fast; see @ref page_errors)
|
|
template <typename T, typename err_policy = err_exception_policy>
|
|
class com_ptr_t
|
|
{
|
|
private:
|
|
using element_type_reference = typename wistd::add_lvalue_reference<T>::type;
|
|
using query_policy = details::query_policy_t<T>;
|
|
|
|
public:
|
|
//! The function return result (HRESULT or void) for the given err_policy (see @ref page_errors).
|
|
using result = typename err_policy::result;
|
|
//! The template type `T` being held by the com_ptr_t.
|
|
using element_type = T;
|
|
//! A pointer to the template type `T` being held by the com_ptr_t (what `get()` returns).
|
|
using pointer = T*;
|
|
|
|
//! @name Constructors
|
|
//! @{
|
|
|
|
//! Default constructor (holds nullptr).
|
|
com_ptr_t() WI_NOEXCEPT : m_ptr(nullptr)
|
|
{
|
|
}
|
|
|
|
//! Implicit construction from nullptr_t (holds nullptr).
|
|
com_ptr_t(wistd::nullptr_t) WI_NOEXCEPT : com_ptr_t()
|
|
{
|
|
}
|
|
|
|
//! Implicit construction from a compatible raw interface pointer (AddRef's the parameter).
|
|
com_ptr_t(pointer ptr) WI_NOEXCEPT : m_ptr(ptr)
|
|
{
|
|
if (m_ptr)
|
|
{
|
|
m_ptr->AddRef();
|
|
}
|
|
}
|
|
|
|
//! Copy-construction from a like `com_ptr_t` (copies and AddRef's the parameter).
|
|
com_ptr_t(const com_ptr_t& other) WI_NOEXCEPT : com_ptr_t(other.get())
|
|
{
|
|
}
|
|
|
|
//! Copy-construction from a convertible `com_ptr_t` (copies and AddRef's the parameter).
|
|
template <class U, typename err, class = wistd::enable_if_t<__is_convertible_to(U*, pointer)>>
|
|
com_ptr_t(const com_ptr_t<U, err>& other) WI_NOEXCEPT : com_ptr_t(static_cast<pointer>(other.get()))
|
|
{
|
|
}
|
|
|
|
//! Move construction from a like `com_ptr_t` (avoids AddRef/Release by moving from the parameter).
|
|
com_ptr_t(com_ptr_t&& other) WI_NOEXCEPT : m_ptr(other.detach())
|
|
{
|
|
}
|
|
|
|
//! Move construction from a compatible `com_ptr_t` (avoids AddRef/Release by moving from the parameter).
|
|
template <class U, typename err, class = wistd::enable_if_t<__is_convertible_to(U*, pointer)>>
|
|
com_ptr_t(com_ptr_t<U, err>&& other) WI_NOEXCEPT : m_ptr(other.detach())
|
|
{
|
|
}
|
|
//! @}
|
|
|
|
//! Destructor (releases the pointer).
|
|
~com_ptr_t() WI_NOEXCEPT
|
|
{
|
|
if (m_ptr)
|
|
{
|
|
m_ptr->Release();
|
|
}
|
|
}
|
|
|
|
//! @name Assignment operators
|
|
//! @{
|
|
|
|
//! Assign to nullptr (releases the current pointer, holds nullptr).
|
|
com_ptr_t& operator=(wistd::nullptr_t) WI_NOEXCEPT
|
|
{
|
|
reset();
|
|
return *this;
|
|
}
|
|
|
|
//! Assign a compatible raw interface pointer (releases current pointer, copies and AddRef's the parameter).
|
|
com_ptr_t& operator=(pointer other) WI_NOEXCEPT
|
|
{
|
|
auto ptr = m_ptr;
|
|
m_ptr = other;
|
|
if (m_ptr)
|
|
{
|
|
m_ptr->AddRef();
|
|
}
|
|
if (ptr)
|
|
{
|
|
ptr->Release();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
//! Assign a like `com_ptr_t` (releases current pointer, copies and AddRef's the parameter).
|
|
com_ptr_t& operator=(const com_ptr_t& other) WI_NOEXCEPT
|
|
{
|
|
return operator=(other.get());
|
|
}
|
|
|
|
//! Assign a convertible `com_ptr_t` (releases current pointer, copies and AddRef's the parameter).
|
|
template <class U, typename err, class = wistd::enable_if_t<__is_convertible_to(U*, pointer)>>
|
|
com_ptr_t& operator=(const com_ptr_t<U, err>& other) WI_NOEXCEPT
|
|
{
|
|
return operator=(static_cast<pointer>(other.get()));
|
|
}
|
|
|
|
//! Move assign from a like `com_ptr_t` (releases current pointer, avoids AddRef/Release by moving the parameter).
|
|
com_ptr_t& operator=(com_ptr_t&& other) WI_NOEXCEPT
|
|
{
|
|
attach(other.detach());
|
|
return *this;
|
|
}
|
|
|
|
//! Move assignment from a compatible `com_ptr_t` (releases current pointer, avoids AddRef/Release by moving from the
|
|
//! parameter).
|
|
template <class U, typename err, class = wistd::enable_if_t<__is_convertible_to(U*, pointer)>>
|
|
com_ptr_t& operator=(com_ptr_t<U, err>&& other) WI_NOEXCEPT
|
|
{
|
|
attach(other.detach());
|
|
return *this;
|
|
}
|
|
//! @}
|
|
|
|
//! @name Modifiers
|
|
//! @{
|
|
|
|
//! Swap pointers with an another named com_ptr_t object.
|
|
template <typename err>
|
|
void swap(com_ptr_t<T, err>& other) WI_NOEXCEPT
|
|
{
|
|
auto ptr = m_ptr;
|
|
m_ptr = other.m_ptr;
|
|
other.m_ptr = ptr;
|
|
}
|
|
|
|
//! Swap pointers with a rvalue reference to another com_ptr_t object.
|
|
template <typename err>
|
|
void swap(com_ptr_t<T, err>&& other) WI_NOEXCEPT
|
|
{
|
|
swap(other);
|
|
}
|
|
|
|
//! Releases the pointer and sets it to nullptr.
|
|
void reset() WI_NOEXCEPT
|
|
{
|
|
auto ptr = m_ptr;
|
|
m_ptr = nullptr;
|
|
if (ptr)
|
|
{
|
|
ptr->Release();
|
|
}
|
|
}
|
|
|
|
//! Releases the pointer and sets it to nullptr.
|
|
void reset(wistd::nullptr_t) WI_NOEXCEPT
|
|
{
|
|
reset();
|
|
}
|
|
|
|
//! Takes ownership of a compatible raw interface pointer (releases pointer, copies but DOES NOT AddRef the parameter).
|
|
void attach(pointer other) WI_NOEXCEPT
|
|
{
|
|
auto ptr = m_ptr;
|
|
m_ptr = other;
|
|
if (ptr)
|
|
{
|
|
ULONG ref = ptr->Release();
|
|
WI_ASSERT_MSG(((other != ptr) || (ref > 0)), "Bug: Attaching the same already assigned, destructed pointer");
|
|
}
|
|
}
|
|
|
|
//! Relinquishes ownership and returns the internal interface pointer (DOES NOT release the detached pointer, sets class
|
|
//! pointer to null).
|
|
WI_NODISCARD pointer detach() WI_NOEXCEPT
|
|
{
|
|
auto temp = m_ptr;
|
|
m_ptr = nullptr;
|
|
return temp;
|
|
}
|
|
|
|
//! Returns the address of the internal pointer (releases ownership of the pointer BEFORE returning the address).
|
|
//! The pointer is explicitly released to prevent accidental leaks of the pointer. Coding standards generally indicate that
|
|
//! there is little valid `_Inout_` use of `IInterface**`, making this safe to do under typical use.
|
|
//! @see addressof
|
|
//! ~~~~
|
|
//! STDAPI GetMuffin(IMuffin **muffin);
|
|
//! wil::com_ptr<IMuffin> myMuffin;
|
|
//! THROW_IF_FAILED(GetMuffin(myMuffin.put()));
|
|
//! ~~~~
|
|
pointer* put() WI_NOEXCEPT
|
|
{
|
|
reset();
|
|
return &m_ptr;
|
|
}
|
|
|
|
//! Returns the address of the internal pointer casted to void** (releases ownership of the pointer BEFORE returning the
|
|
//! address).
|
|
//! @see put
|
|
void** put_void() WI_NOEXCEPT
|
|
{
|
|
return reinterpret_cast<void**>(put());
|
|
}
|
|
|
|
//! Returns the address of the internal pointer casted to IUnknown** (releases ownership of the pointer BEFORE returning the address).
|
|
//! @see put
|
|
::IUnknown** put_unknown() WI_NOEXCEPT
|
|
{
|
|
return reinterpret_cast<::IUnknown**>(put());
|
|
}
|
|
|
|
//! Returns the address of the internal pointer (releases ownership of the pointer BEFORE returning the address).
|
|
//! The pointer is explicitly released to prevent accidental leaks of the pointer. Coding standards generally indicate that
|
|
//! there is little valid `_Inout_` use of `IInterface**`, making this safe to do under typical use. Since this behavior is
|
|
//! not always immediately apparent, prefer to scope variables as close to use as possible (generally avoiding use of the same
|
|
//! com_ptr variable in successive calls to receive an output interface).
|
|
//! @see addressof
|
|
pointer* operator&() WI_NOEXCEPT
|
|
{
|
|
return put();
|
|
}
|
|
|
|
//! Returns the address of the internal pointer (does not release the pointer; should not be used for `_Out_` parameters)
|
|
pointer* addressof() WI_NOEXCEPT
|
|
{
|
|
return &m_ptr;
|
|
}
|
|
//! @}
|
|
|
|
//! @name Inspection
|
|
//! @{
|
|
|
|
//! Returns the address of the const internal pointer (does not release the pointer)
|
|
WI_NODISCARD const pointer* addressof() const WI_NOEXCEPT
|
|
{
|
|
return &m_ptr;
|
|
}
|
|
|
|
//! Returns 'true' if the pointer is assigned (NOT nullptr)
|
|
WI_NODISCARD explicit operator bool() const WI_NOEXCEPT
|
|
{
|
|
return (m_ptr != nullptr);
|
|
}
|
|
|
|
//! Returns the pointer
|
|
WI_NODISCARD pointer get() const WI_NOEXCEPT
|
|
{
|
|
return m_ptr;
|
|
}
|
|
|
|
//! Allows direct calls against the pointer (AV on internal nullptr)
|
|
WI_NODISCARD pointer operator->() const WI_NOEXCEPT
|
|
{
|
|
return m_ptr;
|
|
}
|
|
|
|
//! Dereferences the pointer (AV on internal nullptr)
|
|
WI_NODISCARD element_type_reference operator*() const WI_NOEXCEPT
|
|
{
|
|
return *m_ptr;
|
|
}
|
|
//! @}
|
|
|
|
//! @name Query helpers
|
|
//! * Retrieves the requested interface
|
|
//! * AV if the pointer is null
|
|
//! * Produce an error if the requested interface is unsupported
|
|
//!
|
|
//! See @ref page_query for more information
|
|
//! @{
|
|
|
|
//! Query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.query<IFoo>();`.
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! This method is the primary method that should be used to query a com_ptr in exception-based or fail-fast based code.
|
|
//! Error-code returning code should use @ref query_to so that the returned HRESULT can be examined. In the following
|
|
//! examples, `m_ptr` is an exception-based or fail-fast based com_ptr, com_weak_ref, or com_agile_ref:
|
|
//! ~~~~
|
|
//! auto foo = ptr.query<IFoo>();
|
|
//! foo->Method1();
|
|
//! foo->Method2();
|
|
//! ~~~~
|
|
//! For simple single-method calls, this method allows removing the temporary that holds the com_ptr:
|
|
//! ~~~~
|
|
//! ptr.query<IFoo>()->Method1();
|
|
//! ~~~~
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `com_ptr_t` pointer to the given interface `U`. The pointer is guaranteed not null. The returned
|
|
//! `com_ptr_t` type will be @ref com_ptr or @ref com_ptr_failfast (matching the error handling form of the
|
|
//! pointer being queried (exception based or fail-fast).
|
|
template <class U>
|
|
WI_NODISCARD inline com_ptr_t<U, err_policy> query() const
|
|
{
|
|
static_assert(wistd::is_same<void, result>::value, "query requires exceptions or fail fast; use try_query or query_to");
|
|
return com_ptr_t<U, err_policy>(m_ptr, details::tag_com_query());
|
|
}
|
|
|
|
//! Query for the interface of the given out parameter `U`: `ptr.query_to(&foo);`.
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! For fail-fast and exception-based behavior this routine should primarily be used to write to out parameters and @ref query
|
|
//! should be used to perform most queries. For error-code based code, this routine is the primary method that should be used
|
|
//! to query a com_ptr.
|
|
//!
|
|
//! Error-code based samples:
|
|
//! ~~~~
|
|
//! // class member being queried:
|
|
//! wil::com_ptr_nothrow<IUnknown> m_ptr;
|
|
//!
|
|
//! // simple query example:
|
|
//! wil::com_ptr_nothrow<IFoo> foo;
|
|
//! RETURN_IF_FAILED(m_ptr.query_to(&foo));
|
|
//! foo->FooMethod1();
|
|
//!
|
|
//! // output parameter example:
|
|
//! HRESULT GetFoo(_COM_Outptr_ IFoo** fooPtr)
|
|
//! {
|
|
//! RETURN_IF_FAILED(m_ptr.query_to(fooPtr));
|
|
//! return S_OK;
|
|
//! }
|
|
//! ~~~~
|
|
//! Exception or fail-fast samples:
|
|
//! ~~~~
|
|
//! // class member being queried
|
|
//! wil::com_ptr<IUnknown> m_ptr;
|
|
//!
|
|
//! void GetFoo(_COM_Outptr_ IFoo** fooPtr)
|
|
//! {
|
|
//! m_ptr.query_to(fooPtr);
|
|
//! }
|
|
//! ~~~~
|
|
//! @tparam U Represents the interface being queried (type of the output parameter). This interface does not need
|
|
//! to be specified directly. Rely upon template type deduction to pick up the type from the output
|
|
//! parameter.
|
|
//! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null
|
|
//! on failure.
|
|
//! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow,
|
|
//! @ref com_agile_ref_nothrow) this method returns an `HRESULT` indicating whether the query was
|
|
//! successful. Exception-based and fail-fast based classes do not return a value (void).
|
|
template <class U>
|
|
result query_to(_COM_Outptr_ U** ptrResult) const
|
|
{
|
|
// Prefast cannot see through the error policy + query_policy mapping and as a result fires 6388 and 28196 for this
|
|
// function. Suppression is also not working. Wrapping this entire function in #pragma warning(disable: 6388 28196) does
|
|
// not stop all of the prefast errors from being emitted.
|
|
#if defined(_PREFAST_)
|
|
*ptrResult = nullptr;
|
|
return err_policy::HResult(E_NOINTERFACE);
|
|
#else
|
|
return err_policy::HResult(query_policy::query(m_ptr, ptrResult));
|
|
#endif
|
|
}
|
|
|
|
//! Query for the requested interface using the iid, ppv pattern: `ptr.query_to(riid, ptr);`.
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! This method is built to implement an API boundary that exposes a returned pointer to a caller through the REFIID and
|
|
//! void** pointer pattern (like QueryInterface). This pattern should not be used outside of that pattern (through
|
|
//! IID_PPV_ARGS) as it is less efficient than the typed version of @ref query_to which can elide the QueryInterface in favor
|
|
//! of AddRef when the types are convertible.
|
|
//! ~~~~
|
|
//! // class member being queried:
|
|
//! wil::com_ptr_nothrow<IUnknown> m_ptr;
|
|
//!
|
|
//! // output parameter example:
|
|
//! HRESULT GetFoo(REFIID riid, _COM_Outptr_ void** ptrResult)
|
|
//! {
|
|
//! RETURN_IF_FAILED(m_ptr.query_to(riid, ptrResult));
|
|
//! return S_OK;
|
|
//! }
|
|
//! ~~~~
|
|
//! @param riid The interface to query for.
|
|
//! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null
|
|
//! on failure.
|
|
//! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow,
|
|
//! @ref com_agile_ref_nothrow) this method returns an `HRESULT` indicating whether the query was
|
|
//! successful. Exception-based and fail-fast based classes do not return a value (void).
|
|
result query_to(REFIID riid, _COM_Outptr_ void** ptrResult) const
|
|
{
|
|
// Prefast cannot see through the error policy + query_policy mapping and as a result and as a result fires 6388 and 28196
|
|
// for this function. Suppression is also not working. Wrapping this entire function in #pragma warning(disable: 6388
|
|
// 28196) does not stop the prefast errors from being emitted.
|
|
#if defined(_PREFAST_)
|
|
*ptrResult = nullptr;
|
|
return err_policy::HResult(E_NOINTERFACE);
|
|
#else
|
|
return err_policy::HResult(query_policy::query(m_ptr, riid, ptrResult));
|
|
#endif
|
|
}
|
|
//! @}
|
|
|
|
//! @name Try query helpers
|
|
//! * Attempts to retrieves the requested interface
|
|
//! * AV if the pointer is null
|
|
//! * Produce null if the requested interface is unsupported
|
|
//! * bool returns 'true' when query was successful
|
|
//!
|
|
//! See @ref page_query for more information.
|
|
//! @{
|
|
|
|
//! Attempt a query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.try_query<IFoo>();`
|
|
//! (null result when interface is unsupported).
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! This method can be used to query a com_ptr for an interface when it's known that support for that interface is
|
|
//! optional (failing the query should not produce an error). The caller must examine the returned pointer to see
|
|
//! if it's null before using it:
|
|
//! ~~~~
|
|
//! auto foo = ptr.try_query<IFoo>();
|
|
//! if (foo)
|
|
//! {
|
|
//! foo->Method1();
|
|
//! foo->Method2();
|
|
//! }
|
|
//! ~~~~
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `com_ptr_t` pointer to the given interface `U`. The returned pointer will be null if the interface is
|
|
//! not supported. The returned `com_ptr_t` will have the same error handling policy (exceptions, failfast or
|
|
//! error codes) as the pointer being queried.
|
|
template <class U>
|
|
WI_NODISCARD inline com_ptr_t<U, err_policy> try_query() const
|
|
{
|
|
return com_ptr_t<U, err_policy>(m_ptr, details::tag_try_com_query());
|
|
}
|
|
|
|
//! Attempts to query for the interface matching the given output parameter; returns a bool indicating if the query was
|
|
//! successful (non-null).
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! This method can be used to perform a query against a non-null interface when it's known that support for that interface is
|
|
//! optional (failing the query should not produce an error). The caller must examine the returned bool before using the
|
|
//! returned pointer.
|
|
//! ~~~~
|
|
//! wil::com_ptr_nothrow<IFoo> foo;
|
|
//! if (ptr.try_query_to(&foo))
|
|
//! {
|
|
//! foo->Method1();
|
|
//! foo->Method2();
|
|
//! }
|
|
//! ~~~~
|
|
//! @param ptrResult The pointer to query for. The interface to query is deduced from the type of this out parameter; do
|
|
//! not specify the type directly to the template.
|
|
//! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null).
|
|
template <class U>
|
|
_Success_return_ bool try_query_to(_COM_Outptr_ U** ptrResult) const
|
|
{
|
|
return SUCCEEDED(query_policy::query(m_ptr, ptrResult));
|
|
}
|
|
|
|
//! Attempts a query for the requested interface using the iid, ppv pattern: `ptr.try_query_to(riid, ptr);`.
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! This method is built to implement an API boundary that exposes a returned pointer to a caller through the REFIID and
|
|
//! void** pointer pattern (like QueryInterface). The key distinction is that this routine does not produce an error if the
|
|
//! request isn't fulfilled, so it's appropriate for `_COM_Outptr_result_maybenull_` cases. This pattern should not be used
|
|
//! outside of that pattern (through IID_PPV_ARGS) as it is less efficient than the typed version of @ref try_query_to which
|
|
//! can elide the QueryInterface in favor of AddRef when the types are convertible. The caller must examine the returned bool
|
|
//! before using the returned pointer.
|
|
//! ~~~~
|
|
//! // class member being queried:
|
|
//! wil::com_ptr_nothrow<IUnknown> m_ptr;
|
|
//!
|
|
//! // output parameter example (result may be null):
|
|
//! HRESULT GetFoo(REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult)
|
|
//! {
|
|
//! m_ptr.try_query_to(riid, ptrResult);
|
|
//! return S_OK;
|
|
//! }
|
|
//! ~~~~
|
|
//! @param riid The interface to query for.
|
|
//! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null
|
|
//! on failure.
|
|
//! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null).
|
|
_Success_return_ bool try_query_to(REFIID riid, _COM_Outptr_ void** ptrResult) const
|
|
{
|
|
return SUCCEEDED(query_policy::query(m_ptr, riid, ptrResult));
|
|
}
|
|
//! @}
|
|
|
|
//! @name Copy helpers
|
|
//! * Retrieves the requested interface
|
|
//! * Succeeds with null if the pointer is null
|
|
//! * Produce an error if the requested interface is unsupported
|
|
//!
|
|
//! See @ref page_query for more information.
|
|
//! @{
|
|
|
|
//! Query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.copy<IFoo>();` (succeeds and
|
|
//! returns a null ptr if the queried pointer is null).
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! This method is identical to @ref query with the exception that it can be used when the pointer is null. When used
|
|
//! against a null pointer, the returned pointer will always be null and an error will not be produced. Like query it will
|
|
//! produce an error for a non-null pointer that does not support the requested interface.
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `com_ptr_t` pointer to the given interface `U`. The pointer will be null ONLY if the pointer being queried is
|
|
//! null. The returned `com_ptr_t` type will be @ref com_ptr or @ref com_ptr_failfast (matching the error handling
|
|
//! form of the pointer being queried (exception based or fail-fast).
|
|
template <class U>
|
|
WI_NODISCARD inline com_ptr_t<U, err_policy> copy() const
|
|
{
|
|
static_assert(wistd::is_same<void, result>::value, "copy requires exceptions or fail fast; use the try_copy or copy_to method");
|
|
return com_ptr_t<U, err_policy>(m_ptr, details::tag_com_copy());
|
|
}
|
|
|
|
//! Query for the interface of the given out parameter `U`: `ptr.copy_to(&foo);` (succeeds and returns null ptr if the
|
|
//! queried pointer is null).
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! This method is identical to @ref query_to with the exception that it can be used when the pointer is null. When used
|
|
//! against a null pointer, the returned pointer will always be null and an error will not be produced. Like query_to it will
|
|
//! produce an error for a non-null pointer that does not support the requested interface.
|
|
//! @tparam U Represents the interface being queried (type of the output parameter). This interface does not need
|
|
//! to be specified directly. Rely upon template type deduction to pick up the type from the output
|
|
//! parameter.
|
|
//! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null
|
|
//! on failure OR assigned null when the source pointer is null.
|
|
//! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow,
|
|
//! @ref com_agile_ref_nothrow) this method returns an `HRESULT` indicating whether the query was
|
|
//! successful. Copying a null value is considered success. Exception-based and fail-fast based classes
|
|
//! do not return a value (void).
|
|
template <class U>
|
|
result copy_to(_COM_Outptr_result_maybenull_ U** ptrResult) const
|
|
{
|
|
if (m_ptr)
|
|
{
|
|
// Prefast cannot see through the error policy + query_policy mapping and as a result and as a result fires 6388 and
|
|
// 28196 for this function. Suppression is also not working. Wrapping this entire function in #pragma warning(disable:
|
|
// 6388 28196) does not stop the prefast errors from being emitted.
|
|
#if defined(_PREFAST_)
|
|
*ptrResult = nullptr;
|
|
return err_policy::HResult(E_NOINTERFACE);
|
|
#else
|
|
return err_policy::HResult(query_policy::query(m_ptr, ptrResult));
|
|
#endif
|
|
}
|
|
*ptrResult = nullptr;
|
|
return err_policy::OK();
|
|
}
|
|
|
|
//! Query for the requested interface using the iid, ppv pattern: `ptr.copy_to(riid, ptr);`. (succeeds and returns null ptr
|
|
//! if the queried pointer is null).
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! Identical to the corresponding @ref query_to method with the exception that it can be used when the pointer is null. When
|
|
//! used against a null pointer, the returned pointer will always be null and an error will not be produced. Like query_to it
|
|
//! will produce an error for a non-null pointer that does not support the requested interface.
|
|
//! @param riid The interface to query for.
|
|
//! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null
|
|
//! on failure OR assigned null when the source pointer is null.
|
|
//! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow,
|
|
//! @ref com_agile_ref_nothrow) this method returns an `HRESULT` indicating whether the query was
|
|
//! successful. Copying a null value is considered success. Exception-based and fail-fast based classes
|
|
//! do not return a value (void).
|
|
result copy_to(REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) const
|
|
{
|
|
if (m_ptr)
|
|
{
|
|
// Prefast cannot see through the error policy + query_policy mapping and as a result and as a result fires 6388 and
|
|
// 28196 for this function. Suppression is also not working. Wrapping this entire function in #pragma warning(disable:
|
|
// 6388 28196) does not stop the prefast errors from being emitted.
|
|
#if defined(_PREFAST_)
|
|
*ptrResult = nullptr;
|
|
return err_policy::HResult(E_NOINTERFACE);
|
|
#else
|
|
return err_policy::HResult(query_policy::query(m_ptr, riid, ptrResult));
|
|
#endif
|
|
}
|
|
*ptrResult = nullptr;
|
|
return err_policy::OK();
|
|
}
|
|
//! @}
|
|
|
|
//! @name Try copy helpers
|
|
//! * Attempts to retrieves the requested interface
|
|
//! * Successfully produces null if the queried pointer is already null
|
|
//! * Produce null if the requested interface is unsupported
|
|
//! * bool returns 'false' ONLY when the queried pointer is not null and the requested interface is unsupported
|
|
//!
|
|
//! See @ref page_query for more information.
|
|
//! @{
|
|
|
|
//! Attempt a query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.try_query<IFoo>();`
|
|
//! (null result when interface is unsupported or queried pointer is null).
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! Identical to the corresponding @ref try_query method with the exception that it can be used when the pointer is null.
|
|
//! When used against a null pointer, the returned pointer will always be null and an error will not be produced.
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `com_ptr_t` pointer to the given interface `U`. The returned pointer will be null if the interface was
|
|
//! not supported or the pointer being queried is null. The returned `com_ptr_t` will have the same error
|
|
//! handling policy (exceptions, failfast or error codes) as the pointer being queried.
|
|
template <class U>
|
|
WI_NODISCARD inline com_ptr_t<U, err_policy> try_copy() const
|
|
{
|
|
return com_ptr_t<U, err_policy>(m_ptr, details::tag_try_com_copy());
|
|
}
|
|
|
|
//! Attempts to query for the interface matching the given output parameter; returns a bool indicating if the query was
|
|
//! successful (returns `false` if the pointer is null).
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! Identical to the corresponding @ref try_query_to method with the exception that it can be used when the pointer is null.
|
|
//! When used against a null pointer, the returned pointer will be null and the return value will be `false`.
|
|
//! @param ptrResult The pointer to query for. The interface to query is deduced from the type of this out parameter; do
|
|
//! not specify the type directly to the template.
|
|
//! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null).
|
|
template <class U>
|
|
_Success_return_ bool try_copy_to(_COM_Outptr_result_maybenull_ U** ptrResult) const
|
|
{
|
|
if (m_ptr)
|
|
{
|
|
return SUCCEEDED(query_policy::query(m_ptr, ptrResult));
|
|
}
|
|
*ptrResult = nullptr;
|
|
return false;
|
|
}
|
|
|
|
//! Attempts a query for the requested interface using the iid, ppv pattern: `ptr.try_query_to(riid, ptr);` (returns `false`
|
|
//! if the pointer is null)
|
|
//! See @ref page_query for more information.
|
|
//!
|
|
//! Identical to the corresponding @ref try_query_to method with the exception that it can be used when the pointer is null.
|
|
//! When used against a null pointer, the returned pointer will be null and the return value will be `false`.
|
|
//! @param riid The interface to query for.
|
|
//! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null
|
|
//! on failure or if the source pointer being queried is null.
|
|
//! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null). Querying
|
|
//! a null pointer will return `false` with a null result.
|
|
_Success_return_ bool try_copy_to(REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) const
|
|
{
|
|
if (m_ptr)
|
|
{
|
|
return SUCCEEDED(query_policy::query(m_ptr, riid, ptrResult));
|
|
}
|
|
*ptrResult = nullptr;
|
|
return false;
|
|
}
|
|
//! @}
|
|
|
|
//! @name WRL compatibility
|
|
//! @{
|
|
|
|
//! Copy construct from a compatible WRL ComPtr<T>.
|
|
template <class U, class = wistd::enable_if_t<__is_convertible_to(U*, pointer)>>
|
|
com_ptr_t(const Microsoft::WRL::ComPtr<U>& other) WI_NOEXCEPT : com_ptr_t(static_cast<pointer>(other.Get()))
|
|
{
|
|
}
|
|
|
|
//! Move construct from a compatible WRL ComPtr<T>.
|
|
template <class U, class = wistd::enable_if_t<__is_convertible_to(U*, pointer)>>
|
|
com_ptr_t(Microsoft::WRL::ComPtr<U>&& other) WI_NOEXCEPT : m_ptr(other.Detach())
|
|
{
|
|
}
|
|
|
|
//! Assign from a compatible WRL ComPtr<T>.
|
|
template <class U, class = wistd::enable_if_t<__is_convertible_to(U*, pointer)>>
|
|
com_ptr_t& operator=(const Microsoft::WRL::ComPtr<U>& other) WI_NOEXCEPT
|
|
{
|
|
return operator=(static_cast<pointer>(other.Get()));
|
|
}
|
|
|
|
//! Move assign from a compatible WRL ComPtr<T>.
|
|
template <class U, class = wistd::enable_if_t<__is_convertible_to(U*, pointer)>>
|
|
com_ptr_t& operator=(Microsoft::WRL::ComPtr<U>&& other) WI_NOEXCEPT
|
|
{
|
|
attach(other.Detach());
|
|
return *this;
|
|
}
|
|
|
|
//! Swap pointers with a WRL ComPtr<T> to the same interface.
|
|
void swap(Microsoft::WRL::ComPtr<T>& other) WI_NOEXCEPT
|
|
{
|
|
auto ptr = m_ptr;
|
|
m_ptr = other.Detach();
|
|
other.Attach(ptr);
|
|
}
|
|
|
|
//! Swap pointers with a rvalue reference to a WRL ComPtr<T> to the same interface.
|
|
void swap(Microsoft::WRL::ComPtr<T>&& other) WI_NOEXCEPT
|
|
{
|
|
swap(other);
|
|
}
|
|
//! @} // WRL compatibility
|
|
|
|
public:
|
|
// Internal Helpers
|
|
/// @cond
|
|
template <class U>
|
|
inline com_ptr_t(_In_ U* ptr, details::tag_com_query) : m_ptr(nullptr)
|
|
{
|
|
err_policy::HResult(details::query_policy_t<U>::query(ptr, &m_ptr));
|
|
}
|
|
|
|
template <class U>
|
|
inline com_ptr_t(_In_ U* ptr, details::tag_try_com_query) WI_NOEXCEPT : m_ptr(nullptr)
|
|
{
|
|
details::query_policy_t<U>::query(ptr, &m_ptr);
|
|
}
|
|
|
|
template <class U>
|
|
inline com_ptr_t(_In_opt_ U* ptr, details::tag_com_copy) : m_ptr(nullptr)
|
|
{
|
|
if (ptr)
|
|
{
|
|
err_policy::HResult(details::query_policy_t<U>::query(ptr, &m_ptr));
|
|
}
|
|
}
|
|
|
|
template <class U>
|
|
inline com_ptr_t(_In_opt_ U* ptr, details::tag_try_com_copy) WI_NOEXCEPT : m_ptr(nullptr)
|
|
{
|
|
if (ptr)
|
|
{
|
|
details::query_policy_t<U>::query(ptr, &m_ptr);
|
|
}
|
|
}
|
|
/// @endcond
|
|
|
|
private:
|
|
pointer m_ptr;
|
|
};
|
|
|
|
// Error-policy driven forms of com_ptr
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! COM pointer, errors throw exceptions (see @ref com_ptr_t for details)
|
|
template <typename T>
|
|
using com_ptr = com_ptr_t<T, err_exception_policy>;
|
|
#endif
|
|
|
|
//! COM pointer, errors return error codes (see @ref com_ptr_t for details)
|
|
template <typename T>
|
|
using com_ptr_nothrow = com_ptr_t<T, err_returncode_policy>;
|
|
|
|
//! COM pointer, errors fail-fast (see @ref com_ptr_t for details)
|
|
template <typename T>
|
|
using com_ptr_failfast = com_ptr_t<T, err_failfast_policy>;
|
|
|
|
// Global operators / swap
|
|
|
|
//! Swaps the given com pointers that have different error handling.
|
|
//! Note that there are also corresponding versions to allow you to swap any wil com_ptr<T> with a WRL ComPtr<T>.
|
|
template <typename T, typename ErrLeft, typename ErrRight>
|
|
inline void swap(com_ptr_t<T, ErrLeft>& left, com_ptr_t<T, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
left.swap(right);
|
|
}
|
|
|
|
//! Swaps the given com pointers that have the same error handling.
|
|
template <typename T, typename Err>
|
|
inline void swap(com_ptr_t<T, Err>& left, com_ptr_t<T, Err>& right) WI_NOEXCEPT
|
|
{
|
|
left.swap(right);
|
|
}
|
|
|
|
//! Compare two com pointers.
|
|
//! Compares the two raw com pointers for equivalence. Does NOT compare object identity with a QI for IUnknown.
|
|
//!
|
|
//! Note that documentation for all of the various comparators has not been generated to reduce global function
|
|
//! clutter, but ALL standard comparison operators are supported between wil com_ptr<T> objects, nullptr_t, and
|
|
//! WRL ComPtr<T>.
|
|
template <typename TLeft, typename ErrLeft, typename TRight, typename ErrRight>
|
|
inline bool operator==(const com_ptr_t<TLeft, ErrLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
static_assert(
|
|
__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*),
|
|
"comparison operator requires left and right pointers to be compatible");
|
|
return (left.get() == right.get());
|
|
}
|
|
|
|
// We don't document all of the global comparison operators (reduce clutter)
|
|
/// @cond
|
|
template <typename TLeft, typename ErrLeft, typename TRight, typename ErrRight>
|
|
inline bool operator<(const com_ptr_t<TLeft, ErrLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
static_assert(
|
|
__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*),
|
|
"comparison operator requires left and right pointers to be compatible");
|
|
return (left.get() < right.get());
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft>
|
|
inline bool operator==(const com_ptr_t<TLeft, ErrLeft>& left, wistd::nullptr_t) WI_NOEXCEPT
|
|
{
|
|
return (left.get() == nullptr);
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight, typename ErrRight>
|
|
inline bool operator!=(const com_ptr_t<TLeft, ErrLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(left == right));
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight, typename ErrRight>
|
|
inline bool operator>=(const com_ptr_t<TLeft, ErrLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(left < right));
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight, typename ErrRight>
|
|
inline bool operator>(const com_ptr_t<TLeft, ErrLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (right < left);
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight, typename ErrRight>
|
|
inline bool operator<=(const com_ptr_t<TLeft, ErrLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(right < left));
|
|
}
|
|
|
|
template <typename TRight, typename ErrRight>
|
|
inline bool operator==(wistd::nullptr_t, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (right.get() == nullptr);
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft>
|
|
inline bool operator!=(const com_ptr_t<TLeft, ErrLeft>& left, wistd::nullptr_t) WI_NOEXCEPT
|
|
{
|
|
return (!(left == nullptr));
|
|
}
|
|
|
|
template <typename TRight, typename ErrRight>
|
|
inline bool operator!=(wistd::nullptr_t, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(right == nullptr));
|
|
}
|
|
|
|
// WRL ComPtr support
|
|
|
|
template <typename T, typename ErrLeft>
|
|
inline void swap(com_ptr_t<T, ErrLeft>& left, Microsoft::WRL::ComPtr<T>& right) WI_NOEXCEPT
|
|
{
|
|
left.swap(right);
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator==(const com_ptr_t<TLeft, ErrLeft>& left, const Microsoft::WRL::ComPtr<TRight>& right) WI_NOEXCEPT
|
|
{
|
|
static_assert(
|
|
__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*),
|
|
"comparison operator requires left and right pointers to be compatible");
|
|
return (left.get() == right.Get());
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator<(const com_ptr_t<TLeft, ErrLeft>& left, const Microsoft::WRL::ComPtr<TRight>& right) WI_NOEXCEPT
|
|
{
|
|
static_assert(
|
|
__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*),
|
|
"comparison operator requires left and right pointers to be compatible");
|
|
return (left.get() < right.Get());
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator!=(const com_ptr_t<TLeft, ErrLeft>& left, const Microsoft::WRL::ComPtr<TRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(left == right));
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator>=(const com_ptr_t<TLeft, ErrLeft>& left, const Microsoft::WRL::ComPtr<TRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(left < right));
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator>(const com_ptr_t<TLeft, ErrLeft>& left, const Microsoft::WRL::ComPtr<TRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (right < left);
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator<=(const com_ptr_t<TLeft, ErrLeft>& left, const Microsoft::WRL::ComPtr<TRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(right < left));
|
|
}
|
|
|
|
template <typename T, typename ErrRight>
|
|
inline void swap(Microsoft::WRL::ComPtr<T>& left, com_ptr_t<T, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
right.swap(left);
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator==(const Microsoft::WRL::ComPtr<TLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
static_assert(
|
|
__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*),
|
|
"comparison operator requires left and right pointers to be compatible");
|
|
return (left.Get() == right.get());
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator<(const Microsoft::WRL::ComPtr<TLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
static_assert(
|
|
__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*),
|
|
"comparison operator requires left and right pointers to be compatible");
|
|
return (left.Get() < right.get());
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator!=(const Microsoft::WRL::ComPtr<TLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(left == right));
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator>=(const Microsoft::WRL::ComPtr<TLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(left < right));
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator>(const Microsoft::WRL::ComPtr<TLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (right < left);
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator<=(const Microsoft::WRL::ComPtr<TLeft>& left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(right < left));
|
|
}
|
|
|
|
// raw COM pointer support
|
|
//
|
|
// Use these for convenience and to avoid unnecessary AddRef/Release cyles when using raw
|
|
// pointers to access STL containers. Specify std::less<> to benefit from operator<.
|
|
//
|
|
// Example: std::set<wil::com_ptr<IUnknown>, std::less<>> set;
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator==(const com_ptr_t<TLeft, ErrLeft>& left, TRight* right) WI_NOEXCEPT
|
|
{
|
|
static_assert(
|
|
__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*),
|
|
"comparison operator requires left and right pointers to be compatible");
|
|
return (left.get() == right);
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator<(const com_ptr_t<TLeft, ErrLeft>& left, TRight* right) WI_NOEXCEPT
|
|
{
|
|
static_assert(
|
|
__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*),
|
|
"comparison operator requires left and right pointers to be compatible");
|
|
return (left.get() < right);
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator!=(const com_ptr_t<TLeft, ErrLeft>& left, TRight* right) WI_NOEXCEPT
|
|
{
|
|
return (!(left == right));
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator>=(const com_ptr_t<TLeft, ErrLeft>& left, TRight* right) WI_NOEXCEPT
|
|
{
|
|
return (!(left < right));
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator>(const com_ptr_t<TLeft, ErrLeft>& left, TRight* right) WI_NOEXCEPT
|
|
{
|
|
return (right < left);
|
|
}
|
|
|
|
template <typename TLeft, typename ErrLeft, typename TRight>
|
|
inline bool operator<=(const com_ptr_t<TLeft, ErrLeft>& left, TRight* right) WI_NOEXCEPT
|
|
{
|
|
return (!(right < left));
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator==(TLeft* left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
static_assert(
|
|
__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*),
|
|
"comparison operator requires left and right pointers to be compatible");
|
|
return (left == right.get());
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator<(TLeft* left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
static_assert(
|
|
__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*),
|
|
"comparison operator requires left and right pointers to be compatible");
|
|
return (left < right.get());
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator!=(TLeft* left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(left == right));
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator>=(TLeft* left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(left < right));
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator>(TLeft* left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (right < left);
|
|
}
|
|
|
|
template <typename TLeft, typename TRight, typename ErrRight>
|
|
inline bool operator<=(TLeft* left, const com_ptr_t<TRight, ErrRight>& right) WI_NOEXCEPT
|
|
{
|
|
return (!(right < left));
|
|
}
|
|
|
|
// suppress documentation of every single comparison operator
|
|
/// @endcond
|
|
|
|
//! An overloaded function that retrieves the raw com pointer from a raw pointer, wil::com_ptr_t<T>, WRL ComPtr<T>, or
|
|
//! Platform::Object^. This function is primarily useful by library or helper code. It allows code to be written to accept a
|
|
//! forwarding reference template that can be used as an input com pointer. That input com pointer is allowed to be any of:
|
|
//! * Raw Pointer: `T* com_raw_ptr(T* ptr)`
|
|
//! * Wil com_ptr: `T* com_raw_ptr(const wil::com_ptr_t<T, err>& ptr)`
|
|
//! * WRL ComPtr: `T* com_raw_ptr(const Microsoft::WRL::ComPtr<T>& ptr)`
|
|
//! * C++/CX hat: `IInspectable* com_raw_ptr(Platform::Object^ ptr)`
|
|
//!
|
|
//! Which in turn allows code like the following to be written:
|
|
//! ~~~~
|
|
//! template <typename U, typename T>
|
|
//! void com_query_to(T&& ptrSource, _COM_Outptr_ U** ptrResult)
|
|
//! {
|
|
//! auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
//! // decltype(raw) has the type of the inner pointer and raw is guaranteed to be a raw com pointer
|
|
//! ~~~~
|
|
template <typename T>
|
|
T* com_raw_ptr(T* ptr)
|
|
{
|
|
return ptr;
|
|
}
|
|
|
|
/// @cond
|
|
template <typename T, typename err>
|
|
T* com_raw_ptr(const wil::com_ptr_t<T, err>& ptr)
|
|
{
|
|
return ptr.get();
|
|
}
|
|
|
|
template <typename T>
|
|
T* com_raw_ptr(const Microsoft::WRL::ComPtr<T>& ptr)
|
|
{
|
|
return ptr.Get();
|
|
}
|
|
|
|
// clang-format off
|
|
#ifdef __cplusplus_winrt
|
|
|
|
template <typename T>
|
|
inline IInspectable* com_raw_ptr(T^ ptr)
|
|
{
|
|
return reinterpret_cast<IInspectable*>(static_cast<::Platform::Object^>(ptr));
|
|
}
|
|
|
|
#endif
|
|
// clang-format on
|
|
/// @endcond
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Constructs a `com_ptr` from a raw pointer.
|
|
//! This avoids having to restate the interface in pre-C++20.
|
|
//! Starting in C++20, you can write `wil::com_ptr(p)` directly.
|
|
//! ~~~
|
|
//! void example(ILongNamedThing* thing)
|
|
//! {
|
|
//! callback([thing = wil::make_com_ptr(thing)] { /* do something */ });
|
|
//! }
|
|
//! ~~~
|
|
template <typename T>
|
|
com_ptr<T> make_com_ptr(T* p)
|
|
{
|
|
return p;
|
|
}
|
|
#endif
|
|
|
|
//! Constructs a `com_ptr_nothrow` from a raw pointer.
|
|
//! This avoids having to restate the interface in pre-C++20.
|
|
//! Starting in C++20, you can write `wil::com_ptr_nothrow(p)` directly.
|
|
//! ~~~
|
|
//! void example(ILongNamedThing* thing)
|
|
//! {
|
|
//! callback([thing = wil::make_com_ptr_nothrow(thing)] { /* do something */ });
|
|
//! }
|
|
//! ~~~
|
|
template <typename T>
|
|
com_ptr_nothrow<T> make_com_ptr_nothrow(T* p)
|
|
{
|
|
return p;
|
|
}
|
|
|
|
//! Constructs a `com_ptr_failfast` from a raw pointer.
|
|
//! This avoids having to restate the interface in pre-C++20.
|
|
//! Starting in C++20, you can write `wil::com_ptr_failfast(p)` directly.
|
|
//! ~~~
|
|
//! void example(ILongNamedThing* thing)
|
|
//! {
|
|
//! callback([thing = wil::make_com_ptr_failfast(thing)] { /* do something */ });
|
|
//! }
|
|
//! ~~~
|
|
template <typename T>
|
|
com_ptr_failfast<T> make_com_ptr_failfast(T* p)
|
|
{
|
|
return p;
|
|
}
|
|
|
|
//! @name Stand-alone query helpers
|
|
//! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr
|
|
//! * Retrieves the requested interface
|
|
//! * AV if the source pointer is null
|
|
//! * Produce an error if the requested interface is unsupported
|
|
//!
|
|
//! See @ref page_query for more information
|
|
//! @{
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Queries for the specified interface and returns an exception-based wil::com_ptr to that interface (exception if unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `wil::com_ptr<U>` pointer to the given interface `U`. The returned pointer is guaranteed not null.
|
|
template <typename U, typename T>
|
|
inline com_ptr<U> com_query(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return com_ptr<U>(raw, details::tag_com_query());
|
|
}
|
|
#endif
|
|
|
|
//! Queries for the specified interface and returns a fail-fast-based wil::com_ptr_failfast to that interface (fail-fast if
|
|
//! unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `wil::com_ptr<U>` pointer to the given interface `U`. The returned pointer is guaranteed not null.
|
|
template <typename U, typename T>
|
|
inline com_ptr_failfast<U> com_query_failfast(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return com_ptr_failfast<U>(raw, details::tag_com_query());
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Queries for the interface specified by the type of the output parameter (throws an exception if unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null.
|
|
template <typename U, typename T>
|
|
_Success_true_ void com_query_to(T&& ptrSource, _COM_Outptr_ U** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
THROW_IF_FAILED(details::query_policy_t<decltype(raw)>::query(raw, ptrResult));
|
|
__analysis_assume(*ptrResult != nullptr);
|
|
}
|
|
#endif
|
|
|
|
//! Queries for the interface specified by the type of the output parameter (fail-fast if unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null.
|
|
template <typename U, typename T>
|
|
_Success_true_ void com_query_to_failfast(T&& ptrSource, _COM_Outptr_ U** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
FAIL_FAST_IF_FAILED(details::query_policy_t<decltype(raw)>::query(raw, ptrResult));
|
|
__analysis_assume(*ptrResult != nullptr);
|
|
}
|
|
|
|
//! Queries for the interface specified by the type of the output parameter (returns an error if unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure.
|
|
//! @return Returns an HRESULT representing whether the query succeeded.
|
|
template <typename U, typename T>
|
|
HRESULT com_query_to_nothrow(T&& ptrSource, _COM_Outptr_ U** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
auto hr = details::query_policy_t<decltype(raw)>::query(raw, ptrResult);
|
|
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
|
return hr;
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Queries for the interface specified by the given REFIID parameter (throws an exception if unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @param riid The interface to query for
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null.
|
|
template <typename T>
|
|
_Success_true_ void com_query_to(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
THROW_IF_FAILED(details::query_policy_t<decltype(raw)>::query(raw, riid, ptrResult));
|
|
__analysis_assume(*ptrResult != nullptr);
|
|
}
|
|
#endif
|
|
|
|
//! Queries for the interface specified by the given REFIID parameter (fail-fast if unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @param riid The interface to query for
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null.
|
|
template <typename T>
|
|
_Success_true_ void com_query_to_failfast(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
FAIL_FAST_IF_FAILED(details::query_policy_t<decltype(raw)>::query(raw, riid, ptrResult));
|
|
__analysis_assume(*ptrResult != nullptr);
|
|
}
|
|
|
|
//! Queries for the interface specified by the given REFIID parameter (returns an error if unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @param riid The interface to query for
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure.
|
|
template <typename T>
|
|
HRESULT com_query_to_nothrow(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
auto hr = details::query_policy_t<decltype(raw)>::query(raw, riid, ptrResult);
|
|
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
|
return hr;
|
|
}
|
|
//! @}
|
|
|
|
//! @name Stand-alone try query helpers
|
|
//! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr
|
|
//! * Attempts to retrieves the requested interface
|
|
//! * AV if the source pointer is null
|
|
//! * Produce null if the requested interface is unsupported
|
|
//! * bool returns 'true' when query was successful (non-null return result)
|
|
//!
|
|
//! See @ref page_query for more information.
|
|
//! @{
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Attempts a query for the specified interface and returns an exception-based wil::com_ptr to that interface (returns null if
|
|
//! unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `wil::com_ptr<U>` pointer to the given interface `U`. The returned pointer is null if the requested
|
|
//! interface was not supported.
|
|
template <class U, typename T>
|
|
inline com_ptr<U> try_com_query(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return com_ptr<U>(raw, details::tag_try_com_query());
|
|
}
|
|
#endif
|
|
|
|
//! Attempts a query for the specified interface and returns an fail-fast wil::com_ptr_failfast to that interface (returns null if
|
|
//! unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `wil::com_ptr_failfast<U>` pointer to the given interface `U`. The returned pointer is null if the
|
|
//! requested interface was not supported.
|
|
template <class U, typename T>
|
|
inline com_ptr_failfast<U> try_com_query_failfast(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return com_ptr_failfast<U>(raw, details::tag_try_com_query());
|
|
}
|
|
|
|
//! Attempts a query for the specified interface and returns an error-code-based wil::com_ptr_nothrow to that interface (returns
|
|
//! null if unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `wil::com_ptr_nothrow<U>` pointer to the given interface `U`. The returned pointer is null if the
|
|
//! requested interface was not supported.
|
|
template <class U, typename T>
|
|
inline com_ptr_nothrow<U> try_com_query_nothrow(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return com_ptr_nothrow<U>(raw, details::tag_try_com_query());
|
|
}
|
|
|
|
//! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null.
|
|
//! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be
|
|
//! null.
|
|
//! @return A bool value representing whether the query was successful (non-null return result).
|
|
template <typename U, typename T>
|
|
_Success_return_ bool try_com_query_to(T&& ptrSource, _COM_Outptr_ U** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return (SUCCEEDED(details::query_policy_t<decltype(raw)>::query(raw, ptrResult)));
|
|
}
|
|
|
|
//! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null.
|
|
//! @param riid The interface to query for
|
|
//! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be
|
|
//! null.
|
|
//! @return A bool value representing whether the query was successful (non-null return result).
|
|
template <typename T>
|
|
_Success_return_ bool try_com_query_to(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return (SUCCEEDED(details::query_policy_t<decltype(raw)>::query(raw, riid, ptrResult)));
|
|
}
|
|
//! @}
|
|
|
|
//! @name Stand-alone copy helpers
|
|
//! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr
|
|
//! * Retrieves the requested interface
|
|
//! * Succeeds with null if the source pointer is null
|
|
//! * Produce an error if the requested interface is unsupported
|
|
//!
|
|
//! See @ref page_query for more information
|
|
//! @{
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Queries for the specified interface and returns an exception-based wil::com_ptr to that interface (exception if unsupported,
|
|
//! preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `wil::com_ptr<U>` pointer to the given interface `U`. The returned pointer will be null only if the
|
|
//! source is null.
|
|
template <class U, typename T>
|
|
inline com_ptr<U> com_copy(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return com_ptr<U>(raw, details::tag_com_copy());
|
|
}
|
|
#endif
|
|
|
|
//! Queries for the specified interface and returns a fail-fast-based wil::com_ptr_failfast to that interface (fail-fast if
|
|
//! unsupported, preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `wil::com_ptr<U>` pointer to the given interface `U`. The returned pointer will be null only if the
|
|
//! source is null.
|
|
template <class U, typename T>
|
|
inline com_ptr_failfast<U> com_copy_failfast(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return com_ptr_failfast<U>(raw, details::tag_com_copy());
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Queries for the interface specified by the type of the output parameter (throws an exception if unsupported, preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null.
|
|
template <typename U, typename T>
|
|
_Success_true_ void com_copy_to(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
if (raw)
|
|
{
|
|
THROW_IF_FAILED(details::query_policy_t<decltype(raw)>::query(raw, ptrResult));
|
|
return;
|
|
}
|
|
*ptrResult = nullptr;
|
|
}
|
|
#endif
|
|
|
|
//! Queries for the interface specified by the type of the output parameter (fail-fast if unsupported, preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null.
|
|
template <typename U, typename T>
|
|
_Success_true_ void com_copy_to_failfast(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
if (raw)
|
|
{
|
|
FAIL_FAST_IF_FAILED(details::query_policy_t<decltype(raw)>::query(raw, ptrResult));
|
|
return;
|
|
}
|
|
*ptrResult = nullptr;
|
|
}
|
|
|
|
//! Queries for the interface specified by the type of the output parameter (returns an error if unsupported, preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure or if the
|
|
//! source is null.
|
|
//! @return Returns an HRESULT representing whether the query succeeded (returns S_OK if the source is null).
|
|
template <typename U, typename T>
|
|
HRESULT com_copy_to_nothrow(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
if (raw)
|
|
{
|
|
RETURN_HR(details::query_policy_t<decltype(raw)>::query(raw, ptrResult));
|
|
}
|
|
*ptrResult = nullptr;
|
|
return S_OK;
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Queries for the interface specified by the given REFIID parameter (throws an exception if unsupported, preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @param riid The interface to query for
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null.
|
|
template <typename T>
|
|
_Success_true_ void com_copy_to(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
if (raw)
|
|
{
|
|
THROW_IF_FAILED(details::query_policy_t<decltype(raw)>::query(raw, riid, ptrResult));
|
|
return;
|
|
}
|
|
*ptrResult = nullptr;
|
|
}
|
|
#endif
|
|
|
|
//! Queries for the interface specified by the given REFIID parameter (fail-fast if unsupported, preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @param riid The interface to query for
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null.
|
|
template <typename T>
|
|
_Success_true_ void com_copy_to_failfast(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
if (raw)
|
|
{
|
|
FAIL_FAST_IF_FAILED(details::query_policy_t<decltype(raw)>::query(raw, riid, ptrResult));
|
|
return;
|
|
}
|
|
*ptrResult = nullptr;
|
|
}
|
|
|
|
//! Queries for the interface specified by the given REFIID parameter (returns an error if unsupported, preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @param riid The interface to query for
|
|
//! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure or if the
|
|
//! source is null.
|
|
//! @return Returns an HRESULT representing whether the query succeeded (returns S_OK if the source is null).
|
|
template <typename T>
|
|
HRESULT com_copy_to_nothrow(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
if (raw)
|
|
{
|
|
RETURN_HR(details::query_policy_t<decltype(raw)>::query(raw, riid, ptrResult));
|
|
}
|
|
*ptrResult = nullptr;
|
|
return S_OK;
|
|
}
|
|
//! @}
|
|
|
|
//! @name Stand-alone try copy helpers
|
|
//! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr
|
|
//! * Attempts to retrieves the requested interface
|
|
//! * Succeeds with null if the source pointer is null
|
|
//! * Produce null if the requested interface is unsupported
|
|
//! * bool returns 'true' when query was successful (non-null return result)
|
|
//!
|
|
//! See @ref page_query for more information.
|
|
//! @{
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Attempts a query for the specified interface and returns an exception-based wil::com_ptr to that interface (returns null if
|
|
//! unsupported, preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `wil::com_ptr<U>` pointer to the given interface `U`. The returned pointer is null if the requested
|
|
//! interface was not supported.
|
|
template <class U, typename T>
|
|
inline com_ptr<U> try_com_copy(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return com_ptr<U>(raw, details::tag_try_com_copy());
|
|
}
|
|
#endif
|
|
|
|
//! Attempts a query for the specified interface and returns an fail-fast wil::com_ptr_failfast to that interface (returns null if
|
|
//! unsupported, preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `wil::com_ptr_failfast<U>` pointer to the given interface `U`. The returned pointer is null if the
|
|
//! requested interface was not supported.
|
|
template <class U, typename T>
|
|
inline com_ptr_failfast<U> try_com_copy_failfast(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return com_ptr_failfast<U>(raw, details::tag_try_com_copy());
|
|
}
|
|
|
|
//! Attempts a query for the specified interface and returns an error-code-based wil::com_ptr_nothrow to that interface (returns
|
|
//! null if unsupported, preserves null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null
|
|
//! @tparam U Represents the interface being queried
|
|
//! @return A `wil::com_ptr_nothrow<U>` pointer to the given interface `U`. The returned pointer is null if the
|
|
//! requested interface was not supported.
|
|
template <class U, typename T>
|
|
inline com_ptr_nothrow<U> try_com_copy_nothrow(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
return com_ptr_nothrow<U>(raw, details::tag_try_com_copy());
|
|
}
|
|
|
|
//! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported, preserves
|
|
//! null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null.
|
|
//! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be
|
|
//! null.
|
|
//! @return A bool value representing whether the query was successful (non-null return result).
|
|
template <typename U, typename T>
|
|
_Success_return_ bool try_com_copy_to(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
if (raw)
|
|
{
|
|
return SUCCEEDED(details::query_policy_t<decltype(raw)>::query(raw, ptrResult));
|
|
}
|
|
*ptrResult = nullptr;
|
|
return false;
|
|
}
|
|
|
|
//! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported, preserves
|
|
//! null).
|
|
//! See @ref page_query for more information.
|
|
//! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null.
|
|
//! @param riid The interface to query for
|
|
//! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be
|
|
//! null.
|
|
//! @return A bool value representing whether the query was successful (non-null return result).
|
|
template <typename T>
|
|
_Success_return_ bool try_com_copy_to(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
if (raw)
|
|
{
|
|
return SUCCEEDED(details::query_policy_t<decltype(raw)>::query(raw, riid, ptrResult));
|
|
}
|
|
*ptrResult = nullptr;
|
|
return false;
|
|
}
|
|
//! @}
|
|
|
|
// clang-format off
|
|
#ifdef __cplusplus_winrt
|
|
//! @name Stand-alone helpers to query for CX ref ("hat") types from ABI COM types.
|
|
//! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr
|
|
//! * Retrieves the requested C++/CX interface or ref class.
|
|
//! * Preserves null if the source pointer is null
|
|
//! * Produce an error if the requested interface is unsupported
|
|
//!
|
|
//! See @ref page_query for more information
|
|
//! @{
|
|
|
|
template <typename T>
|
|
::Platform::Object^ cx_object_from_abi(T&& ptr) WI_NOEXCEPT
|
|
{
|
|
IInspectable* const inspectable = com_raw_ptr(wistd::forward<T>(ptr));
|
|
return reinterpret_cast<::Platform::Object^>(inspectable);
|
|
}
|
|
|
|
template <typename U, typename T>
|
|
inline U^ cx_safe_cast(T&& ptrSource)
|
|
{
|
|
return safe_cast<U^>(cx_object_from_abi(wistd::forward<T>(ptrSource)));
|
|
}
|
|
|
|
template <typename U, typename T>
|
|
inline U^ cx_dynamic_cast(T&& ptrSource) WI_NOEXCEPT
|
|
{
|
|
return dynamic_cast<U^>(cx_object_from_abi(wistd::forward<T>(ptrSource)));
|
|
}
|
|
//! @}
|
|
#endif
|
|
// clang-format on
|
|
|
|
//*****************************************************************************
|
|
// Agile References
|
|
//*****************************************************************************
|
|
|
|
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Agile reference to a COM interface, errors throw exceptions (see @ref com_ptr_t and @ref com_agile_query for details)
|
|
using com_agile_ref = com_ptr<IAgileReference>;
|
|
#endif
|
|
//! Agile reference to a COM interface, errors return error codes (see @ref com_ptr_t and @ref com_agile_query_nothrow for
|
|
//! details)
|
|
using com_agile_ref_nothrow = com_ptr_nothrow<IAgileReference>;
|
|
//! Agile reference to a COM interface, errors fail fast (see @ref com_ptr_t and @ref com_agile_query_failfast for details)
|
|
using com_agile_ref_failfast = com_ptr_failfast<IAgileReference>;
|
|
|
|
//! @name Create agile reference helpers
|
|
//! * Attempts to retrieve an agile reference to the requested interface (see
|
|
//! [RoGetAgileReference](https://msdn.microsoft.com/en-us/library/dn269839.aspx))
|
|
//! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr
|
|
//! * `query` methods AV if the source pointer is null
|
|
//! * `copy` methods succeed with null if the source pointer is null
|
|
//! * Accept optional [AgileReferenceOptions](https://msdn.microsoft.com/en-us/library/dn269836.aspx)
|
|
//!
|
|
//! See @ref page_query for more information on resolving an agile ref
|
|
//! @{
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! return a com_agile_ref representing the given source pointer (throws an exception on failure)
|
|
template <typename T>
|
|
com_agile_ref com_agile_query(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
com_agile_ref agileRef;
|
|
THROW_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef));
|
|
return agileRef;
|
|
}
|
|
#endif
|
|
|
|
//! return a com_agile_ref_failfast representing the given source pointer (fail-fast on failure)
|
|
template <typename T>
|
|
com_agile_ref_failfast com_agile_query_failfast(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
com_agile_ref_failfast agileRef;
|
|
FAIL_FAST_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef));
|
|
return agileRef;
|
|
}
|
|
|
|
//! return a com_agile_ref_nothrow representing the given source pointer (returns an HRESULT on failure)
|
|
template <typename T>
|
|
HRESULT com_agile_query_nothrow(T&& ptrSource, _COM_Outptr_ IAgileReference** ptrResult, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
auto hr = ::RoGetAgileReference(options, __uuidof(raw), raw, ptrResult);
|
|
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
|
return hr;
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! return a com_agile_ref representing the given source pointer (throws an exception on failure, source maybe null)
|
|
template <typename T>
|
|
com_agile_ref com_agile_copy(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
com_agile_ref agileRef;
|
|
if (raw)
|
|
{
|
|
THROW_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef));
|
|
}
|
|
return agileRef;
|
|
}
|
|
#endif
|
|
|
|
//! return a com_agile_ref_failfast representing the given source pointer (fail-fast on failure, source maybe null)
|
|
template <typename T>
|
|
com_agile_ref_failfast com_agile_copy_failfast(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
com_agile_ref_failfast agileRef;
|
|
if (raw)
|
|
{
|
|
FAIL_FAST_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef));
|
|
}
|
|
return agileRef;
|
|
}
|
|
|
|
//! return an agile ref (com_agile_ref_XXX or other representation) representing the given source pointer (return error on
|
|
//! failure, source maybe null)
|
|
template <typename T>
|
|
HRESULT com_agile_copy_nothrow(T&& ptrSource, _COM_Outptr_result_maybenull_ IAgileReference** ptrResult, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
if (raw)
|
|
{
|
|
RETURN_HR(::RoGetAgileReference(options, __uuidof(raw), raw, ptrResult));
|
|
}
|
|
*ptrResult = nullptr;
|
|
return S_OK;
|
|
}
|
|
//! @}
|
|
#endif
|
|
|
|
//*****************************************************************************
|
|
// Weak References
|
|
//*****************************************************************************
|
|
|
|
namespace details
|
|
{
|
|
template <typename T>
|
|
HRESULT GetWeakReference(T* ptr, _COM_Outptr_ IWeakReference** weakReference)
|
|
{
|
|
static_assert(!wistd::is_same<IWeakReference, T>::value, "Cannot get an IWeakReference to an IWeakReference");
|
|
|
|
*weakReference = nullptr;
|
|
com_ptr_nothrow<IWeakReferenceSource> source;
|
|
HRESULT hr = ptr->QueryInterface(IID_PPV_ARGS(&source));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = source->GetWeakReference(weakReference);
|
|
}
|
|
return hr;
|
|
}
|
|
} // namespace details
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! Weak reference to a COM interface, errors throw exceptions (see @ref com_ptr_t and @ref com_weak_query for details)
|
|
using com_weak_ref = com_ptr<IWeakReference>;
|
|
#endif
|
|
//! Weak reference to a COM interface, errors return error codes (see @ref com_ptr_t and @ref com_weak_query_nothrow for details)
|
|
using com_weak_ref_nothrow = com_ptr_nothrow<IWeakReference>;
|
|
//! Weak reference to a COM interface, errors fail fast (see @ref com_ptr_t and @ref com_weak_query_failfast for details)
|
|
using com_weak_ref_failfast = com_ptr_failfast<IWeakReference>;
|
|
|
|
//! @name Create weak reference helpers
|
|
//! * Attempts to retrieve a weak reference to the requested interface (see WRL's similar
|
|
//! [WeakRef](https://msdn.microsoft.com/en-us/library/br244853.aspx))
|
|
//! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr
|
|
//! * `query` methods AV if the source pointer is null
|
|
//! * `copy` methods succeed with null if the source pointer is null
|
|
//!
|
|
//! See @ref page_query for more information on resolving a weak ref
|
|
//! @{
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! return a com_weak_ref representing the given source pointer (throws an exception on failure)
|
|
template <typename T>
|
|
com_weak_ref com_weak_query(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
com_weak_ref weakRef;
|
|
THROW_IF_FAILED(details::GetWeakReference(raw, &weakRef));
|
|
return weakRef;
|
|
}
|
|
#endif
|
|
|
|
//! return a com_weak_ref_failfast representing the given source pointer (fail-fast on failure)
|
|
template <typename T>
|
|
com_weak_ref_failfast com_weak_query_failfast(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
com_weak_ref_failfast weakRef;
|
|
FAIL_FAST_IF_FAILED(details::GetWeakReference(raw, &weakRef));
|
|
return weakRef;
|
|
}
|
|
|
|
//! return a com_weak_ref_nothrow representing the given source pointer (returns an HRESULT on failure)
|
|
template <typename T>
|
|
HRESULT com_weak_query_nothrow(T&& ptrSource, _COM_Outptr_ IWeakReference** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
auto hr = details::GetWeakReference(raw, ptrResult);
|
|
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
|
return hr;
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
//! return a com_weak_ref representing the given source pointer (throws an exception on failure, source maybe null)
|
|
template <typename T>
|
|
com_weak_ref com_weak_copy(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
com_weak_ref weakRef;
|
|
if (raw)
|
|
{
|
|
THROW_IF_FAILED(details::GetWeakReference(raw, &weakRef));
|
|
}
|
|
return weakRef;
|
|
}
|
|
#endif
|
|
|
|
//! return a com_weak_ref_failfast representing the given source pointer (fail-fast on failure, source maybe null)
|
|
template <typename T>
|
|
com_weak_ref_failfast com_weak_copy_failfast(T&& ptrSource)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
com_weak_ref_failfast weakRef;
|
|
if (raw)
|
|
{
|
|
FAIL_FAST_IF_FAILED(details::GetWeakReference(raw, &weakRef));
|
|
}
|
|
return weakRef;
|
|
}
|
|
|
|
//! return a com_weak_ref_failfast representing the given source pointer (fail-fast on failure, source maybe null)
|
|
template <typename T>
|
|
HRESULT com_weak_copy_nothrow(T&& ptrSource, _COM_Outptr_result_maybenull_ IWeakReference** ptrResult)
|
|
{
|
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
|
if (raw)
|
|
{
|
|
RETURN_HR(details::GetWeakReference(raw, ptrResult));
|
|
}
|
|
*ptrResult = nullptr;
|
|
return S_OK;
|
|
}
|
|
|
|
//! @}
|
|
|
|
#pragma region COM Object Helpers
|
|
|
|
template <typename T>
|
|
inline bool is_agile(T&& ptrSource)
|
|
{
|
|
wil::com_ptr_nothrow<IAgileObject> agileObject;
|
|
return SUCCEEDED(com_raw_ptr(wistd::forward<T>(ptrSource))->QueryInterface(IID_PPV_ARGS(&agileObject)));
|
|
}
|
|
|
|
/** constructs a COM object using an CLSID on a specific interface or IUnknown.*/
|
|
template <typename Interface = IUnknown, typename error_policy = err_exception_policy>
|
|
wil::com_ptr_t<Interface, error_policy> CoCreateInstance(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
|
{
|
|
wil::com_ptr_t<Interface, error_policy> result;
|
|
error_policy::HResult(::CoCreateInstance(rclsid, nullptr, dwClsContext, IID_PPV_ARGS(&result)));
|
|
return result;
|
|
}
|
|
|
|
/** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or
|
|
IUnknown. */
|
|
template <typename Class, typename Interface = IUnknown, typename error_policy = err_exception_policy>
|
|
wil::com_ptr_t<Interface, error_policy> CoCreateInstance(DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
|
{
|
|
return CoCreateInstance<Interface, error_policy>(__uuidof(Class), dwClsContext);
|
|
}
|
|
|
|
/** constructs a COM object using an CLSID on a specific interface or IUnknown. */
|
|
template <typename Interface = IUnknown>
|
|
wil::com_ptr_failfast<Interface> CoCreateInstanceFailFast(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
|
|
{
|
|
return CoCreateInstance<Interface, err_failfast_policy>(rclsid, dwClsContext);
|
|
}
|
|
|
|
/** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or
|
|
IUnknown. */
|
|
template <typename Class, typename Interface = IUnknown>
|
|
wil::com_ptr_failfast<Interface> CoCreateInstanceFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
|
|
{
|
|
return CoCreateInstanceFailFast<Interface>(__uuidof(Class), dwClsContext);
|
|
}
|
|
|
|
/** constructs a COM object using an CLSID on a specific interface or IUnknown.
|
|
Note, failures are reported as a null result, the HRESULT is lost. */
|
|
template <typename Interface = IUnknown>
|
|
wil::com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
|
|
{
|
|
return CoCreateInstance<Interface, err_returncode_policy>(rclsid, dwClsContext);
|
|
}
|
|
|
|
/** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or
|
|
IUnknown. Note, failures are reported as a null result, the HRESULT is lost. */
|
|
template <typename Class, typename Interface = IUnknown>
|
|
wil::com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
|
|
{
|
|
return CoCreateInstanceNoThrow<Interface>(__uuidof(Class), dwClsContext);
|
|
}
|
|
|
|
/** constructs a COM object class factory using an CLSID on IClassFactory or a specific interface. */
|
|
template <typename Interface = IClassFactory, typename error_policy = err_exception_policy>
|
|
wil::com_ptr_t<Interface, error_policy> CoGetClassObject(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
|
{
|
|
wil::com_ptr_t<Interface, error_policy> result;
|
|
error_policy::HResult(CoGetClassObject(rclsid, dwClsContext, nullptr, IID_PPV_ARGS(&result)));
|
|
return result;
|
|
}
|
|
|
|
/** constructs a COM object class factory using the class as the identifier (that has an associated CLSID)
|
|
on IClassFactory or a specific interface. */
|
|
template <typename Class, typename Interface = IClassFactory, typename error_policy = err_exception_policy>
|
|
wil::com_ptr_t<Interface, error_policy> CoGetClassObject(DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
|
{
|
|
return CoGetClassObject<Interface, error_policy>(__uuidof(Class), dwClsContext);
|
|
}
|
|
|
|
/** constructs a COM object class factory using an CLSID on IClassFactory or a specific interface. */
|
|
template <typename Interface = IClassFactory>
|
|
wil::com_ptr_failfast<Interface> CoGetClassObjectFailFast(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
|
{
|
|
return CoGetClassObject<Interface, err_failfast_policy>(rclsid, dwClsContext);
|
|
}
|
|
|
|
/** constructs a COM object class factory using the class as the identifier (that has an associated CLSID)
|
|
on IClassFactory or a specific interface. */
|
|
template <typename Class, typename Interface = IClassFactory>
|
|
wil::com_ptr_failfast<Interface> CoGetClassObjectFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
|
{
|
|
return CoGetClassObjectFailFast<Interface>(__uuidof(Class), dwClsContext);
|
|
}
|
|
|
|
/** constructs a COM object class factory using an CLSID on IClassFactory or a specific interface.
|
|
Note, failures are reported as a null result, the HRESULT is lost. */
|
|
template <typename Interface = IClassFactory>
|
|
wil::com_ptr_nothrow<Interface> CoGetClassObjectNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
|
{
|
|
return CoGetClassObject<Interface, err_returncode_policy>(rclsid, dwClsContext);
|
|
}
|
|
|
|
/** constructs a COM object class factory using the class as the identifier (that has an associated CLSID)
|
|
on IClassFactory or a specific interface.
|
|
Note, failures are reported as a null result, the HRESULT is lost. */
|
|
template <typename Class, typename Interface = IClassFactory>
|
|
wil::com_ptr_nothrow<Interface> CoGetClassObjectNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
|
{
|
|
return CoGetClassObjectNoThrow<Interface>(__uuidof(Class), dwClsContext);
|
|
}
|
|
|
|
#if __cpp_lib_apply && __has_include(<type_traits>)
|
|
/// @cond
|
|
namespace details
|
|
{
|
|
template <typename error_policy, typename... Results>
|
|
auto CoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx) noexcept
|
|
{
|
|
MULTI_QI multiQis[sizeof...(Results)]{};
|
|
const IID* iids[sizeof...(Results)]{&__uuidof(Results)...};
|
|
|
|
static_assert(sizeof...(Results) > 0);
|
|
|
|
for (auto i = 0U; i < sizeof...(Results); ++i)
|
|
{
|
|
multiQis[i].pIID = iids[i];
|
|
}
|
|
|
|
const auto hr = CoCreateInstanceEx(clsid, nullptr, clsCtx, nullptr, ARRAYSIZE(multiQis), multiQis);
|
|
|
|
std::tuple<wil::com_ptr_t<Results, error_policy>...> resultTuple;
|
|
|
|
std::apply(
|
|
[i = 0, &multiQis](auto&... a) mutable {
|
|
(a.attach(reinterpret_cast<typename std::remove_reference<decltype(a)>::type::pointer>(multiQis[i++].pItf)), ...);
|
|
},
|
|
resultTuple);
|
|
return std::tuple<HRESULT, decltype(resultTuple)>(hr, std::move(resultTuple));
|
|
}
|
|
|
|
template <typename error_policy, typename... Results>
|
|
auto com_multi_query(IUnknown* obj)
|
|
{
|
|
MULTI_QI multiQis[sizeof...(Results)]{};
|
|
const IID* iids[sizeof...(Results)]{&__uuidof(Results)...};
|
|
|
|
static_assert(sizeof...(Results) > 0);
|
|
|
|
for (auto i = 0U; i < sizeof...(Results); ++i)
|
|
{
|
|
multiQis[i].pIID = iids[i];
|
|
}
|
|
|
|
std::tuple<wil::com_ptr_t<Results, error_policy>...> resultTuple{};
|
|
|
|
wil::com_ptr_nothrow<IMultiQI> multiQi;
|
|
auto hr = obj->QueryInterface(IID_PPV_ARGS(&multiQi));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = multiQi->QueryMultipleInterfaces(ARRAYSIZE(multiQis), multiQis);
|
|
std::apply(
|
|
[i = 0, &multiQis](auto&... a) mutable {
|
|
(a.attach(reinterpret_cast<typename std::remove_reference<decltype(a)>::type::pointer>(multiQis[i++].pItf)), ...);
|
|
},
|
|
resultTuple);
|
|
}
|
|
return std::tuple<HRESULT, decltype(resultTuple)>{hr, std::move(resultTuple)};
|
|
}
|
|
} // namespace details
|
|
/// @endcond
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
// CoCreateInstanceEx can be used to improve performance by requesting multiple interfaces
|
|
// from an object at create time. This is most useful for out of process (OOP) servers, saving
|
|
// and RPC per extra interface requested.
|
|
template <typename... Results>
|
|
auto CoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
|
|
{
|
|
auto [error, result] = details::CoCreateInstanceEx<err_exception_policy, Results...>(clsid, clsCtx);
|
|
THROW_IF_FAILED(error);
|
|
THROW_HR_IF(E_NOINTERFACE, error == CO_S_NOTALLINTERFACES);
|
|
return result;
|
|
}
|
|
|
|
template <typename... Results>
|
|
auto TryCoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
|
|
{
|
|
auto [error, result] = details::CoCreateInstanceEx<err_exception_policy, Results...>(clsid, clsCtx);
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
// Returns [error, result] where result is a tuple with each of the requested interfaces.
|
|
template <typename... Results>
|
|
auto CoCreateInstanceExNoThrow(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
|
|
{
|
|
auto [error, result] = details::CoCreateInstanceEx<err_returncode_policy, Results...>(clsid, clsCtx);
|
|
if (SUCCEEDED(error) && (error == CO_S_NOTALLINTERFACES))
|
|
{
|
|
return std::tuple<HRESULT, decltype(result)>{E_NOINTERFACE, {}};
|
|
}
|
|
return std::tuple<HRESULT, decltype(result)>{error, result};
|
|
}
|
|
|
|
template <typename... Results>
|
|
auto TryCoCreateInstanceExNoThrow(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
|
|
{
|
|
auto [error, result] = details::CoCreateInstanceEx<err_returncode_policy, Results...>(clsid, clsCtx);
|
|
return result;
|
|
}
|
|
|
|
template <typename... Results>
|
|
auto CoCreateInstanceExFailFast(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
|
|
{
|
|
auto [error, result] = details::CoCreateInstanceEx<err_failfast_policy, Results...>(clsid, clsCtx);
|
|
FAIL_FAST_IF_FAILED(error);
|
|
FAIL_FAST_HR_IF(E_NOINTERFACE, error == CO_S_NOTALLINTERFACES);
|
|
return result;
|
|
}
|
|
|
|
template <typename... Results>
|
|
auto TryCoCreateInstanceExFailFast(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
|
|
{
|
|
auto [error, result] = details::CoCreateInstanceEx<err_failfast_policy, Results...>(clsid, clsCtx);
|
|
return result;
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
template <typename... Results>
|
|
auto com_multi_query(IUnknown* obj)
|
|
{
|
|
auto [error, result] = details::com_multi_query<err_exception_policy, Results...>(obj);
|
|
THROW_IF_FAILED(error);
|
|
THROW_HR_IF(E_NOINTERFACE, error == S_FALSE);
|
|
return result;
|
|
}
|
|
|
|
template <typename... Results>
|
|
auto try_com_multi_query(IUnknown* obj)
|
|
{
|
|
auto [error, result] = details::com_multi_query<err_exception_policy, Results...>(obj);
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
#endif // __cpp_lib_apply && __has_include(<type_traits>)
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Stream helpers
|
|
|
|
/** Read data from a stream into a buffer.
|
|
Reads up to a certain number of bytes into a buffer. Returns the amount of data written, which
|
|
may be less than the amount requested if the stream ran out.
|
|
@code
|
|
IStream* source = // ...
|
|
ULONG dataBlob = 0;
|
|
size_t read = 0;
|
|
RETURN_IF_FAILED(wil::stream_read_partial_nothrow(source, &dataBlob, sizeof(dataBlob), &read));
|
|
if (read != sizeof(dataBlob))
|
|
{
|
|
// end of stream, probably
|
|
}
|
|
else if (dataBlob == 0x8675309)
|
|
{
|
|
DoThing(dataBlob);
|
|
}
|
|
@endcode
|
|
@param stream The stream from which to read at most `size` bytes.
|
|
@param data A buffer into which up to `size` bytes will be read
|
|
@param size The size, in bytes, of the buffer pointed to by `data`
|
|
@param wrote The amount, in bytes, of data read from `stream` into `data`
|
|
*/
|
|
inline HRESULT stream_read_partial_nothrow(
|
|
_In_ ISequentialStream* stream, _Out_writes_bytes_to_(size, *wrote) void* data, unsigned long size, unsigned long* wrote)
|
|
{
|
|
RETURN_HR(stream->Read(data, size, wrote));
|
|
}
|
|
|
|
/** Read an exact number of bytes from a stream into a buffer.
|
|
Fails if the stream didn't read all the bytes requested.
|
|
~~~~
|
|
IStream* source = // ...
|
|
ULONG dataBlob = 0;
|
|
RETURN_IF_FAILED(wil::stream_read_nothrow(source, &dataBlob, sizeof(dataBlob)));
|
|
if (dataBlob == 0x8675309)
|
|
{
|
|
DoThing(dataBlob);
|
|
}
|
|
~~~~
|
|
@param stream The stream from which to read at most `size` bytes.
|
|
@param data A buffer into which up to `size` bytes will be read
|
|
@param size The size, in bytes, of the buffer pointed to by `data`
|
|
@return The underlying stream read result, or HRESULT_FROM_WIN32(ERROR_INVALID_DATA) if the stream
|
|
did not read the complete buffer.
|
|
*/
|
|
inline HRESULT stream_read_nothrow(_In_ ISequentialStream* stream, _Out_writes_bytes_all_(size) void* data, unsigned long size)
|
|
{
|
|
unsigned long didRead;
|
|
RETURN_IF_FAILED(stream_read_partial_nothrow(stream, data, size, &didRead));
|
|
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), didRead != size);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/** Read from a stream into a POD type.
|
|
Fails if the stream didn't have enough bytes.
|
|
~~~~
|
|
IStream* source = // ...
|
|
MY_HEADER header{};
|
|
RETURN_IF_FAILED(wil::stream_read_nothrow(source, &header));
|
|
if (header.Version == 0x8675309)
|
|
{
|
|
ConsumeOldHeader(stream, header);
|
|
}
|
|
~~~~
|
|
@param stream The stream from which to read at most `size` bytes.
|
|
@param pThing The POD data type to read from the stream.
|
|
@return The underlying stream read result, or HRESULT_FROM_WIN32(ERROR_INVALID_DATA) if the stream
|
|
did not read the complete buffer.
|
|
*/
|
|
template <typename T>
|
|
HRESULT stream_read_nothrow(_In_ ISequentialStream* stream, _Out_ T* pThing)
|
|
{
|
|
static_assert(__is_pod(T), "Type must be POD.");
|
|
return stream_read_nothrow(stream, pThing, sizeof(T));
|
|
}
|
|
|
|
/** Write an exact number of bytes to a stream from a buffer.
|
|
Fails if the stream didn't read write the bytes requested.
|
|
~~~~
|
|
IStream* source = // ...
|
|
ULONG dataBlob = 0x8675309;
|
|
RETURN_IF_FAILED(wil::stream_write_nothrow(source, &dataBlob, sizeof(dataBlob)));
|
|
~~~~
|
|
@param stream The stream to which to write at most `size` bytes.
|
|
@param data A buffer from which up to `size` bytes will be read
|
|
@param size The size, in bytes, of the buffer pointed to by `data`
|
|
*/
|
|
inline HRESULT stream_write_nothrow(_In_ ISequentialStream* stream, _In_reads_bytes_(size) const void* data, unsigned long size)
|
|
{
|
|
unsigned long wrote;
|
|
RETURN_IF_FAILED(stream->Write(data, size, &wrote));
|
|
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), wrote != size);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/** Write a POD type to a stream.
|
|
Fails if not all the bytes were written.
|
|
~~~~
|
|
IStream* source = // ...
|
|
MY_HEADER header { 0x8675309, HEADER_FLAG_1 | HEADER_FLAG_2 };
|
|
RETURN_IF_FAILED(wil::stream_write_nothrow(source, header));
|
|
|
|
ULONGLONG value = 16;
|
|
RETURN_IF_FAILED(wil::stream_write_nothrow(source, value));
|
|
~~~~
|
|
@param stream The stream to which to write `thing`
|
|
@param thing The POD data type to write to the stream.
|
|
*/
|
|
template <typename T>
|
|
inline HRESULT stream_write_nothrow(_In_ ISequentialStream* stream, const T& thing)
|
|
{
|
|
return stream_write_nothrow(stream, wistd::addressof(thing), sizeof(thing));
|
|
}
|
|
|
|
/** Retrieve the size of this stream, in bytes
|
|
~~~~
|
|
IStream* source = // ...
|
|
ULONGLONG size;
|
|
RETURN_IF_FAILED(wil::stream_size_nothrow(source, &size));
|
|
RETURN_HR_IF(E_INVALIDARG, size > ULONG_MAX);
|
|
~~~~
|
|
@param stream The stream whose size is to be returned in `value`
|
|
@param value The size, in bytes, reported by `stream`
|
|
*/
|
|
inline HRESULT stream_size_nothrow(_In_ IStream* stream, _Out_ unsigned long long* value)
|
|
{
|
|
STATSTG st{};
|
|
RETURN_IF_FAILED(stream->Stat(&st, STATFLAG_NONAME));
|
|
*value = st.cbSize.QuadPart;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/** Seek a stream to a relative offset or absolute position
|
|
~~~~
|
|
IStream* source = // ...
|
|
unsigned long long landed;
|
|
RETURN_IF_FAILED(wil::stream_seek_nothrow(source, 16, STREAM_SEEK_CUR, &landed));
|
|
RETURN_IF_FAILED(wil::stream_seek_nothrow(source, -5, STREAM_SEEK_END));
|
|
RETURN_IF_FAILED(wil::stream_seek_nothrow(source, LLONG_MAX, STREAM_SEEK_CUR));
|
|
~~~~
|
|
@param stream The stream to seek
|
|
@param offset The position, in bytes from the current position, to seek
|
|
@param from The starting point from which to seek, from the STREAM_SEEK_* set of values
|
|
@param value Optionally recieves the new absolute position from the stream
|
|
*/
|
|
inline HRESULT stream_seek_nothrow(_In_ IStream* stream, long long offset, unsigned long from, _Out_opt_ unsigned long long* value = nullptr)
|
|
{
|
|
LARGE_INTEGER amount{};
|
|
ULARGE_INTEGER landed{};
|
|
amount.QuadPart = offset;
|
|
RETURN_IF_FAILED(stream->Seek(amount, from, value ? &landed : nullptr));
|
|
assign_to_opt_param(value, landed.QuadPart);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/** Seek a stream to an absolute offset
|
|
~~~~
|
|
IStream* source = // ...
|
|
RETURN_HR(wil::stream_set_position_nothrow(source, 16));
|
|
~~~~
|
|
@param stream The stream whose size is to be returned in `value`
|
|
@param offset The position, in bytes from the start of the stream, to seek to
|
|
@param value Optionally recieves the new absolute position from the stream
|
|
*/
|
|
inline HRESULT stream_set_position_nothrow(_In_ IStream* stream, unsigned long long offset, _Out_opt_ unsigned long long* value = nullptr)
|
|
{
|
|
// IStream::Seek(..., _SET) interprets the first parameter as an unsigned value.
|
|
return stream_seek_nothrow(stream, static_cast<long long>(offset), STREAM_SEEK_SET, value);
|
|
}
|
|
|
|
/** Seek a relative amount in a stream
|
|
~~~~
|
|
IStream* source = // ...
|
|
RETURN_IF_FAILED(wil::stream_seek_from_current_position_nothrow(source, -16));
|
|
|
|
ULONGLONG newPosition;
|
|
RETURN_IF_FAILED(wil::stream_seek_from_current_position_nothrow(source, 16, &newPosition));
|
|
~~~~
|
|
@param stream The stream whose location is to be moved
|
|
@param amount The offset, in bytes, to seek the stream.
|
|
@param value Set to the new absolute steam position, in bytes
|
|
*/
|
|
inline HRESULT stream_seek_from_current_position_nothrow(_In_ IStream* stream, long long amount, _Out_opt_ unsigned long long* value = nullptr)
|
|
{
|
|
return stream_seek_nothrow(stream, amount, STREAM_SEEK_CUR, value);
|
|
}
|
|
|
|
/** Determine the current byte position in the stream
|
|
~~~~
|
|
IStream* source = // ...
|
|
ULONGLONG currentPos;
|
|
RETURN_IF_FAILED(wil::stream_get_position_nothrow(source, ¤tPos));
|
|
~~~~
|
|
@param stream The stream whose location is to be moved
|
|
@param position Set to the current absolute steam position, in bytes
|
|
*/
|
|
inline HRESULT stream_get_position_nothrow(_In_ IStream* stream, _Out_ unsigned long long* position)
|
|
{
|
|
return stream_seek_from_current_position_nothrow(stream, 0, position);
|
|
}
|
|
|
|
/** Moves the stream to absolute position 0
|
|
~~~~
|
|
IStream* source = // ...
|
|
RETURN_IF_FAILED(wil::stream_reset_nothrow(source));
|
|
~~~~
|
|
@param stream The stream whose location is to be moved
|
|
*/
|
|
inline HRESULT stream_reset_nothrow(_In_ IStream* stream)
|
|
{
|
|
return stream_set_position_nothrow(stream, 0);
|
|
}
|
|
|
|
/** Copy data from one stream to another, returning the final amount copied.
|
|
~~~~
|
|
IStream* source = // ...
|
|
IStream* target = // ...
|
|
ULONGLONG copied;
|
|
RETURN_IF_FAILED(wil::stream_copy_bytes_nothrow(source, target, sizeof(MyType), &copied));
|
|
if (copied < sizeof(MyType))
|
|
{
|
|
DoSomethingAboutPartialCopy();
|
|
}
|
|
~~~~
|
|
@param source The stream from which to copy at most `amount` bytes
|
|
@param target The steam to which to copy at most `amount` bytes
|
|
@param amount The maximum number of bytes to copy from `source` to `target`
|
|
@param pCopied If non-null, set to the number of bytes copied between the two.
|
|
*/
|
|
inline HRESULT stream_copy_bytes_nothrow(
|
|
_In_ IStream* source, _In_ IStream* target, unsigned long long amount, _Out_opt_ unsigned long long* pCopied = nullptr)
|
|
{
|
|
ULARGE_INTEGER toCopy{};
|
|
ULARGE_INTEGER copied{};
|
|
toCopy.QuadPart = amount;
|
|
RETURN_IF_FAILED(source->CopyTo(target, toCopy, nullptr, &copied));
|
|
assign_to_opt_param(pCopied, copied.QuadPart);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/** Copy all data from one stream to another, returning the final amount copied.
|
|
~~~~
|
|
IStream* source = // ...
|
|
IStream* target = // ...
|
|
ULONGLONG copied;
|
|
RETURN_IF_FAILED(wil::stream_copy_all_nothrow(source, target, &copied));
|
|
if (copied < 8)
|
|
{
|
|
DoSomethingAboutPartialCopy();
|
|
}
|
|
~~~~
|
|
@param source The stream from which to copy all content
|
|
@param target The steam to which to copy all content
|
|
@param pCopied If non-null, set to the number of bytes copied between the two.
|
|
*/
|
|
inline HRESULT stream_copy_all_nothrow(_In_ IStream* source, _In_ IStream* target, _Out_opt_ unsigned long long* pCopied = nullptr)
|
|
{
|
|
return stream_copy_bytes_nothrow(source, target, ULLONG_MAX, pCopied);
|
|
}
|
|
|
|
/** Copies an exact amount of data from one stream to another, failing otherwise
|
|
~~~~
|
|
IStream* source = // ...
|
|
IStream* target = // ...
|
|
RETURN_IF_FAILED(wil::stream_copy_all_nothrow(source, target, 16));
|
|
~~~~
|
|
@param source The stream from which to copy at most `amount` bytes
|
|
@param target The steam to which to copy at most `amount` bytes
|
|
@param amount The number of bytes to copy from `source` to `target`
|
|
*/
|
|
inline HRESULT stream_copy_exact_nothrow(_In_ IStream* source, _In_ IStream* target, unsigned long long amount)
|
|
{
|
|
unsigned long long copied;
|
|
RETURN_IF_FAILED(stream_copy_bytes_nothrow(source, target, ULLONG_MAX, &copied));
|
|
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), copied != amount);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//! Controls behavior when reading a zero-length string from a stream
|
|
enum class empty_string_options
|
|
{
|
|
//! Zero-length strings are returned as nullptr
|
|
returns_null,
|
|
|
|
//! Zero-length strings are allocated and returned with zero characters
|
|
returns_empty,
|
|
};
|
|
|
|
#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN)
|
|
|
|
/** Read a string from a stream and returns an allocated copy
|
|
Deserializes strings in streams written by both IStream_WriteStr and wil::stream_write_string[_nothrow]. The format
|
|
is a single 16-bit quantity, followed by that many wchar_ts. The returned string is allocated with CoTaskMemAlloc.
|
|
Returns a zero-length (but non-null) string if the stream contained a zero-length string.
|
|
@code
|
|
IStream* source = // ...
|
|
wil::unique_cotaskmem_string content;
|
|
RETURN_IF_FAILED(wil::stream_read_string_nothrow(source, &content));
|
|
if (wcscmp(content.get(), L"waffles") == 0)
|
|
{
|
|
// Waffles!
|
|
}
|
|
@endcode
|
|
@param source The stream from which to read a string
|
|
@param value Set to point to the allocated result of reading a string from `source`
|
|
@param options Controls behavior when reading a zero-length string from a stream
|
|
*/
|
|
inline HRESULT stream_read_string_nothrow(
|
|
_In_ ISequentialStream* source,
|
|
_When_(options == empty_string_options::returns_empty, _Outptr_result_z_)
|
|
_When_(options == empty_string_options::returns_null, _Outptr_result_maybenull_z_) wchar_t** value,
|
|
empty_string_options options = empty_string_options::returns_empty)
|
|
{
|
|
unsigned short cch;
|
|
RETURN_IF_FAILED(stream_read_nothrow(source, &cch));
|
|
|
|
if ((cch == 0) && (options == empty_string_options::returns_null))
|
|
{
|
|
*value = nullptr;
|
|
}
|
|
else
|
|
{
|
|
auto allocated = make_unique_cotaskmem_nothrow<wchar_t[]>(static_cast<size_t>(cch) + 1);
|
|
RETURN_IF_NULL_ALLOC(allocated);
|
|
RETURN_IF_FAILED(stream_read_nothrow(source, allocated.get(), static_cast<unsigned long>(cch) * sizeof(wchar_t)));
|
|
allocated[cch] = 0;
|
|
|
|
*value = allocated.release();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#endif // __WIL_OBJBASE_H
|
|
|
|
/** Write a string to a stream
|
|
Serializes a string into a stream by putting its length and then the wchar_ts in the string
|
|
into the stream. Zero-length strings have their length but no data written. This is the
|
|
form expected by IStream_ReadStr and wil::string_read_stream.
|
|
@code
|
|
IStream* target = // ...
|
|
RETURN_IF_FAILED(wil::stream_write_string_nothrow(target, L"Waffles", 3));
|
|
// Produces wchar_t[] { 0x3, L'W', L'a', L'f' };
|
|
@endcode
|
|
@param target The stream to which to write a string
|
|
@param source The string to write. Can be null if `writeLength` is zero
|
|
@param writeLength The number of characters to write from source into `target`
|
|
*/
|
|
inline HRESULT stream_write_string_nothrow(_In_ ISequentialStream* target, _In_reads_opt_(writeLength) const wchar_t* source, _In_ size_t writeLength)
|
|
{
|
|
FAIL_FAST_IF(writeLength > USHRT_MAX);
|
|
|
|
RETURN_IF_FAILED(stream_write_nothrow(target, static_cast<unsigned short>(writeLength)));
|
|
|
|
if (writeLength > 0)
|
|
{
|
|
RETURN_IF_FAILED(stream_write_nothrow(target, source, static_cast<unsigned short>(writeLength) * sizeof(wchar_t)));
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/** Write a string to a stream
|
|
Serializes a string into a stream by putting its length and then the wchar_ts in the string
|
|
into the stream. Zero-length strings have their length but no data written. This is the
|
|
form expected by IStream_ReadStr and wil::string_read_stream.
|
|
@code
|
|
IStream* target = // ...
|
|
RETURN_IF_FAILED(wil::stream_write_string_nothrow(target, L"Waffles"));
|
|
// Produces wchar_t[] { 0x3, L'W', L'a', L'f', L'f', L'l', L'e', L's' };
|
|
@endcode
|
|
@param target The stream to which to write a string
|
|
@param source The string to write. When nullptr, a zero-length string is written.
|
|
*/
|
|
inline HRESULT stream_write_string_nothrow(_In_ ISequentialStream* target, _In_opt_z_ const wchar_t* source)
|
|
{
|
|
return stream_write_string_nothrow(target, source, source ? wcslen(source) : 0);
|
|
}
|
|
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
|
|
/** Read data from a stream into a buffer.
|
|
@code
|
|
IStream* source = // ...
|
|
ULONG dataBlob = 0;
|
|
auto read = wil::stream_read_partial(source, &dataBlob, sizeof(dataBlob));
|
|
if (read != sizeof(dataBlob))
|
|
{
|
|
// end of stream, probably
|
|
}
|
|
else if (dataBlob == 0x8675309)
|
|
{
|
|
DoThing(dataBlob);
|
|
}
|
|
@endcode
|
|
@param stream The stream from which to read at most `size` bytes.
|
|
@param data A buffer into which up to `size` bytes will be read
|
|
@param size The size, in bytes, of the buffer pointed to by `data`
|
|
@return The amount, in bytes, of data read from `stream` into `data`
|
|
*/
|
|
inline unsigned long stream_read_partial(_In_ ISequentialStream* stream, _Out_writes_bytes_to_(size, return) void* data, unsigned long size)
|
|
{
|
|
unsigned long didRead;
|
|
THROW_IF_FAILED(stream_read_partial_nothrow(stream, data, size, &didRead));
|
|
|
|
return didRead;
|
|
}
|
|
|
|
/** Read an exact number of bytes from a stream into a buffer.
|
|
Fails if the stream didn't read all the bytes requested by throwing HRESULT_FROM_WIN32(ERROR_INVALID_DATA).
|
|
~~~~
|
|
IStream* source = // ...
|
|
ULONG dataBlob = 0;
|
|
wil::stream_read(source, &dataBlob, sizeof(dataBlob));
|
|
if (dataBlob == 0x8675309)
|
|
{
|
|
DoThing(dataBlob);
|
|
}
|
|
~~~~
|
|
@param stream The stream from which to read at most `size` bytes.
|
|
@param data A buffer into which up to `size` bytes will be read
|
|
@param size The size, in bytes, of the buffer pointed to by `data`
|
|
*/
|
|
inline void stream_read(_In_ ISequentialStream* stream, _Out_writes_bytes_all_(size) void* data, unsigned long size)
|
|
{
|
|
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), stream_read_partial(stream, data, size) != size);
|
|
}
|
|
|
|
/** Read from a stream into a POD type.
|
|
Fails if the stream didn't have enough bytes by throwing HRESULT_FROM_WIN32(ERROR_INVALID_DATA).
|
|
~~~~
|
|
IStream* source = // ...
|
|
MY_HEADER header = wil::stream_read<MY_HEADER>(source);
|
|
if (header.Version == 0x8675309)
|
|
{
|
|
ConsumeOldHeader(stream, header);
|
|
}
|
|
~~~~
|
|
@param stream The stream from which to read at most `sizeof(T)` bytes.
|
|
@return An instance of `T` read from the stream
|
|
*/
|
|
template <typename T>
|
|
T stream_read(_In_ ISequentialStream* stream)
|
|
{
|
|
static_assert(__is_pod(T), "Read type must be POD");
|
|
T temp{};
|
|
stream_read(stream, &temp, sizeof(temp));
|
|
|
|
return temp;
|
|
}
|
|
|
|
/** Write an exact number of bytes to a stream from a buffer.
|
|
Fails if the stream didn't read write the bytes requested.
|
|
~~~~
|
|
IStream* source = // ...
|
|
ULONG dataBlob = 0;
|
|
wil::stream_write(source, dataBlob, sizeof(dataBlob));
|
|
~~~~
|
|
@param stream The stream to which to write at most `size` bytes.
|
|
@param data A buffer from which up to `size` bytes will be read
|
|
@param size The size, in bytes, of the buffer pointed to by `data`
|
|
*/
|
|
inline void stream_write(_In_ ISequentialStream* stream, _In_reads_bytes_(size) const void* data, unsigned long size)
|
|
{
|
|
THROW_IF_FAILED(stream_write_nothrow(stream, data, size));
|
|
}
|
|
|
|
/** Write a POD type to a stream.
|
|
Fails if the stream didn't accept the entire size.
|
|
~~~~
|
|
IStream* target = // ...
|
|
|
|
MY_HEADER header { 0x8675309, HEADER_FLAG_1 | HEADER_FLAG_2 };
|
|
wil::stream_write(target, header)
|
|
|
|
wil::stream_write<ULONGLONG>(target, 16);
|
|
~~~~
|
|
@param stream The stream to which to write `thing`
|
|
@param thing The POD data type to write to the stream.
|
|
*/
|
|
template <typename T>
|
|
inline void stream_write(_In_ ISequentialStream* stream, const T& thing)
|
|
{
|
|
stream_write(stream, wistd::addressof(thing), sizeof(thing));
|
|
}
|
|
|
|
/** Retrieve the size of this stream, in bytes
|
|
~~~~
|
|
IStream* source = // ...
|
|
ULONGLONG size = wil::stream_size(source);
|
|
~~~~
|
|
@param stream The stream whose size is to be returned in `value`
|
|
@return The size, in bytes, reported by `stream`
|
|
*/
|
|
inline unsigned long long stream_size(_In_ IStream* stream)
|
|
{
|
|
unsigned long long size;
|
|
THROW_IF_FAILED(stream_size_nothrow(stream, &size));
|
|
|
|
return size;
|
|
}
|
|
|
|
/** Seek a stream to an absolute offset
|
|
~~~~
|
|
IStream* source = // ...
|
|
wil::stream_set_position(source, sizeof(HEADER));
|
|
~~~~
|
|
@param stream The stream whose size is to be returned in `value`
|
|
@param offset The offset, in bytes, to seek the stream.
|
|
@return The new absolute stream position, in bytes
|
|
*/
|
|
inline unsigned long long stream_set_position(_In_ IStream* stream, unsigned long long offset)
|
|
{
|
|
unsigned long long landed;
|
|
THROW_IF_FAILED(stream_set_position_nothrow(stream, offset, &landed));
|
|
return landed;
|
|
}
|
|
|
|
/** Seek a relative amount in a stream
|
|
~~~~
|
|
IStream* source = // ...
|
|
ULONGLONG newPosition = wil::stream_seek_from_current_position(source, 16);
|
|
~~~~
|
|
@param stream The stream whose location is to be moved
|
|
@param amount The offset, in bytes, to seek the stream.
|
|
@return The new absolute stream position, in bytes
|
|
*/
|
|
inline unsigned long long stream_seek_from_current_position(_In_ IStream* stream, long long amount)
|
|
{
|
|
unsigned long long landed;
|
|
THROW_IF_FAILED(stream_seek_from_current_position_nothrow(stream, amount, &landed));
|
|
|
|
return landed;
|
|
}
|
|
|
|
/** Determine the current byte position in the stream
|
|
~~~~
|
|
IStream* source = // ...
|
|
ULONGLONG currentPos = wil::stream_get_position(source);
|
|
~~~~
|
|
@param stream The stream whose location is to be moved
|
|
@return The current position reported by `stream`
|
|
*/
|
|
inline unsigned long long stream_get_position(_In_ IStream* stream)
|
|
{
|
|
return stream_seek_from_current_position(stream, 0);
|
|
}
|
|
|
|
/** Moves the stream to absolute position 0
|
|
~~~~
|
|
IStream* source = // ...
|
|
wil::stream_reset(source);
|
|
ASSERT(wil::stream_get_position(source) == 0);
|
|
~~~~
|
|
@param stream The stream whose location is to be moved
|
|
*/
|
|
inline void stream_reset(_In_ IStream* stream)
|
|
{
|
|
stream_set_position(stream, 0);
|
|
}
|
|
|
|
/** Copy data from one stream to another
|
|
~~~~
|
|
IStream* source = // ...
|
|
IStream* target = // ...
|
|
ULONGLONG copied = ;
|
|
if (wil::stream_copy_bytes(source, target, sizeof(Header)) < sizeof(Header))
|
|
{
|
|
DoSomethingAboutPartialCopy();
|
|
}
|
|
~~~~
|
|
@param source The stream from which to copy at most `amount` bytes
|
|
@param target The steam to which to copy at most `amount` bytes
|
|
@param amount The maximum number of bytes to copy from `source` to `target`
|
|
@return The number of bytes copied between the two streams
|
|
*/
|
|
inline unsigned long long stream_copy_bytes(_In_ IStream* source, _In_ IStream* target, unsigned long long amount)
|
|
{
|
|
unsigned long long copied;
|
|
THROW_IF_FAILED(stream_copy_bytes_nothrow(source, target, amount, &copied));
|
|
|
|
return copied;
|
|
}
|
|
|
|
/** Copy all data from one stream to another
|
|
~~~~
|
|
IStream* source = // ...
|
|
IStream* target = // ...
|
|
ULONGLONG copied = wil::stream_copy_all(source, target);
|
|
~~~~
|
|
@param source The stream from which to copy all content
|
|
@param target The steam to which to copy all content
|
|
@return The number of bytes copied between the two.
|
|
*/
|
|
inline unsigned long long stream_copy_all(_In_ IStream* source, _In_ IStream* target)
|
|
{
|
|
return stream_copy_bytes(source, target, ULLONG_MAX);
|
|
}
|
|
|
|
/** Copies an exact amount of data from one stream to another, failing otherwise
|
|
~~~~
|
|
IStream* source = // ...
|
|
IStream* target = // ...
|
|
wil::stream_copy_all_nothrow(source, target, sizeof(SOMETHING));
|
|
~~~~
|
|
@param source The stream from which to copy at most `amount` bytes
|
|
@param target The steam to which to copy at most `amount` bytes
|
|
@param amount The number of bytes to copy from `source` to `target`
|
|
*/
|
|
inline void stream_copy_exact(_In_ IStream* source, _In_ IStream* target, unsigned long long amount)
|
|
{
|
|
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), stream_copy_bytes(source, target, amount) != amount);
|
|
}
|
|
|
|
#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN)
|
|
|
|
/** Read a string from a stream and returns an allocated copy
|
|
Deserializes strings in streams written by both IStream_WriteStr and wil::stream_write_string[_nothrow]. The format
|
|
is a single 16-bit quantity, followed by that many wchar_ts. The returned string is allocated with CoTaskMemAlloc.
|
|
Returns a zero-length (but non-null) string if the stream contained a zero-length string.
|
|
@code
|
|
IStream* source = // ...
|
|
wil::unique_cotaskmem_string content = wil::stream_read_string(source);
|
|
if (wcscmp(content.get(), L"waffles") == 0)
|
|
{
|
|
// Waffles!
|
|
}
|
|
@endcode
|
|
@param source The stream from which to read a string
|
|
@param options Controls the behavior when reading a zero-length string
|
|
@return An non-null string (but possibly zero lengh) string read from `source`
|
|
*/
|
|
inline wil::unique_cotaskmem_string stream_read_string(_In_ ISequentialStream* source, empty_string_options options = empty_string_options::returns_empty)
|
|
{
|
|
wil::unique_cotaskmem_string result;
|
|
THROW_IF_FAILED(stream_read_string_nothrow(source, &result, options));
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif // __WIL_OBJBASE_H
|
|
|
|
/** Write a string to a stream
|
|
Serializes a string into a stream by putting its length and then the wchar_ts in the string
|
|
into the stream. Zero-length strings have their length but no data written. This is the
|
|
form expected by IStream_ReadStr and wil::string_read_stream.
|
|
~~~~
|
|
IStream* target = // ...
|
|
wil::stream_write_string(target, L"Waffles", 3);
|
|
~~~~
|
|
@param target The stream to which to write a string
|
|
@param source The string to write. Can be null if `toWriteCch` is zero
|
|
@param toWriteCch The number of characters to write from source into `target`
|
|
*/
|
|
inline void stream_write_string(_In_ ISequentialStream* target, _In_reads_opt_(toWriteCch) const wchar_t* source, _In_ size_t toWriteCch)
|
|
{
|
|
THROW_IF_FAILED(stream_write_string_nothrow(target, source, toWriteCch));
|
|
}
|
|
|
|
/** Write a string to a stream
|
|
Serializes a string into a stream by putting its length and then the wchar_ts in the string
|
|
into the stream. Zero-length strings have their length but no data written.This is the
|
|
form expected by IStream_ReadStr and wil::string_read_stream.
|
|
~~~~
|
|
IStream* target = // ...
|
|
wil::stream_write_string(target, L"Waffles");
|
|
~~~~
|
|
@param target The stream to which to write a string
|
|
@param source The string to write. When nullptr, a zero-length string is written.
|
|
*/
|
|
inline void stream_write_string(_In_ ISequentialStream* target, _In_opt_z_ const wchar_t* source)
|
|
{
|
|
THROW_IF_FAILED(stream_write_string_nothrow(target, source, source ? wcslen(source) : 0));
|
|
}
|
|
|
|
/** Saves and restores the position of a stream
|
|
Useful for potentially reading data from a stream, or being able to read ahead, then reset
|
|
back to where one left off, such as conditionally reading content from a stream.
|
|
@code
|
|
void MaybeConsumeStream(IStream* stream)
|
|
{
|
|
// On error, reset the read position in the stream to where we left off
|
|
auto saver = wil::stream_position_saver(stream);
|
|
auto header = wil::stream_read<MY_HEADER>(stream);
|
|
for (ULONG i = 0; i < header.Count; ++i)
|
|
{
|
|
ProcessElement(wil::stream_read<MY_ELEMENT>(stream));
|
|
}
|
|
}
|
|
@endcode
|
|
*/
|
|
class stream_position_saver
|
|
{
|
|
public:
|
|
//! Constructs a saver from the current position of this stream
|
|
//! @param stream The stream instance whose position is to be saved.
|
|
explicit stream_position_saver(_In_opt_ IStream* stream) :
|
|
m_stream(stream), m_position(stream ? stream_get_position(stream) : 0)
|
|
{
|
|
}
|
|
|
|
~stream_position_saver()
|
|
{
|
|
if (m_stream)
|
|
{
|
|
LOG_IF_FAILED(stream_set_position_nothrow(m_stream.get(), m_position));
|
|
}
|
|
}
|
|
|
|
/** Updates the current position in the stream
|
|
@code
|
|
// Read a size marker from the stream, then advance that much.
|
|
IStream* stream1 = // ...
|
|
auto saver = wil::stream_position_saver(stream1);
|
|
auto size = wil::stream_read<long>(stream1);
|
|
wil::stream_seek_from_current_position(stream, size);
|
|
saver.update();
|
|
@endcode
|
|
*/
|
|
void update()
|
|
{
|
|
m_position = stream_get_position(m_stream.get());
|
|
}
|
|
|
|
//! Returns the current position being saved for the stream
|
|
//! @returns The position, in bytes, being saved for the stream
|
|
WI_NODISCARD unsigned long long position() const
|
|
{
|
|
return m_position;
|
|
}
|
|
|
|
/** Resets the position saver to manage a new stream
|
|
Reverts the position of any stream this saver is currently holding a place for.
|
|
~~~~
|
|
IStream* stream1 = // ...
|
|
IStream* stream2 = // ...
|
|
auto saver = wil::stream_position_saver(stream1);
|
|
if (wil::stream_read<MyType>(stream1).Flags != 0)
|
|
{
|
|
saver.reset(stream2); // position in stream1 is reverted, now holding stream2
|
|
}
|
|
~~~~
|
|
@param stream The stream whose position is to be saved
|
|
*/
|
|
void reset(_In_ IStream* stream)
|
|
{
|
|
reset();
|
|
|
|
m_stream = stream;
|
|
m_position = wil::stream_get_position(m_stream.get());
|
|
}
|
|
|
|
/** Resets the position of the stream
|
|
~~~~
|
|
IStream* stream1 = // ...
|
|
auto saver = wil::stream_position_saver(stream1);
|
|
MyType mt = wil::stream_read<MyType>(stream1);
|
|
if (mt.Flags & MyTypeFlags::Extended)
|
|
{
|
|
saver.reset();
|
|
ProcessExtended(stream1, wil::stream_read<MyTypeExtended>(stream1));
|
|
}
|
|
else
|
|
{
|
|
ProcessStandard(stream1, mt);
|
|
}
|
|
~~~~
|
|
*/
|
|
void reset()
|
|
{
|
|
if (m_stream)
|
|
{
|
|
wil::stream_set_position(m_stream.get(), m_position);
|
|
}
|
|
}
|
|
|
|
/** Stops saving the position of the stream
|
|
@code
|
|
// The stream has either a standard or extended header, followed by interesting content.
|
|
// Read either one, leaving the stream after the headers have been read off. On failure,
|
|
// the stream's position is restored.
|
|
std::pair<MyType, MyTypeExtended> get_headers(_In_ IStream* source)
|
|
{
|
|
auto saver = wil::stream_position_saver(stream1);
|
|
MyType mt = wil::stream_read<MyType>(stream1);
|
|
MyTypeExtended mte{};
|
|
if (mt.Flags & MyTypeFlags::Extended)
|
|
{
|
|
mte = wil::stream_read<MyTypeExtended>(stream1);
|
|
}
|
|
saver.dismiss();
|
|
return { mt, mte };
|
|
}
|
|
@endcode
|
|
*/
|
|
void dismiss()
|
|
{
|
|
m_stream.reset();
|
|
}
|
|
|
|
stream_position_saver(stream_position_saver&&) = default;
|
|
stream_position_saver& operator=(stream_position_saver&&) = default;
|
|
|
|
stream_position_saver(const stream_position_saver&) = delete;
|
|
void operator=(const stream_position_saver&) = delete;
|
|
|
|
private:
|
|
com_ptr<IStream> m_stream;
|
|
unsigned long long m_position;
|
|
};
|
|
#endif // WIL_ENABLE_EXCEPTIONS
|
|
#pragma endregion // stream helpers
|
|
|
|
#if defined(__IObjectWithSite_INTERFACE_DEFINED__) || defined(WIL_DOXYGEN)
|
|
/// @cond
|
|
namespace details
|
|
{
|
|
inline void __stdcall SetSiteNull(IObjectWithSite* objWithSite)
|
|
{
|
|
objWithSite->SetSite(nullptr); // break the cycle
|
|
}
|
|
} // namespace details
|
|
/// @endcond
|
|
|
|
using unique_set_site_null_call = wil::unique_com_call<IObjectWithSite, decltype(details::SetSiteNull), details::SetSiteNull>;
|
|
|
|
/** RAII support for managing the site chain. This function sets the site pointer on an object and return an object
|
|
that resets it on destruction to break the cycle.
|
|
Note, this does not preserve the existing site if there is one (an uncommon case) so only use this when that is not required.
|
|
~~~
|
|
auto cleanup = wil::com_set_site(execCommand.get(), serviceProvider->GetAsSite());
|
|
~~~
|
|
Include ocidl.h before wil/com.h to use this.
|
|
*/
|
|
WI_NODISCARD inline unique_set_site_null_call com_set_site(_In_opt_ IUnknown* obj, _In_opt_ IUnknown* site)
|
|
{
|
|
wil::com_ptr_nothrow<IObjectWithSite> objWithSite;
|
|
if (site && wil::try_com_copy_to(obj, &objWithSite))
|
|
{
|
|
objWithSite->SetSite(site);
|
|
}
|
|
return unique_set_site_null_call(objWithSite.get());
|
|
}
|
|
|
|
/** Iterate over each object in a site chain. Useful for debugging site issues, here is sample use.
|
|
~~~
|
|
void OutputDebugSiteChainWatchWindowText(IUnknown* site)
|
|
{
|
|
OutputDebugStringW(L"Copy and paste these entries into the Visual Studio Watch Window\n");
|
|
wil::for_each_site(site, [](IUnknown* site)
|
|
{
|
|
wchar_t msg[64];
|
|
StringCchPrintfW(msg, ARRAYSIZE(msg), L"((IUnknown*)0x%p)->__vfptr[0]\n", site);
|
|
OutputDebugStringW(msg);
|
|
});
|
|
}
|
|
~~~
|
|
*/
|
|
|
|
template <typename TLambda>
|
|
void for_each_site(_In_opt_ IUnknown* siteInput, TLambda&& callback)
|
|
{
|
|
wil::com_ptr_nothrow<IUnknown> site(siteInput);
|
|
while (site)
|
|
{
|
|
callback(site.get());
|
|
auto objWithSite = site.try_query<IObjectWithSite>();
|
|
site.reset();
|
|
if (objWithSite)
|
|
{
|
|
objWithSite->GetSite(IID_PPV_ARGS(&site));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // __IObjectWithSite_INTERFACE_DEFINED__
|
|
|
|
// if C++17 or greater
|
|
#if WIL_HAS_CXX_17
|
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
/// @cond
|
|
namespace details
|
|
{
|
|
template <typename>
|
|
struct com_enumerator_next_traits;
|
|
|
|
template <typename Itf, typename T, typename Ret>
|
|
struct com_enumerator_next_traits<Ret (__stdcall Itf::*)(ULONG, T*, ULONG*)>
|
|
{
|
|
using Interface = Itf;
|
|
using Result = T;
|
|
};
|
|
|
|
template <typename Itf, typename T, typename Ret>
|
|
struct com_enumerator_next_traits<Ret (__stdcall Itf::*)(ULONG, T*, ULONG*) noexcept>
|
|
{
|
|
using Interface = Itf;
|
|
using Result = T;
|
|
};
|
|
|
|
template <typename T>
|
|
struct has_next
|
|
{
|
|
template <typename U = T>
|
|
static auto test(int) -> decltype(wistd::declval<U>()->Next(0, nullptr, nullptr), wistd::true_type{});
|
|
|
|
template <typename>
|
|
static auto test(...) -> wistd::false_type;
|
|
|
|
static constexpr bool value = decltype(test<T>(0))::value;
|
|
};
|
|
|
|
template <typename T>
|
|
constexpr bool has_next_v = has_next<T>::value;
|
|
|
|
template <typename T>
|
|
struct You_must_specify_Smart_Output_type_explicitly
|
|
{
|
|
// If you get this error, you must specify a smart pointer type to receive the enumerated objects.
|
|
// We deduce the enumerator's output type (the type of the second parameter to the Next method).
|
|
// If that type is a COM pointer type (IFoo*), then we use wil::com_ptr<IFoo>. Otherwise, you must explicitly
|
|
// specify a smart-object type to receive the enumerated objects as it is not obvious how to handle disposing
|
|
// of an enumerated object.
|
|
// For example, if you have an enumerator that enumerates BSTRs, you must specify wil::unique_bstr as the
|
|
// smart pointer type to receive the enumerated BSTRs.
|
|
// auto it = wil::com_iterator<wil::unique_bstr>(pEnumBStr);
|
|
static_assert(
|
|
wistd::is_same_v<T, void>,
|
|
"Couldn't deduce a smart pointer type for the enumerator's output. You must explicitly specify a smart-object type to receive the enumerated objects.");
|
|
};
|
|
|
|
template <typename Interface>
|
|
struct com_enumerator_traits
|
|
{
|
|
using Result = typename com_enumerator_next_traits<decltype(&Interface::Next)>::Result;
|
|
|
|
// If the result is a COM pointer type (IFoo*), then we use wil::com_ptr<IFoo>.
|
|
// Otherwise, you must explicitly specify a smart output type.
|
|
using smart_result = wistd::conditional_t<
|
|
wistd::is_pointer_v<Result> && wistd::is_base_of_v<::IUnknown, wistd::remove_pointer_t<Result>>,
|
|
wil::com_ptr<wistd::remove_pointer_t<Result>>,
|
|
You_must_specify_Smart_Output_type_explicitly<Interface>>;
|
|
};
|
|
} // namespace details
|
|
/// @endcond
|
|
|
|
template <typename TStoredType, typename IEnumType>
|
|
struct com_iterator
|
|
{
|
|
using TActualStoredType =
|
|
wistd::conditional_t<wistd::is_same_v<TStoredType, void>, typename wil::details::com_enumerator_traits<IEnumType>::smart_result, TStoredType>;
|
|
|
|
wil::com_ptr<IEnumType> m_enum{};
|
|
TActualStoredType m_currentValue{};
|
|
|
|
using smart_result = TActualStoredType;
|
|
com_iterator(com_iterator&&) = default;
|
|
com_iterator(com_iterator const&) = default;
|
|
com_iterator& operator=(com_iterator&&) = default;
|
|
com_iterator& operator=(com_iterator const&) = default;
|
|
|
|
com_iterator(IEnumType* enumPtr) : m_enum(enumPtr)
|
|
{
|
|
FetchNext();
|
|
}
|
|
|
|
auto operator->()
|
|
{
|
|
return wistd::addressof(m_currentValue);
|
|
}
|
|
|
|
auto& operator*()
|
|
{
|
|
return m_currentValue;
|
|
}
|
|
|
|
const auto& operator*() const
|
|
{
|
|
return m_currentValue;
|
|
}
|
|
|
|
com_iterator& operator++()
|
|
{
|
|
// If we're already at the end, don't try to advance. Otherwise, use Next to advance.
|
|
if (m_enum)
|
|
{
|
|
FetchNext();
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool operator!=(com_iterator const& other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
bool operator==(com_iterator const& other) const
|
|
{
|
|
return (m_enum.get() == other.m_enum.get());
|
|
}
|
|
|
|
private:
|
|
void FetchNext()
|
|
{
|
|
if (m_enum)
|
|
{
|
|
// we cannot say m_currentValue = {} because com_ptr has 2 operator= overloads: one for T* and one for nullptr_t
|
|
m_currentValue = TActualStoredType{};
|
|
auto hr = m_enum->Next(1, &m_currentValue, nullptr);
|
|
if (hr == S_FALSE)
|
|
{
|
|
m_enum = nullptr;
|
|
}
|
|
else
|
|
{
|
|
THROW_IF_FAILED_MSG(hr, "Failed to get next");
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// CTAD for com_iterator
|
|
|
|
template <typename IEnumType>
|
|
com_iterator(IEnumType*) -> com_iterator<void, IEnumType>;
|
|
|
|
template <typename TStoredType = void, typename IEnumXxx, wistd::enable_if_t<wil::details::has_next_v<IEnumXxx*>, int> = 0>
|
|
WI_NODISCARD auto make_range(IEnumXxx* enumPtr)
|
|
{
|
|
using TActualStoredType =
|
|
wistd::conditional_t<wistd::is_same_v<TStoredType, void>, typename wil::details::com_enumerator_traits<IEnumXxx>::smart_result, TStoredType>;
|
|
|
|
struct iterator_range
|
|
{
|
|
|
|
static_assert(!wistd::is_same_v<TActualStoredType, void>, "You must specify a type to receive the enumerated objects.");
|
|
|
|
// the stored type must be constructible from the output type of the enumerator
|
|
static_assert(
|
|
wistd::is_constructible_v<TActualStoredType, typename wil::details::com_enumerator_traits<IEnumXxx>::Result>,
|
|
"The type you specified cannot be converted to the enumerator's output type.");
|
|
|
|
using enumerator_type = com_iterator<TActualStoredType, IEnumXxx>;
|
|
|
|
IEnumXxx* m_enumerator{};
|
|
iterator_range(IEnumXxx* enumPtr) : m_enumerator(enumPtr)
|
|
{
|
|
}
|
|
|
|
WI_NODISCARD auto begin()
|
|
{
|
|
return enumerator_type(m_enumerator);
|
|
}
|
|
|
|
WI_NODISCARD constexpr auto end() const noexcept
|
|
{
|
|
return enumerator_type(nullptr);
|
|
}
|
|
};
|
|
|
|
return iterator_range(enumPtr);
|
|
}
|
|
|
|
#endif // WIL_HAS_CXX_17
|
|
#endif // WIL_ENABLE_EXCEPTIONS
|
|
|
|
} // namespace wil
|
|
|
|
#endif
|