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

Side by Side Diff: chrome/browser/printing/cloud_print/printer_info_win.cc

Issue 1566047: First cut of Cloud Print Proxy implementation. The code is not enabled for no... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Final review changes Created 10 years, 8 months 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 | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 "chrome/browser/printing/cloud_print/printer_info.h"
6
7 #include <windows.h>
8 #include <objidl.h>
9 #include <ocidl.h>
10 #include <olectl.h>
11 #include <prntvpt.h>
12 #include <winspool.h>
13
14 #include "base/lock.h"
15 #include "base/object_watcher.h"
16 #include "base/scoped_bstr_win.h"
17 #include "base/scoped_comptr_win.h"
18 #include "base/scoped_handle_win.h"
19 #include "base/scoped_ptr.h"
20 #include "base/utf_string_conversions.h"
21
22 #pragma comment(lib, "prntvpt.lib")
23 #pragma comment(lib, "rpcrt4.lib")
24
25 namespace {
26
27 class DevMode {
28 public:
29 DevMode() : dm_(NULL) {}
30 ~DevMode() { Free(); }
31
32 void Allocate(int size) {
33 Free();
34 dm_ = reinterpret_cast<DEVMODE*>(new char[size]);
35 }
36
37 void Free() {
38 if (dm_)
39 delete dm_;
40 dm_ = NULL;
41 }
42
43 DEVMODE* dm_;
44
45 private:
46 DISALLOW_COPY_AND_ASSIGN(DevMode);
47 };
48
49 bool InitXPSModule() {
50 HMODULE prntvpt_module = LoadLibrary(L"prntvpt.dll");
51 return (NULL != prntvpt_module);
52 }
53
54 inline HRESULT GetLastErrorHR() {
55 LONG error = GetLastError();
56 return HRESULT_FROM_WIN32(error);
57 }
58
59 HRESULT StreamFromPrintTicket(const std::string& print_ticket,
60 IStream** stream) {
61 DCHECK(stream);
62 HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
63 if (FAILED(hr)) {
64 return hr;
65 }
66 ULONG bytes_written = 0;
67 (*stream)->Write(print_ticket.c_str(), print_ticket.length(), &bytes_written);
68 DCHECK(bytes_written == print_ticket.length());
69 LARGE_INTEGER pos = {0};
70 ULARGE_INTEGER new_pos = {0};
71 (*stream)->Seek(pos, STREAM_SEEK_SET, &new_pos);
72 return S_OK;
73 }
74
75 HRESULT StreamOnHGlobalToString(IStream* stream, std::string* out) {
76 DCHECK(stream);
77 DCHECK(out);
78 HGLOBAL hdata = NULL;
79 HRESULT hr = GetHGlobalFromStream(stream, &hdata);
80 if (SUCCEEDED(hr)) {
81 DCHECK(hdata);
82 ScopedHGlobal<char> locked_data(hdata);
83 out->assign(locked_data.release(), locked_data.Size());
84 }
85 return hr;
86 }
87
88 HRESULT PrintTicketToDevMode(const std::string& printer_name,
89 const std::string& print_ticket,
90 DevMode* dev_mode) {
91 DCHECK(dev_mode);
92
93 ScopedComPtr<IStream> pt_stream;
94 HRESULT hr = StreamFromPrintTicket(print_ticket, pt_stream.Receive());
95 if (FAILED(hr))
96 return hr;
97
98 HPTPROVIDER provider = NULL;
99 hr = PTOpenProvider(UTF8ToWide(printer_name).c_str(), 1, &provider);
100 if (SUCCEEDED(hr)) {
101 ULONG size = 0;
102 DEVMODE* dm = NULL;
103 hr = PTConvertPrintTicketToDevMode(provider,
104 pt_stream,
105 kUserDefaultDevmode,
106 kPTDocumentScope,
107 &size,
108 &dm,
109 NULL);
110 if (SUCCEEDED(hr)) {
111 dev_mode->Allocate(size);
112 memcpy(dev_mode->dm_, dm, size);
113 PTReleaseMemory(dm);
114 }
115 PTCloseProvider(provider);
116 }
117 return hr;
118 }
119
120 HRESULT PrintPdf2DC(HDC dc, const FilePath& pdf_filename) {
121 HRESULT hr = E_NOTIMPL;
122 // TODO(sanjeevr): Implement this.
123 NOTIMPLEMENTED();
124 return hr;
125 }
126
127 } // namespace
128
129 namespace cloud_print {
130
131 void EnumeratePrinters(PrinterList* printer_list) {
132 DCHECK(printer_list);
133 DWORD bytes_needed = 0;
134 DWORD count_returned = 0;
135 BOOL ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2,
136 NULL, 0, &bytes_needed, &count_returned);
137 if (0 != bytes_needed) {
138 scoped_ptr<BYTE> printer_info_buffer(new BYTE[bytes_needed]);
139 ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2,
140 printer_info_buffer.get(), bytes_needed, &bytes_needed,
141 &count_returned);
142 DCHECK(ret);
143 PRINTER_INFO_2* printer_info =
144 reinterpret_cast<PRINTER_INFO_2*>(printer_info_buffer.get());
145 for (DWORD index = 0; index < count_returned; index++) {
146 PrinterBasicInfo info;
147 info.printer_name = WideToUTF8(printer_info[index].pPrinterName);
148 if (printer_info[index].pComment)
149 info.printer_description = WideToUTF8(printer_info[index].pComment);
150 info.printer_status = printer_info[index].Status;
151 printer_list->push_back(info);
152 }
153 }
154 }
155
156 bool GetPrinterCapsAndDefaults(const std::string& printer_name,
157 PrinterCapsAndDefaults* printer_info) {
158 if (!InitXPSModule()) {
159 // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
160 return false;
161 }
162 if (!IsValidPrinter(printer_name)) {
163 return false;
164 }
165 DCHECK(printer_info);
166 HPTPROVIDER provider = NULL;
167 std::wstring printer_name_wide = UTF8ToWide(printer_name);
168 HRESULT hr = PTOpenProvider(printer_name_wide.c_str(), 1, &provider);
169 DCHECK(SUCCEEDED(hr));
170 if (provider) {
171 ScopedComPtr<IStream> print_capabilities_stream;
172 hr = CreateStreamOnHGlobal(NULL, TRUE,
173 print_capabilities_stream.Receive());
174 DCHECK(SUCCEEDED(hr));
175 if (print_capabilities_stream) {
176 ScopedBstr error;
177 hr = PTGetPrintCapabilities(provider, NULL, print_capabilities_stream,
178 error.Receive());
179 DCHECK(SUCCEEDED(hr));
180 if (FAILED(hr)) {
181 return false;
182 }
183 hr = StreamOnHGlobalToString(print_capabilities_stream.get(),
184 &printer_info->printer_capabilities);
185 DCHECK(SUCCEEDED(hr));
186 printer_info->caps_mime_type = "text/xml";
187 }
188 // TODO(sanjeevr): Add ScopedPrinterHandle
189 HANDLE printer_handle = NULL;
190 OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle,
191 NULL);
192 DCHECK(printer_handle);
193 if (printer_handle) {
194 DWORD devmode_size = DocumentProperties(
195 NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()),
196 NULL, NULL, 0);
197 DCHECK(0 != devmode_size);
198 scoped_ptr<BYTE> devmode_out_buffer(new BYTE[devmode_size]);
199 DEVMODE* devmode_out =
200 reinterpret_cast<DEVMODE*>(devmode_out_buffer.get());
201 DocumentProperties(
202 NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()),
203 devmode_out, NULL, DM_OUT_BUFFER);
204 ScopedComPtr<IStream> printer_defaults_stream;
205 hr = CreateStreamOnHGlobal(NULL, TRUE,
206 printer_defaults_stream.Receive());
207 DCHECK(SUCCEEDED(hr));
208 if (printer_defaults_stream) {
209 hr = PTConvertDevModeToPrintTicket(provider, devmode_size,
210 devmode_out, kPTJobScope,
211 printer_defaults_stream);
212 DCHECK(SUCCEEDED(hr));
213 if (SUCCEEDED(hr)) {
214 hr = StreamOnHGlobalToString(printer_defaults_stream.get(),
215 &printer_info->printer_defaults);
216 DCHECK(SUCCEEDED(hr));
217 printer_info->defaults_mime_type = "text/xml";
218 }
219 }
220 ClosePrinter(printer_handle);
221 }
222 PTCloseProvider(provider);
223 }
224 return true;
225 }
226
227 bool ValidatePrintTicket(const std::string& printer_name,
228 const std::string& print_ticket_data) {
229 if (!InitXPSModule()) {
230 // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
231 return false;
232 }
233 bool ret = false;
234 HPTPROVIDER provider = NULL;
235 PTOpenProvider(UTF8ToWide(printer_name.c_str()).c_str(), 1, &provider);
236 if (provider) {
237 ScopedComPtr<IStream> print_ticket_stream;
238 CreateStreamOnHGlobal(NULL, TRUE, print_ticket_stream.Receive());
239 ULONG bytes_written = 0;
240 print_ticket_stream->Write(print_ticket_data.c_str(),
241 print_ticket_data.length(),
242 &bytes_written);
243 DCHECK(bytes_written == print_ticket_data.length());
244 LARGE_INTEGER pos = {0};
245 ULARGE_INTEGER new_pos = {0};
246 print_ticket_stream->Seek(pos, STREAM_SEEK_SET, &new_pos);
247 ScopedBstr error;
248 ScopedComPtr<IStream> result_ticket_stream;
249 CreateStreamOnHGlobal(NULL, TRUE, result_ticket_stream.Receive());
250 ret = SUCCEEDED(PTMergeAndValidatePrintTicket(provider,
251 print_ticket_stream.get(),
252 NULL,
253 kPTJobScope,
254 result_ticket_stream.get(),
255 error.Receive()));
256 PTCloseProvider(provider);
257 }
258 return ret;
259 }
260
261 std::string GenerateProxyId() {
262 GUID proxy_id = {0};
263 HRESULT hr = UuidCreate(&proxy_id);
264 DCHECK(SUCCEEDED(hr));
265 wchar_t* proxy_id_as_string = NULL;
266 UuidToString(&proxy_id, reinterpret_cast<RPC_WSTR *>(&proxy_id_as_string));
267 DCHECK(proxy_id_as_string);
268 std::string ret;
269 WideToUTF8(proxy_id_as_string, wcslen(proxy_id_as_string), &ret);
270 RpcStringFree(reinterpret_cast<RPC_WSTR *>(&proxy_id_as_string));
271 return ret;
272 }
273
274 bool SpoolPrintJob(const std::string& print_ticket,
275 const FilePath& print_data_file_path,
276 const std::string& print_data_mime_type,
277 const std::string& printer_name,
278 const std::string& job_title,
279 PlatformJobId* job_id_ret) {
280 if (!InitXPSModule()) {
281 // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
282 return false;
283 }
284 DevMode pt_dev_mode;
285 HRESULT hr = PrintTicketToDevMode(printer_name, print_ticket, &pt_dev_mode);
286 if (FAILED(hr)) {
287 NOTREACHED();
288 return false;
289 }
290 ScopedHDC dc(CreateDC(L"WINSPOOL", UTF8ToWide(printer_name).c_str(), NULL,
291 pt_dev_mode.dm_));
292 if (!dc.Get()) {
293 NOTREACHED();
294 return false;
295 }
296 hr = E_FAIL;
297 DOCINFO di = {0};
298 di.cbSize = sizeof(DOCINFO);
299 std::wstring doc_name = UTF8ToWide(job_title);
300 di.lpszDocName = doc_name.c_str();
301 int job_id = StartDoc(dc.Get(), &di);
302 if (SP_ERROR != job_id) {
303 if (print_data_mime_type == "application/pdf") {
304 hr = PrintPdf2DC(dc.Get(), print_data_file_path);
305 } else {
306 NOTREACHED();
307 }
308 EndDoc(dc.Get());
309 if (SUCCEEDED(hr) && job_id_ret) {
310 *job_id_ret = job_id;
311 }
312 }
313 return SUCCEEDED(hr);
314 }
315
316 bool GetJobDetails(const std::string& printer_name,
317 PlatformJobId job_id,
318 PrintJobDetails *job_details) {
319 DCHECK(job_details);
320 HANDLE printer_handle = NULL;
321 std::wstring printer_name_wide = UTF8ToWide(printer_name);
322 OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle,
323 NULL);
324 DCHECK(printer_handle);
325 bool ret = false;
326 if (printer_handle) {
327 DWORD bytes_needed = 0;
328 GetJob(printer_handle, job_id, 1, NULL, 0, &bytes_needed);
329 DWORD last_error = GetLastError();
330 if (ERROR_INVALID_PARAMETER != last_error) {
331 // ERROR_INVALID_PARAMETER normally means that the job id is not valid.
332 DCHECK(last_error == ERROR_INSUFFICIENT_BUFFER);
333 scoped_ptr<BYTE> job_info_buffer(new BYTE[bytes_needed]);
334 if (GetJob(printer_handle, job_id, 1, job_info_buffer.get(), bytes_needed,
335 &bytes_needed)) {
336 JOB_INFO_1 *job_info =
337 reinterpret_cast<JOB_INFO_1 *>(job_info_buffer.get());
338 if (job_info->pStatus) {
339 WideToUTF8(job_info->pStatus, wcslen(job_info->pStatus),
340 &job_details->status_message);
341 }
342 job_details->platform_status_flags = job_info->Status;
343 if ((job_info->Status & JOB_STATUS_COMPLETE) ||
344 (job_info->Status & JOB_STATUS_PRINTED)) {
345 job_details->status = PRINT_JOB_STATUS_COMPLETED;
346 } else if (job_info->Status & JOB_STATUS_ERROR) {
347 job_details->status = PRINT_JOB_STATUS_ERROR;
348 } else {
349 job_details->status = PRINT_JOB_STATUS_IN_PROGRESS;
350 }
351 job_details->total_pages = job_info->TotalPages;
352 job_details->pages_printed = job_info->PagesPrinted;
353 ret = true;
354 }
355 }
356 ClosePrinter(printer_handle);
357 }
358 return ret;
359 }
360
361 bool IsValidPrinter(const std::string& printer_name) {
362 std::wstring printer_name_wide = UTF8ToWide(printer_name);
363 HANDLE printer_handle = NULL;
364 OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle,
365 NULL);
366 bool ret = false;
367 if (printer_handle) {
368 ret = true;
369 ClosePrinter(printer_handle);
370 }
371 return ret;
372 }
373
374 class PrinterChangeNotifier::NotificationState
375 : public base::ObjectWatcher::Delegate {
376 public:
377 NotificationState() : printer_(NULL), printer_change_(NULL), delegate_(NULL) {
378 }
379 ~NotificationState() {
380 Stop();
381 }
382 bool Start(const std::string& printer_name,
383 PrinterChangeNotifier::Delegate* delegate) {
384 delegate_ = delegate;
385 // An empty printer name means watch the current server, we need to pass
386 // NULL to OpenPrinter.
387 LPTSTR printer_name_to_use = NULL;
388 std::wstring printer_name_wide;
389 if (!printer_name.empty()) {
390 printer_name_wide = UTF8ToWide(printer_name);
391 printer_name_to_use = const_cast<LPTSTR>(printer_name_wide.c_str());
392 }
393 bool ret = false;
394 OpenPrinter(printer_name_to_use, &printer_, NULL);
395 if (printer_) {
396 printer_change_ = FindFirstPrinterChangeNotification(
397 printer_, PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB, 0, NULL);
398 if (printer_change_) {
399 ret = watcher_.StartWatching(printer_change_, this);
400 }
401 }
402 if (!ret) {
403 Stop();
404 }
405 return ret;
406 }
407 bool Stop() {
408 watcher_.StopWatching();
409 if (printer_) {
410 ClosePrinter(printer_);
411 printer_ = NULL;
412 }
413 if (printer_change_) {
414 FindClosePrinterChangeNotification(printer_change_);
415 printer_change_ = NULL;
416 }
417 return true;
418 }
419
420 void OnObjectSignaled(HANDLE object) {
421 DWORD change = 0;
422 FindNextPrinterChangeNotification(object, &change, NULL, NULL);
423
424 if (change != ((PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB) &
425 (~PRINTER_CHANGE_FAILED_CONNECTION_PRINTER))) {
426 // For printer connections, we get spurious change notifications with
427 // all flags set except PRINTER_CHANGE_FAILED_CONNECTION_PRINTER.
428 // Ignore these.
429 if (change & PRINTER_CHANGE_ADD_PRINTER) {
430 delegate_->OnPrinterAdded();
431 } else if (change & PRINTER_CHANGE_DELETE_PRINTER) {
432 delegate_->OnPrinterDeleted();
433 } else if (change & PRINTER_CHANGE_SET_PRINTER) {
434 delegate_->OnPrinterChanged();
435 }
436 if (change & PRINTER_CHANGE_JOB) {
437 delegate_->OnJobChanged();
438 }
439 }
440 watcher_.StartWatching(printer_change_, this);
441 }
442 HANDLE printer_handle() const {
443 return printer_;
444 }
445 private:
446 base::ObjectWatcher watcher_;
447 HANDLE printer_; // The printer being watched
448 HANDLE printer_change_; // Returned by FindFirstPrinterChangeNotifier
449 PrinterChangeNotifier::Delegate* delegate_; // Delegate to notify
450 bool did_signal_; // DoneWaiting was called
451 };
452
453 PrinterChangeNotifier::PrinterChangeNotifier() : state_(NULL) {
454 }
455
456 PrinterChangeNotifier::~PrinterChangeNotifier() {
457 StopWatching();
458 }
459
460 bool PrinterChangeNotifier::StartWatching(const std::string& printer_name,
461 Delegate* delegate) {
462 if (state_) {
463 NOTREACHED();
464 return false;
465 }
466 state_ = new NotificationState;
467 if (!state_->Start(printer_name, delegate)) {
468 StopWatching();
469 return false;
470 }
471 return true;
472 }
473
474 bool PrinterChangeNotifier::StopWatching() {
475 if (!state_) {
476 return false;
477 }
478 state_->Stop();
479 delete state_;
480 state_ = NULL;
481 return true;
482 }
483
484 bool PrinterChangeNotifier::GetCurrentPrinterInfo(
485 PrinterBasicInfo* printer_info) {
486 if (!state_) {
487 return false;
488 }
489 DCHECK(printer_info);
490 DWORD bytes_needed = 0;
491 bool ret = false;
492 GetPrinter(state_->printer_handle(), 2, NULL, 0, &bytes_needed);
493 if (0 != bytes_needed) {
494 scoped_ptr<BYTE> printer_info_buffer(new BYTE[bytes_needed]);
495 if (GetPrinter(state_->printer_handle(), 2, printer_info_buffer.get(),
496 bytes_needed, &bytes_needed)) {
497 PRINTER_INFO_2* printer_info_win =
498 reinterpret_cast<PRINTER_INFO_2*>(printer_info_buffer.get());
499 printer_info->printer_name = WideToUTF8(printer_info_win->pPrinterName);
500 printer_info->printer_description =
501 WideToUTF8(printer_info_win->pComment);
502 printer_info->printer_status = printer_info_win->Status;
503 ret = true;
504 }
505 }
506 return ret;
507 }
508 } // namespace cloud_print
509
OLDNEW
« no previous file with comments | « chrome/browser/printing/cloud_print/printer_info_mac.cc ('k') | chrome/browser/printing/cloud_print/printer_job_handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698