Bug 1310841: Make mscom registration use CoGetClassObject so that COM will retain a reference to the proxy dll; r?jimm draft
authorAaron Klotz <aklotz@mozilla.com>
Tue, 25 Oct 2016 14:47:16 -0600
changeset 429411 0b0b3a52da47a7c3b13005bb801d484c4a619910
parent 427748 d21a3c0cba86537ec5e5ecaa89c207d389af9ae0
child 429421 eedf9236d8d0f2d6585a7afa1124f86dae444666
push id33562
push useraklotz@mozilla.com
push dateTue, 25 Oct 2016 20:48:10 +0000
reviewersjimm
bugs1310841
milestone52.0a1
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
@@ -25,16 +25,17 @@ UNIFIED_SOURCES += [
 ]
 
 if CONFIG['ACCESSIBILITY']:
     DIRS += [
         'test',
     ]
 
     EXPORTS.mozilla.mscom += [
+        'ActivationContext.h',
         'DispatchForwarder.h',
         'Interceptor.h',
         'InterceptorLog.h',
         'MainThreadHandoff.h',
         'MainThreadInvoker.h',
         'Registration.h',
         'ThreadLocalObject.h',
         'WeakRef.h',
@@ -42,16 +43,17 @@ if CONFIG['ACCESSIBILITY']:
 
     SOURCES += [
         'Interceptor.cpp',
         'Registration.cpp',
         'WeakRef.cpp',
     ]
 
     UNIFIED_SOURCES += [
+        'ActivationContext.cpp',
         'DispatchForwarder.cpp',
         'InterceptorLog.cpp',
         'MainThreadHandoff.cpp',
         'MainThreadInvoker.cpp',
         'ThreadLocalObject.cpp',
     ]
 
 LOCAL_INCLUDES += [