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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include <windows.h>
6 #include <setupapi.h> // Must be included after windows.h
7 #include <winspool.h>
8 #include <stddef.h>
9
10 #include <iomanip>
11
12 #include "base/at_exit.h"
13 #include "base/command_line.h"
14 #include "base/files/file_util.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/windows_version.h"
22 #include "cloud_print/common/win/cloud_print_utils.h"
23 #include "cloud_print/common/win/install_utils.h"
24 #include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
25 #include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
26 #include "grit/virtual_driver_setup_resources.h"
27
28 #include <strsafe.h> // Must be after base headers to avoid deprecation
29 // warnings.
30
31 namespace cloud_print {
32
33 namespace {
34
35 const wchar_t kNameValue[] = L"GCP Virtual Driver";
36 const wchar_t kUninstallId[] = L"{74AA24E0-AC50-4B28-BA46-9CF05467C9B7}";
37 const wchar_t kGcpUrl[] = L"https://www.google.com/cloudprint";
38
39 const wchar_t kInfFileName[] = L"gcp_driver.inf";
40
41 const char kDelete[] = "delete";
42 const char kInstallSwitch[] = "install";
43 const char kRegisterSwitch[] = "register";
44 const char kUninstallSwitch[] = "uninstall";
45 const char kUnregisterSwitch[] = "unregister";
46
47 base::FilePath GetSystemPath(const base::string16& binary) {
48 base::FilePath path;
49 if (!PathService::Get(base::DIR_SYSTEM, &path)) {
50 LOG(ERROR) << "Unable to get system path.";
51 return path;
52 }
53 return path.Append(binary);
54 }
55
56 void SpoolerServiceCommand(const char* command) {
57 base::FilePath net_path = GetSystemPath(L"net");
58 if (net_path.empty())
59 return;
60 base::CommandLine command_line(net_path);
61 command_line.AppendArg(command);
62 command_line.AppendArg("spooler");
63 command_line.AppendArg("/y");
64
65 base::LaunchOptions options;
66 options.wait = true;
67 options.start_hidden = true;
68 VLOG(0) << command_line.GetCommandLineString();
69 base::LaunchProcess(command_line, options);
70 }
71
72 HRESULT RegisterPortMonitor(bool install, const base::FilePath& install_path) {
73 DCHECK(install || install_path.empty());
74 base::FilePath target_path = GetSystemPath(L"gcp_portmon.dll");
75 if (target_path.empty()) {
76 LOG(ERROR) << "Unable to get port monitor target path.";
77 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
78 }
79 if (install) {
80 base::FilePath source_path = install_path.Append(L"gcp_portmon.dll");
81 if (!base::CopyFile(source_path, target_path)) {
82 LOG(ERROR) << "Unable copy port monitor dll from " << source_path.value()
83 << " to " << target_path.value();
84 return GetLastHResult();
85 }
86 } else if (!base::PathExists(target_path)) {
87 // Already removed. Just "succeed" silently.
88 return S_OK;
89 }
90
91 base::FilePath regsvr32_path = GetSystemPath(L"regsvr32.exe");
92 if (regsvr32_path.empty()) {
93 LOG(ERROR) << "Can't find regsvr32.exe.";
94 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
95 }
96
97 base::CommandLine command_line(regsvr32_path);
98 command_line.AppendArg("/s");
99 if (!install) {
100 command_line.AppendArg("/u");
101 }
102
103 // Use system32 path here because otherwise ::AddMonitor would fail.
104 command_line.AppendArgPath(GetSystemPath(L"gcp_portmon.dll"));
105
106 base::LaunchOptions options;
107 options.wait = true;
108
109 base::Process regsvr32_process =
110 base::LaunchProcess(command_line.GetCommandLineString(), options);
111 if (!regsvr32_process.IsValid()) {
112 LOG(ERROR) << "Unable to launch regsvr32.exe.";
113 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
114 }
115
116 DWORD exit_code = S_OK;
117 if (install) {
118 if (!GetExitCodeProcess(regsvr32_process.Handle(), &exit_code)) {
119 LOG(ERROR) << "Unable to get regsvr32.exe exit code.";
120 return GetLastHResult();
121 }
122 if (exit_code != 0) {
123 LOG(ERROR) << "Regsvr32.exe failed with " << exit_code;
124 return HRESULT_FROM_WIN32(exit_code);
125 }
126 } else {
127 if (!base::DeleteFile(target_path, false)) {
128 SpoolerServiceCommand("stop");
129 bool deleted = base::DeleteFile(target_path, false);
130 SpoolerServiceCommand("start");
131
132 if (!deleted) {
133 LOG(ERROR) << "Unable to delete " << target_path.value();
134 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
135 }
136 }
137 }
138 return S_OK;
139 }
140
141 HRESULT InstallDriver(const base::FilePath& install_path) {
142 DWORD size = MAX_PATH * 10;
143 wchar_t package_path[MAX_PATH * 10] = {0};
144
145 base::FilePath inf_file = install_path.Append(kInfFileName);
146 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
147
148 HRESULT result = UploadPrinterDriverPackage(
149 NULL, inf_file.value().c_str(), NULL,
150 UPDP_SILENT_UPLOAD | UPDP_UPLOAD_ALWAYS, NULL, package_path, &size);
151 if (FAILED(result)) {
152 LOG(ERROR)
153 << "Uploading the printer driver package to the driver cache failed.";
154 return result;
155 }
156
157 result = InstallPrinterDriverFromPackage(
158 NULL, package_path, driver_name.c_str(), NULL, IPDFP_COPY_ALL_FILES);
159 if (FAILED(result)) {
160 LOG(ERROR) << "Installing the printer driver failed.";
161 }
162 return result;
163 }
164
165 HRESULT UninstallDriver(const base::FilePath& install_path) {
166 base::FilePath inf_file = install_path.Append(kInfFileName);
167 int tries = 3;
168
169 while (!DeletePrinterDriverPackage(NULL, inf_file.value().c_str(), NULL)) {
170 if (GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER) {
171 LOG(WARNING) << "Print driver is already uninstalled.";
172 return S_OK;
173 }
174 // After deleting the printer it can take a few seconds before
175 // the driver is free for deletion. Retry a few times before giving up.
176 LOG(WARNING) << "Attempt to delete printer driver failed. Retrying.";
177 tries--;
178 Sleep(2000);
179 }
180 if (tries <= 0) {
181 HRESULT result = GetLastHResult();
182 LOG(ERROR) << "Unable to delete printer driver.";
183 return result;
184 }
185 return S_OK;
186 }
187
188 HRESULT InstallPrinter(void) {
189 PRINTER_INFO_2 printer_info = {0};
190
191 // None of the print API structures likes constant strings even though they
192 // don't modify the string. const_casting is the cleanest option.
193 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
194 printer_info.pDriverName = const_cast<LPWSTR>(driver_name.c_str());
195 printer_info.pPrinterName = const_cast<LPWSTR>(driver_name.c_str());
196 printer_info.pComment = const_cast<LPWSTR>(driver_name.c_str());
197 printer_info.pLocation = const_cast<LPWSTR>(kGcpUrl);
198 base::string16 port_name;
199 printer_info.pPortName = const_cast<LPWSTR>(kPortName);
200 printer_info.Attributes = PRINTER_ATTRIBUTE_DIRECT | PRINTER_ATTRIBUTE_LOCAL;
201 printer_info.pPrintProcessor = const_cast<LPWSTR>(L"winprint");
202 HANDLE handle = AddPrinter(NULL, 2, reinterpret_cast<BYTE*>(&printer_info));
203 if (handle == NULL) {
204 HRESULT result = GetLastHResult();
205 LOG(ERROR) << "Unable to add printer";
206 return result;
207 }
208 ClosePrinter(handle);
209 return S_OK;
210 }
211
212 HRESULT UninstallPrinter(void) {
213 HANDLE handle = NULL;
214 PRINTER_DEFAULTS printer_defaults = {0};
215 printer_defaults.DesiredAccess = PRINTER_ALL_ACCESS;
216 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
217 if (!OpenPrinter(const_cast<LPWSTR>(driver_name.c_str()), &handle,
218 &printer_defaults)) {
219 // If we can't open the printer, it was probably already removed.
220 LOG(WARNING) << "Unable to open printer";
221 return S_OK;
222 }
223 if (!DeletePrinter(handle)) {
224 HRESULT result = GetLastHResult();
225 LOG(ERROR) << "Unable to delete printer";
226 ClosePrinter(handle);
227 return result;
228 }
229 ClosePrinter(handle);
230 return S_OK;
231 }
232
233 bool IsOSSupported() {
234 // We don't support Vista or older.
235 base::win::Version version = base::win::GetVersion();
236 return (version >= base::win::VERSION_WIN7);
237 }
238
239 HRESULT RegisterVirtualDriver(const base::FilePath& install_path) {
240 HRESULT result = S_OK;
241
242 DCHECK(base::DirectoryExists(install_path));
243 if (!IsOSSupported()) {
244 LOG(ERROR) << "Requires Windows 7 or later.";
245 return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
246 }
247
248 result = InstallDriver(install_path);
249 if (FAILED(result)) {
250 LOG(ERROR) << "Unable to install driver.";
251 return result;
252 }
253
254 result = RegisterPortMonitor(true, install_path);
255 if (FAILED(result)) {
256 LOG(ERROR) << "Unable to register port monitor.";
257 return result;
258 }
259
260 result = InstallPrinter();
261 if (FAILED(result) &&
262 result != HRESULT_FROM_WIN32(ERROR_PRINTER_ALREADY_EXISTS)) {
263 LOG(ERROR) << "Unable to install printer.";
264 return result;
265 }
266 return S_OK;
267 }
268
269 HRESULT TryUnregisterVirtualDriver(const base::FilePath& install_path) {
270 HRESULT result = S_OK;
271 result = UninstallPrinter();
272 if (FAILED(result)) {
273 LOG(ERROR) << "Unable to delete printer.";
274 return result;
275 }
276 result = UninstallDriver(install_path);
277 if (FAILED(result)) {
278 LOG(ERROR) << "Unable to remove driver.";
279 return result;
280 }
281 // The second argument is ignored if the first is false.
282 result = RegisterPortMonitor(false, base::FilePath());
283 if (FAILED(result)) {
284 LOG(ERROR) << "Unable to remove port monitor.";
285 return result;
286 }
287 return S_OK;
288 }
289
290 HRESULT UnregisterVirtualDriver(const base::FilePath& install_path) {
291 HRESULT hr = S_FALSE;
292 for (int i = 0; i < 2; ++i) {
293 hr = TryUnregisterVirtualDriver(install_path);
294 if (SUCCEEDED(hr)) {
295 break;
296 }
297 // Restart spooler and try again.
298 SpoolerServiceCommand("stop");
299 SpoolerServiceCommand("start");
300 }
301 return hr;
302 }
303
304 HRESULT DoUninstall(const base::FilePath& install_path) {
305 DeleteGoogleUpdateKeys(kGoogleUpdateProductId);
306 HRESULT result = UnregisterVirtualDriver(install_path);
307 if (FAILED(result))
308 return result;
309 DeleteUninstallKey(kUninstallId);
310 DeleteProgramDir(kDelete);
311 return S_OK;
312 }
313
314 HRESULT DoUnregister(const base::FilePath& install_path) {
315 return UnregisterVirtualDriver(install_path);
316 }
317
318 HRESULT DoRegister(const base::FilePath& install_path) {
319 HRESULT result = UnregisterVirtualDriver(install_path);
320 if (FAILED(result))
321 return result;
322 return RegisterVirtualDriver(install_path);
323 }
324
325 HRESULT DoDelete(const base::FilePath& install_path) {
326 if (install_path.value().empty())
327 return E_INVALIDARG;
328 if (!base::DirectoryExists(install_path))
329 return S_FALSE;
330 Sleep(5000); // Give parent some time to exit.
331 return base::DeleteFile(install_path, true) ? S_OK : E_FAIL;
332 }
333
334 HRESULT DoInstall(const base::FilePath& install_path) {
335 HRESULT result = UnregisterVirtualDriver(install_path);
336 if (FAILED(result)) {
337 LOG(ERROR) << "Unable to unregister.";
338 return result;
339 }
340 base::FilePath old_install_path = GetInstallLocation(kUninstallId);
341 if (!old_install_path.value().empty() && install_path != old_install_path) {
342 if (base::DirectoryExists(old_install_path))
343 base::DeleteFile(old_install_path, true);
344 }
345 CreateUninstallKey(kUninstallId, LoadLocalString(IDS_DRIVER_NAME),
346 kUninstallSwitch);
347 result = RegisterVirtualDriver(install_path);
348 if (FAILED(result))
349 return result;
350 SetGoogleUpdateKeys(kGoogleUpdateProductId, kNameValue);
351 return result;
352 }
353
354 HRESULT ExecuteCommands() {
355 const base::CommandLine& command_line =
356 *base::CommandLine::ForCurrentProcess();
357
358 base::FilePath exe_path;
359 if (!PathService::Get(base::DIR_EXE, &exe_path) ||
360 !base::DirectoryExists(exe_path)) {
361 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
362 }
363
364 if (command_line.HasSwitch(kDelete)) {
365 return DoDelete(command_line.GetSwitchValuePath(kDelete));
366 } else if (command_line.HasSwitch(kUninstallSwitch)) {
367 return DoUninstall(exe_path);
368 } else if (command_line.HasSwitch(kInstallSwitch)) {
369 return DoInstall(exe_path);
370 } else if (command_line.HasSwitch(kUnregisterSwitch)) {
371 return DoUnregister(exe_path);
372 } else if (command_line.HasSwitch(kRegisterSwitch)) {
373 return DoRegister(exe_path);
374 }
375
376 return E_INVALIDARG;
377 }
378
379 } // namespace
380
381 } // namespace cloud_print
382
383 int WINAPI WinMain(__in HINSTANCE hInstance,
384 __in HINSTANCE hPrevInstance,
385 __in LPSTR lpCmdLine,
386 __in int nCmdShow) {
387 using namespace cloud_print;
388
389 base::AtExitManager at_exit_manager;
390 base::CommandLine::Init(0, NULL);
391
392 HRESULT retval = ExecuteCommands();
393
394 if (retval == HRESULT_FROM_WIN32(ERROR_BAD_DRIVER)) {
395 SetGoogleUpdateError(kGoogleUpdateProductId,
396 LoadLocalString(IDS_ERROR_NO_XPS));
397 } else if (FAILED(retval)) {
398 SetGoogleUpdateError(kGoogleUpdateProductId, retval);
399 }
400
401 VLOG(0) << GetErrorMessage(retval) << " HRESULT=0x" << std::setbase(16)
402 << retval;
403
404 // Installer is silent by default as required by Google Update.
405 if (base::CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
406 DisplayWindowsMessage(NULL, retval, LoadLocalString(IDS_DRIVER_NAME));
407 }
408 return retval;
409 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698