diff options
Diffstat (limited to 'cloud_print')
6 files changed, 219 insertions, 38 deletions
diff --git a/cloud_print/virtual_driver/win/install/setup.cc b/cloud_print/virtual_driver/win/install/setup.cc index 84bd746..8724429 100644 --- a/cloud_print/virtual_driver/win/install/setup.cc +++ b/cloud_print/virtual_driver/win/install/setup.cc @@ -3,10 +3,12 @@ // found in the LICENSE file. #include <windows.h> +#include <winspool.h> #include "base/at_exit.h" #include "base/command_line.h" #include "base/file_util.h" +#include "base/logging.h" #include "base/path_service.h" #include "base/process_util.h" #include "base/win/scoped_handle.h" @@ -23,6 +25,15 @@ bool IsSystem64Bit() { (arch == base::win::OSInfo::IA64_ARCHITECTURE); } +HRESULT GetGpdPath(FilePath* path) { + if (!PathService::Get(base::DIR_EXE, path)) { + LOG(ERROR) << "Unable to get install path."; + return ERROR_PATH_NOT_FOUND; + } + *path = path->Append(L"gcp.gpd"); + return S_OK; +} + const wchar_t *GetPortMonitorDllName() { if (IsSystem64Bit()) { return cloud_print::kPortMonitorDllName64; @@ -33,6 +44,7 @@ const wchar_t *GetPortMonitorDllName() { HRESULT GetPortMonitorDllPath(FilePath* path) { if (!PathService::Get(base::DIR_EXE, path)) { + LOG(ERROR) << "Unable to get install path."; return ERROR_PATH_NOT_FOUND; } *path = path->Append(GetPortMonitorDllName()); @@ -49,6 +61,7 @@ HRESULT GetPortMonitorInstallPath(FilePath* path) { *path = path->Append(L"sysnative"); } else { if (!PathService::Get(base::DIR_SYSTEM, path)) { + LOG(ERROR) << "Unable to get system path."; return ERROR_PATH_NOT_FOUND; } } @@ -58,6 +71,7 @@ HRESULT GetPortMonitorInstallPath(FilePath* path) { HRESULT GetRegsvr32Path(FilePath* path) { if (!PathService::Get(base::DIR_SYSTEM, path)) { + LOG(ERROR) << "Unable to get system path."; return ERROR_PATH_NOT_FOUND; } *path = path->Append(FilePath(L"regsvr32.exe")); @@ -69,21 +83,26 @@ HRESULT RegisterPortMonitor(bool install) { HRESULT result = S_OK; result = GetPortMonitorInstallPath(&target_path); if (!SUCCEEDED(result)) { + LOG(ERROR) << "Unable to get port monitor target path."; return result; } - FilePath dll_path; - result = GetPortMonitorDllPath(&dll_path); + FilePath source_path; + result = GetPortMonitorDllPath(&source_path); if (!SUCCEEDED(result)) { + LOG(ERROR) << "Unable to get dll source path."; return result; } if (install) { - if (!file_util::CopyFileW(dll_path, target_path)) { - return cloud_print::GetLastHResult(); + if (!file_util::CopyFileW(source_path, target_path)) { + LOG(ERROR) << "Unable copy port monitor dll from " << + source_path.value() << " to " << target_path.value(); + return ERROR_ACCESS_DENIED; } } FilePath regsvr32_path; result = GetRegsvr32Path(®svr32_path); if (!SUCCEEDED(result)) { + LOG(ERROR) << "Can't find regsvr32.exe."; return result; } CommandLine command_line(regsvr32_path); @@ -91,36 +110,181 @@ HRESULT RegisterPortMonitor(bool install) { if (!install) { command_line.AppendArg("/u"); } - command_line.AppendArgPath(dll_path); + command_line.AppendArgPath(source_path); HANDLE process_handle; if (!base::LaunchApp(command_line.command_line_string(), true, false, &process_handle)) { - return cloud_print::GetLastHResult(); + LOG(ERROR) << "Unable to launch regsvr32.exe."; + return ERROR_NOT_SUPPORTED; } base::win::ScopedHandle scoped_process_handle(process_handle); DWORD exit_code = S_OK; if (!GetExitCodeProcess(scoped_process_handle, &exit_code)) { - return cloud_print::GetLastHResult(); + HRESULT result = cloud_print::GetLastHResult(); + LOG(ERROR) << "Unable to get regsvr32.exe exit code."; + return result; } if (exit_code != 0) { - return HRESULT_FROM_WIN32(exit_code); + LOG(ERROR) << "Regsvr32.exe failed with " << exit_code; + return HRESULT_FROM_WIN32(exit_code); } if (!install) { if (!file_util::Delete(target_path, false)) { - return cloud_print::GetLastHResult(); + LOG(ERROR) << "Unable to delete " << target_path.value(); + return ERROR_ACCESS_DENIED; } } return S_OK; } +HRESULT InstallGpd() { + HRESULT result = S_OK; + FilePath source_path; + result = GetGpdPath(&source_path); + if (!SUCCEEDED(result)) { + return result; + } + FilePath driver_dir; + cloud_print::GetPrinterDriverDir(&driver_dir); + FilePath xps_path = driver_dir.Append(L"mxdwdrv.dll"); + FilePath ui_path = driver_dir.Append(L"unidrvui.dll"); + FilePath ui_help_path = driver_dir.Append(L"unidrv.hlp"); + DRIVER_INFO_6 driver_info = {0}; + driver_info.cVersion = 3; + // None of the print API structures likes constant strings even though they + // don't modify the string. const_casting is the cleanest option. + driver_info.pName = const_cast<LPWSTR>(cloud_print::kVirtualDriverName); + driver_info.pDriverPath = const_cast<LPWSTR>(xps_path.value().c_str()); + driver_info.pConfigFile = const_cast<LPWSTR>(ui_path.value().c_str()); + driver_info.pDataFile = const_cast<LPWSTR>(source_path.value().c_str()); + driver_info.pHelpFile = const_cast<LPWSTR>(ui_help_path.value().c_str()); + // TODO(abodenha@chromium.org) Pull these strings from resources. + driver_info.pszMfgName = L"Google"; + driver_info.pszProvider = driver_info.pszMfgName; + driver_info.pszOEMUrl = L"http://www.google.com/cloudprint"; + driver_info.dwlDriverVersion = 1; + driver_info.pDefaultDataType = L"RAW"; + // TODO(abodenha@chromium.org) Properly handle dependencies. + // GPD files are often dependent on various Windows core drivers. + // I haven't found a reliable way to express those dependencies + // other than using an INF for installation. + if (!AddPrinterDriverEx(NULL, + 6, + reinterpret_cast<BYTE*>(&driver_info), + APD_COPY_NEW_FILES|APD_COPY_FROM_DIRECTORY)) { + result = cloud_print::GetLastHResult(); + LOG(ERROR) << "Unable to add printer driver"; + return result; + } + return S_OK; +} + +HRESULT UninstallGpd() { + int tries = 10; + while (!DeletePrinterDriverEx(NULL, + NULL, + const_cast<LPWSTR> + (cloud_print::kVirtualDriverName), + DPD_DELETE_UNUSED_FILES, + 0) && tries > 0) { + // 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 = cloud_print::GetLastHResult(); + LOG(ERROR) << "Unable to delete printer driver."; + return result; + } + return S_OK; +} + +HRESULT InstallPrinter(void) { + PRINTER_INFO_2 printer_info = {0}; + printer_info.pPrinterName = + const_cast<LPWSTR>(cloud_print::kVirtualDriverName); + printer_info.pPortName = const_cast<LPWSTR>(cloud_print::kPortName); + printer_info.pDriverName = + const_cast<LPWSTR>(cloud_print::kVirtualDriverName); + printer_info.pPrinterName = printer_info.pDriverName; + // TODO(abodenha@chromium.org) pComment should be localized. + printer_info.pComment = const_cast<LPWSTR>(cloud_print::kVirtualDriverName); + printer_info.Attributes = PRINTER_ATTRIBUTE_DIRECT|PRINTER_ATTRIBUTE_LOCAL; + printer_info.pPrintProcessor = L"winprint"; + printer_info.pDatatype = L"RAW"; + HANDLE handle = AddPrinter(NULL, 2, reinterpret_cast<BYTE*>(&printer_info)); + if (handle == NULL) { + HRESULT result = cloud_print::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; + if (!OpenPrinter(const_cast<LPWSTR>(cloud_print::kVirtualDriverName), + &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 = cloud_print::GetLastHResult(); + LOG(ERROR) << "Unable to delete printer"; + ClosePrinter(handle); + return result; + } + ClosePrinter(handle); + return S_OK; +} + HRESULT InstallVirtualDriver(void) { - return RegisterPortMonitor(true); + HRESULT result = S_OK; + result = RegisterPortMonitor(true); + if (!SUCCEEDED(result)) { + LOG(ERROR) << "Unable to register port monitor."; + return result; + } + result = InstallGpd(); + if (!SUCCEEDED(result)) { + LOG(ERROR) << "Unable to install gpd."; + return result; + } + result = InstallPrinter(); + if (!SUCCEEDED(result)) { + LOG(ERROR) << "Unable to install printer."; + return result; + } + return S_OK; } HRESULT UninstallVirtualDriver(void) { - return RegisterPortMonitor(false); + HRESULT result = S_OK; + result = UninstallPrinter(); + if (!SUCCEEDED(result)) { + LOG(ERROR) << "Unable to uninstall gpd."; + return result; + } + result = UninstallGpd(); + if (!SUCCEEDED(result)) { + LOG(ERROR) << "Unable to remove gpd."; + return result; + } + result = RegisterPortMonitor(false); + if (!SUCCEEDED(result)) { + LOG(ERROR) << "Unable to remove port monitor."; + return result; + } + return S_OK; } } // namespace diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor.cc b/cloud_print/virtual_driver/win/port_monitor/port_monitor.cc index ff43e1e..f7b3437 100644 --- a/cloud_print/virtual_driver/win/port_monitor/port_monitor.cc +++ b/cloud_print/virtual_driver/win/port_monitor/port_monitor.cc @@ -65,8 +65,6 @@ const wchar_t *kPortMonitorDllName = kPortMonitorDllName64; const wchar_t *kPortMonitorDllName = kPortMonitorDllName32; #endif -const wchar_t kPortName[] = L"GCP:"; - const wchar_t kXpsMimeType[] = L"application/vnd.ms-xpsdocument"; const size_t kMaxCommandLineLen = 0x7FFF; @@ -117,29 +115,10 @@ MONITOR2 g_monitor_2 = { Monitor2Shutdown }; -// Gets the standard install path for "version 3" print drivers. -HRESULT GetPrinterDriverPath(FilePath* path) { - BYTE driver_dir_buffer[MAX_PATH * sizeof(wchar_t)]; - DWORD needed = 0; - if (!GetPrinterDriverDirectory(NULL, - NULL, - 1, - driver_dir_buffer, - MAX_PATH * sizeof(wchar_t), - &needed)) { - // We could try to allocate a larger buffer if needed > MAX_PATH - // but that really shouldn't happen. - return cloud_print::GetLastHResult(); - } - *path = FilePath(reinterpret_cast<wchar_t*>(driver_dir_buffer)); - *path = path->Append(L"3"); - return S_OK; -} - // Returns true if Xps support is installed. bool XpsIsInstalled() { FilePath xps_path; - if (!SUCCEEDED(GetPrinterDriverPath(&xps_path))) { + if (!SUCCEEDED(GetPrinterDriverDir(&xps_path))) { return false; } xps_path = xps_path.Append(L"mxdwdrv.dll"); @@ -334,7 +313,7 @@ BOOL WINAPI Monitor2EnumPorts(HANDLE, SetLastError(ERROR_INVALID_LEVEL); return FALSE; } - *needed_bytes += sizeof(kPortName); + *needed_bytes += static_cast<DWORD>(kPortNameSize); if (ports_size < *needed_bytes) { LOG(WARNING) << *needed_bytes << " bytes are required. Only " << ports_size << " were allocated."; @@ -358,15 +337,15 @@ BOOL WINAPI Monitor2EnumPorts(HANDLE, // EnumPorts to fail until the spooler is restarted. // This is NOT mentioned in the documentation. wchar_t* string_target = - reinterpret_cast<wchar_t*>(ports + ports_size - sizeof(kPortName)); + reinterpret_cast<wchar_t*>(ports + ports_size - kPortNameSize); if (level == 1) { PORT_INFO_1* port_info = reinterpret_cast<PORT_INFO_1*>(ports); port_info->pName = string_target; - StringCbCopy(port_info->pName, sizeof(kPortName), kPortName); + StringCbCopy(port_info->pName, kPortNameSize, kPortName); } else { PORT_INFO_2* port_info = reinterpret_cast<PORT_INFO_2*>(ports); port_info->pPortName = string_target; - StringCbCopy(port_info->pPortName, sizeof(kPortName), kPortName); + StringCbCopy(port_info->pPortName, kPortNameSize, kPortName); port_info->pMonitorName = NULL; port_info->pDescription = NULL; port_info->fPortType = PORT_TYPE_WRITE; diff --git a/cloud_print/virtual_driver/win/virtual_driver_consts.cc b/cloud_print/virtual_driver/win/virtual_driver_consts.cc index 78117f8..d8b02ea 100644 --- a/cloud_print/virtual_driver/win/virtual_driver_consts.cc +++ b/cloud_print/virtual_driver/win/virtual_driver_consts.cc @@ -9,5 +9,13 @@ namespace cloud_print { const wchar_t kPortMonitorDllName64[] = L"gcp_portmon64.dll"; const wchar_t kPortMonitorDllName32[] = L"gcp_portmon.dll"; +const wchar_t kPortName[] = L"GCP:"; +const size_t kPortNameSize = sizeof(kPortName); + +// The driver name is user visible so it SHOULD be localized, BUT +// the name is used as a key to find both the driver and printer. +// We'll need to be careful. If the name changes for the +// driver it could create bugs. +const wchar_t kVirtualDriverName[] = L"Google Cloud Print Virtual Printer"; } diff --git a/cloud_print/virtual_driver/win/virtual_driver_consts.h b/cloud_print/virtual_driver/win/virtual_driver_consts.h index 6d92ea8..acf0b47 100644 --- a/cloud_print/virtual_driver/win/virtual_driver_consts.h +++ b/cloud_print/virtual_driver/win/virtual_driver_consts.h @@ -9,6 +9,9 @@ namespace cloud_print { extern const wchar_t kPortMonitorDllName64[]; extern const wchar_t kPortMonitorDllName32[]; +extern const wchar_t kPortName[]; +extern const size_t kPortNameSize; +extern const wchar_t kVirtualDriverName[]; } #endif // CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_CONSTS_H_ diff --git a/cloud_print/virtual_driver/win/virtual_driver_helpers.cc b/cloud_print/virtual_driver/win/virtual_driver_helpers.cc index c59211c..19d5574 100644 --- a/cloud_print/virtual_driver/win/virtual_driver_helpers.cc +++ b/cloud_print/virtual_driver/win/virtual_driver_helpers.cc @@ -4,6 +4,8 @@ #include "cloud_print/virtual_driver/win/virtual_driver_helpers.h" #include <windows.h> +#include <winspool.h> +#include "base/file_util.h" #include "cloud_print/virtual_driver/win/virtual_driver_consts.h" namespace cloud_print { @@ -20,12 +22,32 @@ void DisplayWindowsMessage(HWND hwnd, HRESULT message_id) { message_text, kMaxMessageLen, NULL); - ::MessageBox(hwnd, message_text, L"GCP", MB_OK); + ::MessageBox(hwnd, message_text, kVirtualDriverName, MB_OK); } HRESULT GetLastHResult() { DWORD error_code = GetLastError(); return HRESULT_FROM_WIN32(error_code); } + +HRESULT GetPrinterDriverDir(FilePath* path) { + BYTE driver_dir_buffer[MAX_PATH * sizeof(wchar_t)]; + DWORD needed = 0; + if (!GetPrinterDriverDirectory(NULL, + NULL, + 1, + driver_dir_buffer, + MAX_PATH * sizeof(wchar_t), + &needed)) { + // We could try to allocate a larger buffer if needed > MAX_PATH + // but that really shouldn't happen. + return cloud_print::GetLastHResult(); + } + *path = FilePath(reinterpret_cast<wchar_t*>(driver_dir_buffer)); + + // The XPS driver is a "Level 3" driver + *path = path->Append(L"3"); + return S_OK; +} } diff --git a/cloud_print/virtual_driver/win/virtual_driver_helpers.h b/cloud_print/virtual_driver/win/virtual_driver_helpers.h index d8cc089..6d2a753 100644 --- a/cloud_print/virtual_driver/win/virtual_driver_helpers.h +++ b/cloud_print/virtual_driver/win/virtual_driver_helpers.h @@ -8,6 +8,8 @@ #include <windows.h> +class FilePath; + namespace cloud_print { // Convert an HRESULT to a localized string and display it in a message box. @@ -15,6 +17,9 @@ void DisplayWindowsMessage(HWND hwnd, HRESULT message_id); // Similar to the Windows API call GetLastError but returns an HRESULT. HRESULT GetLastHResult(); + +// Gets the standard install path for "version 3" print drivers. +HRESULT GetPrinterDriverDir(FilePath* path); } #endif // CLOUD_PRINT_VIRTUAL_DRIVER_WIN_HELPERS_H_ |