2 * PROJECT: ReactOS Spooler API
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
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!\n");
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
)
218 TRACE("AddPrinterW(%S, %lu, %p)\n", pName
, Level
, pPrinter
);
224 ClosePrinter(HANDLE hPrinter
)
227 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
229 TRACE("ClosePrinter(%p)\n", hPrinter
);
234 dwErrorCode
= ERROR_INVALID_HANDLE
;
241 dwErrorCode
= _RpcClosePrinter(&pHandle
->hPrinter
);
243 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
245 dwErrorCode
= RpcExceptionCode();
246 ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode
);
250 // Close any open file handle.
251 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
252 CloseHandle(pHandle
->hSPLFile
);
254 // Free the memory for the handle.
255 HeapFree(hProcessHeap
, 0, pHandle
);
258 SetLastError(dwErrorCode
);
259 return (dwErrorCode
== ERROR_SUCCESS
);
263 DeviceCapabilitiesA(LPCSTR pDevice
, LPCSTR pPort
, WORD fwCapability
, LPSTR pOutput
, const DEVMODEA
* pDevMode
)
265 TRACE("DeviceCapabilitiesA(%s, %s, %hu, %p, %p)\n", pDevice
, pPort
, fwCapability
, pOutput
, pDevMode
);
270 DeviceCapabilitiesW(LPCWSTR pDevice
, LPCWSTR pPort
, WORD fwCapability
, LPWSTR pOutput
, const DEVMODEW
* pDevMode
)
272 TRACE("DeviceCapabilitiesW(%S, %S, %hu, %p, %p)\n", pDevice
, pPort
, fwCapability
, pOutput
, pDevMode
);
277 DocumentPropertiesA(HWND hWnd
, HANDLE hPrinter
, LPSTR pDeviceName
, PDEVMODEA pDevModeOutput
, PDEVMODEA pDevModeInput
, DWORD fMode
)
279 TRACE("DocumentPropertiesA(%p, %p, %s, %p, %p, %lu)\n", hWnd
, hPrinter
, pDeviceName
, pDevModeOutput
, pDevModeInput
, fMode
);
284 DocumentPropertiesW(HWND hWnd
, HANDLE hPrinter
, LPWSTR pDeviceName
, PDEVMODEW pDevModeOutput
, PDEVMODEW pDevModeInput
, DWORD fMode
)
286 TRACE("DocumentPropertiesW(%p, %p, %S, %p, %p, %lu)\n", hWnd
, hPrinter
, pDeviceName
, pDevModeOutput
, pDevModeInput
, fMode
);
291 EndDocPrinter(HANDLE hPrinter
)
294 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
296 TRACE("EndDocPrinter(%p)\n", hPrinter
);
301 dwErrorCode
= ERROR_INVALID_HANDLE
;
305 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
307 // For spooled jobs, the document is finished by calling _RpcScheduleJob.
310 dwErrorCode
= _RpcScheduleJob(pHandle
->hPrinter
, pHandle
->dwJobID
);
312 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
314 dwErrorCode
= RpcExceptionCode();
315 ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode
);
319 // Close the spool file handle.
320 CloseHandle(pHandle
->hSPLFile
);
324 // In all other cases, just call _RpcEndDocPrinter.
327 dwErrorCode
= _RpcEndDocPrinter(pHandle
->hPrinter
);
329 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
331 dwErrorCode
= RpcExceptionCode();
332 ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode
);
337 // A new document can now be started again.
338 pHandle
->bStartedDoc
= FALSE
;
341 SetLastError(dwErrorCode
);
342 return (dwErrorCode
== ERROR_SUCCESS
);
346 EndPagePrinter(HANDLE hPrinter
)
349 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
351 TRACE("EndPagePrinter(%p)\n", hPrinter
);
356 dwErrorCode
= ERROR_INVALID_HANDLE
;
360 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
362 // For spooled jobs, we don't need to do anything.
363 dwErrorCode
= ERROR_SUCCESS
;
367 // In all other cases, just call _RpcEndPagePrinter.
370 dwErrorCode
= _RpcEndPagePrinter(pHandle
->hPrinter
);
372 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
374 dwErrorCode
= RpcExceptionCode();
375 ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode
);
381 SetLastError(dwErrorCode
);
382 return (dwErrorCode
== ERROR_SUCCESS
);
386 EnumPrintersA(DWORD Flags
, PSTR Name
, DWORD Level
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
388 TRACE("EnumPrintersA(%lu, %s, %lu, %p, %lu, %p, %p)\n", Flags
, Name
, Level
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
393 EnumPrintersW(DWORD Flags
, PWSTR Name
, DWORD Level
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
397 TRACE("EnumPrintersW(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags
, Name
, Level
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
399 // Dismiss invalid levels already at this point.
400 if (Level
== 3 || Level
> 5)
402 dwErrorCode
= ERROR_INVALID_LEVEL
;
406 if (cbBuf
&& pPrinterEnum
)
407 ZeroMemory(pPrinterEnum
, cbBuf
);
412 dwErrorCode
= _RpcEnumPrinters(Flags
, Name
, Level
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
414 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
416 dwErrorCode
= RpcExceptionCode();
417 ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode
);
421 if (dwErrorCode
== ERROR_SUCCESS
)
424 PBYTE p
= pPrinterEnum
;
426 for (i
= 0; i
< *pcReturned
; i
++)
427 _MarshallUpPrinterInfo(&p
, Level
);
431 SetLastError(dwErrorCode
);
432 return (dwErrorCode
== ERROR_SUCCESS
);
436 GetDefaultPrinterA(LPSTR pszBuffer
, LPDWORD pcchBuffer
)
439 PWSTR pwszBuffer
= NULL
;
441 TRACE("GetDefaultPrinterA(%p, %p)\n", pszBuffer
, pcchBuffer
);
446 dwErrorCode
= ERROR_INVALID_PARAMETER
;
450 // Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
451 if (pszBuffer
&& *pcchBuffer
)
453 pwszBuffer
= HeapAlloc(hProcessHeap
, 0, *pcchBuffer
* sizeof(WCHAR
));
456 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
457 ERR("HeapAlloc failed!\n");
462 if (!GetDefaultPrinterW(pwszBuffer
, pcchBuffer
))
464 dwErrorCode
= GetLastError();
468 // We successfully got a string in pwszBuffer, so convert the Unicode string to ANSI.
469 WideCharToMultiByte(CP_ACP
, 0, pwszBuffer
, -1, pszBuffer
, *pcchBuffer
, NULL
, NULL
);
471 dwErrorCode
= ERROR_SUCCESS
;
475 HeapFree(hProcessHeap
, 0, pwszBuffer
);
477 SetLastError(dwErrorCode
);
478 return (dwErrorCode
== ERROR_SUCCESS
);
482 GetDefaultPrinterW(LPWSTR pszBuffer
, LPDWORD pcchBuffer
)
485 DWORD cchInputBuffer
;
487 HKEY hWindowsKey
= NULL
;
488 PWSTR pwszDevice
= NULL
;
491 TRACE("GetDefaultPrinterW(%p, %p)\n", pszBuffer
, pcchBuffer
);
496 dwErrorCode
= ERROR_INVALID_PARAMETER
;
500 cchInputBuffer
= *pcchBuffer
;
502 // Open the registry key where the default printer for the current user is stored.
503 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_CURRENT_USER
, wszWindowsKey
, 0, KEY_READ
, &hWindowsKey
);
504 if (dwErrorCode
!= ERROR_SUCCESS
)
506 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
510 // Determine the size of the required buffer.
511 dwErrorCode
= (DWORD
)RegQueryValueExW(hWindowsKey
, wszDeviceValue
, NULL
, NULL
, NULL
, &cbNeeded
);
512 if (dwErrorCode
!= ERROR_SUCCESS
)
514 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
519 pwszDevice
= HeapAlloc(hProcessHeap
, 0, cbNeeded
);
522 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
523 ERR("HeapAlloc failed!\n");
527 // Now get the actual value.
528 dwErrorCode
= RegQueryValueExW(hWindowsKey
, wszDeviceValue
, NULL
, NULL
, (PBYTE
)pwszDevice
, &cbNeeded
);
529 if (dwErrorCode
!= ERROR_SUCCESS
)
531 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
535 // We get a string "<Printer Name>,winspool,<Port>:".
536 // Extract the printer name from it.
537 pwszComma
= wcschr(pwszDevice
, L
',');
540 ERR("Found no or invalid default printer: %S!\n", pwszDevice
);
541 dwErrorCode
= ERROR_INVALID_NAME
;
545 // Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
546 *pcchBuffer
= pwszComma
- pwszDevice
+ 1;
548 // Check if the supplied buffer is large enough.
549 if (cchInputBuffer
< *pcchBuffer
)
551 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
555 // Copy the default printer.
557 CopyMemory(pszBuffer
, pwszDevice
, *pcchBuffer
* sizeof(WCHAR
));
559 dwErrorCode
= ERROR_SUCCESS
;
563 RegCloseKey(hWindowsKey
);
566 HeapFree(hProcessHeap
, 0, pwszDevice
);
568 SetLastError(dwErrorCode
);
569 return (dwErrorCode
== ERROR_SUCCESS
);
573 GetPrinterA(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
575 TRACE("GetPrinterA(%p, %lu, %p, %lu, %p)\n", hPrinter
, Level
, pPrinter
, cbBuf
, pcbNeeded
);
580 GetPrinterDriverA(HANDLE hPrinter
, LPSTR pEnvironment
, DWORD Level
, LPBYTE pDriverInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
582 TRACE("GetPrinterDriverA(%p, %s, %lu, %p, %lu, %p)\n", hPrinter
, pEnvironment
, Level
, pDriverInfo
, cbBuf
, pcbNeeded
);
587 GetPrinterDriverW(HANDLE hPrinter
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pDriverInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
589 TRACE("GetPrinterDriverW(%p, %S, %lu, %p, %lu, %p)\n", hPrinter
, pEnvironment
, Level
, pDriverInfo
, cbBuf
, pcbNeeded
);
594 GetPrinterW(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
598 TRACE("GetPrinterW(%p, %lu, %p, %lu, %p)\n", hPrinter
, Level
, pPrinter
, cbBuf
, pcbNeeded
);
600 // Dismiss invalid levels already at this point.
603 dwErrorCode
= ERROR_INVALID_LEVEL
;
607 if (cbBuf
&& pPrinter
)
608 ZeroMemory(pPrinter
, cbBuf
);
613 dwErrorCode
= _RpcGetPrinter(hPrinter
, Level
, pPrinter
, cbBuf
, pcbNeeded
);
615 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
617 dwErrorCode
= RpcExceptionCode();
618 ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode
);
622 if (dwErrorCode
== ERROR_SUCCESS
)
625 _MarshallUpPrinterInfo(&p
, Level
);
629 SetLastError(dwErrorCode
);
630 return (dwErrorCode
== ERROR_SUCCESS
);
634 OpenPrinterA(LPSTR pPrinterName
, LPHANDLE phPrinter
, LPPRINTER_DEFAULTSA pDefault
)
636 BOOL bReturnValue
= FALSE
;
638 PWSTR pwszPrinterName
= NULL
;
639 PRINTER_DEFAULTSW wDefault
= { 0 };
641 TRACE("OpenPrinterA(%s, %p, %p)\n", pPrinterName
, phPrinter
, pDefault
);
645 // Convert pPrinterName to a Unicode string pwszPrinterName
646 cch
= strlen(pPrinterName
);
648 pwszPrinterName
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
649 if (!pwszPrinterName
)
651 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
652 ERR("HeapAlloc failed!\n");
656 MultiByteToWideChar(CP_ACP
, 0, pPrinterName
, -1, pwszPrinterName
, cch
+ 1);
661 wDefault
.DesiredAccess
= pDefault
->DesiredAccess
;
663 if (pDefault
->pDatatype
)
665 // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
666 cch
= strlen(pDefault
->pDatatype
);
668 wDefault
.pDatatype
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
669 if (!wDefault
.pDatatype
)
671 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
672 ERR("HeapAlloc failed!\n");
676 MultiByteToWideChar(CP_ACP
, 0, pDefault
->pDatatype
, -1, wDefault
.pDatatype
, cch
+ 1);
679 if (pDefault
->pDevMode
)
680 wDefault
.pDevMode
= GdiConvertToDevmodeW(pDefault
->pDevMode
);
683 bReturnValue
= OpenPrinterW(pwszPrinterName
, phPrinter
, &wDefault
);
686 if (wDefault
.pDatatype
)
687 HeapFree(hProcessHeap
, 0, wDefault
.pDatatype
);
689 if (wDefault
.pDevMode
)
690 HeapFree(hProcessHeap
, 0, wDefault
.pDevMode
);
693 HeapFree(hProcessHeap
, 0, pwszPrinterName
);
699 OpenPrinterW(LPWSTR pPrinterName
, LPHANDLE phPrinter
, LPPRINTER_DEFAULTSW pDefault
)
703 PSPOOLER_HANDLE pHandle
;
704 PWSTR pDatatype
= NULL
;
705 WINSPOOL_DEVMODE_CONTAINER DevModeContainer
= { 0 };
706 ACCESS_MASK AccessRequired
= 0;
708 TRACE("OpenPrinterW(%S, %p, %p)\n", pPrinterName
, phPrinter
, pDefault
);
713 dwErrorCode
= ERROR_INVALID_PARAMETER
;
717 // Prepare the additional parameters in the format required by _RpcOpenPrinter
720 pDatatype
= pDefault
->pDatatype
;
721 DevModeContainer
.cbBuf
= sizeof(DEVMODEW
);
722 DevModeContainer
.pDevMode
= (BYTE
*)pDefault
->pDevMode
;
723 AccessRequired
= pDefault
->DesiredAccess
;
729 dwErrorCode
= _RpcOpenPrinter(pPrinterName
, &hPrinter
, pDatatype
, &DevModeContainer
, AccessRequired
);
731 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
733 dwErrorCode
= RpcExceptionCode();
734 ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode
);
738 if (dwErrorCode
== ERROR_SUCCESS
)
740 // Create a new SPOOLER_HANDLE structure.
741 pHandle
= HeapAlloc(hProcessHeap
, HEAP_ZERO_MEMORY
, sizeof(SPOOLER_HANDLE
));
744 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
745 ERR("HeapAlloc failed!\n");
749 pHandle
->hPrinter
= hPrinter
;
750 pHandle
->hSPLFile
= INVALID_HANDLE_VALUE
;
752 // Return it as phPrinter.
753 *phPrinter
= (HANDLE
)pHandle
;
757 SetLastError(dwErrorCode
);
758 return (dwErrorCode
== ERROR_SUCCESS
);
762 ReadPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pNoBytesRead
)
765 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
767 TRACE("ReadPrinter(%p, %p, %lu, %p)\n", hPrinter
, pBuf
, cbBuf
, pNoBytesRead
);
772 dwErrorCode
= ERROR_INVALID_HANDLE
;
779 dwErrorCode
= _RpcReadPrinter(pHandle
->hPrinter
, pBuf
, cbBuf
, pNoBytesRead
);
781 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
783 dwErrorCode
= RpcExceptionCode();
784 ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode
);
789 SetLastError(dwErrorCode
);
790 return (dwErrorCode
== ERROR_SUCCESS
);
794 ResetPrinterW(HANDLE hPrinter
, PPRINTER_DEFAULTSW pDefault
)
796 TRACE("ResetPrinterW(%p, %p)\n", hPrinter
, pDefault
);
802 SetDefaultPrinterA(LPCSTR pszPrinter
)
804 BOOL bReturnValue
= FALSE
;
806 PWSTR pwszPrinter
= NULL
;
808 TRACE("SetDefaultPrinterA(%s)\n", pszPrinter
);
812 // Convert pszPrinter to a Unicode string pwszPrinter
813 cch
= strlen(pszPrinter
);
815 pwszPrinter
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
818 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
819 ERR("HeapAlloc failed!\n");
823 MultiByteToWideChar(CP_ACP
, 0, pszPrinter
, -1, pwszPrinter
, cch
+ 1);
826 bReturnValue
= SetDefaultPrinterW(pwszPrinter
);
830 HeapFree(hProcessHeap
, 0, pwszPrinter
);
836 SetDefaultPrinterW(LPCWSTR pszPrinter
)
838 const WCHAR wszDevicesKey
[] = L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
840 DWORD cbDeviceValueData
;
841 DWORD cbPrinterValueData
= 0;
844 HKEY hDevicesKey
= NULL
;
845 HKEY hWindowsKey
= NULL
;
846 PWSTR pwszDeviceValueData
= NULL
;
847 WCHAR wszPrinter
[MAX_PRINTER_NAME
+ 1];
849 TRACE("SetDefaultPrinterW(%S)\n", pszPrinter
);
851 // Open the Devices registry key.
852 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_CURRENT_USER
, wszDevicesKey
, 0, KEY_READ
, &hDevicesKey
);
853 if (dwErrorCode
!= ERROR_SUCCESS
)
855 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
859 // Did the caller give us a printer to set as default?
860 if (pszPrinter
&& *pszPrinter
)
862 // Check if the given printer exists and query the value data size.
863 dwErrorCode
= (DWORD
)RegQueryValueExW(hDevicesKey
, pszPrinter
, NULL
, NULL
, NULL
, &cbPrinterValueData
);
864 if (dwErrorCode
== ERROR_FILE_NOT_FOUND
)
866 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
869 else if (dwErrorCode
!= ERROR_SUCCESS
)
871 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
875 cchPrinter
= wcslen(pszPrinter
);
879 // If there is already a default printer, we're done!
880 cchPrinter
= _countof(wszPrinter
);
881 if (GetDefaultPrinterW(wszPrinter
, &cchPrinter
))
883 dwErrorCode
= ERROR_SUCCESS
;
887 // Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
888 cchPrinter
= _countof(wszPrinter
);
889 dwErrorCode
= (DWORD
)RegEnumValueW(hDevicesKey
, 0, wszPrinter
, &cchPrinter
, NULL
, NULL
, NULL
, &cbPrinterValueData
);
890 if (dwErrorCode
!= ERROR_MORE_DATA
)
893 pszPrinter
= wszPrinter
;
896 // We now need to query the value data, which has the format "winspool,<Port>:"
897 // and make "<Printer Name>,winspool,<Port>:" out of it.
898 // Allocate a buffer large enough for the final data.
899 cbDeviceValueData
= (cchPrinter
+ 1) * sizeof(WCHAR
) + cbPrinterValueData
;
900 pwszDeviceValueData
= HeapAlloc(hProcessHeap
, 0, cbDeviceValueData
);
901 if (!pwszDeviceValueData
)
903 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
904 ERR("HeapAlloc failed!\n");
908 // Copy the Printer Name and a comma into it.
909 CopyMemory(pwszDeviceValueData
, pszPrinter
, cchPrinter
* sizeof(WCHAR
));
910 pwszDeviceValueData
[cchPrinter
] = L
',';
912 // Append the value data, which has the format "winspool,<Port>:"
913 dwErrorCode
= (DWORD
)RegQueryValueExW(hDevicesKey
, pszPrinter
, NULL
, NULL
, (PBYTE
)&pwszDeviceValueData
[cchPrinter
+ 1], &cbPrinterValueData
);
914 if (dwErrorCode
!= ERROR_SUCCESS
)
917 // Open the Windows registry key.
918 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_CURRENT_USER
, wszWindowsKey
, 0, KEY_SET_VALUE
, &hWindowsKey
);
919 if (dwErrorCode
!= ERROR_SUCCESS
)
921 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
925 // Store our new default printer.
926 dwErrorCode
= (DWORD
)RegSetValueExW(hWindowsKey
, wszDeviceValue
, 0, REG_SZ
, (PBYTE
)pwszDeviceValueData
, cbDeviceValueData
);
927 if (dwErrorCode
!= ERROR_SUCCESS
)
929 ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode
);
935 RegCloseKey(hDevicesKey
);
938 RegCloseKey(hWindowsKey
);
940 if (pwszDeviceValueData
)
941 HeapFree(hProcessHeap
, 0, pwszDeviceValueData
);
943 SetLastError(dwErrorCode
);
944 return (dwErrorCode
== ERROR_SUCCESS
);
948 SetPrinterW(HANDLE hPrinter
, DWORD Level
, PBYTE pPrinter
, DWORD Command
)
950 TRACE("SetPrinterW(%p, %lu, %p, %lu)\n", hPrinter
, Level
, pPrinter
, Command
);
956 StartDocPrinterA(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
958 DOC_INFO_1W wDocInfo1
= { 0 };
961 DWORD dwReturnValue
= 0;
962 PDOC_INFO_1A pDocInfo1
= (PDOC_INFO_1A
)pDocInfo
;
964 TRACE("StartDocPrinterA(%p, %lu, %p)\n", hPrinter
, Level
, pDocInfo
);
966 // Only check the minimum required for accessing pDocInfo.
967 // Additional sanity checks are done in StartDocPrinterW.
970 dwErrorCode
= ERROR_INVALID_PARAMETER
;
976 dwErrorCode
= ERROR_INVALID_LEVEL
;
980 if (pDocInfo1
->pDatatype
)
982 // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
983 cch
= strlen(pDocInfo1
->pDatatype
);
985 wDocInfo1
.pDatatype
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
986 if (!wDocInfo1
.pDatatype
)
988 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
989 ERR("HeapAlloc failed!\n");
993 MultiByteToWideChar(CP_ACP
, 0, pDocInfo1
->pDatatype
, -1, wDocInfo1
.pDatatype
, cch
+ 1);
996 if (pDocInfo1
->pDocName
)
998 // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
999 cch
= strlen(pDocInfo1
->pDocName
);
1001 wDocInfo1
.pDocName
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
1002 if (!wDocInfo1
.pDocName
)
1004 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1005 ERR("HeapAlloc failed!\n");
1009 MultiByteToWideChar(CP_ACP
, 0, pDocInfo1
->pDocName
, -1, wDocInfo1
.pDocName
, cch
+ 1);
1012 if (pDocInfo1
->pOutputFile
)
1014 // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
1015 cch
= strlen(pDocInfo1
->pOutputFile
);
1017 wDocInfo1
.pOutputFile
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
1018 if (!wDocInfo1
.pOutputFile
)
1020 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1021 ERR("HeapAlloc failed!\n");
1025 MultiByteToWideChar(CP_ACP
, 0, pDocInfo1
->pOutputFile
, -1, wDocInfo1
.pOutputFile
, cch
+ 1);
1028 dwReturnValue
= StartDocPrinterW(hPrinter
, Level
, (PBYTE
)&wDocInfo1
);
1029 dwErrorCode
= GetLastError();
1032 if (wDocInfo1
.pDatatype
)
1033 HeapFree(hProcessHeap
, 0, wDocInfo1
.pDatatype
);
1035 if (wDocInfo1
.pDocName
)
1036 HeapFree(hProcessHeap
, 0, wDocInfo1
.pDocName
);
1038 if (wDocInfo1
.pOutputFile
)
1039 HeapFree(hProcessHeap
, 0, wDocInfo1
.pOutputFile
);
1041 SetLastError(dwErrorCode
);
1042 return dwReturnValue
;
1046 StartDocPrinterW(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
1048 DWORD cbAddJobInfo1
;
1051 DWORD dwReturnValue
= 0;
1052 PADDJOB_INFO_1W pAddJobInfo1
= NULL
;
1053 PDOC_INFO_1W pDocInfo1
= (PDOC_INFO_1W
)pDocInfo
;
1054 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
1056 TRACE("StartDocPrinterW(%p, %lu, %p)\n", hPrinter
, Level
, pDocInfo
);
1061 dwErrorCode
= ERROR_INVALID_HANDLE
;
1067 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1073 dwErrorCode
= ERROR_INVALID_LEVEL
;
1077 if (pHandle
->bStartedDoc
)
1079 dwErrorCode
= ERROR_INVALID_PRINTER_STATE
;
1083 // Check if we want to redirect output into a file.
1084 if (pDocInfo1
->pOutputFile
)
1086 // Do a StartDocPrinter RPC call in this case.
1087 dwErrorCode
= _StartDocPrinterWithRPC(pHandle
, pDocInfo1
);
1091 // Allocate memory for the ADDJOB_INFO_1W structure and a path.
1092 cbAddJobInfo1
= sizeof(ADDJOB_INFO_1W
) + MAX_PATH
* sizeof(WCHAR
);
1093 pAddJobInfo1
= HeapAlloc(hProcessHeap
, 0, cbAddJobInfo1
);
1096 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1097 ERR("HeapAlloc failed!\n");
1101 // Try to add a new job.
1102 // This only succeeds if the printer is set to do spooled printing.
1103 if (AddJobW((HANDLE
)pHandle
, 1, (PBYTE
)pAddJobInfo1
, cbAddJobInfo1
, &cbNeeded
))
1105 // Do spooled printing.
1106 dwErrorCode
= _StartDocPrinterSpooled(pHandle
, pDocInfo1
, pAddJobInfo1
);
1108 else if (GetLastError() == ERROR_INVALID_ACCESS
)
1110 // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
1111 // In this case, we do a StartDocPrinter RPC call.
1112 dwErrorCode
= _StartDocPrinterWithRPC(pHandle
, pDocInfo1
);
1116 dwErrorCode
= GetLastError();
1117 ERR("AddJobW failed with error %lu!\n", dwErrorCode
);
1122 if (dwErrorCode
== ERROR_SUCCESS
)
1124 pHandle
->bStartedDoc
= TRUE
;
1125 dwReturnValue
= pHandle
->dwJobID
;
1130 HeapFree(hProcessHeap
, 0, pAddJobInfo1
);
1132 SetLastError(dwErrorCode
);
1133 return dwReturnValue
;
1137 StartPagePrinter(HANDLE hPrinter
)
1140 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
1142 TRACE("StartPagePrinter(%p)\n", hPrinter
);
1147 dwErrorCode
= ERROR_INVALID_HANDLE
;
1154 dwErrorCode
= _RpcStartPagePrinter(pHandle
->hPrinter
);
1156 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
1158 dwErrorCode
= RpcExceptionCode();
1159 ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode
);
1164 SetLastError(dwErrorCode
);
1165 return (dwErrorCode
== ERROR_SUCCESS
);
1169 WritePrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
)
1172 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
1174 TRACE("WritePrinter(%p, %p, %lu, %p)\n", hPrinter
, pBuf
, cbBuf
, pcWritten
);
1179 dwErrorCode
= ERROR_INVALID_HANDLE
;
1183 if (!pHandle
->bStartedDoc
)
1185 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1189 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
1191 // Write to the spool file. This doesn't need an RPC request.
1192 if (!WriteFile(pHandle
->hSPLFile
, pBuf
, cbBuf
, pcWritten
, NULL
))
1194 dwErrorCode
= GetLastError();
1195 ERR("WriteFile failed with error %lu!\n", dwErrorCode
);
1199 dwErrorCode
= ERROR_SUCCESS
;
1203 // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
1204 // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
1209 dwErrorCode
= _RpcWritePrinter(pHandle
->hPrinter
, pBuf
, cbBuf
, pcWritten
);
1211 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
1213 dwErrorCode
= RpcExceptionCode();
1214 ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode
);
1220 SetLastError(dwErrorCode
);
1221 return (dwErrorCode
== ERROR_SUCCESS
);
1225 XcvDataW(HANDLE hXcv
, PCWSTR pszDataName
, PBYTE pInputData
, DWORD cbInputData
, PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
, PDWORD pdwStatus
)
1227 TRACE("XcvDataW(%p, %S, %p, %lu, %p, %lu, %p, %p)\n", hXcv
, pszDataName
, pInputData
, cbInputData
, pOutputData
, cbOutputData
, pcbOutputNeeded
, pdwStatus
);