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 #include <strsafe.h>
12 
13 // Local Constants
14 
15 /** And the award for the most confusingly named setting goes to "Device", for storing the default printer of the current user.
16     Ok, I admit that this has historical reasons. It's still not straightforward in any way though! */
17 static const WCHAR wszWindowsKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
18 static const WCHAR wszDeviceValue[] = L"Device";
19 
20 static DWORD
21 _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB_INFO_1W pAddJobInfo1)
22 {
23     DWORD cbNeeded;
24     DWORD dwErrorCode;
25     PJOB_INFO_1W pJobInfo1 = NULL;
26 
27     // Create the spool file.
28     pHandle->hSPLFile = CreateFileW(pAddJobInfo1->Path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
29     if (pHandle->hSPLFile == INVALID_HANDLE_VALUE)
30     {
31         dwErrorCode = GetLastError();
32         ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1->Path, dwErrorCode);
33         goto Cleanup;
34     }
35 
36     // Get the size of the job information.
37     GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
38     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
39     {
40         dwErrorCode = GetLastError();
41         ERR("GetJobW failed with error %lu!\n", dwErrorCode);
42         goto Cleanup;
43     }
44 
45     // Allocate enough memory for the returned job information.
46     pJobInfo1 = HeapAlloc(hProcessHeap, 0, cbNeeded);
47     if (!pJobInfo1)
48     {
49         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
50         ERR("HeapAlloc failed!\n");
51         goto Cleanup;
52     }
53 
54     // Get the job information.
55     if (!GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
56     {
57         dwErrorCode = GetLastError();
58         ERR("GetJobW failed with error %lu!\n", dwErrorCode);
59         goto Cleanup;
60     }
61 
62     // Add our document information.
63     if (pDocInfo1->pDatatype)
64         pJobInfo1->pDatatype = pDocInfo1->pDatatype;
65 
66     pJobInfo1->pDocument = pDocInfo1->pDocName;
67 
68     // Set the new job information.
69     if (!SetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
70     {
71         dwErrorCode = GetLastError();
72         ERR("SetJobW failed with error %lu!\n", dwErrorCode);
73         goto Cleanup;
74     }
75 
76     // We were successful!
77     pHandle->dwJobID = pAddJobInfo1->JobId;
78     dwErrorCode = ERROR_SUCCESS;
79 
80 Cleanup:
81     if (pJobInfo1)
82         HeapFree(hProcessHeap, 0, pJobInfo1);
83 
84     return dwErrorCode;
85 }
86 
87 static DWORD
88 _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
89 {
90     DWORD dwErrorCode;
91     WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer;
92 
93     DocInfoContainer.Level = 1;
94     DocInfoContainer.DocInfo.pDocInfo1 = (WINSPOOL_DOC_INFO_1*)pDocInfo1;
95 
96     RpcTryExcept
97     {
98         dwErrorCode = _RpcStartDocPrinter(pHandle->hPrinter, &DocInfoContainer, &pHandle->dwJobID);
99     }
100     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
101     {
102         dwErrorCode = RpcExceptionCode();
103         ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode);
104     }
105     RpcEndExcept;
106 
107     return dwErrorCode;
108 }
109 
110 BOOL WINAPI
111 AbortPrinter(HANDLE hPrinter)
112 {
113     TRACE("AbortPrinter(%p)\n", hPrinter);
114     UNIMPLEMENTED;
115     return FALSE;
116 }
117 
118 HANDLE WINAPI
119 AddPrinterA(PSTR pName, DWORD Level, PBYTE pPrinter)
120 {
121     TRACE("AddPrinterA(%s, %lu, %p)\n", pName, Level, pPrinter);
122     UNIMPLEMENTED;
123     return NULL;
124 }
125 
126 HANDLE WINAPI
127 AddPrinterW(PWSTR pName, DWORD Level, PBYTE pPrinter)
128 {
129     TRACE("AddPrinterW(%S, %lu, %p)\n", pName, Level, pPrinter);
130     UNIMPLEMENTED;
131     return NULL;
132 }
133 
134 BOOL WINAPI
135 ClosePrinter(HANDLE hPrinter)
136 {
137     DWORD dwErrorCode;
138     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
139 
140     TRACE("ClosePrinter(%p)\n", hPrinter);
141 
142     // Sanity checks.
143     if (!pHandle)
144     {
145         dwErrorCode = ERROR_INVALID_HANDLE;
146         goto Cleanup;
147     }
148 
149     // Do the RPC call.
150     RpcTryExcept
151     {
152         dwErrorCode = _RpcClosePrinter(&pHandle->hPrinter);
153     }
154     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
155     {
156         dwErrorCode = RpcExceptionCode();
157         ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode);
158     }
159     RpcEndExcept;
160 
161     // Close any open file handle.
162     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
163         CloseHandle(pHandle->hSPLFile);
164 
165     // Free the memory for the handle.
166     HeapFree(hProcessHeap, 0, pHandle);
167 
168 Cleanup:
169     SetLastError(dwErrorCode);
170     return (dwErrorCode == ERROR_SUCCESS);
171 }
172 
173 BOOL WINAPI
174 DeletePrinter(HANDLE hPrinter)
175 {
176     TRACE("DeletePrinter(%p)\n", hPrinter);
177     UNIMPLEMENTED;
178     return FALSE;
179 }
180 
181 DWORD WINAPI
182 DeviceCapabilitiesA(LPCSTR pDevice, LPCSTR pPort, WORD fwCapability, LPSTR pOutput, const DEVMODEA* pDevMode)
183 {
184     TRACE("DeviceCapabilitiesA(%s, %s, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
185     UNIMPLEMENTED;
186     return 0;
187 }
188 
189 DWORD WINAPI
190 DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort, WORD fwCapability, LPWSTR pOutput, const DEVMODEW* pDevMode)
191 {
192     TRACE("DeviceCapabilitiesW(%S, %S, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
193     UNIMPLEMENTED;
194     return 0;
195 }
196 
197 INT WINAPI
198 DocumentEvent( HANDLE hPrinter, HDC hdc, int iEsc, ULONG cbIn, PVOID pvIn, ULONG cbOut, PVOID pvOut)
199 {
200     TRACE("DocumentEvent(%p, %p, %lu, %lu, %p, %lu, %p)\n", hPrinter, hdc, iEsc, cbIn, pvIn, cbOut, pvOut);
201     UNIMPLEMENTED;
202     return DOCUMENTEVENT_UNSUPPORTED;
203 }
204 
205 LONG WINAPI
206 DocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName, PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput, DWORD fMode)
207 {
208     PWSTR pwszDeviceName = NULL;
209     PDEVMODEW pdmwInput = NULL;
210     PDEVMODEW pdmwOutput = NULL;
211     BOOL bReturnValue = -1;
212     DWORD cch;
213 
214     TRACE("DocumentPropertiesA(%p, %p, %s, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
215 
216     if (pDeviceName)
217     {
218         // Convert pName to a Unicode string pwszDeviceName.
219         cch = strlen(pDeviceName);
220 
221         pwszDeviceName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
222         if (!pwszDeviceName)
223         {
224             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
225             ERR("HeapAlloc failed!\n");
226             goto Cleanup;
227         }
228 
229         MultiByteToWideChar(CP_ACP, 0, pDeviceName, -1, pwszDeviceName, cch + 1);
230     }
231 
232     if (pDevModeInput)
233     {
234         // Create working buffer for input to DocumentPropertiesW.
235         pdmwInput = HeapAlloc(hProcessHeap, 0, sizeof(DEVMODEW));
236         if (!pdmwInput)
237         {
238             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
239             ERR("HeapAlloc failed!\n");
240             goto Cleanup;
241         }
242         RosConvertAnsiDevModeToUnicodeDevmode(pDevModeInput, pdmwInput);
243     }
244 
245     if (pDevModeOutput)
246     {
247         // Create working buffer for output from DocumentPropertiesW.
248         pdmwOutput = HeapAlloc(hProcessHeap, 0, sizeof(DEVMODEW));
249         if (!pdmwOutput)
250         {
251             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
252             ERR("HeapAlloc failed!\n");
253             goto Cleanup;
254         }
255     }
256 
257     bReturnValue = DocumentPropertiesW(hWnd, hPrinter, pwszDeviceName, pdmwOutput, pdmwInput, fMode);
258     TRACE("bReturnValue from DocumentPropertiesW is '%ld'.\n", bReturnValue);
259 
260     if (pwszDeviceName)
261     {
262         HeapFree(hProcessHeap, 0, pwszDeviceName);
263     }
264 
265     if (bReturnValue < 0)
266     {
267         TRACE("DocumentPropertiesW failed!\n");
268         goto Cleanup;
269     }
270 
271     if (pdmwOutput)
272     {
273         RosConvertUnicodeDevModeToAnsiDevmode(pdmwOutput, pDevModeOutput);
274     }
275 
276 Cleanup:
277     if(pwszDeviceName)
278         HeapFree(hProcessHeap, 0, pwszDeviceName);
279 
280     if (pdmwInput)
281         HeapFree(hProcessHeap, 0, pdmwInput);
282 
283     if (pdmwOutput)
284         HeapFree(hProcessHeap, 0, pdmwOutput);
285 
286     return bReturnValue;
287 }
288 
289 static PRINTER_INFO_9W * get_devmodeW(HANDLE hprn)
290 {
291     PRINTER_INFO_9W *pi9 = NULL;
292     DWORD needed = 0;
293     BOOL res;
294 
295     res = GetPrinterW(hprn, 9, NULL, 0, &needed);
296     if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
297     {
298         pi9 = HeapAlloc(hProcessHeap, 0, needed);
299         res = GetPrinterW(hprn, 9, (LPBYTE)pi9, needed, &needed);
300     }
301 
302     if (res)
303         return pi9;
304 
305     ERR("GetPrinterW failed with %u\n", GetLastError());
306     HeapFree(hProcessHeap, 0, pi9);
307     return NULL;
308 }
309 
310 LONG WINAPI
311 DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput, DWORD fMode)
312 {
313     HANDLE hUseHandle = NULL;
314     PRINTER_INFO_9W *pi9 = NULL;
315     LONG Result = -1, Length;
316 
317     TRACE("DocumentPropertiesW(%p, %p, %S, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
318     if (hPrinter)
319     {
320         hUseHandle = hPrinter;
321     }
322     else if (!OpenPrinterW(pDeviceName, &hUseHandle, NULL))
323     {
324         ERR("No handle, and no usable printer name passed in\n");
325         return -1;
326     }
327 
328     pi9 = get_devmodeW(hUseHandle);
329 
330     if (pi9)
331     {
332         Length = pi9->pDevMode->dmSize + pi9->pDevMode->dmDriverExtra;
333         // See wineps.drv PSDRV_ExtDeviceMode
334         if (fMode)
335         {
336             Result = 1; /* IDOK */
337 
338             if (fMode & DM_IN_BUFFER)
339             {
340                 FIXME("Merge pDevModeInput with pi9, write back to driver!\n");
341                 // See wineps.drv PSDRV_MergeDevmodes
342             }
343 
344             if (fMode & DM_IN_PROMPT)
345             {
346                 FIXME("Show property sheet!\n");
347                 Result = 2; /* IDCANCEL */
348             }
349 
350             if (fMode & (DM_OUT_BUFFER | DM_OUT_DEFAULT))
351             {
352                 if (pDevModeOutput)
353                 {
354                     memcpy(pDevModeOutput, pi9->pDevMode, pi9->pDevMode->dmSize + pi9->pDevMode->dmDriverExtra);
355                 }
356                 else
357                 {
358                     ERR("No pDevModeOutput\n");
359                     Result = -1;
360                 }
361             }
362         }
363         else
364         {
365             Result = Length;
366         }
367 
368         HeapFree(hProcessHeap, 0, pi9);
369     }
370 
371     if (hUseHandle && !hPrinter)
372         ClosePrinter(hUseHandle);
373     return Result;
374 }
375 
376 BOOL WINAPI
377 EndDocPrinter(HANDLE hPrinter)
378 {
379     DWORD dwErrorCode;
380     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
381 
382     TRACE("EndDocPrinter(%p)\n", hPrinter);
383 
384     // Sanity checks.
385     if (!pHandle)
386     {
387         dwErrorCode = ERROR_INVALID_HANDLE;
388         goto Cleanup;
389     }
390 
391     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
392     {
393         // For spooled jobs, the document is finished by calling _RpcScheduleJob.
394         RpcTryExcept
395         {
396             dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, pHandle->dwJobID);
397         }
398         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
399         {
400             dwErrorCode = RpcExceptionCode();
401             ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
402         }
403         RpcEndExcept;
404 
405         // Close the spool file handle.
406         CloseHandle(pHandle->hSPLFile);
407     }
408     else
409     {
410         // In all other cases, just call _RpcEndDocPrinter.
411         RpcTryExcept
412         {
413             dwErrorCode = _RpcEndDocPrinter(pHandle->hPrinter);
414         }
415         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
416         {
417             dwErrorCode = RpcExceptionCode();
418             ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode);
419         }
420         RpcEndExcept;
421     }
422 
423     // A new document can now be started again.
424     pHandle->bStartedDoc = FALSE;
425 
426 Cleanup:
427     SetLastError(dwErrorCode);
428     return (dwErrorCode == ERROR_SUCCESS);
429 }
430 
431 BOOL WINAPI
432 EndPagePrinter(HANDLE hPrinter)
433 {
434     DWORD dwErrorCode;
435     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
436 
437     TRACE("EndPagePrinter(%p)\n", hPrinter);
438 
439     // Sanity checks.
440     if (!pHandle)
441     {
442         dwErrorCode = ERROR_INVALID_HANDLE;
443         goto Cleanup;
444     }
445 
446     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
447     {
448         // For spooled jobs, we don't need to do anything.
449         dwErrorCode = ERROR_SUCCESS;
450     }
451     else
452     {
453         // In all other cases, just call _RpcEndPagePrinter.
454         RpcTryExcept
455         {
456             dwErrorCode = _RpcEndPagePrinter(pHandle->hPrinter);
457         }
458         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
459         {
460             dwErrorCode = RpcExceptionCode();
461             ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode);
462         }
463         RpcEndExcept;
464     }
465 
466 Cleanup:
467     SetLastError(dwErrorCode);
468     return (dwErrorCode == ERROR_SUCCESS);
469 }
470 
471 BOOL WINAPI
472 EnumPrintersA(DWORD Flags, PSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
473 {
474     BOOL bReturnValue = FALSE;
475     DWORD cch;
476     PWSTR pwszName = NULL;
477     PSTR pszPrinterName = NULL;
478     PSTR pszServerName = NULL;
479     PSTR pszDescription = NULL;
480     PSTR pszName = NULL;
481     PSTR pszComment = NULL;
482     PSTR pszShareName = NULL;
483     PSTR pszPortName = NULL;
484     PSTR pszDriverName = NULL;
485     PSTR pszLocation = NULL;
486     PSTR pszSepFile = NULL;
487     PSTR pszPrintProcessor = NULL;
488     PSTR pszDatatype = NULL;
489     PSTR pszParameters = NULL;
490     DWORD i;
491     PPRINTER_INFO_1W ppi1w = NULL;
492     PPRINTER_INFO_1A ppi1a = NULL;
493     PPRINTER_INFO_2W ppi2w = NULL;
494     PPRINTER_INFO_2A ppi2a = NULL;
495     PPRINTER_INFO_4W ppi4w = NULL;
496     PPRINTER_INFO_4A ppi4a = NULL;
497     PPRINTER_INFO_5W ppi5w = NULL;
498     PPRINTER_INFO_5A ppi5a = NULL;
499 
500     TRACE("EnumPrintersA(%lu, %s, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
501 
502     // Check for invalid levels here for early error return. MSDN says that only 1, 2, 4, and 5 are allowable.
503     if (Level !=  1 && Level != 2 && Level != 4 && Level != 5)
504     {
505         SetLastError(ERROR_INVALID_LEVEL);
506         ERR("Invalid Level!\n");
507         goto Cleanup;
508     }
509 
510     if (Name)
511     {
512         // Convert pName to a Unicode string pwszName.
513         cch = strlen(Name);
514 
515         pwszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
516         if (!pwszName)
517         {
518             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
519             ERR("HeapAlloc failed!\n");
520             goto Cleanup;
521         }
522 
523         MultiByteToWideChar(CP_ACP, 0, Name, -1, pwszName, cch + 1);
524     }
525 
526     /* Ref: https://stackoverflow.com/questions/41147180/why-enumprintersa-and-enumprintersw-request-the-same-amount-of-memory */
527     bReturnValue = EnumPrintersW(Flags, pwszName, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
528     HeapFree(hProcessHeap, 0, pwszName);
529 
530     TRACE("*pcReturned is '%d' and bReturnValue is '%d' and GetLastError is '%ld'.\n", *pcReturned, bReturnValue, GetLastError());
531 
532     /* We are mapping multiple different pointers to the same pPrinterEnum pointer here so that */
533     /* we can do in-place conversion. We read the Unicode response from the EnumPrintersW and */
534     /* then we write back the ANSI conversion into the same buffer for our EnumPrintersA output */
535 
536     /* mapping to pPrinterEnum for Unicode (w) characters for Levels 1, 2, 4, and 5 */
537     ppi1w = (PPRINTER_INFO_1W)pPrinterEnum;
538     ppi2w = (PPRINTER_INFO_2W)pPrinterEnum;
539     ppi4w = (PPRINTER_INFO_4W)pPrinterEnum;
540     ppi5w = (PPRINTER_INFO_5W)pPrinterEnum;
541     /* mapping to pPrinterEnum for ANSI (a) characters for Levels 1, 2, 4, and 5 */
542     ppi1a = (PPRINTER_INFO_1A)pPrinterEnum;
543     ppi2a = (PPRINTER_INFO_2A)pPrinterEnum;
544     ppi4a = (PPRINTER_INFO_4A)pPrinterEnum;
545     ppi5a = (PPRINTER_INFO_5A)pPrinterEnum;
546 
547     for (i = 0; i < *pcReturned; i++)
548     {
549         switch (Level)
550         {
551             case 1:
552             {
553                 if (ppi1w[i].pDescription)
554                 {
555                     // Convert Unicode pDescription to a ANSI string pszDescription.
556                     cch = wcslen(ppi1w[i].pDescription);
557 
558                     pszDescription = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
559                     if (!pszDescription)
560                     {
561                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
562                         ERR("HeapAlloc failed!\n");
563                         goto Cleanup;
564                     }
565 
566                     WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pDescription, -1, pszDescription, cch + 1, NULL, NULL);
567                     StringCchCopyA(ppi1a[i].pDescription, cch + 1, pszDescription);
568 
569                     HeapFree(hProcessHeap, 0, pszDescription);
570                 }
571 
572                 if (ppi1w[i].pName)
573                 {
574                     // Convert Unicode pName to a ANSI string pszName.
575                     cch = wcslen(ppi1w[i].pName);
576 
577                     pszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
578                     if (!pszName)
579                     {
580                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
581                         ERR("HeapAlloc failed!\n");
582                         goto Cleanup;
583                     }
584 
585                     WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pName, -1, pszName, cch + 1, NULL, NULL);
586                     StringCchCopyA(ppi1a[i].pName, cch + 1, pszName);
587 
588                     HeapFree(hProcessHeap, 0, pszName);
589                 }
590 
591                 if (ppi1w[i].pComment)
592                 {
593                     // Convert Unicode pComment to a ANSI string pszComment.
594                     cch = wcslen(ppi1w[i].pComment);
595 
596                     pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
597                     if (!pszComment)
598                     {
599                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
600                         ERR("HeapAlloc failed!\n");
601                         goto Cleanup;
602                     }
603 
604                     WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pComment, -1, pszComment, cch + 1, NULL, NULL);
605                     StringCchCopyA(ppi1a[i].pComment, cch + 1, pszComment);
606 
607                     HeapFree(hProcessHeap, 0, pszComment);
608                 }
609                 break;
610             }
611 
612 
613             case 2:
614             {
615                 if (ppi2w[i].pServerName)
616                 {
617                     // Convert Unicode pServerName to a ANSI string pszServerName.
618                     cch = wcslen(ppi2w[i].pServerName);
619 
620                     pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
621                     if (!pszServerName)
622                     {
623                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
624                         ERR("HeapAlloc failed!\n");
625                         goto Cleanup;
626                     }
627 
628                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pServerName, -1, pszServerName, cch + 1, NULL, NULL);
629                     StringCchCopyA(ppi2a[i].pServerName, cch + 1, pszServerName);
630 
631                     HeapFree(hProcessHeap, 0, pszServerName);
632                 }
633 
634                 if (ppi2w[i].pPrinterName)
635                 {
636                     // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
637                     cch = wcslen(ppi2w[i].pPrinterName);
638 
639                     pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
640                     if (!pszPrinterName)
641                     {
642                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
643                         ERR("HeapAlloc failed!\n");
644                         goto Cleanup;
645                     }
646 
647                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
648                     StringCchCopyA(ppi2a[i].pPrinterName, cch + 1, pszPrinterName);
649 
650                     HeapFree(hProcessHeap, 0, pszPrinterName);
651                 }
652 
653                 if (ppi2w[i].pShareName)
654                 {
655                     // Convert Unicode pShareName to a ANSI string pszShareName.
656                     cch = wcslen(ppi2w[i].pShareName);
657 
658                     pszShareName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
659                     if (!pszShareName)
660                     {
661                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
662                         ERR("HeapAlloc failed!\n");
663                         goto Cleanup;
664                     }
665 
666                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pShareName, -1, pszShareName, cch + 1, NULL, NULL);
667                     StringCchCopyA(ppi2a[i].pShareName, cch + 1, pszShareName);
668 
669                     HeapFree(hProcessHeap, 0, pszShareName);
670                 }
671 
672                 if (ppi2w[i].pPortName)
673                 {
674                     // Convert Unicode pPortName to a ANSI string pszPortName.
675                     cch = wcslen(ppi2w[i].pPortName);
676 
677                     pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
678                     if (!pszPortName)
679                     {
680                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
681                         ERR("HeapAlloc failed!\n");
682                         goto Cleanup;
683                     }
684 
685                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPortName, -1, pszPortName, cch + 1, NULL, NULL);
686                     StringCchCopyA(ppi2a[i].pPortName, cch + 1, pszPortName);
687 
688                     HeapFree(hProcessHeap, 0, pszPortName);
689                 }
690 
691                 if (ppi2w[i].pDriverName)
692                 {
693                     // Convert Unicode pDriverName to a ANSI string pszDriverName.
694                     cch = wcslen(ppi2w[i].pDriverName);
695 
696                     pszDriverName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
697                     if (!pszDriverName)
698                     {
699                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
700                         ERR("HeapAlloc failed!\n");
701                         goto Cleanup;
702                     }
703 
704                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pDriverName, -1, pszDriverName, cch + 1, NULL, NULL);
705                     StringCchCopyA(ppi2a[i].pDriverName, cch + 1, pszDriverName);
706 
707                     HeapFree(hProcessHeap, 0, pszDriverName);
708                 }
709 
710                 if (ppi2w[i].pComment)
711                 {
712                     // Convert Unicode pComment to a ANSI string pszComment.
713                     cch = wcslen(ppi2w[i].pComment);
714 
715                     pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
716                     if (!pszComment)
717                     {
718                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
719                         ERR("HeapAlloc failed!\n");
720                         goto Cleanup;
721                     }
722 
723                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pComment, -1, pszComment, cch + 1, NULL, NULL);
724                     StringCchCopyA(ppi2a[i].pComment, cch + 1, pszComment);
725 
726                     HeapFree(hProcessHeap, 0, pszComment);
727                 }
728 
729                 if (ppi2w[i].pLocation)
730                 {
731                     // Convert Unicode pLocation to a ANSI string pszLocation.
732                     cch = wcslen(ppi2w[i].pLocation);
733 
734                     pszLocation = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
735                     if (!pszLocation)
736                     {
737                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
738                         ERR("HeapAlloc failed!\n");
739                         goto Cleanup;
740                     }
741 
742                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pLocation, -1, pszLocation, cch + 1, NULL, NULL);
743                     StringCchCopyA(ppi2a[i].pLocation, cch + 1, pszLocation);
744 
745                     HeapFree(hProcessHeap, 0, pszLocation);
746                 }
747 
748 
749                 if (ppi2w[i].pSepFile)
750                 {
751                     // Convert Unicode pSepFile to a ANSI string pszSepFile.
752                     cch = wcslen(ppi2w[i].pSepFile);
753 
754                     pszSepFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
755                     if (!pszSepFile)
756                     {
757                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
758                         ERR("HeapAlloc failed!\n");
759                         goto Cleanup;
760                     }
761 
762                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pSepFile, -1, pszSepFile, cch + 1, NULL, NULL);
763                     StringCchCopyA(ppi2a[i].pSepFile, cch + 1, pszSepFile);
764 
765                     HeapFree(hProcessHeap, 0, pszSepFile);
766                 }
767 
768                 if (ppi2w[i].pPrintProcessor)
769                 {
770                     // Convert Unicode pPrintProcessor to a ANSI string pszPrintProcessor.
771                     cch = wcslen(ppi2w[i].pPrintProcessor);
772 
773                     pszPrintProcessor = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
774                     if (!pszPrintProcessor)
775                     {
776                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
777                         ERR("HeapAlloc failed!\n");
778                         goto Cleanup;
779                     }
780 
781                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPrintProcessor, -1, pszPrintProcessor, cch + 1, NULL, NULL);
782                     StringCchCopyA(ppi2a[i].pPrintProcessor, cch + 1, pszPrintProcessor);
783 
784                     HeapFree(hProcessHeap, 0, pszPrintProcessor);
785                 }
786 
787 
788                 if (ppi2w[i].pDatatype)
789                 {
790                     // Convert Unicode pDatatype to a ANSI string pszDatatype.
791                     cch = wcslen(ppi2w[i].pDatatype);
792 
793                     pszDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
794                     if (!pszDatatype)
795                     {
796                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
797                         ERR("HeapAlloc failed!\n");
798                         goto Cleanup;
799                     }
800 
801                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pDatatype, -1, pszDatatype, cch + 1, NULL, NULL);
802                     StringCchCopyA(ppi2a[i].pDatatype, cch + 1, pszDatatype);
803 
804                     HeapFree(hProcessHeap, 0, pszDatatype);
805                 }
806 
807                 if (ppi2w[i].pParameters)
808                 {
809                     // Convert Unicode pParameters to a ANSI string pszParameters.
810                     cch = wcslen(ppi2w[i].pParameters);
811 
812                     pszParameters = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
813                     if (!pszParameters)
814                     {
815                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
816                         ERR("HeapAlloc failed!\n");
817                         goto Cleanup;
818                     }
819 
820                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pParameters, -1, pszParameters, cch + 1, NULL, NULL);
821                     StringCchCopyA(ppi2a[i].pParameters, cch + 1, pszParameters);
822 
823                     HeapFree(hProcessHeap, 0, pszParameters);
824                 }
825                 break;
826 
827             }
828 
829             case 4:
830             {
831                 if (ppi4w[i].pPrinterName)
832                 {
833                     // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
834                     cch = wcslen(ppi4w[i].pPrinterName);
835 
836                     pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
837                     if (!pszPrinterName)
838                     {
839                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
840                         ERR("HeapAlloc failed!\n");
841                         goto Cleanup;
842                     }
843 
844                     WideCharToMultiByte(CP_ACP, 0, ppi4w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
845                     StringCchCopyA(ppi4a[i].pPrinterName, cch + 1, pszPrinterName);
846 
847                     HeapFree(hProcessHeap, 0, pszPrinterName);
848                 }
849 
850                 if (ppi4w[i].pServerName)
851                 {
852                     // Convert Unicode pServerName to a ANSI string pszServerName.
853                     cch = wcslen(ppi4w[i].pServerName);
854 
855                     pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
856                     if (!pszServerName)
857                     {
858                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
859                         ERR("HeapAlloc failed!\n");
860                         goto Cleanup;
861                     }
862 
863                     WideCharToMultiByte(CP_ACP, 0, ppi4w[i].pServerName, -1, pszServerName, cch + 1, NULL, NULL);
864                     StringCchCopyA(ppi4a[i].pServerName, cch + 1, pszServerName);
865 
866                     HeapFree(hProcessHeap, 0, pszServerName);
867                 }
868                 break;
869             }
870 
871             case 5:
872             {
873                 if (ppi5w[i].pPrinterName)
874                 {
875                     // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
876                     cch = wcslen(ppi5w[i].pPrinterName);
877 
878                     pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
879                     if (!pszPrinterName)
880                     {
881                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
882                         ERR("HeapAlloc failed!\n");
883                         goto Cleanup;
884                     }
885 
886                     WideCharToMultiByte(CP_ACP, 0, ppi5w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
887                     StringCchCopyA(ppi5a[i].pPrinterName, cch + 1, pszPrinterName);
888 
889                     HeapFree(hProcessHeap, 0, pszPrinterName);
890                 }
891 
892                 if (ppi5w[i].pPortName)
893                 {
894                     // Convert Unicode pPortName to a ANSI string pszPortName.
895                     cch = wcslen(ppi5w[i].pPortName);
896 
897                     pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
898                     if (!pszPortName)
899                     {
900                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
901                         ERR("HeapAlloc failed!\n");
902                         goto Cleanup;
903                     }
904 
905                     WideCharToMultiByte(CP_ACP, 0, ppi5w[i].pPortName, -1, pszPortName, cch + 1, NULL, NULL);
906                     StringCchCopyA(ppi5a[i].pPortName, cch + 1, pszPortName);
907 
908                     HeapFree(hProcessHeap, 0, pszPortName);
909                 }
910                 break;
911             }
912 
913         }   // switch
914     }       // for
915 
916 Cleanup:
917 
918     return bReturnValue;
919 }
920 
921 BOOL WINAPI
922 EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
923 {
924     DWORD dwErrorCode;
925 
926     TRACE("EnumPrintersW(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
927 
928     // Dismiss invalid levels already at this point.
929     if (Level == 3 || Level > 5)
930     {
931         dwErrorCode = ERROR_INVALID_LEVEL;
932         goto Cleanup;
933     }
934 
935     if (cbBuf && pPrinterEnum)
936         ZeroMemory(pPrinterEnum, cbBuf);
937 
938     // Do the RPC call
939     RpcTryExcept
940     {
941         dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
942     }
943     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
944     {
945         dwErrorCode = RpcExceptionCode();
946         ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
947     }
948     RpcEndExcept;
949 
950     if (dwErrorCode == ERROR_SUCCESS)
951     {
952         // Replace relative offset addresses in the output by absolute pointers.
953         ASSERT(Level <= 9);
954         MarshallUpStructuresArray(cbBuf, pPrinterEnum, *pcReturned, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
955     }
956 
957 Cleanup:
958     SetLastError(dwErrorCode);
959     return (dwErrorCode == ERROR_SUCCESS);
960 }
961 
962 BOOL WINAPI
963 FlushPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten, DWORD cSleep)
964 {
965     TRACE("FlushPrinter(%p, %p, %lu, %p, %lu)\n", hPrinter, pBuf, cbBuf, pcWritten, cSleep);
966     UNIMPLEMENTED;
967     return FALSE;
968 }
969 
970 BOOL WINAPI
971 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
972 {
973     DWORD dwErrorCode;
974     PWSTR pwszBuffer = NULL;
975 
976     TRACE("GetDefaultPrinterA(%p, %p)\n", pszBuffer, pcchBuffer);
977 
978     // Sanity check.
979     if (!pcchBuffer)
980     {
981         dwErrorCode = ERROR_INVALID_PARAMETER;
982         goto Cleanup;
983     }
984 
985     // Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
986     if (pszBuffer && *pcchBuffer)
987     {
988         pwszBuffer = HeapAlloc(hProcessHeap, 0, *pcchBuffer * sizeof(WCHAR));
989         if (!pwszBuffer)
990         {
991             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
992             ERR("HeapAlloc failed!\n");
993             goto Cleanup;
994         }
995     }
996 
997     if (!GetDefaultPrinterW(pwszBuffer, pcchBuffer))
998     {
999         dwErrorCode = GetLastError();
1000         goto Cleanup;
1001     }
1002 
1003     // We successfully got a string in pwszBuffer, so convert the Unicode string to ANSI.
1004     WideCharToMultiByte(CP_ACP, 0, pwszBuffer, -1, pszBuffer, *pcchBuffer, NULL, NULL);
1005 
1006     dwErrorCode = ERROR_SUCCESS;
1007 
1008 Cleanup:
1009     if (pwszBuffer)
1010         HeapFree(hProcessHeap, 0, pwszBuffer);
1011 
1012     SetLastError(dwErrorCode);
1013     return (dwErrorCode == ERROR_SUCCESS);
1014 }
1015 
1016 BOOL WINAPI
1017 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
1018 {
1019     DWORD cbNeeded;
1020     DWORD cchInputBuffer;
1021     DWORD dwErrorCode;
1022     HKEY hWindowsKey = NULL;
1023     PWSTR pwszDevice = NULL;
1024     PWSTR pwszComma;
1025 
1026     TRACE("GetDefaultPrinterW(%p, %p)\n", pszBuffer, pcchBuffer);
1027 
1028     // Sanity check.
1029     if (!pcchBuffer)
1030     {
1031         dwErrorCode = ERROR_INVALID_PARAMETER;
1032         goto Cleanup;
1033     }
1034 
1035     cchInputBuffer = *pcchBuffer;
1036 
1037     // Open the registry key where the default printer for the current user is stored.
1038     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_READ, &hWindowsKey);
1039     if (dwErrorCode != ERROR_SUCCESS)
1040     {
1041         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
1042         goto Cleanup;
1043     }
1044 
1045     // Determine the size of the required buffer.
1046     dwErrorCode = (DWORD)RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, NULL, &cbNeeded);
1047     if (dwErrorCode != ERROR_SUCCESS)
1048     {
1049         ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
1050         goto Cleanup;
1051     }
1052 
1053     // Allocate it.
1054     pwszDevice = HeapAlloc(hProcessHeap, 0, cbNeeded);
1055     if (!pwszDevice)
1056     {
1057         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1058         ERR("HeapAlloc failed!\n");
1059         goto Cleanup;
1060     }
1061 
1062     // Now get the actual value.
1063     dwErrorCode = RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, (PBYTE)pwszDevice, &cbNeeded);
1064     if (dwErrorCode != ERROR_SUCCESS)
1065     {
1066         ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
1067         goto Cleanup;
1068     }
1069 
1070     // We get a string "<Printer Name>,winspool,<Port>:".
1071     // Extract the printer name from it.
1072     pwszComma = wcschr(pwszDevice, L',');
1073     if (!pwszComma)
1074     {
1075         ERR("Found no or invalid default printer: %S!\n", pwszDevice);
1076         dwErrorCode = ERROR_INVALID_NAME;
1077         goto Cleanup;
1078     }
1079 
1080     // Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
1081     *pcchBuffer = pwszComma - pwszDevice + 1;
1082 
1083     // Check if the supplied buffer is large enough.
1084     if (cchInputBuffer < *pcchBuffer)
1085     {
1086         dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
1087         goto Cleanup;
1088     }
1089 
1090     // Copy the default printer.
1091     *pwszComma = 0;
1092     CopyMemory(pszBuffer, pwszDevice, *pcchBuffer * sizeof(WCHAR));
1093 
1094     dwErrorCode = ERROR_SUCCESS;
1095 
1096 Cleanup:
1097     if (hWindowsKey)
1098         RegCloseKey(hWindowsKey);
1099 
1100     if (pwszDevice)
1101         HeapFree(hProcessHeap, 0, pwszDevice);
1102 
1103     SetLastError(dwErrorCode);
1104     return (dwErrorCode == ERROR_SUCCESS);
1105 }
1106 
1107 BOOL WINAPI
1108 GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
1109 {
1110     PPRINTER_INFO_1A ppi1a = (PPRINTER_INFO_1A)pPrinter;
1111     PPRINTER_INFO_1W ppi1w = (PPRINTER_INFO_1W)pPrinter;
1112     PPRINTER_INFO_2A ppi2a = (PPRINTER_INFO_2A)pPrinter;
1113     PPRINTER_INFO_2W ppi2w = (PPRINTER_INFO_2W)pPrinter;
1114     PPRINTER_INFO_4A ppi4a = (PPRINTER_INFO_4A)pPrinter;
1115     PPRINTER_INFO_4W ppi4w = (PPRINTER_INFO_4W)pPrinter;
1116     PPRINTER_INFO_5A ppi5a = (PPRINTER_INFO_5A)pPrinter;
1117     PPRINTER_INFO_5W ppi5w = (PPRINTER_INFO_5W)pPrinter;
1118     PPRINTER_INFO_7A ppi7a = (PPRINTER_INFO_7A)pPrinter;
1119     PPRINTER_INFO_7W ppi7w = (PPRINTER_INFO_7W)pPrinter;
1120     DWORD cch;
1121     BOOL bReturnValue = FALSE;
1122 
1123     TRACE("GetPrinterA(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
1124 
1125     // Check for invalid levels here for early error return. Should be 1-9.
1126     if (Level <  1 || Level > 9)
1127     {
1128         SetLastError(ERROR_INVALID_LEVEL);
1129         ERR("Invalid Level!\n");
1130         goto Cleanup;
1131     }
1132 
1133     bReturnValue = GetPrinterW(hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
1134 
1135     if (!bReturnValue)
1136     {
1137         TRACE("GetPrinterW failed!\n");
1138         goto Cleanup;
1139     }
1140 
1141     switch (Level)
1142     {
1143         case 1:
1144         {
1145             if (ppi1w->pDescription)
1146             {
1147                 PSTR pszDescription;
1148 
1149                 // Convert Unicode pDescription to a ANSI string pszDescription.
1150                 cch = wcslen(ppi1w->pDescription);
1151 
1152                 pszDescription = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1153                 if (!pszDescription)
1154                 {
1155                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1156                     ERR("HeapAlloc failed!\n");
1157                     goto Cleanup;
1158                 }
1159 
1160                 WideCharToMultiByte(CP_ACP, 0, ppi1w->pDescription, -1, pszDescription, cch + 1, NULL, NULL);
1161                 StringCchCopyA(ppi1a->pDescription, cch + 1, pszDescription);
1162 
1163                 HeapFree(hProcessHeap, 0, pszDescription);
1164             }
1165 
1166             if (ppi1w->pName)
1167             {
1168                 PSTR pszName;
1169 
1170                 // Convert Unicode pName to a ANSI string pszName.
1171                 cch = wcslen(ppi1w->pName);
1172 
1173                 pszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1174                 if (!pszName)
1175                 {
1176                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1177                     ERR("HeapAlloc failed!\n");
1178                     goto Cleanup;
1179                 }
1180 
1181                 WideCharToMultiByte(CP_ACP, 0, ppi1w->pName, -1, pszName, cch + 1, NULL, NULL);
1182                 StringCchCopyA(ppi1a->pName, cch + 1, pszName);
1183 
1184                 HeapFree(hProcessHeap, 0, pszName);
1185             }
1186 
1187             if (ppi1w->pComment)
1188             {
1189                 PSTR pszComment;
1190 
1191                 // Convert Unicode pComment to a ANSI string pszComment.
1192                 cch = wcslen(ppi1w->pComment);
1193 
1194                 pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1195                 if (!pszComment)
1196                 {
1197                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1198                     ERR("HeapAlloc failed!\n");
1199                     goto Cleanup;
1200                 }
1201 
1202                 WideCharToMultiByte(CP_ACP, 0, ppi1w->pComment, -1, pszComment, cch + 1, NULL, NULL);
1203                 StringCchCopyA(ppi1a->pComment, cch + 1, pszComment);
1204 
1205                 HeapFree(hProcessHeap, 0, pszComment);
1206             }
1207             break;
1208         }
1209 
1210         case 2:
1211         {
1212             if (ppi2w->pServerName)
1213             {
1214                 PSTR pszServerName;
1215 
1216                 // Convert Unicode pServerName to a ANSI string pszServerName.
1217                 cch = wcslen(ppi2w->pServerName);
1218 
1219                 pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1220                 if (!pszServerName)
1221                 {
1222                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1223                     ERR("HeapAlloc failed!\n");
1224                     goto Cleanup;
1225                 }
1226 
1227                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pServerName, -1, pszServerName, cch + 1, NULL, NULL);
1228                 StringCchCopyA(ppi2a->pServerName, cch + 1, pszServerName);
1229 
1230                 HeapFree(hProcessHeap, 0, pszServerName);
1231             }
1232 
1233             if (ppi2w->pPrinterName)
1234             {
1235                 PSTR pszPrinterName;
1236 
1237                 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
1238                 cch = wcslen(ppi2w->pPrinterName);
1239 
1240                 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1241                 if (!pszPrinterName)
1242                 {
1243                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1244                     ERR("HeapAlloc failed!\n");
1245                     goto Cleanup;
1246                 }
1247 
1248                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
1249                 StringCchCopyA(ppi2a->pPrinterName, cch + 1, pszPrinterName);
1250 
1251                 HeapFree(hProcessHeap, 0, pszPrinterName);
1252             }
1253 
1254             if (ppi2w->pShareName)
1255             {
1256                 PSTR pszShareName;
1257 
1258                 // Convert Unicode pShareName to a ANSI string pszShareName.
1259                 cch = wcslen(ppi2w->pShareName);
1260 
1261                 pszShareName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1262                 if (!pszShareName)
1263                 {
1264                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1265                     ERR("HeapAlloc failed!\n");
1266                     goto Cleanup;
1267                 }
1268 
1269                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pShareName, -1, pszShareName, cch + 1, NULL, NULL);
1270                 StringCchCopyA(ppi2a->pShareName, cch + 1, pszShareName);
1271 
1272                 HeapFree(hProcessHeap, 0, pszShareName);
1273             }
1274 
1275             if (ppi2w->pPortName)
1276             {
1277                 PSTR pszPortName;
1278 
1279                 // Convert Unicode pPortName to a ANSI string pszPortName.
1280                 cch = wcslen(ppi2w->pPortName);
1281 
1282                 pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1283                 if (!pszPortName)
1284                 {
1285                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1286                     ERR("HeapAlloc failed!\n");
1287                     goto Cleanup;
1288                 }
1289 
1290                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pPortName, -1, pszPortName, cch + 1, NULL, NULL);
1291                 StringCchCopyA(ppi2a->pPortName, cch + 1, pszPortName);
1292 
1293                 HeapFree(hProcessHeap, 0, pszPortName);
1294             }
1295 
1296             if (ppi2w->pDriverName)
1297             {
1298                 PSTR pszDriverName;
1299 
1300                 // Convert Unicode pDriverName to a ANSI string pszDriverName.
1301                 cch = wcslen(ppi2w->pDriverName);
1302 
1303                 pszDriverName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1304                 if (!pszDriverName)
1305                 {
1306                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1307                     ERR("HeapAlloc failed!\n");
1308                     goto Cleanup;
1309                 }
1310 
1311                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pDriverName, -1, pszDriverName, cch + 1, NULL, NULL);
1312                 StringCchCopyA(ppi2a->pDriverName, cch + 1, pszDriverName);
1313 
1314                 HeapFree(hProcessHeap, 0, pszDriverName);
1315             }
1316 
1317             if (ppi2w->pComment)
1318             {
1319                 PSTR pszComment;
1320 
1321                 // Convert Unicode pComment to a ANSI string pszComment.
1322                 cch = wcslen(ppi2w->pComment);
1323 
1324                 pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1325                 if (!pszComment)
1326                 {
1327                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1328                     ERR("HeapAlloc failed!\n");
1329                     goto Cleanup;
1330                 }
1331 
1332                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pComment, -1, pszComment, cch + 1, NULL, NULL);
1333                 StringCchCopyA(ppi2a->pComment, cch + 1, pszComment);
1334 
1335                 HeapFree(hProcessHeap, 0, pszComment);
1336             }
1337 
1338             if (ppi2w->pLocation)
1339             {
1340                 PSTR pszLocation;
1341 
1342                 // Convert Unicode pLocation to a ANSI string pszLocation.
1343                 cch = wcslen(ppi2w->pLocation);
1344 
1345                 pszLocation = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1346                 if (!pszLocation)
1347                 {
1348                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1349                     ERR("HeapAlloc failed!\n");
1350                     goto Cleanup;
1351                 }
1352 
1353                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pLocation, -1, pszLocation, cch + 1, NULL, NULL);
1354                 StringCchCopyA(ppi2a->pLocation, cch + 1, pszLocation);
1355 
1356                 HeapFree(hProcessHeap, 0, pszLocation);
1357             }
1358 
1359             if (ppi2w->pSepFile)
1360             {
1361                 PSTR pszSepFile;
1362 
1363                 // Convert Unicode pSepFile to a ANSI string pszSepFile.
1364                 cch = wcslen(ppi2w->pSepFile);
1365 
1366                 pszSepFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1367                 if (!pszSepFile)
1368                 {
1369                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1370                     ERR("HeapAlloc failed!\n");
1371                     goto Cleanup;
1372                 }
1373 
1374                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pSepFile, -1, pszSepFile, cch + 1, NULL, NULL);
1375                 StringCchCopyA(ppi2a->pSepFile, cch + 1, pszSepFile);
1376 
1377                 HeapFree(hProcessHeap, 0, pszSepFile);
1378             }
1379 
1380             if (ppi2w->pPrintProcessor)
1381             {
1382                 PSTR pszPrintProcessor;
1383 
1384                 // Convert Unicode pPrintProcessor to a ANSI string pszPrintProcessor.
1385                 cch = wcslen(ppi2w->pPrintProcessor);
1386 
1387                 pszPrintProcessor = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1388                 if (!pszPrintProcessor)
1389                 {
1390                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1391                     ERR("HeapAlloc failed!\n");
1392                     goto Cleanup;
1393                 }
1394 
1395                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pPrintProcessor, -1, pszPrintProcessor, cch + 1, NULL, NULL);
1396                 StringCchCopyA(ppi2a->pPrintProcessor, cch + 1, pszPrintProcessor);
1397 
1398                 HeapFree(hProcessHeap, 0, pszPrintProcessor);
1399             }
1400 
1401             if (ppi2w->pDatatype)
1402             {
1403                 PSTR pszDatatype;
1404 
1405                 // Convert Unicode pDatatype to a ANSI string pszDatatype.
1406                 cch = wcslen(ppi2w->pDatatype);
1407 
1408                 pszDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1409                 if (!pszDatatype)
1410                 {
1411                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1412                     ERR("HeapAlloc failed!\n");
1413                     goto Cleanup;
1414                 }
1415 
1416                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pDatatype, -1, pszDatatype, cch + 1, NULL, NULL);
1417                 StringCchCopyA(ppi2a->pDatatype, cch + 1, pszDatatype);
1418 
1419                 HeapFree(hProcessHeap, 0, pszDatatype);
1420             }
1421 
1422             if (ppi2w->pParameters)
1423             {
1424                 PSTR pszParameters;
1425 
1426                 // Convert Unicode pParameters to a ANSI string pszParameters.
1427                 cch = wcslen(ppi2w->pParameters);
1428 
1429                 pszParameters = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1430                 if (!pszParameters)
1431                 {
1432                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1433                     ERR("HeapAlloc failed!\n");
1434                     goto Cleanup;
1435                 }
1436 
1437                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pParameters, -1, pszParameters, cch + 1, NULL, NULL);
1438                 StringCchCopyA(ppi2a->pParameters, cch + 1, pszParameters);
1439 
1440                 HeapFree(hProcessHeap, 0, pszParameters);
1441             }
1442             break;
1443         }
1444 
1445         case 4:
1446         {
1447             if (ppi4w->pPrinterName)
1448             {
1449                 PSTR pszPrinterName;
1450 
1451                 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
1452                 cch = wcslen(ppi4w->pPrinterName);
1453 
1454                 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1455                 if (!pszPrinterName)
1456                 {
1457                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1458                     ERR("HeapAlloc failed!\n");
1459                     goto Cleanup;
1460                 }
1461 
1462                 WideCharToMultiByte(CP_ACP, 0, ppi4w->pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
1463                 StringCchCopyA(ppi4a->pPrinterName, cch + 1, pszPrinterName);
1464 
1465                 HeapFree(hProcessHeap, 0, pszPrinterName);
1466             }
1467 
1468             if (ppi4w->pServerName)
1469             {
1470                 PSTR pszServerName;
1471 
1472                 // Convert Unicode pServerName to a ANSI string pszServerName.
1473                 cch = wcslen(ppi4w->pServerName);
1474 
1475                 pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1476                 if (!pszServerName)
1477                 {
1478                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1479                     ERR("HeapAlloc failed!\n");
1480                     goto Cleanup;
1481                 }
1482 
1483                 WideCharToMultiByte(CP_ACP, 0, ppi4w->pServerName, -1, pszServerName, cch + 1, NULL, NULL);
1484                 StringCchCopyA(ppi4a->pServerName, cch + 1, pszServerName);
1485 
1486                 HeapFree(hProcessHeap, 0, pszServerName);
1487             }
1488             break;
1489         }
1490 
1491         case 5:
1492         {
1493             if (ppi5w->pPrinterName)
1494             {
1495                 PSTR pszPrinterName;
1496 
1497                 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
1498                 cch = wcslen(ppi5w->pPrinterName);
1499 
1500                 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1501                 if (!pszPrinterName)
1502                 {
1503                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1504                     ERR("HeapAlloc failed!\n");
1505                     goto Cleanup;
1506                 }
1507 
1508                 WideCharToMultiByte(CP_ACP, 0, ppi5w->pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
1509                 StringCchCopyA(ppi5a->pPrinterName, cch + 1, pszPrinterName);
1510 
1511                 HeapFree(hProcessHeap, 0, pszPrinterName);
1512             }
1513 
1514             if (ppi5w->pPortName)
1515             {
1516                 PSTR pszPortName;
1517 
1518                 // Convert Unicode pPortName to a ANSI string pszPortName.
1519                 cch = wcslen(ppi5w->pPortName);
1520 
1521                 pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1522                 if (!pszPortName)
1523                 {
1524                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1525                     ERR("HeapAlloc failed!\n");
1526                     goto Cleanup;
1527                 }
1528 
1529                 WideCharToMultiByte(CP_ACP, 0, ppi5w->pPortName, -1, pszPortName, cch + 1, NULL, NULL);
1530                 StringCchCopyA(ppi5a->pPortName, cch + 1, pszPortName);
1531 
1532                 HeapFree(hProcessHeap, 0, pszPortName);
1533             }
1534             break;
1535         }
1536 
1537         case 7:
1538         {
1539             if (ppi7w->pszObjectGUID)
1540             {
1541                 PSTR pszaObjectGUID;
1542 
1543                 // Convert Unicode pszObjectGUID to a ANSI string pszaObjectGUID.
1544                 cch = wcslen(ppi7w->pszObjectGUID);
1545 
1546                 pszaObjectGUID = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1547                 if (!pszaObjectGUID)
1548                 {
1549                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1550                     ERR("HeapAlloc failed!\n");
1551                     goto Cleanup;
1552                 }
1553 
1554                 WideCharToMultiByte(CP_ACP, 0, ppi7w->pszObjectGUID, -1, pszaObjectGUID, cch + 1, NULL, NULL);
1555                 StringCchCopyA(ppi7a->pszObjectGUID, cch + 1, pszaObjectGUID);
1556 
1557                 HeapFree(hProcessHeap, 0, pszaObjectGUID);
1558             }
1559             break;
1560         }
1561     }       // switch
1562 
1563 Cleanup:
1564     return bReturnValue;
1565 }
1566 
1567 BOOL WINAPI
1568 GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
1569 {
1570     /*
1571      * We are mapping multiple different pointers to the same pDriverInfo pointer here so that
1572      * we can use the same incoming pointer for different Levels
1573      */
1574     PDRIVER_INFO_1W pdi1w = (PDRIVER_INFO_1W)pDriverInfo;
1575     PDRIVER_INFO_2W pdi2w = (PDRIVER_INFO_2W)pDriverInfo;
1576     PDRIVER_INFO_3W pdi3w = (PDRIVER_INFO_3W)pDriverInfo;
1577     PDRIVER_INFO_4W pdi4w = (PDRIVER_INFO_4W)pDriverInfo;
1578     PDRIVER_INFO_5W pdi5w = (PDRIVER_INFO_5W)pDriverInfo;
1579     PDRIVER_INFO_6W pdi6w = (PDRIVER_INFO_6W)pDriverInfo;
1580 
1581     BOOL bReturnValue = FALSE;
1582     DWORD cch;
1583     PWSTR pwszEnvironment = NULL;
1584 
1585     TRACE("GetPrinterDriverA(%p, %s, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
1586 
1587     // Check for invalid levels here for early error return. Should be 1-6.
1588     if (Level <  1 || Level > 6)
1589     {
1590         SetLastError(ERROR_INVALID_LEVEL);
1591         ERR("Invalid Level!\n");
1592         goto Exit;
1593     }
1594 
1595     if (pEnvironment)
1596     {
1597         // Convert pEnvironment to a Unicode string pwszEnvironment.
1598         cch = strlen(pEnvironment);
1599 
1600         pwszEnvironment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1601         if (!pwszEnvironment)
1602         {
1603             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1604             ERR("HeapAlloc failed!\n");
1605             goto Exit;
1606         }
1607 
1608         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, pwszEnvironment, cch + 1);
1609     }
1610 
1611     bReturnValue = GetPrinterDriverW(hPrinter, pwszEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
1612     TRACE("*pcbNeeded is '%d' and bReturnValue is '%d' and GetLastError is '%ld'.\n", *pcbNeeded, bReturnValue, GetLastError());
1613 
1614     if (pwszEnvironment)
1615     {
1616         HeapFree(hProcessHeap, 0, pwszEnvironment);
1617     }
1618 
1619     if (!bReturnValue)
1620     {
1621         TRACE("GetPrinterDriverW failed!\n");
1622         goto Exit;
1623     }
1624 
1625     // Do Unicode to ANSI conversions for strings based on Level
1626     switch (Level)
1627     {
1628         case 1:
1629         {
1630             if (!UnicodeToAnsiInPlace(pdi1w->pName))
1631                 goto Exit;
1632 
1633             break;
1634         }
1635 
1636         case 2:
1637         {
1638             if (!UnicodeToAnsiInPlace(pdi2w->pName))
1639                 goto Exit;
1640 
1641             if (!UnicodeToAnsiInPlace(pdi2w->pEnvironment))
1642                 goto Exit;
1643 
1644             if (!UnicodeToAnsiInPlace(pdi2w->pDriverPath))
1645                 goto Exit;
1646 
1647             if (!UnicodeToAnsiInPlace(pdi2w->pDataFile))
1648                 goto Exit;
1649 
1650             if (!UnicodeToAnsiInPlace(pdi2w->pConfigFile))
1651                 goto Exit;
1652 
1653             break;
1654         }
1655 
1656         case 3:
1657         {
1658             if (!UnicodeToAnsiInPlace(pdi3w->pName))
1659                 goto Exit;
1660 
1661             if (!UnicodeToAnsiInPlace(pdi3w->pEnvironment))
1662                 goto Exit;
1663 
1664             if (!UnicodeToAnsiInPlace(pdi3w->pDriverPath))
1665                 goto Exit;
1666 
1667             if (!UnicodeToAnsiInPlace(pdi3w->pDataFile))
1668                 goto Exit;
1669 
1670             if (!UnicodeToAnsiInPlace(pdi3w->pConfigFile))
1671                 goto Exit;
1672 
1673             if (!UnicodeToAnsiInPlace(pdi3w->pHelpFile))
1674                 goto Exit;
1675 
1676             if (!UnicodeToAnsiInPlace(pdi3w->pDependentFiles))
1677                 goto Exit;
1678 
1679             if (!UnicodeToAnsiInPlace(pdi3w->pMonitorName))
1680                 goto Exit;
1681 
1682             if (!UnicodeToAnsiInPlace(pdi3w->pDefaultDataType))
1683                 goto Exit;
1684 
1685             break;
1686         }
1687 
1688         case 4:
1689         {
1690             if (!UnicodeToAnsiInPlace(pdi4w->pName))
1691                 goto Exit;
1692 
1693             if (!UnicodeToAnsiInPlace(pdi4w->pEnvironment))
1694                 goto Exit;
1695 
1696             if (!UnicodeToAnsiInPlace(pdi4w->pDriverPath))
1697                 goto Exit;
1698 
1699             if (!UnicodeToAnsiInPlace(pdi4w->pDataFile))
1700                 goto Exit;
1701 
1702             if (!UnicodeToAnsiInPlace(pdi4w->pConfigFile))
1703                 goto Exit;
1704 
1705             if (!UnicodeToAnsiInPlace(pdi4w->pHelpFile))
1706                 goto Exit;
1707 
1708             if (!UnicodeToAnsiInPlace(pdi4w->pDependentFiles))
1709                 goto Exit;
1710 
1711             if (!UnicodeToAnsiInPlace(pdi4w->pMonitorName))
1712                 goto Exit;
1713 
1714             if (!UnicodeToAnsiInPlace(pdi4w->pDefaultDataType))
1715                 goto Exit;
1716 
1717             if (!UnicodeToAnsiInPlace(pdi4w->pszzPreviousNames))
1718                 goto Exit;
1719 
1720             break;
1721         }
1722 
1723         case 5:
1724         {
1725             if (!UnicodeToAnsiInPlace(pdi5w->pName))
1726                 goto Exit;
1727 
1728             if (!UnicodeToAnsiInPlace(pdi5w->pEnvironment))
1729                 goto Exit;
1730 
1731             if (!UnicodeToAnsiInPlace(pdi5w->pDriverPath))
1732                 goto Exit;
1733 
1734             if (!UnicodeToAnsiInPlace(pdi5w->pDataFile))
1735                 goto Exit;
1736 
1737             if (!UnicodeToAnsiInPlace(pdi5w->pConfigFile))
1738                 goto Exit;
1739 
1740             break;
1741         }
1742 
1743         case 6:
1744         {
1745             if (!UnicodeToAnsiInPlace(pdi6w->pName))
1746                 goto Exit;
1747 
1748             if (!UnicodeToAnsiInPlace(pdi6w->pEnvironment))
1749                 goto Exit;
1750 
1751             if (!UnicodeToAnsiInPlace(pdi6w->pDriverPath))
1752                 goto Exit;
1753 
1754             if (!UnicodeToAnsiInPlace(pdi6w->pDataFile))
1755                 goto Exit;
1756 
1757             if (!UnicodeToAnsiInPlace(pdi6w->pConfigFile))
1758                 goto Exit;
1759 
1760             if (!UnicodeToAnsiInPlace(pdi6w->pHelpFile))
1761                 goto Exit;
1762 
1763             if (!UnicodeToAnsiInPlace(pdi6w->pDependentFiles))
1764                 goto Exit;
1765 
1766             if (!UnicodeToAnsiInPlace(pdi6w->pMonitorName))
1767                 goto Exit;
1768 
1769             if (!UnicodeToAnsiInPlace(pdi6w->pDefaultDataType))
1770                 goto Exit;
1771 
1772             if (!UnicodeToAnsiInPlace(pdi6w->pszzPreviousNames))
1773                 goto Exit;
1774 
1775             if (!UnicodeToAnsiInPlace(pdi6w->pszMfgName))
1776                 goto Exit;
1777 
1778             if (!UnicodeToAnsiInPlace(pdi6w->pszOEMUrl))
1779                 goto Exit;
1780 
1781             if (!UnicodeToAnsiInPlace(pdi6w->pszHardwareID))
1782                 goto Exit;
1783 
1784             if (!UnicodeToAnsiInPlace(pdi6w->pszProvider))
1785                 goto Exit;
1786         }
1787     }
1788 
1789     bReturnValue = TRUE;
1790 
1791 Exit:
1792 
1793     return bReturnValue;
1794 }
1795 
1796 BOOL WINAPI
1797 GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
1798 {
1799     DWORD dwErrorCode;
1800     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1801 
1802     TRACE("GetPrinterDriverW(%p, %S, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
1803 
1804     // Sanity checks.
1805     if (!pHandle)
1806     {
1807         dwErrorCode = ERROR_INVALID_HANDLE;
1808         goto Cleanup;
1809     }
1810 
1811     // Dismiss invalid levels already at this point.
1812     if (Level > 8 || Level < 1)
1813     {
1814         dwErrorCode = ERROR_INVALID_LEVEL;
1815         goto Cleanup;
1816     }
1817 
1818     if (cbBuf && pDriverInfo)
1819         ZeroMemory(pDriverInfo, cbBuf);
1820 
1821     // Do the RPC call
1822     RpcTryExcept
1823     {
1824         dwErrorCode = _RpcGetPrinterDriver(pHandle->hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
1825     }
1826     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1827     {
1828         dwErrorCode = RpcExceptionCode();
1829         ERR("_RpcGetPrinterDriver failed with exception code %lu!\n", dwErrorCode);
1830     }
1831     RpcEndExcept;
1832 
1833     if (dwErrorCode == ERROR_SUCCESS)
1834     {
1835         // Replace relative offset addresses in the output by absolute pointers.
1836         ASSERT(Level <= 5);
1837         MarshallUpStructure(cbBuf, pDriverInfo, pPrinterDriverMarshalling[Level]->pInfo, pPrinterDriverMarshalling[Level]->cbStructureSize, TRUE);
1838     }
1839 
1840 Cleanup:
1841     SetLastError(dwErrorCode);
1842     return (dwErrorCode == ERROR_SUCCESS);
1843 }
1844 
1845 BOOL WINAPI
1846 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
1847 {
1848     DWORD dwErrorCode;
1849     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1850 
1851     TRACE("GetPrinterW(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
1852 
1853     // Sanity checks.
1854     if (!pHandle)
1855     {
1856         dwErrorCode = ERROR_INVALID_HANDLE;
1857         goto Cleanup;
1858     }
1859 
1860     // Dismiss invalid levels already at this point.
1861     if (Level > 9)
1862     {
1863         dwErrorCode = ERROR_INVALID_LEVEL;
1864         goto Cleanup;
1865     }
1866 
1867     if (cbBuf && pPrinter)
1868         ZeroMemory(pPrinter, cbBuf);
1869 
1870     // Do the RPC call
1871     RpcTryExcept
1872     {
1873         dwErrorCode = _RpcGetPrinter(pHandle->hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
1874     }
1875     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1876     {
1877         dwErrorCode = RpcExceptionCode();
1878         ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
1879     }
1880     RpcEndExcept;
1881 
1882     if (dwErrorCode == ERROR_SUCCESS)
1883     {
1884         // Replace relative offset addresses in the output by absolute pointers.
1885         ASSERT(Level <= 9);
1886         MarshallUpStructure(cbBuf, pPrinter, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
1887     }
1888 
1889 Cleanup:
1890     SetLastError(dwErrorCode);
1891     return (dwErrorCode == ERROR_SUCCESS);
1892 }
1893 
1894 BOOL WINAPI
1895 OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
1896 {
1897     BOOL bReturnValue = FALSE;
1898     DWORD cch;
1899     PWSTR pwszPrinterName = NULL;
1900     PRINTER_DEFAULTSW wDefault = { 0 };
1901 
1902     TRACE("OpenPrinterA(%s, %p, %p)\n", pPrinterName, phPrinter, pDefault);
1903 
1904     if (pPrinterName)
1905     {
1906         // Convert pPrinterName to a Unicode string pwszPrinterName
1907         cch = strlen(pPrinterName);
1908 
1909         pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1910         if (!pwszPrinterName)
1911         {
1912             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1913             ERR("HeapAlloc failed!\n");
1914             goto Cleanup;
1915         }
1916 
1917         MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
1918     }
1919 
1920     if (pDefault)
1921     {
1922         wDefault.DesiredAccess = pDefault->DesiredAccess;
1923 
1924         if (pDefault->pDatatype)
1925         {
1926             // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
1927             cch = strlen(pDefault->pDatatype);
1928 
1929             wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1930             if (!wDefault.pDatatype)
1931             {
1932                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1933                 ERR("HeapAlloc failed!\n");
1934                 goto Cleanup;
1935             }
1936 
1937             MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
1938         }
1939 
1940         if (pDefault->pDevMode)
1941             wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
1942     }
1943 
1944     bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
1945 
1946 Cleanup:
1947     if (wDefault.pDatatype)
1948         HeapFree(hProcessHeap, 0, wDefault.pDatatype);
1949 
1950     if (wDefault.pDevMode)
1951         HeapFree(hProcessHeap, 0, wDefault.pDevMode);
1952 
1953     if (pwszPrinterName)
1954         HeapFree(hProcessHeap, 0, pwszPrinterName);
1955 
1956     return bReturnValue;
1957 }
1958 
1959 BOOL WINAPI
1960 OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
1961 {
1962     DWORD dwErrorCode;
1963     HANDLE hPrinter;
1964     PSPOOLER_HANDLE pHandle;
1965     PWSTR pDatatype = NULL;
1966     WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
1967     ACCESS_MASK AccessRequired = 0;
1968 
1969     TRACE("OpenPrinterW(%S, %p, %p)\n", pPrinterName, phPrinter, pDefault);
1970 
1971     // Sanity check
1972     if (!phPrinter)
1973     {
1974         dwErrorCode = ERROR_INVALID_PARAMETER;
1975         goto Cleanup;
1976     }
1977 
1978     // Prepare the additional parameters in the format required by _RpcOpenPrinter
1979     if (pDefault)
1980     {
1981         pDatatype = pDefault->pDatatype;
1982         DevModeContainer.cbBuf = sizeof(DEVMODEW);
1983         DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
1984         AccessRequired = pDefault->DesiredAccess;
1985     }
1986 
1987     // Do the RPC call
1988     RpcTryExcept
1989     {
1990         dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
1991     }
1992     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1993     {
1994         dwErrorCode = RpcExceptionCode();
1995         ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
1996     }
1997     RpcEndExcept;
1998 
1999     if (dwErrorCode == ERROR_SUCCESS)
2000     {
2001         // Create a new SPOOLER_HANDLE structure.
2002         pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
2003         if (!pHandle)
2004         {
2005             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2006             ERR("HeapAlloc failed!\n");
2007             goto Cleanup;
2008         }
2009 
2010         pHandle->hPrinter = hPrinter;
2011         pHandle->hSPLFile = INVALID_HANDLE_VALUE;
2012 
2013         // Return it as phPrinter.
2014         *phPrinter = (HANDLE)pHandle;
2015     }
2016 
2017 Cleanup:
2018     SetLastError(dwErrorCode);
2019     return (dwErrorCode == ERROR_SUCCESS);
2020 }
2021 
2022 BOOL WINAPI
2023 ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
2024 {
2025     DWORD dwErrorCode;
2026     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2027 
2028     TRACE("ReadPrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pNoBytesRead);
2029 
2030     // Sanity checks.
2031     if (!pHandle)
2032     {
2033         dwErrorCode = ERROR_INVALID_HANDLE;
2034         goto Cleanup;
2035     }
2036 
2037     // Do the RPC call
2038     RpcTryExcept
2039     {
2040         dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
2041     }
2042     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2043     {
2044         dwErrorCode = RpcExceptionCode();
2045         ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
2046     }
2047     RpcEndExcept;
2048 
2049 Cleanup:
2050     SetLastError(dwErrorCode);
2051     return (dwErrorCode == ERROR_SUCCESS);
2052 }
2053 
2054 BOOL WINAPI
2055 ResetPrinterA(HANDLE hPrinter, PPRINTER_DEFAULTSA pDefault)
2056 {
2057     TRACE("ResetPrinterA(%p, %p)\n", hPrinter, pDefault);
2058     UNIMPLEMENTED;
2059     return FALSE;
2060 }
2061 
2062 BOOL WINAPI
2063 ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
2064 {
2065     TRACE("ResetPrinterW(%p, %p)\n", hPrinter, pDefault);
2066     UNIMPLEMENTED;
2067     return FALSE;
2068 }
2069 
2070 BOOL WINAPI
2071 SetDefaultPrinterA(LPCSTR pszPrinter)
2072 {
2073     BOOL bReturnValue = FALSE;
2074     DWORD cch;
2075     PWSTR pwszPrinter = NULL;
2076 
2077     TRACE("SetDefaultPrinterA(%s)\n", pszPrinter);
2078 
2079     if (pszPrinter)
2080     {
2081         // Convert pszPrinter to a Unicode string pwszPrinter
2082         cch = strlen(pszPrinter);
2083 
2084         pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2085         if (!pwszPrinter)
2086         {
2087             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2088             ERR("HeapAlloc failed!\n");
2089             goto Cleanup;
2090         }
2091 
2092         MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, pwszPrinter, cch + 1);
2093     }
2094 
2095     bReturnValue = SetDefaultPrinterW(pwszPrinter);
2096 
2097 Cleanup:
2098     if (pwszPrinter)
2099         HeapFree(hProcessHeap, 0, pwszPrinter);
2100 
2101     return bReturnValue;
2102 }
2103 
2104 BOOL WINAPI
2105 SetDefaultPrinterW(LPCWSTR pszPrinter)
2106 {
2107     const WCHAR wszDevicesKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
2108 
2109     DWORD cbDeviceValueData;
2110     DWORD cbPrinterValueData = 0;
2111     DWORD cchPrinter;
2112     DWORD dwErrorCode;
2113     HKEY hDevicesKey = NULL;
2114     HKEY hWindowsKey = NULL;
2115     PWSTR pwszDeviceValueData = NULL;
2116     WCHAR wszPrinter[MAX_PRINTER_NAME + 1];
2117 
2118     TRACE("SetDefaultPrinterW(%S)\n", pszPrinter);
2119 
2120     // Open the Devices registry key.
2121     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszDevicesKey, 0, KEY_READ, &hDevicesKey);
2122     if (dwErrorCode != ERROR_SUCCESS)
2123     {
2124         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
2125         goto Cleanup;
2126     }
2127 
2128     // Did the caller give us a printer to set as default?
2129     if (pszPrinter && *pszPrinter)
2130     {
2131         // Check if the given printer exists and query the value data size.
2132         dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, NULL, &cbPrinterValueData);
2133         if (dwErrorCode == ERROR_FILE_NOT_FOUND)
2134         {
2135             dwErrorCode = ERROR_INVALID_PRINTER_NAME;
2136             goto Cleanup;
2137         }
2138         else if (dwErrorCode != ERROR_SUCCESS)
2139         {
2140             ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
2141             goto Cleanup;
2142         }
2143 
2144         cchPrinter = wcslen(pszPrinter);
2145     }
2146     else
2147     {
2148         // If there is already a default printer, we're done!
2149         cchPrinter = _countof(wszPrinter);
2150         if (GetDefaultPrinterW(wszPrinter, &cchPrinter))
2151         {
2152             dwErrorCode = ERROR_SUCCESS;
2153             goto Cleanup;
2154         }
2155 
2156         // Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
2157         cchPrinter = _countof(wszPrinter);
2158         dwErrorCode = (DWORD)RegEnumValueW(hDevicesKey, 0, wszPrinter, &cchPrinter, NULL, NULL, NULL, &cbPrinterValueData);
2159         if (dwErrorCode != ERROR_MORE_DATA)
2160             goto Cleanup;
2161 
2162         pszPrinter = wszPrinter;
2163     }
2164 
2165     // We now need to query the value data, which has the format "winspool,<Port>:"
2166     // and make "<Printer Name>,winspool,<Port>:" out of it.
2167     // Allocate a buffer large enough for the final data.
2168     cbDeviceValueData = (cchPrinter + 1) * sizeof(WCHAR) + cbPrinterValueData;
2169     pwszDeviceValueData = HeapAlloc(hProcessHeap, 0, cbDeviceValueData);
2170     if (!pwszDeviceValueData)
2171     {
2172         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2173         ERR("HeapAlloc failed!\n");
2174         goto Cleanup;
2175     }
2176 
2177     // Copy the Printer Name and a comma into it.
2178     CopyMemory(pwszDeviceValueData, pszPrinter, cchPrinter * sizeof(WCHAR));
2179     pwszDeviceValueData[cchPrinter] = L',';
2180 
2181     // Append the value data, which has the format "winspool,<Port>:"
2182     dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, (PBYTE)&pwszDeviceValueData[cchPrinter + 1], &cbPrinterValueData);
2183     if (dwErrorCode != ERROR_SUCCESS)
2184         goto Cleanup;
2185 
2186     // Open the Windows registry key.
2187     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_SET_VALUE, &hWindowsKey);
2188     if (dwErrorCode != ERROR_SUCCESS)
2189     {
2190         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
2191         goto Cleanup;
2192     }
2193 
2194     // Store our new default printer.
2195     dwErrorCode = (DWORD)RegSetValueExW(hWindowsKey, wszDeviceValue, 0, REG_SZ, (PBYTE)pwszDeviceValueData, cbDeviceValueData);
2196     if (dwErrorCode != ERROR_SUCCESS)
2197     {
2198         ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
2199         goto Cleanup;
2200     }
2201 
2202 Cleanup:
2203     if (hDevicesKey)
2204         RegCloseKey(hDevicesKey);
2205 
2206     if (hWindowsKey)
2207         RegCloseKey(hWindowsKey);
2208 
2209     if (pwszDeviceValueData)
2210         HeapFree(hProcessHeap, 0, pwszDeviceValueData);
2211 
2212     SetLastError(dwErrorCode);
2213     return (dwErrorCode == ERROR_SUCCESS);
2214 }
2215 
2216 BOOL WINAPI
2217 SetPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
2218 {
2219     TRACE("SetPrinterA(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
2220     UNIMPLEMENTED;
2221     return FALSE;
2222 }
2223 
2224 BOOL WINAPI
2225 SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
2226 {
2227     TRACE("SetPrinterW(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
2228     UNIMPLEMENTED;
2229     return FALSE;
2230 }
2231 
2232 BOOL WINAPI
2233 SplDriverUnloadComplete(LPWSTR pDriverFile)
2234 {
2235     TRACE("DriverUnloadComplete(%S)\n", pDriverFile);
2236     UNIMPLEMENTED;
2237     return TRUE; // return true for now.
2238 }
2239 
2240 DWORD WINAPI
2241 StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
2242 {
2243     DOC_INFO_1W wDocInfo1 = { 0 };
2244     DWORD cch;
2245     DWORD dwErrorCode;
2246     DWORD dwReturnValue = 0;
2247     PDOC_INFO_1A pDocInfo1 = (PDOC_INFO_1A)pDocInfo;
2248 
2249     TRACE("StartDocPrinterA(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
2250 
2251     // Only check the minimum required for accessing pDocInfo.
2252     // Additional sanity checks are done in StartDocPrinterW.
2253     if (!pDocInfo1)
2254     {
2255         dwErrorCode = ERROR_INVALID_PARAMETER;
2256         goto Cleanup;
2257     }
2258 
2259     if (Level != 1)
2260     {
2261         dwErrorCode = ERROR_INVALID_LEVEL;
2262         goto Cleanup;
2263     }
2264 
2265     if (pDocInfo1->pDatatype)
2266     {
2267         // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
2268         cch = strlen(pDocInfo1->pDatatype);
2269 
2270         wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2271         if (!wDocInfo1.pDatatype)
2272         {
2273             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2274             ERR("HeapAlloc failed!\n");
2275             goto Cleanup;
2276         }
2277 
2278         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDatatype, -1, wDocInfo1.pDatatype, cch + 1);
2279     }
2280 
2281     if (pDocInfo1->pDocName)
2282     {
2283         // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
2284         cch = strlen(pDocInfo1->pDocName);
2285 
2286         wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2287         if (!wDocInfo1.pDocName)
2288         {
2289             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2290             ERR("HeapAlloc failed!\n");
2291             goto Cleanup;
2292         }
2293 
2294         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDocName, -1, wDocInfo1.pDocName, cch + 1);
2295     }
2296 
2297     if (pDocInfo1->pOutputFile)
2298     {
2299         // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
2300         cch = strlen(pDocInfo1->pOutputFile);
2301 
2302         wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2303         if (!wDocInfo1.pOutputFile)
2304         {
2305             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2306             ERR("HeapAlloc failed!\n");
2307             goto Cleanup;
2308         }
2309 
2310         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pOutputFile, -1, wDocInfo1.pOutputFile, cch + 1);
2311     }
2312 
2313     dwReturnValue = StartDocPrinterW(hPrinter, Level, (PBYTE)&wDocInfo1);
2314     dwErrorCode = GetLastError();
2315 
2316 Cleanup:
2317     if (wDocInfo1.pDatatype)
2318         HeapFree(hProcessHeap, 0, wDocInfo1.pDatatype);
2319 
2320     if (wDocInfo1.pDocName)
2321         HeapFree(hProcessHeap, 0, wDocInfo1.pDocName);
2322 
2323     if (wDocInfo1.pOutputFile)
2324         HeapFree(hProcessHeap, 0, wDocInfo1.pOutputFile);
2325 
2326     SetLastError(dwErrorCode);
2327     return dwReturnValue;
2328 }
2329 
2330 DWORD WINAPI
2331 StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
2332 {
2333     DWORD cbAddJobInfo1;
2334     DWORD cbNeeded;
2335     DWORD dwErrorCode;
2336     DWORD dwReturnValue = 0;
2337     PADDJOB_INFO_1W pAddJobInfo1 = NULL;
2338     PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
2339     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2340 
2341     TRACE("StartDocPrinterW(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
2342 
2343     // Sanity checks.
2344     if (!pHandle)
2345     {
2346         dwErrorCode = ERROR_INVALID_HANDLE;
2347         goto Cleanup;
2348     }
2349 
2350     if (!pDocInfo1)
2351     {
2352         dwErrorCode = ERROR_INVALID_PARAMETER;
2353         goto Cleanup;
2354     }
2355 
2356     if (Level != 1)
2357     {
2358         dwErrorCode = ERROR_INVALID_LEVEL;
2359         goto Cleanup;
2360     }
2361 
2362     if (pHandle->bStartedDoc)
2363     {
2364         dwErrorCode = ERROR_INVALID_PRINTER_STATE;
2365         goto Cleanup;
2366     }
2367 
2368     // Check if we want to redirect output into a file.
2369     if (pDocInfo1->pOutputFile)
2370     {
2371         // Do a StartDocPrinter RPC call in this case.
2372         dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
2373     }
2374     else
2375     {
2376         // Allocate memory for the ADDJOB_INFO_1W structure and a path.
2377         cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
2378         pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
2379         if (!pAddJobInfo1)
2380         {
2381             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2382             ERR("HeapAlloc failed!\n");
2383             goto Cleanup;
2384         }
2385 
2386         // Try to add a new job.
2387         // This only succeeds if the printer is set to do spooled printing.
2388         if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
2389         {
2390             // Do spooled printing.
2391             dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
2392         }
2393         else if (GetLastError() == ERROR_INVALID_ACCESS)
2394         {
2395             // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
2396             // In this case, we do a StartDocPrinter RPC call.
2397             dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
2398         }
2399         else
2400         {
2401             dwErrorCode = GetLastError();
2402             ERR("AddJobW failed with error %lu!\n", dwErrorCode);
2403             goto Cleanup;
2404         }
2405     }
2406 
2407     if (dwErrorCode == ERROR_SUCCESS)
2408     {
2409         pHandle->bStartedDoc = TRUE;
2410         dwReturnValue = pHandle->dwJobID;
2411     }
2412 
2413 Cleanup:
2414     if (pAddJobInfo1)
2415         HeapFree(hProcessHeap, 0, pAddJobInfo1);
2416 
2417     SetLastError(dwErrorCode);
2418     return dwReturnValue;
2419 }
2420 
2421 BOOL WINAPI
2422 StartPagePrinter(HANDLE hPrinter)
2423 {
2424     DWORD dwErrorCode;
2425     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2426 
2427     TRACE("StartPagePrinter(%p)\n", hPrinter);
2428 
2429     // Sanity checks.
2430     if (!pHandle)
2431     {
2432         dwErrorCode = ERROR_INVALID_HANDLE;
2433         goto Cleanup;
2434     }
2435 
2436     // Do the RPC call
2437     RpcTryExcept
2438     {
2439         dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
2440     }
2441     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2442     {
2443         dwErrorCode = RpcExceptionCode();
2444         ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
2445     }
2446     RpcEndExcept;
2447 
2448 Cleanup:
2449     SetLastError(dwErrorCode);
2450     return (dwErrorCode == ERROR_SUCCESS);
2451 }
2452 
2453 BOOL WINAPI
2454 WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
2455 {
2456     DWORD dwErrorCode;
2457     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2458 
2459     TRACE("WritePrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
2460 
2461     // Sanity checks.
2462     if (!pHandle)
2463     {
2464         dwErrorCode = ERROR_INVALID_HANDLE;
2465         goto Cleanup;
2466     }
2467 
2468     if (!pHandle->bStartedDoc)
2469     {
2470         dwErrorCode = ERROR_SPL_NO_STARTDOC;
2471         goto Cleanup;
2472     }
2473 
2474     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
2475     {
2476         // Write to the spool file. This doesn't need an RPC request.
2477         if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
2478         {
2479             dwErrorCode = GetLastError();
2480             ERR("WriteFile failed with error %lu!\n", dwErrorCode);
2481             goto Cleanup;
2482         }
2483 
2484         dwErrorCode = ERROR_SUCCESS;
2485     }
2486     else
2487     {
2488         // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
2489         // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
2490 
2491         // Do the RPC call
2492         RpcTryExcept
2493         {
2494             dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
2495         }
2496         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2497         {
2498             dwErrorCode = RpcExceptionCode();
2499             ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
2500         }
2501         RpcEndExcept;
2502     }
2503 
2504 Cleanup:
2505     SetLastError(dwErrorCode);
2506     return (dwErrorCode == ERROR_SUCCESS);
2507 }
2508 
2509 BOOL WINAPI
2510 XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
2511 {
2512     TRACE("XcvDataW(%p, %S, %p, %lu, %p, %lu, %p, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus);
2513     return FALSE;
2514 }
2515