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 #include "mozilla/mscom/AgileReference.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
14 #include "mozilla/mscom/Utils.h"
16 #if defined(MOZILLA_INTERNAL_API)
18 # include "nsPrintfCString.h"
19 #endif // defined(MOZILLA_INTERNAL_API)
21 #if defined(__MINGW32__)
23 // Declarations from Windows SDK specific to Windows 8.1
25 enum AgileReferenceOptions
{
26 AGILEREFERENCE_DEFAULT
= 0,
27 AGILEREFERENCE_DELAYEDMARSHAL
= 1,
30 HRESULT WINAPI
RoGetAgileReference(AgileReferenceOptions options
, REFIID riid
,
32 IAgileReference
** ppAgileReference
);
34 #endif // defined(__MINGW32__)
40 GlobalInterfaceTableCookie::GlobalInterfaceTableCookie(IUnknown
* aObject
,
44 IGlobalInterfaceTable
* git
= ObtainGit();
47 aOutHResult
= E_POINTER
;
51 aOutHResult
= git
->RegisterInterfaceInGlobal(aObject
, aIid
, &mCookie
);
52 MOZ_ASSERT(SUCCEEDED(aOutHResult
));
55 GlobalInterfaceTableCookie::~GlobalInterfaceTableCookie() {
56 IGlobalInterfaceTable
* git
= ObtainGit();
62 DebugOnly
<HRESULT
> hr
= git
->RevokeInterfaceFromGlobal(mCookie
);
63 #if defined(MOZILLA_INTERNAL_API)
66 nsPrintfCString("IGlobalInterfaceTable::RevokeInterfaceFromGlobal failed "
67 "with HRESULT 0x%08lX",
71 MOZ_ASSERT(SUCCEEDED(hr
));
72 #endif // defined(MOZILLA_INTERNAL_API)
76 HRESULT
GlobalInterfaceTableCookie::GetInterface(REFIID aIid
,
77 void** aOutInterface
) const {
78 IGlobalInterfaceTable
* git
= ObtainGit();
84 MOZ_ASSERT(IsValid());
85 return git
->GetInterfaceFromGlobal(mCookie
, aIid
, aOutInterface
);
89 IGlobalInterfaceTable
* GlobalInterfaceTableCookie::ObtainGit() {
90 // Internally to COM, the Global Interface Table is a singleton, therefore we
91 // don't worry about holding onto this reference indefinitely.
92 static IGlobalInterfaceTable
* sGit
= []() -> IGlobalInterfaceTable
* {
93 IGlobalInterfaceTable
* result
= nullptr;
94 DebugOnly
<HRESULT
> hr
= ::CoCreateInstance(
95 CLSID_StdGlobalInterfaceTable
, nullptr, CLSCTX_INPROC_SERVER
,
96 IID_IGlobalInterfaceTable
, reinterpret_cast<void**>(&result
));
97 MOZ_ASSERT(SUCCEEDED(hr
));
104 } // namespace detail
106 AgileReference::AgileReference() : mIid(), mHResult(E_NOINTERFACE
) {}
108 AgileReference::AgileReference(REFIID aIid
, IUnknown
* aObject
)
109 : mIid(aIid
), mHResult(E_UNEXPECTED
) {
110 AssignInternal(aObject
);
113 AgileReference::AgileReference(AgileReference
&& aOther
)
115 mAgileRef(std::move(aOther
.mAgileRef
)),
116 mGitCookie(std::move(aOther
.mGitCookie
)),
117 mHResult(aOther
.mHResult
) {
118 aOther
.mHResult
= CO_E_RELEASED
;
121 void AgileReference::Assign(REFIID aIid
, IUnknown
* aObject
) {
124 AssignInternal(aObject
);
127 void AgileReference::AssignInternal(IUnknown
* aObject
) {
128 // We expect mIid to already be set
129 DebugOnly
<IID
> zeroIid
= {};
130 MOZ_ASSERT(mIid
!= zeroIid
);
133 * There are two possible techniques for creating agile references. Starting
134 * with Windows 8.1, we may use the RoGetAgileReference API, which is faster.
135 * If that API is not available, we fall back to using the Global Interface
138 static const StaticDynamicallyLinkedFunctionPtr
<
139 decltype(&::RoGetAgileReference
)>
140 pRoGetAgileReference(L
"ole32.dll", "RoGetAgileReference");
144 if (pRoGetAgileReference
&&
146 pRoGetAgileReference(AGILEREFERENCE_DEFAULT
, mIid
, aObject
,
147 getter_AddRefs(mAgileRef
)))) {
151 mGitCookie
= new detail::GlobalInterfaceTableCookie(aObject
, mIid
, mHResult
);
152 MOZ_ASSERT(mGitCookie
->IsValid());
155 AgileReference::~AgileReference() { Clear(); }
157 void AgileReference::Clear() {
160 mGitCookie
= nullptr;
161 mHResult
= E_NOINTERFACE
;
164 AgileReference
& AgileReference::operator=(const AgileReference
& aOther
) {
167 mAgileRef
= aOther
.mAgileRef
;
168 mGitCookie
= aOther
.mGitCookie
;
169 mHResult
= aOther
.mHResult
;
173 AgileReference
& AgileReference::operator=(AgileReference
&& aOther
) {
176 mAgileRef
= std::move(aOther
.mAgileRef
);
177 mGitCookie
= std::move(aOther
.mGitCookie
);
178 mHResult
= aOther
.mHResult
;
179 aOther
.mHResult
= CO_E_RELEASED
;
184 AgileReference::Resolve(REFIID aIid
, void** aOutInterface
) const {
185 MOZ_ASSERT(aOutInterface
);
186 // This check is exclusive-OR; we should have one or the other, but not both
187 MOZ_ASSERT((mAgileRef
|| mGitCookie
) && !(mAgileRef
&& mGitCookie
));
188 MOZ_ASSERT(IsCOMInitializedOnCurrentThread());
190 if (!aOutInterface
) {
194 *aOutInterface
= nullptr;
197 // IAgileReference lets you directly resolve the interface you want...
198 return mAgileRef
->Resolve(aIid
, aOutInterface
);
205 RefPtr
<IUnknown
> originalInterface
;
207 mGitCookie
->GetInterface(mIid
, getter_AddRefs(originalInterface
));
213 originalInterface
.forget(aOutInterface
);
217 // ...Whereas the GIT requires us to obtain the same interface that we
218 // requested and then QI for the desired interface afterward.
219 return originalInterface
->QueryInterface(aIid
, aOutInterface
);
223 } // namespace mozilla