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 <atlbase.h>
      6 #include <atlcom.h>
      7 #include <atlctl.h>
      8 #include <initguid.h>
      9 #include <shellapi.h>
     10 
     11 #include "base/at_exit.h"
     12 #include "base/command_line.h"
     13 #include "base/files/file_util.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/process/kill.h"
     16 #include "base/strings/string16.h"
     17 #include "base/win/scoped_com_initializer.h"
     18 #include "base/win/scoped_comptr.h"
     19 #include "base/win/scoped_handle.h"
     20 #include "base/win/windows_version.h"
     21 #include "breakpad/src/client/windows/handler/exception_handler.h"
     22 #include "chrome/common/chrome_switches.h"
     23 #include "chrome/installer/util/browser_distribution.h"
     24 #include "win8/delegate_execute/command_execute_impl.h"
     25 #include "win8/delegate_execute/crash_server_init.h"
     26 #include "win8/delegate_execute/delegate_execute_operation.h"
     27 #include "win8/delegate_execute/resource.h"
     28 
     29 using namespace ATL;
     30 
     31 // Usually classes derived from CAtlExeModuleT, or other types of ATL
     32 // COM module classes statically define their CLSID at compile time through
     33 // the use of various macros, and ATL internals takes care of creating the
     34 // class objects and registering them.  However, we need to register the same
     35 // object with different CLSIDs depending on a runtime setting, so we handle
     36 // that logic here, before the main ATL message loop runs.
     37 class DelegateExecuteModule
     38     : public ATL::CAtlExeModuleT< DelegateExecuteModule > {
     39  public :
     40   typedef ATL::CAtlExeModuleT<DelegateExecuteModule> ParentClass;
     41   typedef CComObject<CommandExecuteImpl> ImplType;
     42 
     43   DelegateExecuteModule()
     44       : registration_token_(0) {
     45   }
     46 
     47   HRESULT PreMessageLoop(int nShowCmd) {
     48     HRESULT hr = S_OK;
     49     base::string16 clsid_string;
     50     GUID clsid;
     51     BrowserDistribution* dist = BrowserDistribution::GetDistribution();
     52     if (!dist->GetCommandExecuteImplClsid(&clsid_string))
     53       return E_FAIL;
     54     hr = ::CLSIDFromString(clsid_string.c_str(), &clsid);
     55     if (FAILED(hr))
     56       return hr;
     57 
     58     // We use the same class creation logic as ATL itself.  See
     59     // _ATL_OBJMAP_ENTRY::RegisterClassObject() in atlbase.h
     60     hr = ImplType::_ClassFactoryCreatorClass::CreateInstance(
     61         ImplType::_CreatorClass::CreateInstance, IID_IUnknown,
     62         instance_.ReceiveVoid());
     63     if (FAILED(hr))
     64       return hr;
     65     hr = ::CoRegisterClassObject(clsid, instance_, CLSCTX_LOCAL_SERVER,
     66         REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED, &registration_token_);
     67     if (FAILED(hr))
     68       return hr;
     69 
     70     return ParentClass::PreMessageLoop(nShowCmd);
     71   }
     72 
     73   HRESULT PostMessageLoop() {
     74     if (registration_token_ != 0) {
     75       ::CoRevokeClassObject(registration_token_);
     76       registration_token_ = 0;
     77     }
     78 
     79     instance_.Release();
     80 
     81     return ParentClass::PostMessageLoop();
     82   }
     83 
     84  private:
     85   base::win::ScopedComPtr<IUnknown> instance_;
     86   DWORD registration_token_;
     87 };
     88 
     89 DelegateExecuteModule _AtlModule;
     90 
     91 using delegate_execute::DelegateExecuteOperation;
     92 using base::win::ScopedHandle;
     93 
     94 int RelaunchChrome(const DelegateExecuteOperation& operation) {
     95   AtlTrace("Relaunching [%ls] with flags [%ls]\n",
     96            operation.mutex().c_str(), operation.relaunch_flags().c_str());
     97   ScopedHandle mutex(OpenMutexW(SYNCHRONIZE, FALSE, operation.mutex().c_str()));
     98   if (mutex.IsValid()) {
     99     const int kWaitSeconds = 5;
    100     DWORD result = ::WaitForSingleObject(mutex.Get(), kWaitSeconds * 1000);
    101     if (result == WAIT_ABANDONED) {
    102       // This is the normal case. Chrome exits and windows marks the mutex as
    103       // abandoned.
    104     } else if (result == WAIT_OBJECT_0) {
    105       // This is unexpected. Check if somebody is not closing the mutex on
    106       // RelaunchChromehelper, the mutex should not be closed.
    107       AtlTrace("Unexpected release of the relaunch mutex!!\n");
    108     } else if (result == WAIT_TIMEOUT) {
    109       // This could mean that Chrome is hung. Proceed to exterminate.
    110       DWORD pid = operation.GetParentPid();
    111       AtlTrace("%ds timeout. Killing Chrome %d\n", kWaitSeconds, pid);
    112       base::KillProcessById(pid, 0, false);
    113     } else {
    114       AtlTrace("Failed to wait for relaunch mutex, result is 0x%x\n", result);
    115     }
    116   } else {
    117     // It is possible that chrome exits so fast that the mutex is not there.
    118     AtlTrace("No relaunch mutex found\n");
    119   }
    120 
    121   // On Windows 8+ to launch Chrome we rely on Windows to use the
    122   // IExecuteCommand interface exposed by delegate_execute to launch Chrome
    123   // into Windows 8 metro mode or desktop.
    124   // On Windows 7 we don't use delegate_execute and instead use plain vanilla
    125   // ShellExecute to launch Chrome into ASH or desktop.
    126   if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
    127     base::win::ScopedCOMInitializer com_initializer;
    128 
    129     base::string16 relaunch_flags(operation.relaunch_flags());
    130     SHELLEXECUTEINFO sei = { sizeof(sei) };
    131     sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
    132     sei.nShow = SW_SHOWNORMAL;
    133     sei.lpFile = operation.shortcut().value().c_str();
    134     sei.lpParameters = relaunch_flags.c_str();
    135 
    136     AtlTrace(L"Relaunching Chrome via shortcut [%ls]\n", sei.lpFile);
    137 
    138     if (!::ShellExecuteExW(&sei)) {
    139       int error = HRESULT_FROM_WIN32(::GetLastError());
    140       AtlTrace("ShellExecute returned 0x%08X\n", error);
    141       return error;
    142     }
    143   } else {
    144     base::FilePath chrome_exe_path;
    145     bool found_exe = CommandExecuteImpl::FindChromeExe(&chrome_exe_path);
    146     DCHECK(found_exe);
    147     if (found_exe) {
    148       bool launch_ash = CommandLine::ForCurrentProcess()->HasSwitch(
    149           switches::kForceImmersive);
    150       if (launch_ash) {
    151         AtlTrace(L"Relaunching Chrome into Windows ASH on Windows 7\n");
    152       } else {
    153         AtlTrace(L"Relaunching Chrome into Desktop From ASH on Windows 7\n");
    154       }
    155       SHELLEXECUTEINFO sei = { sizeof(sei) };
    156       sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
    157       sei.nShow = SW_SHOWNORMAL;
    158       // No point in using the shortcut if we are launching into ASH as any
    159       // additonal command line switches specified in the shortcut will be
    160       // ignored. This is because we don't have a good way to send the command
    161       // line switches from the viewer to the browser process.
    162       sei.lpFile = (launch_ash || operation.shortcut().empty()) ?
    163             chrome_exe_path.value().c_str() :
    164                 operation.shortcut().value().c_str();
    165       sei.lpParameters =
    166           launch_ash ? L"-ServerName:DefaultBrowserServer" : NULL;
    167       if (!::ShellExecuteExW(&sei)) {
    168         int error = HRESULT_FROM_WIN32(::GetLastError());
    169         AtlTrace("ShellExecute returned 0x%08X\n", error);
    170         return error;
    171       }
    172     }
    173   }
    174   return S_OK;
    175 }
    176 
    177 extern "C" int WINAPI _tWinMain(HINSTANCE , HINSTANCE, LPTSTR, int nShowCmd) {
    178   scoped_ptr<google_breakpad::ExceptionHandler> breakpad =
    179       delegate_execute::InitializeCrashReporting();
    180 
    181   base::AtExitManager exit_manager;
    182   AtlTrace("delegate_execute enter\n");
    183 
    184   CommandLine::Init(0, NULL);
    185   HRESULT ret_code = E_UNEXPECTED;
    186   DelegateExecuteOperation operation;
    187   if (operation.Init(CommandLine::ForCurrentProcess())) {
    188     switch (operation.operation_type()) {
    189       case DelegateExecuteOperation::DELEGATE_EXECUTE:
    190         ret_code = _AtlModule.WinMain(nShowCmd);
    191         break;
    192       case DelegateExecuteOperation::RELAUNCH_CHROME:
    193         ret_code = RelaunchChrome(operation);
    194         break;
    195       default:
    196         NOTREACHED();
    197     }
    198   }
    199   AtlTrace("delegate_execute exit, code = %d\n", ret_code);
    200   return ret_code;
    201 }
    202