Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Unified Diff: cloud_print/virtual_driver/win/install/setup.cc

Issue 2541123003: Resurrect the cloud printer driver. (Closed)
Patch Set: Remove cross-compiling option for the portmon dll. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: cloud_print/virtual_driver/win/install/setup.cc
diff --git a/cloud_print/virtual_driver/win/install/setup.cc b/cloud_print/virtual_driver/win/install/setup.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3c4bcad27a2833a4fa4b959f31de4b394b78bf82
--- /dev/null
+++ b/cloud_print/virtual_driver/win/install/setup.cc
@@ -0,0 +1,409 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+#include <setupapi.h> // Must be included after windows.h
+#include <winspool.h>
+#include <stddef.h>
+
+#include <iomanip>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/win/windows_version.h"
+#include "cloud_print/common/win/cloud_print_utils.h"
+#include "cloud_print/common/win/install_utils.h"
+#include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
+#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
+#include "grit/virtual_driver_setup_resources.h"
+
+#include <strsafe.h> // Must be after base headers to avoid deprecation
+ // warnings.
+
+namespace cloud_print {
+
+namespace {
+
+const wchar_t kNameValue[] = L"GCP Virtual Driver";
+const wchar_t kUninstallId[] = L"{74AA24E0-AC50-4B28-BA46-9CF05467C9B7}";
+const wchar_t kGcpUrl[] = L"https://www.google.com/cloudprint";
+
+const wchar_t kInfFileName[] = L"gcp_driver.inf";
+
+const char kDelete[] = "delete";
+const char kInstallSwitch[] = "install";
+const char kRegisterSwitch[] = "register";
+const char kUninstallSwitch[] = "uninstall";
+const char kUnregisterSwitch[] = "unregister";
+
+base::FilePath GetSystemPath(const base::string16& binary) {
+ base::FilePath path;
+ if (!PathService::Get(base::DIR_SYSTEM, &path)) {
+ LOG(ERROR) << "Unable to get system path.";
+ return path;
+ }
+ return path.Append(binary);
+}
+
+void SpoolerServiceCommand(const char* command) {
+ base::FilePath net_path = GetSystemPath(L"net");
+ if (net_path.empty())
+ return;
+ base::CommandLine command_line(net_path);
+ command_line.AppendArg(command);
+ command_line.AppendArg("spooler");
+ command_line.AppendArg("/y");
+
+ base::LaunchOptions options;
+ options.wait = true;
+ options.start_hidden = true;
+ VLOG(0) << command_line.GetCommandLineString();
+ base::LaunchProcess(command_line, options);
+}
+
+HRESULT RegisterPortMonitor(bool install, const base::FilePath& install_path) {
+ DCHECK(install || install_path.empty());
+ base::FilePath target_path = GetSystemPath(L"gcp_portmon.dll");
+ if (target_path.empty()) {
+ LOG(ERROR) << "Unable to get port monitor target path.";
+ return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+ }
+ if (install) {
+ base::FilePath source_path = install_path.Append(L"gcp_portmon.dll");
+ if (!base::CopyFile(source_path, target_path)) {
+ LOG(ERROR) << "Unable copy port monitor dll from " << source_path.value()
+ << " to " << target_path.value();
+ return GetLastHResult();
+ }
+ } else if (!base::PathExists(target_path)) {
+ // Already removed. Just "succeed" silently.
+ return S_OK;
+ }
+
+ base::FilePath regsvr32_path = GetSystemPath(L"regsvr32.exe");
+ if (regsvr32_path.empty()) {
+ LOG(ERROR) << "Can't find regsvr32.exe.";
+ return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+ }
+
+ base::CommandLine command_line(regsvr32_path);
+ command_line.AppendArg("/s");
+ if (!install) {
+ command_line.AppendArg("/u");
+ }
+
+ // Use system32 path here because otherwise ::AddMonitor would fail.
+ command_line.AppendArgPath(GetSystemPath(L"gcp_portmon.dll"));
+
+ base::LaunchOptions options;
+ options.wait = true;
+
+ base::Process regsvr32_process =
+ base::LaunchProcess(command_line.GetCommandLineString(), options);
+ if (!regsvr32_process.IsValid()) {
+ LOG(ERROR) << "Unable to launch regsvr32.exe.";
+ return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
+ }
+
+ DWORD exit_code = S_OK;
+ if (install) {
+ if (!GetExitCodeProcess(regsvr32_process.Handle(), &exit_code)) {
+ LOG(ERROR) << "Unable to get regsvr32.exe exit code.";
+ return GetLastHResult();
+ }
+ if (exit_code != 0) {
+ LOG(ERROR) << "Regsvr32.exe failed with " << exit_code;
+ return HRESULT_FROM_WIN32(exit_code);
+ }
+ } else {
+ if (!base::DeleteFile(target_path, false)) {
+ SpoolerServiceCommand("stop");
+ bool deleted = base::DeleteFile(target_path, false);
+ SpoolerServiceCommand("start");
+
+ if (!deleted) {
+ LOG(ERROR) << "Unable to delete " << target_path.value();
+ return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
+ }
+ }
+ }
+ return S_OK;
+}
+
+HRESULT InstallDriver(const base::FilePath& install_path) {
+ DWORD size = MAX_PATH * 10;
+ wchar_t package_path[MAX_PATH * 10] = {0};
+
+ base::FilePath inf_file = install_path.Append(kInfFileName);
+ base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
+
+ HRESULT result = UploadPrinterDriverPackage(
+ NULL, inf_file.value().c_str(), NULL,
+ UPDP_SILENT_UPLOAD | UPDP_UPLOAD_ALWAYS, NULL, package_path, &size);
+ if (FAILED(result)) {
+ LOG(ERROR)
+ << "Uploading the printer driver package to the driver cache failed.";
+ return result;
+ }
+
+ result = InstallPrinterDriverFromPackage(
+ NULL, package_path, driver_name.c_str(), NULL, IPDFP_COPY_ALL_FILES);
+ if (FAILED(result)) {
+ LOG(ERROR) << "Installing the printer driver failed.";
+ }
+ return result;
+}
+
+HRESULT UninstallDriver(const base::FilePath& install_path) {
+ base::FilePath inf_file = install_path.Append(kInfFileName);
+ int tries = 3;
+
+ while (!DeletePrinterDriverPackage(NULL, inf_file.value().c_str(), NULL)) {
+ if (GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER) {
+ LOG(WARNING) << "Print driver is already uninstalled.";
+ return S_OK;
+ }
+ // After deleting the printer it can take a few seconds before
+ // the driver is free for deletion. Retry a few times before giving up.
+ LOG(WARNING) << "Attempt to delete printer driver failed. Retrying.";
+ tries--;
+ Sleep(2000);
+ }
+ if (tries <= 0) {
+ HRESULT result = GetLastHResult();
+ LOG(ERROR) << "Unable to delete printer driver.";
+ return result;
+ }
+ return S_OK;
+}
+
+HRESULT InstallPrinter(void) {
+ PRINTER_INFO_2 printer_info = {0};
+
+ // None of the print API structures likes constant strings even though they
+ // don't modify the string. const_casting is the cleanest option.
+ base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
+ printer_info.pDriverName = const_cast<LPWSTR>(driver_name.c_str());
+ printer_info.pPrinterName = const_cast<LPWSTR>(driver_name.c_str());
+ printer_info.pComment = const_cast<LPWSTR>(driver_name.c_str());
+ printer_info.pLocation = const_cast<LPWSTR>(kGcpUrl);
+ base::string16 port_name;
+ printer_info.pPortName = const_cast<LPWSTR>(kPortName);
+ printer_info.Attributes = PRINTER_ATTRIBUTE_DIRECT | PRINTER_ATTRIBUTE_LOCAL;
+ printer_info.pPrintProcessor = const_cast<LPWSTR>(L"winprint");
+ HANDLE handle = AddPrinter(NULL, 2, reinterpret_cast<BYTE*>(&printer_info));
+ if (handle == NULL) {
+ HRESULT result = GetLastHResult();
+ LOG(ERROR) << "Unable to add printer";
+ return result;
+ }
+ ClosePrinter(handle);
+ return S_OK;
+}
+
+HRESULT UninstallPrinter(void) {
+ HANDLE handle = NULL;
+ PRINTER_DEFAULTS printer_defaults = {0};
+ printer_defaults.DesiredAccess = PRINTER_ALL_ACCESS;
+ base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
+ if (!OpenPrinter(const_cast<LPWSTR>(driver_name.c_str()), &handle,
+ &printer_defaults)) {
+ // If we can't open the printer, it was probably already removed.
+ LOG(WARNING) << "Unable to open printer";
+ return S_OK;
+ }
+ if (!DeletePrinter(handle)) {
+ HRESULT result = GetLastHResult();
+ LOG(ERROR) << "Unable to delete printer";
+ ClosePrinter(handle);
+ return result;
+ }
+ ClosePrinter(handle);
+ return S_OK;
+}
+
+bool IsOSSupported() {
+ // We don't support Vista or older.
+ base::win::Version version = base::win::GetVersion();
+ return (version >= base::win::VERSION_WIN7);
+}
+
+HRESULT RegisterVirtualDriver(const base::FilePath& install_path) {
+ HRESULT result = S_OK;
+
+ DCHECK(base::DirectoryExists(install_path));
+ if (!IsOSSupported()) {
+ LOG(ERROR) << "Requires Windows 7 or later.";
+ return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
+ }
+
+ result = InstallDriver(install_path);
+ if (FAILED(result)) {
+ LOG(ERROR) << "Unable to install driver.";
+ return result;
+ }
+
+ result = RegisterPortMonitor(true, install_path);
+ if (FAILED(result)) {
+ LOG(ERROR) << "Unable to register port monitor.";
+ return result;
+ }
+
+ result = InstallPrinter();
+ if (FAILED(result) &&
+ result != HRESULT_FROM_WIN32(ERROR_PRINTER_ALREADY_EXISTS)) {
+ LOG(ERROR) << "Unable to install printer.";
+ return result;
+ }
+ return S_OK;
+}
+
+HRESULT TryUnregisterVirtualDriver(const base::FilePath& install_path) {
+ HRESULT result = S_OK;
+ result = UninstallPrinter();
+ if (FAILED(result)) {
+ LOG(ERROR) << "Unable to delete printer.";
+ return result;
+ }
+ result = UninstallDriver(install_path);
+ if (FAILED(result)) {
+ LOG(ERROR) << "Unable to remove driver.";
+ return result;
+ }
+ // The second argument is ignored if the first is false.
+ result = RegisterPortMonitor(false, base::FilePath());
+ if (FAILED(result)) {
+ LOG(ERROR) << "Unable to remove port monitor.";
+ return result;
+ }
+ return S_OK;
+}
+
+HRESULT UnregisterVirtualDriver(const base::FilePath& install_path) {
+ HRESULT hr = S_FALSE;
+ for (int i = 0; i < 2; ++i) {
+ hr = TryUnregisterVirtualDriver(install_path);
+ if (SUCCEEDED(hr)) {
+ break;
+ }
+ // Restart spooler and try again.
+ SpoolerServiceCommand("stop");
+ SpoolerServiceCommand("start");
+ }
+ return hr;
+}
+
+HRESULT DoUninstall(const base::FilePath& install_path) {
+ DeleteGoogleUpdateKeys(kGoogleUpdateProductId);
+ HRESULT result = UnregisterVirtualDriver(install_path);
+ if (FAILED(result))
+ return result;
+ DeleteUninstallKey(kUninstallId);
+ DeleteProgramDir(kDelete);
+ return S_OK;
+}
+
+HRESULT DoUnregister(const base::FilePath& install_path) {
+ return UnregisterVirtualDriver(install_path);
+}
+
+HRESULT DoRegister(const base::FilePath& install_path) {
+ HRESULT result = UnregisterVirtualDriver(install_path);
+ if (FAILED(result))
+ return result;
+ return RegisterVirtualDriver(install_path);
+}
+
+HRESULT DoDelete(const base::FilePath& install_path) {
+ if (install_path.value().empty())
+ return E_INVALIDARG;
+ if (!base::DirectoryExists(install_path))
+ return S_FALSE;
+ Sleep(5000); // Give parent some time to exit.
+ return base::DeleteFile(install_path, true) ? S_OK : E_FAIL;
+}
+
+HRESULT DoInstall(const base::FilePath& install_path) {
+ HRESULT result = UnregisterVirtualDriver(install_path);
+ if (FAILED(result)) {
+ LOG(ERROR) << "Unable to unregister.";
+ return result;
+ }
+ base::FilePath old_install_path = GetInstallLocation(kUninstallId);
+ if (!old_install_path.value().empty() && install_path != old_install_path) {
+ if (base::DirectoryExists(old_install_path))
+ base::DeleteFile(old_install_path, true);
+ }
+ CreateUninstallKey(kUninstallId, LoadLocalString(IDS_DRIVER_NAME),
+ kUninstallSwitch);
+ result = RegisterVirtualDriver(install_path);
+ if (FAILED(result))
+ return result;
+ SetGoogleUpdateKeys(kGoogleUpdateProductId, kNameValue);
+ return result;
+}
+
+HRESULT ExecuteCommands() {
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+
+ base::FilePath exe_path;
+ if (!PathService::Get(base::DIR_EXE, &exe_path) ||
+ !base::DirectoryExists(exe_path)) {
+ return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+ }
+
+ if (command_line.HasSwitch(kDelete)) {
+ return DoDelete(command_line.GetSwitchValuePath(kDelete));
+ } else if (command_line.HasSwitch(kUninstallSwitch)) {
+ return DoUninstall(exe_path);
+ } else if (command_line.HasSwitch(kInstallSwitch)) {
+ return DoInstall(exe_path);
+ } else if (command_line.HasSwitch(kUnregisterSwitch)) {
+ return DoUnregister(exe_path);
+ } else if (command_line.HasSwitch(kRegisterSwitch)) {
+ return DoRegister(exe_path);
+ }
+
+ return E_INVALIDARG;
+}
+
+} // namespace
+
+} // namespace cloud_print
+
+int WINAPI WinMain(__in HINSTANCE hInstance,
+ __in HINSTANCE hPrevInstance,
+ __in LPSTR lpCmdLine,
+ __in int nCmdShow) {
+ using namespace cloud_print;
+
+ base::AtExitManager at_exit_manager;
+ base::CommandLine::Init(0, NULL);
+
+ HRESULT retval = ExecuteCommands();
+
+ if (retval == HRESULT_FROM_WIN32(ERROR_BAD_DRIVER)) {
+ SetGoogleUpdateError(kGoogleUpdateProductId,
+ LoadLocalString(IDS_ERROR_NO_XPS));
+ } else if (FAILED(retval)) {
+ SetGoogleUpdateError(kGoogleUpdateProductId, retval);
+ }
+
+ VLOG(0) << GetErrorMessage(retval) << " HRESULT=0x" << std::setbase(16)
+ << retval;
+
+ // Installer is silent by default as required by Google Update.
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
+ DisplayWindowsMessage(NULL, retval, LoadLocalString(IDS_DRIVER_NAME));
+ }
+ return retval;
+}

Powered by Google App Engine
This is Rietveld 408576698