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 // COM registration data structures are built with C code, so we need to
8 // simulate that in our C++ code by defining CINTERFACE before including
9 // anything else that could possibly pull in Windows header files.
12 #include "mozilla/mscom/Registration.h"
16 #include "mozilla/ArrayUtils.h"
17 #include "mozilla/Assertions.h"
18 #include "mozilla/RefPtr.h"
19 #include "mozilla/StaticPtr.h"
20 #include "mozilla/Vector.h"
21 #include "mozilla/mscom/ActivationContext.h"
22 #include "mozilla/mscom/Utils.h"
23 #include "nsWindowsHelpers.h"
25 #if defined(MOZILLA_INTERNAL_API)
26 # include "mozilla/ClearOnShutdown.h"
27 # include "mozilla/mscom/EnsureMTA.h"
28 HRESULT
RegisterPassthruProxy();
31 #endif // defined(MOZILLA_INTERNAL_API)
40 /* This code MUST NOT use any non-inlined internal Mozilla APIs, as it will be
41 compiled into DLLs that COM may load into non-Mozilla processes! */
45 // This function is defined in generated code for proxy DLLs but is not declared
46 // in rpcproxy.h, so we need this declaration.
47 void RPC_ENTRY
GetProxyDllInfo(const ProxyFileInfo
*** aInfo
, const CLSID
** aId
);
53 static bool GetContainingLibPath(wchar_t* aBuffer
, size_t aBufferLen
) {
54 HMODULE thisModule
= reinterpret_cast<HMODULE
>(GetContainingModuleHandle());
59 DWORD fileNameResult
= GetModuleFileName(thisModule
, aBuffer
, aBufferLen
);
60 if (!fileNameResult
|| (fileNameResult
== aBufferLen
&&
61 ::GetLastError() == ERROR_INSUFFICIENT_BUFFER
)) {
68 static bool BuildLibPath(RegistrationFlags aFlags
, wchar_t* aBuffer
,
69 size_t aBufferLen
, const wchar_t* aLeafName
) {
70 if (aFlags
== RegistrationFlags::eUseBinDirectory
) {
71 if (!GetContainingLibPath(aBuffer
, aBufferLen
)) {
75 if (!PathRemoveFileSpec(aBuffer
)) {
78 } else if (aFlags
== RegistrationFlags::eUseSystemDirectory
) {
79 UINT result
= GetSystemDirectoryW(aBuffer
, static_cast<UINT
>(aBufferLen
));
80 if (!result
|| result
> aBufferLen
) {
87 if (!PathAppend(aBuffer
, aLeafName
)) {
93 static bool RegisterPSClsids(const ProxyFileInfo
** aProxyInfo
,
94 const CLSID
* aProxyClsid
) {
96 const ProxyFileInfo
& curInfo
= **aProxyInfo
;
97 for (unsigned short idx
= 0, size
= curInfo
.TableSize
; idx
< size
; ++idx
) {
98 HRESULT hr
= CoRegisterPSClsid(*(curInfo
.pStubVtblList
[idx
]->header
.piid
),
110 #if !defined(MOZILLA_INTERNAL_API)
111 using GetProxyDllInfoFnT
= decltype(&GetProxyDllInfo
);
113 static GetProxyDllInfoFnT
ResolveGetProxyDllInfo() {
114 HMODULE thisModule
= reinterpret_cast<HMODULE
>(GetContainingModuleHandle());
119 return reinterpret_cast<GetProxyDllInfoFnT
>(
120 GetProcAddress(thisModule
, "GetProxyDllInfo"));
122 #endif // !defined(MOZILLA_INTERNAL_API)
124 UniquePtr
<RegisteredProxy
> RegisterProxy() {
125 #if !defined(MOZILLA_INTERNAL_API)
126 GetProxyDllInfoFnT GetProxyDllInfoFn
= ResolveGetProxyDllInfo();
127 MOZ_ASSERT(!!GetProxyDllInfoFn
);
128 if (!GetProxyDllInfoFn
) {
131 #endif // !defined(MOZILLA_INTERNAL_API)
133 const ProxyFileInfo
** proxyInfo
= nullptr;
134 const CLSID
* proxyClsid
= nullptr;
135 #if defined(MOZILLA_INTERNAL_API)
136 GetProxyDllInfo(&proxyInfo
, &proxyClsid
);
138 GetProxyDllInfoFn(&proxyInfo
, &proxyClsid
);
139 #endif // defined(MOZILLA_INTERNAL_API)
140 if (!proxyInfo
|| !proxyClsid
) {
144 IUnknown
* classObject
= nullptr;
146 DllGetClassObject(*proxyClsid
, IID_IUnknown
, (void**)&classObject
);
152 hr
= CoRegisterClassObject(*proxyClsid
, classObject
, CLSCTX_INPROC_SERVER
,
153 REGCLS_MULTIPLEUSE
, ®Cookie
);
155 classObject
->lpVtbl
->Release(classObject
);
159 wchar_t modulePathBuf
[MAX_PATH
+ 1] = {0};
160 if (!GetContainingLibPath(modulePathBuf
, ArrayLength(modulePathBuf
))) {
161 CoRevokeClassObject(regCookie
);
162 classObject
->lpVtbl
->Release(classObject
);
166 ITypeLib
* typeLib
= nullptr;
167 hr
= LoadTypeLibEx(modulePathBuf
, REGKIND_NONE
, &typeLib
);
168 MOZ_ASSERT(SUCCEEDED(hr
));
170 CoRevokeClassObject(regCookie
);
171 classObject
->lpVtbl
->Release(classObject
);
175 #if defined(MOZILLA_INTERNAL_API)
176 hr
= RegisterPassthruProxy();
177 MOZ_ASSERT(SUCCEEDED(hr
));
179 CoRevokeClassObject(regCookie
);
180 classObject
->lpVtbl
->Release(classObject
);
183 #endif // defined(MOZILLA_INTERNAL_API)
185 // RegisteredProxy takes ownership of classObject and typeLib references
186 auto result(MakeUnique
<RegisteredProxy
>(classObject
, regCookie
, typeLib
));
188 if (!RegisterPSClsids(proxyInfo
, proxyClsid
)) {
195 UniquePtr
<RegisteredProxy
> RegisterProxy(const wchar_t* aLeafName
,
196 RegistrationFlags aFlags
) {
197 wchar_t modulePathBuf
[MAX_PATH
+ 1] = {0};
198 if (!BuildLibPath(aFlags
, modulePathBuf
, ArrayLength(modulePathBuf
),
203 nsModuleHandle
proxyDll(LoadLibrary(modulePathBuf
));
204 if (!proxyDll
.get()) {
208 // Instantiate an activation context so that CoGetClassObject will use any
209 // COM metadata embedded in proxyDll's manifest to resolve CLSIDs.
210 ActivationContextRegion
actCtxRgn(proxyDll
.get());
212 auto GetProxyDllInfoFn
= reinterpret_cast<decltype(&GetProxyDllInfo
)>(
213 GetProcAddress(proxyDll
, "GetProxyDllInfo"));
214 if (!GetProxyDllInfoFn
) {
218 const ProxyFileInfo
** proxyInfo
= nullptr;
219 const CLSID
* proxyClsid
= nullptr;
220 GetProxyDllInfoFn(&proxyInfo
, &proxyClsid
);
221 if (!proxyInfo
|| !proxyClsid
) {
225 // We call CoGetClassObject instead of DllGetClassObject because it forces
226 // the COM runtime to manage the lifetime of the DLL.
227 IUnknown
* classObject
= nullptr;
228 HRESULT hr
= CoGetClassObject(*proxyClsid
, CLSCTX_INPROC_SERVER
, nullptr,
229 IID_IUnknown
, (void**)&classObject
);
235 hr
= CoRegisterClassObject(*proxyClsid
, classObject
, CLSCTX_INPROC_SERVER
,
236 REGCLS_MULTIPLEUSE
, ®Cookie
);
238 classObject
->lpVtbl
->Release(classObject
);
242 ITypeLib
* typeLib
= nullptr;
243 hr
= LoadTypeLibEx(modulePathBuf
, REGKIND_NONE
, &typeLib
);
244 MOZ_ASSERT(SUCCEEDED(hr
));
246 CoRevokeClassObject(regCookie
);
247 classObject
->lpVtbl
->Release(classObject
);
251 // RegisteredProxy takes ownership of proxyDll, classObject, and typeLib
253 auto result(MakeUnique
<RegisteredProxy
>(
254 reinterpret_cast<uintptr_t>(proxyDll
.disown()), classObject
, regCookie
,
257 if (!RegisterPSClsids(proxyInfo
, proxyClsid
)) {
264 UniquePtr
<RegisteredProxy
> RegisterTypelib(const wchar_t* aLeafName
,
265 RegistrationFlags aFlags
) {
266 wchar_t modulePathBuf
[MAX_PATH
+ 1] = {0};
267 if (!BuildLibPath(aFlags
, modulePathBuf
, ArrayLength(modulePathBuf
),
272 ITypeLib
* typeLib
= nullptr;
273 HRESULT hr
= LoadTypeLibEx(modulePathBuf
, REGKIND_NONE
, &typeLib
);
278 // RegisteredProxy takes ownership of typeLib reference
279 auto result(MakeUnique
<RegisteredProxy
>(typeLib
));
283 RegisteredProxy::RegisteredProxy(uintptr_t aModule
, IUnknown
* aClassObject
,
284 uint32_t aRegCookie
, ITypeLib
* aTypeLib
)
286 mClassObject(aClassObject
),
287 mRegCookie(aRegCookie
),
289 #if defined(MOZILLA_INTERNAL_API)
291 mIsRegisteredInMTA(IsCurrentThreadMTA())
292 #endif // defined(MOZILLA_INTERNAL_API)
294 MOZ_ASSERT(aClassObject
);
295 MOZ_ASSERT(aTypeLib
);
299 RegisteredProxy::RegisteredProxy(IUnknown
* aClassObject
, uint32_t aRegCookie
,
302 mClassObject(aClassObject
),
303 mRegCookie(aRegCookie
),
305 #if defined(MOZILLA_INTERNAL_API)
307 mIsRegisteredInMTA(IsCurrentThreadMTA())
308 #endif // defined(MOZILLA_INTERNAL_API)
310 MOZ_ASSERT(aClassObject
);
311 MOZ_ASSERT(aTypeLib
);
315 // If we're initializing from a typelib, it doesn't matter which apartment we
316 // run in, so mIsRegisteredInMTA may always be set to false in this case.
317 RegisteredProxy::RegisteredProxy(ITypeLib
* aTypeLib
)
319 mClassObject(nullptr),
322 #if defined(MOZILLA_INTERNAL_API)
324 mIsRegisteredInMTA(false)
325 #endif // defined(MOZILLA_INTERNAL_API)
327 MOZ_ASSERT(aTypeLib
);
331 void RegisteredProxy::Clear() {
333 mTypeLib
->lpVtbl
->Release(mTypeLib
);
337 // NB: mClassObject and mRegCookie must be freed from inside the apartment
338 // which they were created in.
339 auto cleanupFn
= [&]() -> void {
340 ::CoRevokeClassObject(mRegCookie
);
342 mClassObject
->lpVtbl
->Release(mClassObject
);
343 mClassObject
= nullptr;
345 #if defined(MOZILLA_INTERNAL_API)
346 // This code only supports MTA when built internally
347 if (mIsRegisteredInMTA
) {
348 EnsureMTA
mta(cleanupFn
);
354 #endif // defined(MOZILLA_INTERNAL_API)
357 ::FreeLibrary(reinterpret_cast<HMODULE
>(mModule
));
362 RegisteredProxy::~RegisteredProxy() {
363 DeleteFromRegistry(this);
367 RegisteredProxy::RegisteredProxy(RegisteredProxy
&& aOther
)
369 mClassObject(nullptr),
372 #if defined(MOZILLA_INTERNAL_API)
374 mIsRegisteredInMTA(false)
375 #endif // defined(MOZILLA_INTERNAL_API)
377 *this = std::forward
<RegisteredProxy
>(aOther
);
381 RegisteredProxy
& RegisteredProxy::operator=(RegisteredProxy
&& aOther
) {
384 mModule
= aOther
.mModule
;
386 mClassObject
= aOther
.mClassObject
;
387 aOther
.mClassObject
= nullptr;
388 mRegCookie
= aOther
.mRegCookie
;
389 aOther
.mRegCookie
= 0;
390 mTypeLib
= aOther
.mTypeLib
;
391 aOther
.mTypeLib
= nullptr;
393 #if defined(MOZILLA_INTERNAL_API)
394 mIsRegisteredInMTA
= aOther
.mIsRegisteredInMTA
;
395 #endif // defined(MOZILLA_INTERNAL_API)
401 RegisteredProxy::GetTypeInfoForGuid(REFGUID aGuid
,
402 ITypeInfo
** aOutTypeInfo
) const {
409 return mTypeLib
->lpVtbl
->GetTypeInfoOfGuid(mTypeLib
, aGuid
, aOutTypeInfo
);
412 static StaticAutoPtr
<Vector
<RegisteredProxy
*>> sRegistry
;
414 namespace UseGetMutexForAccess
{
416 // This must not be accessed directly; use GetMutex() instead
417 static CRITICAL_SECTION sMutex
;
419 } // namespace UseGetMutexForAccess
421 static CRITICAL_SECTION
* GetMutex() {
422 static CRITICAL_SECTION
& mutex
= []() -> CRITICAL_SECTION
& {
423 #if defined(RELEASE_OR_BETA)
424 DWORD flags
= CRITICAL_SECTION_NO_DEBUG_INFO
;
428 InitializeCriticalSectionEx(&UseGetMutexForAccess::sMutex
, 4000, flags
);
429 #if !defined(MOZILLA_INTERNAL_API)
430 atexit([]() { DeleteCriticalSection(&UseGetMutexForAccess::sMutex
); });
432 return UseGetMutexForAccess::sMutex
;
438 bool RegisteredProxy::Find(REFIID aIid
, ITypeInfo
** aTypeInfo
) {
439 AutoCriticalSection
lock(GetMutex());
445 for (auto&& proxy
: *sRegistry
) {
446 if (SUCCEEDED(proxy
->GetTypeInfoForGuid(aIid
, aTypeInfo
))) {
455 void RegisteredProxy::AddToRegistry(RegisteredProxy
* aProxy
) {
458 AutoCriticalSection
lock(GetMutex());
461 sRegistry
= new Vector
<RegisteredProxy
*>();
463 #if !defined(MOZILLA_INTERNAL_API)
464 // sRegistry allocation is fallible outside of Mozilla processes
471 MOZ_ALWAYS_TRUE(sRegistry
->emplaceBack(aProxy
));
475 void RegisteredProxy::DeleteFromRegistry(RegisteredProxy
* aProxy
) {
478 AutoCriticalSection
lock(GetMutex());
480 MOZ_ASSERT(sRegistry
&& !sRegistry
->empty());
486 sRegistry
->erase(std::remove(sRegistry
->begin(), sRegistry
->end(), aProxy
),
489 if (sRegistry
->empty()) {
494 #if defined(MOZILLA_INTERNAL_API)
496 static StaticAutoPtr
<Vector
<std::pair
<const ArrayData
*, size_t>>> sArrayData
;
498 void RegisterArrayData(const ArrayData
* aArrayData
, size_t aLength
) {
499 AutoCriticalSection
lock(GetMutex());
502 sArrayData
= new Vector
<std::pair
<const ArrayData
*, size_t>>();
503 ClearOnShutdown(&sArrayData
, ShutdownPhase::XPCOMShutdownThreads
);
506 MOZ_ALWAYS_TRUE(sArrayData
->emplaceBack(std::make_pair(aArrayData
, aLength
)));
509 const ArrayData
* FindArrayData(REFIID aIid
, ULONG aMethodIndex
) {
510 AutoCriticalSection
lock(GetMutex());
516 for (auto&& data
: *sArrayData
) {
517 for (size_t innerIdx
= 0, innerLen
= data
.second
; innerIdx
< innerLen
;
519 const ArrayData
* array
= data
.first
;
520 if (aMethodIndex
== array
[innerIdx
].mMethodIndex
&&
521 IsInterfaceEqualToOrInheritedFrom(aIid
, array
[innerIdx
].mIid
,
523 return &array
[innerIdx
];
531 #endif // defined(MOZILLA_INTERNAL_API)
534 } // namespace mozilla