OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // NOTE: This code is a legacy utility API for partners to check whether | 5 // NOTE: This code is a legacy utility API for partners to check whether |
6 // Chrome can be installed and launched. Recent updates are being made | 6 // Chrome can be installed and launched. Recent updates are being made |
7 // to add new functionality. These updates use code from Chromium, the old | 7 // to add new functionality. These updates use code from Chromium, the old |
8 // coded against the win32 api directly. If you have an itch to shave a | 8 // coded against the win32 api directly. If you have an itch to shave a |
9 // yak, feel free to re-write the old code too. | 9 // yak, feel free to re-write the old code too. |
10 | 10 |
11 #include "chrome/installer/gcapi/gcapi.h" | 11 #include "chrome/installer/gcapi/gcapi.h" |
12 | 12 |
13 #include <atlbase.h> | |
14 #include <atlcom.h> | |
15 #include <sddl.h> | 13 #include <sddl.h> |
16 #define STRSAFE_NO_DEPRECATE | 14 #define STRSAFE_NO_DEPRECATE |
17 #include <strsafe.h> | 15 #include <strsafe.h> |
18 #include <tlhelp32.h> | 16 #include <tlhelp32.h> |
19 #include <windows.h> | 17 #include <windows.h> |
20 | 18 |
21 #include <cstdlib> | 19 #include <cstdlib> |
22 #include <limits> | 20 #include <limits> |
23 #include <string> | 21 #include <string> |
24 | 22 |
25 #include "base/basictypes.h" | 23 #include "base/basictypes.h" |
24 #include "base/file_path.h" | |
25 #include "base/file_util.h" | |
26 #include "base/string_number_conversions.h" | 26 #include "base/string_number_conversions.h" |
27 #include "base/time.h" | 27 #include "base/time.h" |
28 #include "base/win/registry.h" | 28 #include "base/win/registry.h" |
29 #include "base/win/scoped_com_initializer.h" | |
30 #include "base/win/scoped_comptr.h" | |
31 #include "base/win/scoped_handle.h" | |
29 #include "chrome/installer/util/google_update_constants.h" | 32 #include "chrome/installer/util/google_update_constants.h" |
33 #include "chrome/installer/util/util_constants.h" | |
30 | 34 |
31 #include "google_update_idl.h" // NOLINT | 35 #include "google_update_idl.h" // NOLINT |
32 | 36 |
37 using base::win::RegKey; | |
38 using base::win::ScopedCOMInitializer; | |
39 using base::win::ScopedComPtr; | |
40 using base::win::ScopedHandle; | |
41 using base::Time; | |
grt (UTC plus 2)
2011/12/05 15:14:22
what do you think about moving these above RegKey
robertshield
2011/12/05 18:18:00
I think that's a splendid idea.
| |
42 using base::TimeDelta; | |
43 | |
33 namespace { | 44 namespace { |
34 | 45 |
35 const wchar_t kChromeRegClientsKey[] = | 46 const wchar_t kChromeRegClientsKey[] = |
36 L"Software\\Google\\Update\\Clients\\" | 47 L"Software\\Google\\Update\\Clients\\" |
37 L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; | 48 L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; |
38 const wchar_t kChromeRegClientStateKey[] = | 49 const wchar_t kChromeRegClientStateKey[] = |
39 L"Software\\Google\\Update\\ClientState\\" | 50 L"Software\\Google\\Update\\ClientState\\" |
40 L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; | 51 L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; |
41 const wchar_t kChromeRegClientStateMediumKey[] = | 52 const wchar_t kChromeRegClientStateMediumKey[] = |
42 L"Software\\Google\\Update\\ClientStateMedium\\" | 53 L"Software\\Google\\Update\\ClientStateMedium\\" |
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
332 local_reasons |= GCCC_ERROR_ALREADYOFFERED; | 343 local_reasons |= GCCC_ERROR_ALREADYOFFERED; |
333 | 344 |
334 // Done. Copy/return results. | 345 // Done. Copy/return results. |
335 if (reasons != NULL) | 346 if (reasons != NULL) |
336 *reasons = local_reasons; | 347 *reasons = local_reasons; |
337 | 348 |
338 return (local_reasons == 0); | 349 return (local_reasons == 0); |
339 } | 350 } |
340 | 351 |
341 BOOL __stdcall LaunchGoogleChrome() { | 352 BOOL __stdcall LaunchGoogleChrome() { |
342 wchar_t launch_cmd[MAX_PATH]; | 353 // Check to make sure we have a valid Chrome installation. |
343 size_t size = _countof(launch_cmd); | 354 HKEY install_key = HKEY_LOCAL_MACHINE; |
344 if (!ReadValueFromRegistry(HKEY_LOCAL_MACHINE, kChromeRegClientStateKey, | 355 if (!IsChromeInstalled(install_key)) { |
345 kChromeRegLastLaunchCmd, launch_cmd, &size)) { | 356 install_key = HKEY_CURRENT_USER; |
346 size = _countof(launch_cmd); | 357 if (!IsChromeInstalled(install_key)) { |
Roger Tawa OOO till Jul 10th
2011/12/05 16:19:45
For my own info, why try HKLM before HKCU?
robertshield
2011/12/05 18:18:00
If both exist and the user-level one is launched,
| |
347 if (!ReadValueFromRegistry(HKEY_LOCAL_MACHINE, kChromeRegClientStateKey, | |
348 kChromeRegLaunchCmd, launch_cmd, &size)) { | |
349 return false; | 358 return false; |
350 } | 359 } |
351 } | 360 } |
352 | 361 |
353 HRESULT hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); | 362 // Now grab the uninstall string from the appropriate ClientState key |
354 if (hr != S_OK) { | 363 // and use that as the base for a path to chrome.exe. |
355 if (hr == S_FALSE) | 364 FilePath chrome_exe_path; |
356 ::CoUninitialize(); | 365 RegKey client_state(install_key, kChromeRegClientStateKey, KEY_QUERY_VALUE); |
357 return false; | 366 if (client_state.Valid()) { |
367 std::wstring uninstall_string; | |
368 if (client_state.ReadValue(installer::kUninstallStringField, | |
369 &uninstall_string) == ERROR_SUCCESS) { | |
370 // The uninstall path contains the path to setup.exe which is two levels | |
371 // down from chrome.exe. Move up two levels (plus one to drop the file | |
372 // name) and look for chrome.exe from there. | |
373 FilePath uninstall_path(uninstall_string); | |
374 chrome_exe_path = uninstall_path.DirName() | |
375 .DirName() | |
376 .DirName() | |
377 .Append(installer::kChromeExe); | |
378 if (!file_util::PathExists(chrome_exe_path)) { | |
379 // By way of mild future proofing, look up one to see if there's a | |
380 // chrome.exe in the version directory | |
381 chrome_exe_path = | |
382 uninstall_path.DirName().DirName().Append(installer::kChromeExe); | |
383 } | |
Roger Tawa OOO till Jul 10th
2011/12/05 16:19:45
You don't want to do another path exists check her
robertshield
2011/12/05 18:18:00
Good idea, done.
| |
384 } | |
358 } | 385 } |
359 | 386 |
387 ScopedCOMInitializer com_initializer; | |
360 if (::CoInitializeSecurity(NULL, -1, NULL, NULL, | 388 if (::CoInitializeSecurity(NULL, -1, NULL, NULL, |
361 RPC_C_AUTHN_LEVEL_PKT_PRIVACY, | 389 RPC_C_AUTHN_LEVEL_PKT_PRIVACY, |
362 RPC_C_IMP_LEVEL_IDENTIFY, NULL, | 390 RPC_C_IMP_LEVEL_IDENTIFY, NULL, |
363 EOAC_DYNAMIC_CLOAKING, NULL) != S_OK) { | 391 EOAC_DYNAMIC_CLOAKING, NULL) != S_OK) { |
364 ::CoUninitialize(); | |
365 return false; | 392 return false; |
366 } | 393 } |
367 | 394 |
368 bool impersonation_success = false; | 395 bool impersonation_success = false; |
369 if (IsRunningElevated()) { | 396 if (IsRunningElevated()) { |
370 wchar_t* curr_proc_sid; | 397 wchar_t* curr_proc_sid; |
371 if (!GetUserIdForProcess(GetCurrentProcessId(), &curr_proc_sid)) { | 398 if (!GetUserIdForProcess(GetCurrentProcessId(), &curr_proc_sid)) { |
372 ::CoUninitialize(); | |
373 return false; | 399 return false; |
374 } | 400 } |
375 | 401 |
376 DWORD pid = 0; | 402 DWORD pid = 0; |
377 ::GetWindowThreadProcessId(::GetShellWindow(), &pid); | 403 ::GetWindowThreadProcessId(::GetShellWindow(), &pid); |
378 if (pid <= 0) { | 404 if (pid <= 0) { |
379 ::LocalFree(curr_proc_sid); | 405 ::LocalFree(curr_proc_sid); |
380 ::CoUninitialize(); | |
381 return false; | 406 return false; |
382 } | 407 } |
383 | 408 |
384 wchar_t* exp_proc_sid; | 409 wchar_t* exp_proc_sid; |
385 if (GetUserIdForProcess(pid, &exp_proc_sid)) { | 410 if (GetUserIdForProcess(pid, &exp_proc_sid)) { |
386 if (_wcsicmp(curr_proc_sid, exp_proc_sid) == 0) { | 411 if (_wcsicmp(curr_proc_sid, exp_proc_sid) == 0) { |
387 HANDLE process_handle = ::OpenProcess( | 412 ScopedHandle process_handle( |
388 PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, TRUE, pid); | 413 ::OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, |
389 if (process_handle != NULL) { | 414 TRUE, |
415 pid)); | |
416 if (process_handle.IsValid()) { | |
390 HANDLE process_token = NULL; | 417 HANDLE process_token = NULL; |
391 HANDLE user_token = NULL; | 418 HANDLE user_token = NULL; |
392 if (::OpenProcessToken(process_handle, TOKEN_DUPLICATE | TOKEN_QUERY, | 419 if (::OpenProcessToken(process_handle, TOKEN_DUPLICATE | TOKEN_QUERY, |
393 &process_token) && | 420 &process_token) && |
394 ::DuplicateTokenEx(process_token, | 421 ::DuplicateTokenEx(process_token, |
395 TOKEN_IMPERSONATE | TOKEN_QUERY | | 422 TOKEN_IMPERSONATE | TOKEN_QUERY | |
396 TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE, | 423 TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE, |
397 NULL, SecurityImpersonation, | 424 NULL, SecurityImpersonation, |
398 TokenPrimary, &user_token) && | 425 TokenPrimary, &user_token) && |
399 (::ImpersonateLoggedOnUser(user_token) != 0)) { | 426 (::ImpersonateLoggedOnUser(user_token) != 0)) { |
400 impersonation_success = true; | 427 impersonation_success = true; |
401 } | 428 } |
402 if (user_token) | 429 if (user_token) |
403 ::CloseHandle(user_token); | 430 ::CloseHandle(user_token); |
404 if (process_token) | 431 if (process_token) |
405 ::CloseHandle(process_token); | 432 ::CloseHandle(process_token); |
406 ::CloseHandle(process_handle); | |
407 } | 433 } |
408 } | 434 } |
409 ::LocalFree(exp_proc_sid); | 435 ::LocalFree(exp_proc_sid); |
410 } | 436 } |
411 | 437 |
412 ::LocalFree(curr_proc_sid); | 438 ::LocalFree(curr_proc_sid); |
413 if (!impersonation_success) { | 439 if (!impersonation_success) { |
414 ::CoUninitialize(); | |
415 return false; | 440 return false; |
416 } | 441 } |
417 } | 442 } |
418 | 443 |
419 bool ret = false; | 444 bool ret = false; |
420 CComPtr<IProcessLauncher> ipl; | 445 ScopedComPtr<IProcessLauncher> ipl; |
421 if (!FAILED(ipl.CoCreateInstance(__uuidof(ProcessLauncherClass), NULL, | 446 if (SUCCEEDED(ipl.CreateInstance(__uuidof(ProcessLauncherClass), |
grt (UTC plus 2)
2011/12/05 15:14:22
what, !FAILED implies SUCCEEDED to you?
robertshield
2011/12/05 18:18:00
#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)
#defi
| |
447 NULL, | |
422 CLSCTX_LOCAL_SERVER))) { | 448 CLSCTX_LOCAL_SERVER))) { |
423 if (!FAILED(ipl->LaunchCmdLine(launch_cmd))) | 449 if (SUCCEEDED(ipl->LaunchCmdLine(chrome_exe_path.value().c_str()))) |
424 ret = true; | 450 ret = true; |
425 ipl.Release(); | 451 ipl.Release(); |
426 } | 452 } |
427 | 453 |
428 if (impersonation_success) | 454 if (impersonation_success) |
429 ::RevertToSelf(); | 455 ::RevertToSelf(); |
430 ::CoUninitialize(); | |
431 return ret; | 456 return ret; |
432 } | 457 } |
433 | 458 |
434 BOOL __stdcall LaunchGoogleChromeWithDimensions(int x, | 459 BOOL __stdcall LaunchGoogleChromeWithDimensions(int x, |
435 int y, | 460 int y, |
436 int width, | 461 int width, |
437 int height) { | 462 int height, |
463 bool in_background) { | |
438 if (!LaunchGoogleChrome()) | 464 if (!LaunchGoogleChrome()) |
439 return false; | 465 return false; |
440 | 466 |
441 HWND handle = NULL; | 467 HWND handle = NULL; |
442 int seconds_elapsed = 0; | 468 int seconds_elapsed = 0; |
443 | 469 |
444 // Chrome may have been launched, but the window may not have appeared | 470 // Chrome may have been launched, but the window may not have appeared |
445 // yet. Wait for it to appear for 10 seconds, but exit if it takes longer | 471 // yet. Wait for it to appear for 10 seconds, but exit if it takes longer |
446 // than that. | 472 // than that. |
447 while (!handle && seconds_elapsed < 10) { | 473 while (!handle && seconds_elapsed < 10) { |
448 handle = FindWindowEx(NULL, handle, L"Chrome_WindowImpl_0", NULL); | 474 handle = FindWindowEx(NULL, handle, L"Chrome_WidgetWin_0", NULL); |
grt (UTC plus 2)
2011/12/05 15:14:22
how about making a kChromeWindowClass for this?
robertshield
2011/12/05 18:18:00
Done.
| |
449 if (!handle) { | 475 if (!handle) { |
450 Sleep(1000); | 476 Sleep(1000); |
451 seconds_elapsed++; | 477 seconds_elapsed++; |
452 } | 478 } |
453 } | 479 } |
454 | 480 |
455 if (!handle) | 481 if (!handle) |
456 return false; | 482 return false; |
457 | 483 |
458 // At this point, there are several top-level Chrome windows | 484 // At this point, there are several top-level Chrome windows |
459 // but we only want the window that has child windows. | 485 // but we only want the window that has child windows. |
460 | 486 |
461 // This loop iterates through all of the top-level Windows named | 487 // This loop iterates through all of the top-level Windows named |
462 // Chrome_WindowImpl_0, and looks for the first one with any children. | 488 // Chrome_WindowImpl_0, and looks for the first one with any children. |
463 while (handle && !FindWindowEx(handle, NULL, L"Chrome_WindowImpl_0", NULL)) { | 489 while (handle && !FindWindowEx(handle, NULL, L"Chrome_WidgetWin_0", NULL)) { |
464 // Get the next top-level Chrome window. | 490 // Get the next top-level Chrome window. |
465 handle = FindWindowEx(NULL, handle, L"Chrome_WindowImpl_0", NULL); | 491 handle = FindWindowEx(NULL, handle, L"Chrome_WidgetWin_0", NULL); |
466 } | 492 } |
467 | 493 |
494 HWND set_window_hwnd_insert_after = in_background ? HWND_BOTTOM : NULL; | |
495 DWORD set_window_flags = in_background ? SWP_NOACTIVATE : SWP_NOZORDER; | |
496 | |
468 return (handle && | 497 return (handle && |
469 SetWindowPos(handle, 0, x, y, width, height, SWP_NOZORDER)); | 498 SetWindowPos(handle, set_window_hwnd_insert_after, x, y, |
499 width, height, set_window_flags)); | |
470 } | 500 } |
471 | 501 |
472 int __stdcall GoogleChromeDaysSinceLastRun() { | 502 int __stdcall GoogleChromeDaysSinceLastRun() { |
473 using base::win::RegKey; | |
474 using base::Time; | |
475 using base::TimeDelta; | |
476 | |
477 int days_since_last_run = std::numeric_limits<int>::max(); | 503 int days_since_last_run = std::numeric_limits<int>::max(); |
478 | 504 |
479 struct { | 505 struct { |
480 HKEY hive; | 506 HKEY hive; |
481 const wchar_t* path; | 507 const wchar_t* path; |
482 } reg_data[] = { | 508 } reg_data[] = { |
483 { HKEY_LOCAL_MACHINE, kChromeRegClientStateMediumKey }, | 509 { HKEY_LOCAL_MACHINE, kChromeRegClientStateMediumKey }, |
484 { HKEY_CURRENT_USER, kChromeRegClientStateKey } | 510 { HKEY_CURRENT_USER, kChromeRegClientStateKey } |
485 }; | 511 }; |
486 | 512 |
(...skipping 20 matching lines...) Expand all Loading... | |
507 } | 533 } |
508 } | 534 } |
509 } | 535 } |
510 | 536 |
511 if (days_since_last_run == std::numeric_limits<int>::max()) { | 537 if (days_since_last_run == std::numeric_limits<int>::max()) { |
512 days_since_last_run = -1; | 538 days_since_last_run = -1; |
513 } | 539 } |
514 | 540 |
515 return days_since_last_run; | 541 return days_since_last_run; |
516 } | 542 } |
OLD | NEW |