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, ®istration_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