1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_mscom_AgileReference_h
8 #define mozilla_mscom_AgileReference_h
10 #include "mozilla/Attributes.h"
11 #include "mozilla/RefPtr.h"
12 #include "mozilla/Result.h"
13 #include "mozilla/Unused.h"
15 #include "nsISupportsImpl.h"
19 namespace mozilla::mscom
{
22 // Detemplatized implementation details of `AgileReference`.
23 HRESULT
AgileReference_CreateImpl(RefPtr
<IAgileReference
>&, REFIID
, IUnknown
*);
24 HRESULT
AgileReference_ResolveImpl(RefPtr
<IAgileReference
> const&, REFIID
,
29 * This class encapsulates an "agile reference". These are references that allow
30 * you to pass COM interfaces between apartments. When you have an interface
31 * that you would like to pass between apartments, you wrap that interface in an
32 * AgileReference and pass that instead. Then you can "unwrap" the interface by
33 * calling Resolve(), which will return a proxy object implementing the same
39 * // From a non-main thread, where `foo` is an `IFoo*` or `RefPtr<IFoo>`:
40 * auto myAgileRef = AgileReference(foo);
41 * NS_DispatchToMainThread([mar = std::move(myAgileRef)] {
42 * RefPtr<IFoo> foo = mar.Resolve();
43 * // Now methods may be invoked on `foo`
47 template <typename InterfaceT
>
48 class AgileReference final
{
50 std::is_base_of_v
<IUnknown
, InterfaceT
>,
51 "template parameter of AgileReference must be a COM interface type");
54 AgileReference() = default;
55 ~AgileReference() = default;
57 AgileReference(const AgileReference
& aOther
) = default;
58 AgileReference(AgileReference
&& aOther
) noexcept
= default;
60 AgileReference
& operator=(const AgileReference
& aOther
) = default;
61 AgileReference
& operator=(AgileReference
&& aOther
) noexcept
= default;
63 AgileReference
& operator=(std::nullptr_t
) {
68 // Create a new AgileReference from an existing COM object.
70 // These constructors do not provide the HRESULT on failure. If that's
71 // desired, use `AgileReference::Create()`, below.
72 explicit AgileReference(InterfaceT
* aObject
) {
73 HRESULT
const hr
= detail::AgileReference_CreateImpl(
74 mAgileRef
, __uuidof(InterfaceT
), aObject
);
75 Unused
<< NS_WARN_IF(FAILED(hr
));
77 explicit AgileReference(RefPtr
<InterfaceT
> const& aObject
)
78 : AgileReference(aObject
.get()) {}
80 // Create a new AgileReference from an existing COM object, or alternatively,
81 // return the HRESULT explaining why one couldn't be created.
83 // A convenience wrapper `MakeAgileReference()` which infers `InterfaceT` from
84 // the RefPtr's concrete type is provided below.
85 static Result
<AgileReference
<InterfaceT
>, HRESULT
> Create(
86 RefPtr
<InterfaceT
> const& aObject
) {
88 HRESULT
const hr
= detail::AgileReference_CreateImpl(
89 ret
.mAgileRef
, __uuidof(InterfaceT
), aObject
.get());
96 explicit operator bool() const { return !!mAgileRef
; }
98 // Common case: resolve directly to the originally-specified interface-type.
99 RefPtr
<InterfaceT
> Resolve() const {
100 auto res
= ResolveAs
<InterfaceT
>();
101 if (res
.isErr()) return nullptr;
105 // Uncommon cases: resolve directly to a different interface type, and/or
106 // provide IAgileReference::Resolve()'s HRESULT.
108 // When used in other COM apartments, `IAgileInterface::Resolve()` returns a
109 // proxy object which (at time of writing) is not documented to provide any
110 // interface other than the one for which it was instantiated. (Calling
111 // `QueryInterface` _might_ work, but isn't explicitly guaranteed.)
113 template <typename OtherInterface
= InterfaceT
>
114 Result
<RefPtr
<OtherInterface
>, HRESULT
> ResolveAs() const {
115 RefPtr
<OtherInterface
> p
;
116 auto const hr
= ResolveRaw(__uuidof(OtherInterface
), getter_AddRefs(p
));
123 // Raw version of Resolve/ResolveAs. Rarely, if ever, preferable to the
124 // statically-typed versions.
125 HRESULT
ResolveRaw(REFIID aIid
, void** aOutInterface
) const {
126 return detail::AgileReference_ResolveImpl(mAgileRef
, aIid
, aOutInterface
);
130 RefPtr
<IAgileReference
> mAgileRef
;
133 // Attempt to create an AgileReference from a refcounted interface pointer,
134 // providing the HRESULT as a secondary return-value.
135 template <typename InterfaceT
>
136 inline Result
<AgileReference
<InterfaceT
>, HRESULT
> MakeAgileReference(
137 RefPtr
<InterfaceT
> const& aObj
) {
138 return AgileReference
<InterfaceT
>::Create(aObj
);
141 } // namespace mozilla::mscom
143 #endif // mozilla_mscom_AgileReference_h