|
9 | 9 |
|
10 | 10 | #include "mozilla/Attributes.h"
|
11 | 11 | #include "mozilla/RefPtr.h"
|
| 12 | +#include "mozilla/Result.h" |
| 13 | +#include "mozilla/Unused.h" |
| 14 | +#include "nsDebug.h" |
12 | 15 | #include "nsISupportsImpl.h"
|
13 | 16 |
|
14 | 17 | #include <objidl.h>
|
15 | 18 |
|
16 | 19 | namespace mozilla::mscom {
|
17 | 20 |
|
| 21 | +namespace detail { |
| 22 | +// Detemplatized implementation details of `AgileReference`. |
| 23 | +HRESULT AgileReference_CreateImpl(RefPtr<IAgileReference>&, REFIID, IUnknown*); |
| 24 | +HRESULT AgileReference_ResolveImpl(RefPtr<IAgileReference> const&, REFIID, |
| 25 | + void**); |
| 26 | +} // namespace detail |
| 27 | + |
18 | 28 | /**
|
19 |
| - * This class encapsulates an "agile reference." These are references that |
20 |
| - * allow you to pass COM interfaces between apartments. When you have an |
21 |
| - * interface that you would like to pass between apartments, you wrap that |
22 |
| - * interface in an AgileReference and pass the agile reference instead. Then |
23 |
| - * you unwrap the interface by calling AgileReference::Resolve. |
| 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 |
| 34 | + * interface. |
24 | 35 | *
|
25 | 36 | * Sample usage:
|
26 | 37 | *
|
27 |
| - * // In the multithreaded apartment, foo is an IFoo* |
28 |
| - * auto myAgileRef = AgileReference(IID_IFoo, foo); |
29 |
| - * |
30 |
| - * // myAgileRef is passed to our main thread, which runs in a single-threaded |
31 |
| - * // apartment: |
32 |
| - * |
33 |
| - * RefPtr<IFoo> foo; |
34 |
| - * HRESULT hr = myAgileRef.Resolve(IID_IFoo, getter_AddRefs(foo)); |
35 |
| - * // Now foo may be called from the main thread |
| 38 | + * ``` |
| 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` |
| 44 | + * }); |
| 45 | + * ``` |
36 | 46 | */
|
| 47 | +template <typename InterfaceT> |
37 | 48 | class AgileReference final {
|
38 |
| - public: |
39 |
| - AgileReference(); |
40 |
| - |
41 |
| - template <typename InterfaceT> |
42 |
| - explicit AgileReference(RefPtr<InterfaceT>& aObject) |
43 |
| - : AgileReference(__uuidof(InterfaceT), aObject) {} |
| 49 | + static_assert( |
| 50 | + std::is_base_of_v<IUnknown, InterfaceT>, |
| 51 | + "template parameter of AgileReference must be a COM interface type"); |
44 | 52 |
|
45 |
| - AgileReference(REFIID aIid, IUnknown* aObject); |
| 53 | + public: |
| 54 | + AgileReference() = default; |
| 55 | + ~AgileReference() = default; |
46 | 56 |
|
47 | 57 | AgileReference(const AgileReference& aOther) = default;
|
48 |
| - AgileReference(AgileReference&& aOther) noexcept; |
49 |
| - |
50 |
| - ~AgileReference(); |
| 58 | + AgileReference(AgileReference&& aOther) noexcept = default; |
51 | 59 |
|
52 |
| - explicit operator bool() const { return !!mAgileRef; } |
| 60 | + AgileReference& operator=(const AgileReference& aOther) = default; |
| 61 | + AgileReference& operator=(AgileReference&& aOther) noexcept = default; |
53 | 62 |
|
54 |
| - HRESULT GetHResult() const { return mHResult; } |
| 63 | + AgileReference& operator=(std::nullptr_t) { |
| 64 | + mAgileRef = nullptr; |
| 65 | + return *this; |
| 66 | + } |
55 | 67 |
|
56 |
| - template <typename T> |
57 |
| - void Assign(const RefPtr<T>& aOther) { |
58 |
| - Assign(__uuidof(T), aOther); |
| 68 | + // Create a new AgileReference from an existing COM object. |
| 69 | + // |
| 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)); |
| 76 | + } |
| 77 | + explicit AgileReference(RefPtr<InterfaceT> const& aObject) |
| 78 | + : AgileReference(aObject.get()) {} |
| 79 | + |
| 80 | + // Create a new AgileReference from an existing COM object, or alternatively, |
| 81 | + // return the HRESULT explaining why one couldn't be created. |
| 82 | + // |
| 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) { |
| 87 | + AgileReference ret; |
| 88 | + HRESULT const hr = detail::AgileReference_CreateImpl( |
| 89 | + ret.mAgileRef, __uuidof(InterfaceT), aObject.get()); |
| 90 | + if (FAILED(hr)) { |
| 91 | + return Err(hr); |
| 92 | + } |
| 93 | + return ret; |
59 | 94 | }
|
60 | 95 |
|
61 |
| - // Raw version, and implementation, of Resolve(). Can be used directly if |
62 |
| - // necessary, but in general, prefer one of the templated versions below |
63 |
| - // (depending on whether or not you need the HRESULT). |
64 |
| - HRESULT ResolveRaw(REFIID aIid, void** aOutInterface) const; |
| 96 | + explicit operator bool() const { return !!mAgileRef; } |
65 | 97 |
|
66 |
| - template <typename Interface> |
67 |
| - HRESULT Resolve(RefPtr<Interface>& aOutInterface) const { |
68 |
| - return this->ResolveRaw(__uuidof(Interface), getter_AddRefs(aOutInterface)); |
| 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; |
| 102 | + return res.unwrap(); |
69 | 103 | }
|
70 | 104 |
|
71 |
| - template <typename T> |
72 |
| - RefPtr<T> Resolve() { |
73 |
| - RefPtr<T> p; |
74 |
| - Resolve<T>(p); |
| 105 | + // Uncommon cases: resolve directly to a different interface type, and/or |
| 106 | + // provide IAgileReference::Resolve()'s HRESULT. |
| 107 | + // |
| 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.) |
| 112 | + // |
| 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)); |
| 117 | + if (FAILED(hr)) { |
| 118 | + return Err(hr); |
| 119 | + } |
75 | 120 | return p;
|
76 | 121 | }
|
77 | 122 |
|
78 |
| - AgileReference& operator=(const AgileReference& aOther); |
79 |
| - AgileReference& operator=(AgileReference&& aOther) noexcept; |
80 |
| - |
81 |
| - AgileReference& operator=(decltype(nullptr)) { |
82 |
| - Clear(); |
83 |
| - return *this; |
| 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); |
84 | 127 | }
|
85 | 128 |
|
86 |
| - void Clear(); |
87 |
| - |
88 |
| - private: |
89 |
| - void Assign(REFIID aIid, IUnknown* aObject); |
90 |
| - void AssignInternal(IUnknown* aObject); |
91 |
| - |
92 | 129 | private:
|
93 |
| - // The interface ID with which this reference was constructed. |
94 |
| - IID mIid; |
95 | 130 | RefPtr<IAgileReference> mAgileRef;
|
96 |
| - // The result associated with this reference's construction. May be modified |
97 |
| - // when mAgileRef changes, but is explicitly not touched by `Resolve`. |
98 |
| - HRESULT mHResult; |
99 | 131 | };
|
100 | 132 |
|
| 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); |
| 139 | +} |
| 140 | + |
101 | 141 | } // namespace mozilla::mscom
|
102 | 142 |
|
103 | 143 | #endif // mozilla_mscom_AgileReference_h
|
0 commit comments