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