| 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
|
|
|
|
|