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 |
index 1b83cbb35205a8fd8349a7f1232a608d919de40b..d64ea9aebfee918a12cde377a4dfd241e1c2a60e 100644 |
--- a/cloud_print/virtual_driver/win/install/setup.cc |
+++ b/cloud_print/virtual_driver/win/install/setup.cc |
@@ -3,6 +3,7 @@ |
// found in the LICENSE file. |
#include <windows.h> |
+#include <setupapi.h> // Must be included after windows.h |
#include <winspool.h> |
#include "base/at_exit.h" |
@@ -19,14 +20,24 @@ |
#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 { |
const wchar_t kVersionKey[] = L"pv"; |
const wchar_t kNameKey[] = L"name"; |
-const wchar_t kLangKey[] = L"lang"; |
const wchar_t kNameValue[] = L"GCP Virtual Driver"; |
-const wchar_t kLangValue[] = L"rn"; |
+const wchar_t kPpdName[] = L"GCP-DRIVER.PPD"; |
+const wchar_t kDriverName[] = L"MXDWDRV.DLL"; |
+const wchar_t kUiDriverName[] = L"PS5UI.DLL"; |
+const wchar_t kHelpName[] = L"PSCRIPT.HLP"; |
+const wchar_t* kDependencyList[] = {kDriverName, kUiDriverName, kHelpName}; |
+const wchar_t kUninstallRegistry[] = |
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" |
+ L"{74AA24E0-AC50-4B28-BA46-9CF05467C9B7}"; |
+const wchar_t kInstallerName[] = L"virtual_driver_setup.exe"; |
-void SetRegistryKeys() { |
+void SetOmahaKeys() { |
base::win::RegKey key; |
if (key.Create(HKEY_LOCAL_MACHINE, cloud_print::kKeyLocation, |
KEY_SET_VALUE) != ERROR_SUCCESS) { |
@@ -49,13 +60,12 @@ void SetRegistryKeys() { |
} |
if (key.WriteValue(kVersionKey, version_string.c_str()) != ERROR_SUCCESS || |
- key.WriteValue(kNameKey, kNameValue) != ERROR_SUCCESS || |
- key.WriteValue(kLangKey, kLangValue) != ERROR_SUCCESS) { |
+ key.WriteValue(kNameKey, kNameValue) != ERROR_SUCCESS) { |
LOG(ERROR) << "Unable to set registry keys"; |
} |
} |
-void DeleteRegistryKeys() { |
+void DeleteOmahaKeys() { |
base::win::RegKey key; |
if (key.Open(HKEY_LOCAL_MACHINE, cloud_print::kKeyLocation, |
DELETE) != ERROR_SUCCESS) { |
@@ -66,25 +76,7 @@ void DeleteRegistryKeys() { |
} |
} |
-HRESULT GetPpdPath(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-driver.ppd"); |
- return S_OK; |
-} |
- |
-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(cloud_print::GetPortMonitorDllName()); |
- return S_OK; |
-} |
- |
-HRESULT GetPortMonitorInstallPath(FilePath* path) { |
+HRESULT GetNativeSystemPath(FilePath* path) { |
if (cloud_print::IsSystem64Bit()) { |
if (!PathService::Get(base::DIR_WINDOWS, path)) { |
return ERROR_PATH_NOT_FOUND; |
@@ -98,34 +90,35 @@ HRESULT GetPortMonitorInstallPath(FilePath* path) { |
return ERROR_PATH_NOT_FOUND; |
} |
} |
- *path = path->Append(cloud_print::GetPortMonitorDllName()); |
return S_OK; |
} |
+HRESULT GetPortMonitorTargetPath(FilePath* path) { |
+ HRESULT result = GetNativeSystemPath(path); |
+ if (SUCCEEDED(result)) |
+ *path = path->Append(cloud_print::GetPortMonitorDllName()); |
+ return result; |
+} |
+ |
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")); |
- return S_OK; |
+ HRESULT result = GetNativeSystemPath(path); |
+ if (SUCCEEDED(result)) |
+ *path = path->Append(FilePath(L"regsvr32.exe")); |
+ return result; |
} |
-HRESULT RegisterPortMonitor(bool install) { |
+HRESULT RegisterPortMonitor(bool install, const FilePath& install_path) { |
FilePath target_path; |
HRESULT result = S_OK; |
- result = GetPortMonitorInstallPath(&target_path); |
+ result = GetPortMonitorTargetPath(&target_path); |
if (!SUCCEEDED(result)) { |
LOG(ERROR) << "Unable to get port monitor target path."; |
return result; |
} |
- FilePath source_path; |
- result = GetPortMonitorDllPath(&source_path); |
- if (!SUCCEEDED(result)) { |
- LOG(ERROR) << "Unable to get dll source path."; |
- return result; |
- } |
+ string16 source; |
+ source = cloud_print::GetPortMonitorDllName(); |
if (install) { |
+ FilePath source_path = install_path.Append(source); |
if (!file_util::CopyFileW(source_path, target_path)) { |
LOG(ERROR) << "Unable copy port monitor dll from " << |
source_path.value() << " to " << target_path.value(); |
@@ -144,7 +137,14 @@ HRESULT RegisterPortMonitor(bool install) { |
if (!install) { |
command_line.AppendArg("/u"); |
} |
- command_line.AppendArgPath(source_path); |
+ |
+ FilePath final_path; |
+ if (!PathService::Get(base::DIR_SYSTEM, &final_path)) { |
+ LOG(ERROR) << "Unable to get system path."; |
+ return ERROR_PATH_NOT_FOUND; |
+ } |
+ final_path = final_path.Append(cloud_print::GetPortMonitorDllName()); |
+ command_line.AppendArgPath(final_path); |
base::LaunchOptions options; |
HANDLE process_handle; |
@@ -189,24 +189,63 @@ DWORDLONG GetVersionNumber() { |
return retval; |
} |
-HRESULT InstallPpd() { |
+UINT CALLBACK CabinetCallback(PVOID data, |
+ UINT notification, |
+ UINT_PTR param1, |
+ UINT_PTR param2 ) { |
+ FilePath* temp_path = reinterpret_cast<FilePath*>(data); |
+ if (notification == SPFILENOTIFY_FILEINCABINET) { |
+ FILE_IN_CABINET_INFO* info = |
+ reinterpret_cast<FILE_IN_CABINET_INFO*>(param1); |
+ for (int i = 0; i < arraysize(kDependencyList); i++) { |
+ if (wcsstr(info->NameInCabinet, kDependencyList[i])) { |
+ StringCchCopy(info->FullTargetName, MAX_PATH, |
+ temp_path->Append(kDependencyList[i]).value().c_str()); |
+ return FILEOP_DOIT; |
+ } |
+ } |
+ |
+ return FILEOP_SKIP; |
+ } |
+ return NO_ERROR; |
+} |
+ |
+void ReadyPpdDependencies(const FilePath& install_path) { |
+ CORE_PRINTER_DRIVER driver; |
+ GetCorePrinterDrivers(NULL, |
+ NULL, |
+ L"{D20EA372-DD35-4950-9ED8-A6335AFE79F0}", |
+ 1, |
+ &driver); |
+ DWORD size = MAX_PATH; |
+ wchar_t package_path[MAX_PATH]; |
+ GetPrinterDriverPackagePath(NULL, |
+ NULL, |
+ NULL, |
+ driver.szPackageID, |
+ package_path, |
+ MAX_PATH, |
+ &size); |
+ |
+ SetupIterateCabinet(package_path, |
+ 0, |
+ CabinetCallback, |
+ const_cast<FilePath*>(&install_path)); |
+} |
+ |
+HRESULT InstallPpd(const FilePath& install_path) { |
DRIVER_INFO_6 driver_info = {0}; |
HRESULT result = S_OK; |
// Set up paths for the files we depend on. |
- FilePath source_path; |
- FilePath driver_dir; |
- cloud_print::GetPrinterDriverDir(&driver_dir); |
- FilePath xps_path = driver_dir.Append(L"mxdwdrv.dll"); |
- FilePath ui_path = driver_dir.Append(L"ps5ui.dll"); |
- FilePath ui_help_path = driver_dir.Append(L"unidrv.hlp"); |
- result = GetPpdPath(&source_path); |
- if (!SUCCEEDED(result)) { |
- return result; |
- } |
+ FilePath ppd_path = install_path.Append(kPpdName); |
+ FilePath xps_path = install_path.Append(kDriverName); |
+ FilePath ui_path = install_path.Append(kUiDriverName); |
+ FilePath ui_help_path = install_path.Append(kHelpName); |
+ ReadyPpdDependencies(install_path); |
// 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.pDataFile = const_cast<LPWSTR>(source_path.value().c_str()); |
+ driver_info.pDataFile = const_cast<LPWSTR>(ppd_path.value().c_str()); |
driver_info.pHelpFile = const_cast<LPWSTR>(ui_help_path.value().c_str()); |
driver_info.pDriverPath = const_cast<LPWSTR>(xps_path.value().c_str()); |
driver_info.pConfigFile = const_cast<LPWSTR>(ui_path.value().c_str()); |
@@ -223,19 +262,14 @@ HRESULT InstallPpd() { |
// Set up supported print system version. Must be 3. |
driver_info.cVersion = 3; |
- // 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; |
+ return result; |
} |
HRESULT UninstallPpd() { |
@@ -309,14 +343,59 @@ HRESULT UninstallPrinter(void) { |
return S_OK; |
} |
-HRESULT InstallVirtualDriver(void) { |
+void SetupUninstall(const FilePath& install_path) { |
+ // Now write the Windows Uninstall entries |
+ // Minimal error checking here since the install can contiunue |
+ // if this fails. |
+ base::win::RegKey key; |
+ if (key.Create(HKEY_LOCAL_MACHINE, kUninstallRegistry, |
+ KEY_SET_VALUE) != ERROR_SUCCESS) { |
+ LOG(ERROR) << "Unable to open key"; |
+ return; |
+ } |
+ CommandLine uninstall_command(install_path.Append(kInstallerName)); |
+ uninstall_command.AppendArg("--uninstall"); |
+ key.WriteValue(L"UninstallString", |
+ uninstall_command.GetCommandLineString().c_str()); |
+ key.WriteValue(L"InstallLocation", install_path.value().c_str()); |
+ |
+ |
+ // Get the version resource. |
+ scoped_ptr<FileVersionInfo> version_info( |
+ FileVersionInfo::CreateFileVersionInfoForCurrentModule()); |
+ |
+ if (version_info.get()) { |
+ FileVersionInfoWin* version_info_win = |
+ static_cast<FileVersionInfoWin*>(version_info.get()); |
+ key.WriteValue(L"DisplayVersion", |
+ version_info_win->file_version().c_str()); |
+ key.WriteValue(L"Publisher", version_info_win->company_name().c_str()); |
+ } else { |
+ LOG(ERROR) << "Unable to get version string"; |
+ } |
+ key.WriteValue(L"DisplayName", |
+ cloud_print::LoadLocalString(IDS_DRIVER_NAME).c_str()); |
+ key.WriteValue(L"NoModify", 1); |
+ key.WriteValue(L"NoRepair", 1); |
+} |
+ |
+void CleanupUninstall() { |
+ ::RegDeleteKey(HKEY_LOCAL_MACHINE, kUninstallRegistry); |
+} |
+ |
+HRESULT InstallVirtualDriver(const FilePath& install_path) { |
HRESULT result = S_OK; |
- result = RegisterPortMonitor(true); |
+ if (!file_util::CreateDirectory(install_path)) { |
+ LOG(ERROR) << "Can't create install directory"; |
+ return ERROR_ACCESS_DENIED; |
+ } |
+ SetupUninstall(install_path); |
+ result = RegisterPortMonitor(true, install_path); |
if (!SUCCEEDED(result)) { |
LOG(ERROR) << "Unable to register port monitor."; |
return result; |
} |
- result = InstallPpd(); |
+ result = InstallPpd(install_path); |
if (!SUCCEEDED(result)) { |
LOG(ERROR) << "Unable to install Ppd."; |
return result; |
@@ -326,11 +405,11 @@ HRESULT InstallVirtualDriver(void) { |
LOG(ERROR) << "Unable to install printer."; |
return result; |
} |
- SetRegistryKeys(); |
+ SetOmahaKeys(); |
return S_OK; |
} |
-HRESULT UninstallVirtualDriver(void) { |
+HRESULT UninstallVirtualDriver(const FilePath& install_path) { |
HRESULT result = S_OK; |
result = UninstallPrinter(); |
if (!SUCCEEDED(result)) { |
@@ -342,15 +421,35 @@ HRESULT UninstallVirtualDriver(void) { |
LOG(ERROR) << "Unable to remove Ppd."; |
return result; |
} |
- result = RegisterPortMonitor(false); |
+ result = RegisterPortMonitor(false, install_path); |
if (!SUCCEEDED(result)) { |
LOG(ERROR) << "Unable to remove port monitor."; |
return result; |
} |
- DeleteRegistryKeys(); |
+ DeleteOmahaKeys(); |
+ file_util::Delete(install_path, true); |
+ CleanupUninstall(); |
return S_OK; |
} |
+HRESULT LaunchChildForUninstall() { |
+ FilePath installer_source; |
+ if (PathService::Get(base::FILE_EXE, &installer_source)) { |
+ FilePath temp_path; |
+ if (file_util::CreateTemporaryFile(&temp_path)) { |
+ file_util::Move(installer_source, temp_path); |
+ file_util::DeleteAfterReboot(temp_path); |
+ CommandLine command_line(temp_path); |
+ command_line.AppendArg("--douninstall"); |
+ base::LaunchOptions options; |
+ if (!base::LaunchProcess(command_line, options, NULL)) { |
+ LOG(ERROR) << "Unable to launch child uninstall."; |
+ return ERROR_NOT_SUPPORTED; |
+ } |
+ } |
+ } |
+ return S_OK; |
+} |
} // namespace |
int WINAPI WinMain(__in HINSTANCE hInstance, |
@@ -359,11 +458,17 @@ int WINAPI WinMain(__in HINSTANCE hInstance, |
__in int nCmdShow) { |
base::AtExitManager at_exit_manager; |
CommandLine::Init(0, NULL); |
- HRESULT retval = S_OK; |
- if (CommandLine::ForCurrentProcess()->HasSwitch("uninstall")) { |
- retval = UninstallVirtualDriver(); |
- } else { |
- retval = InstallVirtualDriver(); |
+ |
+ FilePath install_path; |
+ HRESULT retval = PathService::Get(base::DIR_EXE, &install_path); |
+ if (SUCCEEDED(retval)) { |
+ if (CommandLine::ForCurrentProcess()->HasSwitch("douninstall")) { |
+ retval = UninstallVirtualDriver(install_path); |
+ } else if (CommandLine::ForCurrentProcess()->HasSwitch("uninstall")) { |
+ retval = LaunchChildForUninstall(); |
+ } else { |
+ retval = InstallVirtualDriver(install_path); |
+ } |
} |
// Installer is silent by default as required by Omaha. |
if (CommandLine::ForCurrentProcess()->HasSwitch("verbose")) { |