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 AbortPrinter(HANDLE hPrinter
)
218 TRACE("AbortPrinter(%p)\n", hPrinter
);
224 AddPrinterA(PSTR pName
, DWORD Level
, PBYTE pPrinter
)
226 TRACE("AddPrinterA(%s, %lu, %p)\n", pName
, Level
, pPrinter
);
232 AddPrinterW(PWSTR pName
, DWORD Level
, PBYTE pPrinter
)
234 TRACE("AddPrinterW(%S, %lu, %p)\n", pName
, Level
, pPrinter
);
240 ClosePrinter(HANDLE hPrinter
)
243 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
245 TRACE("ClosePrinter(%p)\n", hPrinter
);
250 dwErrorCode
= ERROR_INVALID_HANDLE
;
257 dwErrorCode
= _RpcClosePrinter(&pHandle
->hPrinter
);
259 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
261 dwErrorCode
= RpcExceptionCode();
262 ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode
);
266 // Close any open file handle.
267 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
268 CloseHandle(pHandle
->hSPLFile
);
270 // Free the memory for the handle.
271 HeapFree(hProcessHeap
, 0, pHandle
);
274 SetLastError(dwErrorCode
);
275 return (dwErrorCode
== ERROR_SUCCESS
);
279 DeletePrinter(HANDLE hPrinter
)
281 TRACE("DeletePrinter(%p)\n", hPrinter
);
287 DeviceCapabilitiesA(LPCSTR pDevice
, LPCSTR pPort
, WORD fwCapability
, LPSTR pOutput
, const DEVMODEA
* pDevMode
)
289 TRACE("DeviceCapabilitiesA(%s, %s, %hu, %p, %p)\n", pDevice
, pPort
, fwCapability
, pOutput
, pDevMode
);
295 DeviceCapabilitiesW(LPCWSTR pDevice
, LPCWSTR pPort
, WORD fwCapability
, LPWSTR pOutput
, const DEVMODEW
* pDevMode
)
297 TRACE("DeviceCapabilitiesW(%S, %S, %hu, %p, %p)\n", pDevice
, pPort
, fwCapability
, pOutput
, pDevMode
);
303 DocumentPropertiesA(HWND hWnd
, HANDLE hPrinter
, LPSTR pDeviceName
, PDEVMODEA pDevModeOutput
, PDEVMODEA pDevModeInput
, DWORD fMode
)
305 TRACE("DocumentPropertiesA(%p, %p, %s, %p, %p, %lu)\n", hWnd
, hPrinter
, pDeviceName
, pDevModeOutput
, pDevModeInput
, fMode
);
311 DocumentPropertiesW(HWND hWnd
, HANDLE hPrinter
, LPWSTR pDeviceName
, PDEVMODEW pDevModeOutput
, PDEVMODEW pDevModeInput
, DWORD fMode
)
313 TRACE("DocumentPropertiesW(%p, %p, %S, %p, %p, %lu)\n", hWnd
, hPrinter
, pDeviceName
, pDevModeOutput
, pDevModeInput
, fMode
);
319 EndDocPrinter(HANDLE hPrinter
)
322 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
324 TRACE("EndDocPrinter(%p)\n", hPrinter
);
329 dwErrorCode
= ERROR_INVALID_HANDLE
;
333 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
335 // For spooled jobs, the document is finished by calling _RpcScheduleJob.
338 dwErrorCode
= _RpcScheduleJob(pHandle
->hPrinter
, pHandle
->dwJobID
);
340 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
342 dwErrorCode
= RpcExceptionCode();
343 ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode
);
347 // Close the spool file handle.
348 CloseHandle(pHandle
->hSPLFile
);
352 // In all other cases, just call _RpcEndDocPrinter.
355 dwErrorCode
= _RpcEndDocPrinter(pHandle
->hPrinter
);
357 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
359 dwErrorCode
= RpcExceptionCode();
360 ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode
);
365 // A new document can now be started again.
366 pHandle
->bStartedDoc
= FALSE
;
369 SetLastError(dwErrorCode
);
370 return (dwErrorCode
== ERROR_SUCCESS
);
374 EndPagePrinter(HANDLE hPrinter
)
377 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
379 TRACE("EndPagePrinter(%p)\n", hPrinter
);
384 dwErrorCode
= ERROR_INVALID_HANDLE
;
388 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
390 // For spooled jobs, we don't need to do anything.
391 dwErrorCode
= ERROR_SUCCESS
;
395 // In all other cases, just call _RpcEndPagePrinter.
398 dwErrorCode
= _RpcEndPagePrinter(pHandle
->hPrinter
);
400 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
402 dwErrorCode
= RpcExceptionCode();
403 ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode
);
409 SetLastError(dwErrorCode
);
410 return (dwErrorCode
== ERROR_SUCCESS
);
414 EnumPrintersA(DWORD Flags
, PSTR Name
, DWORD Level
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
416 TRACE("EnumPrintersA(%lu, %s, %lu, %p, %lu, %p, %p)\n", Flags
, Name
, Level
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
421 EnumPrintersW(DWORD Flags
, PWSTR Name
, DWORD Level
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
425 TRACE("EnumPrintersW(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags
, Name
, Level
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
427 // Dismiss invalid levels already at this point.
428 if (Level
== 3 || Level
> 5)
430 dwErrorCode
= ERROR_INVALID_LEVEL
;
434 if (cbBuf
&& pPrinterEnum
)
435 ZeroMemory(pPrinterEnum
, cbBuf
);
440 dwErrorCode
= _RpcEnumPrinters(Flags
, Name
, Level
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
442 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
444 dwErrorCode
= RpcExceptionCode();
445 ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode
);
449 if (dwErrorCode
== ERROR_SUCCESS
)
452 PBYTE p
= pPrinterEnum
;
454 for (i
= 0; i
< *pcReturned
; i
++)
455 _MarshallUpPrinterInfo(&p
, Level
);
459 SetLastError(dwErrorCode
);
460 return (dwErrorCode
== ERROR_SUCCESS
);
464 FlushPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
, DWORD cSleep
)
466 TRACE("FlushPrinter(%p, %p, %lu, %p, %lu)\n", hPrinter
, pBuf
, cbBuf
, pcWritten
, cSleep
);
472 GetDefaultPrinterA(LPSTR pszBuffer
, LPDWORD pcchBuffer
)
475 PWSTR pwszBuffer
= NULL
;
477 TRACE("GetDefaultPrinterA(%p, %p)\n", pszBuffer
, pcchBuffer
);
482 dwErrorCode
= ERROR_INVALID_PARAMETER
;
486 // Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
487 if (pszBuffer
&& *pcchBuffer
)
489 pwszBuffer
= HeapAlloc(hProcessHeap
, 0, *pcchBuffer
* sizeof(WCHAR
));
492 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
493 ERR("HeapAlloc failed!\n");
498 if (!GetDefaultPrinterW(pwszBuffer
, pcchBuffer
))
500 dwErrorCode
= GetLastError();
504 // We successfully got a string in pwszBuffer, so convert the Unicode string to ANSI.
505 WideCharToMultiByte(CP_ACP
, 0, pwszBuffer
, -1, pszBuffer
, *pcchBuffer
, NULL
, NULL
);
507 dwErrorCode
= ERROR_SUCCESS
;
511 HeapFree(hProcessHeap
, 0, pwszBuffer
);
513 SetLastError(dwErrorCode
);
514 return (dwErrorCode
== ERROR_SUCCESS
);
518 GetDefaultPrinterW(LPWSTR pszBuffer
, LPDWORD pcchBuffer
)
521 DWORD cchInputBuffer
;
523 HKEY hWindowsKey
= NULL
;
524 PWSTR pwszDevice
= NULL
;
527 TRACE("GetDefaultPrinterW(%p, %p)\n", pszBuffer
, pcchBuffer
);
532 dwErrorCode
= ERROR_INVALID_PARAMETER
;
536 cchInputBuffer
= *pcchBuffer
;
538 // Open the registry key where the default printer for the current user is stored.
539 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_CURRENT_USER
, wszWindowsKey
, 0, KEY_READ
, &hWindowsKey
);
540 if (dwErrorCode
!= ERROR_SUCCESS
)
542 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
546 // Determine the size of the required buffer.
547 dwErrorCode
= (DWORD
)RegQueryValueExW(hWindowsKey
, wszDeviceValue
, NULL
, NULL
, NULL
, &cbNeeded
);
548 if (dwErrorCode
!= ERROR_SUCCESS
)
550 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
555 pwszDevice
= HeapAlloc(hProcessHeap
, 0, cbNeeded
);
558 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
559 ERR("HeapAlloc failed!\n");
563 // Now get the actual value.
564 dwErrorCode
= RegQueryValueExW(hWindowsKey
, wszDeviceValue
, NULL
, NULL
, (PBYTE
)pwszDevice
, &cbNeeded
);
565 if (dwErrorCode
!= ERROR_SUCCESS
)
567 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
571 // We get a string "<Printer Name>,winspool,<Port>:".
572 // Extract the printer name from it.
573 pwszComma
= wcschr(pwszDevice
, L
',');
576 ERR("Found no or invalid default printer: %S!\n", pwszDevice
);
577 dwErrorCode
= ERROR_INVALID_NAME
;
581 // Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
582 *pcchBuffer
= pwszComma
- pwszDevice
+ 1;
584 // Check if the supplied buffer is large enough.
585 if (cchInputBuffer
< *pcchBuffer
)
587 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
591 // Copy the default printer.
593 CopyMemory(pszBuffer
, pwszDevice
, *pcchBuffer
* sizeof(WCHAR
));
595 dwErrorCode
= ERROR_SUCCESS
;
599 RegCloseKey(hWindowsKey
);
602 HeapFree(hProcessHeap
, 0, pwszDevice
);
604 SetLastError(dwErrorCode
);
605 return (dwErrorCode
== ERROR_SUCCESS
);
609 GetPrinterA(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
611 TRACE("GetPrinterA(%p, %lu, %p, %lu, %p)\n", hPrinter
, Level
, pPrinter
, cbBuf
, pcbNeeded
);
616 GetPrinterDriverA(HANDLE hPrinter
, LPSTR pEnvironment
, DWORD Level
, LPBYTE pDriverInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
618 TRACE("GetPrinterDriverA(%p, %s, %lu, %p, %lu, %p)\n", hPrinter
, pEnvironment
, Level
, pDriverInfo
, cbBuf
, pcbNeeded
);
623 GetPrinterDriverW(HANDLE hPrinter
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pDriverInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
625 TRACE("GetPrinterDriverW(%p, %S, %lu, %p, %lu, %p)\n", hPrinter
, pEnvironment
, Level
, pDriverInfo
, cbBuf
, pcbNeeded
);
630 GetPrinterW(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
633 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
635 TRACE("GetPrinterW(%p, %lu, %p, %lu, %p)\n", hPrinter
, Level
, pPrinter
, cbBuf
, pcbNeeded
);
640 dwErrorCode
= ERROR_INVALID_HANDLE
;
644 // Dismiss invalid levels already at this point.
647 dwErrorCode
= ERROR_INVALID_LEVEL
;
651 if (cbBuf
&& pPrinter
)
652 ZeroMemory(pPrinter
, cbBuf
);
657 dwErrorCode
= _RpcGetPrinter(pHandle
->hPrinter
, Level
, pPrinter
, cbBuf
, pcbNeeded
);
659 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
661 dwErrorCode
= RpcExceptionCode();
662 ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode
);
666 if (dwErrorCode
== ERROR_SUCCESS
)
669 _MarshallUpPrinterInfo(&p
, Level
);
673 SetLastError(dwErrorCode
);
674 return (dwErrorCode
== ERROR_SUCCESS
);
678 OpenPrinterA(LPSTR pPrinterName
, LPHANDLE phPrinter
, LPPRINTER_DEFAULTSA pDefault
)
680 BOOL bReturnValue
= FALSE
;
682 PWSTR pwszPrinterName
= NULL
;
683 PRINTER_DEFAULTSW wDefault
= { 0 };
685 TRACE("OpenPrinterA(%s, %p, %p)\n", pPrinterName
, phPrinter
, pDefault
);
689 // Convert pPrinterName to a Unicode string pwszPrinterName
690 cch
= strlen(pPrinterName
);
692 pwszPrinterName
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
693 if (!pwszPrinterName
)
695 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
696 ERR("HeapAlloc failed!\n");
700 MultiByteToWideChar(CP_ACP
, 0, pPrinterName
, -1, pwszPrinterName
, cch
+ 1);
705 wDefault
.DesiredAccess
= pDefault
->DesiredAccess
;
707 if (pDefault
->pDatatype
)
709 // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
710 cch
= strlen(pDefault
->pDatatype
);
712 wDefault
.pDatatype
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
713 if (!wDefault
.pDatatype
)
715 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
716 ERR("HeapAlloc failed!\n");
720 MultiByteToWideChar(CP_ACP
, 0, pDefault
->pDatatype
, -1, wDefault
.pDatatype
, cch
+ 1);
723 if (pDefault
->pDevMode
)
724 wDefault
.pDevMode
= GdiConvertToDevmodeW(pDefault
->pDevMode
);
727 bReturnValue
= OpenPrinterW(pwszPrinterName
, phPrinter
, &wDefault
);
730 if (wDefault
.pDatatype
)
731 HeapFree(hProcessHeap
, 0, wDefault
.pDatatype
);
733 if (wDefault
.pDevMode
)
734 HeapFree(hProcessHeap
, 0, wDefault
.pDevMode
);
737 HeapFree(hProcessHeap
, 0, pwszPrinterName
);
743 OpenPrinterW(LPWSTR pPrinterName
, LPHANDLE phPrinter
, LPPRINTER_DEFAULTSW pDefault
)
747 PSPOOLER_HANDLE pHandle
;
748 PWSTR pDatatype
= NULL
;
749 WINSPOOL_DEVMODE_CONTAINER DevModeContainer
= { 0 };
750 ACCESS_MASK AccessRequired
= 0;
752 TRACE("OpenPrinterW(%S, %p, %p)\n", pPrinterName
, phPrinter
, pDefault
);
757 dwErrorCode
= ERROR_INVALID_PARAMETER
;
761 // Prepare the additional parameters in the format required by _RpcOpenPrinter
764 pDatatype
= pDefault
->pDatatype
;
765 DevModeContainer
.cbBuf
= sizeof(DEVMODEW
);
766 DevModeContainer
.pDevMode
= (BYTE
*)pDefault
->pDevMode
;
767 AccessRequired
= pDefault
->DesiredAccess
;
773 dwErrorCode
= _RpcOpenPrinter(pPrinterName
, &hPrinter
, pDatatype
, &DevModeContainer
, AccessRequired
);
775 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
777 dwErrorCode
= RpcExceptionCode();
778 ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode
);
782 if (dwErrorCode
== ERROR_SUCCESS
)
784 // Create a new SPOOLER_HANDLE structure.
785 pHandle
= HeapAlloc(hProcessHeap
, HEAP_ZERO_MEMORY
, sizeof(SPOOLER_HANDLE
));
788 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
789 ERR("HeapAlloc failed!\n");
793 pHandle
->hPrinter
= hPrinter
;
794 pHandle
->hSPLFile
= INVALID_HANDLE_VALUE
;
796 // Return it as phPrinter.
797 *phPrinter
= (HANDLE
)pHandle
;
801 SetLastError(dwErrorCode
);
802 return (dwErrorCode
== ERROR_SUCCESS
);
806 ReadPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pNoBytesRead
)
809 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
811 TRACE("ReadPrinter(%p, %p, %lu, %p)\n", hPrinter
, pBuf
, cbBuf
, pNoBytesRead
);
816 dwErrorCode
= ERROR_INVALID_HANDLE
;
823 dwErrorCode
= _RpcReadPrinter(pHandle
->hPrinter
, pBuf
, cbBuf
, pNoBytesRead
);
825 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
827 dwErrorCode
= RpcExceptionCode();
828 ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode
);
833 SetLastError(dwErrorCode
);
834 return (dwErrorCode
== ERROR_SUCCESS
);
838 ResetPrinterA(HANDLE hPrinter
, PPRINTER_DEFAULTSA pDefault
)
840 TRACE("ResetPrinterA(%p, %p)\n", hPrinter
, pDefault
);
846 ResetPrinterW(HANDLE hPrinter
, PPRINTER_DEFAULTSW pDefault
)
848 TRACE("ResetPrinterW(%p, %p)\n", hPrinter
, pDefault
);
854 SetDefaultPrinterA(LPCSTR pszPrinter
)
856 BOOL bReturnValue
= FALSE
;
858 PWSTR pwszPrinter
= NULL
;
860 TRACE("SetDefaultPrinterA(%s)\n", pszPrinter
);
864 // Convert pszPrinter to a Unicode string pwszPrinter
865 cch
= strlen(pszPrinter
);
867 pwszPrinter
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
870 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
871 ERR("HeapAlloc failed!\n");
875 MultiByteToWideChar(CP_ACP
, 0, pszPrinter
, -1, pwszPrinter
, cch
+ 1);
878 bReturnValue
= SetDefaultPrinterW(pwszPrinter
);
882 HeapFree(hProcessHeap
, 0, pwszPrinter
);
888 SetDefaultPrinterW(LPCWSTR pszPrinter
)
890 const WCHAR wszDevicesKey
[] = L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
892 DWORD cbDeviceValueData
;
893 DWORD cbPrinterValueData
= 0;
896 HKEY hDevicesKey
= NULL
;
897 HKEY hWindowsKey
= NULL
;
898 PWSTR pwszDeviceValueData
= NULL
;
899 WCHAR wszPrinter
[MAX_PRINTER_NAME
+ 1];
901 TRACE("SetDefaultPrinterW(%S)\n", pszPrinter
);
903 // Open the Devices registry key.
904 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_CURRENT_USER
, wszDevicesKey
, 0, KEY_READ
, &hDevicesKey
);
905 if (dwErrorCode
!= ERROR_SUCCESS
)
907 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
911 // Did the caller give us a printer to set as default?
912 if (pszPrinter
&& *pszPrinter
)
914 // Check if the given printer exists and query the value data size.
915 dwErrorCode
= (DWORD
)RegQueryValueExW(hDevicesKey
, pszPrinter
, NULL
, NULL
, NULL
, &cbPrinterValueData
);
916 if (dwErrorCode
== ERROR_FILE_NOT_FOUND
)
918 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
921 else if (dwErrorCode
!= ERROR_SUCCESS
)
923 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
927 cchPrinter
= wcslen(pszPrinter
);
931 // If there is already a default printer, we're done!
932 cchPrinter
= _countof(wszPrinter
);
933 if (GetDefaultPrinterW(wszPrinter
, &cchPrinter
))
935 dwErrorCode
= ERROR_SUCCESS
;
939 // Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
940 cchPrinter
= _countof(wszPrinter
);
941 dwErrorCode
= (DWORD
)RegEnumValueW(hDevicesKey
, 0, wszPrinter
, &cchPrinter
, NULL
, NULL
, NULL
, &cbPrinterValueData
);
942 if (dwErrorCode
!= ERROR_MORE_DATA
)
945 pszPrinter
= wszPrinter
;
948 // We now need to query the value data, which has the format "winspool,<Port>:"
949 // and make "<Printer Name>,winspool,<Port>:" out of it.
950 // Allocate a buffer large enough for the final data.
951 cbDeviceValueData
= (cchPrinter
+ 1) * sizeof(WCHAR
) + cbPrinterValueData
;
952 pwszDeviceValueData
= HeapAlloc(hProcessHeap
, 0, cbDeviceValueData
);
953 if (!pwszDeviceValueData
)
955 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
956 ERR("HeapAlloc failed!\n");
960 // Copy the Printer Name and a comma into it.
961 CopyMemory(pwszDeviceValueData
, pszPrinter
, cchPrinter
* sizeof(WCHAR
));
962 pwszDeviceValueData
[cchPrinter
] = L
',';
964 // Append the value data, which has the format "winspool,<Port>:"
965 dwErrorCode
= (DWORD
)RegQueryValueExW(hDevicesKey
, pszPrinter
, NULL
, NULL
, (PBYTE
)&pwszDeviceValueData
[cchPrinter
+ 1], &cbPrinterValueData
);
966 if (dwErrorCode
!= ERROR_SUCCESS
)
969 // Open the Windows registry key.
970 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_CURRENT_USER
, wszWindowsKey
, 0, KEY_SET_VALUE
, &hWindowsKey
);
971 if (dwErrorCode
!= ERROR_SUCCESS
)
973 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
977 // Store our new default printer.
978 dwErrorCode
= (DWORD
)RegSetValueExW(hWindowsKey
, wszDeviceValue
, 0, REG_SZ
, (PBYTE
)pwszDeviceValueData
, cbDeviceValueData
);
979 if (dwErrorCode
!= ERROR_SUCCESS
)
981 ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode
);
987 RegCloseKey(hDevicesKey
);
990 RegCloseKey(hWindowsKey
);
992 if (pwszDeviceValueData
)
993 HeapFree(hProcessHeap
, 0, pwszDeviceValueData
);
995 SetLastError(dwErrorCode
);
996 return (dwErrorCode
== ERROR_SUCCESS
);
1000 SetPrinterA(HANDLE hPrinter
, DWORD Level
, PBYTE pPrinter
, DWORD Command
)
1002 TRACE("SetPrinterA(%p, %lu, %p, %lu)\n", hPrinter
, Level
, pPrinter
, Command
);
1008 SetPrinterW(HANDLE hPrinter
, DWORD Level
, PBYTE pPrinter
, DWORD Command
)
1010 TRACE("SetPrinterW(%p, %lu, %p, %lu)\n", hPrinter
, Level
, pPrinter
, Command
);
1016 StartDocPrinterA(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
1018 DOC_INFO_1W wDocInfo1
= { 0 };
1021 DWORD dwReturnValue
= 0;
1022 PDOC_INFO_1A pDocInfo1
= (PDOC_INFO_1A
)pDocInfo
;
1024 TRACE("StartDocPrinterA(%p, %lu, %p)\n", hPrinter
, Level
, pDocInfo
);
1026 // Only check the minimum required for accessing pDocInfo.
1027 // Additional sanity checks are done in StartDocPrinterW.
1030 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1036 dwErrorCode
= ERROR_INVALID_LEVEL
;
1040 if (pDocInfo1
->pDatatype
)
1042 // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
1043 cch
= strlen(pDocInfo1
->pDatatype
);
1045 wDocInfo1
.pDatatype
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
1046 if (!wDocInfo1
.pDatatype
)
1048 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1049 ERR("HeapAlloc failed!\n");
1053 MultiByteToWideChar(CP_ACP
, 0, pDocInfo1
->pDatatype
, -1, wDocInfo1
.pDatatype
, cch
+ 1);
1056 if (pDocInfo1
->pDocName
)
1058 // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
1059 cch
= strlen(pDocInfo1
->pDocName
);
1061 wDocInfo1
.pDocName
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
1062 if (!wDocInfo1
.pDocName
)
1064 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1065 ERR("HeapAlloc failed!\n");
1069 MultiByteToWideChar(CP_ACP
, 0, pDocInfo1
->pDocName
, -1, wDocInfo1
.pDocName
, cch
+ 1);
1072 if (pDocInfo1
->pOutputFile
)
1074 // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
1075 cch
= strlen(pDocInfo1
->pOutputFile
);
1077 wDocInfo1
.pOutputFile
= HeapAlloc(hProcessHeap
, 0, (cch
+ 1) * sizeof(WCHAR
));
1078 if (!wDocInfo1
.pOutputFile
)
1080 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1081 ERR("HeapAlloc failed!\n");
1085 MultiByteToWideChar(CP_ACP
, 0, pDocInfo1
->pOutputFile
, -1, wDocInfo1
.pOutputFile
, cch
+ 1);
1088 dwReturnValue
= StartDocPrinterW(hPrinter
, Level
, (PBYTE
)&wDocInfo1
);
1089 dwErrorCode
= GetLastError();
1092 if (wDocInfo1
.pDatatype
)
1093 HeapFree(hProcessHeap
, 0, wDocInfo1
.pDatatype
);
1095 if (wDocInfo1
.pDocName
)
1096 HeapFree(hProcessHeap
, 0, wDocInfo1
.pDocName
);
1098 if (wDocInfo1
.pOutputFile
)
1099 HeapFree(hProcessHeap
, 0, wDocInfo1
.pOutputFile
);
1101 SetLastError(dwErrorCode
);
1102 return dwReturnValue
;
1106 StartDocPrinterW(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
1108 DWORD cbAddJobInfo1
;
1111 DWORD dwReturnValue
= 0;
1112 PADDJOB_INFO_1W pAddJobInfo1
= NULL
;
1113 PDOC_INFO_1W pDocInfo1
= (PDOC_INFO_1W
)pDocInfo
;
1114 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
1116 TRACE("StartDocPrinterW(%p, %lu, %p)\n", hPrinter
, Level
, pDocInfo
);
1121 dwErrorCode
= ERROR_INVALID_HANDLE
;
1127 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1133 dwErrorCode
= ERROR_INVALID_LEVEL
;
1137 if (pHandle
->bStartedDoc
)
1139 dwErrorCode
= ERROR_INVALID_PRINTER_STATE
;
1143 // Check if we want to redirect output into a file.
1144 if (pDocInfo1
->pOutputFile
)
1146 // Do a StartDocPrinter RPC call in this case.
1147 dwErrorCode
= _StartDocPrinterWithRPC(pHandle
, pDocInfo1
);
1151 // Allocate memory for the ADDJOB_INFO_1W structure and a path.
1152 cbAddJobInfo1
= sizeof(ADDJOB_INFO_1W
) + MAX_PATH
* sizeof(WCHAR
);
1153 pAddJobInfo1
= HeapAlloc(hProcessHeap
, 0, cbAddJobInfo1
);
1156 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1157 ERR("HeapAlloc failed!\n");
1161 // Try to add a new job.
1162 // This only succeeds if the printer is set to do spooled printing.
1163 if (AddJobW((HANDLE
)pHandle
, 1, (PBYTE
)pAddJobInfo1
, cbAddJobInfo1
, &cbNeeded
))
1165 // Do spooled printing.
1166 dwErrorCode
= _StartDocPrinterSpooled(pHandle
, pDocInfo1
, pAddJobInfo1
);
1168 else if (GetLastError() == ERROR_INVALID_ACCESS
)
1170 // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
1171 // In this case, we do a StartDocPrinter RPC call.
1172 dwErrorCode
= _StartDocPrinterWithRPC(pHandle
, pDocInfo1
);
1176 dwErrorCode
= GetLastError();
1177 ERR("AddJobW failed with error %lu!\n", dwErrorCode
);
1182 if (dwErrorCode
== ERROR_SUCCESS
)
1184 pHandle
->bStartedDoc
= TRUE
;
1185 dwReturnValue
= pHandle
->dwJobID
;
1190 HeapFree(hProcessHeap
, 0, pAddJobInfo1
);
1192 SetLastError(dwErrorCode
);
1193 return dwReturnValue
;
1197 StartPagePrinter(HANDLE hPrinter
)
1200 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
1202 TRACE("StartPagePrinter(%p)\n", hPrinter
);
1207 dwErrorCode
= ERROR_INVALID_HANDLE
;
1214 dwErrorCode
= _RpcStartPagePrinter(pHandle
->hPrinter
);
1216 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
1218 dwErrorCode
= RpcExceptionCode();
1219 ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode
);
1224 SetLastError(dwErrorCode
);
1225 return (dwErrorCode
== ERROR_SUCCESS
);
1229 WritePrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
)
1232 PSPOOLER_HANDLE pHandle
= (PSPOOLER_HANDLE
)hPrinter
;
1234 TRACE("WritePrinter(%p, %p, %lu, %p)\n", hPrinter
, pBuf
, cbBuf
, pcWritten
);
1239 dwErrorCode
= ERROR_INVALID_HANDLE
;
1243 if (!pHandle
->bStartedDoc
)
1245 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1249 if (pHandle
->hSPLFile
!= INVALID_HANDLE_VALUE
)
1251 // Write to the spool file. This doesn't need an RPC request.
1252 if (!WriteFile(pHandle
->hSPLFile
, pBuf
, cbBuf
, pcWritten
, NULL
))
1254 dwErrorCode
= GetLastError();
1255 ERR("WriteFile failed with error %lu!\n", dwErrorCode
);
1259 dwErrorCode
= ERROR_SUCCESS
;
1263 // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
1264 // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
1269 dwErrorCode
= _RpcWritePrinter(pHandle
->hPrinter
, pBuf
, cbBuf
, pcWritten
);
1271 RpcExcept(EXCEPTION_EXECUTE_HANDLER
)
1273 dwErrorCode
= RpcExceptionCode();
1274 ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode
);
1280 SetLastError(dwErrorCode
);
1281 return (dwErrorCode
== ERROR_SUCCESS
);
1285 XcvDataW(HANDLE hXcv
, PCWSTR pszDataName
, PBYTE pInputData
, DWORD cbInputData
, PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
, PDWORD pdwStatus
)
1287 TRACE("XcvDataW(%p, %S, %p, %lu, %p, %lu, %p, %p)\n", hXcv
, pszDataName
, pInputData
, cbInputData
, pOutputData
, cbOutputData
, pcbOutputNeeded
, pdwStatus
);