Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / cloud_print / virtual_driver / win / install / setup.cc
blobfe764a108e30bee9760b25e0da07698d004eced3
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.
5 #include <windows.h>
6 #include <setupapi.h> // Must be included after windows.h
7 #include <winspool.h>
8 #include <iomanip>
10 #include "base/at_exit.h"
11 #include "base/command_line.h"
12 #include "base/file_version_info_win.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/logging.h"
16 #include "base/path_service.h"
17 #include "base/process/launch.h"
18 #include "base/process/process.h"
19 #include "base/strings/string16.h"
20 #include "base/strings/string_util.h"
21 #include "base/win/registry.h"
22 #include "base/win/scoped_handle.h"
23 #include "base/win/windows_version.h"
24 #include "cloud_print/common/win/cloud_print_utils.h"
25 #include "cloud_print/common/win/install_utils.h"
26 #include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
27 #include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
28 #include "grit/virtual_driver_setup_resources.h"
30 #include <strsafe.h> // Must be after base headers to avoid deprecation
31 // warnings.
33 namespace cloud_print {
35 namespace {
37 const wchar_t kNameValue[] = L"GCP Virtual Driver";
38 const wchar_t kUninstallId[] = L"{74AA24E0-AC50-4B28-BA46-9CF05467C9B7}";
39 const wchar_t kGcpUrl[] = L"http://www.google.com/cloudprint";
41 const wchar_t kDataFileName[] = L"gcp_driver.gpd";
42 const wchar_t kDriverName[] = L"MXDWDRV.DLL";
43 const wchar_t kUiDriverName[] = L"UNIDRVUI.DLL";
44 const wchar_t kHelpName[] = L"UNIDRV.HLP";
45 const wchar_t* kDependencyList[] = {
46 kDriverName,
47 kHelpName,
48 kUiDriverName,
49 L"STDDTYPE.GDL",
50 L"STDNAMES.GPD",
51 L"STDSCHEM.GDL",
52 L"STDSCHMX.GDL",
53 L"UNIDRV.DLL",
54 L"UNIRES.DLL",
55 L"XPSSVCS.DLL",
58 const char kDelete[] = "delete";
59 const char kInstallSwitch[] = "install";
60 const char kRegisterSwitch[] = "register";
61 const char kUninstallSwitch[] = "uninstall";
62 const char kUnregisterSwitch[] = "unregister";
64 base::FilePath GetSystemPath(const base::string16& binary) {
65 base::FilePath path;
66 if (!PathService::Get(base::DIR_SYSTEM, &path)) {
67 LOG(ERROR) << "Unable to get system path.";
68 return path;
70 return path.Append(binary);
73 base::FilePath GetNativeSystemPath(const base::string16& binary) {
74 if (!IsSystem64Bit())
75 return GetSystemPath(binary);
76 base::FilePath path;
77 // Sysnative will bypass filesystem redirection and give us
78 // the location of the 64bit system32 from a 32 bit process.
79 if (!PathService::Get(base::DIR_WINDOWS, &path)) {
80 LOG(ERROR) << "Unable to get windows path.";
81 return path;
83 return path.Append(L"sysnative").Append(binary);
86 void SpoolerServiceCommand(const char* command) {
87 base::FilePath net_path = GetNativeSystemPath(L"net");
88 if (net_path.empty())
89 return;
90 base::CommandLine command_line(net_path);
91 command_line.AppendArg(command);
92 command_line.AppendArg("spooler");
93 command_line.AppendArg("/y");
95 base::LaunchOptions options;
96 options.wait = true;
97 options.start_hidden = true;
98 VLOG(0) << command_line.GetCommandLineString();
99 base::LaunchProcess(command_line, options);
102 HRESULT RegisterPortMonitor(bool install, const base::FilePath& install_path) {
103 DCHECK(install || install_path.empty());
104 base::FilePath target_path = GetNativeSystemPath(GetPortMonitorDllName());
105 if (target_path.empty()) {
106 LOG(ERROR) << "Unable to get port monitor target path.";
107 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
109 if (install) {
110 base::FilePath source_path =
111 install_path.Append(GetPortMonitorDllName());
112 if (!base::CopyFile(source_path, target_path)) {
113 LOG(ERROR) << "Unable copy port monitor dll from " <<
114 source_path.value() << " to " << target_path.value();
115 return GetLastHResult();
117 } else if (!base::PathExists(target_path)) {
118 // Already removed. Just "succeed" silently.
119 return S_OK;
122 base::FilePath regsvr32_path = GetNativeSystemPath(L"regsvr32.exe");
123 if (regsvr32_path.empty()) {
124 LOG(ERROR) << "Can't find regsvr32.exe.";
125 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
128 base::CommandLine command_line(regsvr32_path);
129 command_line.AppendArg("/s");
130 if (!install) {
131 command_line.AppendArg("/u");
134 // Use system32 path here because otherwise ::AddMonitor would fail.
135 command_line.AppendArgPath(GetSystemPath(GetPortMonitorDllName()));
137 base::LaunchOptions options;
138 options.wait = true;
140 base::Process regsvr32_process =
141 base::LaunchProcess(command_line.GetCommandLineString(), options);
142 if (!regsvr32_process.IsValid()) {
143 LOG(ERROR) << "Unable to launch regsvr32.exe.";
144 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
147 DWORD exit_code = S_OK;
148 if (install) {
149 if (!GetExitCodeProcess(regsvr32_process.Handle(), &exit_code)) {
150 LOG(ERROR) << "Unable to get regsvr32.exe exit code.";
151 return GetLastHResult();
153 if (exit_code != 0) {
154 LOG(ERROR) << "Regsvr32.exe failed with " << exit_code;
155 return HRESULT_FROM_WIN32(exit_code);
157 } else {
158 if (!base::DeleteFile(target_path, false)) {
159 SpoolerServiceCommand("stop");
160 bool deleted = base::DeleteFile(target_path, false);
161 SpoolerServiceCommand("start");
163 if(!deleted) {
164 LOG(ERROR) << "Unable to delete " << target_path.value();
165 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
169 return S_OK;
172 DWORDLONG GetVersionNumber() {
173 DWORDLONG retval = 0;
174 scoped_ptr<FileVersionInfo> version_info(
175 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
176 if (version_info.get()) {
177 FileVersionInfoWin* version_info_win =
178 static_cast<FileVersionInfoWin*>(version_info.get());
179 VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info();
180 retval = fixed_file_info->dwFileVersionMS;
181 retval <<= 32;
182 retval |= fixed_file_info->dwFileVersionLS;
184 return retval;
187 UINT CALLBACK CabinetCallback(PVOID data,
188 UINT notification,
189 UINT_PTR param1,
190 UINT_PTR param2) {
191 const base::FilePath* temp_path(
192 reinterpret_cast<const base::FilePath*>(data));
193 if (notification == SPFILENOTIFY_FILEINCABINET) {
194 FILE_IN_CABINET_INFO* info =
195 reinterpret_cast<FILE_IN_CABINET_INFO*>(param1);
196 for (int i = 0; i < arraysize(kDependencyList); i++) {
197 base::FilePath base_name(info->NameInCabinet);
198 base_name = base_name.BaseName();
199 if (base::FilePath::CompareEqualIgnoreCase(base_name.value().c_str(),
200 kDependencyList[i])) {
201 StringCchCopy(info->FullTargetName, MAX_PATH,
202 temp_path->Append(kDependencyList[i]).value().c_str());
203 return FILEOP_DOIT;
206 return FILEOP_SKIP;
208 return NO_ERROR;
211 void ReadyDriverDependencies(const base::FilePath& destination) {
212 base::FilePath destination_copy(destination);
213 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
214 // GetCorePrinterDrivers and GetPrinterDriverPackagePath only exist on
215 // Vista and later. Winspool.drv must be delayloaded so these calls don't
216 // create problems on XP.
217 DWORD size = MAX_PATH;
218 wchar_t package_path[MAX_PATH] = {0};
219 CORE_PRINTER_DRIVER driver;
220 GetCorePrinterDrivers(NULL, NULL, L"{D20EA372-DD35-4950-9ED8-A6335AFE79F5}",
221 1, &driver);
222 GetPrinterDriverPackagePath(NULL, NULL, NULL, driver.szPackageID,
223 package_path, MAX_PATH, &size);
224 SetupIterateCabinet(package_path, 0, &CabinetCallback, &destination_copy);
225 } else {
226 // Driver files are in the sp3 cab.
227 base::FilePath package_path;
228 PathService::Get(base::DIR_WINDOWS, &package_path);
229 package_path = package_path.Append(L"Driver Cache\\i386\\sp3.cab");
230 SetupIterateCabinet(package_path.value().c_str(), 0, &CabinetCallback,
231 &destination_copy);
233 // Copy the rest from the driver cache or system dir.
234 base::FilePath driver_cache_path;
235 PathService::Get(base::DIR_WINDOWS, &driver_cache_path);
236 driver_cache_path = driver_cache_path.Append(L"Driver Cache\\i386");
237 for (size_t i = 0; i < arraysize(kDependencyList); ++i) {
238 base::FilePath dst_path = destination.Append(kDependencyList[i]);
239 if (!base::PathExists(dst_path)) {
240 base::FilePath src_path = driver_cache_path.Append(kDependencyList[i]);
241 if (!base::PathExists(src_path))
242 src_path = GetSystemPath(kDependencyList[i]);
243 base::CopyFile(src_path, dst_path);
249 HRESULT InstallDriver(const base::FilePath& install_path) {
250 base::ScopedTempDir temp_path;
251 if (!temp_path.CreateUniqueTempDir())
252 return HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE);
253 ReadyDriverDependencies(temp_path.path());
255 std::vector<base::string16> dependent_array;
256 // Add all files. AddPrinterDriverEx will removes unnecessary.
257 for (size_t i = 0; i < arraysize(kDependencyList); ++i) {
258 base::FilePath file_path = temp_path.path().Append(kDependencyList[i]);
259 if (base::PathExists(file_path))
260 dependent_array.push_back(file_path.value());
261 else
262 LOG(WARNING) << "File is missing: " << file_path.BaseName().value();
265 // Set up paths for the files we depend on.
266 base::FilePath data_file = install_path.Append(kDataFileName);
267 base::FilePath xps_path = temp_path.path().Append(kDriverName);
268 base::FilePath ui_path = temp_path.path().Append(kUiDriverName);
269 base::FilePath ui_help_path = temp_path.path().Append(kHelpName);
271 if (!base::PathExists(xps_path)) {
272 return HRESULT_FROM_WIN32(ERROR_BAD_DRIVER);
275 DRIVER_INFO_6 driver_info = {0};
276 // Set up supported print system version. Must be 3.
277 driver_info.cVersion = 3;
279 // None of the print API structures likes constant strings even though they
280 // don't modify the string. const_casting is the cleanest option.
281 driver_info.pDataFile = const_cast<LPWSTR>(data_file.value().c_str());
282 driver_info.pHelpFile = const_cast<LPWSTR>(ui_help_path.value().c_str());
283 driver_info.pDriverPath = const_cast<LPWSTR>(xps_path.value().c_str());
284 driver_info.pConfigFile = const_cast<LPWSTR>(ui_path.value().c_str());
286 base::string16 dependent_files(base::JoinString(dependent_array, L"\n"));
287 dependent_files.push_back(L'\n');
288 std::replace(dependent_files.begin(), dependent_files.end(), L'\n', L'\0');
289 driver_info.pDependentFiles = &dependent_files[0];
291 // Set up user visible strings.
292 base::string16 manufacturer = LoadLocalString(IDS_GOOGLE);
293 driver_info.pszMfgName = const_cast<LPWSTR>(manufacturer.c_str());
294 driver_info.pszProvider = const_cast<LPWSTR>(manufacturer.c_str());
295 driver_info.pszOEMUrl = const_cast<LPWSTR>(kGcpUrl);
296 driver_info.dwlDriverVersion = GetVersionNumber();
297 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
298 driver_info.pName = const_cast<LPWSTR>(driver_name.c_str());
300 if (!::AddPrinterDriverEx(NULL, 6, reinterpret_cast<BYTE*>(&driver_info),
301 APD_COPY_NEW_FILES | APD_COPY_FROM_DIRECTORY)) {
302 LOG(ERROR) << "Unable to add printer driver";
303 return GetLastHResult();
305 return S_OK;
308 HRESULT UninstallDriver() {
309 int tries = 3;
310 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
311 while (!DeletePrinterDriverEx(NULL,
312 NULL,
313 const_cast<LPWSTR>(driver_name.c_str()),
314 DPD_DELETE_UNUSED_FILES,
315 0) && tries > 0) {
316 if (GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER) {
317 LOG(WARNING) << "Print driver is already uninstalled.";
318 return S_OK;
320 // After deleting the printer it can take a few seconds before
321 // the driver is free for deletion. Retry a few times before giving up.
322 LOG(WARNING) << "Attempt to delete printer driver failed. Retrying.";
323 tries--;
324 Sleep(2000);
326 if (tries <= 0) {
327 HRESULT result = GetLastHResult();
328 LOG(ERROR) << "Unable to delete printer driver.";
329 return result;
331 return S_OK;
334 HRESULT InstallPrinter(void) {
335 PRINTER_INFO_2 printer_info = {0};
337 // None of the print API structures likes constant strings even though they
338 // don't modify the string. const_casting is the cleanest option.
339 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
340 printer_info.pDriverName = const_cast<LPWSTR>(driver_name.c_str());
341 printer_info.pPrinterName = const_cast<LPWSTR>(driver_name.c_str());
342 printer_info.pComment = const_cast<LPWSTR>(driver_name.c_str());
343 printer_info.pLocation = const_cast<LPWSTR>(kGcpUrl);
344 base::string16 port_name;
345 printer_info.pPortName = const_cast<LPWSTR>(kPortName);
346 printer_info.Attributes = PRINTER_ATTRIBUTE_DIRECT|PRINTER_ATTRIBUTE_LOCAL;
347 printer_info.pPrintProcessor = const_cast<LPWSTR>(L"winprint");
348 HANDLE handle = AddPrinter(NULL, 2, reinterpret_cast<BYTE*>(&printer_info));
349 if (handle == NULL) {
350 HRESULT result = GetLastHResult();
351 LOG(ERROR) << "Unable to add printer";
352 return result;
354 ClosePrinter(handle);
355 return S_OK;
358 HRESULT UninstallPrinter(void) {
359 HANDLE handle = NULL;
360 PRINTER_DEFAULTS printer_defaults = {0};
361 printer_defaults.DesiredAccess = PRINTER_ALL_ACCESS;
362 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
363 if (!OpenPrinter(const_cast<LPWSTR>(driver_name.c_str()),
364 &handle,
365 &printer_defaults)) {
366 // If we can't open the printer, it was probably already removed.
367 LOG(WARNING) << "Unable to open printer";
368 return S_OK;
370 if (!DeletePrinter(handle)) {
371 HRESULT result = GetLastHResult();
372 LOG(ERROR) << "Unable to delete printer";
373 ClosePrinter(handle);
374 return result;
376 ClosePrinter(handle);
377 return S_OK;
380 bool IsOSSupported() {
381 // We don't support XP service pack 2 or older.
382 base::win::Version version = base::win::GetVersion();
383 return (version > base::win::VERSION_XP) ||
384 ((version == base::win::VERSION_XP) &&
385 (base::win::OSInfo::GetInstance()->service_pack().major >= 3));
388 HRESULT RegisterVirtualDriver(const base::FilePath& install_path) {
389 HRESULT result = S_OK;
391 DCHECK(base::DirectoryExists(install_path));
392 if (!IsOSSupported()) {
393 LOG(ERROR) << "Requires XP SP3 or later.";
394 return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
397 result = InstallDriver(install_path);
398 if (FAILED(result)) {
399 LOG(ERROR) << "Unable to install driver.";
400 return result;
403 result = RegisterPortMonitor(true, install_path);
404 if (FAILED(result)) {
405 LOG(ERROR) << "Unable to register port monitor.";
406 return result;
409 result = InstallPrinter();
410 if (FAILED(result) &&
411 result != HRESULT_FROM_WIN32(ERROR_PRINTER_ALREADY_EXISTS)) {
412 LOG(ERROR) << "Unable to install printer.";
413 return result;
415 return S_OK;
418 HRESULT TryUnregisterVirtualDriver() {
419 HRESULT result = S_OK;
420 result = UninstallPrinter();
421 if (FAILED(result)) {
422 LOG(ERROR) << "Unable to delete printer.";
423 return result;
425 result = UninstallDriver();
426 if (FAILED(result)) {
427 LOG(ERROR) << "Unable to remove driver.";
428 return result;
430 // The second argument is ignored if the first is false.
431 result = RegisterPortMonitor(false, base::FilePath());
432 if (FAILED(result)) {
433 LOG(ERROR) << "Unable to remove port monitor.";
434 return result;
436 return S_OK;
439 HRESULT UnregisterVirtualDriver() {
440 HRESULT hr = S_FALSE;
441 for (int i = 0; i < 2; ++i) {
442 hr = TryUnregisterVirtualDriver();
443 if (SUCCEEDED(hr)) {
444 break;
446 // Restart spooler and try again.
447 SpoolerServiceCommand("stop");
448 SpoolerServiceCommand("start");
450 return hr;
453 HRESULT DoUninstall() {
454 DeleteGoogleUpdateKeys(kGoogleUpdateProductId);
455 HRESULT result = UnregisterVirtualDriver();
456 if (FAILED(result))
457 return result;
458 DeleteUninstallKey(kUninstallId);
459 DeleteProgramDir(kDelete);
460 return S_OK;
463 HRESULT DoUnregister() {
464 return UnregisterVirtualDriver();
467 HRESULT DoRegister(const base::FilePath& install_path) {
468 HRESULT result = UnregisterVirtualDriver();
469 if (FAILED(result))
470 return result;
471 return RegisterVirtualDriver(install_path);
474 HRESULT DoDelete(const base::FilePath& install_path) {
475 if (install_path.value().empty())
476 return E_INVALIDARG;
477 if (!base::DirectoryExists(install_path))
478 return S_FALSE;
479 Sleep(5000); // Give parent some time to exit.
480 return base::DeleteFile(install_path, true) ? S_OK : E_FAIL;
483 HRESULT DoInstall(const base::FilePath& install_path) {
484 HRESULT result = UnregisterVirtualDriver();
485 if (FAILED(result)) {
486 LOG(ERROR) << "Unable to unregister.";
487 return result;
489 base::FilePath old_install_path = GetInstallLocation(kUninstallId);
490 if (!old_install_path.value().empty() &&
491 install_path != old_install_path) {
492 if (base::DirectoryExists(old_install_path))
493 base::DeleteFile(old_install_path, true);
495 CreateUninstallKey(kUninstallId, LoadLocalString(IDS_DRIVER_NAME),
496 kUninstallSwitch);
497 result = RegisterVirtualDriver(install_path);
498 if (FAILED(result))
499 return result;
500 SetGoogleUpdateKeys(kGoogleUpdateProductId, kNameValue);
501 return result;
504 HRESULT ExecuteCommands() {
505 const base::CommandLine& command_line =
506 *base::CommandLine::ForCurrentProcess();
508 base::FilePath exe_path;
509 if (!PathService::Get(base::DIR_EXE, &exe_path) ||
510 !base::DirectoryExists(exe_path)) {
511 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
514 if (command_line.HasSwitch(kDelete)) {
515 return DoDelete(command_line.GetSwitchValuePath(kDelete));
516 } else if (command_line.HasSwitch(kUninstallSwitch)) {
517 return DoUninstall();
518 } else if (command_line.HasSwitch(kInstallSwitch)) {
519 return DoInstall(exe_path);
520 } else if (command_line.HasSwitch(kUnregisterSwitch)) {
521 return DoUnregister();
522 } else if (command_line.HasSwitch(kRegisterSwitch)) {
523 return DoRegister(exe_path);
526 return E_INVALIDARG;
529 } // namespace
531 } // namespace cloud_print
533 int WINAPI WinMain(__in HINSTANCE hInstance,
534 __in HINSTANCE hPrevInstance,
535 __in LPSTR lpCmdLine,
536 __in int nCmdShow) {
537 using namespace cloud_print;
539 base::AtExitManager at_exit_manager;
540 base::CommandLine::Init(0, NULL);
542 HRESULT retval = ExecuteCommands();
544 if (retval == HRESULT_FROM_WIN32(ERROR_BAD_DRIVER)) {
545 SetGoogleUpdateError(kGoogleUpdateProductId,
546 LoadLocalString(IDS_ERROR_NO_XPS));
547 } else if (FAILED(retval)) {
548 SetGoogleUpdateError(kGoogleUpdateProductId, retval);
551 VLOG(0) << GetErrorMessage(retval)
552 << " HRESULT=0x" << std::setbase(16) << retval;
554 // Installer is silent by default as required by Google Update.
555 if (base::CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
556 DisplayWindowsMessage(NULL, retval, LoadLocalString(IDS_DRIVER_NAME));
558 return retval;