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.
9 #include <setupapi.h> // Must be included after windows.h
11 #include "base/at_exit.h"
12 #include "base/command_line.h"
13 #include "base/file_util.h"
14 #include "base/file_version_info_win.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/logging.h"
17 #include "base/path_service.h"
18 #include "base/process/process.h"
19 #include "base/process/launch.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/string_util.h"
22 #include "base/win/registry.h"
23 #include "base/win/scoped_handle.h"
24 #include "base/win/windows_version.h"
25 #include "cloud_print/common/win/cloud_print_utils.h"
26 #include "cloud_print/common/win/install_utils.h"
27 #include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
28 #include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
29 #include "grit/virtual_driver_setup_resources.h"
31 #include <strsafe.h> // Must be after base headers to avoid deprecation
34 namespace cloud_print {
38 const wchar_t kNameValue[] = L"GCP Virtual Driver";
39 const wchar_t kUninstallId[] = 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 const wchar_t kDataFileName[] = L"gcp_driver.gpd";
44 const wchar_t kDriverName[] = L"MXDWDRV.DLL";
45 const wchar_t kUiDriverName[] = L"UNIDRVUI.DLL";
46 const wchar_t kHelpName[] = L"UNIDRV.HLP";
47 const wchar_t* kDependencyList[] = {
60 const char kDelete[] = "delete";
61 const char kInstallSwitch[] = "install";
62 const char kRegisterSwitch[] = "register";
63 const char kUninstallSwitch[] = "uninstall";
64 const char kUnregisterSwitch[] = "unregister";
66 base::FilePath GetSystemPath(const string16& binary) {
68 if (!PathService::Get(base::DIR_SYSTEM, &path)) {
69 LOG(ERROR) << "Unable to get system path.";
72 return path.Append(binary);
75 base::FilePath GetNativeSystemPath(const string16& binary) {
77 return GetSystemPath(binary);
79 // Sysnative will bypass filesystem redirection and give us
80 // the location of the 64bit system32 from a 32 bit process.
81 if (!PathService::Get(base::DIR_WINDOWS, &path)) {
82 LOG(ERROR) << "Unable to get windows path.";
85 return path.Append(L"sysnative").Append(binary);
88 void SpoolerServiceCommand(const char* command) {
89 base::FilePath net_path = GetNativeSystemPath(L"net");
92 CommandLine command_line(net_path);
93 command_line.AppendArg(command);
94 command_line.AppendArg("spooler");
95 command_line.AppendArg("/y");
97 base::LaunchOptions options;
99 options.start_hidden = true;
100 LOG(INFO) << command_line.GetCommandLineString();
101 base::LaunchProcess(command_line, options, NULL);
104 HRESULT RegisterPortMonitor(bool install, const base::FilePath& install_path) {
105 DCHECK(install || install_path.empty());
106 base::FilePath target_path = GetNativeSystemPath(GetPortMonitorDllName());
107 if (target_path.empty()) {
108 LOG(ERROR) << "Unable to get port monitor target path.";
109 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
112 base::FilePath source_path =
113 install_path.Append(GetPortMonitorDllName());
114 if (!base::CopyFile(source_path, target_path)) {
115 LOG(ERROR) << "Unable copy port monitor dll from " <<
116 source_path.value() << " to " << target_path.value();
117 return GetLastHResult();
119 } else if (!base::PathExists(target_path)) {
120 // Already removed. Just "succeed" silently.
124 base::FilePath regsvr32_path = GetNativeSystemPath(L"regsvr32.exe");
125 if (regsvr32_path.empty()) {
126 LOG(ERROR) << "Can't find regsvr32.exe.";
127 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
130 CommandLine command_line(regsvr32_path);
131 command_line.AppendArg("/s");
133 command_line.AppendArg("/u");
136 // Use system32 path here because otherwise ::AddMonitor would fail.
137 command_line.AppendArgPath(GetSystemPath(GetPortMonitorDllName()));
139 base::LaunchOptions options;
142 base::win::ScopedHandle regsvr32_handle;
143 if (!base::LaunchProcess(command_line, options, regsvr32_handle.Receive())) {
144 LOG(ERROR) << "Unable to launch regsvr32.exe.";
145 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
148 DWORD exit_code = S_OK;
150 if (!GetExitCodeProcess(regsvr32_handle, &exit_code)) {
151 LOG(ERROR) << "Unable to get regsvr32.exe exit code.";
152 return GetLastHResult();
154 if (exit_code != 0) {
155 LOG(ERROR) << "Regsvr32.exe failed with " << exit_code;
156 return HRESULT_FROM_WIN32(exit_code);
159 if (!base::DeleteFile(target_path, false)) {
160 SpoolerServiceCommand("stop");
161 bool deleted = base::DeleteFile(target_path, false);
162 SpoolerServiceCommand("start");
165 LOG(ERROR) << "Unable to delete " << target_path.value();
166 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
173 DWORDLONG GetVersionNumber() {
174 DWORDLONG retval = 0;
175 scoped_ptr<FileVersionInfo> version_info(
176 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
177 if (version_info.get()) {
178 FileVersionInfoWin* version_info_win =
179 static_cast<FileVersionInfoWin*>(version_info.get());
180 VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info();
181 retval = fixed_file_info->dwFileVersionMS;
183 retval |= fixed_file_info->dwFileVersionLS;
188 UINT CALLBACK CabinetCallback(PVOID data,
192 const base::FilePath* temp_path(
193 reinterpret_cast<const base::FilePath*>(data));
194 if (notification == SPFILENOTIFY_FILEINCABINET) {
195 FILE_IN_CABINET_INFO* info =
196 reinterpret_cast<FILE_IN_CABINET_INFO*>(param1);
197 for (int i = 0; i < arraysize(kDependencyList); i++) {
198 base::FilePath base_name(info->NameInCabinet);
199 base_name = base_name.BaseName();
200 if (base::FilePath::CompareEqualIgnoreCase(base_name.value().c_str(),
201 kDependencyList[i])) {
202 StringCchCopy(info->FullTargetName, MAX_PATH,
203 temp_path->Append(kDependencyList[i]).value().c_str());
212 void ReadyDriverDependencies(const base::FilePath& 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}",
222 GetPrinterDriverPackagePath(NULL, NULL, NULL, driver.szPackageID,
223 package_path, MAX_PATH, &size);
224 SetupIterateCabinet(package_path, 0, &CabinetCallback,
225 &base::FilePath(destination));
227 // Driver files are in the sp3 cab.
228 base::FilePath package_path;
229 PathService::Get(base::DIR_WINDOWS, &package_path);
230 package_path = package_path.Append(L"Driver Cache\\i386\\sp3.cab");
231 SetupIterateCabinet(package_path.value().c_str(), 0, &CabinetCallback,
232 &base::FilePath(destination));
234 // Copy the rest from the driver cache or system dir.
235 base::FilePath driver_cache_path;
236 PathService::Get(base::DIR_WINDOWS, &driver_cache_path);
237 driver_cache_path = driver_cache_path.Append(L"Driver Cache\\i386");
238 for (size_t i = 0; i < arraysize(kDependencyList); ++i) {
239 base::FilePath dst_path = destination.Append(kDependencyList[i]);
240 if (!base::PathExists(dst_path)) {
241 base::FilePath src_path = driver_cache_path.Append(kDependencyList[i]);
242 if (!base::PathExists(src_path))
243 src_path = GetSystemPath(kDependencyList[i]);
244 base::CopyFile(src_path, dst_path);
250 HRESULT InstallDriver(const base::FilePath& install_path) {
251 base::ScopedTempDir temp_path;
252 if (!temp_path.CreateUniqueTempDir())
253 return HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE);
254 ReadyDriverDependencies(temp_path.path());
256 std::vector<string16> dependent_array;
257 // Add all files. AddPrinterDriverEx will removes unnecessary.
258 for (size_t i = 0; i < arraysize(kDependencyList); ++i) {
259 base::FilePath file_path = temp_path.path().Append(kDependencyList[i]);
260 if (base::PathExists(file_path))
261 dependent_array.push_back(file_path.value());
263 LOG(WARNING) << "File is missing: " << file_path.BaseName().value();
266 // Set up paths for the files we depend on.
267 base::FilePath data_file = install_path.Append(kDataFileName);
268 base::FilePath xps_path = temp_path.path().Append(kDriverName);
269 base::FilePath ui_path = temp_path.path().Append(kUiDriverName);
270 base::FilePath ui_help_path = temp_path.path().Append(kHelpName);
272 if (!base::PathExists(xps_path)) {
273 SetGoogleUpdateError(kGoogleUpdateProductId,
274 LoadLocalString(IDS_ERROR_NO_XPS));
275 return HRESULT_FROM_WIN32(ERROR_BAD_DRIVER);
278 DRIVER_INFO_6 driver_info = {0};
279 // Set up supported print system version. Must be 3.
280 driver_info.cVersion = 3;
282 // None of the print API structures likes constant strings even though they
283 // don't modify the string. const_casting is the cleanest option.
284 driver_info.pDataFile = const_cast<LPWSTR>(data_file.value().c_str());
285 driver_info.pHelpFile = const_cast<LPWSTR>(ui_help_path.value().c_str());
286 driver_info.pDriverPath = const_cast<LPWSTR>(xps_path.value().c_str());
287 driver_info.pConfigFile = const_cast<LPWSTR>(ui_path.value().c_str());
289 string16 dependent_files(JoinString(dependent_array, L'\n'));
290 dependent_files.push_back(L'\n');
291 std::replace(dependent_files.begin(), dependent_files.end(), L'\n', L'\0');
292 driver_info.pDependentFiles = &dependent_files[0];
294 // Set up user visible strings.
295 string16 manufacturer = LoadLocalString(IDS_GOOGLE);
296 driver_info.pszMfgName = const_cast<LPWSTR>(manufacturer.c_str());
297 driver_info.pszProvider = const_cast<LPWSTR>(manufacturer.c_str());
298 driver_info.pszOEMUrl = const_cast<LPWSTR>(kGcpUrl);
299 driver_info.dwlDriverVersion = GetVersionNumber();
300 string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
301 driver_info.pName = const_cast<LPWSTR>(driver_name.c_str());
303 if (!::AddPrinterDriverEx(NULL, 6, reinterpret_cast<BYTE*>(&driver_info),
304 APD_COPY_NEW_FILES | APD_COPY_FROM_DIRECTORY)) {
305 LOG(ERROR) << "Unable to add printer driver";
306 return GetLastHResult();
311 HRESULT UninstallDriver() {
313 string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
314 while (!DeletePrinterDriverEx(NULL,
316 const_cast<LPWSTR>(driver_name.c_str()),
317 DPD_DELETE_UNUSED_FILES,
319 if (GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER) {
320 LOG(WARNING) << "Print driver is already uninstalled.";
323 // After deleting the printer it can take a few seconds before
324 // the driver is free for deletion. Retry a few times before giving up.
325 LOG(WARNING) << "Attempt to delete printer driver failed. Retrying.";
330 HRESULT result = GetLastHResult();
331 LOG(ERROR) << "Unable to delete printer driver.";
337 HRESULT InstallPrinter(void) {
338 PRINTER_INFO_2 printer_info = {0};
340 // None of the print API structures likes constant strings even though they
341 // don't modify the string. const_casting is the cleanest option.
342 string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
343 printer_info.pDriverName = const_cast<LPWSTR>(driver_name.c_str());
344 printer_info.pPrinterName = const_cast<LPWSTR>(driver_name.c_str());
345 printer_info.pComment = const_cast<LPWSTR>(driver_name.c_str());
346 printer_info.pLocation = const_cast<LPWSTR>(kGcpUrl);
348 printer_info.pPortName = const_cast<LPWSTR>(kPortName);
349 printer_info.Attributes = PRINTER_ATTRIBUTE_DIRECT|PRINTER_ATTRIBUTE_LOCAL;
350 printer_info.pPrintProcessor = L"winprint";
351 HANDLE handle = AddPrinter(NULL, 2, reinterpret_cast<BYTE*>(&printer_info));
352 if (handle == NULL) {
353 HRESULT result = GetLastHResult();
354 LOG(ERROR) << "Unable to add printer";
357 ClosePrinter(handle);
361 HRESULT UninstallPrinter(void) {
362 HANDLE handle = NULL;
363 PRINTER_DEFAULTS printer_defaults = {0};
364 printer_defaults.DesiredAccess = PRINTER_ALL_ACCESS;
365 string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
366 if (!OpenPrinter(const_cast<LPWSTR>(driver_name.c_str()),
368 &printer_defaults)) {
369 // If we can't open the printer, it was probably already removed.
370 LOG(WARNING) << "Unable to open printer";
373 if (!DeletePrinter(handle)) {
374 HRESULT result = GetLastHResult();
375 LOG(ERROR) << "Unable to delete printer";
376 ClosePrinter(handle);
379 ClosePrinter(handle);
383 bool IsOSSupported() {
384 // We don't support XP service pack 2 or older.
385 base::win::Version version = base::win::GetVersion();
386 return (version > base::win::VERSION_XP) ||
387 ((version == base::win::VERSION_XP) &&
388 (base::win::OSInfo::GetInstance()->service_pack().major >= 3));
391 HRESULT RegisterVirtualDriver(const base::FilePath& install_path) {
392 HRESULT result = S_OK;
394 DCHECK(base::DirectoryExists(install_path));
395 if (!IsOSSupported()) {
396 LOG(ERROR) << "Requires XP SP3 or later.";
397 return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
400 result = InstallDriver(install_path);
401 if (FAILED(result)) {
402 LOG(ERROR) << "Unable to install driver.";
406 result = RegisterPortMonitor(true, install_path);
407 if (FAILED(result)) {
408 LOG(ERROR) << "Unable to register port monitor.";
412 result = InstallPrinter();
413 if (FAILED(result) &&
414 result != HRESULT_FROM_WIN32(ERROR_PRINTER_ALREADY_EXISTS)) {
415 LOG(ERROR) << "Unable to install printer.";
421 HRESULT TryUnregisterVirtualDriver() {
422 HRESULT result = S_OK;
423 result = UninstallPrinter();
424 if (FAILED(result)) {
425 LOG(ERROR) << "Unable to delete printer.";
428 result = UninstallDriver();
429 if (FAILED(result)) {
430 LOG(ERROR) << "Unable to remove driver.";
433 // The second argument is ignored if the first is false.
434 result = RegisterPortMonitor(false, base::FilePath());
435 if (FAILED(result)) {
436 LOG(ERROR) << "Unable to remove port monitor.";
442 HRESULT UnregisterVirtualDriver() {
443 HRESULT hr = S_FALSE;
444 for (int i = 0; i < 2; ++i) {
445 hr = TryUnregisterVirtualDriver();
449 // Restart spooler and try again.
450 SpoolerServiceCommand("stop");
451 SpoolerServiceCommand("start");
456 HRESULT DoUninstall() {
457 DeleteGoogleUpdateKeys(kGoogleUpdateProductId);
458 HRESULT result = UnregisterVirtualDriver();
461 DeleteUninstallKey(kUninstallId);
462 DeleteProgramDir(kDelete);
466 HRESULT DoUnregister() {
467 return UnregisterVirtualDriver();
470 HRESULT DoRegister(const base::FilePath& install_path) {
471 HRESULT result = UnregisterVirtualDriver();
474 return RegisterVirtualDriver(install_path);
477 HRESULT DoDelete(const base::FilePath& install_path) {
478 if (install_path.value().empty())
480 if (!base::DirectoryExists(install_path))
482 Sleep(5000); // Give parent some time to exit.
483 return base::DeleteFile(install_path, true) ? S_OK : E_FAIL;
486 HRESULT DoInstall(const base::FilePath& install_path) {
487 HRESULT result = UnregisterVirtualDriver();
488 if (FAILED(result)) {
489 LOG(ERROR) << "Unable to unregister.";
492 base::FilePath old_install_path = GetInstallLocation(kUninstallId);
493 if (!old_install_path.value().empty() &&
494 install_path != old_install_path) {
495 if (base::DirectoryExists(old_install_path))
496 base::DeleteFile(old_install_path, true);
498 CreateUninstallKey(kUninstallId, LoadLocalString(IDS_DRIVER_NAME),
500 result = RegisterVirtualDriver(install_path);
503 SetGoogleUpdateKeys(kGoogleUpdateProductId, kNameValue);
507 HRESULT ExecuteCommands() {
508 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
510 base::FilePath exe_path;
511 if (FAILED(PathService::Get(base::DIR_EXE, &exe_path)) ||
512 !base::DirectoryExists(exe_path)) {
513 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
516 if (command_line.HasSwitch(kDelete)) {
517 return DoDelete(command_line.GetSwitchValuePath(kDelete));
518 } else if (command_line.HasSwitch(kUninstallSwitch)) {
519 return DoUninstall();
520 } else if (command_line.HasSwitch(kInstallSwitch)) {
521 return DoInstall(exe_path);
522 } else if (command_line.HasSwitch(kUnregisterSwitch)) {
523 return DoUnregister();
524 } else if (command_line.HasSwitch(kRegisterSwitch)) {
525 return DoRegister(exe_path);
533 } // namespace cloud_print
535 int WINAPI WinMain(__in HINSTANCE hInstance,
536 __in HINSTANCE hPrevInstance,
537 __in LPSTR lpCmdLine,
539 base::AtExitManager at_exit_manager;
540 CommandLine::Init(0, NULL);
541 HRESULT retval = cloud_print::ExecuteCommands();
543 LOG(INFO) << _com_error(retval).ErrorMessage() << " HRESULT=0x" <<
544 std::setbase(16) << retval;
546 // Installer is silent by default as required by Google Update.
547 if (CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
548 cloud_print::DisplayWindowsMessage(NULL, retval,
549 cloud_print::LoadLocalString(IDS_DRIVER_NAME));