1 /*
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-2018 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 #include <marshalling/printers.h>
10 #include <marshalling/printerdrivers.h>
11 
12 // Local Constants
13 
14 /** And the award for the most confusingly named setting goes to "Device", for storing the default printer of the current user.
15     Ok, I admit that this has historical reasons. It's still not straightforward in any way though! */
16 static const WCHAR wszWindowsKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
17 static const WCHAR wszDeviceValue[] = L"Device";
18 
19 static DWORD
20 _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB_INFO_1W pAddJobInfo1)
21 {
22     DWORD cbNeeded;
23     DWORD dwErrorCode;
24     PJOB_INFO_1W pJobInfo1 = NULL;
25 
26     // Create the spool file.
27     pHandle->hSPLFile = CreateFileW(pAddJobInfo1->Path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
28     if (pHandle->hSPLFile == INVALID_HANDLE_VALUE)
29     {
30         dwErrorCode = GetLastError();
31         ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1->Path, dwErrorCode);
32         goto Cleanup;
33     }
34 
35     // Get the size of the job information.
36     GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
37     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
38     {
39         dwErrorCode = GetLastError();
40         ERR("GetJobW failed with error %lu!\n", dwErrorCode);
41         goto Cleanup;
42     }
43 
44     // Allocate enough memory for the returned job information.
45     pJobInfo1 = HeapAlloc(hProcessHeap, 0, cbNeeded);
46     if (!pJobInfo1)
47     {
48         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
49         ERR("HeapAlloc failed!\n");
50         goto Cleanup;
51     }
52 
53     // Get the job information.
54     if (!GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
55     {
56         dwErrorCode = GetLastError();
57         ERR("GetJobW failed with error %lu!\n", dwErrorCode);
58         goto Cleanup;
59     }
60 
61     // Add our document information.
62     if (pDocInfo1->pDatatype)
63         pJobInfo1->pDatatype = pDocInfo1->pDatatype;
64 
65     pJobInfo1->pDocument = pDocInfo1->pDocName;
66 
67     // Set the new job information.
68     if (!SetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
69     {
70         dwErrorCode = GetLastError();
71         ERR("SetJobW failed with error %lu!\n", dwErrorCode);
72         goto Cleanup;
73     }
74 
75     // We were successful!
76     pHandle->dwJobID = pAddJobInfo1->JobId;
77     dwErrorCode = ERROR_SUCCESS;
78 
79 Cleanup:
80     if (pJobInfo1)
81         HeapFree(hProcessHeap, 0, pJobInfo1);
82 
83     return dwErrorCode;
84 }
85 
86 static DWORD
87 _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
88 {
89     DWORD dwErrorCode;
90     WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer;
91 
92     DocInfoContainer.Level = 1;
93     DocInfoContainer.DocInfo.pDocInfo1 = (WINSPOOL_DOC_INFO_1*)pDocInfo1;
94 
95     RpcTryExcept
96     {
97         dwErrorCode = _RpcStartDocPrinter(pHandle->hPrinter, &DocInfoContainer, &pHandle->dwJobID);
98     }
99     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
100     {
101         dwErrorCode = RpcExceptionCode();
102         ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode);
103     }
104     RpcEndExcept;
105 
106     return dwErrorCode;
107 }
108 
109 BOOL WINAPI
110 AbortPrinter(HANDLE hPrinter)
111 {
112     TRACE("AbortPrinter(%p)\n", hPrinter);
113     UNIMPLEMENTED;
114     return FALSE;
115 }
116 
117 HANDLE WINAPI
118 AddPrinterA(PSTR pName, DWORD Level, PBYTE pPrinter)
119 {
120     TRACE("AddPrinterA(%s, %lu, %p)\n", pName, Level, pPrinter);
121     UNIMPLEMENTED;
122     return NULL;
123 }
124 
125 HANDLE WINAPI
126 AddPrinterW(PWSTR pName, DWORD Level, PBYTE pPrinter)
127 {
128     TRACE("AddPrinterW(%S, %lu, %p)\n", pName, Level, pPrinter);
129     UNIMPLEMENTED;
130     return NULL;
131 }
132 
133 BOOL WINAPI
134 ClosePrinter(HANDLE hPrinter)
135 {
136     DWORD dwErrorCode;
137     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
138 
139     TRACE("ClosePrinter(%p)\n", hPrinter);
140 
141     // Sanity checks.
142     if (!pHandle)
143     {
144         dwErrorCode = ERROR_INVALID_HANDLE;
145         goto Cleanup;
146     }
147 
148     // Do the RPC call.
149     RpcTryExcept
150     {
151         dwErrorCode = _RpcClosePrinter(&pHandle->hPrinter);
152     }
153     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
154     {
155         dwErrorCode = RpcExceptionCode();
156         ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode);
157     }
158     RpcEndExcept;
159 
160     // Close any open file handle.
161     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
162         CloseHandle(pHandle->hSPLFile);
163 
164     // Free the memory for the handle.
165     HeapFree(hProcessHeap, 0, pHandle);
166 
167 Cleanup:
168     SetLastError(dwErrorCode);
169     return (dwErrorCode == ERROR_SUCCESS);
170 }
171 
172 BOOL WINAPI
173 DeletePrinter(HANDLE hPrinter)
174 {
175     TRACE("DeletePrinter(%p)\n", hPrinter);
176     UNIMPLEMENTED;
177     return FALSE;
178 }
179 
180 DWORD WINAPI
181 DeviceCapabilitiesA(LPCSTR pDevice, LPCSTR pPort, WORD fwCapability, LPSTR pOutput, const DEVMODEA* pDevMode)
182 {
183     TRACE("DeviceCapabilitiesA(%s, %s, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
184     UNIMPLEMENTED;
185     return 0;
186 }
187 
188 DWORD WINAPI
189 DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort, WORD fwCapability, LPWSTR pOutput, const DEVMODEW* pDevMode)
190 {
191     TRACE("DeviceCapabilitiesW(%S, %S, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
192     UNIMPLEMENTED;
193     return 0;
194 }
195 
196 LONG WINAPI
197 DocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName, PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput, DWORD fMode)
198 {
199     TRACE("DocumentPropertiesA(%p, %p, %s, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
200     UNIMPLEMENTED;
201     return -1;
202 }
203 
204 static PRINTER_INFO_9W * get_devmodeW(HANDLE hprn)
205 {
206     PRINTER_INFO_9W *pi9 = NULL;
207     DWORD needed = 0;
208     BOOL res;
209 
210     res = GetPrinterW(hprn, 9, NULL, 0, &needed);
211     if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
212     {
213         pi9 = HeapAlloc(hProcessHeap, 0, needed);
214         res = GetPrinterW(hprn, 9, (LPBYTE)pi9, needed, &needed);
215     }
216 
217     if (res)
218         return pi9;
219 
220     ERR("GetPrinterW failed with %u\n", GetLastError());
221     HeapFree(hProcessHeap, 0, pi9);
222     return NULL;
223 }
224 
225 LONG WINAPI
226 DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput, DWORD fMode)
227 {
228     HANDLE hUseHandle = NULL;
229     PRINTER_INFO_9W *pi9 = NULL;
230     LONG Result = -1, Length;
231 
232     TRACE("DocumentPropertiesW(%p, %p, %S, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
233     if (hPrinter)
234     {
235         hUseHandle = hPrinter;
236     }
237     else if (!OpenPrinterW(pDeviceName, &hUseHandle, NULL))
238     {
239         ERR("No handle, and no usable printer name passed in\n");
240         return -1;
241     }
242 
243     pi9 = get_devmodeW(hUseHandle);
244 
245     if (pi9)
246     {
247         Length = pi9->pDevMode->dmSize + pi9->pDevMode->dmDriverExtra;
248         // See wineps.drv PSDRV_ExtDeviceMode
249         if (fMode)
250         {
251             Result = 1; /* IDOK */
252 
253             if (fMode & DM_IN_BUFFER)
254             {
255                 FIXME("Merge pDevModeInput with pi9, write back to driver!\n");
256                 // See wineps.drv PSDRV_MergeDevmodes
257             }
258 
259             if (fMode & DM_IN_PROMPT)
260             {
261                 FIXME("Show property sheet!\n");
262                 Result = 2; /* IDCANCEL */
263             }
264 
265             if (fMode & (DM_OUT_BUFFER | DM_OUT_DEFAULT))
266             {
267                 if (pDevModeOutput)
268                 {
269                     memcpy(pDevModeOutput, pi9->pDevMode, pi9->pDevMode->dmSize + pi9->pDevMode->dmDriverExtra);
270                 }
271                 else
272                 {
273                     ERR("No pDevModeOutput\n");
274                     Result = -1;
275                 }
276             }
277         }
278         else
279         {
280             Result = Length;
281         }
282 
283         HeapFree(hProcessHeap, 0, pi9);
284     }
285 
286     if (hUseHandle && !hPrinter)
287         ClosePrinter(hUseHandle);
288     return Result;
289 }
290 
291 BOOL WINAPI
292 EndDocPrinter(HANDLE hPrinter)
293 {
294     DWORD dwErrorCode;
295     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
296 
297     TRACE("EndDocPrinter(%p)\n", hPrinter);
298 
299     // Sanity checks.
300     if (!pHandle)
301     {
302         dwErrorCode = ERROR_INVALID_HANDLE;
303         goto Cleanup;
304     }
305 
306     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
307     {
308         // For spooled jobs, the document is finished by calling _RpcScheduleJob.
309         RpcTryExcept
310         {
311             dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, pHandle->dwJobID);
312         }
313         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
314         {
315             dwErrorCode = RpcExceptionCode();
316             ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
317         }
318         RpcEndExcept;
319 
320         // Close the spool file handle.
321         CloseHandle(pHandle->hSPLFile);
322     }
323     else
324     {
325         // In all other cases, just call _RpcEndDocPrinter.
326         RpcTryExcept
327         {
328             dwErrorCode = _RpcEndDocPrinter(pHandle->hPrinter);
329         }
330         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
331         {
332             dwErrorCode = RpcExceptionCode();
333             ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode);
334         }
335         RpcEndExcept;
336     }
337 
338     // A new document can now be started again.
339     pHandle->bStartedDoc = FALSE;
340 
341 Cleanup:
342     SetLastError(dwErrorCode);
343     return (dwErrorCode == ERROR_SUCCESS);
344 }
345 
346 BOOL WINAPI
347 EndPagePrinter(HANDLE hPrinter)
348 {
349     DWORD dwErrorCode;
350     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
351 
352     TRACE("EndPagePrinter(%p)\n", hPrinter);
353 
354     // Sanity checks.
355     if (!pHandle)
356     {
357         dwErrorCode = ERROR_INVALID_HANDLE;
358         goto Cleanup;
359     }
360 
361     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
362     {
363         // For spooled jobs, we don't need to do anything.
364         dwErrorCode = ERROR_SUCCESS;
365     }
366     else
367     {
368         // In all other cases, just call _RpcEndPagePrinter.
369         RpcTryExcept
370         {
371             dwErrorCode = _RpcEndPagePrinter(pHandle->hPrinter);
372         }
373         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
374         {
375             dwErrorCode = RpcExceptionCode();
376             ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode);
377         }
378         RpcEndExcept;
379     }
380 
381 Cleanup:
382     SetLastError(dwErrorCode);
383     return (dwErrorCode == ERROR_SUCCESS);
384 }
385 
386 BOOL WINAPI
387 EnumPrintersA(DWORD Flags, PSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
388 {
389     TRACE("EnumPrintersA(%lu, %s, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
390     return FALSE;
391 }
392 
393 BOOL WINAPI
394 EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
395 {
396     DWORD dwErrorCode;
397 
398     TRACE("EnumPrintersW(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
399 
400     // Dismiss invalid levels already at this point.
401     if (Level == 3 || Level > 5)
402     {
403         dwErrorCode = ERROR_INVALID_LEVEL;
404         goto Cleanup;
405     }
406 
407     if (cbBuf && pPrinterEnum)
408         ZeroMemory(pPrinterEnum, cbBuf);
409 
410     // Do the RPC call
411     RpcTryExcept
412     {
413         dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
414     }
415     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
416     {
417         dwErrorCode = RpcExceptionCode();
418         ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
419     }
420     RpcEndExcept;
421 
422     if (dwErrorCode == ERROR_SUCCESS)
423     {
424         // Replace relative offset addresses in the output by absolute pointers.
425         ASSERT(Level <= 9);
426         MarshallUpStructuresArray(cbBuf, pPrinterEnum, *pcReturned, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
427     }
428 
429 Cleanup:
430     SetLastError(dwErrorCode);
431     return (dwErrorCode == ERROR_SUCCESS);
432 }
433 
434 BOOL WINAPI
435 FlushPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten, DWORD cSleep)
436 {
437     TRACE("FlushPrinter(%p, %p, %lu, %p, %lu)\n", hPrinter, pBuf, cbBuf, pcWritten, cSleep);
438     UNIMPLEMENTED;
439     return FALSE;
440 }
441 
442 BOOL WINAPI
443 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
444 {
445     DWORD dwErrorCode;
446     PWSTR pwszBuffer = NULL;
447 
448     TRACE("GetDefaultPrinterA(%p, %p)\n", pszBuffer, pcchBuffer);
449 
450     // Sanity check.
451     if (!pcchBuffer)
452     {
453         dwErrorCode = ERROR_INVALID_PARAMETER;
454         goto Cleanup;
455     }
456 
457     // Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
458     if (pszBuffer && *pcchBuffer)
459     {
460         pwszBuffer = HeapAlloc(hProcessHeap, 0, *pcchBuffer * sizeof(WCHAR));
461         if (!pwszBuffer)
462         {
463             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
464             ERR("HeapAlloc failed!\n");
465             goto Cleanup;
466         }
467     }
468 
469     if (!GetDefaultPrinterW(pwszBuffer, pcchBuffer))
470     {
471         dwErrorCode = GetLastError();
472         goto Cleanup;
473     }
474 
475     // We successfully got a string in pwszBuffer, so convert the Unicode string to ANSI.
476     WideCharToMultiByte(CP_ACP, 0, pwszBuffer, -1, pszBuffer, *pcchBuffer, NULL, NULL);
477 
478     dwErrorCode = ERROR_SUCCESS;
479 
480 Cleanup:
481     if (pwszBuffer)
482         HeapFree(hProcessHeap, 0, pwszBuffer);
483 
484     SetLastError(dwErrorCode);
485     return (dwErrorCode == ERROR_SUCCESS);
486 }
487 
488 BOOL WINAPI
489 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
490 {
491     DWORD cbNeeded;
492     DWORD cchInputBuffer;
493     DWORD dwErrorCode;
494     HKEY hWindowsKey = NULL;
495     PWSTR pwszDevice = NULL;
496     PWSTR pwszComma;
497 
498     TRACE("GetDefaultPrinterW(%p, %p)\n", pszBuffer, pcchBuffer);
499 
500     // Sanity check.
501     if (!pcchBuffer)
502     {
503         dwErrorCode = ERROR_INVALID_PARAMETER;
504         goto Cleanup;
505     }
506 
507     cchInputBuffer = *pcchBuffer;
508 
509     // Open the registry key where the default printer for the current user is stored.
510     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_READ, &hWindowsKey);
511     if (dwErrorCode != ERROR_SUCCESS)
512     {
513         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
514         goto Cleanup;
515     }
516 
517     // Determine the size of the required buffer.
518     dwErrorCode = (DWORD)RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, NULL, &cbNeeded);
519     if (dwErrorCode != ERROR_SUCCESS)
520     {
521         ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
522         goto Cleanup;
523     }
524 
525     // Allocate it.
526     pwszDevice = HeapAlloc(hProcessHeap, 0, cbNeeded);
527     if (!pwszDevice)
528     {
529         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
530         ERR("HeapAlloc failed!\n");
531         goto Cleanup;
532     }
533 
534     // Now get the actual value.
535     dwErrorCode = RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, (PBYTE)pwszDevice, &cbNeeded);
536     if (dwErrorCode != ERROR_SUCCESS)
537     {
538         ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
539         goto Cleanup;
540     }
541 
542     // We get a string "<Printer Name>,winspool,<Port>:".
543     // Extract the printer name from it.
544     pwszComma = wcschr(pwszDevice, L',');
545     if (!pwszComma)
546     {
547         ERR("Found no or invalid default printer: %S!\n", pwszDevice);
548         dwErrorCode = ERROR_INVALID_NAME;
549         goto Cleanup;
550     }
551 
552     // Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
553     *pcchBuffer = pwszComma - pwszDevice + 1;
554 
555     // Check if the supplied buffer is large enough.
556     if (cchInputBuffer < *pcchBuffer)
557     {
558         dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
559         goto Cleanup;
560     }
561 
562     // Copy the default printer.
563     *pwszComma = 0;
564     CopyMemory(pszBuffer, pwszDevice, *pcchBuffer * sizeof(WCHAR));
565 
566     dwErrorCode = ERROR_SUCCESS;
567 
568 Cleanup:
569     if (hWindowsKey)
570         RegCloseKey(hWindowsKey);
571 
572     if (pwszDevice)
573         HeapFree(hProcessHeap, 0, pwszDevice);
574 
575     SetLastError(dwErrorCode);
576     return (dwErrorCode == ERROR_SUCCESS);
577 }
578 
579 BOOL WINAPI
580 GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
581 {
582     TRACE("GetPrinterA(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
583     return FALSE;
584 }
585 
586 BOOL WINAPI
587 GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
588 {
589     TRACE("GetPrinterDriverA(%p, %s, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
590     return FALSE;
591 }
592 
593 BOOL WINAPI
594 GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
595 {
596     DWORD dwErrorCode;
597     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
598 
599     TRACE("GetPrinterDriverW(%p, %S, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
600 
601     // Sanity checks.
602     if (!pHandle)
603     {
604         dwErrorCode = ERROR_INVALID_HANDLE;
605         goto Cleanup;
606     }
607 
608     // Dismiss invalid levels already at this point.
609     if (Level > 8 || Level < 1)
610     {
611         dwErrorCode = ERROR_INVALID_LEVEL;
612         goto Cleanup;
613     }
614 
615     if (cbBuf && pDriverInfo)
616         ZeroMemory(pDriverInfo, cbBuf);
617 
618     // Do the RPC call
619     RpcTryExcept
620     {
621         dwErrorCode = _RpcGetPrinterDriver(pHandle->hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
622     }
623     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
624     {
625         dwErrorCode = RpcExceptionCode();
626         ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
627     }
628     RpcEndExcept;
629 
630     if (dwErrorCode == ERROR_SUCCESS)
631     {
632         // Replace relative offset addresses in the output by absolute pointers.
633         ASSERT(Level <= 3);
634         MarshallUpStructure(cbBuf, pDriverInfo, pPrinterDriverMarshalling[Level]->pInfo, pPrinterDriverMarshalling[Level]->cbStructureSize, TRUE);
635     }
636 
637 Cleanup:
638     SetLastError(dwErrorCode);
639     return (dwErrorCode == ERROR_SUCCESS);
640 }
641 
642 BOOL WINAPI
643 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
644 {
645     DWORD dwErrorCode;
646     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
647 
648     TRACE("GetPrinterW(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
649 
650     // Sanity checks.
651     if (!pHandle)
652     {
653         dwErrorCode = ERROR_INVALID_HANDLE;
654         goto Cleanup;
655     }
656 
657     // Dismiss invalid levels already at this point.
658     if (Level > 9)
659     {
660         dwErrorCode = ERROR_INVALID_LEVEL;
661         goto Cleanup;
662     }
663 
664     if (cbBuf && pPrinter)
665         ZeroMemory(pPrinter, cbBuf);
666 
667     // Do the RPC call
668     RpcTryExcept
669     {
670         dwErrorCode = _RpcGetPrinter(pHandle->hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
671     }
672     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
673     {
674         dwErrorCode = RpcExceptionCode();
675         ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
676     }
677     RpcEndExcept;
678 
679     if (dwErrorCode == ERROR_SUCCESS)
680     {
681         // Replace relative offset addresses in the output by absolute pointers.
682         ASSERT(Level <= 9);
683         MarshallUpStructure(cbBuf, pPrinter, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
684     }
685 
686 Cleanup:
687     SetLastError(dwErrorCode);
688     return (dwErrorCode == ERROR_SUCCESS);
689 }
690 
691 BOOL WINAPI
692 OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
693 {
694     BOOL bReturnValue = FALSE;
695     DWORD cch;
696     PWSTR pwszPrinterName = NULL;
697     PRINTER_DEFAULTSW wDefault = { 0 };
698 
699     TRACE("OpenPrinterA(%s, %p, %p)\n", pPrinterName, phPrinter, pDefault);
700 
701     if (pPrinterName)
702     {
703         // Convert pPrinterName to a Unicode string pwszPrinterName
704         cch = strlen(pPrinterName);
705 
706         pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
707         if (!pwszPrinterName)
708         {
709             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
710             ERR("HeapAlloc failed!\n");
711             goto Cleanup;
712         }
713 
714         MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
715     }
716 
717     if (pDefault)
718     {
719         wDefault.DesiredAccess = pDefault->DesiredAccess;
720 
721         if (pDefault->pDatatype)
722         {
723             // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
724             cch = strlen(pDefault->pDatatype);
725 
726             wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
727             if (!wDefault.pDatatype)
728             {
729                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
730                 ERR("HeapAlloc failed!\n");
731                 goto Cleanup;
732             }
733 
734             MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
735         }
736 
737         if (pDefault->pDevMode)
738             wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
739     }
740 
741     bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
742 
743 Cleanup:
744     if (wDefault.pDatatype)
745         HeapFree(hProcessHeap, 0, wDefault.pDatatype);
746 
747     if (wDefault.pDevMode)
748         HeapFree(hProcessHeap, 0, wDefault.pDevMode);
749 
750     if (pwszPrinterName)
751         HeapFree(hProcessHeap, 0, pwszPrinterName);
752 
753     return bReturnValue;
754 }
755 
756 BOOL WINAPI
757 OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
758 {
759     DWORD dwErrorCode;
760     HANDLE hPrinter;
761     PSPOOLER_HANDLE pHandle;
762     PWSTR pDatatype = NULL;
763     WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
764     ACCESS_MASK AccessRequired = 0;
765 
766     TRACE("OpenPrinterW(%S, %p, %p)\n", pPrinterName, phPrinter, pDefault);
767 
768     // Sanity check
769     if (!phPrinter)
770     {
771         dwErrorCode = ERROR_INVALID_PARAMETER;
772         goto Cleanup;
773     }
774 
775     // Prepare the additional parameters in the format required by _RpcOpenPrinter
776     if (pDefault)
777     {
778         pDatatype = pDefault->pDatatype;
779         DevModeContainer.cbBuf = sizeof(DEVMODEW);
780         DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
781         AccessRequired = pDefault->DesiredAccess;
782     }
783 
784     // Do the RPC call
785     RpcTryExcept
786     {
787         dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
788     }
789     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
790     {
791         dwErrorCode = RpcExceptionCode();
792         ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
793     }
794     RpcEndExcept;
795 
796     if (dwErrorCode == ERROR_SUCCESS)
797     {
798         // Create a new SPOOLER_HANDLE structure.
799         pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
800         if (!pHandle)
801         {
802             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
803             ERR("HeapAlloc failed!\n");
804             goto Cleanup;
805         }
806 
807         pHandle->hPrinter = hPrinter;
808         pHandle->hSPLFile = INVALID_HANDLE_VALUE;
809 
810         // Return it as phPrinter.
811         *phPrinter = (HANDLE)pHandle;
812     }
813 
814 Cleanup:
815     SetLastError(dwErrorCode);
816     return (dwErrorCode == ERROR_SUCCESS);
817 }
818 
819 BOOL WINAPI
820 ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
821 {
822     DWORD dwErrorCode;
823     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
824 
825     TRACE("ReadPrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pNoBytesRead);
826 
827     // Sanity checks.
828     if (!pHandle)
829     {
830         dwErrorCode = ERROR_INVALID_HANDLE;
831         goto Cleanup;
832     }
833 
834     // Do the RPC call
835     RpcTryExcept
836     {
837         dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
838     }
839     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
840     {
841         dwErrorCode = RpcExceptionCode();
842         ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
843     }
844     RpcEndExcept;
845 
846 Cleanup:
847     SetLastError(dwErrorCode);
848     return (dwErrorCode == ERROR_SUCCESS);
849 }
850 
851 BOOL WINAPI
852 ResetPrinterA(HANDLE hPrinter, PPRINTER_DEFAULTSA pDefault)
853 {
854     TRACE("ResetPrinterA(%p, %p)\n", hPrinter, pDefault);
855     UNIMPLEMENTED;
856     return FALSE;
857 }
858 
859 BOOL WINAPI
860 ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
861 {
862     TRACE("ResetPrinterW(%p, %p)\n", hPrinter, pDefault);
863     UNIMPLEMENTED;
864     return FALSE;
865 }
866 
867 BOOL WINAPI
868 SetDefaultPrinterA(LPCSTR pszPrinter)
869 {
870     BOOL bReturnValue = FALSE;
871     DWORD cch;
872     PWSTR pwszPrinter = NULL;
873 
874     TRACE("SetDefaultPrinterA(%s)\n", pszPrinter);
875 
876     if (pszPrinter)
877     {
878         // Convert pszPrinter to a Unicode string pwszPrinter
879         cch = strlen(pszPrinter);
880 
881         pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
882         if (!pwszPrinter)
883         {
884             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
885             ERR("HeapAlloc failed!\n");
886             goto Cleanup;
887         }
888 
889         MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, pwszPrinter, cch + 1);
890     }
891 
892     bReturnValue = SetDefaultPrinterW(pwszPrinter);
893 
894 Cleanup:
895     if (pwszPrinter)
896         HeapFree(hProcessHeap, 0, pwszPrinter);
897 
898     return bReturnValue;
899 }
900 
901 BOOL WINAPI
902 SetDefaultPrinterW(LPCWSTR pszPrinter)
903 {
904     const WCHAR wszDevicesKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
905 
906     DWORD cbDeviceValueData;
907     DWORD cbPrinterValueData = 0;
908     DWORD cchPrinter;
909     DWORD dwErrorCode;
910     HKEY hDevicesKey = NULL;
911     HKEY hWindowsKey = NULL;
912     PWSTR pwszDeviceValueData = NULL;
913     WCHAR wszPrinter[MAX_PRINTER_NAME + 1];
914 
915     TRACE("SetDefaultPrinterW(%S)\n", pszPrinter);
916 
917     // Open the Devices registry key.
918     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszDevicesKey, 0, KEY_READ, &hDevicesKey);
919     if (dwErrorCode != ERROR_SUCCESS)
920     {
921         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
922         goto Cleanup;
923     }
924 
925     // Did the caller give us a printer to set as default?
926     if (pszPrinter && *pszPrinter)
927     {
928         // Check if the given printer exists and query the value data size.
929         dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, NULL, &cbPrinterValueData);
930         if (dwErrorCode == ERROR_FILE_NOT_FOUND)
931         {
932             dwErrorCode = ERROR_INVALID_PRINTER_NAME;
933             goto Cleanup;
934         }
935         else if (dwErrorCode != ERROR_SUCCESS)
936         {
937             ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
938             goto Cleanup;
939         }
940 
941         cchPrinter = wcslen(pszPrinter);
942     }
943     else
944     {
945         // If there is already a default printer, we're done!
946         cchPrinter = _countof(wszPrinter);
947         if (GetDefaultPrinterW(wszPrinter, &cchPrinter))
948         {
949             dwErrorCode = ERROR_SUCCESS;
950             goto Cleanup;
951         }
952 
953         // Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
954         cchPrinter = _countof(wszPrinter);
955         dwErrorCode = (DWORD)RegEnumValueW(hDevicesKey, 0, wszPrinter, &cchPrinter, NULL, NULL, NULL, &cbPrinterValueData);
956         if (dwErrorCode != ERROR_MORE_DATA)
957             goto Cleanup;
958 
959         pszPrinter = wszPrinter;
960     }
961 
962     // We now need to query the value data, which has the format "winspool,<Port>:"
963     // and make "<Printer Name>,winspool,<Port>:" out of it.
964     // Allocate a buffer large enough for the final data.
965     cbDeviceValueData = (cchPrinter + 1) * sizeof(WCHAR) + cbPrinterValueData;
966     pwszDeviceValueData = HeapAlloc(hProcessHeap, 0, cbDeviceValueData);
967     if (!pwszDeviceValueData)
968     {
969         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
970         ERR("HeapAlloc failed!\n");
971         goto Cleanup;
972     }
973 
974     // Copy the Printer Name and a comma into it.
975     CopyMemory(pwszDeviceValueData, pszPrinter, cchPrinter * sizeof(WCHAR));
976     pwszDeviceValueData[cchPrinter] = L',';
977 
978     // Append the value data, which has the format "winspool,<Port>:"
979     dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, (PBYTE)&pwszDeviceValueData[cchPrinter + 1], &cbPrinterValueData);
980     if (dwErrorCode != ERROR_SUCCESS)
981         goto Cleanup;
982 
983     // Open the Windows registry key.
984     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_SET_VALUE, &hWindowsKey);
985     if (dwErrorCode != ERROR_SUCCESS)
986     {
987         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
988         goto Cleanup;
989     }
990 
991     // Store our new default printer.
992     dwErrorCode = (DWORD)RegSetValueExW(hWindowsKey, wszDeviceValue, 0, REG_SZ, (PBYTE)pwszDeviceValueData, cbDeviceValueData);
993     if (dwErrorCode != ERROR_SUCCESS)
994     {
995         ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
996         goto Cleanup;
997     }
998 
999 Cleanup:
1000     if (hDevicesKey)
1001         RegCloseKey(hDevicesKey);
1002 
1003     if (hWindowsKey)
1004         RegCloseKey(hWindowsKey);
1005 
1006     if (pwszDeviceValueData)
1007         HeapFree(hProcessHeap, 0, pwszDeviceValueData);
1008 
1009     SetLastError(dwErrorCode);
1010     return (dwErrorCode == ERROR_SUCCESS);
1011 }
1012 
1013 BOOL WINAPI
1014 SetPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
1015 {
1016     TRACE("SetPrinterA(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
1017     UNIMPLEMENTED;
1018     return FALSE;
1019 }
1020 
1021 BOOL WINAPI
1022 SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
1023 {
1024     TRACE("SetPrinterW(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
1025     UNIMPLEMENTED;
1026     return FALSE;
1027 }
1028 
1029 DWORD WINAPI
1030 StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
1031 {
1032     DOC_INFO_1W wDocInfo1 = { 0 };
1033     DWORD cch;
1034     DWORD dwErrorCode;
1035     DWORD dwReturnValue = 0;
1036     PDOC_INFO_1A pDocInfo1 = (PDOC_INFO_1A)pDocInfo;
1037 
1038     TRACE("StartDocPrinterA(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
1039 
1040     // Only check the minimum required for accessing pDocInfo.
1041     // Additional sanity checks are done in StartDocPrinterW.
1042     if (!pDocInfo1)
1043     {
1044         dwErrorCode = ERROR_INVALID_PARAMETER;
1045         goto Cleanup;
1046     }
1047 
1048     if (Level != 1)
1049     {
1050         dwErrorCode = ERROR_INVALID_LEVEL;
1051         goto Cleanup;
1052     }
1053 
1054     if (pDocInfo1->pDatatype)
1055     {
1056         // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
1057         cch = strlen(pDocInfo1->pDatatype);
1058 
1059         wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1060         if (!wDocInfo1.pDatatype)
1061         {
1062             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1063             ERR("HeapAlloc failed!\n");
1064             goto Cleanup;
1065         }
1066 
1067         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDatatype, -1, wDocInfo1.pDatatype, cch + 1);
1068     }
1069 
1070     if (pDocInfo1->pDocName)
1071     {
1072         // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
1073         cch = strlen(pDocInfo1->pDocName);
1074 
1075         wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1076         if (!wDocInfo1.pDocName)
1077         {
1078             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1079             ERR("HeapAlloc failed!\n");
1080             goto Cleanup;
1081         }
1082 
1083         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDocName, -1, wDocInfo1.pDocName, cch + 1);
1084     }
1085 
1086     if (pDocInfo1->pOutputFile)
1087     {
1088         // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
1089         cch = strlen(pDocInfo1->pOutputFile);
1090 
1091         wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1092         if (!wDocInfo1.pOutputFile)
1093         {
1094             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1095             ERR("HeapAlloc failed!\n");
1096             goto Cleanup;
1097         }
1098 
1099         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pOutputFile, -1, wDocInfo1.pOutputFile, cch + 1);
1100     }
1101 
1102     dwReturnValue = StartDocPrinterW(hPrinter, Level, (PBYTE)&wDocInfo1);
1103     dwErrorCode = GetLastError();
1104 
1105 Cleanup:
1106     if (wDocInfo1.pDatatype)
1107         HeapFree(hProcessHeap, 0, wDocInfo1.pDatatype);
1108 
1109     if (wDocInfo1.pDocName)
1110         HeapFree(hProcessHeap, 0, wDocInfo1.pDocName);
1111 
1112     if (wDocInfo1.pOutputFile)
1113         HeapFree(hProcessHeap, 0, wDocInfo1.pOutputFile);
1114 
1115     SetLastError(dwErrorCode);
1116     return dwReturnValue;
1117 }
1118 
1119 DWORD WINAPI
1120 StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
1121 {
1122     DWORD cbAddJobInfo1;
1123     DWORD cbNeeded;
1124     DWORD dwErrorCode;
1125     DWORD dwReturnValue = 0;
1126     PADDJOB_INFO_1W pAddJobInfo1 = NULL;
1127     PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
1128     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1129 
1130     TRACE("StartDocPrinterW(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
1131 
1132     // Sanity checks.
1133     if (!pHandle)
1134     {
1135         dwErrorCode = ERROR_INVALID_HANDLE;
1136         goto Cleanup;
1137     }
1138 
1139     if (!pDocInfo1)
1140     {
1141         dwErrorCode = ERROR_INVALID_PARAMETER;
1142         goto Cleanup;
1143     }
1144 
1145     if (Level != 1)
1146     {
1147         dwErrorCode = ERROR_INVALID_LEVEL;
1148         goto Cleanup;
1149     }
1150 
1151     if (pHandle->bStartedDoc)
1152     {
1153         dwErrorCode = ERROR_INVALID_PRINTER_STATE;
1154         goto Cleanup;
1155     }
1156 
1157     // Check if we want to redirect output into a file.
1158     if (pDocInfo1->pOutputFile)
1159     {
1160         // Do a StartDocPrinter RPC call in this case.
1161         dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
1162     }
1163     else
1164     {
1165         // Allocate memory for the ADDJOB_INFO_1W structure and a path.
1166         cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
1167         pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
1168         if (!pAddJobInfo1)
1169         {
1170             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1171             ERR("HeapAlloc failed!\n");
1172             goto Cleanup;
1173         }
1174 
1175         // Try to add a new job.
1176         // This only succeeds if the printer is set to do spooled printing.
1177         if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
1178         {
1179             // Do spooled printing.
1180             dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
1181         }
1182         else if (GetLastError() == ERROR_INVALID_ACCESS)
1183         {
1184             // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
1185             // In this case, we do a StartDocPrinter RPC call.
1186             dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
1187         }
1188         else
1189         {
1190             dwErrorCode = GetLastError();
1191             ERR("AddJobW failed with error %lu!\n", dwErrorCode);
1192             goto Cleanup;
1193         }
1194     }
1195 
1196     if (dwErrorCode == ERROR_SUCCESS)
1197     {
1198         pHandle->bStartedDoc = TRUE;
1199         dwReturnValue = pHandle->dwJobID;
1200     }
1201 
1202 Cleanup:
1203     if (pAddJobInfo1)
1204         HeapFree(hProcessHeap, 0, pAddJobInfo1);
1205 
1206     SetLastError(dwErrorCode);
1207     return dwReturnValue;
1208 }
1209 
1210 BOOL WINAPI
1211 StartPagePrinter(HANDLE hPrinter)
1212 {
1213     DWORD dwErrorCode;
1214     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1215 
1216     TRACE("StartPagePrinter(%p)\n", hPrinter);
1217 
1218     // Sanity checks.
1219     if (!pHandle)
1220     {
1221         dwErrorCode = ERROR_INVALID_HANDLE;
1222         goto Cleanup;
1223     }
1224 
1225     // Do the RPC call
1226     RpcTryExcept
1227     {
1228         dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
1229     }
1230     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1231     {
1232         dwErrorCode = RpcExceptionCode();
1233         ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
1234     }
1235     RpcEndExcept;
1236 
1237 Cleanup:
1238     SetLastError(dwErrorCode);
1239     return (dwErrorCode == ERROR_SUCCESS);
1240 }
1241 
1242 BOOL WINAPI
1243 WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
1244 {
1245     DWORD dwErrorCode;
1246     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1247 
1248     TRACE("WritePrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
1249 
1250     // Sanity checks.
1251     if (!pHandle)
1252     {
1253         dwErrorCode = ERROR_INVALID_HANDLE;
1254         goto Cleanup;
1255     }
1256 
1257     if (!pHandle->bStartedDoc)
1258     {
1259         dwErrorCode = ERROR_SPL_NO_STARTDOC;
1260         goto Cleanup;
1261     }
1262 
1263     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
1264     {
1265         // Write to the spool file. This doesn't need an RPC request.
1266         if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
1267         {
1268             dwErrorCode = GetLastError();
1269             ERR("WriteFile failed with error %lu!\n", dwErrorCode);
1270             goto Cleanup;
1271         }
1272 
1273         dwErrorCode = ERROR_SUCCESS;
1274     }
1275     else
1276     {
1277         // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
1278         // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
1279 
1280         // Do the RPC call
1281         RpcTryExcept
1282         {
1283             dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
1284         }
1285         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1286         {
1287             dwErrorCode = RpcExceptionCode();
1288             ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
1289         }
1290         RpcEndExcept;
1291     }
1292 
1293 Cleanup:
1294     SetLastError(dwErrorCode);
1295     return (dwErrorCode == ERROR_SUCCESS);
1296 }
1297 
1298 BOOL WINAPI
1299 XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
1300 {
1301     TRACE("XcvDataW(%p, %S, %p, %lu, %p, %lu, %p, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus);
1302     return FALSE;
1303 }
1304