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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/printing/cloud_print/printer_info_win.cc
===================================================================
--- chrome/browser/printing/cloud_print/printer_info_win.cc (revision 0)
+++ chrome/browser/printing/cloud_print/printer_info_win.cc (revision 0)
@@ -0,0 +1,509 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/printing/cloud_print/printer_info.h"
+
+#include <windows.h>
+#include <objidl.h>
+#include <ocidl.h>
+#include <olectl.h>
+#include <prntvpt.h>
+#include <winspool.h>
+
+#include "base/lock.h"
+#include "base/object_watcher.h"
+#include "base/scoped_bstr_win.h"
+#include "base/scoped_comptr_win.h"
+#include "base/scoped_handle_win.h"
+#include "base/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+
+#pragma comment(lib, "prntvpt.lib")
+#pragma comment(lib, "rpcrt4.lib")
+
+namespace {
+
+class DevMode {
+ public:
+ DevMode() : dm_(NULL) {}
+ ~DevMode() { Free(); }
+
+ void Allocate(int size) {
+ Free();
+ dm_ = reinterpret_cast<DEVMODE*>(new char[size]);
+ }
+
+ void Free() {
+ if (dm_)
+ delete dm_;
+ dm_ = NULL;
+ }
+
+ DEVMODE* dm_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DevMode);
+};
+
+bool InitXPSModule() {
+ HMODULE prntvpt_module = LoadLibrary(L"prntvpt.dll");
+ return (NULL != prntvpt_module);
+}
+
+inline HRESULT GetLastErrorHR() {
+ LONG error = GetLastError();
+ return HRESULT_FROM_WIN32(error);
+}
+
+HRESULT StreamFromPrintTicket(const std::string& print_ticket,
+ IStream** stream) {
+ DCHECK(stream);
+ HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ ULONG bytes_written = 0;
+ (*stream)->Write(print_ticket.c_str(), print_ticket.length(), &bytes_written);
+ DCHECK(bytes_written == print_ticket.length());
+ LARGE_INTEGER pos = {0};
+ ULARGE_INTEGER new_pos = {0};
+ (*stream)->Seek(pos, STREAM_SEEK_SET, &new_pos);
+ return S_OK;
+}
+
+HRESULT StreamOnHGlobalToString(IStream* stream, std::string* out) {
+ DCHECK(stream);
+ DCHECK(out);
+ HGLOBAL hdata = NULL;
+ HRESULT hr = GetHGlobalFromStream(stream, &hdata);
+ if (SUCCEEDED(hr)) {
+ DCHECK(hdata);
+ ScopedHGlobal<char> locked_data(hdata);
+ out->assign(locked_data.release(), locked_data.Size());
+ }
+ return hr;
+}
+
+HRESULT PrintTicketToDevMode(const std::string& printer_name,
+ const std::string& print_ticket,
+ DevMode* dev_mode) {
+ DCHECK(dev_mode);
+
+ ScopedComPtr<IStream> pt_stream;
+ HRESULT hr = StreamFromPrintTicket(print_ticket, pt_stream.Receive());
+ if (FAILED(hr))
+ return hr;
+
+ HPTPROVIDER provider = NULL;
+ hr = PTOpenProvider(UTF8ToWide(printer_name).c_str(), 1, &provider);
+ if (SUCCEEDED(hr)) {
+ ULONG size = 0;
+ DEVMODE* dm = NULL;
+ hr = PTConvertPrintTicketToDevMode(provider,
+ pt_stream,
+ kUserDefaultDevmode,
+ kPTDocumentScope,
+ &size,
+ &dm,
+ NULL);
+ if (SUCCEEDED(hr)) {
+ dev_mode->Allocate(size);
+ memcpy(dev_mode->dm_, dm, size);
+ PTReleaseMemory(dm);
+ }
+ PTCloseProvider(provider);
+ }
+ return hr;
+}
+
+HRESULT PrintPdf2DC(HDC dc, const FilePath& pdf_filename) {
+ HRESULT hr = E_NOTIMPL;
+ // TODO(sanjeevr): Implement this.
+ NOTIMPLEMENTED();
+ return hr;
+}
+
+} // namespace
+
+namespace cloud_print {
+
+void EnumeratePrinters(PrinterList* printer_list) {
+ DCHECK(printer_list);
+ DWORD bytes_needed = 0;
+ DWORD count_returned = 0;
+ BOOL ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2,
+ NULL, 0, &bytes_needed, &count_returned);
+ if (0 != bytes_needed) {
+ scoped_ptr<BYTE> printer_info_buffer(new BYTE[bytes_needed]);
+ ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2,
+ printer_info_buffer.get(), bytes_needed, &bytes_needed,
+ &count_returned);
+ DCHECK(ret);
+ PRINTER_INFO_2* printer_info =
+ reinterpret_cast<PRINTER_INFO_2*>(printer_info_buffer.get());
+ for (DWORD index = 0; index < count_returned; index++) {
+ PrinterBasicInfo info;
+ info.printer_name = WideToUTF8(printer_info[index].pPrinterName);
+ if (printer_info[index].pComment)
+ info.printer_description = WideToUTF8(printer_info[index].pComment);
+ info.printer_status = printer_info[index].Status;
+ printer_list->push_back(info);
+ }
+ }
+}
+
+bool GetPrinterCapsAndDefaults(const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info) {
+ if (!InitXPSModule()) {
+ // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
+ return false;
+ }
+ if (!IsValidPrinter(printer_name)) {
+ return false;
+ }
+ DCHECK(printer_info);
+ HPTPROVIDER provider = NULL;
+ std::wstring printer_name_wide = UTF8ToWide(printer_name);
+ HRESULT hr = PTOpenProvider(printer_name_wide.c_str(), 1, &provider);
+ DCHECK(SUCCEEDED(hr));
+ if (provider) {
+ ScopedComPtr<IStream> print_capabilities_stream;
+ hr = CreateStreamOnHGlobal(NULL, TRUE,
+ print_capabilities_stream.Receive());
+ DCHECK(SUCCEEDED(hr));
+ if (print_capabilities_stream) {
+ ScopedBstr error;
+ hr = PTGetPrintCapabilities(provider, NULL, print_capabilities_stream,
+ error.Receive());
+ DCHECK(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return false;
+ }
+ hr = StreamOnHGlobalToString(print_capabilities_stream.get(),
+ &printer_info->printer_capabilities);
+ DCHECK(SUCCEEDED(hr));
+ printer_info->caps_mime_type = "text/xml";
+ }
+ // TODO(sanjeevr): Add ScopedPrinterHandle
+ HANDLE printer_handle = NULL;
+ OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle,
+ NULL);
+ DCHECK(printer_handle);
+ if (printer_handle) {
+ DWORD devmode_size = DocumentProperties(
+ NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()),
+ NULL, NULL, 0);
+ DCHECK(0 != devmode_size);
+ scoped_ptr<BYTE> devmode_out_buffer(new BYTE[devmode_size]);
+ DEVMODE* devmode_out =
+ reinterpret_cast<DEVMODE*>(devmode_out_buffer.get());
+ DocumentProperties(
+ NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()),
+ devmode_out, NULL, DM_OUT_BUFFER);
+ ScopedComPtr<IStream> printer_defaults_stream;
+ hr = CreateStreamOnHGlobal(NULL, TRUE,
+ printer_defaults_stream.Receive());
+ DCHECK(SUCCEEDED(hr));
+ if (printer_defaults_stream) {
+ hr = PTConvertDevModeToPrintTicket(provider, devmode_size,
+ devmode_out, kPTJobScope,
+ printer_defaults_stream);
+ DCHECK(SUCCEEDED(hr));
+ if (SUCCEEDED(hr)) {
+ hr = StreamOnHGlobalToString(printer_defaults_stream.get(),
+ &printer_info->printer_defaults);
+ DCHECK(SUCCEEDED(hr));
+ printer_info->defaults_mime_type = "text/xml";
+ }
+ }
+ ClosePrinter(printer_handle);
+ }
+ PTCloseProvider(provider);
+ }
+ return true;
+}
+
+bool ValidatePrintTicket(const std::string& printer_name,
+ const std::string& print_ticket_data) {
+ if (!InitXPSModule()) {
+ // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
+ return false;
+ }
+ bool ret = false;
+ HPTPROVIDER provider = NULL;
+ PTOpenProvider(UTF8ToWide(printer_name.c_str()).c_str(), 1, &provider);
+ if (provider) {
+ ScopedComPtr<IStream> print_ticket_stream;
+ CreateStreamOnHGlobal(NULL, TRUE, print_ticket_stream.Receive());
+ ULONG bytes_written = 0;
+ print_ticket_stream->Write(print_ticket_data.c_str(),
+ print_ticket_data.length(),
+ &bytes_written);
+ DCHECK(bytes_written == print_ticket_data.length());
+ LARGE_INTEGER pos = {0};
+ ULARGE_INTEGER new_pos = {0};
+ print_ticket_stream->Seek(pos, STREAM_SEEK_SET, &new_pos);
+ ScopedBstr error;
+ ScopedComPtr<IStream> result_ticket_stream;
+ CreateStreamOnHGlobal(NULL, TRUE, result_ticket_stream.Receive());
+ ret = SUCCEEDED(PTMergeAndValidatePrintTicket(provider,
+ print_ticket_stream.get(),
+ NULL,
+ kPTJobScope,
+ result_ticket_stream.get(),
+ error.Receive()));
+ PTCloseProvider(provider);
+ }
+ return ret;
+}
+
+std::string GenerateProxyId() {
+ GUID proxy_id = {0};
+ HRESULT hr = UuidCreate(&proxy_id);
+ DCHECK(SUCCEEDED(hr));
+ wchar_t* proxy_id_as_string = NULL;
+ UuidToString(&proxy_id, reinterpret_cast<RPC_WSTR *>(&proxy_id_as_string));
+ DCHECK(proxy_id_as_string);
+ std::string ret;
+ WideToUTF8(proxy_id_as_string, wcslen(proxy_id_as_string), &ret);
+ RpcStringFree(reinterpret_cast<RPC_WSTR *>(&proxy_id_as_string));
+ return ret;
+}
+
+bool SpoolPrintJob(const std::string& print_ticket,
+ const FilePath& print_data_file_path,
+ const std::string& print_data_mime_type,
+ const std::string& printer_name,
+ const std::string& job_title,
+ PlatformJobId* job_id_ret) {
+ if (!InitXPSModule()) {
+ // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
+ return false;
+ }
+ DevMode pt_dev_mode;
+ HRESULT hr = PrintTicketToDevMode(printer_name, print_ticket, &pt_dev_mode);
+ if (FAILED(hr)) {
+ NOTREACHED();
+ return false;
+ }
+ ScopedHDC dc(CreateDC(L"WINSPOOL", UTF8ToWide(printer_name).c_str(), NULL,
+ pt_dev_mode.dm_));
+ if (!dc.Get()) {
+ NOTREACHED();
+ return false;
+ }
+ hr = E_FAIL;
+ DOCINFO di = {0};
+ di.cbSize = sizeof(DOCINFO);
+ std::wstring doc_name = UTF8ToWide(job_title);
+ di.lpszDocName = doc_name.c_str();
+ int job_id = StartDoc(dc.Get(), &di);
+ if (SP_ERROR != job_id) {
+ if (print_data_mime_type == "application/pdf") {
+ hr = PrintPdf2DC(dc.Get(), print_data_file_path);
+ } else {
+ NOTREACHED();
+ }
+ EndDoc(dc.Get());
+ if (SUCCEEDED(hr) && job_id_ret) {
+ *job_id_ret = job_id;
+ }
+ }
+ return SUCCEEDED(hr);
+}
+
+bool GetJobDetails(const std::string& printer_name,
+ PlatformJobId job_id,
+ PrintJobDetails *job_details) {
+ DCHECK(job_details);
+ HANDLE printer_handle = NULL;
+ std::wstring printer_name_wide = UTF8ToWide(printer_name);
+ OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle,
+ NULL);
+ DCHECK(printer_handle);
+ bool ret = false;
+ if (printer_handle) {
+ DWORD bytes_needed = 0;
+ GetJob(printer_handle, job_id, 1, NULL, 0, &bytes_needed);
+ DWORD last_error = GetLastError();
+ if (ERROR_INVALID_PARAMETER != last_error) {
+ // ERROR_INVALID_PARAMETER normally means that the job id is not valid.
+ DCHECK(last_error == ERROR_INSUFFICIENT_BUFFER);
+ scoped_ptr<BYTE> job_info_buffer(new BYTE[bytes_needed]);
+ if (GetJob(printer_handle, job_id, 1, job_info_buffer.get(), bytes_needed,
+ &bytes_needed)) {
+ JOB_INFO_1 *job_info =
+ reinterpret_cast<JOB_INFO_1 *>(job_info_buffer.get());
+ if (job_info->pStatus) {
+ WideToUTF8(job_info->pStatus, wcslen(job_info->pStatus),
+ &job_details->status_message);
+ }
+ job_details->platform_status_flags = job_info->Status;
+ if ((job_info->Status & JOB_STATUS_COMPLETE) ||
+ (job_info->Status & JOB_STATUS_PRINTED)) {
+ job_details->status = PRINT_JOB_STATUS_COMPLETED;
+ } else if (job_info->Status & JOB_STATUS_ERROR) {
+ job_details->status = PRINT_JOB_STATUS_ERROR;
+ } else {
+ job_details->status = PRINT_JOB_STATUS_IN_PROGRESS;
+ }
+ job_details->total_pages = job_info->TotalPages;
+ job_details->pages_printed = job_info->PagesPrinted;
+ ret = true;
+ }
+ }
+ ClosePrinter(printer_handle);
+ }
+ return ret;
+}
+
+bool IsValidPrinter(const std::string& printer_name) {
+ std::wstring printer_name_wide = UTF8ToWide(printer_name);
+ HANDLE printer_handle = NULL;
+ OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle,
+ NULL);
+ bool ret = false;
+ if (printer_handle) {
+ ret = true;
+ ClosePrinter(printer_handle);
+ }
+ return ret;
+}
+
+class PrinterChangeNotifier::NotificationState
+ : public base::ObjectWatcher::Delegate {
+ public:
+ NotificationState() : printer_(NULL), printer_change_(NULL), delegate_(NULL) {
+ }
+ ~NotificationState() {
+ Stop();
+ }
+ bool Start(const std::string& printer_name,
+ PrinterChangeNotifier::Delegate* delegate) {
+ delegate_ = delegate;
+ // An empty printer name means watch the current server, we need to pass
+ // NULL to OpenPrinter.
+ LPTSTR printer_name_to_use = NULL;
+ std::wstring printer_name_wide;
+ if (!printer_name.empty()) {
+ printer_name_wide = UTF8ToWide(printer_name);
+ printer_name_to_use = const_cast<LPTSTR>(printer_name_wide.c_str());
+ }
+ bool ret = false;
+ OpenPrinter(printer_name_to_use, &printer_, NULL);
+ if (printer_) {
+ printer_change_ = FindFirstPrinterChangeNotification(
+ printer_, PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB, 0, NULL);
+ if (printer_change_) {
+ ret = watcher_.StartWatching(printer_change_, this);
+ }
+ }
+ if (!ret) {
+ Stop();
+ }
+ return ret;
+ }
+ bool Stop() {
+ watcher_.StopWatching();
+ if (printer_) {
+ ClosePrinter(printer_);
+ printer_ = NULL;
+ }
+ if (printer_change_) {
+ FindClosePrinterChangeNotification(printer_change_);
+ printer_change_ = NULL;
+ }
+ return true;
+ }
+
+ void OnObjectSignaled(HANDLE object) {
+ DWORD change = 0;
+ FindNextPrinterChangeNotification(object, &change, NULL, NULL);
+
+ if (change != ((PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB) &
+ (~PRINTER_CHANGE_FAILED_CONNECTION_PRINTER))) {
+ // For printer connections, we get spurious change notifications with
+ // all flags set except PRINTER_CHANGE_FAILED_CONNECTION_PRINTER.
+ // Ignore these.
+ if (change & PRINTER_CHANGE_ADD_PRINTER) {
+ delegate_->OnPrinterAdded();
+ } else if (change & PRINTER_CHANGE_DELETE_PRINTER) {
+ delegate_->OnPrinterDeleted();
+ } else if (change & PRINTER_CHANGE_SET_PRINTER) {
+ delegate_->OnPrinterChanged();
+ }
+ if (change & PRINTER_CHANGE_JOB) {
+ delegate_->OnJobChanged();
+ }
+ }
+ watcher_.StartWatching(printer_change_, this);
+ }
+ HANDLE printer_handle() const {
+ return printer_;
+ }
+ private:
+ base::ObjectWatcher watcher_;
+ HANDLE printer_; // The printer being watched
+ HANDLE printer_change_; // Returned by FindFirstPrinterChangeNotifier
+ PrinterChangeNotifier::Delegate* delegate_; // Delegate to notify
+ bool did_signal_; // DoneWaiting was called
+};
+
+PrinterChangeNotifier::PrinterChangeNotifier() : state_(NULL) {
+}
+
+PrinterChangeNotifier::~PrinterChangeNotifier() {
+ StopWatching();
+}
+
+bool PrinterChangeNotifier::StartWatching(const std::string& printer_name,
+ Delegate* delegate) {
+ if (state_) {
+ NOTREACHED();
+ return false;
+ }
+ state_ = new NotificationState;
+ if (!state_->Start(printer_name, delegate)) {
+ StopWatching();
+ return false;
+ }
+ return true;
+}
+
+bool PrinterChangeNotifier::StopWatching() {
+ if (!state_) {
+ return false;
+ }
+ state_->Stop();
+ delete state_;
+ state_ = NULL;
+ return true;
+}
+
+bool PrinterChangeNotifier::GetCurrentPrinterInfo(
+ PrinterBasicInfo* printer_info) {
+ if (!state_) {
+ return false;
+ }
+ DCHECK(printer_info);
+ DWORD bytes_needed = 0;
+ bool ret = false;
+ GetPrinter(state_->printer_handle(), 2, NULL, 0, &bytes_needed);
+ if (0 != bytes_needed) {
+ scoped_ptr<BYTE> printer_info_buffer(new BYTE[bytes_needed]);
+ if (GetPrinter(state_->printer_handle(), 2, printer_info_buffer.get(),
+ bytes_needed, &bytes_needed)) {
+ PRINTER_INFO_2* printer_info_win =
+ reinterpret_cast<PRINTER_INFO_2*>(printer_info_buffer.get());
+ printer_info->printer_name = WideToUTF8(printer_info_win->pPrinterName);
+ printer_info->printer_description =
+ WideToUTF8(printer_info_win->pComment);
+ printer_info->printer_status = printer_info_win->Status;
+ ret = true;
+ }
+ }
+ return ret;
+}
+} // namespace cloud_print
+
Property changes on: chrome\browser\printing\cloud_print\printer_info_win.cc
___________________________________________________________________
Added: svn:eol-style
+ LF
« 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