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-2017 Colin Finck <colin@reactos.org>
12 /** And the award for the most confusingly named setting goes to "Device", for storing the default printer of the current user.
13 Ok, I admit that this has historical reasons. It's still not straightforward in any way though! */
14 static const WCHAR wszWindowsKey
[] = L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
15 static const WCHAR wszDeviceValue
[] = L
"Device";
18 _MarshallUpPrinterInfo(PBYTE
* ppPrinterInfo
, DWORD Level
)
20 // Replace relative offset addresses in the output by absolute pointers and advance to the next structure.
23 PPRINTER_INFO_STRESS pPrinterInfo0
= (PPRINTER_INFO_STRESS
)(*ppPrinterInfo
);
25 pPrinterInfo0
->pPrinterName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo0
->pPrinterName
+ (ULONG_PTR
)pPrinterInfo0
);
27 if (pPrinterInfo0
->pServerName
)
28 pPrinterInfo0
->pServerName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo0
->pServerName
+ (ULONG_PTR
)pPrinterInfo0
);
30 *ppPrinterInfo
+= sizeof(PRINTER_INFO_STRESS
);
34 PPRINTER_INFO_1W pPrinterInfo1
= (PPRINTER_INFO_1W
)(*ppPrinterInfo
);
36 pPrinterInfo1
->pName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo1
->pName
+ (ULONG_PTR
)pPrinterInfo1
);
37 pPrinterInfo1
->pDescription
= (PWSTR
)((ULONG_PTR
)pPrinterInfo1
->pDescription
+ (ULONG_PTR
)pPrinterInfo1
);
38 pPrinterInfo1
->pComment
= (PWSTR
)((ULONG_PTR
)pPrinterInfo1
->pComment
+ (ULONG_PTR
)pPrinterInfo1
);
40 *ppPrinterInfo
+= sizeof(PRINTER_INFO_1W
);
44 PPRINTER_INFO_2W pPrinterInfo2
= (PPRINTER_INFO_2W
)(*ppPrinterInfo
);
46 pPrinterInfo2
->pPrinterName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pPrinterName
+ (ULONG_PTR
)pPrinterInfo2
);
47 pPrinterInfo2
->pShareName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pShareName
+ (ULONG_PTR
)pPrinterInfo2
);
48 pPrinterInfo2
->pPortName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pPortName
+ (ULONG_PTR
)pPrinterInfo2
);
49 pPrinterInfo2
->pDriverName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pDriverName
+ (ULONG_PTR
)pPrinterInfo2
);
50 pPrinterInfo2
->pComment
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pComment
+ (ULONG_PTR
)pPrinterInfo2
);
51 pPrinterInfo2
->pLocation
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pLocation
+ (ULONG_PTR
)pPrinterInfo2
);
52 pPrinterInfo2
->pDevMode
= (PDEVMODEW
)((ULONG_PTR
)pPrinterInfo2
->pDevMode
+ (ULONG_PTR
)pPrinterInfo2
);
53 pPrinterInfo2
->pSepFile
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pSepFile
+ (ULONG_PTR
)pPrinterInfo2
);
54 pPrinterInfo2
->pPrintProcessor
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pPrintProcessor
+ (ULONG_PTR
)pPrinterInfo2
);
55 pPrinterInfo2
->pDatatype
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pDatatype
+ (ULONG_PTR
)pPrinterInfo2
);
56 pPrinterInfo2
->pParameters
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pParameters
+ (ULONG_PTR
)pPrinterInfo2
);
58 if (pPrinterInfo2
->pServerName
)
59 pPrinterInfo2
->pServerName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo2
->pServerName
+ (ULONG_PTR
)pPrinterInfo2
);
61 if (pPrinterInfo2
->pSecurityDescriptor
)
62 pPrinterInfo2
->pSecurityDescriptor
= (PSECURITY_DESCRIPTOR
)((ULONG_PTR
)pPrinterInfo2
->pSecurityDescriptor
+ (ULONG_PTR
)pPrinterInfo2
);
64 *ppPrinterInfo
+= sizeof(PRINTER_INFO_2W
);
68 PPRINTER_INFO_3 pPrinterInfo3
= (PPRINTER_INFO_3
)(*ppPrinterInfo
);
70 pPrinterInfo3
->pSecurityDescriptor
= (PSECURITY_DESCRIPTOR
)((ULONG_PTR
)pPrinterInfo3
->pSecurityDescriptor
+ (ULONG_PTR
)pPrinterInfo3
);
72 *ppPrinterInfo
+= sizeof(PRINTER_INFO_3
);
76 PPRINTER_INFO_4W pPrinterInfo4
= (PPRINTER_INFO_4W
)(*ppPrinterInfo
);
78 pPrinterInfo4
->pPrinterName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo4
->pPrinterName
+ (ULONG_PTR
)pPrinterInfo4
);
80 if (pPrinterInfo4
->pServerName
)
81 pPrinterInfo4
->pServerName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo4
->pServerName
+ (ULONG_PTR
)pPrinterInfo4
);
83 *ppPrinterInfo
+= sizeof(PRINTER_INFO_4W
);
87 PPRINTER_INFO_5W pPrinterInfo5
= (PPRINTER_INFO_5W
)(*ppPrinterInfo
);
89 pPrinterInfo5
->pPrinterName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo5
->pPrinterName
+ (ULONG_PTR
)pPrinterInfo5
);
90 pPrinterInfo5
->pPortName
= (PWSTR
)((ULONG_PTR
)pPrinterInfo5
->pPortName
+ (ULONG_PTR
)pPrinterInfo5
);
92 *ppPrinterInfo
+= sizeof(PRINTER_INFO_5W
);
96 *ppPrinterInfo
+= sizeof(PRINTER_INFO_6
);
100 PPRINTER_INFO_7W pPrinterInfo7
= (PPRINTER_INFO_7W
)(*ppPrinterInfo
);
102 if (pPrinterInfo7
->pszObjectGUID
)
103 pPrinterInfo7
->pszObjectGUID
= (PWSTR
)((ULONG_PTR
)pPrinterInfo7
->pszObjectGUID
+ (ULONG_PTR
)pPrinterInfo7
);
105 *ppPrinterInfo
+= sizeof(PRINTER_INFO_7W
);
109 PPRINTER_INFO_8W pPrinterInfo8
= (PPRINTER_INFO_8W
)(*ppPrinterInfo
);
111 pPrinterInfo8
->pDevMode
= (PDEVMODEW
)((ULONG_PTR
)pPrinterInfo8
->pDevMode
+ (ULONG_PTR
)pPrinterInfo8
);
113 *ppPrinterInfo
+= sizeof(PRINTER_INFO_8W
);
117 PPRINTER_INFO_9W pPrinterInfo9
= (PPRINTER_INFO_9W
)(*ppPrinterInfo
);
119 pPrinterInfo9
->pDevMode
= (PDEVMODEW
)((ULONG_PTR
)pPrinterInfo9
->pDevMode
+ (ULONG_PTR
)pPrinterInfo9
);
121 *ppPrinterInfo
+= sizeof(PRINTER_INFO_9W
);
126 _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle
, PDOC_INFO_1W pDocInfo1
, PADDJOB_INFO_1W pAddJobInfo1
)
130 PJOB_INFO_1W pJobInfo1
= NULL
;
132 // Create the spool file.
133 pHandle
->hSPLFile
= CreateFileW(pAddJobInfo1
->Path
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, 0, NULL
);
134 if (pHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
136 dwErrorCode
= GetLastError();
137 ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1
->Path
, dwErrorCode
);
141 // Get the size of the job information.
142 GetJobW((HANDLE
)pHandle
, pAddJobInfo1
->JobId
, 1, NULL
, 0, &cbNeeded
);
143 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
145 dwErrorCode
= GetLastError();
146 ERR("GetJobW failed with error %lu!\n", dwErrorCode
);
150 // Allocate enough memory for the returned job information.
151 pJobInfo1
= HeapAlloc(hProcessHeap
, 0, cbNeeded
);
154 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
155 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
159 // Get the job information.
160 if (!GetJobW((HANDLE
)pHandle
, pAddJobInfo1
->JobId
, 1, (PBYTE
)pJobInfo1
, cbNeeded
, &cbNeeded
))
162 dwErrorCode
= GetLastError();
163 ERR("GetJobW failed with error %lu!\n", dwErrorCode
);
167 // Add our document information.
168 if (pDocInfo1
->pDatatype
)
169 pJobInfo1
->pDatatype
= pDocInfo1
->pDatatype
;
171 pJobInfo1
->pDocument
= pDocInfo1
->pDocName
;
173 // Set the new job information.
174 if (!SetJobW((HANDLE
)pHandle
, pAddJobInfo1
->JobId
, 1, (PBYTE
)pJobInfo1
, 0))
176 dwErrorCode
= GetLastError();
177 ERR("SetJobW failed with error %lu!\n", dwErrorCode
);
181 // We were successful!
182 pHandle
->dwJobID
= pAddJobInfo1
->JobId
;
183 dwErrorCode
= ERROR_SUCCESS
;
187 HeapFree(hProcessHeap
, 0, pJobInfo1
);
193 _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle
, PDOC_INFO_1W pDocInfo1
)
196 WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer
;
198 DocInfoContainer
.Level
= 1;
199 DocInfoContainer
.DocInfo
.pDocInfo1
= (WINSPOOL_DOC_INFO_1
*)pDocInfo1
;
203 dwErrorCode
= _RpcStartDocPrinter(pHandle
->hPrinter
, &DocInfoContainer
, &pHandle
->dwJobID
);
205 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
207 dwErrorCode
= RpcExceptionCode();
208 ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode
);
216 AddPrinterW(PWSTR pName
, DWORD Level
, PBYTE pPrinter
)
223 ClosePrinter(HANDLE hPrinter
)
226 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
231 dwErrorCode
= ERROR_INVALID_HANDLE
;
238 dwErrorCode
= _RpcClosePrinter(pHandle
->hPrinter
);
240 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
242 dwErrorCode
= RpcExceptionCode();
243 ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode
);
247 // Close any open file handle.
248 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
249 CloseHandle(pHandle
->hSPLFile
);
251 // Free the memory for the handle.
252 HeapFree(hProcessHeap
, 0, pHandle
);
255 SetLastError(dwErrorCode
);
256 return (dwErrorCode
== ERROR_SUCCESS
);
261 DeviceCapabilitiesA(LPCSTR pDevice
, LPCSTR pPort
, WORD fwCapability
, LPSTR pOutput
, const DEVMODEA
* pDevMode
)
267 DeviceCapabilitiesW(LPCWSTR pDevice
, LPCWSTR pPort
, WORD fwCapability
, LPWSTR pOutput
, const DEVMODEW
* pDevMode
)
273 DocumentPropertiesA(HWND hWnd
, HANDLE hPrinter
, LPSTR pDeviceName
, PDEVMODEA pDevModeOutput
, PDEVMODEA pDevModeInput
, DWORD fMode
)
279 DocumentPropertiesW(HWND hWnd
, HANDLE hPrinter
, LPWSTR pDeviceName
, PDEVMODEW pDevModeOutput
, PDEVMODEW pDevModeInput
, DWORD fMode
)
285 EndDocPrinter(HANDLE hPrinter
)
288 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
293 dwErrorCode
= ERROR_INVALID_HANDLE
;
297 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
299 // For spooled jobs, the document is finished by calling _RpcScheduleJob.
302 dwErrorCode
= _RpcScheduleJob(pHandle
->hPrinter
, pHandle
->dwJobID
);
304 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
306 dwErrorCode
= RpcExceptionCode();
307 ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode
);
311 // Close the spool file handle.
312 CloseHandle(pHandle
->hSPLFile
);
316 // In all other cases, just call _RpcEndDocPrinter.
319 dwErrorCode
= _RpcEndDocPrinter(pHandle
->hPrinter
);
321 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
323 dwErrorCode
= RpcExceptionCode();
324 ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode
);
329 // A new document can now be started again.
330 pHandle
->bStartedDoc
= FALSE
;
333 SetLastError(dwErrorCode
);
334 return (dwErrorCode
== ERROR_SUCCESS
);
338 EndPagePrinter(HANDLE hPrinter
)
341 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
346 dwErrorCode
= ERROR_INVALID_HANDLE
;
350 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
352 // For spooled jobs, we don't need to do anything.
353 dwErrorCode
= ERROR_SUCCESS
;
357 // In all other cases, just call _RpcEndPagePrinter.
360 dwErrorCode
= _RpcEndPagePrinter(pHandle
->hPrinter
);
362 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
364 dwErrorCode
= RpcExceptionCode();
365 ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode
);
371 SetLastError(dwErrorCode
);
372 return (dwErrorCode
== ERROR_SUCCESS
);
376 EnumPrintersA(DWORD Flags
, PSTR Name
, DWORD Level
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
382 EnumPrintersW(DWORD Flags
, PWSTR Name
, DWORD Level
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
386 // Dismiss invalid levels already at this point.
387 if (Level
== 3 || Level
> 5)
389 dwErrorCode
= ERROR_INVALID_LEVEL
;
393 if (cbBuf
&& pPrinterEnum
)
394 ZeroMemory(pPrinterEnum
, cbBuf
);
399 dwErrorCode
= _RpcEnumPrinters(Flags
, Name
, Level
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
401 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
403 dwErrorCode
= RpcExceptionCode();
404 ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode
);
408 if (dwErrorCode
== ERROR_SUCCESS
)
411 PBYTE p
= pPrinterEnum
;
413 for (i
= 0; i
< *pcReturned
; i
++)
414 _MarshallUpPrinterInfo(&p
, Level
);
418 SetLastError(dwErrorCode
);
419 return (dwErrorCode
== ERROR_SUCCESS
);
423 GetDefaultPrinterA(LPSTR pszBuffer
, LPDWORD pcchBuffer
)
426 PWSTR pwszBuffer
= NULL
;
431 dwErrorCode
= ERROR_INVALID_PARAMETER
;
435 // Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
436 if (pszBuffer
&& *pcchBuffer
)
438 pwszBuffer
= HeapAlloc(hProcessHeap
, 0, *pcchBuffer
* sizeof(WCHAR
));
441 dwErrorCode
= GetLastError();
442 ERR("HeapAlloc failed with error %lu!\n", dwErrorCode
);
447 if (!GetDefaultPrinterW(pwszBuffer
, pcchBuffer
))
449 dwErrorCode
= GetLastError();
453 dwErrorCode
= ERROR_SUCCESS
;
457 HeapFree(hProcessHeap
, 0, pwszBuffer
);
459 SetLastError(dwErrorCode
);
460 return (dwErrorCode
== ERROR_SUCCESS
);
464 GetDefaultPrinterW(LPWSTR pszBuffer
, LPDWORD pcchBuffer
)
467 DWORD cchInputBuffer
;
469 HKEY hWindowsKey
= NULL
;
470 PWSTR pwszDevice
= NULL
;
476 dwErrorCode
= ERROR_INVALID_PARAMETER
;
480 cchInputBuffer
= *pcchBuffer
;
482 // Open the registry key where the default printer for the current user is stored.
483 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_CURRENT_USER
, wszWindowsKey
, 0, KEY_READ
, &hWindowsKey
);
484 if (dwErrorCode
!= ERROR_SUCCESS
)
486 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
490 // Determine the size of the required buffer.
491 dwErrorCode
= (DWORD
)RegQueryValueExW(hWindowsKey
, wszDeviceValue
, NULL
, NULL
, NULL
, &cbNeeded
);
492 if (dwErrorCode
!= ERROR_SUCCESS
)
494 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
499 pwszDevice
= HeapAlloc(hProcessHeap
, 0, cbNeeded
);
502 dwErrorCode
= GetLastError();
503 ERR("HeapAlloc failed with error %lu!\n", dwErrorCode
);
507 // Now get the actual value.
508 dwErrorCode
= RegQueryValueExW(hWindowsKey
, wszDeviceValue
, NULL
, NULL
, (PBYTE
)pwszDevice
, &cbNeeded
);
509 if (dwErrorCode
!= ERROR_SUCCESS
)
511 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
515 // We get a string "<Printer Name>,winspool,<Port>:".
516 // Extract the printer name from it.
517 pwszComma
= wcschr(pwszDevice
, L
',');
520 ERR("Found no or invalid default printer: %S!\n", pwszDevice
);
521 dwErrorCode
= ERROR_INVALID_NAME
;
525 // Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
526 *pcchBuffer
= pwszComma
- pwszDevice
+ 1;
528 // Check if the supplied buffer is large enough.
529 if (cchInputBuffer
< *pcchBuffer
)
531 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
535 // Copy the default printer.
537 CopyMemory(pszBuffer
, pwszDevice
, *pcchBuffer
* sizeof(WCHAR
));
539 dwErrorCode
= ERROR_SUCCESS
;
543 RegCloseKey(hWindowsKey
);
546 HeapFree(hProcessHeap
, 0, pwszDevice
);
548 SetLastError(dwErrorCode
);
549 return (dwErrorCode
== ERROR_SUCCESS
);
553 GetPrinterA(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
559 GetPrinterDriverA(HANDLE hPrinter
, LPSTR pEnvironment
, DWORD Level
, LPBYTE pDriverInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
565 GetPrinterDriverW(HANDLE hPrinter
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pDriverInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
571 GetPrinterW(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
575 // Dismiss invalid levels already at this point.
578 dwErrorCode
= ERROR_INVALID_LEVEL
;
582 if (cbBuf
&& pPrinter
)
583 ZeroMemory(pPrinter
, cbBuf
);
588 dwErrorCode
= _RpcGetPrinter(hPrinter
, Level
, pPrinter
, cbBuf
, pcbNeeded
);
590 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
592 dwErrorCode
= RpcExceptionCode();
593 ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode
);
597 if (dwErrorCode
== ERROR_SUCCESS
)
600 _MarshallUpPrinterInfo(&p
, Level
);
604 SetLastError(dwErrorCode
);
605 return (dwErrorCode
== ERROR_SUCCESS
);
609 OpenPrinterA(LPSTR pPrinterName
, LPHANDLE phPrinter
, LPPRINTER_DEFAULTSA pDefault
)
611 BOOL bReturnValue
= FALSE
;
613 PWSTR pwszPrinterName
= NULL
;
614 PRINTER_DEFAULTSW wDefault
= { 0 };
618 // Convert pPrinterName to a Unicode string pwszPrinterName
619 cch
= strlen(pPrinterName
);
621 pwszPrinterName
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
622 if (!pwszPrinterName
)
624 ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
628 MultiByteToWideChar(CP_ACP
, 0, pPrinterName
, -1, pwszPrinterName
, cch
+ 1);
633 wDefault
.DesiredAccess
= pDefault
->DesiredAccess
;
635 if (pDefault
->pDatatype
)
637 // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
638 cch
= strlen(pDefault
->pDatatype
);
640 wDefault
.pDatatype
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
641 if (!wDefault
.pDatatype
)
643 ERR("HeapAlloc failed for wDefault.pDatatype with last error %lu!\n", GetLastError());
647 MultiByteToWideChar(CP_ACP
, 0, pDefault
->pDatatype
, -1, wDefault
.pDatatype
, cch
+ 1);
650 if (pDefault
->pDevMode
)
651 wDefault
.pDevMode
= GdiConvertToDevmodeW(pDefault
->pDevMode
);
654 bReturnValue
= OpenPrinterW(pwszPrinterName
, phPrinter
, &wDefault
);
657 if (wDefault
.pDatatype
)
658 HeapFree(hProcessHeap
, 0, wDefault
.pDatatype
);
660 if (wDefault
.pDevMode
)
661 HeapFree(hProcessHeap
, 0, wDefault
.pDevMode
);
664 HeapFree(hProcessHeap
, 0, pwszPrinterName
);
670 OpenPrinterW(LPWSTR pPrinterName
, LPHANDLE phPrinter
, LPPRINTER_DEFAULTSW pDefault
)
674 PSPOOLER_HANDLE pHandle
;
675 PWSTR pDatatype
= NULL
;
676 WINSPOOL_DEVMODE_CONTAINER DevModeContainer
= { 0 };
677 ACCESS_MASK AccessRequired
= 0;
679 // Prepare the additional parameters in the format required by _RpcOpenPrinter
682 pDatatype
= pDefault
->pDatatype
;
683 DevModeContainer
.cbBuf
= sizeof(DEVMODEW
);
684 DevModeContainer
.pDevMode
= (BYTE
*)pDefault
->pDevMode
;
685 AccessRequired
= pDefault
->DesiredAccess
;
691 dwErrorCode
= _RpcOpenPrinter(pPrinterName
, &hPrinter
, pDatatype
, &DevModeContainer
, AccessRequired
);
693 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
695 dwErrorCode
= RpcExceptionCode();
696 ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode
);
700 if (dwErrorCode
== ERROR_SUCCESS
)
702 // Create a new SPOOLER_HANDLE structure.
703 pHandle
= HeapAlloc(hProcessHeap
, HEAP_ZERO_MEMORY
, sizeof(SPOOLER_HANDLE
));
706 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
707 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
711 pHandle
->hPrinter
= hPrinter
;
712 pHandle
->hSPLFile
= INVALID_HANDLE_VALUE
;
714 // Return it as phPrinter.
715 *phPrinter
= (HANDLE
)pHandle
;
719 SetLastError(dwErrorCode
);
720 return (dwErrorCode
== ERROR_SUCCESS
);
724 ReadPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pNoBytesRead
)
727 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
732 dwErrorCode
= ERROR_INVALID_HANDLE
;
739 dwErrorCode
= _RpcReadPrinter(pHandle
->hPrinter
, pBuf
, cbBuf
, pNoBytesRead
);
741 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
743 dwErrorCode
= RpcExceptionCode();
744 ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode
);
749 SetLastError(dwErrorCode
);
750 return (dwErrorCode
== ERROR_SUCCESS
);
754 ResetPrinterW(HANDLE hPrinter
, PPRINTER_DEFAULTSW pDefault
)
761 SetDefaultPrinterA(LPCSTR pszPrinter
)
763 BOOL bReturnValue
= FALSE
;
765 PWSTR pwszPrinter
= NULL
;
769 // Convert pszPrinter to a Unicode string pwszPrinter
770 cch
= strlen(pszPrinter
);
772 pwszPrinter
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
775 ERR("HeapAlloc failed for pwszPrinter with last error %lu!\n", GetLastError());
779 MultiByteToWideChar(CP_ACP
, 0, pszPrinter
, -1, pwszPrinter
, cch
+ 1);
782 bReturnValue
= SetDefaultPrinterW(pwszPrinter
);
786 HeapFree(hProcessHeap
, 0, pwszPrinter
);
792 SetDefaultPrinterW(LPCWSTR pszPrinter
)
794 const WCHAR wszDevicesKey
[] = L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
796 DWORD cbDeviceValueData
;
797 DWORD cbPrinterValueData
= 0;
800 HKEY hDevicesKey
= NULL
;
801 HKEY hWindowsKey
= NULL
;
802 PWSTR pwszDeviceValueData
= NULL
;
803 WCHAR wszPrinter
[MAX_PRINTER_NAME
+ 1];
805 // Open the Devices registry key.
806 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_CURRENT_USER
, wszDevicesKey
, 0, KEY_READ
, &hDevicesKey
);
807 if (dwErrorCode
!= ERROR_SUCCESS
)
809 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
813 // Did the caller give us a printer to set as default?
814 if (pszPrinter
&& *pszPrinter
)
816 // Check if the given printer exists and query the value data size.
817 dwErrorCode
= (DWORD
)RegQueryValueExW(hDevicesKey
, pszPrinter
, NULL
, NULL
, NULL
, &cbPrinterValueData
);
818 if (dwErrorCode
== ERROR_FILE_NOT_FOUND
)
820 // The caller gave us an invalid printer name, return with ERROR_FILE_NOT_FOUND.
823 else if (dwErrorCode
!= ERROR_SUCCESS
)
825 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
829 cchPrinter
= wcslen(pszPrinter
);
833 // If there is already a default printer, we're done!
834 cchPrinter
= _countof(wszPrinter
);
835 if (GetDefaultPrinterW(wszPrinter
, &cchPrinter
))
837 dwErrorCode
= ERROR_SUCCESS
;
841 // Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
842 cchPrinter
= _countof(wszPrinter
);
843 dwErrorCode
= (DWORD
)RegEnumValueW(hDevicesKey
, 0, wszPrinter
, &cchPrinter
, NULL
, NULL
, NULL
, &cbPrinterValueData
);
844 if (dwErrorCode
!= ERROR_MORE_DATA
)
847 pszPrinter
= wszPrinter
;
850 // We now need to query the value data, which has the format "winspool,<Port>:"
851 // and make "<Printer Name>,winspool,<Port>:" out of it.
852 // Allocate a buffer large enough for the final data.
853 cbDeviceValueData
= (cchPrinter
+ 1) * sizeof(WCHAR
) + cbPrinterValueData
;
854 pwszDeviceValueData
= HeapAlloc(hProcessHeap
, 0, cbDeviceValueData
);
855 if (!pwszDeviceValueData
)
857 dwErrorCode
= GetLastError();
858 ERR("HeapAlloc failed with error %lu\n", dwErrorCode
);
862 // Copy the Printer Name and a comma into it.
863 CopyMemory(pwszDeviceValueData
, pszPrinter
, cchPrinter
* sizeof(WCHAR
));
864 pwszDeviceValueData
[cchPrinter
] = L
',';
866 // Append the value data, which has the format "winspool,<Port>:"
867 dwErrorCode
= (DWORD
)RegQueryValueExW(hDevicesKey
, pszPrinter
, NULL
, NULL
, (PBYTE
)&pwszDeviceValueData
[cchPrinter
+ 1], &cbPrinterValueData
);
868 if (dwErrorCode
!= ERROR_SUCCESS
)
871 // Open the Windows registry key.
872 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_CURRENT_USER
, wszWindowsKey
, 0, KEY_SET_VALUE
, &hWindowsKey
);
873 if (dwErrorCode
!= ERROR_SUCCESS
)
875 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
879 // Store our new default printer.
880 dwErrorCode
= (DWORD
)RegSetValueExW(hWindowsKey
, wszDeviceValue
, 0, REG_SZ
, (PBYTE
)pwszDeviceValueData
, cbDeviceValueData
);
881 if (dwErrorCode
!= ERROR_SUCCESS
)
883 ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode
);
889 RegCloseKey(hDevicesKey
);
892 RegCloseKey(hWindowsKey
);
894 if (pwszDeviceValueData
)
895 HeapFree(hProcessHeap
, 0, pwszDeviceValueData
);
897 SetLastError(dwErrorCode
);
898 return (dwErrorCode
== ERROR_SUCCESS
);
902 SetPrinterW(HANDLE hPrinter
, DWORD Level
, PBYTE pPrinter
, DWORD Command
)
909 StartDocPrinterA(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
911 DOC_INFO_1W wDocInfo1
= { 0 };
914 DWORD dwReturnValue
= 0;
915 PDOC_INFO_1A pDocInfo1
= (PDOC_INFO_1A
)pDocInfo
;
917 // Only check the minimum required for accessing pDocInfo.
918 // Additional sanity checks are done in StartDocPrinterW.
921 dwErrorCode
= ERROR_INVALID_PARAMETER
;
927 dwErrorCode
= ERROR_INVALID_LEVEL
;
931 if (pDocInfo1
->pDatatype
)
933 // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
934 cch
= strlen(pDocInfo1
->pDatatype
);
936 wDocInfo1
.pDatatype
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
937 if (!wDocInfo1
.pDatatype
)
939 ERR("HeapAlloc failed for wDocInfo1.pDatatype with last error %lu!\n", GetLastError());
943 MultiByteToWideChar(CP_ACP
, 0, pDocInfo1
->pDatatype
, -1, wDocInfo1
.pDatatype
, cch
+ 1);
946 if (pDocInfo1
->pDocName
)
948 // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
949 cch
= strlen(pDocInfo1
->pDocName
);
951 wDocInfo1
.pDocName
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
952 if (!wDocInfo1
.pDocName
)
954 ERR("HeapAlloc failed for wDocInfo1.pDocName with last error %lu!\n", GetLastError());
958 MultiByteToWideChar(CP_ACP
, 0, pDocInfo1
->pDocName
, -1, wDocInfo1
.pDocName
, cch
+ 1);
961 if (pDocInfo1
->pOutputFile
)
963 // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
964 cch
= strlen(pDocInfo1
->pOutputFile
);
966 wDocInfo1
.pOutputFile
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
967 if (!wDocInfo1
.pOutputFile
)
969 ERR("HeapAlloc failed for wDocInfo1.pOutputFile with last error %lu!\n", GetLastError());
973 MultiByteToWideChar(CP_ACP
, 0, pDocInfo1
->pOutputFile
, -1, wDocInfo1
.pOutputFile
, cch
+ 1);
976 dwReturnValue
= StartDocPrinterW(hPrinter
, Level
, (PBYTE
)&wDocInfo1
);
977 dwErrorCode
= GetLastError();
980 if (wDocInfo1
.pDatatype
)
981 HeapFree(hProcessHeap
, 0, wDocInfo1
.pDatatype
);
983 if (wDocInfo1
.pDocName
)
984 HeapFree(hProcessHeap
, 0, wDocInfo1
.pDocName
);
986 if (wDocInfo1
.pOutputFile
)
987 HeapFree(hProcessHeap
, 0, wDocInfo1
.pOutputFile
);
989 SetLastError(dwErrorCode
);
990 return dwReturnValue
;
994 StartDocPrinterW(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
999 DWORD dwReturnValue
= 0;
1000 PADDJOB_INFO_1W pAddJobInfo1
= NULL
;
1001 PDOC_INFO_1W pDocInfo1
= (PDOC_INFO_1W
)pDocInfo
;
1002 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
1007 dwErrorCode
= ERROR_INVALID_HANDLE
;
1013 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1019 dwErrorCode
= ERROR_INVALID_LEVEL
;
1023 if (pHandle
->bStartedDoc
)
1025 dwErrorCode
= ERROR_INVALID_PRINTER_STATE
;
1029 // Check if we want to redirect output into a file.
1030 if (pDocInfo1
->pOutputFile
)
1032 // Do a StartDocPrinter RPC call in this case.
1033 dwErrorCode
= _StartDocPrinterWithRPC(pHandle
, pDocInfo1
);
1037 // Allocate memory for the ADDJOB_INFO_1W structure and a path.
1038 cbAddJobInfo1
= sizeof(ADDJOB_INFO_1W
) + MAX_PATH
* sizeof(WCHAR
);
1039 pAddJobInfo1
= HeapAlloc(hProcessHeap
, 0, cbAddJobInfo1
);
1042 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1043 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
1047 // Try to add a new job.
1048 // This only succeeds if the printer is set to do spooled printing.
1049 if (AddJobW((HANDLE
)pHandle
, 1, (PBYTE
)pAddJobInfo1
, cbAddJobInfo1
, &cbNeeded
))
1051 // Do spooled printing.
1052 dwErrorCode
= _StartDocPrinterSpooled(pHandle
, pDocInfo1
, pAddJobInfo1
);
1054 else if (GetLastError() == ERROR_INVALID_ACCESS
)
1056 // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
1057 // In this case, we do a StartDocPrinter RPC call.
1058 dwErrorCode
= _StartDocPrinterWithRPC(pHandle
, pDocInfo1
);
1062 dwErrorCode
= GetLastError();
1063 ERR("AddJobW failed with error %lu!\n", dwErrorCode
);
1068 if (dwErrorCode
== ERROR_SUCCESS
)
1070 pHandle
->bStartedDoc
= TRUE
;
1071 dwReturnValue
= pHandle
->dwJobID
;
1076 HeapFree(hProcessHeap
, 0, pAddJobInfo1
);
1078 SetLastError(dwErrorCode
);
1079 return dwReturnValue
;
1083 StartPagePrinter(HANDLE hPrinter
)
1086 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
1091 dwErrorCode
= ERROR_INVALID_HANDLE
;
1098 dwErrorCode
= _RpcStartPagePrinter(pHandle
->hPrinter
);
1100 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
1102 dwErrorCode
= RpcExceptionCode();
1103 ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode
);
1108 SetLastError(dwErrorCode
);
1109 return (dwErrorCode
== ERROR_SUCCESS
);
1113 WritePrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
)
1116 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
1121 dwErrorCode
= ERROR_INVALID_HANDLE
;
1125 if (!pHandle
->bStartedDoc
)
1127 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1131 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
1133 // Write to the spool file. This doesn't need an RPC request.
1134 if (!WriteFile(pHandle
->hSPLFile
, pBuf
, cbBuf
, pcWritten
, NULL
))
1136 dwErrorCode
= GetLastError();
1137 ERR("WriteFile failed with error %lu!\n", dwErrorCode
);
1141 dwErrorCode
= ERROR_SUCCESS
;
1145 // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
1146 // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
1151 dwErrorCode
= _RpcWritePrinter(pHandle
->hPrinter
, pBuf
, cbBuf
, pcWritten
);
1153 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
1155 dwErrorCode
= RpcExceptionCode();
1156 ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode
);
1162 SetLastError(dwErrorCode
);
1163 return (dwErrorCode
== ERROR_SUCCESS
);
1167 XcvDataW(HANDLE hXcv
, PCWSTR pszDataName
, PBYTE pInputData
, DWORD cbInputData
, PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
, PDWORD pdwStatus
)