Mailbox support for texture layers.
[chromium-blink-merge.git] / cloud_print / virtual_driver / win / install / setup.cc
blob32a2dba13143cc04d5d6d9361cfa692dfff1ef16
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>
9 #include "base/at_exit.h"
10 #include "base/command_line.h"
11 #include "base/file_util.h"
12 #include "base/file_version_info_win.h"
13 #include "base/logging.h"
14 #include "base/path_service.h"
15 #include "base/process.h"
16 #include "base/process_util.h"
17 #include "base/string16.h"
18 #include "base/win/registry.h"
19 #include "base/win/scoped_handle.h"
20 #include "base/win/windows_version.h"
21 #include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
22 #include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
23 #include "grit/virtual_driver_setup_resources.h"
25 #include <strsafe.h> // Must be after base headers to avoid deprecation
26 // warnings.
28 namespace {
29 const wchar_t kVersionKey[] = L"pv";
30 const wchar_t kNameKey[] = L"name";
31 const wchar_t kNameValue[] = L"GCP Virtual Driver";
32 const wchar_t kPpdName[] = L"gcp-driver.ppd";
33 const wchar_t kDriverName[] = L"MXDWDRV.DLL";
34 const wchar_t kUiDriverName[] = L"PS5UI.DLL";
35 const wchar_t kHelpName[] = L"PSCRIPT.HLP";
36 const wchar_t* kDependencyList[] = {kDriverName, kUiDriverName, kHelpName};
37 const wchar_t kUninstallRegistry[] =
38 L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"
39 L"{74AA24E0-AC50-4B28-BA46-9CF05467C9B7}";
40 const wchar_t kInstallerName[] = L"virtual_driver_setup.exe";
41 const wchar_t kGcpUrl[] = L"http://www.google.com/cloudprint";
43 void SetOmahaKeys() {
44 base::win::RegKey key;
45 if (key.Create(HKEY_LOCAL_MACHINE, cloud_print::kKeyLocation,
46 KEY_SET_VALUE) != ERROR_SUCCESS) {
47 LOG(ERROR) << "Unable to open key";
50 // Get the version from the resource file.
51 string16 version_string;
52 scoped_ptr<FileVersionInfo> version_info(
53 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
55 if (version_info.get()) {
56 FileVersionInfoWin* version_info_win =
57 static_cast<FileVersionInfoWin*>(version_info.get());
58 version_string = version_info_win->product_version();
59 } else {
60 LOG(ERROR) << "Unable to get version string";
61 // Use a random version string so that Omaha has something to go by.
62 version_string = L"0.0.0.99";
65 if (key.WriteValue(kVersionKey, version_string.c_str()) != ERROR_SUCCESS ||
66 key.WriteValue(kNameKey, kNameValue) != ERROR_SUCCESS) {
67 LOG(ERROR) << "Unable to set registry keys";
71 void DeleteOmahaKeys() {
72 base::win::RegKey key;
73 if (key.Open(HKEY_LOCAL_MACHINE, cloud_print::kKeyLocation,
74 DELETE) != ERROR_SUCCESS) {
75 LOG(ERROR) << "Unable to open key to delete";
76 return;
78 if (key.DeleteKey(L"") != ERROR_SUCCESS) {
79 LOG(ERROR) << "Unable to delete key";
83 HRESULT GetNativeSystemPath(FilePath* path) {
84 if (cloud_print::IsSystem64Bit()) {
85 if (!PathService::Get(base::DIR_WINDOWS, path)) {
86 return ERROR_PATH_NOT_FOUND;
88 // Sysnative will bypass filesystem redirection and give us
89 // the location of the 64bit system32 from a 32 bit process.
90 *path = path->Append(L"sysnative");
91 } else {
92 if (!PathService::Get(base::DIR_SYSTEM, path)) {
93 LOG(ERROR) << "Unable to get system path.";
94 return ERROR_PATH_NOT_FOUND;
97 return S_OK;
100 HRESULT GetPortMonitorTargetPath(FilePath* path) {
101 HRESULT result = GetNativeSystemPath(path);
102 if (SUCCEEDED(result))
103 *path = path->Append(cloud_print::GetPortMonitorDllName());
104 return result;
107 HRESULT GetRegsvr32Path(FilePath* path) {
108 HRESULT result = GetNativeSystemPath(path);
109 if (SUCCEEDED(result))
110 *path = path->Append(FilePath(L"regsvr32.exe"));
111 return result;
114 HRESULT RegisterPortMonitor(bool install, const FilePath& install_path) {
115 FilePath target_path;
116 HRESULT result = S_OK;
117 result = GetPortMonitorTargetPath(&target_path);
118 if (!SUCCEEDED(result)) {
119 LOG(ERROR) << "Unable to get port monitor target path.";
120 return result;
122 string16 source;
123 source = cloud_print::GetPortMonitorDllName();
124 FilePath source_path = install_path.Append(source);
125 if (install) {
126 if (!file_util::CopyFileW(source_path, target_path)) {
127 LOG(ERROR) << "Unable copy port monitor dll from " <<
128 source_path.value() << " to " << target_path.value();
129 return ERROR_ACCESS_DENIED;
131 } else if (!file_util::PathExists(source_path)) {
132 // Already removed. Just "succeed" silently.
133 return S_OK;
136 FilePath regsvr32_path;
137 result = GetRegsvr32Path(&regsvr32_path);
138 if (!SUCCEEDED(result)) {
139 LOG(ERROR) << "Can't find regsvr32.exe.";
140 return result;
143 CommandLine command_line(regsvr32_path);
144 command_line.AppendArg("/s");
145 if (!install) {
146 command_line.AppendArg("/u");
149 FilePath final_path;
150 if (!PathService::Get(base::DIR_SYSTEM, &final_path)) {
151 LOG(ERROR) << "Unable to get system path.";
152 return ERROR_PATH_NOT_FOUND;
154 final_path = final_path.Append(cloud_print::GetPortMonitorDllName());
155 command_line.AppendArgPath(final_path);
157 base::LaunchOptions options;
158 HANDLE process_handle;
159 options.wait = true;
160 if (!base::LaunchProcess(command_line, options, &process_handle)) {
161 LOG(ERROR) << "Unable to launch regsvr32.exe.";
162 return ERROR_NOT_SUPPORTED;
164 base::win::ScopedHandle scoped_process_handle(process_handle);
166 DWORD exit_code = S_OK;
167 if (install) {
168 if (!GetExitCodeProcess(scoped_process_handle, &exit_code)) {
169 HRESULT result = cloud_print::GetLastHResult();
170 LOG(ERROR) << "Unable to get regsvr32.exe exit code.";
171 return result;
173 if (exit_code != 0) {
174 LOG(ERROR) << "Regsvr32.exe failed with " << exit_code;
175 return HRESULT_FROM_WIN32(exit_code);
177 } else {
178 if (!file_util::Delete(target_path, false)) {
179 LOG(ERROR) << "Unable to delete " << target_path.value();
180 return ERROR_ACCESS_DENIED;
183 return S_OK;
186 DWORDLONG GetVersionNumber() {
187 DWORDLONG retval = 0;
188 scoped_ptr<FileVersionInfo> version_info(
189 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
190 if (version_info.get()) {
191 FileVersionInfoWin* version_info_win =
192 static_cast<FileVersionInfoWin*>(version_info.get());
193 VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info();
194 retval = fixed_file_info->dwFileVersionMS;
195 retval <<= 32;
196 retval |= fixed_file_info->dwFileVersionLS;
198 return retval;
201 UINT CALLBACK CabinetCallback(PVOID data,
202 UINT notification,
203 UINT_PTR param1,
204 UINT_PTR param2 ) {
205 FilePath* temp_path = reinterpret_cast<FilePath*>(data);
206 if (notification == SPFILENOTIFY_FILEINCABINET) {
207 FILE_IN_CABINET_INFO* info =
208 reinterpret_cast<FILE_IN_CABINET_INFO*>(param1);
209 for (int i = 0; i < arraysize(kDependencyList); i++) {
210 FilePath base_name(info->NameInCabinet);
211 base_name = base_name.BaseName();
212 if (FilePath::CompareEqualIgnoreCase(base_name.value().c_str(),
213 kDependencyList[i])) {
214 StringCchCopy(info->FullTargetName, MAX_PATH,
215 temp_path->Append(kDependencyList[i]).value().c_str());
216 return FILEOP_DOIT;
220 return FILEOP_SKIP;
222 return NO_ERROR;
225 void ReadyPpdDependencies(const FilePath& install_path) {
226 base::win::Version version = base::win::GetVersion();
227 if (version >= base::win::VERSION_VISTA) {
228 // GetCorePrinterDrivers and GetPrinterDriverPackagePath only exist on
229 // Vista and later. Winspool.drv must be delayloaded so these calls don't
230 // create problems on XP.
231 DWORD size = MAX_PATH;
232 wchar_t package_path[MAX_PATH] = {0};
233 CORE_PRINTER_DRIVER driver;
234 GetCorePrinterDrivers(NULL,
235 NULL,
236 L"{D20EA372-DD35-4950-9ED8-A6335AFE79F5}",
238 &driver);
239 GetPrinterDriverPackagePath(NULL,
240 NULL,
241 NULL,
242 driver.szPackageID,
243 package_path,
244 MAX_PATH,
245 &size);
246 SetupIterateCabinet(package_path,
248 CabinetCallback,
249 const_cast<FilePath*>(&install_path));
250 } else {
251 // PS driver files are in the sp3 cab.
252 FilePath package_path;
253 PathService::Get(base::DIR_WINDOWS, &package_path);
254 package_path = package_path.Append(L"Driver Cache\\i386\\sp3.cab");
255 SetupIterateCabinet(package_path.value().c_str(),
257 CabinetCallback,
258 const_cast<FilePath*>(&install_path));
260 // The XPS driver files are just sitting uncompressed in the driver cache.
261 FilePath xps_path;
262 PathService::Get(base::DIR_WINDOWS, &xps_path);
263 xps_path = xps_path.Append(L"Driver Cache\\i386");
264 xps_path = xps_path.Append(kDriverName);
265 file_util::CopyFile(xps_path, install_path.Append(kDriverName));
269 HRESULT InstallPpd(const FilePath& install_path) {
270 DRIVER_INFO_6 driver_info = {0};
271 HRESULT result = S_OK;
273 // Set up paths for the files we depend on.
274 FilePath ppd_path = install_path.Append(kPpdName);
275 FilePath xps_path = install_path.Append(kDriverName);
276 FilePath ui_path = install_path.Append(kUiDriverName);
277 FilePath ui_help_path = install_path.Append(kHelpName);
278 ReadyPpdDependencies(install_path);
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>(ppd_path.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 // Set up user visible strings.
287 string16 manufacturer = cloud_print::LoadLocalString(IDS_GOOGLE);
288 driver_info.pszMfgName = const_cast<LPWSTR>(manufacturer.c_str());
289 driver_info.pszProvider = const_cast<LPWSTR>(manufacturer.c_str());
290 driver_info.pszOEMUrl = const_cast<LPWSTR>(kGcpUrl);
291 driver_info.dwlDriverVersion = GetVersionNumber();
292 string16 driver_name = cloud_print::LoadLocalString(IDS_DRIVER_NAME);
293 driver_info.pName = const_cast<LPWSTR>(driver_name.c_str());
295 // Set up supported print system version. Must be 3.
296 driver_info.cVersion = 3;
298 if (!AddPrinterDriverEx(NULL,
300 reinterpret_cast<BYTE*>(&driver_info),
301 APD_COPY_NEW_FILES|APD_COPY_FROM_DIRECTORY)) {
302 result = cloud_print::GetLastHResult();
303 LOG(ERROR) << "Unable to add printer driver";
305 return result;
308 HRESULT UninstallPpd() {
309 int tries = 10;
310 string16 driver_name = cloud_print::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 = cloud_print::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 string16 driver_name = cloud_print::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 string16 port_name;
345 printer_info.pPortName = const_cast<LPWSTR>(cloud_print::kPortName);
346 printer_info.Attributes = PRINTER_ATTRIBUTE_DIRECT|PRINTER_ATTRIBUTE_LOCAL;
347 printer_info.pPrintProcessor = L"winprint";
348 HANDLE handle = AddPrinter(NULL, 2, reinterpret_cast<BYTE*>(&printer_info));
349 if (handle == NULL) {
350 HRESULT result = cloud_print::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 string16 driver_name = cloud_print::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 = cloud_print::GetLastHResult();
372 LOG(ERROR) << "Unable to delete printer";
373 ClosePrinter(handle);
374 return result;
376 ClosePrinter(handle);
377 return S_OK;
380 void SetupUninstall(const FilePath& install_path) {
381 // Now write the Windows Uninstall entries
382 // Minimal error checking here since the install can contiunue
383 // if this fails.
384 base::win::RegKey key;
385 if (key.Create(HKEY_LOCAL_MACHINE, kUninstallRegistry,
386 KEY_SET_VALUE) != ERROR_SUCCESS) {
387 LOG(ERROR) << "Unable to open key";
388 return;
390 CommandLine uninstall_command(install_path.Append(kInstallerName));
391 uninstall_command.AppendArg("--uninstall");
392 key.WriteValue(L"UninstallString",
393 uninstall_command.GetCommandLineString().c_str());
394 key.WriteValue(L"InstallLocation", install_path.value().c_str());
397 // Get the version resource.
398 scoped_ptr<FileVersionInfo> version_info(
399 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
401 if (version_info.get()) {
402 FileVersionInfoWin* version_info_win =
403 static_cast<FileVersionInfoWin*>(version_info.get());
404 key.WriteValue(L"DisplayVersion",
405 version_info_win->file_version().c_str());
406 key.WriteValue(L"Publisher", version_info_win->company_name().c_str());
407 } else {
408 LOG(ERROR) << "Unable to get version string";
410 key.WriteValue(L"DisplayName",
411 cloud_print::LoadLocalString(IDS_DRIVER_NAME).c_str());
412 key.WriteValue(L"NoModify", 1);
413 key.WriteValue(L"NoRepair", 1);
416 void CleanupUninstall() {
417 ::RegDeleteKey(HKEY_LOCAL_MACHINE, kUninstallRegistry);
420 bool IsOSSupported() {
421 // We don't support XP service pack 2 or older.
422 base::win::Version version = base::win::GetVersion();
423 return (version > base::win::VERSION_XP) ||
424 ((version == base::win::VERSION_XP) &&
425 (base::win::OSInfo::GetInstance()->service_pack().major >= 3));
428 HRESULT InstallVirtualDriver(const FilePath& install_path) {
429 HRESULT result = S_OK;
431 if (!IsOSSupported()) {
432 LOG(ERROR) << "Requires XP SP3 or later.";
433 return ERROR_OLD_WIN_VERSION;
436 if (!file_util::CreateDirectory(install_path)) {
437 LOG(ERROR) << "Can't create install directory.";
438 return ERROR_ACCESS_DENIED;
440 SetupUninstall(install_path);
441 result = RegisterPortMonitor(true, install_path);
442 if (!SUCCEEDED(result)) {
443 LOG(ERROR) << "Unable to register port monitor.";
444 return result;
446 result = InstallPpd(install_path);
447 if (!SUCCEEDED(result)) {
448 LOG(ERROR) << "Unable to install Ppd.";
449 return result;
451 result = InstallPrinter();
452 if (!SUCCEEDED(result)) {
453 LOG(ERROR) << "Unable to install printer.";
454 return result;
456 SetOmahaKeys();
457 return S_OK;
460 void GetCurrentInstallPath(FilePath* install_path) {
461 base::win::RegKey key;
462 if (key.Open(HKEY_LOCAL_MACHINE, kUninstallRegistry,
463 KEY_QUERY_VALUE) != ERROR_SUCCESS) {
464 // Not installed.
465 *install_path = FilePath();
466 return;
468 string16 install_path_value;
469 key.ReadValue(L"InstallLocation", &install_path_value);
470 *install_path = FilePath(install_path_value);
473 HRESULT UninstallVirtualDriver() {
474 FilePath install_path;
475 GetCurrentInstallPath(&install_path);
476 if (install_path.value().empty()) {
477 return S_OK;
479 HRESULT result = S_OK;
480 result = UninstallPrinter();
481 if (!SUCCEEDED(result)) {
482 LOG(ERROR) << "Unable to uninstall Ppd.";
483 return result;
485 result = UninstallPpd();
486 if (!SUCCEEDED(result)) {
487 LOG(ERROR) << "Unable to remove Ppd.";
488 // Put the printer back since we're not able to
489 // complete the uninstallation.
490 // TODO(abodenha@chromium.org) Figure out a better way to recover.
491 // See http://code.google.com/p/chromium/issues/detail?id=123039
492 InstallPrinter();
493 return result;
495 result = RegisterPortMonitor(false, install_path);
496 if (!SUCCEEDED(result)) {
497 LOG(ERROR) << "Unable to remove port monitor.";
498 return result;
500 DeleteOmahaKeys();
501 file_util::Delete(install_path, true);
502 CleanupUninstall();
503 return S_OK;
506 HRESULT DoLaunchUninstall(const FilePath& installer_source, bool wait) {
507 FilePath temp_path;
508 if (file_util::CreateTemporaryFile(&temp_path)) {
509 file_util::CopyFile(installer_source, temp_path);
510 file_util::DeleteAfterReboot(temp_path);
511 CommandLine command_line(temp_path);
512 command_line.AppendArg("--douninstall");
513 base::LaunchOptions options;
514 options.wait = wait;
515 base::ProcessHandle process_handle;
516 if (!base::LaunchProcess(command_line, options, &process_handle)) {
517 LOG(ERROR) << "Unable to launch child uninstall.";
518 return ERROR_NOT_SUPPORTED;
520 if (wait) {
521 int exit_code = -1;
522 base::TerminationStatus status =
523 base::GetTerminationStatus(process_handle, &exit_code);
524 if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION) {
525 return exit_code;
526 } else {
527 LOG(ERROR) << "Improper termination of uninstall. " << status;
528 return E_FAIL;
532 return S_OK;
535 HRESULT LaunchChildForUninstall() {
536 FilePath installer_source;
537 if (PathService::Get(base::FILE_EXE, &installer_source)) {
538 return DoLaunchUninstall(installer_source, false);
540 return S_OK;
543 HRESULT UninstallPreviousVersion() {
544 base::win::RegKey key;
545 if (key.Open(HKEY_LOCAL_MACHINE, kUninstallRegistry,
546 KEY_QUERY_VALUE) != ERROR_SUCCESS) {
547 // Not installed.
548 return S_OK;
550 string16 install_path;
551 key.ReadValue(L"InstallLocation", &install_path);
552 FilePath installer_source(install_path);
553 installer_source = installer_source.Append(kInstallerName);
554 if (file_util::PathExists(installer_source)) {
555 return DoLaunchUninstall(installer_source, true);
557 return S_OK;
560 } // namespace
562 int WINAPI WinMain(__in HINSTANCE hInstance,
563 __in HINSTANCE hPrevInstance,
564 __in LPSTR lpCmdLine,
565 __in int nCmdShow) {
566 base::AtExitManager at_exit_manager;
567 CommandLine::Init(0, NULL);
568 HRESULT retval = S_OK;
569 if (CommandLine::ForCurrentProcess()->HasSwitch("douninstall")) {
570 retval = UninstallVirtualDriver();
571 } else if (CommandLine::ForCurrentProcess()->HasSwitch("uninstall")) {
572 retval = LaunchChildForUninstall();
573 } else {
574 retval = UninstallPreviousVersion();
575 if (SUCCEEDED(retval)) {
576 FilePath install_path;
577 retval = PathService::Get(base::DIR_EXE, &install_path);
578 if (SUCCEEDED(retval)) {
579 retval = InstallVirtualDriver(install_path);
583 // Installer is silent by default as required by Omaha.
584 if (CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
585 cloud_print::DisplayWindowsMessage(NULL, retval,
586 cloud_print::LoadLocalString(IDS_DRIVER_NAME));
588 return retval;