Home | History | Annotate | Download | only in delegate_execute
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "build/intsafe_workaround.h"
      6 
      7 #include <atlbase.h>
      8 #include <atlcom.h>
      9 #include <atlctl.h>
     10 #include <initguid.h>
     11 #include <shellapi.h>
     12 
     13 #include "base/at_exit.h"
     14 #include "base/command_line.h"
     15 #include "base/file_util.h"
     16 #include "base/memory/scoped_ptr.h"
     17 #include "base/process/kill.h"
     18 #include "base/strings/string16.h"
     19 #include "base/win/scoped_com_initializer.h"
     20 #include "base/win/scoped_comptr.h"
     21 #include "base/win/scoped_handle.h"
     22 #include "breakpad/src/client/windows/handler/exception_handler.h"
     23 #include "chrome/common/chrome_switches.h"
     24 #include "chrome/installer/util/browser_distribution.h"
     25 #include "win8/delegate_execute/command_execute_impl.h"
     26 #include "win8/delegate_execute/crash_server_init.h"
     27 #include "win8/delegate_execute/delegate_execute_operation.h"
     28 #include "win8/delegate_execute/resource.h"
     29 
     30 using namespace ATL;
     31 
     32 // Usually classes derived from CAtlExeModuleT, or other types of ATL
     33 // COM module classes statically define their CLSID at compile time through
     34 // the use of various macros, and ATL internals takes care of creating the
     35 // class objects and registering them.  However, we need to register the same
     36 // object with different CLSIDs depending on a runtime setting, so we handle
     37 // that logic here, before the main ATL message loop runs.
     38 class DelegateExecuteModule
     39     : public ATL::CAtlExeModuleT< DelegateExecuteModule > {
     40  public :
     41   typedef ATL::CAtlExeModuleT<DelegateExecuteModule> ParentClass;
     42   typedef CComObject<CommandExecuteImpl> ImplType;
     43 
     44   DelegateExecuteModule()
     45       : registration_token_(0) {
     46   }
     47 
     48   HRESULT PreMessageLoop(int nShowCmd) {
     49     HRESULT hr = S_OK;
     50     base::string16 clsid_string;
     51     GUID clsid;
     52     BrowserDistribution* dist = BrowserDistribution::GetDistribution();
     53     if (!dist->GetCommandExecuteImplClsid(&clsid_string))
     54       return E_FAIL;
     55     hr = ::CLSIDFromString(clsid_string.c_str(), &clsid);
     56     if (FAILED(hr))
     57       return hr;
     58 
     59     // We use the same class creation logic as ATL itself.  See
     60     // _ATL_OBJMAP_ENTRY::RegisterClassObject() in atlbase.h
     61     hr = ImplType::_ClassFactoryCreatorClass::CreateInstance(
     62         ImplType::_CreatorClass::CreateInstance, IID_IUnknown,
     63         instance_.ReceiveVoid());
     64     if (FAILED(hr))
     65       return hr;
     66     hr = ::CoRegisterClassObject(clsid, instance_, CLSCTX_LOCAL_SERVER,
     67         REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED, &registration_token_);
     68     if (FAILED(hr))
     69       return hr;
     70 
     71     return ParentClass::PreMessageLoop(nShowCmd);
     72   }
     73 
     74   HRESULT PostMessageLoop() {
     75     if (registration_token_ != 0) {
     76       ::CoRevokeClassObject(registration_token_);
     77       registration_token_ = 0;
     78     }
     79 
     80     instance_.Release();
     81 
     82     return ParentClass::PostMessageLoop();
     83   }
     84 
     85  private:
     86   base::win::ScopedComPtr<IUnknown> instance_;
     87   DWORD registration_token_;
     88 };
     89 
     90 DelegateExecuteModule _AtlModule;
     91 
     92 using delegate_execute::DelegateExecuteOperation;
     93 using base::win::ScopedHandle;
     94 
     95 int RelaunchChrome(const DelegateExecuteOperation& operation) {
     96   AtlTrace("Relaunching [%ls] with flags [%s]\n",
     97            operation.mutex().c_str(), operation.relaunch_flags());
     98   ScopedHandle mutex(OpenMutexW(SYNCHRONIZE, FALSE, operation.mutex().c_str()));
     99   if (mutex.IsValid()) {
    100     const int kWaitSeconds = 5;
    101     DWORD result = ::WaitForSingleObject(mutex, kWaitSeconds * 1000);
    102     if (result == WAIT_ABANDONED) {
    103       // This is the normal case. Chrome exits and windows marks the mutex as
    104       // abandoned.
    105     } else if (result == WAIT_OBJECT_0) {
    106       // This is unexpected. Check if somebody is not closing the mutex on
    107       // RelaunchChromehelper, the mutex should not be closed.
    108       AtlTrace("Unexpected release of the relaunch mutex!!\n");
    109     } else if (result == WAIT_TIMEOUT) {
    110       // This could mean that Chrome is hung. Proceed to exterminate.
    111       DWORD pid = operation.GetParentPid();
    112       AtlTrace("%ds timeout. Killing Chrome %d\n", kWaitSeconds, pid);
    113       base::KillProcessById(pid, 0, false);
    114     } else {
    115       AtlTrace("Failed to wait for relaunch mutex, result is 0x%x\n", result);
    116     }
    117   } else {
    118     // It is possible that chrome exits so fast that the mutex is not there.
    119     AtlTrace("No relaunch mutex found\n");
    120   }
    121 
    122   base::win::ScopedCOMInitializer com_initializer;
    123 
    124   base::string16 relaunch_flags(operation.relaunch_flags());
    125   SHELLEXECUTEINFO sei = { sizeof(sei) };
    126   sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
    127   sei.nShow = SW_SHOWNORMAL;
    128   sei.lpFile = operation.shortcut().value().c_str();
    129   sei.lpParameters = relaunch_flags.c_str();
    130 
    131   AtlTrace(L"Relaunching Chrome via shortcut [%ls]\n", sei.lpFile);
    132 
    133   if (!::ShellExecuteExW(&sei)) {
    134     int error = HRESULT_FROM_WIN32(::GetLastError());
    135     AtlTrace("ShellExecute returned 0x%08X\n", error);
    136     return error;
    137   }
    138   return S_OK;
    139 }
    140 
    141 extern "C" int WINAPI _tWinMain(HINSTANCE , HINSTANCE, LPTSTR, int nShowCmd) {
    142   scoped_ptr<google_breakpad::ExceptionHandler> breakpad =
    143       delegate_execute::InitializeCrashReporting();
    144 
    145   base::AtExitManager exit_manager;
    146   AtlTrace("delegate_execute enter\n");
    147 
    148   CommandLine::Init(0, NULL);
    149   HRESULT ret_code = E_UNEXPECTED;
    150   DelegateExecuteOperation operation;
    151   if (operation.Init(CommandLine::ForCurrentProcess())) {
    152     switch (operation.operation_type()) {
    153       case DelegateExecuteOperation::DELEGATE_EXECUTE:
    154         ret_code = _AtlModule.WinMain(nShowCmd);
    155         break;
    156       case DelegateExecuteOperation::RELAUNCH_CHROME:
    157         ret_code = RelaunchChrome(operation);
    158         break;
    159       default:
    160         NOTREACHED();
    161     }
    162   }
    163   AtlTrace("delegate_execute exit, code = %d\n", ret_code);
    164   return ret_code;
    165 }
    166