blob: 54731ca30fa18e41a63c7d05b72f123b1fd09d7a [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>
[email protected]6927f2e02012-09-18 00:13:498#include <shobjidl.h>
[email protected]b1d7844b2012-08-22 22:37:479
[email protected]fc14cef2009-01-27 22:17:2910#include "base/base_paths.h"
11#include "base/command_line.h"
[email protected]f805fe82010-08-03 22:47:1012#include "base/file_path.h"
[email protected]9e9b6e8e2009-12-02 08:45:0113#include "base/path_service.h"
[email protected]fc14cef2009-01-27 22:17:2914#include "base/process_util.h"
[email protected]f394bc62012-08-22 21:42:2915#include "base/stringprintf.h"
[email protected]0f26d7b2011-01-05 19:10:4416#include "base/utf_string_conversions.h"
[email protected]f394bc62012-08-22 21:42:2917#include "base/win/metro.h"
[email protected]6927f2e02012-09-18 00:13:4918#include "base/win/scoped_com_initializer.h"
19#include "base/win/scoped_comptr.h"
[email protected]b90d7e802011-01-09 16:32:2020#include "base/win/scoped_handle.h"
[email protected]6927f2e02012-09-18 00:13:4921#include "base/win/windows_version.h"
[email protected]ecb924c2011-03-17 00:34:0922#include "base/win/wrapped_window_proc.h"
[email protected]b50892c5f2012-05-13 07:34:1423#include "chrome/browser/ui/simple_message_box.h"
[email protected]fc14cef2009-01-27 22:17:2924#include "chrome/common/chrome_constants.h"
[email protected]6927f2e02012-09-18 00:13:4925#include "chrome/installer/util/browser_distribution.h"
26#include "chrome/installer/util/shell_util.h"
[email protected]0a194552011-09-14 17:53:3527#include "chrome/installer/util/wmi.h"
[email protected]b39ef1cb2011-10-25 04:46:5528#include "content/public/common/result_codes.h"
[email protected]34ac8f32009-02-22 23:03:2729#include "grit/chromium_strings.h"
30#include "grit/generated_resources.h"
[email protected]f394bc62012-08-22 21:42:2931#include "net/base/escape.h"
[email protected]c051a1b2011-01-21 23:30:1732#include "ui/base/l10n/l10n_util.h"
[email protected]e7661062011-01-19 22:16:5333#include "ui/base/win/hwnd_util.h"
[email protected]fc14cef2009-01-27 22:17:2934
35namespace {
36
[email protected]2b7ff5b92012-07-17 22:45:1837const char kLockfile[] = "lockfile";
38
[email protected]f394bc62012-08-22 21:42:2939const char kSearchUrl[] =
40 "http://www.google.com/search?q=%s&sourceid=chrome&ie=UTF-8";
41
[email protected]f891fb32009-04-08 00:20:3242// Checks the visibility of the enumerated window and signals once a visible
[email protected]fc14cef2009-01-27 22:17:2943// window has been found.
44BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
45 bool* result = reinterpret_cast<bool*>(param);
46 *result = IsWindowVisible(window) != 0;
47 // Stops enumeration if a visible window has been found.
48 return !*result;
49}
50
[email protected]e3ce40a2012-01-31 03:03:0351// This function thunks to the object's version of the windowproc, taking in
52// consideration that there are several messages being dispatched before
53// WM_NCCREATE which we let windows handle.
54LRESULT CALLBACK ThunkWndProc(HWND hwnd, UINT message,
55 WPARAM wparam, LPARAM lparam) {
56 ProcessSingleton* singleton =
57 reinterpret_cast<ProcessSingleton*>(ui::GetWindowUserData(hwnd));
58 if (message == WM_NCCREATE) {
59 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lparam);
60 singleton = reinterpret_cast<ProcessSingleton*>(cs->lpCreateParams);
61 CHECK(singleton);
62 ui::SetWindowUserData(hwnd, singleton);
63 } else if (!singleton) {
64 return ::DefWindowProc(hwnd, message, wparam, lparam);
65 }
66 return singleton->WndProc(hwnd, message, wparam, lparam);
67}
68
[email protected]edf04b512012-02-23 09:47:4369bool ParseCommandLine(const COPYDATASTRUCT* cds,
70 CommandLine* parsed_command_line,
71 FilePath* current_directory) {
72 // We should have enough room for the shortest command (min_message_size)
73 // and also be a multiple of wchar_t bytes. The shortest command
74 // possible is L"START\0\0" (empty current directory and command line).
75 static const int min_message_size = 7;
76 if (cds->cbData < min_message_size * sizeof(wchar_t) ||
77 cds->cbData % sizeof(wchar_t) != 0) {
78 LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
79 return false;
80 }
81
82 // We split the string into 4 parts on NULLs.
83 DCHECK(cds->lpData);
84 const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
85 cds->cbData / sizeof(wchar_t));
86 const std::wstring::size_type first_null = msg.find_first_of(L'\0');
87 if (first_null == 0 || first_null == std::wstring::npos) {
88 // no NULL byte, don't know what to do
89 LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
90 ", first null = " << first_null;
91 return false;
92 }
93
94 // Decode the command, which is everything until the first NULL.
95 if (msg.substr(0, first_null) == L"START") {
96 // Another instance is starting parse the command line & do what it would
97 // have done.
98 VLOG(1) << "Handling STARTUP request from another process";
99 const std::wstring::size_type second_null =
100 msg.find_first_of(L'\0', first_null + 1);
101 if (second_null == std::wstring::npos ||
102 first_null == msg.length() - 1 || second_null == msg.length()) {
103 LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
104 "parts separated by NULLs";
105 return false;
106 }
107
108 // Get current directory.
109 *current_directory = FilePath(msg.substr(first_null + 1,
110 second_null - first_null));
111
112 const std::wstring::size_type third_null =
113 msg.find_first_of(L'\0', second_null + 1);
114 if (third_null == std::wstring::npos ||
115 third_null == msg.length()) {
116 LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
117 "parts separated by NULLs";
118 }
119
120 // Get command line.
121 const std::wstring cmd_line =
122 msg.substr(second_null + 1, third_null - second_null);
123 *parsed_command_line = CommandLine::FromString(cmd_line);
124 return true;
125 }
126 return false;
127}
128
[email protected]6927f2e02012-09-18 00:13:49129bool ActivateMetroChrome() {
130 FilePath chrome_exe;
131 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
132 NOTREACHED() << "Failed to get chrome exe path";
133 return false;
134 }
135 string16 app_id = ShellUtil::GetBrowserModelId(
136 BrowserDistribution::GetDistribution(), chrome_exe.value());
137 if (app_id.empty()) {
138 NOTREACHED() << "Failed to get chrome app user model id.";
139 return false;
140 }
141
142 base::win::ScopedComPtr<IApplicationActivationManager> activation_manager;
143 HRESULT hr = activation_manager.CreateInstance(
144 CLSID_ApplicationActivationManager);
145 if (!activation_manager) {
146 NOTREACHED() << "Failed to cocreate activation manager. Error: " << hr;
147 return false;
148 }
149
150 unsigned long pid = 0;
151 hr = activation_manager->ActivateApplication(app_id.c_str(),
152 L"open",
153 AO_NONE,
154 &pid);
155 if (FAILED(hr)) {
156 NOTREACHED() << "Failed to activate metro chrome. Error: " << hr;
157 return false;
158 }
159 return true;
160}
161
[email protected]fc14cef2009-01-27 22:17:29162} // namespace
163
[email protected]0a194552011-09-14 17:53:35164// Microsoft's Softricity virtualization breaks the sandbox processes.
165// So, if we detect the Softricity DLL we use WMI Win32_Process.Create to
166// break out of the virtualization environment.
167// http://code.google.com/p/chromium/issues/detail?id=43650
168bool ProcessSingleton::EscapeVirtualization(const FilePath& user_data_dir) {
169 if (::GetModuleHandle(L"sftldr_wow64.dll") ||
170 ::GetModuleHandle(L"sftldr.dll")) {
171 int process_id;
172 if (!installer::WMIProcess::Launch(GetCommandLineW(), &process_id))
173 return false;
174 is_virtualized_ = true;
175 // The new window was spawned from WMI, and won't be in the foreground.
176 // So, first we sleep while the new chrome.exe instance starts (because
177 // WaitForInputIdle doesn't work here). Then we poll for up to two more
178 // seconds and make the window foreground if we find it (or we give up).
179 HWND hwnd = 0;
180 ::Sleep(90);
181 for (int tries = 200; tries; --tries) {
182 hwnd = FindWindowEx(HWND_MESSAGE, NULL, chrome::kMessageWindowClass,
183 user_data_dir.value().c_str());
184 if (hwnd) {
185 ::SetForegroundWindow(hwnd);
186 break;
187 }
188 ::Sleep(10);
189 }
190 return true;
191 }
192 return false;
193}
194
[email protected]f891fb32009-04-08 00:20:32195// Look for a Chrome instance that uses the same profile directory.
[email protected]9a182832012-02-10 18:45:58196// If there isn't one, create a message window with its title set to
197// the profile directory path.
[email protected]7c47ae3e2009-02-18 00:34:21198ProcessSingleton::ProcessSingleton(const FilePath& user_data_dir)
[email protected]0a194552011-09-14 17:53:35199 : window_(NULL), locked_(false), foreground_window_(NULL),
[email protected]2b7ff5b92012-07-17 22:45:18200 is_virtualized_(false), lock_file_(INVALID_HANDLE_VALUE) {
[email protected]bbef41f02010-03-04 16:16:19201 remote_window_ = FindWindowEx(HWND_MESSAGE, NULL,
202 chrome::kMessageWindowClass,
[email protected]8a205c02011-02-04 20:41:33203 user_data_dir.value().c_str());
[email protected]0a194552011-09-14 17:53:35204 if (!remote_window_ && !EscapeVirtualization(user_data_dir)) {
[email protected]bbef41f02010-03-04 16:16:19205 // Make sure we will be the one and only process creating the window.
206 // We use a named Mutex since we are protecting against multi-process
207 // access. As documented, it's clearer to NOT request ownership on creation
208 // since it isn't guaranteed we will get it. It is better to create it
209 // without ownership and explicitly get the ownership afterward.
[email protected]e132804b2010-12-22 12:48:25210 std::wstring mutex_name(L"Local\\ChromeProcessSingletonStartup!");
[email protected]b90d7e802011-01-09 16:32:20211 base::win::ScopedHandle only_me(
212 CreateMutex(NULL, FALSE, mutex_name.c_str()));
[email protected]bbef41f02010-03-04 16:16:19213 DCHECK(only_me.Get() != NULL) << "GetLastError = " << GetLastError();
214
215 // This is how we acquire the mutex (as opposed to the initial ownership).
216 DWORD result = WaitForSingleObject(only_me, INFINITE);
217 DCHECK(result == WAIT_OBJECT_0) << "Result = " << result <<
218 "GetLastError = " << GetLastError();
219
220 // We now own the mutex so we are the only process that can create the
221 // window at this time, but we must still check if someone created it
222 // between the time where we looked for it above and the time the mutex
223 // was given to us.
224 remote_window_ = FindWindowEx(HWND_MESSAGE, NULL,
225 chrome::kMessageWindowClass,
[email protected]8a205c02011-02-04 20:41:33226 user_data_dir.value().c_str());
[email protected]9a182832012-02-10 18:45:58227 if (!remote_window_) {
[email protected]2b7ff5b92012-07-17 22:45:18228 // We have to make sure there is no Chrome instance running on another
229 // machine that uses the same profile.
230 FilePath lock_file_path = user_data_dir.AppendASCII(kLockfile);
231 lock_file_ = CreateFile(lock_file_path.value().c_str(),
232 GENERIC_WRITE,
233 FILE_SHARE_READ,
234 NULL,
235 CREATE_ALWAYS,
236 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
237 NULL);
238 DWORD error = GetLastError();
239 LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
240 error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
241 LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
242 << "Lock file can not be created! Error code: " << error;
[email protected]9a182832012-02-10 18:45:58243
[email protected]2b7ff5b92012-07-17 22:45:18244 if (lock_file_ != INVALID_HANDLE_VALUE) {
245 HINSTANCE hinst = base::GetModuleFromAddress(&ThunkWndProc);
[email protected]9a182832012-02-10 18:45:58246
[email protected]2b7ff5b92012-07-17 22:45:18247 WNDCLASSEX wc = {0};
248 wc.cbSize = sizeof(wc);
249 wc.lpfnWndProc = base::win::WrappedWindowProc<ThunkWndProc>;
250 wc.hInstance = hinst;
251 wc.lpszClassName = chrome::kMessageWindowClass;
252 ATOM clazz = ::RegisterClassEx(&wc);
253 DCHECK(clazz);
254
255 // Set the window's title to the path of our user data directory so
256 // other Chrome instances can decide if they should forward to us.
257 window_ = ::CreateWindow(MAKEINTATOM(clazz),
258 user_data_dir.value().c_str(),
259 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, this);
260 CHECK(window_);
261 }
[email protected]9a182832012-02-10 18:45:58262 }
[email protected]bbef41f02010-03-04 16:16:19263 BOOL success = ReleaseMutex(only_me);
264 DCHECK(success) << "GetLastError = " << GetLastError();
265 }
[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)
278 CloseHandle(lock_file_);
[email protected]fc14cef2009-01-27 22:17:29279}
280
[email protected]9f20a6d02009-08-21 01:18:37281ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
[email protected]0a194552011-09-14 17:53:35282 if (is_virtualized_)
283 return PROCESS_NOTIFIED; // We already spawned the process in this case.
[email protected]2b7ff5b92012-07-17 22:45:18284 if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_)
285 return LOCK_ERROR;
[email protected]0a194552011-09-14 17:53:35286 else if (!remote_window_)
[email protected]9f20a6d02009-08-21 01:18:37287 return PROCESS_NONE;
[email protected]fc14cef2009-01-27 22:17:29288
[email protected]f394bc62012-08-22 21:42:29289 DWORD process_id = 0;
290 DWORD thread_id = GetWindowThreadProcessId(remote_window_, &process_id);
291 // It is possible that the process owning this window may have died by now.
292 if (!thread_id || !process_id) {
293 remote_window_ = NULL;
294 return PROCESS_NONE;
295 }
296
297 if (base::win::IsMetroProcess()) {
298 // Interesting corner case. We are launched as a metro process but we
299 // found another chrome running. Since metro enforces single instance then
300 // the other chrome must be desktop chrome and this must be a search charm
301 // activation. This scenario is unique; other cases should be properly
302 // handled by the delegate_execute which will not activate a second chrome.
303 string16 terms;
304 base::win::MetroLaunchType launch = base::win::GetMetroLaunchParams(&terms);
305 if (launch != base::win::METRO_SEARCH) {
306 LOG(WARNING) << "In metro mode, but and launch is " << launch;
307 } else {
308 std::string query = net::EscapeQueryParamValue(UTF16ToUTF8(terms), true);
309 std::string url = base::StringPrintf(kSearchUrl, query.c_str());
310 SHELLEXECUTEINFOA sei = { sizeof(sei) };
311 sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
312 sei.nShow = SW_SHOWNORMAL;
313 sei.lpFile = url.c_str();
314 OutputDebugStringA(sei.lpFile);
315 sei.lpDirectory = "";
316 ::ShellExecuteExA(&sei);
317 }
318 return PROCESS_NOTIFIED;
319 }
320 // Non-metro mode, send our command line to the other chrome message window.
[email protected]fc14cef2009-01-27 22:17:29321 // format is "START\0<<<current directory>>>\0<<<commandline>>>".
322 std::wstring to_send(L"START\0", 6); // want the NULL in the string.
[email protected]b9696482010-11-30 23:56:18323 FilePath cur_dir;
[email protected]fc14cef2009-01-27 22:17:29324 if (!PathService::Get(base::DIR_CURRENT, &cur_dir))
[email protected]9f20a6d02009-08-21 01:18:37325 return PROCESS_NONE;
[email protected]b9696482010-11-30 23:56:18326 to_send.append(cur_dir.value());
[email protected]fc14cef2009-01-27 22:17:29327 to_send.append(L"\0", 1); // Null separator.
328 to_send.append(GetCommandLineW());
329 to_send.append(L"\0", 1); // Null separator.
330
331 // Allow the current running browser window making itself the foreground
332 // window (otherwise it will just flash in the taskbar).
[email protected]fc14cef2009-01-27 22:17:29333 AllowSetForegroundWindow(process_id);
334
[email protected]fc14cef2009-01-27 22:17:29335 COPYDATASTRUCT cds;
336 cds.dwData = 0;
337 cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
338 cds.lpData = const_cast<wchar_t*>(to_send.c_str());
339 DWORD_PTR result = 0;
340 if (SendMessageTimeout(remote_window_,
341 WM_COPYDATA,
342 NULL,
343 reinterpret_cast<LPARAM>(&cds),
344 SMTO_ABORTIFHUNG,
[email protected]8b08cbd2009-08-04 05:34:19345 kTimeoutInSeconds * 1000,
[email protected]fc14cef2009-01-27 22:17:29346 &result)) {
[email protected]0815b6d2009-02-11 00:39:37347 // It is possible that the process owning this window may have died by now.
348 if (!result) {
349 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37350 return PROCESS_NONE;
[email protected]0815b6d2009-02-11 00:39:37351 }
[email protected]6927f2e02012-09-18 00:13:49352
353 base::win::ScopedHandle process_handle;
354 if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
355 base::OpenProcessHandleWithAccess(
356 process_id, PROCESS_QUERY_INFORMATION,
357 process_handle.Receive())) {
358 if (base::win::IsProcessImmersive(process_handle.Get()))
359 ActivateMetroChrome();
360 }
[email protected]9f20a6d02009-08-21 01:18:37361 return PROCESS_NOTIFIED;
[email protected]fc14cef2009-01-27 22:17:29362 }
363
[email protected]0815b6d2009-02-11 00:39:37364 // It is possible that the process owning this window may have died by now.
365 if (!IsWindow(remote_window_)) {
366 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37367 return PROCESS_NONE;
[email protected]0815b6d2009-02-11 00:39:37368 }
369
[email protected]fc14cef2009-01-27 22:17:29370 // The window is hung. Scan for every window to find a visible one.
371 bool visible_window = false;
372 EnumThreadWindows(thread_id,
373 &BrowserWindowEnumeration,
374 reinterpret_cast<LPARAM>(&visible_window));
375
376 // If there is a visible browser window, ask the user before killing it.
[email protected]d33220292012-07-04 01:41:27377 if (visible_window && chrome::ShowMessageBox(NULL,
[email protected]5da155e2012-05-26 16:31:16378 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
379 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE),
[email protected]d33220292012-07-04 01:41:27380 chrome::MESSAGE_BOX_TYPE_QUESTION) == chrome::MESSAGE_BOX_RESULT_NO) {
[email protected]5da155e2012-05-26 16:31:16381 // The user denied. Quit silently.
382 return PROCESS_NOTIFIED;
[email protected]fc14cef2009-01-27 22:17:29383 }
384
385 // Time to take action. Kill the browser process.
[email protected]1fcfb202011-07-19 19:53:14386 base::KillProcessById(process_id, content::RESULT_CODE_HUNG, true);
[email protected]fc14cef2009-01-27 22:17:29387 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37388 return PROCESS_NONE;
[email protected]fc14cef2009-01-27 22:17:29389}
390
[email protected]5d364542012-04-05 07:15:39391ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate(
392 const NotificationCallback& notification_callback) {
[email protected]4a44bc32010-05-28 22:22:44393 NotifyResult result = NotifyOtherProcess();
394 if (result != PROCESS_NONE)
395 return result;
[email protected]5d364542012-04-05 07:15:39396 return Create(notification_callback) ? PROCESS_NONE : PROFILE_IN_USE;
[email protected]4a44bc32010-05-28 22:22:44397}
398
[email protected]9a182832012-02-10 18:45:58399// On Windows, there is no need to call Create() since the message
400// window is created in the constructor but to avoid having more
401// platform specific code in browser_main.cc we tolerate calls to
[email protected]5d364542012-04-05 07:15:39402// Create().
403bool ProcessSingleton::Create(
404 const NotificationCallback& notification_callback) {
[email protected]fc14cef2009-01-27 22:17:29405 DCHECK(!remote_window_);
[email protected]5d364542012-04-05 07:15:39406 DCHECK(notification_callback_.is_null());
407
408 if (window_ != NULL)
409 notification_callback_ = notification_callback;
410
[email protected]9a182832012-02-10 18:45:58411 return window_ != NULL;
[email protected]fc14cef2009-01-27 22:17:29412}
413
[email protected]9f20a6d02009-08-21 01:18:37414void ProcessSingleton::Cleanup() {
[email protected]9f20a6d02009-08-21 01:18:37415}
416
[email protected]7c47ae3e2009-02-18 00:34:21417LRESULT ProcessSingleton::OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds) {
[email protected]175a7a22009-05-03 15:57:53418 // If locked, it means we are not ready to process this message because
[email protected]afd20c022010-06-10 00:48:20419 // we are probably in a first run critical phase.
[email protected]175a7a22009-05-03 15:57:53420 if (locked_) {
[email protected]95259c62011-10-25 23:23:53421#if defined(USE_AURA)
422 NOTIMPLEMENTED();
423#else
[email protected]175a7a22009-05-03 15:57:53424 // Attempt to place ourselves in the foreground / flash the task bar.
[email protected]edf04b512012-02-23 09:47:43425 if (foreground_window_ != NULL && IsWindow(foreground_window_)) {
[email protected]175a7a22009-05-03 15:57:53426 SetForegroundWindow(foreground_window_);
[email protected]edf04b512012-02-23 09:47:43427 } else {
428 // Read the command line and store it. It will be replayed when the
429 // ProcessSingleton becomes unlocked.
430 CommandLine parsed_command_line(CommandLine::NO_PROGRAM);
431 FilePath current_directory;
432 if (ParseCommandLine(cds, &parsed_command_line, &current_directory))
433 saved_startup_messages_.push_back(
434 std::make_pair(parsed_command_line.argv(), current_directory));
435 }
[email protected]95259c62011-10-25 23:23:53436#endif
[email protected]175a7a22009-05-03 15:57:53437 return TRUE;
438 }
439
[email protected]edf04b512012-02-23 09:47:43440 CommandLine parsed_command_line(CommandLine::NO_PROGRAM);
441 FilePath current_directory;
442 if (!ParseCommandLine(cds, &parsed_command_line, &current_directory))
[email protected]fc14cef2009-01-27 22:17:29443 return TRUE;
[email protected]5d364542012-04-05 07:15:39444 return notification_callback_.Run(parsed_command_line, current_directory) ?
445 TRUE : FALSE;
[email protected]fc14cef2009-01-27 22:17:29446}
447
[email protected]e3ce40a2012-01-31 03:03:03448LRESULT ProcessSingleton::WndProc(HWND hwnd, UINT message,
449 WPARAM wparam, LPARAM lparam) {
[email protected]fc14cef2009-01-27 22:17:29450 switch (message) {
451 case WM_COPYDATA:
452 return OnCopyData(reinterpret_cast<HWND>(wparam),
453 reinterpret_cast<COPYDATASTRUCT*>(lparam));
454 default:
455 break;
456 }
457
458 return ::DefWindowProc(hwnd, message, wparam, lparam);
459}