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 _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle
, PDOC_INFO_1W pDocInfo1
, PADDJOB_INFO_1W pAddJobInfo1
)
15 PJOB_INFO_1W pJobInfo1
= NULL
;
17 // Create the spool file.
18 pHandle
->hSPLFile
= CreateFileW(pAddJobInfo1
->Path
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, 0, NULL
);
19 if (pHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
21 dwErrorCode
= GetLastError();
22 ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1
->Path
, dwErrorCode
);
26 // Get the size of the job information.
27 GetJobW(pHandle
->hPrinter
, pAddJobInfo1
->JobId
, 1, NULL
, 0, &cbNeeded
);
28 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
30 dwErrorCode
= GetLastError();
31 ERR("GetJobW failed with error %lu!\n", dwErrorCode
);
35 // Allocate enough memory for the returned job information.
36 pJobInfo1
= HeapAlloc(hProcessHeap
, 0, cbNeeded
);
39 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
40 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
44 // Get the job information.
45 if (!GetJobW(pHandle
->hPrinter
, pAddJobInfo1
->JobId
, 1, (PBYTE
)pJobInfo1
, cbNeeded
, &cbNeeded
))
47 dwErrorCode
= GetLastError();
48 ERR("GetJobW failed with error %lu!\n", dwErrorCode
);
52 // Add our document information.
53 pJobInfo1
->pDatatype
= pDocInfo1
->pDatatype
;
54 pJobInfo1
->pDocument
= pDocInfo1
->pDocName
;
56 // Set the new job information.
57 if (!SetJobW(pHandle
->hPrinter
, pAddJobInfo1
->JobId
, 1, (PBYTE
)pJobInfo1
, 0))
59 dwErrorCode
= GetLastError();
60 ERR("SetJobW failed with error %lu!\n", dwErrorCode
);
64 // We were successful!
65 pHandle
->dwJobID
= pAddJobInfo1
->JobId
;
66 dwErrorCode
= ERROR_SUCCESS
;
70 HeapFree(hProcessHeap
, 0, pJobInfo1
);
76 _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle
, PDOC_INFO_1W pDocInfo1
)
79 WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer
;
81 DocInfoContainer
.Level
= 1;
82 DocInfoContainer
.DocInfo
.pDocInfo1
= (WINSPOOL_DOC_INFO_1
*)pDocInfo1
;
86 dwErrorCode
= _RpcStartDocPrinter(pHandle
->hPrinter
, &DocInfoContainer
, &pHandle
->dwJobID
);
88 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
90 dwErrorCode
= RpcExceptionCode();
91 ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode
);
99 EnumPrintersA(DWORD Flags
, LPSTR Name
, DWORD Level
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
105 EnumPrintersW(DWORD Flags
, LPWSTR Name
, DWORD Level
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
112 dwErrorCode
= _RpcEnumPrinters(Flags
, Name
, Level
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
114 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
116 dwErrorCode
= RpcExceptionCode();
117 ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode
);
121 SetLastError(dwErrorCode
);
122 return (dwErrorCode
== ERROR_SUCCESS
);
126 ClosePrinter(HANDLE hPrinter
)
129 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
134 dwErrorCode
= ERROR_INVALID_HANDLE
;
141 dwErrorCode
= _RpcClosePrinter(pHandle
->hPrinter
);
143 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
145 dwErrorCode
= RpcExceptionCode();
146 ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode
);
150 // Close any open file handle.
151 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
152 CloseHandle(pHandle
->hSPLFile
);
154 // Free the memory for the handle.
155 HeapFree(hProcessHeap
, 0, pHandle
);
158 SetLastError(dwErrorCode
);
159 return (dwErrorCode
== ERROR_SUCCESS
);
164 DeviceCapabilitiesA(LPCSTR pDevice
, LPCSTR pPort
, WORD fwCapability
, LPSTR pOutput
, const DEVMODEA
* pDevMode
)
170 DeviceCapabilitiesW(LPCWSTR pDevice
, LPCWSTR pPort
, WORD fwCapability
, LPWSTR pOutput
, const DEVMODEW
* pDevMode
)
176 DocumentPropertiesA(HWND hWnd
, HANDLE hPrinter
, LPSTR pDeviceName
, PDEVMODEA pDevModeOutput
, PDEVMODEA pDevModeInput
, DWORD fMode
)
182 DocumentPropertiesW(HWND hWnd
, HANDLE hPrinter
, LPWSTR pDeviceName
, PDEVMODEW pDevModeOutput
, PDEVMODEW pDevModeInput
, DWORD fMode
)
188 EndDocPrinter(HANDLE hPrinter
)
191 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
196 dwErrorCode
= ERROR_INVALID_HANDLE
;
200 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
202 // For spooled jobs, the document is finished by calling _RpcScheduleJob.
205 dwErrorCode
= _RpcScheduleJob(pHandle
->hPrinter
, pHandle
->dwJobID
);
207 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
209 dwErrorCode
= RpcExceptionCode();
210 ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode
);
214 // Close the spool file handle.
215 CloseHandle(pHandle
->hSPLFile
);
219 // In all other cases, just call _RpcEndDocPrinter.
222 dwErrorCode
= _RpcEndDocPrinter(pHandle
->hPrinter
);
224 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
226 dwErrorCode
= RpcExceptionCode();
227 ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode
);
232 // A new document can now be started again.
233 pHandle
->bStartedDoc
= FALSE
;
236 SetLastError(dwErrorCode
);
237 return (dwErrorCode
== ERROR_SUCCESS
);
241 EndPagePrinter(HANDLE hPrinter
)
244 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
249 dwErrorCode
= ERROR_INVALID_HANDLE
;
253 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
255 // For spooled jobs, we don't need to do anything.
256 dwErrorCode
= ERROR_SUCCESS
;
260 // In all other cases, just call _RpcEndPagePrinter.
263 dwErrorCode
= _RpcEndPagePrinter(pHandle
->hPrinter
);
265 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
267 dwErrorCode
= RpcExceptionCode();
268 ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode
);
274 SetLastError(dwErrorCode
);
275 return (dwErrorCode
== ERROR_SUCCESS
);
279 GetDefaultPrinterA(LPSTR pszBuffer
, LPDWORD pcchBuffer
)
285 GetDefaultPrinterW(LPWSTR pszBuffer
, LPDWORD pcchBuffer
)
291 GetPrinterA(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
297 GetPrinterDriverA(HANDLE hPrinter
, LPSTR pEnvironment
, DWORD Level
, LPBYTE pDriverInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
303 GetPrinterDriverW(HANDLE hPrinter
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pDriverInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
309 GetPrinterW(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
315 OpenPrinterA(LPSTR pPrinterName
, LPHANDLE phPrinter
, LPPRINTER_DEFAULTSA pDefault
)
317 BOOL bReturnValue
= FALSE
;
318 PWSTR pwszPrinterName
= NULL
;
319 PWSTR pwszDatatype
= NULL
;
320 PRINTER_DEFAULTSW wDefault
= { 0 };
325 // Convert pPrinterName to a Unicode string pwszPrinterName
326 StringLength
= strlen(pPrinterName
) + 1;
328 pwszPrinterName
= HeapAlloc(hProcessHeap
, 0, StringLength
* sizeof(WCHAR
));
329 if (!pwszPrinterName
)
331 ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
335 MultiByteToWideChar(CP_ACP
, 0, pPrinterName
, -1, pwszPrinterName
, StringLength
);
340 wDefault
.DesiredAccess
= pDefault
->DesiredAccess
;
342 if (pDefault
->pDatatype
)
344 // Convert pDefault->pDatatype to a Unicode string pwszDatatype that later becomes wDefault.pDatatype
345 StringLength
= strlen(pDefault
->pDatatype
) + 1;
347 pwszDatatype
= HeapAlloc(hProcessHeap
, 0, StringLength
* sizeof(WCHAR
));
350 ERR("HeapAlloc failed for pwszDatatype with last error %lu!\n", GetLastError());
354 MultiByteToWideChar(CP_ACP
, 0, pDefault
->pDatatype
, -1, pwszDatatype
, StringLength
);
355 wDefault
.pDatatype
= pwszDatatype
;
358 if (pDefault
->pDevMode
)
359 wDefault
.pDevMode
= GdiConvertToDevmodeW(pDefault
->pDevMode
);
362 bReturnValue
= OpenPrinterW(pwszPrinterName
, phPrinter
, &wDefault
);
365 if (wDefault
.pDevMode
)
366 HeapFree(hProcessHeap
, 0, wDefault
.pDevMode
);
369 HeapFree(hProcessHeap
, 0, pwszPrinterName
);
372 HeapFree(hProcessHeap
, 0, pwszDatatype
);
378 OpenPrinterW(LPWSTR pPrinterName
, LPHANDLE phPrinter
, LPPRINTER_DEFAULTSW pDefault
)
382 PSPOOLER_HANDLE pHandle
;
383 PWSTR pDatatype
= NULL
;
384 WINSPOOL_DEVMODE_CONTAINER DevModeContainer
;
385 WINSPOOL_DEVMODE_CONTAINER
* pDevModeContainer
= NULL
;
386 ACCESS_MASK AccessRequired
= 0;
388 // Prepare the additional parameters in the format required by _RpcOpenPrinter
391 pDatatype
= pDefault
->pDatatype
;
392 DevModeContainer
.cbBuf
= sizeof(DEVMODEW
);
393 DevModeContainer
.pDevMode
= (BYTE
*)pDefault
->pDevMode
;
394 pDevModeContainer
= &DevModeContainer
;
395 AccessRequired
= pDefault
->DesiredAccess
;
401 dwErrorCode
= _RpcOpenPrinter(pPrinterName
, &hPrinter
, pDatatype
, pDevModeContainer
, AccessRequired
);
403 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
405 dwErrorCode
= RpcExceptionCode();
406 ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode
);
410 if (dwErrorCode
== ERROR_SUCCESS
)
412 // Create a new SPOOLER_HANDLE structure.
413 pHandle
= HeapAlloc(hProcessHeap
, HEAP_ZERO_MEMORY
, sizeof(SPOOLER_HANDLE
));
416 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
417 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
421 pHandle
->hPrinter
= hPrinter
;
422 pHandle
->hSPLFile
= INVALID_HANDLE_VALUE
;
424 // Return it as phPrinter.
425 *phPrinter
= (HANDLE
)pHandle
;
429 SetLastError(dwErrorCode
);
430 return (dwErrorCode
== ERROR_SUCCESS
);
434 ReadPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pNoBytesRead
)
437 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
442 dwErrorCode
= ERROR_INVALID_HANDLE
;
449 dwErrorCode
= _RpcReadPrinter(pHandle
->hPrinter
, pBuf
, cbBuf
, pNoBytesRead
);
451 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
453 dwErrorCode
= RpcExceptionCode();
454 ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode
);
459 SetLastError(dwErrorCode
);
460 return (dwErrorCode
== ERROR_SUCCESS
);
464 StartDocPrinterW(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
469 PADDJOB_INFO_1W pAddJobInfo1
= NULL
;
470 PDOC_INFO_1W pDocInfo1
= (PDOC_INFO_1W
)pDocInfo
;
471 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
476 dwErrorCode
= ERROR_INVALID_HANDLE
;
482 dwErrorCode
= ERROR_INVALID_PARAMETER
;
488 dwErrorCode
= ERROR_INVALID_LEVEL
;
492 if (pHandle
->bStartedDoc
)
494 dwErrorCode
= ERROR_INVALID_PRINTER_STATE
;
498 // Check if we want to redirect output into a file.
499 if (pDocInfo1
->pOutputFile
)
501 // Do a StartDocPrinter RPC call in this case.
502 dwErrorCode
= _StartDocPrinterWithRPC(pHandle
, pDocInfo1
);
506 // Allocate memory for the ADDJOB_INFO_1W structure and a path.
507 cbAddJobInfo1
= sizeof(ADDJOB_INFO_1W
) + MAX_PATH
* sizeof(WCHAR
);
508 pAddJobInfo1
= HeapAlloc(hProcessHeap
, 0, cbAddJobInfo1
);
511 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
512 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
516 // Try to add a new job.
517 // This only succeeds if the printer is set to do spooled printing.
518 if (AddJobW(pHandle
->hPrinter
, 1, (PBYTE
)pAddJobInfo1
, cbAddJobInfo1
, &cbNeeded
))
520 // Do spooled printing.
521 dwErrorCode
= _StartDocPrinterSpooled(pHandle
, pDocInfo1
, pAddJobInfo1
);
523 else if (GetLastError() == ERROR_INVALID_ACCESS
)
525 // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
526 // In this case, we do a StartDocPrinter RPC call.
527 dwErrorCode
= _StartDocPrinterWithRPC(pHandle
, pDocInfo1
);
531 dwErrorCode
= GetLastError();
532 ERR("AddJobW failed with error %lu!\n", dwErrorCode
);
537 if (dwErrorCode
== ERROR_SUCCESS
)
538 pHandle
->bStartedDoc
= TRUE
;
542 HeapFree(hProcessHeap
, 0, pAddJobInfo1
);
544 SetLastError(dwErrorCode
);
545 return pHandle
->dwJobID
;
549 StartPagePrinter(HANDLE hPrinter
)
552 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
557 dwErrorCode
= ERROR_INVALID_HANDLE
;
564 dwErrorCode
= _RpcStartPagePrinter(pHandle
->hPrinter
);
566 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
568 dwErrorCode
= RpcExceptionCode();
569 ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode
);
574 SetLastError(dwErrorCode
);
575 return (dwErrorCode
== ERROR_SUCCESS
);
579 WritePrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
)
582 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
587 dwErrorCode
= ERROR_INVALID_HANDLE
;
591 if (!pHandle
->bStartedDoc
)
593 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
597 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
599 // Write to the spool file. This doesn't need an RPC request.
600 if (!WriteFile(pHandle
->hSPLFile
, pBuf
, cbBuf
, pcWritten
, NULL
))
602 dwErrorCode
= GetLastError();
603 ERR("WriteFile failed with error %lu!\n", dwErrorCode
);
607 dwErrorCode
= ERROR_SUCCESS
;
611 // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
612 // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
617 dwErrorCode
= _RpcWritePrinter(pHandle
->hPrinter
, pBuf
, cbBuf
, pcWritten
);
619 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
621 dwErrorCode
= RpcExceptionCode();
622 ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode
);
628 SetLastError(dwErrorCode
);
629 return (dwErrorCode
== ERROR_SUCCESS
);
633 XcvDataW(HANDLE hXcv
, PCWSTR pszDataName
, PBYTE pInputData
, DWORD cbInputData
, PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
, PDWORD pdwStatus
)