Home | History | Annotate | Download | only in backend
      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 "printing/backend/win_helper.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/file_version_info.h"
     10 #include "base/files/file_path.h"
     11 #include "base/logging.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "printing/backend/print_backend.h"
     15 #include "printing/backend/print_backend_consts.h"
     16 #include "printing/backend/printing_info_win.h"
     17 
     18 namespace {
     19 
     20 typedef HRESULT (WINAPI* PTOpenProviderProc)(PCWSTR printer_name,
     21                                              DWORD version,
     22                                              HPTPROVIDER* provider);
     23 
     24 typedef HRESULT (WINAPI* PTGetPrintCapabilitiesProc)(HPTPROVIDER provider,
     25                                                      IStream* print_ticket,
     26                                                      IStream* capabilities,
     27                                                      BSTR* error_message);
     28 
     29 typedef HRESULT (WINAPI* PTConvertDevModeToPrintTicketProc)(
     30     HPTPROVIDER provider,
     31     ULONG devmode_size_in_bytes,
     32     PDEVMODE devmode,
     33     EPrintTicketScope scope,
     34     IStream* print_ticket);
     35 
     36 typedef HRESULT (WINAPI* PTConvertPrintTicketToDevModeProc)(
     37     HPTPROVIDER provider,
     38     IStream* print_ticket,
     39     EDefaultDevmodeType base_devmode_type,
     40     EPrintTicketScope scope,
     41     ULONG* devmode_byte_count,
     42     PDEVMODE* devmode,
     43     BSTR* error_message);
     44 
     45 typedef HRESULT (WINAPI* PTMergeAndValidatePrintTicketProc)(
     46     HPTPROVIDER provider,
     47     IStream* base_ticket,
     48     IStream* delta_ticket,
     49     EPrintTicketScope scope,
     50     IStream* result_ticket,
     51     BSTR* error_message);
     52 
     53 typedef HRESULT (WINAPI* PTReleaseMemoryProc)(PVOID buffer);
     54 
     55 typedef HRESULT (WINAPI* PTCloseProviderProc)(HPTPROVIDER provider);
     56 
     57 typedef HRESULT (WINAPI* StartXpsPrintJobProc)(
     58     const LPCWSTR printer_name,
     59     const LPCWSTR job_name,
     60     const LPCWSTR output_file_name,
     61     HANDLE progress_event,
     62     HANDLE completion_event,
     63     UINT8* printable_pages_on,
     64     UINT32 printable_pages_on_count,
     65     IXpsPrintJob** xps_print_job,
     66     IXpsPrintJobStream** document_stream,
     67     IXpsPrintJobStream** print_ticket_stream);
     68 
     69 PTOpenProviderProc g_open_provider_proc = NULL;
     70 PTGetPrintCapabilitiesProc g_get_print_capabilities_proc = NULL;
     71 PTConvertDevModeToPrintTicketProc g_convert_devmode_to_print_ticket_proc = NULL;
     72 PTConvertPrintTicketToDevModeProc g_convert_print_ticket_to_devmode_proc = NULL;
     73 PTMergeAndValidatePrintTicketProc g_merge_and_validate_print_ticket_proc = NULL;
     74 PTReleaseMemoryProc g_release_memory_proc = NULL;
     75 PTCloseProviderProc g_close_provider_proc = NULL;
     76 StartXpsPrintJobProc g_start_xps_print_job_proc = NULL;
     77 
     78 }  // namespace
     79 
     80 
     81 namespace printing {
     82 
     83 bool XPSModule::Init() {
     84   static bool initialized = InitImpl();
     85   return initialized;
     86 }
     87 
     88 bool XPSModule::InitImpl() {
     89   HMODULE prntvpt_module = LoadLibrary(L"prntvpt.dll");
     90   if (prntvpt_module == NULL)
     91     return false;
     92   g_open_provider_proc = reinterpret_cast<PTOpenProviderProc>(
     93       GetProcAddress(prntvpt_module, "PTOpenProvider"));
     94   if (!g_open_provider_proc) {
     95     NOTREACHED();
     96     return false;
     97   }
     98   g_get_print_capabilities_proc = reinterpret_cast<PTGetPrintCapabilitiesProc>(
     99       GetProcAddress(prntvpt_module, "PTGetPrintCapabilities"));
    100   if (!g_get_print_capabilities_proc) {
    101     NOTREACHED();
    102     return false;
    103   }
    104   g_convert_devmode_to_print_ticket_proc =
    105       reinterpret_cast<PTConvertDevModeToPrintTicketProc>(
    106           GetProcAddress(prntvpt_module, "PTConvertDevModeToPrintTicket"));
    107   if (!g_convert_devmode_to_print_ticket_proc) {
    108     NOTREACHED();
    109     return false;
    110   }
    111   g_convert_print_ticket_to_devmode_proc =
    112       reinterpret_cast<PTConvertPrintTicketToDevModeProc>(
    113           GetProcAddress(prntvpt_module, "PTConvertPrintTicketToDevMode"));
    114   if (!g_convert_print_ticket_to_devmode_proc) {
    115     NOTREACHED();
    116     return false;
    117   }
    118   g_merge_and_validate_print_ticket_proc =
    119       reinterpret_cast<PTMergeAndValidatePrintTicketProc>(
    120           GetProcAddress(prntvpt_module, "PTMergeAndValidatePrintTicket"));
    121   if (!g_merge_and_validate_print_ticket_proc) {
    122     NOTREACHED();
    123     return false;
    124   }
    125   g_release_memory_proc =
    126       reinterpret_cast<PTReleaseMemoryProc>(
    127           GetProcAddress(prntvpt_module, "PTReleaseMemory"));
    128   if (!g_release_memory_proc) {
    129     NOTREACHED();
    130     return false;
    131   }
    132   g_close_provider_proc =
    133       reinterpret_cast<PTCloseProviderProc>(
    134           GetProcAddress(prntvpt_module, "PTCloseProvider"));
    135   if (!g_close_provider_proc) {
    136     NOTREACHED();
    137     return false;
    138   }
    139   return true;
    140 }
    141 
    142 HRESULT XPSModule::OpenProvider(const string16& printer_name,
    143                                 DWORD version,
    144                                 HPTPROVIDER* provider) {
    145   return g_open_provider_proc(printer_name.c_str(), version, provider);
    146 }
    147 
    148 HRESULT XPSModule::GetPrintCapabilities(HPTPROVIDER provider,
    149                                         IStream* print_ticket,
    150                                         IStream* capabilities,
    151                                         BSTR* error_message) {
    152   return g_get_print_capabilities_proc(provider,
    153                                        print_ticket,
    154                                        capabilities,
    155                                        error_message);
    156 }
    157 
    158 HRESULT XPSModule::ConvertDevModeToPrintTicket(HPTPROVIDER provider,
    159                                                ULONG devmode_size_in_bytes,
    160                                                PDEVMODE devmode,
    161                                                EPrintTicketScope scope,
    162                                                IStream* print_ticket) {
    163   return g_convert_devmode_to_print_ticket_proc(provider,
    164                                                 devmode_size_in_bytes,
    165                                                 devmode,
    166                                                 scope,
    167                                                 print_ticket);
    168 }
    169 
    170 HRESULT XPSModule::ConvertPrintTicketToDevMode(
    171     HPTPROVIDER provider,
    172     IStream* print_ticket,
    173     EDefaultDevmodeType base_devmode_type,
    174     EPrintTicketScope scope,
    175     ULONG* devmode_byte_count,
    176     PDEVMODE* devmode,
    177     BSTR* error_message) {
    178   return g_convert_print_ticket_to_devmode_proc(provider,
    179                                                 print_ticket,
    180                                                 base_devmode_type,
    181                                                 scope,
    182                                                 devmode_byte_count,
    183                                                 devmode,
    184                                                 error_message);
    185 }
    186 
    187 HRESULT XPSModule::MergeAndValidatePrintTicket(HPTPROVIDER provider,
    188                                                IStream* base_ticket,
    189                                                IStream* delta_ticket,
    190                                                EPrintTicketScope scope,
    191                                                IStream* result_ticket,
    192                                                BSTR* error_message) {
    193   return g_merge_and_validate_print_ticket_proc(provider,
    194                                                 base_ticket,
    195                                                 delta_ticket,
    196                                                 scope,
    197                                                 result_ticket,
    198                                                 error_message);
    199 }
    200 
    201 HRESULT XPSModule::ReleaseMemory(PVOID buffer) {
    202   return g_release_memory_proc(buffer);
    203 }
    204 
    205 HRESULT XPSModule::CloseProvider(HPTPROVIDER provider) {
    206   return g_close_provider_proc(provider);
    207 }
    208 
    209 ScopedXPSInitializer::ScopedXPSInitializer() : initialized_(false) {
    210   if (!XPSModule::Init())
    211     return;
    212   // Calls to XPS APIs typically require the XPS provider to be opened with
    213   // PTOpenProvider. PTOpenProvider calls CoInitializeEx with
    214   // COINIT_MULTITHREADED. We have seen certain buggy HP printer driver DLLs
    215   // that call CoInitializeEx with COINIT_APARTMENTTHREADED in the context of
    216   // PTGetPrintCapabilities. This call fails but the printer driver calls
    217   // CoUninitialize anyway. This results in the apartment being torn down too
    218   // early and the msxml DLL being unloaded which in turn causes code in
    219   // unidrvui.dll to have a dangling pointer to an XML document which causes a
    220   // crash. To protect ourselves from such drivers we make sure we always have
    221   // an extra CoInitialize (calls to CoInitialize/CoUninitialize are
    222   // refcounted).
    223   HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    224   // If this succeeded we are done because the PTOpenProvider call will provide
    225   // the extra refcount on the apartment. If it failed because someone already
    226   // called CoInitializeEx with COINIT_APARTMENTTHREADED, we try the other model
    227   // to provide the additional refcount (since we don't know which model buggy
    228   // printer drivers will use).
    229   if (!SUCCEEDED(hr))
    230     hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    231   DCHECK(SUCCEEDED(hr));
    232   initialized_ = true;
    233 }
    234 
    235 ScopedXPSInitializer::~ScopedXPSInitializer() {
    236   if (initialized_)
    237     CoUninitialize();
    238   initialized_ = false;
    239 }
    240 
    241 bool XPSPrintModule::Init() {
    242   static bool initialized = InitImpl();
    243   return initialized;
    244 }
    245 
    246 bool XPSPrintModule::InitImpl() {
    247   HMODULE xpsprint_module = LoadLibrary(L"xpsprint.dll");
    248   if (xpsprint_module == NULL)
    249     return false;
    250   g_start_xps_print_job_proc = reinterpret_cast<StartXpsPrintJobProc>(
    251       GetProcAddress(xpsprint_module, "StartXpsPrintJob"));
    252   if (!g_start_xps_print_job_proc) {
    253     NOTREACHED();
    254     return false;
    255   }
    256   return true;
    257 }
    258 
    259 HRESULT XPSPrintModule::StartXpsPrintJob(
    260     const LPCWSTR printer_name,
    261     const LPCWSTR job_name,
    262     const LPCWSTR output_file_name,
    263     HANDLE progress_event,
    264     HANDLE completion_event,
    265     UINT8* printable_pages_on,
    266     UINT32 printable_pages_on_count,
    267     IXpsPrintJob** xps_print_job,
    268     IXpsPrintJobStream** document_stream,
    269     IXpsPrintJobStream** print_ticket_stream) {
    270   return g_start_xps_print_job_proc(printer_name,
    271                                     job_name,
    272                                     output_file_name,
    273                                     progress_event,
    274                                     completion_event,
    275                                     printable_pages_on,
    276                                     printable_pages_on_count,
    277                                     xps_print_job,
    278                                     document_stream,
    279                                     print_ticket_stream);
    280 }
    281 
    282 bool InitBasicPrinterInfo(HANDLE printer, PrinterBasicInfo* printer_info) {
    283   DCHECK(printer);
    284   DCHECK(printer_info);
    285   if (!printer)
    286     return false;
    287 
    288   PrinterInfo2 info_2;
    289   if (!info_2.Init(printer))
    290     return false;
    291 
    292   printer_info->printer_name = WideToUTF8(info_2.get()->pPrinterName);
    293   if (info_2.get()->pComment)
    294     printer_info->printer_description = WideToUTF8(info_2.get()->pComment);
    295   if (info_2.get()->pLocation)
    296     printer_info->options[kLocationTagName] =
    297         WideToUTF8(info_2.get()->pLocation);
    298   if (info_2.get()->pDriverName)
    299     printer_info->options[kDriverNameTagName] =
    300         WideToUTF8(info_2.get()->pDriverName);
    301   printer_info->printer_status = info_2.get()->Status;
    302 
    303   std::string driver_info = GetDriverInfo(printer);
    304   if (!driver_info.empty())
    305     printer_info->options[kDriverInfoTagName] = driver_info;
    306   return true;
    307 }
    308 
    309 std::string GetDriverInfo(HANDLE printer) {
    310   DCHECK(printer);
    311   std::string driver_info;
    312 
    313   if (!printer)
    314     return driver_info;
    315 
    316   DriverInfo6 info_6;
    317   if (!info_6.Init(printer))
    318     return driver_info;
    319 
    320   std::string info[4];
    321   if (info_6.get()->pName)
    322     info[0] = WideToUTF8(info_6.get()->pName);
    323 
    324   if (info_6.get()->pDriverPath) {
    325     scoped_ptr<FileVersionInfo> version_info(
    326         FileVersionInfo::CreateFileVersionInfo(
    327             base::FilePath(info_6.get()->pDriverPath)));
    328     if (version_info.get()) {
    329       info[1] = WideToUTF8(version_info->file_version());
    330       info[2] = WideToUTF8(version_info->product_name());
    331       info[3] = WideToUTF8(version_info->product_version());
    332     }
    333   }
    334 
    335   for (size_t i = 0; i < arraysize(info); ++i) {
    336     std::replace(info[i].begin(), info[i].end(), ';', ',');
    337     driver_info.append(info[i]);
    338     if (i < arraysize(info) - 1)
    339       driver_info.append(";");
    340   }
    341   return driver_info;
    342 }
    343 
    344 }  // namespace printing
    345