AgileReference.h (5019B)
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/. */ 6 7 #ifndef mozilla_mscom_AgileReference_h 8 #define mozilla_mscom_AgileReference_h 9 10 #include "mozilla/RefPtr.h" 11 #include "mozilla/Result.h" 12 #include "nsDebug.h" 13 #include "nsISupportsImpl.h" 14 15 #include <objidl.h> 16 17 namespace mozilla::mscom { 18 19 namespace detail { 20 // Detemplatized implementation details of `AgileReference`. 21 HRESULT AgileReference_CreateImpl(RefPtr<IAgileReference>&, REFIID, IUnknown*); 22 HRESULT AgileReference_ResolveImpl(RefPtr<IAgileReference> const&, REFIID, 23 void**); 24 } // namespace detail 25 26 /** 27 * This class encapsulates an "agile reference". These are references that allow 28 * you to pass COM interfaces between apartments. When you have an interface 29 * that you would like to pass between apartments, you wrap that interface in an 30 * AgileReference and pass that instead. Then you can "unwrap" the interface by 31 * calling Resolve(), which will return a proxy object implementing the same 32 * interface. 33 * 34 * Sample usage: 35 * 36 * ``` 37 * // From a non-main thread, where `foo` is an `IFoo*` or `RefPtr<IFoo>`: 38 * auto myAgileRef = AgileReference(foo); 39 * NS_DispatchToMainThread([mar = std::move(myAgileRef)] { 40 * RefPtr<IFoo> foo = mar.Resolve(); 41 * // Now methods may be invoked on `foo` 42 * }); 43 * ``` 44 */ 45 template <typename InterfaceT> 46 class AgileReference final { 47 static_assert( 48 std::is_base_of_v<IUnknown, InterfaceT>, 49 "template parameter of AgileReference must be a COM interface type"); 50 51 public: 52 AgileReference() = default; 53 ~AgileReference() = default; 54 55 AgileReference(const AgileReference& aOther) = default; 56 AgileReference(AgileReference&& aOther) noexcept = default; 57 58 AgileReference& operator=(const AgileReference& aOther) = default; 59 AgileReference& operator=(AgileReference&& aOther) noexcept = default; 60 61 AgileReference& operator=(std::nullptr_t) { 62 mAgileRef = nullptr; 63 return *this; 64 } 65 66 // Create a new AgileReference from an existing COM object. 67 // 68 // These constructors do not provide the HRESULT on failure. If that's 69 // desired, use `AgileReference::Create()`, below. 70 explicit AgileReference(InterfaceT* aObject) { 71 HRESULT const hr = detail::AgileReference_CreateImpl( 72 mAgileRef, __uuidof(InterfaceT), aObject); 73 (void)NS_WARN_IF(FAILED(hr)); 74 } 75 explicit AgileReference(RefPtr<InterfaceT> const& aObject) 76 : AgileReference(aObject.get()) {} 77 78 // Create a new AgileReference from an existing COM object, or alternatively, 79 // return the HRESULT explaining why one couldn't be created. 80 // 81 // A convenience wrapper `MakeAgileReference()` which infers `InterfaceT` from 82 // the RefPtr's concrete type is provided below. 83 static Result<AgileReference<InterfaceT>, HRESULT> Create( 84 RefPtr<InterfaceT> const& aObject) { 85 AgileReference ret; 86 HRESULT const hr = detail::AgileReference_CreateImpl( 87 ret.mAgileRef, __uuidof(InterfaceT), aObject.get()); 88 if (FAILED(hr)) { 89 return Err(hr); 90 } 91 return ret; 92 } 93 94 explicit operator bool() const { return !!mAgileRef; } 95 96 // Common case: resolve directly to the originally-specified interface-type. 97 RefPtr<InterfaceT> Resolve() const { 98 auto res = ResolveAs<InterfaceT>(); 99 if (res.isErr()) return nullptr; 100 return res.unwrap(); 101 } 102 103 // Uncommon cases: resolve directly to a different interface type, and/or 104 // provide IAgileReference::Resolve()'s HRESULT. 105 // 106 // When used in other COM apartments, `IAgileInterface::Resolve()` returns a 107 // proxy object which (at time of writing) is not documented to provide any 108 // interface other than the one for which it was instantiated. (Calling 109 // `QueryInterface` _might_ work, but isn't explicitly guaranteed.) 110 // 111 template <typename OtherInterface = InterfaceT> 112 Result<RefPtr<OtherInterface>, HRESULT> ResolveAs() const { 113 RefPtr<OtherInterface> p; 114 auto const hr = ResolveRaw(__uuidof(OtherInterface), getter_AddRefs(p)); 115 if (FAILED(hr)) { 116 return Err(hr); 117 } 118 return p; 119 } 120 121 // Raw version of Resolve/ResolveAs. Rarely, if ever, preferable to the 122 // statically-typed versions. 123 HRESULT ResolveRaw(REFIID aIid, void** aOutInterface) const { 124 return detail::AgileReference_ResolveImpl(mAgileRef, aIid, aOutInterface); 125 } 126 127 private: 128 RefPtr<IAgileReference> mAgileRef; 129 }; 130 131 // Attempt to create an AgileReference from a refcounted interface pointer, 132 // providing the HRESULT as a secondary return-value. 133 template <typename InterfaceT> 134 inline Result<AgileReference<InterfaceT>, HRESULT> MakeAgileReference( 135 RefPtr<InterfaceT> const& aObj) { 136 return AgileReference<InterfaceT>::Create(aObj); 137 } 138 139 } // namespace mozilla::mscom 140 141 #endif // mozilla_mscom_AgileReference_h