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     DWORD dwErrorCode;
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         dwErrorCode = 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             dwErrorCode = 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     if (!EnumPrintersW(Flags, pwszName, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned))
528     {
529         dwErrorCode = GetLastError();
530         goto Cleanup;
531     }
532 
533     /* We are mapping multiple different pointers to the same pPrinterEnum pointer here so that */
534     /* we can do in-place conversion. We read the Unicode response from the EnumPrintersW and */
535     /* then we write back the ANSI conversion into the same buffer for our EnumPrintersA output */
536 
537     /* mapping to pPrinterEnum for Unicode (w) characters for Levels 1, 2, 4, and 5 */
538     ppi1w = (PPRINTER_INFO_1W)pPrinterEnum;
539     ppi2w = (PPRINTER_INFO_2W)pPrinterEnum;
540     ppi4w = (PPRINTER_INFO_4W)pPrinterEnum;
541     ppi5w = (PPRINTER_INFO_5W)pPrinterEnum;
542     /* mapping to pPrinterEnum for ANSI (a) characters for Levels 1, 2, 4, and 5 */
543     ppi1a = (PPRINTER_INFO_1A)pPrinterEnum;
544     ppi2a = (PPRINTER_INFO_2A)pPrinterEnum;
545     ppi4a = (PPRINTER_INFO_4A)pPrinterEnum;
546     ppi5a = (PPRINTER_INFO_5A)pPrinterEnum;
547 
548     for (i = 0; i < *pcReturned; i++)
549     {
550         switch (Level)
551         {
552             case 1:
553             {
554                 if (ppi1w[i].pDescription)
555                 {
556                     // Convert Unicode pDescription to a ANSI string pszDescription.
557                     cch = wcslen(ppi1w[i].pDescription);
558 
559                     pszDescription = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
560                     if (!pszDescription)
561                     {
562                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
563                         ERR("HeapAlloc failed!\n");
564                         goto Cleanup;
565                     }
566 
567                     WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pDescription, -1, pszDescription, cch + 1, NULL, NULL);
568                     StringCchCopyA(ppi1a[i].pDescription, cch + 1, pszDescription);
569 
570                     HeapFree(hProcessHeap, 0, pszDescription);
571                 }
572 
573                 if (ppi1w[i].pName)
574                 {
575                     // Convert Unicode pName to a ANSI string pszName.
576                     cch = wcslen(ppi1w[i].pName);
577 
578                     pszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
579                     if (!pszName)
580                     {
581                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
582                         ERR("HeapAlloc failed!\n");
583                         goto Cleanup;
584                     }
585 
586                     WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pName, -1, pszName, cch + 1, NULL, NULL);
587                     StringCchCopyA(ppi1a[i].pName, cch + 1, pszName);
588 
589                     HeapFree(hProcessHeap, 0, pszName);
590                 }
591 
592                 if (ppi1w[i].pComment)
593                 {
594                     // Convert Unicode pComment to a ANSI string pszComment.
595                     cch = wcslen(ppi1w[i].pComment);
596 
597                     pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
598                     if (!pszComment)
599                     {
600                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
601                         ERR("HeapAlloc failed!\n");
602                         goto Cleanup;
603                     }
604 
605                     WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pComment, -1, pszComment, cch + 1, NULL, NULL);
606                     StringCchCopyA(ppi1a[i].pComment, cch + 1, pszComment);
607 
608                     HeapFree(hProcessHeap, 0, pszComment);
609                 }
610                 break;
611             }
612 
613 
614             case 2:
615             {
616                 if (ppi2w[i].pServerName)
617                 {
618                     // Convert Unicode pServerName to a ANSI string pszServerName.
619                     cch = wcslen(ppi2w[i].pServerName);
620 
621                     pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
622                     if (!pszServerName)
623                     {
624                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
625                         ERR("HeapAlloc failed!\n");
626                         goto Cleanup;
627                     }
628 
629                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pServerName, -1, pszServerName, cch + 1, NULL, NULL);
630                     StringCchCopyA(ppi2a[i].pServerName, cch + 1, pszServerName);
631 
632                     HeapFree(hProcessHeap, 0, pszServerName);
633                 }
634 
635                 if (ppi2w[i].pPrinterName)
636                 {
637                     // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
638                     cch = wcslen(ppi2w[i].pPrinterName);
639 
640                     pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
641                     if (!pszPrinterName)
642                     {
643                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
644                         ERR("HeapAlloc failed!\n");
645                         goto Cleanup;
646                     }
647 
648                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
649                     StringCchCopyA(ppi2a[i].pPrinterName, cch + 1, pszPrinterName);
650 
651                     HeapFree(hProcessHeap, 0, pszPrinterName);
652                 }
653 
654                 if (ppi2w[i].pShareName)
655                 {
656                     // Convert Unicode pShareName to a ANSI string pszShareName.
657                     cch = wcslen(ppi2w[i].pShareName);
658 
659                     pszShareName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
660                     if (!pszShareName)
661                     {
662                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
663                         ERR("HeapAlloc failed!\n");
664                         goto Cleanup;
665                     }
666 
667                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pShareName, -1, pszShareName, cch + 1, NULL, NULL);
668                     StringCchCopyA(ppi2a[i].pShareName, cch + 1, pszShareName);
669 
670                     HeapFree(hProcessHeap, 0, pszShareName);
671                 }
672 
673                 if (ppi2w[i].pPortName)
674                 {
675                     // Convert Unicode pPortName to a ANSI string pszPortName.
676                     cch = wcslen(ppi2w[i].pPortName);
677 
678                     pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
679                     if (!pszPortName)
680                     {
681                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
682                         ERR("HeapAlloc failed!\n");
683                         goto Cleanup;
684                     }
685 
686                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPortName, -1, pszPortName, cch + 1, NULL, NULL);
687                     StringCchCopyA(ppi2a[i].pPortName, cch + 1, pszPortName);
688 
689                     HeapFree(hProcessHeap, 0, pszPortName);
690                 }
691 
692                 if (ppi2w[i].pDriverName)
693                 {
694                     // Convert Unicode pDriverName to a ANSI string pszDriverName.
695                     cch = wcslen(ppi2w[i].pDriverName);
696 
697                     pszDriverName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
698                     if (!pszDriverName)
699                     {
700                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
701                         ERR("HeapAlloc failed!\n");
702                         goto Cleanup;
703                     }
704 
705                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pDriverName, -1, pszDriverName, cch + 1, NULL, NULL);
706                     StringCchCopyA(ppi2a[i].pDriverName, cch + 1, pszDriverName);
707 
708                     HeapFree(hProcessHeap, 0, pszDriverName);
709                 }
710 
711                 if (ppi2w[i].pComment)
712                 {
713                     // Convert Unicode pComment to a ANSI string pszComment.
714                     cch = wcslen(ppi2w[i].pComment);
715 
716                     pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
717                     if (!pszComment)
718                     {
719                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
720                         ERR("HeapAlloc failed!\n");
721                         goto Cleanup;
722                     }
723 
724                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pComment, -1, pszComment, cch + 1, NULL, NULL);
725                     StringCchCopyA(ppi2a[i].pComment, cch + 1, pszComment);
726 
727                     HeapFree(hProcessHeap, 0, pszComment);
728                 }
729 
730                 if (ppi2w[i].pLocation)
731                 {
732                     // Convert Unicode pLocation to a ANSI string pszLocation.
733                     cch = wcslen(ppi2w[i].pLocation);
734 
735                     pszLocation = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
736                     if (!pszLocation)
737                     {
738                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
739                         ERR("HeapAlloc failed!\n");
740                         goto Cleanup;
741                     }
742 
743                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pLocation, -1, pszLocation, cch + 1, NULL, NULL);
744                     StringCchCopyA(ppi2a[i].pLocation, cch + 1, pszLocation);
745 
746                     HeapFree(hProcessHeap, 0, pszLocation);
747                 }
748 
749 
750                 if (ppi2w[i].pSepFile)
751                 {
752                     // Convert Unicode pSepFile to a ANSI string pszSepFile.
753                     cch = wcslen(ppi2w[i].pSepFile);
754 
755                     pszSepFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
756                     if (!pszSepFile)
757                     {
758                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
759                         ERR("HeapAlloc failed!\n");
760                         goto Cleanup;
761                     }
762 
763                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pSepFile, -1, pszSepFile, cch + 1, NULL, NULL);
764                     StringCchCopyA(ppi2a[i].pSepFile, cch + 1, pszSepFile);
765 
766                     HeapFree(hProcessHeap, 0, pszSepFile);
767                 }
768 
769                 if (ppi2w[i].pPrintProcessor)
770                 {
771                     // Convert Unicode pPrintProcessor to a ANSI string pszPrintProcessor.
772                     cch = wcslen(ppi2w[i].pPrintProcessor);
773 
774                     pszPrintProcessor = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
775                     if (!pszPrintProcessor)
776                     {
777                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
778                         ERR("HeapAlloc failed!\n");
779                         goto Cleanup;
780                     }
781 
782                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPrintProcessor, -1, pszPrintProcessor, cch + 1, NULL, NULL);
783                     StringCchCopyA(ppi2a[i].pPrintProcessor, cch + 1, pszPrintProcessor);
784 
785                     HeapFree(hProcessHeap, 0, pszPrintProcessor);
786                 }
787 
788 
789                 if (ppi2w[i].pDatatype)
790                 {
791                     // Convert Unicode pDatatype to a ANSI string pszDatatype.
792                     cch = wcslen(ppi2w[i].pDatatype);
793 
794                     pszDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
795                     if (!pszDatatype)
796                     {
797                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
798                         ERR("HeapAlloc failed!\n");
799                         goto Cleanup;
800                     }
801 
802                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pDatatype, -1, pszDatatype, cch + 1, NULL, NULL);
803                     StringCchCopyA(ppi2a[i].pDatatype, cch + 1, pszDatatype);
804 
805                     HeapFree(hProcessHeap, 0, pszDatatype);
806                 }
807 
808                 if (ppi2w[i].pParameters)
809                 {
810                     // Convert Unicode pParameters to a ANSI string pszParameters.
811                     cch = wcslen(ppi2w[i].pParameters);
812 
813                     pszParameters = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
814                     if (!pszParameters)
815                     {
816                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
817                         ERR("HeapAlloc failed!\n");
818                         goto Cleanup;
819                     }
820 
821                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pParameters, -1, pszParameters, cch + 1, NULL, NULL);
822                     StringCchCopyA(ppi2a[i].pParameters, cch + 1, pszParameters);
823 
824                     HeapFree(hProcessHeap, 0, pszParameters);
825                 }
826                 break;
827 
828             }
829 
830             case 4:
831             {
832                 if (ppi4w[i].pPrinterName)
833                 {
834                     // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
835                     cch = wcslen(ppi4w[i].pPrinterName);
836 
837                     pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
838                     if (!pszPrinterName)
839                     {
840                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
841                         ERR("HeapAlloc failed!\n");
842                         goto Cleanup;
843                     }
844 
845                     WideCharToMultiByte(CP_ACP, 0, ppi4w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
846                     StringCchCopyA(ppi4a[i].pPrinterName, cch + 1, pszPrinterName);
847 
848                     HeapFree(hProcessHeap, 0, pszPrinterName);
849                 }
850 
851                 if (ppi4w[i].pServerName)
852                 {
853                     // Convert Unicode pServerName to a ANSI string pszServerName.
854                     cch = wcslen(ppi4w[i].pServerName);
855 
856                     pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
857                     if (!pszServerName)
858                     {
859                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
860                         ERR("HeapAlloc failed!\n");
861                         goto Cleanup;
862                     }
863 
864                     WideCharToMultiByte(CP_ACP, 0, ppi4w[i].pServerName, -1, pszServerName, cch + 1, NULL, NULL);
865                     StringCchCopyA(ppi4a[i].pServerName, cch + 1, pszServerName);
866 
867                     HeapFree(hProcessHeap, 0, pszServerName);
868                 }
869                 break;
870             }
871 
872             case 5:
873             {
874                 if (ppi5w[i].pPrinterName)
875                 {
876                     // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
877                     cch = wcslen(ppi5w[i].pPrinterName);
878 
879                     pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
880                     if (!pszPrinterName)
881                     {
882                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
883                         ERR("HeapAlloc failed!\n");
884                         goto Cleanup;
885                     }
886 
887                     WideCharToMultiByte(CP_ACP, 0, ppi5w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
888                     StringCchCopyA(ppi5a[i].pPrinterName, cch + 1, pszPrinterName);
889 
890                     HeapFree(hProcessHeap, 0, pszPrinterName);
891                 }
892 
893                 if (ppi5w[i].pPortName)
894                 {
895                     // Convert Unicode pPortName to a ANSI string pszPortName.
896                     cch = wcslen(ppi5w[i].pPortName);
897 
898                     pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
899                     if (!pszPortName)
900                     {
901                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
902                         ERR("HeapAlloc failed!\n");
903                         goto Cleanup;
904                     }
905 
906                     WideCharToMultiByte(CP_ACP, 0, ppi5w[i].pPortName, -1, pszPortName, cch + 1, NULL, NULL);
907                     StringCchCopyA(ppi5a[i].pPortName, cch + 1, pszPortName);
908 
909                     HeapFree(hProcessHeap, 0, pszPortName);
910                 }
911                 break;
912             }
913 
914         }   // switch
915     }       // for
916 
917     dwErrorCode = ERROR_SUCCESS;
918 
919 Cleanup:
920     if (pwszName)
921     {
922         HeapFree(hProcessHeap, 0, pwszName);
923     }
924 
925     SetLastError(dwErrorCode);
926     return (dwErrorCode == ERROR_SUCCESS);
927 }
928 
929 BOOL WINAPI
930 EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
931 {
932     DWORD dwErrorCode;
933 
934     TRACE("EnumPrintersW(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
935 
936     // Dismiss invalid levels already at this point.
937     if (Level == 3 || Level > 5)
938     {
939         dwErrorCode = ERROR_INVALID_LEVEL;
940         goto Cleanup;
941     }
942 
943     if (cbBuf && pPrinterEnum)
944         ZeroMemory(pPrinterEnum, cbBuf);
945 
946     // Do the RPC call
947     RpcTryExcept
948     {
949         dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
950     }
951     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
952     {
953         dwErrorCode = RpcExceptionCode();
954         ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
955     }
956     RpcEndExcept;
957 
958     if (dwErrorCode == ERROR_SUCCESS)
959     {
960         // Replace relative offset addresses in the output by absolute pointers.
961         ASSERT(Level <= 9);
962         MarshallUpStructuresArray(cbBuf, pPrinterEnum, *pcReturned, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
963     }
964 
965 Cleanup:
966     SetLastError(dwErrorCode);
967     return (dwErrorCode == ERROR_SUCCESS);
968 }
969 
970 BOOL WINAPI
971 FlushPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten, DWORD cSleep)
972 {
973     TRACE("FlushPrinter(%p, %p, %lu, %p, %lu)\n", hPrinter, pBuf, cbBuf, pcWritten, cSleep);
974     UNIMPLEMENTED;
975     return FALSE;
976 }
977 
978 BOOL WINAPI
979 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
980 {
981     DWORD dwErrorCode;
982     PWSTR pwszBuffer = NULL;
983 
984     TRACE("GetDefaultPrinterA(%p, %p)\n", pszBuffer, pcchBuffer);
985 
986     // Sanity check.
987     if (!pcchBuffer)
988     {
989         dwErrorCode = ERROR_INVALID_PARAMETER;
990         goto Cleanup;
991     }
992 
993     // Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
994     if (pszBuffer && *pcchBuffer)
995     {
996         pwszBuffer = HeapAlloc(hProcessHeap, 0, *pcchBuffer * sizeof(WCHAR));
997         if (!pwszBuffer)
998         {
999             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1000             ERR("HeapAlloc failed!\n");
1001             goto Cleanup;
1002         }
1003     }
1004 
1005     if (!GetDefaultPrinterW(pwszBuffer, pcchBuffer))
1006     {
1007         dwErrorCode = GetLastError();
1008         goto Cleanup;
1009     }
1010 
1011     // We successfully got a string in pwszBuffer, so convert the Unicode string to ANSI.
1012     WideCharToMultiByte(CP_ACP, 0, pwszBuffer, -1, pszBuffer, *pcchBuffer, NULL, NULL);
1013 
1014     dwErrorCode = ERROR_SUCCESS;
1015 
1016 Cleanup:
1017     if (pwszBuffer)
1018         HeapFree(hProcessHeap, 0, pwszBuffer);
1019 
1020     SetLastError(dwErrorCode);
1021     return (dwErrorCode == ERROR_SUCCESS);
1022 }
1023 
1024 BOOL WINAPI
1025 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
1026 {
1027     DWORD cbNeeded;
1028     DWORD cchInputBuffer;
1029     DWORD dwErrorCode;
1030     HKEY hWindowsKey = NULL;
1031     PWSTR pwszDevice = NULL;
1032     PWSTR pwszComma;
1033 
1034     TRACE("GetDefaultPrinterW(%p, %p)\n", pszBuffer, pcchBuffer);
1035 
1036     // Sanity check.
1037     if (!pcchBuffer)
1038     {
1039         dwErrorCode = ERROR_INVALID_PARAMETER;
1040         goto Cleanup;
1041     }
1042 
1043     cchInputBuffer = *pcchBuffer;
1044 
1045     // Open the registry key where the default printer for the current user is stored.
1046     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_READ, &hWindowsKey);
1047     if (dwErrorCode != ERROR_SUCCESS)
1048     {
1049         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
1050         goto Cleanup;
1051     }
1052 
1053     // Determine the size of the required buffer.
1054     dwErrorCode = (DWORD)RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, NULL, &cbNeeded);
1055     if (dwErrorCode != ERROR_SUCCESS)
1056     {
1057         ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
1058         goto Cleanup;
1059     }
1060 
1061     // Allocate it.
1062     pwszDevice = HeapAlloc(hProcessHeap, 0, cbNeeded);
1063     if (!pwszDevice)
1064     {
1065         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1066         ERR("HeapAlloc failed!\n");
1067         goto Cleanup;
1068     }
1069 
1070     // Now get the actual value.
1071     dwErrorCode = RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, (PBYTE)pwszDevice, &cbNeeded);
1072     if (dwErrorCode != ERROR_SUCCESS)
1073     {
1074         ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
1075         goto Cleanup;
1076     }
1077 
1078     // We get a string "<Printer Name>,winspool,<Port>:".
1079     // Extract the printer name from it.
1080     pwszComma = wcschr(pwszDevice, L',');
1081     if (!pwszComma)
1082     {
1083         ERR("Found no or invalid default printer: %S!\n", pwszDevice);
1084         dwErrorCode = ERROR_INVALID_NAME;
1085         goto Cleanup;
1086     }
1087 
1088     // Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
1089     *pcchBuffer = pwszComma - pwszDevice + 1;
1090 
1091     // Check if the supplied buffer is large enough.
1092     if (cchInputBuffer < *pcchBuffer)
1093     {
1094         dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
1095         goto Cleanup;
1096     }
1097 
1098     // Copy the default printer.
1099     *pwszComma = 0;
1100     CopyMemory(pszBuffer, pwszDevice, *pcchBuffer * sizeof(WCHAR));
1101 
1102     dwErrorCode = ERROR_SUCCESS;
1103 
1104 Cleanup:
1105     if (hWindowsKey)
1106         RegCloseKey(hWindowsKey);
1107 
1108     if (pwszDevice)
1109         HeapFree(hProcessHeap, 0, pwszDevice);
1110 
1111     SetLastError(dwErrorCode);
1112     return (dwErrorCode == ERROR_SUCCESS);
1113 }
1114 
1115 BOOL WINAPI
1116 GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
1117 {
1118     DWORD dwErrorCode;
1119     PPRINTER_INFO_1A ppi1a = (PPRINTER_INFO_1A)pPrinter;
1120     PPRINTER_INFO_1W ppi1w = (PPRINTER_INFO_1W)pPrinter;
1121     PPRINTER_INFO_2A ppi2a = (PPRINTER_INFO_2A)pPrinter;
1122     PPRINTER_INFO_2W ppi2w = (PPRINTER_INFO_2W)pPrinter;
1123     PPRINTER_INFO_4A ppi4a = (PPRINTER_INFO_4A)pPrinter;
1124     PPRINTER_INFO_4W ppi4w = (PPRINTER_INFO_4W)pPrinter;
1125     PPRINTER_INFO_5A ppi5a = (PPRINTER_INFO_5A)pPrinter;
1126     PPRINTER_INFO_5W ppi5w = (PPRINTER_INFO_5W)pPrinter;
1127     PPRINTER_INFO_7A ppi7a = (PPRINTER_INFO_7A)pPrinter;
1128     PPRINTER_INFO_7W ppi7w = (PPRINTER_INFO_7W)pPrinter;
1129     DWORD cch;
1130 
1131     TRACE("GetPrinterA(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
1132 
1133     // Check for invalid levels here for early error return. Should be 1-9.
1134     if (Level <  1 || Level > 9)
1135     {
1136         dwErrorCode = ERROR_INVALID_LEVEL;
1137         ERR("Invalid Level!\n");
1138         goto Cleanup;
1139     }
1140 
1141     if (!GetPrinterW(hPrinter, Level, pPrinter, cbBuf, pcbNeeded))
1142     {
1143         dwErrorCode = GetLastError();
1144         goto Cleanup;
1145     }
1146 
1147     switch (Level)
1148     {
1149         case 1:
1150         {
1151             if (ppi1w->pDescription)
1152             {
1153                 PSTR pszDescription;
1154 
1155                 // Convert Unicode pDescription to a ANSI string pszDescription.
1156                 cch = wcslen(ppi1w->pDescription);
1157 
1158                 pszDescription = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1159                 if (!pszDescription)
1160                 {
1161                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1162                     ERR("HeapAlloc failed!\n");
1163                     goto Cleanup;
1164                 }
1165 
1166                 WideCharToMultiByte(CP_ACP, 0, ppi1w->pDescription, -1, pszDescription, cch + 1, NULL, NULL);
1167                 StringCchCopyA(ppi1a->pDescription, cch + 1, pszDescription);
1168 
1169                 HeapFree(hProcessHeap, 0, pszDescription);
1170             }
1171 
1172             if (ppi1w->pName)
1173             {
1174                 PSTR pszName;
1175 
1176                 // Convert Unicode pName to a ANSI string pszName.
1177                 cch = wcslen(ppi1w->pName);
1178 
1179                 pszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1180                 if (!pszName)
1181                 {
1182                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1183                     ERR("HeapAlloc failed!\n");
1184                     goto Cleanup;
1185                 }
1186 
1187                 WideCharToMultiByte(CP_ACP, 0, ppi1w->pName, -1, pszName, cch + 1, NULL, NULL);
1188                 StringCchCopyA(ppi1a->pName, cch + 1, pszName);
1189 
1190                 HeapFree(hProcessHeap, 0, pszName);
1191             }
1192 
1193             if (ppi1w->pComment)
1194             {
1195                 PSTR pszComment;
1196 
1197                 // Convert Unicode pComment to a ANSI string pszComment.
1198                 cch = wcslen(ppi1w->pComment);
1199 
1200                 pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1201                 if (!pszComment)
1202                 {
1203                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1204                     ERR("HeapAlloc failed!\n");
1205                     goto Cleanup;
1206                 }
1207 
1208                 WideCharToMultiByte(CP_ACP, 0, ppi1w->pComment, -1, pszComment, cch + 1, NULL, NULL);
1209                 StringCchCopyA(ppi1a->pComment, cch + 1, pszComment);
1210 
1211                 HeapFree(hProcessHeap, 0, pszComment);
1212             }
1213             break;
1214         }
1215 
1216         case 2:
1217         {
1218             if (ppi2w->pServerName)
1219             {
1220                 PSTR pszServerName;
1221 
1222                 // Convert Unicode pServerName to a ANSI string pszServerName.
1223                 cch = wcslen(ppi2w->pServerName);
1224 
1225                 pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1226                 if (!pszServerName)
1227                 {
1228                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1229                     ERR("HeapAlloc failed!\n");
1230                     goto Cleanup;
1231                 }
1232 
1233                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pServerName, -1, pszServerName, cch + 1, NULL, NULL);
1234                 StringCchCopyA(ppi2a->pServerName, cch + 1, pszServerName);
1235 
1236                 HeapFree(hProcessHeap, 0, pszServerName);
1237             }
1238 
1239             if (ppi2w->pPrinterName)
1240             {
1241                 PSTR pszPrinterName;
1242 
1243                 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
1244                 cch = wcslen(ppi2w->pPrinterName);
1245 
1246                 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1247                 if (!pszPrinterName)
1248                 {
1249                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1250                     ERR("HeapAlloc failed!\n");
1251                     goto Cleanup;
1252                 }
1253 
1254                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
1255                 StringCchCopyA(ppi2a->pPrinterName, cch + 1, pszPrinterName);
1256 
1257                 HeapFree(hProcessHeap, 0, pszPrinterName);
1258             }
1259 
1260             if (ppi2w->pShareName)
1261             {
1262                 PSTR pszShareName;
1263 
1264                 // Convert Unicode pShareName to a ANSI string pszShareName.
1265                 cch = wcslen(ppi2w->pShareName);
1266 
1267                 pszShareName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1268                 if (!pszShareName)
1269                 {
1270                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1271                     ERR("HeapAlloc failed!\n");
1272                     goto Cleanup;
1273                 }
1274 
1275                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pShareName, -1, pszShareName, cch + 1, NULL, NULL);
1276                 StringCchCopyA(ppi2a->pShareName, cch + 1, pszShareName);
1277 
1278                 HeapFree(hProcessHeap, 0, pszShareName);
1279             }
1280 
1281             if (ppi2w->pPortName)
1282             {
1283                 PSTR pszPortName;
1284 
1285                 // Convert Unicode pPortName to a ANSI string pszPortName.
1286                 cch = wcslen(ppi2w->pPortName);
1287 
1288                 pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1289                 if (!pszPortName)
1290                 {
1291                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1292                     ERR("HeapAlloc failed!\n");
1293                     goto Cleanup;
1294                 }
1295 
1296                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pPortName, -1, pszPortName, cch + 1, NULL, NULL);
1297                 StringCchCopyA(ppi2a->pPortName, cch + 1, pszPortName);
1298 
1299                 HeapFree(hProcessHeap, 0, pszPortName);
1300             }
1301 
1302             if (ppi2w->pDriverName)
1303             {
1304                 PSTR pszDriverName;
1305 
1306                 // Convert Unicode pDriverName to a ANSI string pszDriverName.
1307                 cch = wcslen(ppi2w->pDriverName);
1308 
1309                 pszDriverName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1310                 if (!pszDriverName)
1311                 {
1312                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1313                     ERR("HeapAlloc failed!\n");
1314                     goto Cleanup;
1315                 }
1316 
1317                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pDriverName, -1, pszDriverName, cch + 1, NULL, NULL);
1318                 StringCchCopyA(ppi2a->pDriverName, cch + 1, pszDriverName);
1319 
1320                 HeapFree(hProcessHeap, 0, pszDriverName);
1321             }
1322 
1323             if (ppi2w->pComment)
1324             {
1325                 PSTR pszComment;
1326 
1327                 // Convert Unicode pComment to a ANSI string pszComment.
1328                 cch = wcslen(ppi2w->pComment);
1329 
1330                 pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1331                 if (!pszComment)
1332                 {
1333                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1334                     ERR("HeapAlloc failed!\n");
1335                     goto Cleanup;
1336                 }
1337 
1338                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pComment, -1, pszComment, cch + 1, NULL, NULL);
1339                 StringCchCopyA(ppi2a->pComment, cch + 1, pszComment);
1340 
1341                 HeapFree(hProcessHeap, 0, pszComment);
1342             }
1343 
1344             if (ppi2w->pLocation)
1345             {
1346                 PSTR pszLocation;
1347 
1348                 // Convert Unicode pLocation to a ANSI string pszLocation.
1349                 cch = wcslen(ppi2w->pLocation);
1350 
1351                 pszLocation = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1352                 if (!pszLocation)
1353                 {
1354                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1355                     ERR("HeapAlloc failed!\n");
1356                     goto Cleanup;
1357                 }
1358 
1359                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pLocation, -1, pszLocation, cch + 1, NULL, NULL);
1360                 StringCchCopyA(ppi2a->pLocation, cch + 1, pszLocation);
1361 
1362                 HeapFree(hProcessHeap, 0, pszLocation);
1363             }
1364 
1365             if (ppi2w->pSepFile)
1366             {
1367                 PSTR pszSepFile;
1368 
1369                 // Convert Unicode pSepFile to a ANSI string pszSepFile.
1370                 cch = wcslen(ppi2w->pSepFile);
1371 
1372                 pszSepFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1373                 if (!pszSepFile)
1374                 {
1375                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1376                     ERR("HeapAlloc failed!\n");
1377                     goto Cleanup;
1378                 }
1379 
1380                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pSepFile, -1, pszSepFile, cch + 1, NULL, NULL);
1381                 StringCchCopyA(ppi2a->pSepFile, cch + 1, pszSepFile);
1382 
1383                 HeapFree(hProcessHeap, 0, pszSepFile);
1384             }
1385 
1386             if (ppi2w->pPrintProcessor)
1387             {
1388                 PSTR pszPrintProcessor;
1389 
1390                 // Convert Unicode pPrintProcessor to a ANSI string pszPrintProcessor.
1391                 cch = wcslen(ppi2w->pPrintProcessor);
1392 
1393                 pszPrintProcessor = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1394                 if (!pszPrintProcessor)
1395                 {
1396                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1397                     ERR("HeapAlloc failed!\n");
1398                     goto Cleanup;
1399                 }
1400 
1401                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pPrintProcessor, -1, pszPrintProcessor, cch + 1, NULL, NULL);
1402                 StringCchCopyA(ppi2a->pPrintProcessor, cch + 1, pszPrintProcessor);
1403 
1404                 HeapFree(hProcessHeap, 0, pszPrintProcessor);
1405             }
1406 
1407             if (ppi2w->pDatatype)
1408             {
1409                 PSTR pszDatatype;
1410 
1411                 // Convert Unicode pDatatype to a ANSI string pszDatatype.
1412                 cch = wcslen(ppi2w->pDatatype);
1413 
1414                 pszDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1415                 if (!pszDatatype)
1416                 {
1417                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1418                     ERR("HeapAlloc failed!\n");
1419                     goto Cleanup;
1420                 }
1421 
1422                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pDatatype, -1, pszDatatype, cch + 1, NULL, NULL);
1423                 StringCchCopyA(ppi2a->pDatatype, cch + 1, pszDatatype);
1424 
1425                 HeapFree(hProcessHeap, 0, pszDatatype);
1426             }
1427 
1428             if (ppi2w->pParameters)
1429             {
1430                 PSTR pszParameters;
1431 
1432                 // Convert Unicode pParameters to a ANSI string pszParameters.
1433                 cch = wcslen(ppi2w->pParameters);
1434 
1435                 pszParameters = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1436                 if (!pszParameters)
1437                 {
1438                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1439                     ERR("HeapAlloc failed!\n");
1440                     goto Cleanup;
1441                 }
1442 
1443                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pParameters, -1, pszParameters, cch + 1, NULL, NULL);
1444                 StringCchCopyA(ppi2a->pParameters, cch + 1, pszParameters);
1445 
1446                 HeapFree(hProcessHeap, 0, pszParameters);
1447             }
1448             break;
1449         }
1450 
1451         case 4:
1452         {
1453             if (ppi4w->pPrinterName)
1454             {
1455                 PSTR pszPrinterName;
1456 
1457                 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
1458                 cch = wcslen(ppi4w->pPrinterName);
1459 
1460                 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1461                 if (!pszPrinterName)
1462                 {
1463                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1464                     ERR("HeapAlloc failed!\n");
1465                     goto Cleanup;
1466                 }
1467 
1468                 WideCharToMultiByte(CP_ACP, 0, ppi4w->pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
1469                 StringCchCopyA(ppi4a->pPrinterName, cch + 1, pszPrinterName);
1470 
1471                 HeapFree(hProcessHeap, 0, pszPrinterName);
1472             }
1473 
1474             if (ppi4w->pServerName)
1475             {
1476                 PSTR pszServerName;
1477 
1478                 // Convert Unicode pServerName to a ANSI string pszServerName.
1479                 cch = wcslen(ppi4w->pServerName);
1480 
1481                 pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1482                 if (!pszServerName)
1483                 {
1484                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1485                     ERR("HeapAlloc failed!\n");
1486                     goto Cleanup;
1487                 }
1488 
1489                 WideCharToMultiByte(CP_ACP, 0, ppi4w->pServerName, -1, pszServerName, cch + 1, NULL, NULL);
1490                 StringCchCopyA(ppi4a->pServerName, cch + 1, pszServerName);
1491 
1492                 HeapFree(hProcessHeap, 0, pszServerName);
1493             }
1494             break;
1495         }
1496 
1497         case 5:
1498         {
1499             if (ppi5w->pPrinterName)
1500             {
1501                 PSTR pszPrinterName;
1502 
1503                 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
1504                 cch = wcslen(ppi5w->pPrinterName);
1505 
1506                 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1507                 if (!pszPrinterName)
1508                 {
1509                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1510                     ERR("HeapAlloc failed!\n");
1511                     goto Cleanup;
1512                 }
1513 
1514                 WideCharToMultiByte(CP_ACP, 0, ppi5w->pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
1515                 StringCchCopyA(ppi5a->pPrinterName, cch + 1, pszPrinterName);
1516 
1517                 HeapFree(hProcessHeap, 0, pszPrinterName);
1518             }
1519 
1520             if (ppi5w->pPortName)
1521             {
1522                 PSTR pszPortName;
1523 
1524                 // Convert Unicode pPortName to a ANSI string pszPortName.
1525                 cch = wcslen(ppi5w->pPortName);
1526 
1527                 pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1528                 if (!pszPortName)
1529                 {
1530                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1531                     ERR("HeapAlloc failed!\n");
1532                     goto Cleanup;
1533                 }
1534 
1535                 WideCharToMultiByte(CP_ACP, 0, ppi5w->pPortName, -1, pszPortName, cch + 1, NULL, NULL);
1536                 StringCchCopyA(ppi5a->pPortName, cch + 1, pszPortName);
1537 
1538                 HeapFree(hProcessHeap, 0, pszPortName);
1539             }
1540             break;
1541         }
1542 
1543         case 7:
1544         {
1545             if (ppi7w->pszObjectGUID)
1546             {
1547                 PSTR pszaObjectGUID;
1548 
1549                 // Convert Unicode pszObjectGUID to a ANSI string pszaObjectGUID.
1550                 cch = wcslen(ppi7w->pszObjectGUID);
1551 
1552                 pszaObjectGUID = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1553                 if (!pszaObjectGUID)
1554                 {
1555                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1556                     ERR("HeapAlloc failed!\n");
1557                     goto Cleanup;
1558                 }
1559 
1560                 WideCharToMultiByte(CP_ACP, 0, ppi7w->pszObjectGUID, -1, pszaObjectGUID, cch + 1, NULL, NULL);
1561                 StringCchCopyA(ppi7a->pszObjectGUID, cch + 1, pszaObjectGUID);
1562 
1563                 HeapFree(hProcessHeap, 0, pszaObjectGUID);
1564             }
1565             break;
1566         }
1567     }       // switch
1568 
1569     dwErrorCode = ERROR_SUCCESS;
1570 
1571 Cleanup:
1572     SetLastError(dwErrorCode);
1573     return (dwErrorCode == ERROR_SUCCESS);
1574 }
1575 
1576 BOOL WINAPI
1577 GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
1578 {
1579     DWORD dwErrorCode;
1580     /*
1581      * We are mapping multiple different pointers to the same pDriverInfo pointer here so that
1582      * we can use the same incoming pointer for different Levels
1583      */
1584     PDRIVER_INFO_1W pdi1w = (PDRIVER_INFO_1W)pDriverInfo;
1585     PDRIVER_INFO_2W pdi2w = (PDRIVER_INFO_2W)pDriverInfo;
1586     PDRIVER_INFO_3W pdi3w = (PDRIVER_INFO_3W)pDriverInfo;
1587     PDRIVER_INFO_4W pdi4w = (PDRIVER_INFO_4W)pDriverInfo;
1588     PDRIVER_INFO_5W pdi5w = (PDRIVER_INFO_5W)pDriverInfo;
1589     PDRIVER_INFO_6W pdi6w = (PDRIVER_INFO_6W)pDriverInfo;
1590 
1591     DWORD cch;
1592     PWSTR pwszEnvironment = NULL;
1593 
1594     TRACE("GetPrinterDriverA(%p, %s, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
1595 
1596     // Check for invalid levels here for early error return. Should be 1-6.
1597     if (Level <  1 || Level > 6)
1598     {
1599         dwErrorCode = ERROR_INVALID_LEVEL;
1600         ERR("Invalid Level!\n");
1601         goto Cleanup;
1602     }
1603 
1604     if (pEnvironment)
1605     {
1606         // Convert pEnvironment to a Unicode string pwszEnvironment.
1607         cch = strlen(pEnvironment);
1608 
1609         pwszEnvironment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1610         if (!pwszEnvironment)
1611         {
1612             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1613             ERR("HeapAlloc failed!\n");
1614             goto Cleanup;
1615         }
1616 
1617         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, pwszEnvironment, cch + 1);
1618     }
1619 
1620     if (!GetPrinterDriverW(hPrinter, pwszEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded))
1621     {
1622         dwErrorCode = GetLastError();
1623         goto Cleanup;
1624     }
1625 
1626     // Do Unicode to ANSI conversions for strings based on Level
1627     switch (Level)
1628     {
1629         case 1:
1630         {
1631             dwErrorCode = UnicodeToAnsiInPlace(pdi1w->pName);
1632             if (dwErrorCode != ERROR_SUCCESS)
1633             {
1634                 goto Cleanup;
1635             }
1636 
1637             break;
1638         }
1639 
1640         case 2:
1641         {
1642             dwErrorCode = UnicodeToAnsiInPlace(pdi2w->pName);
1643             if (dwErrorCode != ERROR_SUCCESS)
1644             {
1645                 goto Cleanup;
1646             }
1647 
1648             dwErrorCode = UnicodeToAnsiInPlace(pdi2w->pEnvironment);
1649             if (dwErrorCode != ERROR_SUCCESS)
1650             {
1651                 goto Cleanup;
1652             }
1653 
1654             dwErrorCode = UnicodeToAnsiInPlace(pdi2w->pDriverPath);
1655             if (dwErrorCode != ERROR_SUCCESS)
1656             {
1657                 goto Cleanup;
1658             }
1659 
1660             dwErrorCode = UnicodeToAnsiInPlace(pdi2w->pDataFile);
1661             if (dwErrorCode != ERROR_SUCCESS)
1662             {
1663                 goto Cleanup;
1664             }
1665 
1666             dwErrorCode = UnicodeToAnsiInPlace(pdi2w->pConfigFile);
1667             if (dwErrorCode != ERROR_SUCCESS)
1668             {
1669                 goto Cleanup;
1670             }
1671 
1672             break;
1673         }
1674 
1675         case 3:
1676         {
1677             dwErrorCode = UnicodeToAnsiInPlace(pdi3w->pName);
1678             if (dwErrorCode != ERROR_SUCCESS)
1679             {
1680                 goto Cleanup;
1681             }
1682 
1683             dwErrorCode = UnicodeToAnsiInPlace(pdi3w->pEnvironment);
1684             if (dwErrorCode != ERROR_SUCCESS)
1685             {
1686                 goto Cleanup;
1687             }
1688 
1689             dwErrorCode = UnicodeToAnsiInPlace(pdi3w->pDriverPath);
1690             if (dwErrorCode != ERROR_SUCCESS)
1691             {
1692                 goto Cleanup;
1693             }
1694 
1695             dwErrorCode = UnicodeToAnsiInPlace(pdi3w->pDataFile);
1696             if (dwErrorCode != ERROR_SUCCESS)
1697             {
1698                 goto Cleanup;
1699             }
1700 
1701             dwErrorCode = UnicodeToAnsiInPlace(pdi3w->pConfigFile);
1702             if (dwErrorCode != ERROR_SUCCESS)
1703             {
1704                 goto Cleanup;
1705             }
1706 
1707             dwErrorCode = UnicodeToAnsiInPlace(pdi3w->pHelpFile);
1708             if (dwErrorCode != ERROR_SUCCESS)
1709             {
1710                 goto Cleanup;
1711             }
1712 
1713             dwErrorCode = UnicodeToAnsiInPlace(pdi3w->pDependentFiles);
1714             if (dwErrorCode != ERROR_SUCCESS)
1715             {
1716                 goto Cleanup;
1717             }
1718 
1719             dwErrorCode = UnicodeToAnsiInPlace(pdi3w->pMonitorName);
1720             if (dwErrorCode != ERROR_SUCCESS)
1721             {
1722                 goto Cleanup;
1723             }
1724 
1725             dwErrorCode = UnicodeToAnsiInPlace(pdi3w->pDefaultDataType);
1726             if (dwErrorCode != ERROR_SUCCESS)
1727             {
1728                 goto Cleanup;
1729             }
1730 
1731             break;
1732         }
1733 
1734         case 4:
1735         {
1736             dwErrorCode = UnicodeToAnsiInPlace(pdi4w->pName);
1737             if (dwErrorCode != ERROR_SUCCESS)
1738             {
1739                 goto Cleanup;
1740             }
1741 
1742             dwErrorCode = UnicodeToAnsiInPlace(pdi4w->pEnvironment);
1743             if (dwErrorCode != ERROR_SUCCESS)
1744             {
1745                 goto Cleanup;
1746             }
1747 
1748             dwErrorCode = UnicodeToAnsiInPlace(pdi4w->pDriverPath);
1749             if (dwErrorCode != ERROR_SUCCESS)
1750             {
1751                 goto Cleanup;
1752             }
1753 
1754             dwErrorCode = UnicodeToAnsiInPlace(pdi4w->pDataFile);
1755             if (dwErrorCode != ERROR_SUCCESS)
1756             {
1757                 goto Cleanup;
1758             }
1759 
1760             dwErrorCode = UnicodeToAnsiInPlace(pdi4w->pConfigFile);
1761             if (dwErrorCode != ERROR_SUCCESS)
1762             {
1763                 goto Cleanup;
1764             }
1765 
1766             dwErrorCode = UnicodeToAnsiInPlace(pdi4w->pHelpFile);
1767             if (dwErrorCode != ERROR_SUCCESS)
1768             {
1769                 goto Cleanup;
1770             }
1771 
1772             dwErrorCode = UnicodeToAnsiInPlace(pdi4w->pDependentFiles);
1773             if (dwErrorCode != ERROR_SUCCESS)
1774             {
1775                 goto Cleanup;
1776             }
1777 
1778             dwErrorCode = UnicodeToAnsiInPlace(pdi4w->pMonitorName);
1779             if (dwErrorCode != ERROR_SUCCESS)
1780             {
1781                 goto Cleanup;
1782             }
1783 
1784             dwErrorCode = UnicodeToAnsiInPlace(pdi4w->pDefaultDataType);
1785             if (dwErrorCode != ERROR_SUCCESS)
1786             {
1787                 goto Cleanup;
1788             }
1789 
1790             dwErrorCode = UnicodeToAnsiInPlace(pdi4w->pszzPreviousNames);
1791             if (dwErrorCode != ERROR_SUCCESS)
1792             {
1793                 goto Cleanup;
1794             }
1795 
1796             break;
1797         }
1798 
1799         case 5:
1800         {
1801             dwErrorCode = UnicodeToAnsiInPlace(pdi5w->pName);
1802             if (dwErrorCode != ERROR_SUCCESS)
1803             {
1804                 goto Cleanup;
1805             }
1806 
1807             dwErrorCode = UnicodeToAnsiInPlace(pdi5w->pEnvironment);
1808             if (dwErrorCode != ERROR_SUCCESS)
1809             {
1810                 goto Cleanup;
1811             }
1812 
1813             dwErrorCode = UnicodeToAnsiInPlace(pdi5w->pDriverPath);
1814             if (dwErrorCode != ERROR_SUCCESS)
1815             {
1816                 goto Cleanup;
1817             }
1818 
1819             dwErrorCode = UnicodeToAnsiInPlace(pdi5w->pDataFile);
1820             if (dwErrorCode != ERROR_SUCCESS)
1821             {
1822                 goto Cleanup;
1823             }
1824 
1825             dwErrorCode = UnicodeToAnsiInPlace(pdi5w->pConfigFile);
1826             if (dwErrorCode != ERROR_SUCCESS)
1827             {
1828                 goto Cleanup;
1829             }
1830 
1831             break;
1832         }
1833 
1834         case 6:
1835         {
1836             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pName);
1837             if (dwErrorCode != ERROR_SUCCESS)
1838             {
1839                 goto Cleanup;
1840             }
1841 
1842             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pEnvironment);
1843             if (dwErrorCode != ERROR_SUCCESS)
1844             {
1845                 goto Cleanup;
1846             }
1847 
1848             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pDriverPath);
1849             if (dwErrorCode != ERROR_SUCCESS)
1850             {
1851                 goto Cleanup;
1852             }
1853 
1854             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pDataFile);
1855             if (dwErrorCode != ERROR_SUCCESS)
1856             {
1857                 goto Cleanup;
1858             }
1859 
1860             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pConfigFile);
1861             if (dwErrorCode != ERROR_SUCCESS)
1862             {
1863                 goto Cleanup;
1864             }
1865 
1866             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pHelpFile);
1867             if (dwErrorCode != ERROR_SUCCESS)
1868             {
1869                 goto Cleanup;
1870             }
1871 
1872             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pDependentFiles);
1873             if (dwErrorCode != ERROR_SUCCESS)
1874             {
1875                 goto Cleanup;
1876             }
1877 
1878             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pMonitorName);
1879             if (dwErrorCode != ERROR_SUCCESS)
1880             {
1881                 goto Cleanup;
1882             }
1883 
1884             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pDefaultDataType);
1885             if (dwErrorCode != ERROR_SUCCESS)
1886             {
1887                 goto Cleanup;
1888             }
1889 
1890             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pszzPreviousNames);
1891             if (dwErrorCode != ERROR_SUCCESS)
1892             {
1893                 goto Cleanup;
1894             }
1895 
1896             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pszMfgName);
1897             if (dwErrorCode != ERROR_SUCCESS)
1898             {
1899                 goto Cleanup;
1900             }
1901 
1902             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pszOEMUrl);
1903             if (dwErrorCode != ERROR_SUCCESS)
1904             {
1905                 goto Cleanup;
1906             }
1907 
1908             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pszHardwareID);
1909             if (dwErrorCode != ERROR_SUCCESS)
1910             {
1911                 goto Cleanup;
1912             }
1913 
1914             dwErrorCode = UnicodeToAnsiInPlace(pdi6w->pszProvider);
1915             if (dwErrorCode != ERROR_SUCCESS)
1916             {
1917                 goto Cleanup;
1918             }
1919         }
1920     }
1921 
1922     dwErrorCode = ERROR_SUCCESS;
1923 
1924 Cleanup:
1925     if (pwszEnvironment)
1926     {
1927         HeapFree(hProcessHeap, 0, pwszEnvironment);
1928     }
1929 
1930     SetLastError(dwErrorCode);
1931     return (dwErrorCode == ERROR_SUCCESS);
1932 }
1933 
1934 BOOL WINAPI
1935 GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
1936 {
1937     DWORD dwErrorCode;
1938     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1939 
1940     TRACE("GetPrinterDriverW(%p, %S, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
1941 
1942     // Sanity checks.
1943     if (!pHandle)
1944     {
1945         dwErrorCode = ERROR_INVALID_HANDLE;
1946         goto Cleanup;
1947     }
1948 
1949     // Dismiss invalid levels already at this point.
1950     if (Level > 8 || Level < 1)
1951     {
1952         dwErrorCode = ERROR_INVALID_LEVEL;
1953         goto Cleanup;
1954     }
1955 
1956     if (cbBuf && pDriverInfo)
1957         ZeroMemory(pDriverInfo, cbBuf);
1958 
1959     // Do the RPC call
1960     RpcTryExcept
1961     {
1962         dwErrorCode = _RpcGetPrinterDriver(pHandle->hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
1963     }
1964     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1965     {
1966         dwErrorCode = RpcExceptionCode();
1967         ERR("_RpcGetPrinterDriver failed with exception code %lu!\n", dwErrorCode);
1968     }
1969     RpcEndExcept;
1970 
1971     if (dwErrorCode == ERROR_SUCCESS)
1972     {
1973         // Replace relative offset addresses in the output by absolute pointers.
1974         ASSERT(Level <= 5);
1975         MarshallUpStructure(cbBuf, pDriverInfo, pPrinterDriverMarshalling[Level]->pInfo, pPrinterDriverMarshalling[Level]->cbStructureSize, TRUE);
1976     }
1977 
1978 Cleanup:
1979     SetLastError(dwErrorCode);
1980     return (dwErrorCode == ERROR_SUCCESS);
1981 }
1982 
1983 BOOL WINAPI
1984 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
1985 {
1986     DWORD dwErrorCode;
1987     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1988 
1989     TRACE("GetPrinterW(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
1990 
1991     // Sanity checks.
1992     if (!pHandle)
1993     {
1994         dwErrorCode = ERROR_INVALID_HANDLE;
1995         goto Cleanup;
1996     }
1997 
1998     // Dismiss invalid levels already at this point.
1999     if (Level > 9)
2000     {
2001         dwErrorCode = ERROR_INVALID_LEVEL;
2002         goto Cleanup;
2003     }
2004 
2005     if (cbBuf && pPrinter)
2006         ZeroMemory(pPrinter, cbBuf);
2007 
2008     // Do the RPC call
2009     RpcTryExcept
2010     {
2011         dwErrorCode = _RpcGetPrinter(pHandle->hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
2012     }
2013     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2014     {
2015         dwErrorCode = RpcExceptionCode();
2016         ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
2017     }
2018     RpcEndExcept;
2019 
2020     if (dwErrorCode == ERROR_SUCCESS)
2021     {
2022         // Replace relative offset addresses in the output by absolute pointers.
2023         ASSERT(Level <= 9);
2024         MarshallUpStructure(cbBuf, pPrinter, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
2025     }
2026 
2027 Cleanup:
2028     SetLastError(dwErrorCode);
2029     return (dwErrorCode == ERROR_SUCCESS);
2030 }
2031 
2032 BOOL WINAPI
2033 OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
2034 {
2035     BOOL bReturnValue = FALSE;
2036     DWORD cch;
2037     PWSTR pwszPrinterName = NULL;
2038     PRINTER_DEFAULTSW wDefault = { 0 };
2039 
2040     TRACE("OpenPrinterA(%s, %p, %p)\n", pPrinterName, phPrinter, pDefault);
2041 
2042     if (pPrinterName)
2043     {
2044         // Convert pPrinterName to a Unicode string pwszPrinterName
2045         cch = strlen(pPrinterName);
2046 
2047         pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2048         if (!pwszPrinterName)
2049         {
2050             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2051             ERR("HeapAlloc failed!\n");
2052             goto Cleanup;
2053         }
2054 
2055         MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
2056     }
2057 
2058     if (pDefault)
2059     {
2060         wDefault.DesiredAccess = pDefault->DesiredAccess;
2061 
2062         if (pDefault->pDatatype)
2063         {
2064             // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
2065             cch = strlen(pDefault->pDatatype);
2066 
2067             wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2068             if (!wDefault.pDatatype)
2069             {
2070                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2071                 ERR("HeapAlloc failed!\n");
2072                 goto Cleanup;
2073             }
2074 
2075             MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
2076         }
2077 
2078         if (pDefault->pDevMode)
2079             wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
2080     }
2081 
2082     bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
2083 
2084 Cleanup:
2085     if (wDefault.pDatatype)
2086         HeapFree(hProcessHeap, 0, wDefault.pDatatype);
2087 
2088     if (wDefault.pDevMode)
2089         HeapFree(hProcessHeap, 0, wDefault.pDevMode);
2090 
2091     if (pwszPrinterName)
2092         HeapFree(hProcessHeap, 0, pwszPrinterName);
2093 
2094     return bReturnValue;
2095 }
2096 
2097 BOOL WINAPI
2098 OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
2099 {
2100     DWORD dwErrorCode;
2101     HANDLE hPrinter;
2102     PSPOOLER_HANDLE pHandle;
2103     PWSTR pDatatype = NULL;
2104     WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
2105     ACCESS_MASK AccessRequired = 0;
2106 
2107     TRACE("OpenPrinterW(%S, %p, %p)\n", pPrinterName, phPrinter, pDefault);
2108 
2109     // Sanity check
2110     if (!phPrinter)
2111     {
2112         dwErrorCode = ERROR_INVALID_PARAMETER;
2113         goto Cleanup;
2114     }
2115 
2116     // Prepare the additional parameters in the format required by _RpcOpenPrinter
2117     if (pDefault)
2118     {
2119         pDatatype = pDefault->pDatatype;
2120         DevModeContainer.cbBuf = sizeof(DEVMODEW);
2121         DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
2122         AccessRequired = pDefault->DesiredAccess;
2123     }
2124 
2125     // Do the RPC call
2126     RpcTryExcept
2127     {
2128         dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
2129     }
2130     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2131     {
2132         dwErrorCode = RpcExceptionCode();
2133         ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
2134     }
2135     RpcEndExcept;
2136 
2137     if (dwErrorCode == ERROR_SUCCESS)
2138     {
2139         // Create a new SPOOLER_HANDLE structure.
2140         pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
2141         if (!pHandle)
2142         {
2143             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2144             ERR("HeapAlloc failed!\n");
2145             goto Cleanup;
2146         }
2147 
2148         pHandle->hPrinter = hPrinter;
2149         pHandle->hSPLFile = INVALID_HANDLE_VALUE;
2150 
2151         // Return it as phPrinter.
2152         *phPrinter = (HANDLE)pHandle;
2153     }
2154 
2155 Cleanup:
2156     SetLastError(dwErrorCode);
2157     return (dwErrorCode == ERROR_SUCCESS);
2158 }
2159 
2160 BOOL WINAPI
2161 ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
2162 {
2163     DWORD dwErrorCode;
2164     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2165 
2166     TRACE("ReadPrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pNoBytesRead);
2167 
2168     // Sanity checks.
2169     if (!pHandle)
2170     {
2171         dwErrorCode = ERROR_INVALID_HANDLE;
2172         goto Cleanup;
2173     }
2174 
2175     // Do the RPC call
2176     RpcTryExcept
2177     {
2178         dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
2179     }
2180     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2181     {
2182         dwErrorCode = RpcExceptionCode();
2183         ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
2184     }
2185     RpcEndExcept;
2186 
2187 Cleanup:
2188     SetLastError(dwErrorCode);
2189     return (dwErrorCode == ERROR_SUCCESS);
2190 }
2191 
2192 BOOL WINAPI
2193 ResetPrinterA(HANDLE hPrinter, PPRINTER_DEFAULTSA pDefault)
2194 {
2195     TRACE("ResetPrinterA(%p, %p)\n", hPrinter, pDefault);
2196     UNIMPLEMENTED;
2197     return FALSE;
2198 }
2199 
2200 BOOL WINAPI
2201 ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
2202 {
2203     TRACE("ResetPrinterW(%p, %p)\n", hPrinter, pDefault);
2204     UNIMPLEMENTED;
2205     return FALSE;
2206 }
2207 
2208 BOOL WINAPI
2209 SetDefaultPrinterA(LPCSTR pszPrinter)
2210 {
2211     BOOL bReturnValue = FALSE;
2212     DWORD cch;
2213     PWSTR pwszPrinter = NULL;
2214 
2215     TRACE("SetDefaultPrinterA(%s)\n", pszPrinter);
2216 
2217     if (pszPrinter)
2218     {
2219         // Convert pszPrinter to a Unicode string pwszPrinter
2220         cch = strlen(pszPrinter);
2221 
2222         pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2223         if (!pwszPrinter)
2224         {
2225             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2226             ERR("HeapAlloc failed!\n");
2227             goto Cleanup;
2228         }
2229 
2230         MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, pwszPrinter, cch + 1);
2231     }
2232 
2233     bReturnValue = SetDefaultPrinterW(pwszPrinter);
2234 
2235 Cleanup:
2236     if (pwszPrinter)
2237         HeapFree(hProcessHeap, 0, pwszPrinter);
2238 
2239     return bReturnValue;
2240 }
2241 
2242 BOOL WINAPI
2243 SetDefaultPrinterW(LPCWSTR pszPrinter)
2244 {
2245     const WCHAR wszDevicesKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
2246 
2247     DWORD cbDeviceValueData;
2248     DWORD cbPrinterValueData = 0;
2249     DWORD cchPrinter;
2250     DWORD dwErrorCode;
2251     HKEY hDevicesKey = NULL;
2252     HKEY hWindowsKey = NULL;
2253     PWSTR pwszDeviceValueData = NULL;
2254     WCHAR wszPrinter[MAX_PRINTER_NAME + 1];
2255 
2256     TRACE("SetDefaultPrinterW(%S)\n", pszPrinter);
2257 
2258     // Open the Devices registry key.
2259     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszDevicesKey, 0, KEY_READ, &hDevicesKey);
2260     if (dwErrorCode != ERROR_SUCCESS)
2261     {
2262         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
2263         goto Cleanup;
2264     }
2265 
2266     // Did the caller give us a printer to set as default?
2267     if (pszPrinter && *pszPrinter)
2268     {
2269         // Check if the given printer exists and query the value data size.
2270         dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, NULL, &cbPrinterValueData);
2271         if (dwErrorCode == ERROR_FILE_NOT_FOUND)
2272         {
2273             dwErrorCode = ERROR_INVALID_PRINTER_NAME;
2274             goto Cleanup;
2275         }
2276         else if (dwErrorCode != ERROR_SUCCESS)
2277         {
2278             ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
2279             goto Cleanup;
2280         }
2281 
2282         cchPrinter = wcslen(pszPrinter);
2283     }
2284     else
2285     {
2286         // If there is already a default printer, we're done!
2287         cchPrinter = _countof(wszPrinter);
2288         if (GetDefaultPrinterW(wszPrinter, &cchPrinter))
2289         {
2290             dwErrorCode = ERROR_SUCCESS;
2291             goto Cleanup;
2292         }
2293 
2294         // Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
2295         cchPrinter = _countof(wszPrinter);
2296         dwErrorCode = (DWORD)RegEnumValueW(hDevicesKey, 0, wszPrinter, &cchPrinter, NULL, NULL, NULL, &cbPrinterValueData);
2297         if (dwErrorCode != ERROR_MORE_DATA)
2298             goto Cleanup;
2299 
2300         pszPrinter = wszPrinter;
2301     }
2302 
2303     // We now need to query the value data, which has the format "winspool,<Port>:"
2304     // and make "<Printer Name>,winspool,<Port>:" out of it.
2305     // Allocate a buffer large enough for the final data.
2306     cbDeviceValueData = (cchPrinter + 1) * sizeof(WCHAR) + cbPrinterValueData;
2307     pwszDeviceValueData = HeapAlloc(hProcessHeap, 0, cbDeviceValueData);
2308     if (!pwszDeviceValueData)
2309     {
2310         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2311         ERR("HeapAlloc failed!\n");
2312         goto Cleanup;
2313     }
2314 
2315     // Copy the Printer Name and a comma into it.
2316     CopyMemory(pwszDeviceValueData, pszPrinter, cchPrinter * sizeof(WCHAR));
2317     pwszDeviceValueData[cchPrinter] = L',';
2318 
2319     // Append the value data, which has the format "winspool,<Port>:"
2320     dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, (PBYTE)&pwszDeviceValueData[cchPrinter + 1], &cbPrinterValueData);
2321     if (dwErrorCode != ERROR_SUCCESS)
2322         goto Cleanup;
2323 
2324     // Open the Windows registry key.
2325     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_SET_VALUE, &hWindowsKey);
2326     if (dwErrorCode != ERROR_SUCCESS)
2327     {
2328         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
2329         goto Cleanup;
2330     }
2331 
2332     // Store our new default printer.
2333     dwErrorCode = (DWORD)RegSetValueExW(hWindowsKey, wszDeviceValue, 0, REG_SZ, (PBYTE)pwszDeviceValueData, cbDeviceValueData);
2334     if (dwErrorCode != ERROR_SUCCESS)
2335     {
2336         ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
2337         goto Cleanup;
2338     }
2339 
2340 Cleanup:
2341     if (hDevicesKey)
2342         RegCloseKey(hDevicesKey);
2343 
2344     if (hWindowsKey)
2345         RegCloseKey(hWindowsKey);
2346 
2347     if (pwszDeviceValueData)
2348         HeapFree(hProcessHeap, 0, pwszDeviceValueData);
2349 
2350     SetLastError(dwErrorCode);
2351     return (dwErrorCode == ERROR_SUCCESS);
2352 }
2353 
2354 BOOL WINAPI
2355 SetPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
2356 {
2357     TRACE("SetPrinterA(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
2358     UNIMPLEMENTED;
2359     return FALSE;
2360 }
2361 
2362 BOOL WINAPI
2363 SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
2364 {
2365     TRACE("SetPrinterW(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
2366     UNIMPLEMENTED;
2367     return FALSE;
2368 }
2369 
2370 BOOL WINAPI
2371 SplDriverUnloadComplete(LPWSTR pDriverFile)
2372 {
2373     TRACE("DriverUnloadComplete(%S)\n", pDriverFile);
2374     UNIMPLEMENTED;
2375     return TRUE; // return true for now.
2376 }
2377 
2378 DWORD WINAPI
2379 StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
2380 {
2381     DOC_INFO_1W wDocInfo1 = { 0 };
2382     DWORD cch;
2383     DWORD dwErrorCode;
2384     DWORD dwReturnValue = 0;
2385     PDOC_INFO_1A pDocInfo1 = (PDOC_INFO_1A)pDocInfo;
2386 
2387     TRACE("StartDocPrinterA(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
2388 
2389     // Only check the minimum required for accessing pDocInfo.
2390     // Additional sanity checks are done in StartDocPrinterW.
2391     if (!pDocInfo1)
2392     {
2393         dwErrorCode = ERROR_INVALID_PARAMETER;
2394         goto Cleanup;
2395     }
2396 
2397     if (Level != 1)
2398     {
2399         dwErrorCode = ERROR_INVALID_LEVEL;
2400         goto Cleanup;
2401     }
2402 
2403     if (pDocInfo1->pDatatype)
2404     {
2405         // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
2406         cch = strlen(pDocInfo1->pDatatype);
2407 
2408         wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2409         if (!wDocInfo1.pDatatype)
2410         {
2411             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2412             ERR("HeapAlloc failed!\n");
2413             goto Cleanup;
2414         }
2415 
2416         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDatatype, -1, wDocInfo1.pDatatype, cch + 1);
2417     }
2418 
2419     if (pDocInfo1->pDocName)
2420     {
2421         // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
2422         cch = strlen(pDocInfo1->pDocName);
2423 
2424         wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2425         if (!wDocInfo1.pDocName)
2426         {
2427             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2428             ERR("HeapAlloc failed!\n");
2429             goto Cleanup;
2430         }
2431 
2432         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDocName, -1, wDocInfo1.pDocName, cch + 1);
2433     }
2434 
2435     if (pDocInfo1->pOutputFile)
2436     {
2437         // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
2438         cch = strlen(pDocInfo1->pOutputFile);
2439 
2440         wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2441         if (!wDocInfo1.pOutputFile)
2442         {
2443             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2444             ERR("HeapAlloc failed!\n");
2445             goto Cleanup;
2446         }
2447 
2448         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pOutputFile, -1, wDocInfo1.pOutputFile, cch + 1);
2449     }
2450 
2451     dwReturnValue = StartDocPrinterW(hPrinter, Level, (PBYTE)&wDocInfo1);
2452     dwErrorCode = GetLastError();
2453 
2454 Cleanup:
2455     if (wDocInfo1.pDatatype)
2456         HeapFree(hProcessHeap, 0, wDocInfo1.pDatatype);
2457 
2458     if (wDocInfo1.pDocName)
2459         HeapFree(hProcessHeap, 0, wDocInfo1.pDocName);
2460 
2461     if (wDocInfo1.pOutputFile)
2462         HeapFree(hProcessHeap, 0, wDocInfo1.pOutputFile);
2463 
2464     SetLastError(dwErrorCode);
2465     return dwReturnValue;
2466 }
2467 
2468 DWORD WINAPI
2469 StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
2470 {
2471     DWORD cbAddJobInfo1;
2472     DWORD cbNeeded;
2473     DWORD dwErrorCode;
2474     DWORD dwReturnValue = 0;
2475     PADDJOB_INFO_1W pAddJobInfo1 = NULL;
2476     PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
2477     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2478 
2479     TRACE("StartDocPrinterW(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
2480 
2481     // Sanity checks.
2482     if (!pHandle)
2483     {
2484         dwErrorCode = ERROR_INVALID_HANDLE;
2485         goto Cleanup;
2486     }
2487 
2488     if (!pDocInfo1)
2489     {
2490         dwErrorCode = ERROR_INVALID_PARAMETER;
2491         goto Cleanup;
2492     }
2493 
2494     if (Level != 1)
2495     {
2496         dwErrorCode = ERROR_INVALID_LEVEL;
2497         goto Cleanup;
2498     }
2499 
2500     if (pHandle->bStartedDoc)
2501     {
2502         dwErrorCode = ERROR_INVALID_PRINTER_STATE;
2503         goto Cleanup;
2504     }
2505 
2506     // Check if we want to redirect output into a file.
2507     if (pDocInfo1->pOutputFile)
2508     {
2509         // Do a StartDocPrinter RPC call in this case.
2510         dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
2511     }
2512     else
2513     {
2514         // Allocate memory for the ADDJOB_INFO_1W structure and a path.
2515         cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
2516         pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
2517         if (!pAddJobInfo1)
2518         {
2519             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2520             ERR("HeapAlloc failed!\n");
2521             goto Cleanup;
2522         }
2523 
2524         // Try to add a new job.
2525         // This only succeeds if the printer is set to do spooled printing.
2526         if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
2527         {
2528             // Do spooled printing.
2529             dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
2530         }
2531         else if (GetLastError() == ERROR_INVALID_ACCESS)
2532         {
2533             // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
2534             // In this case, we do a StartDocPrinter RPC call.
2535             dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
2536         }
2537         else
2538         {
2539             dwErrorCode = GetLastError();
2540             ERR("AddJobW failed with error %lu!\n", dwErrorCode);
2541             goto Cleanup;
2542         }
2543     }
2544 
2545     if (dwErrorCode == ERROR_SUCCESS)
2546     {
2547         pHandle->bStartedDoc = TRUE;
2548         dwReturnValue = pHandle->dwJobID;
2549     }
2550 
2551 Cleanup:
2552     if (pAddJobInfo1)
2553         HeapFree(hProcessHeap, 0, pAddJobInfo1);
2554 
2555     SetLastError(dwErrorCode);
2556     return dwReturnValue;
2557 }
2558 
2559 BOOL WINAPI
2560 StartPagePrinter(HANDLE hPrinter)
2561 {
2562     DWORD dwErrorCode;
2563     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2564 
2565     TRACE("StartPagePrinter(%p)\n", hPrinter);
2566 
2567     // Sanity checks.
2568     if (!pHandle)
2569     {
2570         dwErrorCode = ERROR_INVALID_HANDLE;
2571         goto Cleanup;
2572     }
2573 
2574     // Do the RPC call
2575     RpcTryExcept
2576     {
2577         dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
2578     }
2579     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2580     {
2581         dwErrorCode = RpcExceptionCode();
2582         ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
2583     }
2584     RpcEndExcept;
2585 
2586 Cleanup:
2587     SetLastError(dwErrorCode);
2588     return (dwErrorCode == ERROR_SUCCESS);
2589 }
2590 
2591 BOOL WINAPI
2592 WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
2593 {
2594     DWORD dwErrorCode;
2595     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2596 
2597     TRACE("WritePrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
2598 
2599     // Sanity checks.
2600     if (!pHandle)
2601     {
2602         dwErrorCode = ERROR_INVALID_HANDLE;
2603         goto Cleanup;
2604     }
2605 
2606     if (!pHandle->bStartedDoc)
2607     {
2608         dwErrorCode = ERROR_SPL_NO_STARTDOC;
2609         goto Cleanup;
2610     }
2611 
2612     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
2613     {
2614         // Write to the spool file. This doesn't need an RPC request.
2615         if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
2616         {
2617             dwErrorCode = GetLastError();
2618             ERR("WriteFile failed with error %lu!\n", dwErrorCode);
2619             goto Cleanup;
2620         }
2621 
2622         dwErrorCode = ERROR_SUCCESS;
2623     }
2624     else
2625     {
2626         // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
2627         // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
2628 
2629         // Do the RPC call
2630         RpcTryExcept
2631         {
2632             dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
2633         }
2634         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2635         {
2636             dwErrorCode = RpcExceptionCode();
2637             ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
2638         }
2639         RpcEndExcept;
2640     }
2641 
2642 Cleanup:
2643     SetLastError(dwErrorCode);
2644     return (dwErrorCode == ERROR_SUCCESS);
2645 }
2646 
2647 BOOL WINAPI
2648 XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
2649 {
2650     TRACE("XcvDataW(%p, %S, %p, %lu, %p, %lu, %p, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus);
2651     return FALSE;
2652 }
2653