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
) {
50 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
51 const base::string16 clsid_string
= dist
->GetCommandExecuteImplClsid();
52 if (clsid_string
.empty())
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_
.get(), CLSCTX_LOCAL_SERVER
,
66 REGCLS_MULTIPLEUSE
| REGCLS_SUSPENDED
,
67 ®istration_token_
);
71 return ParentClass::PreMessageLoop(nShowCmd
);
74 HRESULT
PostMessageLoop() {
75 if (registration_token_
!= 0) {
76 ::CoRevokeClassObject(registration_token_
);
77 registration_token_
= 0;
82 return ParentClass::PostMessageLoop();
86 base::win::ScopedComPtr
<IUnknown
> instance_
;
87 DWORD registration_token_
;
90 DelegateExecuteModule _AtlModule
;
92 using delegate_execute::DelegateExecuteOperation
;
93 using base::win::ScopedHandle
;
95 int RelaunchChrome(const DelegateExecuteOperation
& operation
) {
96 AtlTrace("Relaunching [%ls] with flags [%ls]\n",
97 operation
.mutex().c_str(), operation
.relaunch_flags().c_str());
98 ScopedHandle
mutex(OpenMutexW(SYNCHRONIZE
, FALSE
, operation
.mutex().c_str()));
99 if (mutex
.IsValid()) {
100 const int kWaitSeconds
= 5;
101 DWORD result
= ::WaitForSingleObject(mutex
.Get(), kWaitSeconds
* 1000);
102 if (result
== WAIT_ABANDONED
) {
103 // This is the normal case. Chrome exits and windows marks the mutex as
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 base::Process process
= operation
.GetParent();
112 AtlTrace("%ds timeout. Killing Chrome %d\n", kWaitSeconds
, process
.Pid());
113 process
.Terminate(0, false);
115 AtlTrace("Failed to wait for relaunch mutex, result is 0x%x\n", result
);
118 // It is possible that chrome exits so fast that the mutex is not there.
119 AtlTrace("No relaunch mutex found\n");
122 // On Windows 8+ to launch Chrome we rely on Windows to use the
123 // IExecuteCommand interface exposed by delegate_execute to launch Chrome
124 // into Windows 8 metro mode or desktop.
125 // On Windows 7 we don't use delegate_execute and instead use plain vanilla
126 // ShellExecute to launch Chrome into ASH or desktop.
127 if (base::win::GetVersion() >= base::win::VERSION_WIN8
) {
128 base::win::ScopedCOMInitializer com_initializer
;
130 base::string16
relaunch_flags(operation
.relaunch_flags());
131 SHELLEXECUTEINFO sei
= { sizeof(sei
) };
132 sei
.fMask
= SEE_MASK_FLAG_LOG_USAGE
;
133 sei
.nShow
= SW_SHOWNORMAL
;
134 sei
.lpFile
= operation
.shortcut().value().c_str();
135 sei
.lpParameters
= relaunch_flags
.c_str();
137 AtlTrace(L
"Relaunching Chrome via shortcut [%ls]\n", sei
.lpFile
);
139 if (!::ShellExecuteExW(&sei
)) {
140 int error
= HRESULT_FROM_WIN32(::GetLastError());
141 AtlTrace("ShellExecute returned 0x%08X\n", error
);
145 base::FilePath chrome_exe_path
;
146 bool found_exe
= CommandExecuteImpl::FindChromeExe(&chrome_exe_path
);
149 bool launch_ash
= base::CommandLine::ForCurrentProcess()->HasSwitch(
150 switches::kForceImmersive
);
152 AtlTrace(L
"Relaunching Chrome into Windows ASH on Windows 7\n");
154 AtlTrace(L
"Relaunching Chrome into Desktop From ASH on Windows 7\n");
156 SHELLEXECUTEINFO sei
= { sizeof(sei
) };
157 sei
.fMask
= SEE_MASK_FLAG_LOG_USAGE
;
158 sei
.nShow
= SW_SHOWNORMAL
;
159 // No point in using the shortcut if we are launching into ASH as any
160 // additonal command line switches specified in the shortcut will be
161 // ignored. This is because we don't have a good way to send the command
162 // line switches from the viewer to the browser process.
163 sei
.lpFile
= (launch_ash
|| operation
.shortcut().empty()) ?
164 chrome_exe_path
.value().c_str() :
165 operation
.shortcut().value().c_str();
167 launch_ash
? L
"-ServerName:DefaultBrowserServer" : NULL
;
168 if (!::ShellExecuteExW(&sei
)) {
169 int error
= HRESULT_FROM_WIN32(::GetLastError());
170 AtlTrace("ShellExecute returned 0x%08X\n", error
);
178 extern "C" int WINAPI
_tWinMain(HINSTANCE
, HINSTANCE
, LPTSTR
, int nShowCmd
) {
179 scoped_ptr
<google_breakpad::ExceptionHandler
> breakpad
=
180 delegate_execute::InitializeCrashReporting();
182 base::AtExitManager exit_manager
;
183 AtlTrace("delegate_execute enter\n");
185 base::CommandLine::Init(0, NULL
);
186 HRESULT ret_code
= E_UNEXPECTED
;
187 DelegateExecuteOperation operation
;
188 if (operation
.Init(base::CommandLine::ForCurrentProcess())) {
189 switch (operation
.operation_type()) {
190 case DelegateExecuteOperation::DELEGATE_EXECUTE
:
191 ret_code
= _AtlModule
.WinMain(nShowCmd
);
193 case DelegateExecuteOperation::RELAUNCH_CHROME
:
194 ret_code
= RelaunchChrome(operation
);
200 AtlTrace("delegate_execute exit, code = %d\n", ret_code
);