Bug 1310841: Make mscom registration use CoGetClassObject so that COM will retain a reference to the proxy dll; r=jimm
authorAaron Klotz <aklotz@mozilla.com>
Tue, 25 Oct 2016 15:04:47 -0600
changeset 319475 b59a4d7a3d2c57c33aae29a8844100f2223ab5bc
parent 319474 1eed5e6b3f51c41cbb05886dd4fdef3a4f33d7af
child 319476 1bee9d28f684dc94b8326570b50d3c1c451cf835
push id30871
push usercbook@mozilla.com
push dateWed, 26 Oct 2016 14:54:32 +0000
treeherdermozilla-central@d26ac63f1b81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1310841
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1310841: Make mscom registration use CoGetClassObject so that COM will retain a reference to the proxy dll; r=jimm MozReview-Commit-ID: GZxbLCC6gVi
accessible/interfaces/ia2/IA2Marshal.dll.manifest
ipc/mscom/ActivationContext.cpp
ipc/mscom/ActivationContext.h
ipc/mscom/Registration.cpp
ipc/mscom/moz.build
new file mode 100644
--- /dev/null
+++ b/accessible/interfaces/ia2/IA2Marshal.dll.manifest
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" name="IA2Marshal" version="1.0.0.0" />
+  <file name="IA2Marshal.dll">
+    <comInterfaceProxyStub
+        iid="{E89F726E-C4F4-4c19-BB19-B647D7FA8478}"
+        proxyStubClsid32="{E89F726E-C4F4-4c19-BB19-B647D7FA8478}"
+        name="IAccessible2"
+        tlbid="{CE3F726E-D1D3-44FE-B995-FF1DB3B48B2B}"
+    />
+  </file>
+</assembly>
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/ActivationContext.cpp
@@ -0,0 +1,46 @@
+/* -*- 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 "mozilla/mscom/ActivationContext.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+
+namespace mozilla {
+namespace mscom {
+
+ActivationContext::ActivationContext(HMODULE aLoadFromModule)
+  : mActCtx(INVALID_HANDLE_VALUE)
+  , mActivationCookie(0)
+{
+  ACTCTX actCtx = {sizeof(actCtx)};
+  actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_HMODULE_VALID;
+  actCtx.lpResourceName = MAKEINTRESOURCE(2);
+  actCtx.hModule = aLoadFromModule;
+
+  mActCtx = ::CreateActCtx(&actCtx);
+  MOZ_ASSERT(mActCtx != INVALID_HANDLE_VALUE);
+  if (mActCtx == INVALID_HANDLE_VALUE) {
+    return;
+  }
+  if (!::ActivateActCtx(mActCtx, &mActivationCookie)) {
+    ::ReleaseActCtx(mActCtx);
+    mActCtx = INVALID_HANDLE_VALUE;
+  }
+}
+
+ActivationContext::~ActivationContext()
+{
+  if (mActCtx == INVALID_HANDLE_VALUE) {
+    return;
+  }
+  DebugOnly<BOOL> deactivated = ::DeactivateActCtx(0, mActivationCookie);
+  MOZ_ASSERT(deactivated);
+  ::ReleaseActCtx(mActCtx);
+}
+
+} // namespace mscom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/ActivationContext.h
@@ -0,0 +1,35 @@
+/* -*- 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_ActivationContext_h
+#define mozilla_mscom_ActivationContext_h
+
+#include <windows.h>
+
+namespace mozilla {
+namespace mscom {
+
+class ActivationContext
+{
+public:
+  explicit ActivationContext(HMODULE aLoadFromModule);
+  ~ActivationContext();
+
+  explicit operator bool() const
+  {
+    return mActCtx != INVALID_HANDLE_VALUE;
+  }
+
+private:
+  HANDLE    mActCtx;
+  ULONG_PTR mActivationCookie;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_ActivationContext_h
+
--- a/ipc/mscom/Registration.cpp
+++ b/ipc/mscom/Registration.cpp
@@ -4,28 +4,30 @@
  * 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/. */
 
 // COM registration data structures are built with C code, so we need to
 // simulate that in our C++ code by defining CINTERFACE before including
 // anything else that could possibly pull in Windows header files.
 #define CINTERFACE
 
+#include "mozilla/mscom/ActivationContext.h"
 #include "mozilla/mscom/EnsureMTA.h"
 #include "mozilla/mscom/Registration.h"
 #include "mozilla/mscom/Utils.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Move.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Pair.h"
 #include "mozilla/StaticPtr.h"
 #include "nsTArray.h"
+#include "nsWindowsHelpers.h"
 
 #include <oaidl.h>
 #include <objidl.h>
 #include <rpcproxy.h>
 #include <shlwapi.h>
 
 /* This code MUST NOT use any non-inlined internal Mozilla APIs, as it will be
    compiled into DLLs that COM may load into non-Mozilla processes! */
@@ -81,73 +83,70 @@ UniquePtr<RegisteredProxy>
 RegisterProxy(const wchar_t* aLeafName, RegistrationFlags aFlags)
 {
   wchar_t modulePathBuf[MAX_PATH + 1] = {0};
   if (!BuildLibPath(aFlags, modulePathBuf, ArrayLength(modulePathBuf),
                     aLeafName)) {
     return nullptr;
   }
 
-  HMODULE proxyDll = LoadLibrary(modulePathBuf);
-  if (!proxyDll) {
+  nsModuleHandle proxyDll(LoadLibrary(modulePathBuf));
+  if (!proxyDll.get()) {
     return nullptr;
   }
 
-  auto DllGetClassObjectFn = reinterpret_cast<LPFNGETCLASSOBJECT>(
-      GetProcAddress(proxyDll, "DllGetClassObject"));
-  if (!DllGetClassObjectFn) {
-    FreeLibrary(proxyDll);
+  // Instantiate an activation context so that CoGetClassObject will use any
+  // COM metadata embedded in proxyDll's manifest to resolve CLSIDs.
+  ActivationContext actCtx(proxyDll);
+  if (!actCtx) {
     return nullptr;
   }
 
   auto GetProxyDllInfoFn = reinterpret_cast<GetProxyDllInfoFnPtr>(
       GetProcAddress(proxyDll, "GetProxyDllInfo"));
   if (!GetProxyDllInfoFn) {
-    FreeLibrary(proxyDll);
     return nullptr;
   }
 
   const ProxyFileInfo** proxyInfo = nullptr;
   const CLSID* proxyClsid = nullptr;
   GetProxyDllInfoFn(&proxyInfo, &proxyClsid);
   if (!proxyInfo || !proxyClsid) {
-    FreeLibrary(proxyDll);
     return nullptr;
   }
 
+  // We call CoGetClassObject instead of DllGetClassObject because it forces
+  // the COM runtime to manage the lifetime of the DLL.
   IUnknown* classObject = nullptr;
-  HRESULT hr = DllGetClassObjectFn(*proxyClsid, IID_IUnknown,
-                                   (void**) &classObject);
+  HRESULT hr = CoGetClassObject(*proxyClsid, CLSCTX_INPROC_SERVER, nullptr,
+                                IID_IUnknown, (void**) &classObject);
   if (FAILED(hr)) {
-    FreeLibrary(proxyDll);
     return nullptr;
   }
 
   DWORD regCookie;
   hr = CoRegisterClassObject(*proxyClsid, classObject, CLSCTX_INPROC_SERVER,
                              REGCLS_MULTIPLEUSE, &regCookie);
   if (FAILED(hr)) {
     classObject->lpVtbl->Release(classObject);
-    FreeLibrary(proxyDll);
     return nullptr;
   }
 
   ITypeLib* typeLib = nullptr;
   hr = LoadTypeLibEx(modulePathBuf, REGKIND_NONE, &typeLib);
   MOZ_ASSERT(SUCCEEDED(hr));
   if (FAILED(hr)) {
     CoRevokeClassObject(regCookie);
     classObject->lpVtbl->Release(classObject);
-    FreeLibrary(proxyDll);
     return nullptr;
   }
 
   // RegisteredProxy takes ownership of proxyDll, classObject, and typeLib
   // references
-  auto result(MakeUnique<RegisteredProxy>(reinterpret_cast<uintptr_t>(proxyDll),
+  auto result(MakeUnique<RegisteredProxy>(reinterpret_cast<uintptr_t>(proxyDll.disown()),
                                           classObject, regCookie, typeLib));
 
   while (*proxyInfo) {
     const ProxyFileInfo& curInfo = **proxyInfo;
     for (unsigned short i = 0, e = curInfo.TableSize; i < e; ++i) {
       hr = CoRegisterPSClsid(*(curInfo.pStubVtblList[i]->header.piid),
                              *proxyClsid);
       if (FAILED(hr)) {
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -21,32 +21,34 @@ SOURCES += [
 UNIFIED_SOURCES += [
     'EnsureMTA.cpp',
     'MainThreadRuntime.cpp',
     'ProxyStream.cpp',
 ]
 
 if CONFIG['ACCESSIBILITY']:
     EXPORTS.mozilla.mscom += [
+        'ActivationContext.h',
         'DispatchForwarder.h',
         'Interceptor.h',
         'InterceptorLog.h',
         'MainThreadHandoff.h',
         'MainThreadInvoker.h',
         'Registration.h',
         'WeakRef.h',
     ]
 
     SOURCES += [
         'Interceptor.cpp',
         'Registration.cpp',
         'WeakRef.cpp',
     ]
 
     UNIFIED_SOURCES += [
+        'ActivationContext.cpp',
         'DispatchForwarder.cpp',
         'InterceptorLog.cpp',
         'MainThreadHandoff.cpp',
         'MainThreadInvoker.cpp',
     ]
 
 LOCAL_INCLUDES += [
     '/xpcom/build',