diff options
Diffstat (limited to 'ipc/mscom/oop')
-rw-r--r-- | ipc/mscom/oop/Factory.h | 142 | ||||
-rw-r--r-- | ipc/mscom/oop/Handler.cpp | 281 | ||||
-rw-r--r-- | ipc/mscom/oop/Handler.h | 134 | ||||
-rw-r--r-- | ipc/mscom/oop/Module.cpp | 292 | ||||
-rw-r--r-- | ipc/mscom/oop/Module.h | 98 | ||||
-rw-r--r-- | ipc/mscom/oop/moz.build | 43 |
6 files changed, 990 insertions, 0 deletions
diff --git a/ipc/mscom/oop/Factory.h b/ipc/mscom/oop/Factory.h new file mode 100644 index 0000000000..e95f1d2499 --- /dev/null +++ b/ipc/mscom/oop/Factory.h @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_mscom_Factory_h +#define mozilla_mscom_Factory_h + +#if defined(MOZILLA_INTERNAL_API) +# error This code is NOT for internal Gecko use! +#endif // defined(MOZILLA_INTERNAL_API) + +#include <objbase.h> +#include <unknwn.h> + +#include <utility> + +#include "Module.h" +#include "mozilla/Attributes.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/RefPtr.h" +#include "mozilla/StaticPtr.h" + +/* WARNING! The code in this file may be loaded into the address spaces of other + processes! It MUST NOT link against xul.dll or other Gecko binaries! Only + inline code may be included! */ + +namespace mozilla { +namespace mscom { + +template <typename T> +class MOZ_NONHEAP_CLASS Factory : public IClassFactory { + template <typename... Args> + HRESULT DoCreate(Args&&... args) { + MOZ_DIAGNOSTIC_ASSERT(false, "This should not be executed"); + return E_NOTIMPL; + } + + template <typename... Args> + HRESULT DoCreate(HRESULT (*aFnPtr)(IUnknown*, REFIID, void**), + Args&&... args) { + return aFnPtr(std::forward<Args>(args)...); + } + + public: + // IUnknown + STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override { + if (!aOutInterface) { + return E_INVALIDARG; + } + + if (aIid == IID_IUnknown || aIid == IID_IClassFactory) { + RefPtr<IClassFactory> punk(this); + punk.forget(aOutInterface); + return S_OK; + } + + *aOutInterface = nullptr; + + return E_NOINTERFACE; + } + + STDMETHODIMP_(ULONG) AddRef() override { + Module::Lock(); + return 2; + } + + STDMETHODIMP_(ULONG) Release() override { + Module::Unlock(); + return 1; + } + + // IClassFactory + STDMETHODIMP CreateInstance(IUnknown* aOuter, REFIID aIid, + void** aOutInterface) override { + return DoCreate(&T::Create, aOuter, aIid, aOutInterface); + } + + STDMETHODIMP LockServer(BOOL aLock) override { + if (aLock) { + Module::Lock(); + } else { + Module::Unlock(); + } + return S_OK; + } +}; + +template <typename T> +class MOZ_NONHEAP_CLASS SingletonFactory : public Factory<T> { + public: + STDMETHODIMP CreateInstance(IUnknown* aOuter, REFIID aIid, + void** aOutInterface) override { + if (aOuter || !aOutInterface) { + return E_INVALIDARG; + } + + RefPtr<T> obj(sInstance); + if (!obj) { + obj = GetOrCreateSingleton(); + } + + return obj->QueryInterface(aIid, aOutInterface); + } + + RefPtr<T> GetOrCreateSingleton() { + if (!sInstance) { + RefPtr<T> object; + if (FAILED(T::Create(getter_AddRefs(object)))) { + return nullptr; + } + + sInstance = object.forget(); + } + + return sInstance; + } + + RefPtr<T> GetSingleton() { return sInstance; } + + void ClearSingleton() { + if (!sInstance) { + return; + } + + DebugOnly<HRESULT> hr = ::CoDisconnectObject(sInstance.get(), 0); + MOZ_ASSERT(SUCCEEDED(hr)); + sInstance = nullptr; + } + + private: + static StaticRefPtr<T> sInstance; +}; + +template <typename T> +StaticRefPtr<T> SingletonFactory<T>::sInstance; + +} // namespace mscom +} // namespace mozilla + +#endif // mozilla_mscom_Factory_h diff --git a/ipc/mscom/oop/Handler.cpp b/ipc/mscom/oop/Handler.cpp new file mode 100644 index 0000000000..1c40e5300f --- /dev/null +++ b/ipc/mscom/oop/Handler.cpp @@ -0,0 +1,281 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Handler.h" +#include "Module.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/mscom/Objref.h" +#include "nsWindowsHelpers.h" + +#include <objbase.h> +#include <shlwapi.h> +#include <string.h> + +/* WARNING! The code in this file may be loaded into the address spaces of other + processes! It MUST NOT link against xul.dll or other Gecko binaries! Only + inline code may be included! */ + +namespace mozilla { +namespace mscom { + +Handler::Handler(IUnknown* aOuter, HRESULT* aResult) + : mRefCnt(0), mOuter(aOuter), mUnmarshal(nullptr), mHasPayload(false) { + MOZ_ASSERT(aResult); + + if (!aOuter) { + *aResult = E_INVALIDARG; + return; + } + + StabilizedRefCount<ULONG> stabilizer(mRefCnt); + + *aResult = + ::CoGetStdMarshalEx(aOuter, SMEXF_HANDLER, getter_AddRefs(mInnerUnk)); + if (FAILED(*aResult)) { + return; + } + + *aResult = mInnerUnk->QueryInterface(IID_IMarshal, (void**)&mUnmarshal); + if (FAILED(*aResult)) { + return; + } + + // mUnmarshal is a weak ref + mUnmarshal->Release(); +} + +HRESULT +Handler::InternalQueryInterface(REFIID riid, void** ppv) { + if (!ppv) { + return E_INVALIDARG; + } + + if (riid == IID_IUnknown) { + RefPtr<IUnknown> punk(static_cast<IUnknown*>(&mInternalUnknown)); + punk.forget(ppv); + return S_OK; + } + + if (riid == IID_IMarshal) { + RefPtr<IMarshal> ptr(this); + ptr.forget(ppv); + return S_OK; + } + + // Try the handler implementation + HRESULT hr = QueryHandlerInterface(mInnerUnk, riid, ppv); + if (hr == S_FALSE) { + // The handler knows this interface is not available, so don't bother + // asking the proxy. + return E_NOINTERFACE; + } + if (hr != E_NOINTERFACE) { + return hr; + } + + // Now forward to the marshaler's inner + return mInnerUnk->QueryInterface(riid, ppv); +} + +ULONG +Handler::InternalAddRef() { + if (!mRefCnt) { + Module::Lock(); + } + return ++mRefCnt; +} + +ULONG +Handler::InternalRelease() { + ULONG newRefCnt = --mRefCnt; + if (newRefCnt == 0) { + delete this; + Module::Unlock(); + } + return newRefCnt; +} + +HRESULT +Handler::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext, + void* pvDestContext, DWORD mshlflags, CLSID* pCid) { + return mUnmarshal->GetUnmarshalClass(MarshalAs(riid), pv, dwDestContext, + pvDestContext, mshlflags, pCid); +} + +HRESULT +Handler::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext, + void* pvDestContext, DWORD mshlflags, DWORD* pSize) { + if (!pSize) { + return E_INVALIDARG; + } + + *pSize = 0; + + RefPtr<IUnknown> unkToMarshal; + HRESULT hr; + + REFIID marshalAs = MarshalAs(riid); + if (marshalAs == riid) { + unkToMarshal = static_cast<IUnknown*>(pv); + } else { + hr = mInnerUnk->QueryInterface(marshalAs, getter_AddRefs(unkToMarshal)); + if (FAILED(hr)) { + return hr; + } + } + + // We do not necessarily want to use the pv that COM is giving us; we may want + // to marshal a different proxy that is more appropriate to what we're + // wrapping... + hr = mUnmarshal->GetMarshalSizeMax(marshalAs, unkToMarshal.get(), + dwDestContext, pvDestContext, mshlflags, + pSize); + +#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER) + return hr; +#else + if (FAILED(hr)) { + return hr; + } + + if (!HasPayload()) { + return S_OK; + } + + DWORD payloadSize = 0; + hr = GetHandlerPayloadSize(marshalAs, &payloadSize); + if (FAILED(hr)) { + return hr; + } + + *pSize += payloadSize; + return S_OK; +#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER) +} + +HRESULT +Handler::GetMarshalInterface(REFIID aMarshalAsIid, NotNull<IUnknown*> aProxy, + NotNull<IID*> aOutIid, + NotNull<IUnknown**> aOutUnk) { + *aOutIid = aMarshalAsIid; + return aProxy->QueryInterface( + aMarshalAsIid, + reinterpret_cast<void**>(static_cast<IUnknown**>(aOutUnk))); +} + +HRESULT +Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv, + DWORD dwDestContext, void* pvDestContext, + DWORD mshlflags) { + // We do not necessarily want to use the pv that COM is giving us; we may want + // to marshal a different proxy that is more appropriate to what we're + // wrapping... + RefPtr<IUnknown> unkToMarshal; + HRESULT hr; + +#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER) + LARGE_INTEGER seekTo; + seekTo.QuadPart = 0; + + ULARGE_INTEGER objrefPos; + + // Save the current position as it points to the location where the OBJREF + // will be written. + hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos); + if (FAILED(hr)) { + return hr; + } +#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER) + + REFIID marshalAs = MarshalAs(riid); + IID marshalOutAs; + + hr = GetMarshalInterface( + marshalAs, WrapNotNull<IUnknown*>(mInnerUnk), WrapNotNull(&marshalOutAs), + WrapNotNull<IUnknown**>(getter_AddRefs(unkToMarshal))); + if (FAILED(hr)) { + return hr; + } + + hr = mUnmarshal->MarshalInterface(pStm, marshalAs, unkToMarshal.get(), + dwDestContext, pvDestContext, mshlflags); + if (FAILED(hr)) { + return hr; + } + +#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER) + // Obtain the current stream position which is the end of the OBJREF + ULARGE_INTEGER endPos; + hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &endPos); + if (FAILED(hr)) { + return hr; + } + + // Now strip out the handler. + if (!StripHandlerFromOBJREF(WrapNotNull(pStm), objrefPos.QuadPart, + endPos.QuadPart)) { + return E_FAIL; + } + + // Fix the IID + if (!SetIID(WrapNotNull(pStm), objrefPos.QuadPart, marshalOutAs)) { + return E_FAIL; + } + + return S_OK; +#else + if (!HasPayload()) { + return S_OK; + } + + // Unfortunately when COM re-marshals a proxy that prevouisly had a payload, + // we must re-serialize it. + return WriteHandlerPayload(pStm, marshalAs); +#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER) +} + +HRESULT +Handler::UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv) { + REFIID unmarshalAs = MarshalAs(riid); + HRESULT hr = mUnmarshal->UnmarshalInterface(pStm, unmarshalAs, ppv); + if (FAILED(hr)) { + return hr; + } + + // This method may be called on the same object multiple times (as new + // interfaces are queried off the proxy). Not all interfaces will necessarily + // refresh the payload, so we set mHasPayload using OR to reflect that fact. + // (Otherwise mHasPayload could be cleared and the handler would think that + // it doesn't have a payload even though it actually does). + mHasPayload |= (ReadHandlerPayload(pStm, unmarshalAs) == S_OK); + + return hr; +} + +HRESULT +Handler::ReleaseMarshalData(IStream* pStm) { + return mUnmarshal->ReleaseMarshalData(pStm); +} + +HRESULT +Handler::DisconnectObject(DWORD dwReserved) { + return mUnmarshal->DisconnectObject(dwReserved); +} + +HRESULT +Handler::Unregister(REFCLSID aClsid) { return Module::Deregister(aClsid); } + +HRESULT +Handler::Register(REFCLSID aClsid, const bool aMsixContainer) { + return Module::Register(aClsid, Module::ThreadingModel::DedicatedUiThreadOnly, + Module::ClassType::InprocHandler, nullptr, + aMsixContainer); +} + +} // namespace mscom +} // namespace mozilla diff --git a/ipc/mscom/oop/Handler.h b/ipc/mscom/oop/Handler.h new file mode 100644 index 0000000000..b22c925c22 --- /dev/null +++ b/ipc/mscom/oop/Handler.h @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_mscom_Handler_h +#define mozilla_mscom_Handler_h + +#if defined(MOZILLA_INTERNAL_API) +# error This code is NOT for internal Gecko use! +#endif // defined(MOZILLA_INTERNAL_API) + +#include <objidl.h> + +#include "mozilla/mscom/Aggregation.h" +#include "mozilla/NotNull.h" +#include "mozilla/RefPtr.h" + +/* WARNING! The code in this file may be loaded into the address spaces of other + processes! It MUST NOT link against xul.dll or other Gecko binaries! Only + inline code may be included! */ + +namespace mozilla { +namespace mscom { + +class Handler : public IMarshal { + public: + // IMarshal + STDMETHODIMP GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext, + void* pvDestContext, DWORD mshlflags, + CLSID* pCid) override; + STDMETHODIMP GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext, + void* pvDestContext, DWORD mshlflags, + DWORD* pSize) override; + STDMETHODIMP MarshalInterface(IStream* pStm, REFIID riid, void* pv, + DWORD dwDestContext, void* pvDestContext, + DWORD mshlflags) override; + STDMETHODIMP UnmarshalInterface(IStream* pStm, REFIID riid, + void** ppv) override; + STDMETHODIMP ReleaseMarshalData(IStream* pStm) override; + STDMETHODIMP DisconnectObject(DWORD dwReserved) override; + + /** + * This method allows the handler to return its own interfaces that override + * those interfaces that are exposed by the underlying COM proxy. + * @param aProxyUnknown is the IUnknown of the underlying COM proxy. This is + * provided to give the handler implementation an + * opportunity to acquire interfaces to the underlying + * remote object, if needed. + * @param aIid Interface requested, similar to IUnknown::QueryInterface + * @param aOutInterface Outparam for the resulting interface to return to the + * client. + * @return The usual HRESULT codes similarly to IUnknown::QueryInterface. + * If E_NOINTERFACE is returned, the proxy will be queried. + * If the handler is certain that this interface is not available, + * it can return S_FALSE to avoid querying the proxy. This will be + * translated to E_NOINTERFACE before it is returned to the client. + */ + virtual HRESULT QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid, + void** aOutInterface) = 0; + /** + * Called when the implementer should deserialize data in aStream. + * @return S_OK on success; + * S_FALSE if the deserialization was successful but there was no + * data; HRESULT error code otherwise. + */ + virtual HRESULT ReadHandlerPayload(IStream* aStream, REFIID aIid) { + return S_FALSE; + } + + /** + * Unfortunately when COM marshals a proxy, it doesn't implicitly marshal + * the payload that was originally sent with the proxy. We must implement + * that code in the handler in order to make this happen. + */ + + /** + * This function allows the implementer to substitute a different interface + * for marshaling than the one that COM is intending to marshal. For example, + * the implementer might want to marshal a proxy for an interface that is + * derived from the requested interface. + * + * The default implementation is the identity function. + */ + virtual REFIID MarshalAs(REFIID aRequestedIid) { return aRequestedIid; } + + virtual HRESULT GetMarshalInterface(REFIID aMarshalAsIid, + NotNull<IUnknown*> aProxy, + NotNull<IID*> aOutIid, + NotNull<IUnknown**> aOutUnk); + + /** + * Called when the implementer must provide the size of the payload. + */ + virtual HRESULT GetHandlerPayloadSize(REFIID aIid, DWORD* aOutPayloadSize) { + if (!aOutPayloadSize) { + return E_INVALIDARG; + } + *aOutPayloadSize = 0; + return S_OK; + } + + /** + * Called when the implementer should serialize the payload data into aStream. + */ + virtual HRESULT WriteHandlerPayload(IStream* aStream, REFIID aIid) { + return S_OK; + } + + IUnknown* GetProxy() const { return mInnerUnk; } + + static HRESULT Register(REFCLSID aClsid, const bool aMsixContainer = false); + static HRESULT Unregister(REFCLSID aClsid); + + protected: + Handler(IUnknown* aOuter, HRESULT* aResult); + virtual ~Handler() {} + bool HasPayload() const { return mHasPayload; } + IUnknown* GetOuter() const { return mOuter; } + + private: + ULONG mRefCnt; + IUnknown* mOuter; + RefPtr<IUnknown> mInnerUnk; + IMarshal* mUnmarshal; // WEAK + bool mHasPayload; + DECLARE_AGGREGATABLE(Handler); +}; + +} // namespace mscom +} // namespace mozilla + +#endif // mozilla_mscom_Handler_h diff --git a/ipc/mscom/oop/Module.cpp b/ipc/mscom/oop/Module.cpp new file mode 100644 index 0000000000..88ebf91531 --- /dev/null +++ b/ipc/mscom/oop/Module.cpp @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Module.h" + +#include <stdlib.h> + +#include <ktmw32.h> +#include <memory.h> +#include <rpc.h> + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/mscom/Utils.h" +#include "mozilla/Range.h" +#include "nsWindowsHelpers.h" + +template <size_t N> +static const mozilla::Range<const wchar_t> LiteralToRange( + const wchar_t (&aArg)[N]) { + return mozilla::Range(aArg, N); +} + +namespace mozilla { +namespace mscom { + +ULONG Module::sRefCount = 0; + +static const wchar_t* SubkeyNameFromClassType( + const Module::ClassType aClassType) { + switch (aClassType) { + case Module::ClassType::InprocServer: + return L"InprocServer32"; + case Module::ClassType::InprocHandler: + return L"InprocHandler32"; + default: + MOZ_CRASH("Unknown ClassType"); + return nullptr; + } +} + +static const Range<const wchar_t> ThreadingModelAsString( + const Module::ThreadingModel aThreadingModel) { + switch (aThreadingModel) { + case Module::ThreadingModel::DedicatedUiThreadOnly: + return LiteralToRange(L"Apartment"); + case Module::ThreadingModel::MultiThreadedApartmentOnly: + return LiteralToRange(L"Free"); + case Module::ThreadingModel::DedicatedUiThreadXorMultiThreadedApartment: + return LiteralToRange(L"Both"); + case Module::ThreadingModel::AllThreadsAllApartments: + return LiteralToRange(L"Neutral"); + default: + MOZ_CRASH("Unknown ThreadingModel"); + return Range<const wchar_t>(); + } +} + +/* static */ +HRESULT Module::Register(const CLSID* const* aClsids, const size_t aNumClsids, + const ThreadingModel aThreadingModel, + const ClassType aClassType, const GUID* const aAppId, + const bool aMsixContainer) { + MOZ_ASSERT(aClsids && aNumClsids); + if (!aClsids || !aNumClsids) { + return E_INVALIDARG; + } + MOZ_ASSERT(!aAppId || !aMsixContainer, + "aAppId isn't valid in an MSIX container"); + + const wchar_t* inprocName = SubkeyNameFromClassType(aClassType); + + const Range<const wchar_t> threadingModelStr = + ThreadingModelAsString(aThreadingModel); + const DWORD threadingModelStrLenBytesInclNul = + threadingModelStr.length() * sizeof(wchar_t); + + wchar_t strAppId[kGuidRegFormatCharLenInclNul] = {}; + if (aAppId) { + GUIDToString(*aAppId, strAppId); + } + + // Obtain the full path to this DLL + HMODULE thisModule; + if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast<LPCWSTR>(&Module::CanUnload), + &thisModule)) { + return HRESULT_FROM_WIN32(::GetLastError()); + } + + wchar_t absThisModulePath[MAX_PATH + 1] = {}; + DWORD actualPathLenCharsExclNul = ::GetModuleFileNameW( + thisModule, absThisModulePath, ArrayLength(absThisModulePath)); + if (!actualPathLenCharsExclNul || + actualPathLenCharsExclNul == ArrayLength(absThisModulePath)) { + return HRESULT_FROM_WIN32(::GetLastError()); + } + const DWORD actualPathLenBytesInclNul = + (actualPathLenCharsExclNul + 1) * sizeof(wchar_t); + + nsAutoHandle txn; + // RegCreateKeyTransacted doesn't work in MSIX containers. + if (!aMsixContainer) { + // Use the name of this DLL as the name of the transaction + wchar_t txnName[_MAX_FNAME] = {}; + if (_wsplitpath_s(absThisModulePath, nullptr, 0, nullptr, 0, txnName, + ArrayLength(txnName), nullptr, 0)) { + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + // Manipulate the registry using a transaction so that any failures are + // rolled back. + txn.own(::CreateTransaction(nullptr, nullptr, TRANSACTION_DO_NOT_PROMOTE, 0, + 0, 0, txnName)); + if (txn.get() == INVALID_HANDLE_VALUE) { + return HRESULT_FROM_WIN32(::GetLastError()); + } + } + + HRESULT hr; + LSTATUS status; + + // A single DLL may serve multiple components. For each CLSID, we register + // this DLL as its server and, when an AppId is specified, set up a reference + // from the CLSID to the specified AppId. + for (size_t idx = 0; idx < aNumClsids; ++idx) { + if (!aClsids[idx]) { + return E_INVALIDARG; + } + + wchar_t clsidKeyPath[256]; + hr = BuildClsidPath(*aClsids[idx], clsidKeyPath); + if (FAILED(hr)) { + return hr; + } + + // Create the CLSID key + HKEY rawClsidKey; + // Subtle: If aMsixContainer is true, as well as calling a different + // function, we also use HKEY_CURRENT_USER. When false, we use + // HKEY_LOCAL_MACHINE. + if (aMsixContainer) { + status = ::RegCreateKeyExW(HKEY_CURRENT_USER, clsidKeyPath, 0, nullptr, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, + nullptr, &rawClsidKey, nullptr); + } else { + status = ::RegCreateKeyTransactedW( + HKEY_LOCAL_MACHINE, clsidKeyPath, 0, nullptr, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, nullptr, &rawClsidKey, nullptr, txn, nullptr); + } + if (status != ERROR_SUCCESS) { + return HRESULT_FROM_WIN32(status); + } + nsAutoRegKey clsidKey(rawClsidKey); + + if (aAppId) { + // This value associates the registered CLSID with the specified AppID + status = ::RegSetValueExW(clsidKey, L"AppID", 0, REG_SZ, + reinterpret_cast<const BYTE*>(strAppId), + ArrayLength(strAppId) * sizeof(wchar_t)); + if (status != ERROR_SUCCESS) { + return HRESULT_FROM_WIN32(status); + } + } + + HKEY rawInprocKey; + if (aMsixContainer) { + status = ::RegCreateKeyExW(clsidKey, inprocName, 0, nullptr, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, + nullptr, &rawInprocKey, nullptr); + } else { + status = ::RegCreateKeyTransactedW( + clsidKey, inprocName, 0, nullptr, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, nullptr, &rawInprocKey, nullptr, txn, nullptr); + } + if (status != ERROR_SUCCESS) { + return HRESULT_FROM_WIN32(status); + } + nsAutoRegKey inprocKey(rawInprocKey); + + // Set the component's path to this DLL + status = ::RegSetValueExW(inprocKey, nullptr, 0, REG_EXPAND_SZ, + reinterpret_cast<const BYTE*>(absThisModulePath), + actualPathLenBytesInclNul); + if (status != ERROR_SUCCESS) { + return HRESULT_FROM_WIN32(status); + } + + status = ::RegSetValueExW( + inprocKey, L"ThreadingModel", 0, REG_SZ, + reinterpret_cast<const BYTE*>(threadingModelStr.begin().get()), + threadingModelStrLenBytesInclNul); + if (status != ERROR_SUCCESS) { + return HRESULT_FROM_WIN32(status); + } + } + + if (aAppId) { + // When specified, we must also create a key for the AppID. + wchar_t appidKeyPath[256]; + hr = BuildAppidPath(*aAppId, appidKeyPath); + if (FAILED(hr)) { + return hr; + } + + HKEY rawAppidKey; + status = ::RegCreateKeyTransactedW( + HKEY_LOCAL_MACHINE, appidKeyPath, 0, nullptr, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, nullptr, &rawAppidKey, nullptr, txn, nullptr); + if (status != ERROR_SUCCESS) { + return HRESULT_FROM_WIN32(status); + } + nsAutoRegKey appidKey(rawAppidKey); + + // Setting DllSurrogate to a null or empty string indicates to Windows that + // we want to use the default surrogate (i.e. dllhost.exe) to load our DLL. + status = + ::RegSetValueExW(appidKey, L"DllSurrogate", 0, REG_SZ, + reinterpret_cast<const BYTE*>(L""), sizeof(wchar_t)); + if (status != ERROR_SUCCESS) { + return HRESULT_FROM_WIN32(status); + } + } + + if (!aMsixContainer) { + if (!::CommitTransaction(txn)) { + return HRESULT_FROM_WIN32(::GetLastError()); + } + } + + return S_OK; +} + +/** + * Unfortunately the registry transaction APIs are not as well-developed for + * deleting things as they are for creating them. We just use RegDeleteTree + * for the implementation of this method. + */ +HRESULT Module::Deregister(const CLSID* const* aClsids, const size_t aNumClsids, + const GUID* const aAppId) { + MOZ_ASSERT(aClsids && aNumClsids); + if (!aClsids || !aNumClsids) { + return E_INVALIDARG; + } + + HRESULT hr; + LSTATUS status; + + // Delete the key for each CLSID. This will also delete any references to + // the AppId. + for (size_t idx = 0; idx < aNumClsids; ++idx) { + if (!aClsids[idx]) { + return E_INVALIDARG; + } + + wchar_t clsidKeyPath[256]; + hr = BuildClsidPath(*aClsids[idx], clsidKeyPath); + if (FAILED(hr)) { + return hr; + } + + status = ::RegDeleteTreeW(HKEY_LOCAL_MACHINE, clsidKeyPath); + // We allow the deletion to succeed if the key was already gone + if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) { + return HRESULT_FROM_WIN32(status); + } + } + + // Now delete the AppID key, if desired. + if (aAppId) { + wchar_t appidKeyPath[256]; + hr = BuildAppidPath(*aAppId, appidKeyPath); + if (FAILED(hr)) { + return hr; + } + + status = ::RegDeleteTreeW(HKEY_LOCAL_MACHINE, appidKeyPath); + // We allow the deletion to succeed if the key was already gone + if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) { + return HRESULT_FROM_WIN32(status); + } + } + + return S_OK; +} + +} // namespace mscom +} // namespace mozilla diff --git a/ipc/mscom/oop/Module.h b/ipc/mscom/oop/Module.h new file mode 100644 index 0000000000..1fa4b31c49 --- /dev/null +++ b/ipc/mscom/oop/Module.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_mscom_Module_h +#define mozilla_mscom_Module_h + +#if defined(MOZILLA_INTERNAL_API) +# error This code is NOT for internal Gecko use! +#endif // defined(MOZILLA_INTERNAL_API) + +#include <objbase.h> + +/* WARNING! The code in this file may be loaded into the address spaces of other + processes! It MUST NOT link against xul.dll or other Gecko binaries! Only + inline code may be included! */ + +namespace mozilla { +namespace mscom { + +class Module { + public: + static HRESULT CanUnload() { return sRefCount == 0 ? S_OK : S_FALSE; } + + static void Lock() { ++sRefCount; } + static void Unlock() { --sRefCount; } + + enum class ThreadingModel { + DedicatedUiThreadOnly, + MultiThreadedApartmentOnly, + DedicatedUiThreadXorMultiThreadedApartment, + AllThreadsAllApartments, + }; + + enum class ClassType { + InprocServer, + InprocHandler, + }; + + /** + * In the Register functions, the aMsixContainer parameter specifies whether + * this registration is being performed inside an MSIX container. If true, + * the CLSID is registered in HKCU and a registry transaction is not used, as + * registry transactions don't work in an MSIX container. If false (the + * default), the CLSID is registered in HKLM and a registry transaction is + * used so that any failures roll back the entire operation. Specifying aAppId + * is only valid when aMsixContainer is false. + */ + static HRESULT Register(REFCLSID aClsid, const ThreadingModel aThreadingModel, + const ClassType aClassType = ClassType::InprocServer, + const GUID* const aAppId = nullptr, + const bool aMsixContainer = false) { + const CLSID* clsidArray[] = {&aClsid}; + return Register(clsidArray, aThreadingModel, aClassType, aAppId, + aMsixContainer); + } + + template <size_t N> + static HRESULT Register(const CLSID* (&aClsids)[N], + const ThreadingModel aThreadingModel, + const ClassType aClassType = ClassType::InprocServer, + const GUID* const aAppId = nullptr, + const bool aMsixContainer = false) { + return Register(aClsids, N, aThreadingModel, aClassType, aAppId, + aMsixContainer); + } + + static HRESULT Deregister(REFCLSID aClsid, + const GUID* const aAppId = nullptr) { + const CLSID* clsidArray[] = {&aClsid}; + return Deregister(clsidArray, aAppId); + } + + template <size_t N> + static HRESULT Deregister(const CLSID* (&aClsids)[N], + const GUID* const aAppId = nullptr) { + return Deregister(aClsids, N, aAppId); + } + + private: + static HRESULT Register(const CLSID* const* aClsids, const size_t aNumClsids, + const ThreadingModel aThreadingModel, + const ClassType aClassType, const GUID* const aAppId, + const bool aMsixContainer); + + static HRESULT Deregister(const CLSID* const* aClsids, + const size_t aNumClsids, const GUID* const aAppId); + + private: + static ULONG sRefCount; +}; + +} // namespace mscom +} // namespace mozilla + +#endif // mozilla_mscom_Module_h diff --git a/ipc/mscom/oop/moz.build b/ipc/mscom/oop/moz.build new file mode 100644 index 0000000000..5ead655e6f --- /dev/null +++ b/ipc/mscom/oop/moz.build @@ -0,0 +1,43 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Library("mscom_oop") + +SOURCES += [ + "../ActivationContext.cpp", + "../COMWrappers.cpp", + "../Objref.cpp", + "../Registration.cpp", + "../StructStream.cpp", + "../Utils.cpp", +] + +UNIFIED_SOURCES += [ + "Handler.cpp", + "Module.cpp", +] + +OS_LIBS += [ + "ktmw32", + "ole32", + "oleaut32", + "shlwapi", +] + +LIBRARY_DEFINES["UNICODE"] = True +LIBRARY_DEFINES["_UNICODE"] = True +LIBRARY_DEFINES["MOZ_NO_MOZALLOC"] = True +LIBRARY_DEFINES["MOZ_MSCOM_REMARSHAL_NO_HANDLER"] = True + +DisableStlWrapping() +NO_EXPAND_LIBS = True +FORCE_STATIC_LIB = True + +# This DLL may be loaded into other processes, so we need static libs for +# Windows 7 and Windows 8. +USE_STATIC_LIBS = True + +REQUIRES_UNIFIED_BUILD = True |