Bug 1303060: Additions to ipc/mscom for out-of-process components; r?jimm draft
authorAaron Klotz <aklotz@mozilla.com>
Fri, 17 Feb 2017 16:20:51 -0700
changeset 487571 fedb899cc3ef03c6265d3fa9745c22e589864654
parent 487570 1942b091dbee4d6a902b88a6798368dc68a9c051
child 546481 8409f399e0546362328023d42c99623493bc4643
push id46250
push useraklotz@mozilla.com
push dateTue, 21 Feb 2017 19:21:23 +0000
reviewersjimm
bugs1303060
milestone54.0a1
Bug 1303060: Additions to ipc/mscom for out-of-process components; r?jimm MozReview-Commit-ID: IYjONGbBraG
ipc/mscom/moz.build
ipc/mscom/oop/Factory.h
ipc/mscom/oop/Handler.cpp
ipc/mscom/oop/Handler.h
ipc/mscom/oop/Module.cpp
ipc/mscom/oop/Module.h
ipc/mscom/oop/moz.build
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -21,16 +21,20 @@ UNIFIED_SOURCES += [
     'AgileReference.cpp',
     'EnsureMTA.cpp',
     'MainThreadRuntime.cpp',
     'ProxyStream.cpp',
     'Utils.cpp',
 ]
 
 if CONFIG['ACCESSIBILITY']:
+    DIRS += [
+        'oop',
+    ]
+
     EXPORTS.mozilla.mscom += [
         'ActivationContext.h',
         'DispatchForwarder.h',
         'IHandlerPayload.h',
         'Interceptor.h',
         'InterceptorLog.h',
         'MainThreadHandoff.h',
         'MainThreadInvoker.h',
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/Factory.h
@@ -0,0 +1,155 @@
+/* -*- 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 "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Move.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/StaticPtr.h"
+#include "Module.h"
+
+#include <objbase.h>
+#include <unknwn.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(mozilla::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
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/Handler.cpp
@@ -0,0 +1,339 @@
+/* -*- 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 "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)
+{
+  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;
+  }
+
+  // mInnerMarshal 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 != 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()
+{
+  if (--mRefCnt == 0) {
+    delete this;
+    Module::Unlock();
+  }
+  return mRefCnt;
+}
+
+HRESULT
+Handler::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
+                           void* pvDestContext, DWORD mshlflags,
+                           CLSID* pCid)
+{
+  return mUnmarshal->GetUnmarshalClass(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 (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;
+}
+
+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;
+
+  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;
+    }
+  }
+
+  hr = mUnmarshal->MarshalInterface(pStm, marshalAs, unkToMarshal.get(),
+                                    dwDestContext, pvDestContext, mshlflags);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  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);
+}
+
+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;
+  }
+
+  hr = ReadHandlerPayload(pStm, unmarshalAs);
+
+  // 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 |= (hr == S_OK);
+
+  // hr may be S_FALSE, but we don't want to return that
+  return SUCCEEDED(hr) ? S_OK : hr;
+}
+
+HRESULT
+Handler::ReleaseMarshalData(IStream* pStm)
+{
+  return mUnmarshal->ReleaseMarshalData(pStm);
+}
+
+HRESULT
+Handler::DisconnectObject(DWORD dwReserved)
+{
+  return mUnmarshal->DisconnectObject(dwReserved);
+}
+
+template <size_t N>
+static HRESULT
+BuildClsidPath(wchar_t (&aPath)[N], REFCLSID aClsid)
+{
+  const wchar_t kClsid[] = {L'C', L'L', L'S', L'I', L'D', L'\\'};
+  const size_t kReqdGuidLen = 39;
+  static_assert(N >= kReqdGuidLen + mozilla::ArrayLength(kClsid),
+                "aPath array is too short");
+  if (wcsncpy_s(aPath, kClsid, mozilla::ArrayLength(kClsid))) {
+    return E_INVALIDARG;
+  }
+
+  int guidConversionResult =
+    StringFromGUID2(aClsid, &aPath[mozilla::ArrayLength(kClsid)],
+                    N - mozilla::ArrayLength(kClsid));
+  if (!guidConversionResult) {
+    return E_INVALIDARG;
+  }
+
+  return S_OK;
+}
+
+HRESULT
+Handler::Unregister(REFCLSID aClsid)
+{
+  wchar_t path[256] = {};
+  HRESULT hr = BuildClsidPath(path, aClsid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = HRESULT_FROM_WIN32(SHDeleteKey(HKEY_CLASSES_ROOT, path));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT
+Handler::Register(REFCLSID aClsid)
+{
+  wchar_t path[256] = {};
+  HRESULT hr = BuildClsidPath(path, aClsid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  HKEY rawClsidKey;
+  DWORD disposition;
+  LONG result = RegCreateKeyEx(HKEY_CLASSES_ROOT, path, 0, nullptr,
+                               REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+                               nullptr, &rawClsidKey, &disposition);
+  if (result != ERROR_SUCCESS) {
+    return HRESULT_FROM_WIN32(result);
+  }
+  nsAutoRegKey clsidKey(rawClsidKey);
+
+  if (wcscat_s(path, L"\\InprocHandler32")) {
+    return E_UNEXPECTED;
+  }
+
+  HKEY rawInprocHandlerKey;
+  result = RegCreateKeyEx(HKEY_CLASSES_ROOT, path, 0, nullptr,
+                          REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+                          nullptr, &rawInprocHandlerKey, &disposition);
+  if (result != ERROR_SUCCESS) {
+    Unregister(aClsid);
+    return HRESULT_FROM_WIN32(result);
+  }
+  nsAutoRegKey inprocHandlerKey(rawInprocHandlerKey);
+
+  wchar_t absLibPath[MAX_PATH + 1] = {};
+  HMODULE thisModule;
+  if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+                         GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+                         reinterpret_cast<LPCTSTR>(&Handler::Register),
+                         &thisModule)) {
+    return HRESULT_FROM_WIN32(GetLastError());
+  }
+
+  DWORD size = GetModuleFileName(thisModule, absLibPath,
+                                 mozilla::ArrayLength(absLibPath));
+  if (!size || (size == mozilla::ArrayLength(absLibPath) &&
+      GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
+    DWORD lastError = GetLastError();
+    Unregister(aClsid);
+    return HRESULT_FROM_WIN32(lastError);
+  }
+
+  result = RegSetValueEx(inprocHandlerKey, L"", 0, REG_EXPAND_SZ,
+                         reinterpret_cast<const BYTE*>(absLibPath),
+                         sizeof(absLibPath));
+  if (result != ERROR_SUCCESS) {
+    Unregister(aClsid);
+    return HRESULT_FROM_WIN32(result);
+  }
+
+  const wchar_t kApartment[] = L"Apartment";
+  result = RegSetValueEx(inprocHandlerKey, L"ThreadingModel", 0, REG_SZ,
+                         reinterpret_cast<const BYTE*>(kApartment),
+                         sizeof(kApartment));
+  if (result != ERROR_SUCCESS) {
+    Unregister(aClsid);
+    return HRESULT_FROM_WIN32(result);
+  }
+
+  return S_OK;
+}
+
+} // namespace mscom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/Handler.h
@@ -0,0 +1,127 @@
+/* -*- 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/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
+   */
+  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; }
+
+  /**
+   * 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);
+  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
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/Module.cpp
@@ -0,0 +1,19 @@
+/* -*- 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 <memory.h>
+#include <rpc.h>
+
+namespace mozilla {
+namespace mscom {
+
+ULONG Module::sRefCount = 0;
+
+} // namespace mscom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/Module.h
@@ -0,0 +1,39 @@
+/* -*- 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; }
+
+private:
+  static ULONG sRefCount;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_Module_h
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/oop/moz.build
@@ -0,0 +1,36 @@
+# -*- 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',
+    '../Registration.cpp',
+    '../StructStream.cpp',
+]
+
+UNIFIED_SOURCES += [
+    'Handler.cpp',
+    'Module.cpp',
+]
+
+OS_LIBS += [
+    'ole32',
+    'oleaut32',
+    'shlwapi',
+]
+
+LIBRARY_DEFINES['UNICODE'] = True
+LIBRARY_DEFINES['_UNICODE'] = True
+LIBRARY_DEFINES['MOZ_NO_MOZALLOC'] = True
+
+DISABLE_STL_WRAPPING = True
+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