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.
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"
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
> {
40 typedef ATL::CAtlExeModuleT
<DelegateExecuteModule
> ParentClass
;
41 typedef CComObject
<CommandExecuteImpl
> ImplType
;
43 DelegateExecuteModule()
44 : registration_token_(0) {
47 HRESULT
PreMessageLoop(int nShowCmd
) {
49 base::string16 clsid_string
;
51 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
52 if (!dist
->GetCommandExecuteImplClsid(&clsid_string
))
54 hr
= ::CLSIDFromString(clsid_string
.c_str(), &clsid
);
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());
65 hr
= ::CoRegisterClassObject(clsid
, instance_
, CLSCTX_LOCAL_SERVER
,
66 REGCLS_MULTIPLEUSE
| REGCLS_SUSPENDED
, ®istration_token_
);
70 return ParentClass::PreMessageLoop(nShowCmd
);
73 HRESULT
PostMessageLoop() {
74 if (registration_token_
!= 0) {
75 ::CoRevokeClassObject(registration_token_
);
76 registration_token_
= 0;
81 return ParentClass::PostMessageLoop();
85 base::win::ScopedComPtr
<IUnknown
> instance_
;
86 DWORD registration_token_
;
89 DelegateExecuteModule _AtlModule
;
91 using delegate_execute::DelegateExecuteOperation
;
92 using base::win::ScopedHandle
;
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
, kWaitSeconds
* 1000);
101 if (result
== WAIT_ABANDONED
) {
102 // This is the normal case. Chrome exits and windows marks the mutex as
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);
114 AtlTrace("Failed to wait for relaunch mutex, result is 0x%x\n", result
);
117 // It is possible that chrome exits so fast that the mutex is not there.
118 AtlTrace("No relaunch mutex found\n");
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
;
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();
136 AtlTrace(L
"Relaunching Chrome via shortcut [%ls]\n", sei
.lpFile
);
138 if (!::ShellExecuteExW(&sei
)) {
139 int error
= HRESULT_FROM_WIN32(::GetLastError());
140 AtlTrace("ShellExecute returned 0x%08X\n", error
);
144 base::FilePath chrome_exe_path
;
145 bool found_exe
= CommandExecuteImpl::FindChromeExe(&chrome_exe_path
);
148 bool launch_ash
= CommandLine::ForCurrentProcess()->HasSwitch(
149 switches::kForceImmersive
);
151 AtlTrace(L
"Relaunching Chrome into Windows ASH on Windows 7\n");
153 AtlTrace(L
"Relaunching Chrome into Desktop From ASH on Windows 7\n");
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();
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
);
177 extern "C" int WINAPI
_tWinMain(HINSTANCE
, HINSTANCE
, LPTSTR
, int nShowCmd
) {
178 scoped_ptr
<google_breakpad::ExceptionHandler
> breakpad
=
179 delegate_execute::InitializeCrashReporting();
181 base::AtExitManager exit_manager
;
182 AtlTrace("delegate_execute enter\n");
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
);
192 case DelegateExecuteOperation::RELAUNCH_CHROME
:
193 ret_code
= RelaunchChrome(operation
);
199 AtlTrace("delegate_execute exit, code = %d\n", ret_code
);