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 18namespace { 19 20typedef HRESULT (WINAPI* PTOpenProviderProc)(PCWSTR printer_name, 21 DWORD version, 22 HPTPROVIDER* provider); 23 24typedef HRESULT (WINAPI* PTGetPrintCapabilitiesProc)(HPTPROVIDER provider, 25 IStream* print_ticket, 26 IStream* capabilities, 27 BSTR* error_message); 28 29typedef HRESULT (WINAPI* PTConvertDevModeToPrintTicketProc)( 30 HPTPROVIDER provider, 31 ULONG devmode_size_in_bytes, 32 PDEVMODE devmode, 33 EPrintTicketScope scope, 34 IStream* print_ticket); 35 36typedef 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 45typedef 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 53typedef HRESULT (WINAPI* PTReleaseMemoryProc)(PVOID buffer); 54 55typedef HRESULT (WINAPI* PTCloseProviderProc)(HPTPROVIDER provider); 56 57typedef 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 69PTOpenProviderProc g_open_provider_proc = NULL; 70PTGetPrintCapabilitiesProc g_get_print_capabilities_proc = NULL; 71PTConvertDevModeToPrintTicketProc g_convert_devmode_to_print_ticket_proc = NULL; 72PTConvertPrintTicketToDevModeProc g_convert_print_ticket_to_devmode_proc = NULL; 73PTMergeAndValidatePrintTicketProc g_merge_and_validate_print_ticket_proc = NULL; 74PTReleaseMemoryProc g_release_memory_proc = NULL; 75PTCloseProviderProc g_close_provider_proc = NULL; 76StartXpsPrintJobProc g_start_xps_print_job_proc = NULL; 77 78} // namespace 79 80 81namespace printing { 82 83bool XPSModule::Init() { 84 static bool initialized = InitImpl(); 85 return initialized; 86} 87 88bool 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 142HRESULT 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 148HRESULT 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 158HRESULT 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 170HRESULT 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 187HRESULT 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 201HRESULT XPSModule::ReleaseMemory(PVOID buffer) { 202 return g_release_memory_proc(buffer); 203} 204 205HRESULT XPSModule::CloseProvider(HPTPROVIDER provider) { 206 return g_close_provider_proc(provider); 207} 208 209ScopedXPSInitializer::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 235ScopedXPSInitializer::~ScopedXPSInitializer() { 236 if (initialized_) 237 CoUninitialize(); 238 initialized_ = false; 239} 240 241bool XPSPrintModule::Init() { 242 static bool initialized = InitImpl(); 243 return initialized; 244} 245 246bool 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 259HRESULT 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 282bool 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 309std::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