blob: cb4a1c3c28bf74e1c19a8d245b079f79dc866833 [file] [log] [blame]
[email protected]e3ce40a2012-01-31 03:03:031// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]fc14cef2009-01-27 22:17:292// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]7c47ae3e2009-02-18 00:34:215#include "chrome/browser/process_singleton.h"
[email protected]fc14cef2009-01-27 22:17:296
[email protected]b1d7844b2012-08-22 22:37:477#include <shellapi.h>
8
[email protected]fc14cef2009-01-27 22:17:299#include "base/base_paths.h"
10#include "base/command_line.h"
[email protected]57999812013-02-24 05:40:5211#include "base/files/file_path.h"
[email protected]9e9b6e8e2009-12-02 08:45:0112#include "base/path_service.h"
[email protected]84c584462013-05-13 05:05:0013#include "base/process_info.h"
[email protected]fc14cef2009-01-27 22:17:2914#include "base/process_util.h"
[email protected]84c584462013-05-13 05:05:0015#include "base/strings/string_number_conversions.h"
[email protected]76fb05c2013-06-11 04:38:0516#include "base/strings/stringprintf.h"
[email protected]e309f312013-06-07 21:50:0817#include "base/strings/utf_string_conversions.h"
[email protected]84813472013-06-28 00:25:1918#include "base/time/time.h"
[email protected]f394bc62012-08-22 21:42:2919#include "base/win/metro.h"
[email protected]b3d791c92012-10-05 19:01:2420#include "base/win/registry.h"
[email protected]b90d7e802011-01-09 16:32:2021#include "base/win/scoped_handle.h"
[email protected]b3d791c92012-10-05 19:01:2422#include "base/win/win_util.h"
[email protected]6927f2e02012-09-18 00:13:4923#include "base/win/windows_version.h"
[email protected]ecb924c2011-03-17 00:34:0924#include "base/win/wrapped_window_proc.h"
[email protected]e9613b52012-11-27 22:35:1325#include "chrome/browser/browser_process.h"
[email protected]f2068f92013-05-20 05:30:1726#include "chrome/browser/browser_process_platform_part.h"
[email protected]10118162013-06-05 00:24:4527#include "chrome/browser/chrome_process_finder_win.h"
28#include "chrome/browser/metro_utils/metro_chrome_win.h"
[email protected]245900672012-10-13 02:27:1129#include "chrome/browser/shell_integration.h"
[email protected]b50892c5f2012-05-13 07:34:1430#include "chrome/browser/ui/simple_message_box.h"
[email protected]fc14cef2009-01-27 22:17:2931#include "chrome/common/chrome_constants.h"
[email protected]b3d791c92012-10-05 19:01:2432#include "chrome/common/chrome_paths.h"
33#include "chrome/common/chrome_paths_internal.h"
[email protected]cfa118a2012-10-10 22:27:5334#include "chrome/common/chrome_switches.h"
[email protected]0a194552011-09-14 17:53:3535#include "chrome/installer/util/wmi.h"
[email protected]b39ef1cb2011-10-25 04:46:5536#include "content/public/common/result_codes.h"
[email protected]34ac8f32009-02-22 23:03:2737#include "grit/chromium_strings.h"
38#include "grit/generated_resources.h"
[email protected]f394bc62012-08-22 21:42:2939#include "net/base/escape.h"
[email protected]c051a1b2011-01-21 23:30:1740#include "ui/base/l10n/l10n_util.h"
[email protected]e7661062011-01-19 22:16:5341#include "ui/base/win/hwnd_util.h"
[email protected]fc14cef2009-01-27 22:17:2942
43namespace {
44
[email protected]2b7ff5b92012-07-17 22:45:1845const char kLockfile[] = "lockfile";
46
[email protected]14649ecd2012-12-05 01:00:2447const int kMetroChromeActivationTimeoutMs = 3000;
48
49// A helper class that acquires the given |mutex| while the AutoLockMutex is in
50// scope.
51class AutoLockMutex {
52 public:
53 explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
[email protected]b9057302013-03-28 12:40:3854 DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
[email protected]14649ecd2012-12-05 01:00:2455 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
56 }
57
58 ~AutoLockMutex() {
[email protected]b9057302013-03-28 12:40:3859 BOOL released = ::ReleaseMutex(mutex_);
[email protected]14649ecd2012-12-05 01:00:2460 DPCHECK(released);
61 }
62
63 private:
64 HANDLE mutex_;
65 DISALLOW_COPY_AND_ASSIGN(AutoLockMutex);
66};
67
68// A helper class that releases the given |mutex| while the AutoUnlockMutex is
69// in scope and immediately re-acquires it when going out of scope.
70class AutoUnlockMutex {
71 public:
72 explicit AutoUnlockMutex(HANDLE mutex) : mutex_(mutex) {
[email protected]b9057302013-03-28 12:40:3873 BOOL released = ::ReleaseMutex(mutex_);
[email protected]14649ecd2012-12-05 01:00:2474 DPCHECK(released);
75 }
76
77 ~AutoUnlockMutex() {
[email protected]b9057302013-03-28 12:40:3878 DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
[email protected]14649ecd2012-12-05 01:00:2479 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
80 }
81
82 private:
83 HANDLE mutex_;
84 DISALLOW_COPY_AND_ASSIGN(AutoUnlockMutex);
85};
[email protected]b3d791c92012-10-05 19:01:2486
[email protected]f891fb32009-04-08 00:20:3287// Checks the visibility of the enumerated window and signals once a visible
[email protected]fc14cef2009-01-27 22:17:2988// window has been found.
89BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
90 bool* result = reinterpret_cast<bool*>(param);
[email protected]b9057302013-03-28 12:40:3891 *result = ::IsWindowVisible(window) != 0;
[email protected]fc14cef2009-01-27 22:17:2992 // Stops enumeration if a visible window has been found.
93 return !*result;
94}
95
[email protected]e3ce40a2012-01-31 03:03:0396// This function thunks to the object's version of the windowproc, taking in
97// consideration that there are several messages being dispatched before
98// WM_NCCREATE which we let windows handle.
99LRESULT CALLBACK ThunkWndProc(HWND hwnd, UINT message,
100 WPARAM wparam, LPARAM lparam) {
101 ProcessSingleton* singleton =
102 reinterpret_cast<ProcessSingleton*>(ui::GetWindowUserData(hwnd));
103 if (message == WM_NCCREATE) {
104 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lparam);
105 singleton = reinterpret_cast<ProcessSingleton*>(cs->lpCreateParams);
106 CHECK(singleton);
107 ui::SetWindowUserData(hwnd, singleton);
108 } else if (!singleton) {
109 return ::DefWindowProc(hwnd, message, wparam, lparam);
110 }
111 return singleton->WndProc(hwnd, message, wparam, lparam);
112}
113
[email protected]edf04b512012-02-23 09:47:43114bool ParseCommandLine(const COPYDATASTRUCT* cds,
115 CommandLine* parsed_command_line,
[email protected]650b2d52013-02-10 03:41:45116 base::FilePath* current_directory) {
[email protected]edf04b512012-02-23 09:47:43117 // We should have enough room for the shortest command (min_message_size)
118 // and also be a multiple of wchar_t bytes. The shortest command
119 // possible is L"START\0\0" (empty current directory and command line).
120 static const int min_message_size = 7;
121 if (cds->cbData < min_message_size * sizeof(wchar_t) ||
122 cds->cbData % sizeof(wchar_t) != 0) {
123 LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
124 return false;
125 }
126
127 // We split the string into 4 parts on NULLs.
128 DCHECK(cds->lpData);
129 const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
130 cds->cbData / sizeof(wchar_t));
131 const std::wstring::size_type first_null = msg.find_first_of(L'\0');
132 if (first_null == 0 || first_null == std::wstring::npos) {
133 // no NULL byte, don't know what to do
134 LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
135 ", first null = " << first_null;
136 return false;
137 }
138
139 // Decode the command, which is everything until the first NULL.
140 if (msg.substr(0, first_null) == L"START") {
141 // Another instance is starting parse the command line & do what it would
142 // have done.
143 VLOG(1) << "Handling STARTUP request from another process";
144 const std::wstring::size_type second_null =
145 msg.find_first_of(L'\0', first_null + 1);
146 if (second_null == std::wstring::npos ||
147 first_null == msg.length() - 1 || second_null == msg.length()) {
148 LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
149 "parts separated by NULLs";
150 return false;
151 }
152
153 // Get current directory.
[email protected]650b2d52013-02-10 03:41:45154 *current_directory = base::FilePath(msg.substr(first_null + 1,
[email protected]b9057302013-03-28 12:40:38155 second_null - first_null));
[email protected]edf04b512012-02-23 09:47:43156
157 const std::wstring::size_type third_null =
158 msg.find_first_of(L'\0', second_null + 1);
159 if (third_null == std::wstring::npos ||
160 third_null == msg.length()) {
161 LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
162 "parts separated by NULLs";
163 }
164
165 // Get command line.
166 const std::wstring cmd_line =
167 msg.substr(second_null + 1, third_null - second_null);
168 *parsed_command_line = CommandLine::FromString(cmd_line);
169 return true;
170 }
171 return false;
172}
173
[email protected]b3d791c92012-10-05 19:01:24174// Returns true if Chrome needs to be relaunched into Windows 8 immersive mode.
175// Following conditions apply:-
176// 1. Windows 8 or greater.
177// 2. Not in Windows 8 immersive mode.
[email protected]245900672012-10-13 02:27:11178// 3. Chrome is default browser.
179// 4. Process integrity level is not high.
180// 5. The profile data directory is the default directory.
181// 6. Last used mode was immersive/machine is a tablet.
[email protected]b3d791c92012-10-05 19:01:24182// TODO(ananta)
183// Move this function to a common place as the Windows 8 delegate_execute
184// handler can possibly use this.
[email protected]650b2d52013-02-10 03:41:45185bool ShouldLaunchInWindows8ImmersiveMode(const base::FilePath& user_data_dir) {
[email protected]e1c9a0e62013-06-05 06:02:23186#if defined(USE_AURA)
187 // Returning false from this function doesn't mean we don't launch immersive
188 // mode in Aura. This function is specifically called in case when we need
189 // to relaunch desktop launched chrome into immersive mode through 'relaunch'
190 // menu. In case of Aura, we will use delegate_execute to do the relaunch.
191 return false;
192#endif
193
[email protected]b3d791c92012-10-05 19:01:24194 if (base::win::GetVersion() < base::win::VERSION_WIN8)
195 return false;
196
197 if (base::win::IsProcessImmersive(base::GetCurrentProcessHandle()))
198 return false;
199
[email protected]89886652012-12-11 18:09:07200 if (ShellIntegration::GetDefaultBrowser() != ShellIntegration::IS_DEFAULT)
[email protected]245900672012-10-13 02:27:11201 return false;
202
[email protected]b3d791c92012-10-05 19:01:24203 base::IntegrityLevel integrity_level = base::INTEGRITY_UNKNOWN;
204 base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(),
205 &integrity_level);
206 if (integrity_level == base::HIGH_INTEGRITY)
207 return false;
208
[email protected]650b2d52013-02-10 03:41:45209 base::FilePath default_user_data_dir;
[email protected]b3d791c92012-10-05 19:01:24210 if (!chrome::GetDefaultUserDataDirectory(&default_user_data_dir))
211 return false;
212
213 if (default_user_data_dir != user_data_dir)
214 return false;
215
[email protected]e212a3062012-11-14 15:59:07216 base::win::RegKey reg_key;
[email protected]7e416002012-10-13 05:19:21217 DWORD reg_value = 0;
[email protected]e212a3062012-11-14 15:59:07218 if (reg_key.Create(HKEY_CURRENT_USER, chrome::kMetroRegistryPath,
219 KEY_READ) == ERROR_SUCCESS &&
[email protected]7e416002012-10-13 05:19:21220 reg_key.ReadValueDW(chrome::kLaunchModeValue,
221 &reg_value) == ERROR_SUCCESS) {
[email protected]e212a3062012-11-14 15:59:07222 return reg_value == 1;
[email protected]b3d791c92012-10-05 19:01:24223 }
[email protected]bb481b102013-04-24 21:56:12224 return base::win::IsTouchEnabledDevice();
[email protected]b3d791c92012-10-05 19:01:24225}
226
[email protected]fc14cef2009-01-27 22:17:29227} // namespace
228
[email protected]0a194552011-09-14 17:53:35229// Microsoft's Softricity virtualization breaks the sandbox processes.
230// So, if we detect the Softricity DLL we use WMI Win32_Process.Create to
231// break out of the virtualization environment.
232// http://code.google.com/p/chromium/issues/detail?id=43650
[email protected]650b2d52013-02-10 03:41:45233bool ProcessSingleton::EscapeVirtualization(
234 const base::FilePath& user_data_dir) {
[email protected]0a194552011-09-14 17:53:35235 if (::GetModuleHandle(L"sftldr_wow64.dll") ||
236 ::GetModuleHandle(L"sftldr.dll")) {
237 int process_id;
[email protected]b9057302013-03-28 12:40:38238 if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id))
[email protected]0a194552011-09-14 17:53:35239 return false;
240 is_virtualized_ = true;
241 // The new window was spawned from WMI, and won't be in the foreground.
242 // So, first we sleep while the new chrome.exe instance starts (because
243 // WaitForInputIdle doesn't work here). Then we poll for up to two more
244 // seconds and make the window foreground if we find it (or we give up).
245 HWND hwnd = 0;
246 ::Sleep(90);
247 for (int tries = 200; tries; --tries) {
[email protected]10118162013-06-05 00:24:45248 hwnd = chrome::FindRunningChromeWindow(user_data_dir);
[email protected]0a194552011-09-14 17:53:35249 if (hwnd) {
250 ::SetForegroundWindow(hwnd);
251 break;
252 }
253 ::Sleep(10);
254 }
255 return true;
256 }
257 return false;
258}
259
[email protected]dd85d452013-03-28 12:39:59260ProcessSingleton::ProcessSingleton(
261 const base::FilePath& user_data_dir,
262 const NotificationCallback& notification_callback)
[email protected]9a47c432013-04-19 20:33:55263 : window_(NULL), notification_callback_(notification_callback),
[email protected]14649ecd2012-12-05 01:00:24264 is_virtualized_(false), lock_file_(INVALID_HANDLE_VALUE),
265 user_data_dir_(user_data_dir) {
[email protected]fc14cef2009-01-27 22:17:29266}
267
[email protected]7c47ae3e2009-02-18 00:34:21268ProcessSingleton::~ProcessSingleton() {
[email protected]a9b36c92012-06-18 08:47:57269 // We need to unregister the window as late as possible so that we can detect
270 // another instance of chrome running. Otherwise we may end up writing out
271 // data while a new chrome is starting up.
272 if (window_) {
273 ::DestroyWindow(window_);
274 ::UnregisterClass(chrome::kMessageWindowClass,
275 base::GetModuleFromAddress(&ThunkWndProc));
276 }
[email protected]2b7ff5b92012-07-17 22:45:18277 if (lock_file_ != INVALID_HANDLE_VALUE)
[email protected]b9057302013-03-28 12:40:38278 ::CloseHandle(lock_file_);
[email protected]fc14cef2009-01-27 22:17:29279}
280
[email protected]14649ecd2012-12-05 01:00:24281// Code roughly based on Mozilla.
[email protected]9f20a6d02009-08-21 01:18:37282ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
[email protected]0a194552011-09-14 17:53:35283 if (is_virtualized_)
284 return PROCESS_NOTIFIED; // We already spawned the process in this case.
[email protected]e9613b52012-11-27 22:35:13285 if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
[email protected]2b7ff5b92012-07-17 22:45:18286 return LOCK_ERROR;
[email protected]e9613b52012-11-27 22:35:13287 } else if (!remote_window_) {
[email protected]9f20a6d02009-08-21 01:18:37288 return PROCESS_NONE;
[email protected]e9613b52012-11-27 22:35:13289 }
[email protected]fc14cef2009-01-27 22:17:29290
[email protected]10118162013-06-05 00:24:45291 switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
292 case chrome::NOTIFY_SUCCESS:
293 return PROCESS_NOTIFIED;
294 case chrome::NOTIFY_FAILED:
[email protected]0815b6d2009-02-11 00:39:37295 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37296 return PROCESS_NONE;
[email protected]10118162013-06-05 00:24:45297 case chrome::NOTIFY_WINDOW_HUNG:
298 remote_window_ = NULL;
299 break;
[email protected]fc14cef2009-01-27 22:17:29300 }
301
[email protected]10118162013-06-05 00:24:45302 DWORD process_id = 0;
303 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
304 if (!thread_id || !process_id) {
[email protected]0815b6d2009-02-11 00:39:37305 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37306 return PROCESS_NONE;
[email protected]0815b6d2009-02-11 00:39:37307 }
308
[email protected]fc14cef2009-01-27 22:17:29309 // The window is hung. Scan for every window to find a visible one.
310 bool visible_window = false;
[email protected]b9057302013-03-28 12:40:38311 ::EnumThreadWindows(thread_id,
312 &BrowserWindowEnumeration,
313 reinterpret_cast<LPARAM>(&visible_window));
[email protected]fc14cef2009-01-27 22:17:29314
315 // If there is a visible browser window, ask the user before killing it.
[email protected]10118162013-06-05 00:24:45316 if (visible_window &&
317 chrome::ShowMessageBox(
318 NULL,
319 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
320 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE),
321 chrome::MESSAGE_BOX_TYPE_QUESTION) == chrome::MESSAGE_BOX_RESULT_NO) {
[email protected]5da155e2012-05-26 16:31:16322 // The user denied. Quit silently.
323 return PROCESS_NOTIFIED;
[email protected]fc14cef2009-01-27 22:17:29324 }
325
326 // Time to take action. Kill the browser process.
[email protected]1fcfb202011-07-19 19:53:14327 base::KillProcessById(process_id, content::RESULT_CODE_HUNG, true);
[email protected]fc14cef2009-01-27 22:17:29328 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37329 return PROCESS_NONE;
[email protected]fc14cef2009-01-27 22:17:29330}
331
[email protected]10118162013-06-05 00:24:45332ProcessSingleton::NotifyResult
333ProcessSingleton::NotifyOtherProcessOrCreate() {
[email protected]14649ecd2012-12-05 01:00:24334 ProcessSingleton::NotifyResult result = PROCESS_NONE;
[email protected]dd85d452013-03-28 12:39:59335 if (!Create()) {
[email protected]14649ecd2012-12-05 01:00:24336 result = NotifyOtherProcess();
337 if (result == PROCESS_NONE)
338 result = PROFILE_IN_USE;
[email protected]351f7d42012-12-07 22:42:02339 } else {
[email protected]99106ba2013-05-17 23:36:35340 g_browser_process->platform_part()->PlatformSpecificCommandLineProcessing(
[email protected]351f7d42012-12-07 22:42:02341 *CommandLine::ForCurrentProcess());
[email protected]14649ecd2012-12-05 01:00:24342 }
343 return result;
[email protected]4a44bc32010-05-28 22:22:44344}
345
[email protected]14649ecd2012-12-05 01:00:24346// Look for a Chrome instance that uses the same profile directory. If there
347// isn't one, create a message window with its title set to the profile
348// directory path.
[email protected]dd85d452013-03-28 12:39:59349bool ProcessSingleton::Create() {
[email protected]14649ecd2012-12-05 01:00:24350 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!";
351 static const wchar_t kMetroActivationEventName[] =
352 L"Local\\ChromeProcessSingletonStartupMetroActivation!";
353
[email protected]10118162013-06-05 00:24:45354 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
[email protected]14649ecd2012-12-05 01:00:24355 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) {
356 // Make sure we will be the one and only process creating the window.
357 // We use a named Mutex since we are protecting against multi-process
358 // access. As documented, it's clearer to NOT request ownership on creation
359 // since it isn't guaranteed we will get it. It is better to create it
360 // without ownership and explicitly get the ownership afterward.
[email protected]b9057302013-03-28 12:40:38361 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
[email protected]14649ecd2012-12-05 01:00:24362 DPCHECK(only_me.IsValid());
363
364 AutoLockMutex auto_lock_only_me(only_me);
365
366 // We now own the mutex so we are the only process that can create the
367 // window at this time, but we must still check if someone created it
368 // between the time where we looked for it above and the time the mutex
369 // was given to us.
[email protected]10118162013-06-05 00:24:45370 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
[email protected]14649ecd2012-12-05 01:00:24371
372
373 // In Win8+, a new Chrome process launched in Desktop mode may need to be
374 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for
375 // heuristics). To accomplish this, the current Chrome activates Metro
376 // Chrome, releases the startup mutex, and waits for metro Chrome to take
377 // the singleton. From that point onward, the command line for this Chrome
378 // process will be sent to Metro Chrome by the usual channels.
379 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 &&
380 !base::win::IsMetroProcess()) {
381 // |metro_activation_event| is created right before activating a Metro
382 // Chrome (note that there can only be one Metro Chrome process; by OS
383 // design); all following Desktop processes will then wait for this event
384 // to be signaled by Metro Chrome which will do so as soon as it grabs
385 // this singleton (should any of the waiting processes timeout waiting for
386 // the signal they will try to grab the singleton for themselves which
387 // will result in a forced Desktop Chrome launch in the worst case).
388 base::win::ScopedHandle metro_activation_event(
[email protected]b9057302013-03-28 12:40:38389 ::OpenEvent(SYNCHRONIZE, FALSE, kMetroActivationEventName));
[email protected]14649ecd2012-12-05 01:00:24390 if (!metro_activation_event.IsValid() &&
391 ShouldLaunchInWindows8ImmersiveMode(user_data_dir_)) {
392 // No Metro activation is under way, but the desire is to launch in
393 // Metro mode: activate and rendez-vous with the activated process.
394 metro_activation_event.Set(
[email protected]b9057302013-03-28 12:40:38395 ::CreateEvent(NULL, TRUE, FALSE, kMetroActivationEventName));
[email protected]281b5da2013-03-05 06:13:46396 if (!chrome::ActivateMetroChrome()) {
[email protected]14649ecd2012-12-05 01:00:24397 // Failed to launch immersive Chrome, default to launching on Desktop.
398 LOG(ERROR) << "Failed to launch immersive chrome";
399 metro_activation_event.Close();
400 }
401 }
402
403 if (metro_activation_event.IsValid()) {
404 // Release |only_me| (to let Metro Chrome grab this singleton) and wait
405 // until the event is signaled (i.e. Metro Chrome was successfully
406 // activated). Ignore timeout waiting for |metro_activation_event|.
407 {
408 AutoUnlockMutex auto_unlock_only_me(only_me);
409
[email protected]b9057302013-03-28 12:40:38410 DWORD result = ::WaitForSingleObject(metro_activation_event,
411 kMetroChromeActivationTimeoutMs);
[email protected]14649ecd2012-12-05 01:00:24412 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
413 << "Result = " << result;
414 }
415
416 // Check if this singleton was successfully grabbed by another process
417 // (hopefully Metro Chrome). Failing to do so, this process will grab
418 // the singleton and launch in Desktop mode.
[email protected]10118162013-06-05 00:24:45419 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
[email protected]14649ecd2012-12-05 01:00:24420 }
421 }
422
423 if (!remote_window_) {
424 // We have to make sure there is no Chrome instance running on another
425 // machine that uses the same profile.
[email protected]650b2d52013-02-10 03:41:45426 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
[email protected]b9057302013-03-28 12:40:38427 lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
428 GENERIC_WRITE,
429 FILE_SHARE_READ,
430 NULL,
431 CREATE_ALWAYS,
432 FILE_ATTRIBUTE_NORMAL |
433 FILE_FLAG_DELETE_ON_CLOSE,
434 NULL);
435 DWORD error = ::GetLastError();
[email protected]14649ecd2012-12-05 01:00:24436 LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
437 error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
438 LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
439 << "Lock file can not be created! Error code: " << error;
440
441 if (lock_file_ != INVALID_HANDLE_VALUE) {
442 HINSTANCE hinst = base::GetModuleFromAddress(&ThunkWndProc);
443
444 WNDCLASSEX wc = {0};
445 wc.cbSize = sizeof(wc);
446 wc.lpfnWndProc = base::win::WrappedWindowProc<ThunkWndProc>;
447 wc.hInstance = hinst;
448 wc.lpszClassName = chrome::kMessageWindowClass;
449 ATOM clazz = ::RegisterClassEx(&wc);
450 DCHECK(clazz);
451
452 // Set the window's title to the path of our user data directory so
453 // other Chrome instances can decide if they should forward to us.
454 window_ = ::CreateWindow(MAKEINTATOM(clazz),
455 user_data_dir_.value().c_str(),
456 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, this);
457 CHECK(window_);
458 }
459
460 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
461 // Make sure no one is still waiting on Metro activation whether it
462 // succeeded (i.e., this is the Metro process) or failed.
463 base::win::ScopedHandle metro_activation_event(
[email protected]b9057302013-03-28 12:40:38464 ::OpenEvent(EVENT_MODIFY_STATE, FALSE, kMetroActivationEventName));
[email protected]14649ecd2012-12-05 01:00:24465 if (metro_activation_event.IsValid())
[email protected]b9057302013-03-28 12:40:38466 ::SetEvent(metro_activation_event);
[email protected]14649ecd2012-12-05 01:00:24467 }
468 }
469 }
470
[email protected]9a182832012-02-10 18:45:58471 return window_ != NULL;
[email protected]fc14cef2009-01-27 22:17:29472}
473
[email protected]9f20a6d02009-08-21 01:18:37474void ProcessSingleton::Cleanup() {
475}
476
[email protected]7c47ae3e2009-02-18 00:34:21477LRESULT ProcessSingleton::OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds) {
[email protected]edf04b512012-02-23 09:47:43478 CommandLine parsed_command_line(CommandLine::NO_PROGRAM);
[email protected]650b2d52013-02-10 03:41:45479 base::FilePath current_directory;
[email protected]edf04b512012-02-23 09:47:43480 if (!ParseCommandLine(cds, &parsed_command_line, &current_directory))
[email protected]fc14cef2009-01-27 22:17:29481 return TRUE;
[email protected]5d364542012-04-05 07:15:39482 return notification_callback_.Run(parsed_command_line, current_directory) ?
483 TRUE : FALSE;
[email protected]fc14cef2009-01-27 22:17:29484}
485
[email protected]e3ce40a2012-01-31 03:03:03486LRESULT ProcessSingleton::WndProc(HWND hwnd, UINT message,
487 WPARAM wparam, LPARAM lparam) {
[email protected]fc14cef2009-01-27 22:17:29488 switch (message) {
489 case WM_COPYDATA:
490 return OnCopyData(reinterpret_cast<HWND>(wparam),
491 reinterpret_cast<COPYDATASTRUCT*>(lparam));
492 default:
493 break;
494 }
495
496 return ::DefWindowProc(hwnd, message, wparam, lparam);
497}