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