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.
5 #include "printing/backend/win_helper.h"
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"
20 typedef HRESULT (WINAPI
* PTOpenProviderProc
)(PCWSTR printer_name
,
22 HPTPROVIDER
* provider
);
24 typedef HRESULT (WINAPI
* PTGetPrintCapabilitiesProc
)(HPTPROVIDER provider
,
25 IStream
* print_ticket
,
26 IStream
* capabilities
,
29 typedef HRESULT (WINAPI
* PTConvertDevModeToPrintTicketProc
)(
31 ULONG devmode_size_in_bytes
,
33 EPrintTicketScope scope
,
34 IStream
* print_ticket
);
36 typedef HRESULT (WINAPI
* PTConvertPrintTicketToDevModeProc
)(
38 IStream
* print_ticket
,
39 EDefaultDevmodeType base_devmode_type
,
40 EPrintTicketScope scope
,
41 ULONG
* devmode_byte_count
,
45 typedef HRESULT (WINAPI
* PTMergeAndValidatePrintTicketProc
)(
48 IStream
* delta_ticket
,
49 EPrintTicketScope scope
,
50 IStream
* result_ticket
,
53 typedef HRESULT (WINAPI
* PTReleaseMemoryProc
)(PVOID buffer
);
55 typedef HRESULT (WINAPI
* PTCloseProviderProc
)(HPTPROVIDER provider
);
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
);
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
;
83 bool XPSModule::Init() {
84 static bool initialized
= InitImpl();
88 bool XPSModule::InitImpl() {
89 HMODULE prntvpt_module
= LoadLibrary(L
"prntvpt.dll");
90 if (prntvpt_module
== NULL
)
92 g_open_provider_proc
= reinterpret_cast<PTOpenProviderProc
>(
93 GetProcAddress(prntvpt_module
, "PTOpenProvider"));
94 if (!g_open_provider_proc
) {
98 g_get_print_capabilities_proc
= reinterpret_cast<PTGetPrintCapabilitiesProc
>(
99 GetProcAddress(prntvpt_module
, "PTGetPrintCapabilities"));
100 if (!g_get_print_capabilities_proc
) {
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
) {
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
) {
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
) {
125 g_release_memory_proc
=
126 reinterpret_cast<PTReleaseMemoryProc
>(
127 GetProcAddress(prntvpt_module
, "PTReleaseMemory"));
128 if (!g_release_memory_proc
) {
132 g_close_provider_proc
=
133 reinterpret_cast<PTCloseProviderProc
>(
134 GetProcAddress(prntvpt_module
, "PTCloseProvider"));
135 if (!g_close_provider_proc
) {
142 HRESULT
XPSModule::OpenProvider(const string16
& printer_name
,
144 HPTPROVIDER
* provider
) {
145 return g_open_provider_proc(printer_name
.c_str(), version
, provider
);
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
,
158 HRESULT
XPSModule::ConvertDevModeToPrintTicket(HPTPROVIDER provider
,
159 ULONG devmode_size_in_bytes
,
161 EPrintTicketScope scope
,
162 IStream
* print_ticket
) {
163 return g_convert_devmode_to_print_ticket_proc(provider
,
164 devmode_size_in_bytes
,
170 HRESULT
XPSModule::ConvertPrintTicketToDevMode(
171 HPTPROVIDER provider
,
172 IStream
* print_ticket
,
173 EDefaultDevmodeType base_devmode_type
,
174 EPrintTicketScope scope
,
175 ULONG
* devmode_byte_count
,
177 BSTR
* error_message
) {
178 return g_convert_print_ticket_to_devmode_proc(provider
,
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
,
201 HRESULT
XPSModule::ReleaseMemory(PVOID buffer
) {
202 return g_release_memory_proc(buffer
);
205 HRESULT
XPSModule::CloseProvider(HPTPROVIDER provider
) {
206 return g_close_provider_proc(provider
);
209 ScopedXPSInitializer::ScopedXPSInitializer() : initialized_(false) {
210 if (!XPSModule::Init())
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
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).
230 hr
= CoInitializeEx(NULL
, COINIT_APARTMENTTHREADED
);
231 DCHECK(SUCCEEDED(hr
));
235 ScopedXPSInitializer::~ScopedXPSInitializer() {
238 initialized_
= false;
241 bool XPSPrintModule::Init() {
242 static bool initialized
= InitImpl();
246 bool XPSPrintModule::InitImpl() {
247 HMODULE xpsprint_module
= LoadLibrary(L
"xpsprint.dll");
248 if (xpsprint_module
== NULL
)
250 g_start_xps_print_job_proc
= reinterpret_cast<StartXpsPrintJobProc
>(
251 GetProcAddress(xpsprint_module
, "StartXpsPrintJob"));
252 if (!g_start_xps_print_job_proc
) {
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
,
276 printable_pages_on_count
,
279 print_ticket_stream
);
282 bool InitBasicPrinterInfo(HANDLE printer
, PrinterBasicInfo
* printer_info
) {
284 DCHECK(printer_info
);
289 if (!info_2
.Init(printer
))
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
;
303 std::string driver_info
= GetDriverInfo(printer
);
304 if (!driver_info
.empty())
305 printer_info
->options
[kDriverInfoTagName
] = driver_info
;
309 std::string
GetDriverInfo(HANDLE printer
) {
311 std::string driver_info
;
317 if (!info_6
.Init(printer
))
321 if (info_6
.get()->pName
)
322 info
[0] = WideToUTF8(info_6
.get()->pName
);
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());
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(";");
344 } // namespace printing