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.
6 #include <setupapi.h> // Must be included after windows.h
10 #include "base/at_exit.h"
11 #include "base/command_line.h"
12 #include "base/file_version_info_win.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/logging.h"
16 #include "base/path_service.h"
17 #include "base/process/launch.h"
18 #include "base/process/process.h"
19 #include "base/strings/string16.h"
20 #include "base/strings/string_util.h"
21 #include "base/win/registry.h"
22 #include "base/win/scoped_handle.h"
23 #include "base/win/windows_version.h"
24 #include "cloud_print/common/win/cloud_print_utils.h"
25 #include "cloud_print/common/win/install_utils.h"
26 #include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
27 #include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
28 #include "grit/virtual_driver_setup_resources.h"
30 #include <strsafe.h> // Must be after base headers to avoid deprecation
33 namespace cloud_print
{
37 const wchar_t kNameValue
[] = L
"GCP Virtual Driver";
38 const wchar_t kUninstallId
[] = L
"{74AA24E0-AC50-4B28-BA46-9CF05467C9B7}";
39 const wchar_t kInstallerName
[] = L
"virtual_driver_setup.exe";
40 const wchar_t kGcpUrl
[] = L
"http://www.google.com/cloudprint";
42 const wchar_t kDataFileName
[] = L
"gcp_driver.gpd";
43 const wchar_t kDriverName
[] = L
"MXDWDRV.DLL";
44 const wchar_t kUiDriverName
[] = L
"UNIDRVUI.DLL";
45 const wchar_t kHelpName
[] = L
"UNIDRV.HLP";
46 const wchar_t* kDependencyList
[] = {
59 const char kDelete
[] = "delete";
60 const char kInstallSwitch
[] = "install";
61 const char kRegisterSwitch
[] = "register";
62 const char kUninstallSwitch
[] = "uninstall";
63 const char kUnregisterSwitch
[] = "unregister";
65 base::FilePath
GetSystemPath(const base::string16
& binary
) {
67 if (!PathService::Get(base::DIR_SYSTEM
, &path
)) {
68 LOG(ERROR
) << "Unable to get system path.";
71 return path
.Append(binary
);
74 base::FilePath
GetNativeSystemPath(const base::string16
& binary
) {
76 return GetSystemPath(binary
);
78 // Sysnative will bypass filesystem redirection and give us
79 // the location of the 64bit system32 from a 32 bit process.
80 if (!PathService::Get(base::DIR_WINDOWS
, &path
)) {
81 LOG(ERROR
) << "Unable to get windows path.";
84 return path
.Append(L
"sysnative").Append(binary
);
87 void SpoolerServiceCommand(const char* command
) {
88 base::FilePath net_path
= GetNativeSystemPath(L
"net");
91 CommandLine
command_line(net_path
);
92 command_line
.AppendArg(command
);
93 command_line
.AppendArg("spooler");
94 command_line
.AppendArg("/y");
96 base::LaunchOptions options
;
98 options
.start_hidden
= true;
99 VLOG(0) << command_line
.GetCommandLineString();
100 base::LaunchProcess(command_line
, options
, NULL
);
103 HRESULT
RegisterPortMonitor(bool install
, const base::FilePath
& install_path
) {
104 DCHECK(install
|| install_path
.empty());
105 base::FilePath target_path
= GetNativeSystemPath(GetPortMonitorDllName());
106 if (target_path
.empty()) {
107 LOG(ERROR
) << "Unable to get port monitor target path.";
108 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND
);
111 base::FilePath source_path
=
112 install_path
.Append(GetPortMonitorDllName());
113 if (!base::CopyFile(source_path
, target_path
)) {
114 LOG(ERROR
) << "Unable copy port monitor dll from " <<
115 source_path
.value() << " to " << target_path
.value();
116 return GetLastHResult();
118 } else if (!base::PathExists(target_path
)) {
119 // Already removed. Just "succeed" silently.
123 base::FilePath regsvr32_path
= GetNativeSystemPath(L
"regsvr32.exe");
124 if (regsvr32_path
.empty()) {
125 LOG(ERROR
) << "Can't find regsvr32.exe.";
126 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND
);
129 CommandLine
command_line(regsvr32_path
);
130 command_line
.AppendArg("/s");
132 command_line
.AppendArg("/u");
135 // Use system32 path here because otherwise ::AddMonitor would fail.
136 command_line
.AppendArgPath(GetSystemPath(GetPortMonitorDllName()));
138 base::LaunchOptions options
;
141 base::win::ScopedHandle regsvr32_handle
;
142 if (!base::LaunchProcess(command_line
.GetCommandLineString(), options
,
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
.Get(), &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 base::FilePath
destination_copy(destination
);
214 if (base::win::GetVersion() >= base::win::VERSION_VISTA
) {
215 // GetCorePrinterDrivers and GetPrinterDriverPackagePath only exist on
216 // Vista and later. Winspool.drv must be delayloaded so these calls don't
217 // create problems on XP.
218 DWORD size
= MAX_PATH
;
219 wchar_t package_path
[MAX_PATH
] = {0};
220 CORE_PRINTER_DRIVER driver
;
221 GetCorePrinterDrivers(NULL
, NULL
, L
"{D20EA372-DD35-4950-9ED8-A6335AFE79F5}",
223 GetPrinterDriverPackagePath(NULL
, NULL
, NULL
, driver
.szPackageID
,
224 package_path
, MAX_PATH
, &size
);
225 SetupIterateCabinet(package_path
, 0, &CabinetCallback
, &destination_copy
);
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
,
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
<base::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 return HRESULT_FROM_WIN32(ERROR_BAD_DRIVER
);
276 DRIVER_INFO_6 driver_info
= {0};
277 // Set up supported print system version. Must be 3.
278 driver_info
.cVersion
= 3;
280 // None of the print API structures likes constant strings even though they
281 // don't modify the string. const_casting is the cleanest option.
282 driver_info
.pDataFile
= const_cast<LPWSTR
>(data_file
.value().c_str());
283 driver_info
.pHelpFile
= const_cast<LPWSTR
>(ui_help_path
.value().c_str());
284 driver_info
.pDriverPath
= const_cast<LPWSTR
>(xps_path
.value().c_str());
285 driver_info
.pConfigFile
= const_cast<LPWSTR
>(ui_path
.value().c_str());
287 base::string16
dependent_files(JoinString(dependent_array
, L
'\n'));
288 dependent_files
.push_back(L
'\n');
289 std::replace(dependent_files
.begin(), dependent_files
.end(), L
'\n', L
'\0');
290 driver_info
.pDependentFiles
= &dependent_files
[0];
292 // Set up user visible strings.
293 base::string16 manufacturer
= LoadLocalString(IDS_GOOGLE
);
294 driver_info
.pszMfgName
= const_cast<LPWSTR
>(manufacturer
.c_str());
295 driver_info
.pszProvider
= const_cast<LPWSTR
>(manufacturer
.c_str());
296 driver_info
.pszOEMUrl
= const_cast<LPWSTR
>(kGcpUrl
);
297 driver_info
.dwlDriverVersion
= GetVersionNumber();
298 base::string16 driver_name
= LoadLocalString(IDS_DRIVER_NAME
);
299 driver_info
.pName
= const_cast<LPWSTR
>(driver_name
.c_str());
301 if (!::AddPrinterDriverEx(NULL
, 6, reinterpret_cast<BYTE
*>(&driver_info
),
302 APD_COPY_NEW_FILES
| APD_COPY_FROM_DIRECTORY
)) {
303 LOG(ERROR
) << "Unable to add printer driver";
304 return GetLastHResult();
309 HRESULT
UninstallDriver() {
311 base::string16 driver_name
= LoadLocalString(IDS_DRIVER_NAME
);
312 while (!DeletePrinterDriverEx(NULL
,
314 const_cast<LPWSTR
>(driver_name
.c_str()),
315 DPD_DELETE_UNUSED_FILES
,
317 if (GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER
) {
318 LOG(WARNING
) << "Print driver is already uninstalled.";
321 // After deleting the printer it can take a few seconds before
322 // the driver is free for deletion. Retry a few times before giving up.
323 LOG(WARNING
) << "Attempt to delete printer driver failed. Retrying.";
328 HRESULT result
= GetLastHResult();
329 LOG(ERROR
) << "Unable to delete printer driver.";
335 HRESULT
InstallPrinter(void) {
336 PRINTER_INFO_2 printer_info
= {0};
338 // None of the print API structures likes constant strings even though they
339 // don't modify the string. const_casting is the cleanest option.
340 base::string16 driver_name
= LoadLocalString(IDS_DRIVER_NAME
);
341 printer_info
.pDriverName
= const_cast<LPWSTR
>(driver_name
.c_str());
342 printer_info
.pPrinterName
= const_cast<LPWSTR
>(driver_name
.c_str());
343 printer_info
.pComment
= const_cast<LPWSTR
>(driver_name
.c_str());
344 printer_info
.pLocation
= const_cast<LPWSTR
>(kGcpUrl
);
345 base::string16 port_name
;
346 printer_info
.pPortName
= const_cast<LPWSTR
>(kPortName
);
347 printer_info
.Attributes
= PRINTER_ATTRIBUTE_DIRECT
|PRINTER_ATTRIBUTE_LOCAL
;
348 printer_info
.pPrintProcessor
= const_cast<LPWSTR
>(L
"winprint");
349 HANDLE handle
= AddPrinter(NULL
, 2, reinterpret_cast<BYTE
*>(&printer_info
));
350 if (handle
== NULL
) {
351 HRESULT result
= GetLastHResult();
352 LOG(ERROR
) << "Unable to add printer";
355 ClosePrinter(handle
);
359 HRESULT
UninstallPrinter(void) {
360 HANDLE handle
= NULL
;
361 PRINTER_DEFAULTS printer_defaults
= {0};
362 printer_defaults
.DesiredAccess
= PRINTER_ALL_ACCESS
;
363 base::string16 driver_name
= LoadLocalString(IDS_DRIVER_NAME
);
364 if (!OpenPrinter(const_cast<LPWSTR
>(driver_name
.c_str()),
366 &printer_defaults
)) {
367 // If we can't open the printer, it was probably already removed.
368 LOG(WARNING
) << "Unable to open printer";
371 if (!DeletePrinter(handle
)) {
372 HRESULT result
= GetLastHResult();
373 LOG(ERROR
) << "Unable to delete printer";
374 ClosePrinter(handle
);
377 ClosePrinter(handle
);
381 bool IsOSSupported() {
382 // We don't support XP service pack 2 or older.
383 base::win::Version version
= base::win::GetVersion();
384 return (version
> base::win::VERSION_XP
) ||
385 ((version
== base::win::VERSION_XP
) &&
386 (base::win::OSInfo::GetInstance()->service_pack().major
>= 3));
389 HRESULT
RegisterVirtualDriver(const base::FilePath
& install_path
) {
390 HRESULT result
= S_OK
;
392 DCHECK(base::DirectoryExists(install_path
));
393 if (!IsOSSupported()) {
394 LOG(ERROR
) << "Requires XP SP3 or later.";
395 return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION
);
398 result
= InstallDriver(install_path
);
399 if (FAILED(result
)) {
400 LOG(ERROR
) << "Unable to install driver.";
404 result
= RegisterPortMonitor(true, install_path
);
405 if (FAILED(result
)) {
406 LOG(ERROR
) << "Unable to register port monitor.";
410 result
= InstallPrinter();
411 if (FAILED(result
) &&
412 result
!= HRESULT_FROM_WIN32(ERROR_PRINTER_ALREADY_EXISTS
)) {
413 LOG(ERROR
) << "Unable to install printer.";
419 HRESULT
TryUnregisterVirtualDriver() {
420 HRESULT result
= S_OK
;
421 result
= UninstallPrinter();
422 if (FAILED(result
)) {
423 LOG(ERROR
) << "Unable to delete printer.";
426 result
= UninstallDriver();
427 if (FAILED(result
)) {
428 LOG(ERROR
) << "Unable to remove driver.";
431 // The second argument is ignored if the first is false.
432 result
= RegisterPortMonitor(false, base::FilePath());
433 if (FAILED(result
)) {
434 LOG(ERROR
) << "Unable to remove port monitor.";
440 HRESULT
UnregisterVirtualDriver() {
441 HRESULT hr
= S_FALSE
;
442 for (int i
= 0; i
< 2; ++i
) {
443 hr
= TryUnregisterVirtualDriver();
447 // Restart spooler and try again.
448 SpoolerServiceCommand("stop");
449 SpoolerServiceCommand("start");
454 HRESULT
DoUninstall() {
455 DeleteGoogleUpdateKeys(kGoogleUpdateProductId
);
456 HRESULT result
= UnregisterVirtualDriver();
459 DeleteUninstallKey(kUninstallId
);
460 DeleteProgramDir(kDelete
);
464 HRESULT
DoUnregister() {
465 return UnregisterVirtualDriver();
468 HRESULT
DoRegister(const base::FilePath
& install_path
) {
469 HRESULT result
= UnregisterVirtualDriver();
472 return RegisterVirtualDriver(install_path
);
475 HRESULT
DoDelete(const base::FilePath
& install_path
) {
476 if (install_path
.value().empty())
478 if (!base::DirectoryExists(install_path
))
480 Sleep(5000); // Give parent some time to exit.
481 return base::DeleteFile(install_path
, true) ? S_OK
: E_FAIL
;
484 HRESULT
DoInstall(const base::FilePath
& install_path
) {
485 HRESULT result
= UnregisterVirtualDriver();
486 if (FAILED(result
)) {
487 LOG(ERROR
) << "Unable to unregister.";
490 base::FilePath old_install_path
= GetInstallLocation(kUninstallId
);
491 if (!old_install_path
.value().empty() &&
492 install_path
!= old_install_path
) {
493 if (base::DirectoryExists(old_install_path
))
494 base::DeleteFile(old_install_path
, true);
496 CreateUninstallKey(kUninstallId
, LoadLocalString(IDS_DRIVER_NAME
),
498 result
= RegisterVirtualDriver(install_path
);
501 SetGoogleUpdateKeys(kGoogleUpdateProductId
, kNameValue
);
505 HRESULT
ExecuteCommands() {
506 const CommandLine
& command_line
= *CommandLine::ForCurrentProcess();
508 base::FilePath exe_path
;
509 if (FAILED(PathService::Get(base::DIR_EXE
, &exe_path
)) ||
510 !base::DirectoryExists(exe_path
)) {
511 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND
);
514 if (command_line
.HasSwitch(kDelete
)) {
515 return DoDelete(command_line
.GetSwitchValuePath(kDelete
));
516 } else if (command_line
.HasSwitch(kUninstallSwitch
)) {
517 return DoUninstall();
518 } else if (command_line
.HasSwitch(kInstallSwitch
)) {
519 return DoInstall(exe_path
);
520 } else if (command_line
.HasSwitch(kUnregisterSwitch
)) {
521 return DoUnregister();
522 } else if (command_line
.HasSwitch(kRegisterSwitch
)) {
523 return DoRegister(exe_path
);
531 } // namespace cloud_print
533 int WINAPI
WinMain(__in HINSTANCE hInstance
,
534 __in HINSTANCE hPrevInstance
,
535 __in LPSTR lpCmdLine
,
537 using namespace cloud_print
;
539 base::AtExitManager at_exit_manager
;
540 CommandLine::Init(0, NULL
);
542 HRESULT retval
= ExecuteCommands();
544 if (retval
== HRESULT_FROM_WIN32(ERROR_BAD_DRIVER
)) {
545 SetGoogleUpdateError(kGoogleUpdateProductId
,
546 LoadLocalString(IDS_ERROR_NO_XPS
));
547 } else if (FAILED(retval
)) {
548 SetGoogleUpdateError(kGoogleUpdateProductId
, retval
);
551 VLOG(0) << GetErrorMessage(retval
)
552 << " HRESULT=0x" << std::setbase(16) << retval
;
554 // Installer is silent by default as required by Google Update.
555 if (CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
556 DisplayWindowsMessage(NULL
, retval
, LoadLocalString(IDS_DRIVER_NAME
));