Chromium Code Reviews| 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 |