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.
8 #include "base/command_line.h"
9 #include "base/debug/trace_event.h"
10 #include "base/environment.h"
11 #include "base/file_version_info.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/version.h"
19 #include "base/win/registry.h"
20 #include "chrome/app/breakpad_win.h"
21 #include "chrome/app/client_util.h"
22 #include "chrome/common/chrome_constants.h"
23 #include "chrome/common/chrome_result_codes.h"
24 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/env_vars.h"
26 #include "chrome/installer/util/browser_distribution.h"
27 #include "chrome/installer/util/channel_info.h"
28 #include "chrome/installer/util/google_update_constants.h"
29 #include "chrome/installer/util/google_update_settings.h"
30 #include "chrome/installer/util/install_util.h"
31 #include "chrome/installer/util/util_constants.h"
34 // The entry point signature of chrome.dll.
35 typedef int (*DLL_MAIN
)(HINSTANCE
, sandbox::SandboxInterfaceInfo
*);
37 typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc
)();
39 // Gets chrome version according to the load path. |exe_path| must be the
40 // backslash terminated directory of the current chrome.exe.
41 // TODO(cpu): This is now only used to support metro_driver, remove it.
42 bool GetChromeVersion(const wchar_t* exe_dir
, const wchar_t* key_path
,
44 HKEY reg_root
= InstallUtil::IsPerUserInstall(exe_dir
) ? HKEY_CURRENT_USER
:
48 base::win::RegKey
key(reg_root
, key_path
, KEY_QUERY_VALUE
);
50 string16
new_chrome_exe(exe_dir
);
51 new_chrome_exe
.append(installer::kChromeNewExe
);
52 if (::PathFileExistsW(new_chrome_exe
.c_str()) &&
53 key
.ReadValue(google_update::kRegOldVersionField
,
54 version
) == ERROR_SUCCESS
) {
57 success
= (key
.ReadValue(google_update::kRegVersionField
,
58 version
) == ERROR_SUCCESS
);
65 // Expects that |dir| has a trailing backslash. |dir| is modified so it
66 // contains the full path that was tried. Caller must check for the return
67 // value not being null to determine if this path contains a valid dll.
68 HMODULE
LoadChromeWithDirectory(string16
* dir
) {
69 ::SetCurrentDirectoryW(dir
->c_str());
70 dir
->append(installer::kChromeDll
);
72 return ::LoadLibraryExW(dir
->c_str(), NULL
,
73 LOAD_WITH_ALTERED_SEARCH_PATH
);
76 void RecordDidRun(const string16
& dll_path
) {
77 bool system_level
= !InstallUtil::IsPerUserInstall(dll_path
.c_str());
78 GoogleUpdateSettings::UpdateDidRunState(true, system_level
);
81 void ClearDidRun(const string16
& dll_path
) {
82 bool system_level
= !InstallUtil::IsPerUserInstall(dll_path
.c_str());
83 GoogleUpdateSettings::UpdateDidRunState(false, system_level
);
86 #if defined(CHROME_SPLIT_DLL)
87 // Deferred initialization entry point for chrome1.dll.
88 typedef BOOL (__stdcall
*DoDeferredCrtInitFunc
)(HINSTANCE hinstance
);
90 bool InitSplitChromeDll(HMODULE mod
) {
93 DoDeferredCrtInitFunc init
= reinterpret_cast<DoDeferredCrtInitFunc
>(
94 ::GetProcAddress(mod
, "_DoDeferredCrtInit@4"));
95 return (init(mod
) == TRUE
);
100 string16
GetExecutablePath() {
101 wchar_t path
[MAX_PATH
];
102 ::GetModuleFileNameW(NULL
, path
, MAX_PATH
);
103 if (!::PathRemoveFileSpecW(path
))
105 string16
exe_path(path
);
106 return exe_path
.append(1, L
'\\');
109 //=============================================================================
111 MainDllLoader::MainDllLoader() : dll_(NULL
) {
114 MainDllLoader::~MainDllLoader() {
117 // Loading chrome is an interesting affair. First we try loading from the
118 // current directory to support run-what-you-compile and other development
120 // If that fails then we look at the --chrome-version command line flag followed
121 // by the 'CHROME_VERSION' env variable to determine if we should stick with an
122 // older dll version even if a new one is available to support upgrade-in-place
124 // If that fails then finally we look at the registry which should point us
125 // to the latest version. This is the expected path for the first chrome.exe
126 // browser instance in an installed build.
127 HMODULE
MainDllLoader::Load(string16
* out_version
, string16
* out_file
) {
128 const CommandLine
& cmd_line
= *CommandLine::ForCurrentProcess();
129 const string16
dir(GetExecutablePath());
131 HMODULE dll
= LoadChromeWithDirectory(out_file
);
133 // Loading from same directory (for developers) failed.
134 string16 version_string
;
136 if (cmd_line
.HasSwitch(switches::kChromeVersion
)) {
137 // This is used to support Chrome Frame, see http://crbug.com/88589.
138 version_string
= cmd_line
.GetSwitchValueNative(switches::kChromeVersion
);
139 version
= Version(WideToASCII(version_string
));
141 if (!version
.IsValid()) {
142 // If a bogus command line flag was given, then abort.
143 LOG(ERROR
) << "Invalid command line version: " << version_string
;
148 // If no version on the command line, then look at the version resource in
149 // the current module and try loading that.
150 if (!version
.IsValid()) {
151 scoped_ptr
<FileVersionInfo
> file_version_info(
152 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
153 if (file_version_info
.get()) {
154 version_string
= file_version_info
->file_version();
155 version
= Version(WideToASCII(version_string
));
159 if (!version
.IsValid()) {
160 LOG(ERROR
) << "No valid Chrome version found";
165 *out_version
= version_string
;
166 out_file
->append(*out_version
).append(1, L
'\\');
167 dll
= LoadChromeWithDirectory(out_file
);
169 LOG(ERROR
) << "Failed to load Chrome DLL from " << *out_file
;
174 #if defined(CHROME_SPLIT_DLL)
175 // In split dlls mode, we need to manually initialize both DLLs because
176 // the circular dependencies between them make the loader not call the
177 // Dllmain for DLL_PROCESS_ATTACH.
178 InitSplitChromeDll(dll
);
179 InitSplitChromeDll(::GetModuleHandleA("chrome1.dll"));
185 // Launching is a matter of loading the right dll, setting the CHROME_VERSION
186 // environment variable and just calling the entry point. Derived classes can
187 // add custom code in the OnBeforeLaunch callback.
188 int MainDllLoader::Launch(HINSTANCE instance
,
189 sandbox::SandboxInterfaceInfo
* sbox_info
) {
192 dll_
= Load(&version
, &file
);
194 return chrome::RESULT_CODE_MISSING_DATA
;
196 scoped_ptr
<base::Environment
> env(base::Environment::Create());
197 env
->SetVar(chrome::kChromeVersionEnvVar
, WideToUTF8(version
));
198 // TODO(erikwright): Remove this when http://crbug.com/174953 is fixed and
200 env
->UnSetVar(env_vars::kGoogleUpdateIsMachineEnvVar
);
203 OnBeforeLaunch(file
);
205 DLL_MAIN entry_point
=
206 reinterpret_cast<DLL_MAIN
>(::GetProcAddress(dll_
, "ChromeMain"));
208 return chrome::RESULT_CODE_BAD_PROCESS_TYPE
;
210 int rc
= entry_point(instance
, sbox_info
);
211 return OnBeforeExit(rc
, file
);
214 string16
MainDllLoader::GetVersion() {
215 string16
reg_path(GetRegistryPath());
216 string16 version_string
;
217 string16
dir(GetExecutablePath());
218 if (!GetChromeVersion(dir
.c_str(), reg_path
.c_str(), &version_string
))
220 return version_string
;
223 void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
224 RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function
=
225 reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc
>(
226 ::GetProcAddress(dll_
,
227 "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
228 if (!relaunch_function
) {
229 LOG(ERROR
) << "Could not find exported function "
230 << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
236 //=============================================================================
238 class ChromeDllLoader
: public MainDllLoader
{
240 virtual string16
GetRegistryPath() {
241 string16
key(google_update::kRegPathClients
);
242 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
243 key
.append(L
"\\").append(dist
->GetAppGuid());
247 virtual void OnBeforeLaunch(const string16
& dll_path
) {
248 RecordDidRun(dll_path
);
251 virtual int OnBeforeExit(int return_code
, const string16
& dll_path
) {
252 // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
253 // so we need to reset the did_run signal so omaha does not count
254 // this run as active usage.
255 if (chrome::RESULT_CODE_NORMAL_EXIT_CANCEL
== return_code
) {
256 ClearDidRun(dll_path
);
262 //=============================================================================
264 class ChromiumDllLoader
: public MainDllLoader
{
266 virtual string16
GetRegistryPath() {
267 BrowserDistribution
* dist
= BrowserDistribution::GetDistribution();
268 return dist
->GetVersionKey();
272 MainDllLoader
* MakeMainDllLoader() {
273 #if defined(GOOGLE_CHROME_BUILD)
274 return new ChromeDllLoader();
276 return new ChromiumDllLoader();