2 * PROJECT: ReactOS Spooler API
3 * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation
4 * PURPOSE: Functions related to Printers and printing
5 * COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
11 _MarshallUpPrinterInfo(PBYTE pPrinterInfo
, DWORD Level
)
13 PPRINTER_INFO_1W pPrinterInfo1
;
14 PPRINTER_INFO_2W pPrinterInfo2
;
16 // Replace relative offset addresses in the output by absolute pointers.
19 pPrinterInfo1
= (PPRINTER_INFO_1W
)pPrinterInfo
;
21 pPrinterInfo1
->pName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo1
->pName
+ (ULONG_PTR
)pPrinterInfo1
);
22 pPrinterInfo1
->pDescription
= (PWSTR
)((ULONG_PTR
)pPrinterInfo1
->pDescription
+ (ULONG_PTR
)pPrinterInfo1
);
23 pPrinterInfo1
->pComment
= (PWSTR
)((ULONG_PTR
)pPrinterInfo1
->pComment
+ (ULONG_PTR
)pPrinterInfo1
);
27 pPrinterInfo2
= (PPRINTER_INFO_2W
)pPrinterInfo
;
29 pPrinterInfo2
->pPrinterName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pPrinterName
+ (ULONG_PTR
)pPrinterInfo2
);
30 pPrinterInfo2
->pShareName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pShareName
+ (ULONG_PTR
)pPrinterInfo2
);
31 pPrinterInfo2
->pPortName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pPortName
+ (ULONG_PTR
)pPrinterInfo2
);
32 pPrinterInfo2
->pDriverName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pDriverName
+ (ULONG_PTR
)pPrinterInfo2
);
33 pPrinterInfo2
->pComment
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pComment
+ (ULONG_PTR
)pPrinterInfo2
);
34 pPrinterInfo2
->pLocation
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pLocation
+ (ULONG_PTR
)pPrinterInfo2
);
35 pPrinterInfo2
->pDevMode
= (PDEVMODEW
)((ULONG_PTR
)pPrinterInfo2
->pDevMode
+ (ULONG_PTR
)pPrinterInfo2
);
36 pPrinterInfo2
->pSepFile
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pSepFile
+ (ULONG_PTR
)pPrinterInfo2
);
37 pPrinterInfo2
->pPrintProcessor
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pPrintProcessor
+ (ULONG_PTR
)pPrinterInfo2
);
38 pPrinterInfo2
->pDatatype
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pDatatype
+ (ULONG_PTR
)pPrinterInfo2
);
39 pPrinterInfo2
->pParameters
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pParameters
+ (ULONG_PTR
)pPrinterInfo2
);
41 if (pPrinterInfo2
->pServerName
)
42 pPrinterInfo2
->pServerName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pServerName
+ (ULONG_PTR
)pPrinterInfo2
);
44 if (pPrinterInfo2
->pSecurityDescriptor
)
45 pPrinterInfo2
->pSecurityDescriptor
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pSecurityDescriptor
+ (ULONG_PTR
)pPrinterInfo2
);
50 _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle
, PDOC_INFO_1W pDocInfo1
, PADDJOB_INFO_1W pAddJobInfo1
)
54 PJOB_INFO_1W pJobInfo1
= NULL
;
56 // Create the spool file.
57 pHandle
->hSPLFile
= CreateFileW(pAddJobInfo1
->Path
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, 0, NULL
);
58 if (pHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
60 dwErrorCode
= GetLastError();
61 ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1
->Path
, dwErrorCode
);
65 // Get the size of the job information.
66 GetJobW((HANDLE
)pHandle
, pAddJobInfo1
->JobId
, 1, NULL
, 0, &cbNeeded
);
67 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
69 dwErrorCode
= GetLastError();
70 ERR("GetJobW failed with error %lu!\n", dwErrorCode
);
74 // Allocate enough memory for the returned job information.
75 pJobInfo1
= HeapAlloc(hProcessHeap
, 0, cbNeeded
);
78 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
79 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
83 // Get the job information.
84 if (!GetJobW((HANDLE
)pHandle
, pAddJobInfo1
->JobId
, 1, (PBYTE
)pJobInfo1
, cbNeeded
, &cbNeeded
))
86 dwErrorCode
= GetLastError();
87 ERR("GetJobW failed with error %lu!\n", dwErrorCode
);
91 // Add our document information.
92 pJobInfo1
->pDatatype
= pDocInfo1
->pDatatype
;
93 pJobInfo1
->pDocument
= pDocInfo1
->pDocName
;
95 // Set the new job information.
96 if (!SetJobW((HANDLE
)pHandle
, pAddJobInfo1
->JobId
, 1, (PBYTE
)pJobInfo1
, 0))
98 dwErrorCode
= GetLastError();
99 ERR("SetJobW failed with error %lu!\n", dwErrorCode
);
103 // We were successful!
104 pHandle
->dwJobID
= pAddJobInfo1
->JobId
;
105 dwErrorCode
= ERROR_SUCCESS
;
109 HeapFree(hProcessHeap
, 0, pJobInfo1
);
115 _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle
, PDOC_INFO_1W pDocInfo1
)
118 WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer
;
120 DocInfoContainer
.Level
= 1;
121 DocInfoContainer
.DocInfo
.pDocInfo1
= (WINSPOOL_DOC_INFO_1
*)pDocInfo1
;
125 dwErrorCode
= _RpcStartDocPrinter(pHandle
->hPrinter
, &DocInfoContainer
, &pHandle
->dwJobID
);
127 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
129 dwErrorCode
= RpcExceptionCode();
130 ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode
);
138 ClosePrinter(HANDLE hPrinter
)
141 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
146 dwErrorCode
= ERROR_INVALID_HANDLE
;
153 dwErrorCode
= _RpcClosePrinter(pHandle
->hPrinter
);
155 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
157 dwErrorCode
= RpcExceptionCode();
158 ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode
);
162 // Close any open file handle.
163 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
164 CloseHandle(pHandle
->hSPLFile
);
166 // Free the memory for the handle.
167 HeapFree(hProcessHeap
, 0, pHandle
);
170 SetLastError(dwErrorCode
);
171 return (dwErrorCode
== ERROR_SUCCESS
);
176 DeviceCapabilitiesA(LPCSTR pDevice
, LPCSTR pPort
, WORD fwCapability
, LPSTR pOutput
, const DEVMODEA
* pDevMode
)
182 DeviceCapabilitiesW(LPCWSTR pDevice
, LPCWSTR pPort
, WORD fwCapability
, LPWSTR pOutput
, const DEVMODEW
* pDevMode
)
188 DocumentPropertiesA(HWND hWnd
, HANDLE hPrinter
, LPSTR pDeviceName
, PDEVMODEA pDevModeOutput
, PDEVMODEA pDevModeInput
, DWORD fMode
)
194 DocumentPropertiesW(HWND hWnd
, HANDLE hPrinter
, LPWSTR pDeviceName
, PDEVMODEW pDevModeOutput
, PDEVMODEW pDevModeInput
, DWORD fMode
)
200 EndDocPrinter(HANDLE hPrinter
)
203 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
208 dwErrorCode
= ERROR_INVALID_HANDLE
;
212 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
214 // For spooled jobs, the document is finished by calling _RpcScheduleJob.
217 dwErrorCode
= _RpcScheduleJob(pHandle
->hPrinter
, pHandle
->dwJobID
);
219 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
221 dwErrorCode
= RpcExceptionCode();
222 ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode
);
226 // Close the spool file handle.
227 CloseHandle(pHandle
->hSPLFile
);
231 // In all other cases, just call _RpcEndDocPrinter.
234 dwErrorCode
= _RpcEndDocPrinter(pHandle
->hPrinter
);
236 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
238 dwErrorCode
= RpcExceptionCode();
239 ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode
);
244 // A new document can now be started again.
245 pHandle
->bStartedDoc
= FALSE
;
248 SetLastError(dwErrorCode
);
249 return (dwErrorCode
== ERROR_SUCCESS
);
253 EndPagePrinter(HANDLE hPrinter
)
256 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
261 dwErrorCode
= ERROR_INVALID_HANDLE
;
265 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
267 // For spooled jobs, we don't need to do anything.
268 dwErrorCode
= ERROR_SUCCESS
;
272 // In all other cases, just call _RpcEndPagePrinter.
275 dwErrorCode
= _RpcEndPagePrinter(pHandle
->hPrinter
);
277 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
279 dwErrorCode
= RpcExceptionCode();
280 ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode
);
286 SetLastError(dwErrorCode
);
287 return (dwErrorCode
== ERROR_SUCCESS
);
291 EnumPrintersA(DWORD Flags
, PSTR Name
, DWORD Level
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
297 EnumPrintersW(DWORD Flags
, PWSTR Name
, DWORD Level
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
301 PBYTE p
= pPrinterEnum
;
306 dwErrorCode
= _RpcEnumPrinters(Flags
, Name
, Level
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
308 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
310 dwErrorCode
= RpcExceptionCode();
311 ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode
);
315 if (dwErrorCode
== ERROR_SUCCESS
)
317 // Replace relative offset addresses in the output by absolute pointers.
318 for (i
= 0; i
< *pcReturned
; i
++)
320 _MarshallUpPrinterInfo(p
, Level
);
323 p
+= sizeof(PRINTER_INFO_1W
);
325 p
+= sizeof(PRINTER_INFO_2W
);
329 SetLastError(dwErrorCode
);
330 return (dwErrorCode
== ERROR_SUCCESS
);
334 GetDefaultPrinterA(LPSTR pszBuffer
, LPDWORD pcchBuffer
)
340 GetDefaultPrinterW(LPWSTR pszBuffer
, LPDWORD pcchBuffer
)
346 GetPrinterA(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
352 GetPrinterDriverA(HANDLE hPrinter
, LPSTR pEnvironment
, DWORD Level
, LPBYTE pDriverInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
358 GetPrinterDriverW(HANDLE hPrinter
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pDriverInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
364 GetPrinterW(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
370 OpenPrinterA(LPSTR pPrinterName
, LPHANDLE phPrinter
, LPPRINTER_DEFAULTSA pDefault
)
372 BOOL bReturnValue
= FALSE
;
373 PWSTR pwszPrinterName
= NULL
;
374 PWSTR pwszDatatype
= NULL
;
375 PRINTER_DEFAULTSW wDefault
= { 0 };
380 // Convert pPrinterName to a Unicode string pwszPrinterName
381 StringLength
= strlen(pPrinterName
) + 1;
383 pwszPrinterName
= HeapAlloc(hProcessHeap
, 0, StringLength
* sizeof(WCHAR
));
384 if (!pwszPrinterName
)
386 ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
390 MultiByteToWideChar(CP_ACP
, 0, pPrinterName
, -1, pwszPrinterName
, StringLength
);
395 wDefault
.DesiredAccess
= pDefault
->DesiredAccess
;
397 if (pDefault
->pDatatype
)
399 // Convert pDefault->pDatatype to a Unicode string pwszDatatype that later becomes wDefault.pDatatype
400 StringLength
= strlen(pDefault
->pDatatype
) + 1;
402 pwszDatatype
= HeapAlloc(hProcessHeap
, 0, StringLength
* sizeof(WCHAR
));
405 ERR("HeapAlloc failed for pwszDatatype with last error %lu!\n", GetLastError());
409 MultiByteToWideChar(CP_ACP
, 0, pDefault
->pDatatype
, -1, pwszDatatype
, StringLength
);
410 wDefault
.pDatatype
= pwszDatatype
;
413 if (pDefault
->pDevMode
)
414 wDefault
.pDevMode
= GdiConvertToDevmodeW(pDefault
->pDevMode
);
417 bReturnValue
= OpenPrinterW(pwszPrinterName
, phPrinter
, &wDefault
);
420 if (wDefault
.pDevMode
)
421 HeapFree(hProcessHeap
, 0, wDefault
.pDevMode
);
424 HeapFree(hProcessHeap
, 0, pwszPrinterName
);
427 HeapFree(hProcessHeap
, 0, pwszDatatype
);
433 OpenPrinterW(LPWSTR pPrinterName
, LPHANDLE phPrinter
, LPPRINTER_DEFAULTSW pDefault
)
437 PSPOOLER_HANDLE pHandle
;
438 PWSTR pDatatype
= NULL
;
439 WINSPOOL_DEVMODE_CONTAINER DevModeContainer
= { 0 };
440 ACCESS_MASK AccessRequired
= 0;
442 // Prepare the additional parameters in the format required by _RpcOpenPrinter
445 pDatatype
= pDefault
->pDatatype
;
446 DevModeContainer
.cbBuf
= sizeof(DEVMODEW
);
447 DevModeContainer
.pDevMode
= (BYTE
*)pDefault
->pDevMode
;
448 AccessRequired
= pDefault
->DesiredAccess
;
454 dwErrorCode
= _RpcOpenPrinter(pPrinterName
, &hPrinter
, pDatatype
, &DevModeContainer
, AccessRequired
);
456 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
458 dwErrorCode
= RpcExceptionCode();
459 ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode
);
463 if (dwErrorCode
== ERROR_SUCCESS
)
465 // Create a new SPOOLER_HANDLE structure.
466 pHandle
= HeapAlloc(hProcessHeap
, HEAP_ZERO_MEMORY
, sizeof(SPOOLER_HANDLE
));
469 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
470 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
474 pHandle
->hPrinter
= hPrinter
;
475 pHandle
->hSPLFile
= INVALID_HANDLE_VALUE
;
477 // Return it as phPrinter.
478 *phPrinter
= (HANDLE
)pHandle
;
482 SetLastError(dwErrorCode
);
483 return (dwErrorCode
== ERROR_SUCCESS
);
487 ReadPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pNoBytesRead
)
490 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
495 dwErrorCode
= ERROR_INVALID_HANDLE
;
502 dwErrorCode
= _RpcReadPrinter(pHandle
->hPrinter
, pBuf
, cbBuf
, pNoBytesRead
);
504 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
506 dwErrorCode
= RpcExceptionCode();
507 ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode
);
512 SetLastError(dwErrorCode
);
513 return (dwErrorCode
== ERROR_SUCCESS
);
517 StartDocPrinterW(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
522 PADDJOB_INFO_1W pAddJobInfo1
= NULL
;
523 PDOC_INFO_1W pDocInfo1
= (PDOC_INFO_1W
)pDocInfo
;
524 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
529 dwErrorCode
= ERROR_INVALID_HANDLE
;
535 dwErrorCode
= ERROR_INVALID_PARAMETER
;
541 dwErrorCode
= ERROR_INVALID_LEVEL
;
545 if (pHandle
->bStartedDoc
)
547 dwErrorCode
= ERROR_INVALID_PRINTER_STATE
;
551 // Check if we want to redirect output into a file.
552 if (pDocInfo1
->pOutputFile
)
554 // Do a StartDocPrinter RPC call in this case.
555 dwErrorCode
= _StartDocPrinterWithRPC(pHandle
, pDocInfo1
);
559 // Allocate memory for the ADDJOB_INFO_1W structure and a path.
560 cbAddJobInfo1
= sizeof(ADDJOB_INFO_1W
) + MAX_PATH
* sizeof(WCHAR
);
561 pAddJobInfo1
= HeapAlloc(hProcessHeap
, 0, cbAddJobInfo1
);
564 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
565 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
569 // Try to add a new job.
570 // This only succeeds if the printer is set to do spooled printing.
571 if (AddJobW((HANDLE
)pHandle
, 1, (PBYTE
)pAddJobInfo1
, cbAddJobInfo1
, &cbNeeded
))
573 // Do spooled printing.
574 dwErrorCode
= _StartDocPrinterSpooled(pHandle
, pDocInfo1
, pAddJobInfo1
);
576 else if (GetLastError() == ERROR_INVALID_ACCESS
)
578 // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
579 // In this case, we do a StartDocPrinter RPC call.
580 dwErrorCode
= _StartDocPrinterWithRPC(pHandle
, pDocInfo1
);
584 dwErrorCode
= GetLastError();
585 ERR("AddJobW failed with error %lu!\n", dwErrorCode
);
590 if (dwErrorCode
== ERROR_SUCCESS
)
591 pHandle
->bStartedDoc
= TRUE
;
595 HeapFree(hProcessHeap
, 0, pAddJobInfo1
);
597 SetLastError(dwErrorCode
);
598 return pHandle
->dwJobID
;
602 StartPagePrinter(HANDLE hPrinter
)
605 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
610 dwErrorCode
= ERROR_INVALID_HANDLE
;
617 dwErrorCode
= _RpcStartPagePrinter(pHandle
->hPrinter
);
619 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
621 dwErrorCode
= RpcExceptionCode();
622 ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode
);
627 SetLastError(dwErrorCode
);
628 return (dwErrorCode
== ERROR_SUCCESS
);
632 WritePrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
)
635 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
640 dwErrorCode
= ERROR_INVALID_HANDLE
;
644 if (!pHandle
->bStartedDoc
)
646 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
650 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
652 // Write to the spool file. This doesn't need an RPC request.
653 if (!WriteFile(pHandle
->hSPLFile
, pBuf
, cbBuf
, pcWritten
, NULL
))
655 dwErrorCode
= GetLastError();
656 ERR("WriteFile failed with error %lu!\n", dwErrorCode
);
660 dwErrorCode
= ERROR_SUCCESS
;
664 // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
665 // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
670 dwErrorCode
= _RpcWritePrinter(pHandle
->hPrinter
, pBuf
, cbBuf
, pcWritten
);
672 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
674 dwErrorCode
= RpcExceptionCode();
675 ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode
);
681 SetLastError(dwErrorCode
);
682 return (dwErrorCode
== ERROR_SUCCESS
);
686 XcvDataW(HANDLE hXcv
, PCWSTR pszDataName
, PBYTE pInputData
, DWORD cbInputData
, PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
, PDWORD pdwStatus
)