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 extern HINSTANCE hinstWinSpool;
14 //
15 // See winddiui.h, ReactOS version is limited.
16 // Loading from XyzUI.dll part of XyzDRV.dll set. example TTYUI.DLL or UniDrvUI.DLL.
17 //
18 typedef DWORD (WINAPI *DEVICECAPABILITIES) (HANDLE,PWSTR,WORD,PVOID,PDEVMODEW);
19 static DEVICECAPABILITIES fpDeviceCapabilities;
20 
21 typedef LONG (WINAPI *DEVICEPROPERTYSHEETS) (PPROPSHEETUI_INFO,LPARAM);
22 static DEVICEPROPERTYSHEETS fpDevicePropertySheets;
23 typedef LONG (WINAPI *DOCUMENTPROPERTYSHEETS) (PPROPSHEETUI_INFO,LPARAM);
24 static DOCUMENTPROPERTYSHEETS fpDocumentPropertySheets;
25 
26 typedef LONG (WINAPI *COMMONPROPERTYSHEETUIW) (HWND,PFNPROPSHEETUI,LPARAM,LPDWORD);
27 static COMMONPROPERTYSHEETUIW fpCommonPropertySheetUIW;
28 
29 typedef LONG (WINAPI *QUERYCOLORPROFILE) (HANDLE,PDEVMODEW,ULONG,PVOID,ULONG*,FLONG*);
30 static  QUERYCOLORPROFILE fpQueryColorProfile;
31 
32 typedef BOOL (WINAPI *SPOOLERPRINTEREVENT) (LPWSTR,int,DWORD,LPARAM);
33 static SPOOLERPRINTEREVENT fpPrinterEvent;
34 
35 typedef BOOL (WINAPI *DEVQUERYPRINT) (HANDLE,LPDEVMODEW,DWORD*);
36 static DEVQUERYPRINT fpDevQueryPrint;
37 
38 typedef BOOL (WINAPI *DEVQUERYPRINTEX) (PDEVQUERYPRINT_INFO);
39 static DEVQUERYPRINTEX fpDevQueryPrintEx;
40 
41 //
42 //  PrintUI.dll
43 //
44 LONG WINAPI ConstructPrinterFriendlyName( PWSTR, PVOID, LPDWORD Size );
45 typedef LONG (WINAPI *CONSTRUCTPRINTERFRIENDLYNAME) (PWSTR,PVOID,LPDWORD);
46 static CONSTRUCTPRINTERFRIENDLYNAME fpConstructPrinterFriendlyName;
47 
48 //
49 //  CompstUI User Data
50 //
51 typedef struct _COMPUI_USERDATA
52 {
53   HMODULE hModule;
54   LPWSTR pszPrinterName;
55 } COMPUI_USERDATA, *PCOMPUI_USERDATA;
56 
57 // Local Constants
58 
59 /** And the award for the most confusingly named setting goes to "Device", for storing the default printer of the current user.
60     Ok, I admit that this has historical reasons. It's still not straightforward in any way though! */
61 static const WCHAR wszWindowsKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
62 static const WCHAR wszDeviceValue[] = L"Device";
63 
64 static DWORD
65 _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB_INFO_1W pAddJobInfo1)
66 {
67     DWORD cbNeeded;
68     DWORD dwErrorCode;
69     PJOB_INFO_1W pJobInfo1 = NULL;
70 
71     // Create the spool file.
72     pHandle->hSPLFile = CreateFileW(pAddJobInfo1->Path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
73     if (pHandle->hSPLFile == INVALID_HANDLE_VALUE)
74     {
75         dwErrorCode = GetLastError();
76         ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1->Path, dwErrorCode);
77         goto Cleanup;
78     }
79 
80     // Get the size of the job information.
81     GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
82     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
83     {
84         dwErrorCode = GetLastError();
85         ERR("GetJobW failed with error %lu!\n", dwErrorCode);
86         goto Cleanup;
87     }
88 
89     // Allocate enough memory for the returned job information.
90     pJobInfo1 = HeapAlloc(hProcessHeap, 0, cbNeeded);
91     if (!pJobInfo1)
92     {
93         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
94         ERR("HeapAlloc failed!\n");
95         goto Cleanup;
96     }
97 
98     // Get the job information.
99     if (!GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
100     {
101         dwErrorCode = GetLastError();
102         ERR("GetJobW failed with error %lu!\n", dwErrorCode);
103         goto Cleanup;
104     }
105 
106     // Add our document information.
107     if (pDocInfo1->pDatatype)
108         pJobInfo1->pDatatype = pDocInfo1->pDatatype;
109 
110     pJobInfo1->pDocument = pDocInfo1->pDocName;
111 
112     // Set the new job information.
113     if (!SetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
114     {
115         dwErrorCode = GetLastError();
116         ERR("SetJobW failed with error %lu!\n", dwErrorCode);
117         goto Cleanup;
118     }
119 
120     // We were successful!
121     pHandle->dwJobID = pAddJobInfo1->JobId;
122     dwErrorCode = ERROR_SUCCESS;
123 
124 Cleanup:
125     if (pJobInfo1)
126         HeapFree(hProcessHeap, 0, pJobInfo1);
127 
128     return dwErrorCode;
129 }
130 
131 static DWORD
132 _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
133 {
134     DWORD dwErrorCode;
135     WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer;
136 
137     DocInfoContainer.Level = 1;
138     DocInfoContainer.DocInfo.pDocInfo1 = (WINSPOOL_DOC_INFO_1*)pDocInfo1;
139 
140     RpcTryExcept
141     {
142         dwErrorCode = _RpcStartDocPrinter(pHandle->hPrinter, &DocInfoContainer, &pHandle->dwJobID);
143     }
144     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
145     {
146         dwErrorCode = RpcExceptionCode();
147         ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode);
148     }
149     RpcEndExcept;
150 
151     return dwErrorCode;
152 }
153 
154 BOOL WINAPI
155 AbortPrinter(HANDLE hPrinter)
156 {
157     DWORD dwErrorCode;
158     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
159 
160     TRACE("AbortPrinter(%p)\n", hPrinter);
161 
162     // Sanity checks.
163     if (!pHandle)
164     {
165         dwErrorCode = ERROR_INVALID_HANDLE;
166         goto Cleanup;
167     }
168 
169     pHandle->bTrayIcon = pHandle->bStartedDoc = FALSE;
170 
171     if ( pHandle->hSPLFile != INVALID_HANDLE_VALUE && pHandle->bJob )
172     {
173         // Close any open file handle.
174         CloseHandle( pHandle->hSPLFile );
175         pHandle->hSPLFile = INVALID_HANDLE_VALUE;
176 
177         SetJobW( hPrinter, pHandle->dwJobID, 0, NULL, JOB_CONTROL_DELETE );
178 
179         return ScheduleJob( hPrinter, pHandle->dwJobID );
180     }
181 
182     // Do the RPC call.
183     RpcTryExcept
184     {
185         dwErrorCode = _RpcAbortPrinter(&pHandle->hPrinter);
186     }
187     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
188     {
189         dwErrorCode = RpcExceptionCode();
190         ERR("_RpcAbortPrinter failed with exception code %lu!\n", dwErrorCode);
191     }
192     RpcEndExcept;
193 
194 Cleanup:
195     SetLastError(dwErrorCode);
196     return (dwErrorCode == ERROR_SUCCESS);
197 }
198 
199 HANDLE WINAPI
200 AddPrinterA(PSTR pName, DWORD Level, PBYTE pPrinter)
201 {
202     UNICODE_STRING pNameW, usBuffer;
203     PWSTR pwstrNameW;
204     PRINTER_INFO_2W *ppi2w = (PRINTER_INFO_2W*)pPrinter;
205     PRINTER_INFO_2A *ppi2a = (PRINTER_INFO_2A*)pPrinter;
206     HANDLE ret = NULL;
207     PWSTR pwszPrinterName = NULL;
208     PWSTR pwszServerName = NULL;
209     PWSTR pwszShareName = NULL;
210     PWSTR pwszPortName = NULL;
211     PWSTR pwszDriverName = NULL;
212     PWSTR pwszComment = NULL;
213     PWSTR pwszLocation = NULL;
214     PWSTR pwszSepFile = NULL;
215     PWSTR pwszPrintProcessor = NULL;
216     PWSTR pwszDatatype = NULL;
217     PWSTR pwszParameters = NULL;
218     PDEVMODEW pdmw = NULL;
219 
220     TRACE("AddPrinterA(%s, %d, %p)\n", debugstr_a(pName), Level, pPrinter);
221 
222     if(Level != 2)
223     {
224         ERR("Level = %d, unsupported!\n", Level);
225         SetLastError(ERROR_INVALID_LEVEL);
226         return NULL;
227     }
228 
229     pwstrNameW = AsciiToUnicode(&pNameW,pName);
230 
231     if (ppi2a->pShareName)
232     {
233         pwszShareName = AsciiToUnicode(&usBuffer, ppi2a->pShareName);
234         if (!(ppi2w->pShareName = pwszShareName)) goto Cleanup;
235     }
236     if (ppi2a->pPortName)
237     {
238         pwszPortName = AsciiToUnicode(&usBuffer, ppi2a->pPortName);
239         if (!(ppi2w->pPortName = pwszPortName)) goto Cleanup;
240     }
241     if (ppi2a->pDriverName)
242     {
243         pwszDriverName = AsciiToUnicode(&usBuffer, ppi2a->pDriverName);
244         if (!(ppi2w->pDriverName = pwszDriverName)) goto Cleanup;
245     }
246     if (ppi2a->pComment)
247     {
248         pwszComment = AsciiToUnicode(&usBuffer, ppi2a->pComment);
249         if (!(ppi2w->pComment = pwszComment)) goto Cleanup;
250     }
251     if (ppi2a->pLocation)
252     {
253         pwszLocation = AsciiToUnicode(&usBuffer, ppi2a->pLocation);
254         if (!(ppi2w->pLocation = pwszLocation)) goto Cleanup;
255     }
256     if (ppi2a->pSepFile)
257     {
258         pwszSepFile = AsciiToUnicode(&usBuffer, ppi2a->pSepFile);
259         if (!(ppi2w->pSepFile = pwszSepFile)) goto Cleanup;
260     }
261     if (ppi2a->pServerName)
262     {
263         pwszPrintProcessor = AsciiToUnicode(&usBuffer, ppi2a->pPrintProcessor);
264         if (!(ppi2w->pPrintProcessor = pwszPrintProcessor)) goto Cleanup;
265     }
266     if (ppi2a->pDatatype)
267     {
268         pwszDatatype = AsciiToUnicode(&usBuffer, ppi2a->pDatatype);
269         if (!(ppi2w->pDatatype = pwszDatatype)) goto Cleanup;
270     }
271     if (ppi2a->pParameters)
272     {
273         pwszParameters = AsciiToUnicode(&usBuffer, ppi2a->pParameters);
274         if (!(ppi2w->pParameters = pwszParameters)) goto Cleanup;
275     }
276     if ( ppi2a->pDevMode )
277     {
278         RosConvertAnsiDevModeToUnicodeDevmode( ppi2a->pDevMode, &pdmw );
279         ppi2w->pDevMode = pdmw;
280     }
281     if (ppi2a->pServerName)
282     {
283         pwszServerName = AsciiToUnicode(&usBuffer, ppi2a->pServerName);
284         if (!(ppi2w->pPrinterName = pwszServerName)) goto Cleanup;
285     }
286     if (ppi2a->pPrinterName)
287     {
288         pwszPrinterName = AsciiToUnicode(&usBuffer, ppi2a->pPrinterName);
289         if (!(ppi2w->pPrinterName = pwszPrinterName)) goto Cleanup;
290     }
291 
292     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)ppi2w);
293 
294 Cleanup:
295     if (pdmw) HeapFree(hProcessHeap, 0, pdmw);
296     if (pwszPrinterName) HeapFree(hProcessHeap, 0, pwszPrinterName);
297     if (pwszServerName) HeapFree(hProcessHeap, 0, pwszServerName);
298     if (pwszShareName) HeapFree(hProcessHeap, 0, pwszShareName);
299     if (pwszPortName) HeapFree(hProcessHeap, 0, pwszPortName);
300     if (pwszDriverName) HeapFree(hProcessHeap, 0, pwszDriverName);
301     if (pwszComment) HeapFree(hProcessHeap, 0, pwszComment);
302     if (pwszLocation) HeapFree(hProcessHeap, 0, pwszLocation);
303     if (pwszSepFile) HeapFree(hProcessHeap, 0, pwszSepFile);
304     if (pwszPrintProcessor) HeapFree(hProcessHeap, 0, pwszPrintProcessor);
305     if (pwszDatatype) HeapFree(hProcessHeap, 0, pwszDatatype);
306     if (pwszParameters) HeapFree(hProcessHeap, 0, pwszParameters);
307 
308     RtlFreeUnicodeString(&pNameW);
309     return ret;
310 }
311 
312 HANDLE WINAPI
313 AddPrinterW(PWSTR pName, DWORD Level, PBYTE pPrinter)
314 {
315     DWORD dwErrorCode;
316     WINSPOOL_PRINTER_CONTAINER PrinterContainer;
317     WINSPOOL_DEVMODE_CONTAINER DevModeContainer;
318     WINSPOOL_SECURITY_CONTAINER SecurityContainer;
319     SECURITY_DESCRIPTOR *sd = NULL;
320     DWORD size;
321     HANDLE hPrinter = NULL, hHandle = NULL;
322     PSPOOLER_HANDLE pHandle = NULL;
323 
324     TRACE("AddPrinterW(%S, %lu, %p)\n", pName, Level, pPrinter);
325 
326     DevModeContainer.cbBuf = 0;
327     DevModeContainer.pDevMode = NULL;
328 
329     SecurityContainer.cbBuf = 0;
330     SecurityContainer.pSecurity = NULL;
331 
332     if ( Level != 2 )
333     {
334         FIXME( "Unsupported level %d\n", Level );
335         SetLastError( ERROR_INVALID_LEVEL );
336         return hHandle;
337     }
338     else
339     {
340         PPRINTER_INFO_2W pi2w = (PPRINTER_INFO_2W)pPrinter;
341         if ( pi2w )
342         {
343             if ( pi2w->pDevMode )
344             {
345                 if ( IsValidDevmodeNoSizeW( pi2w->pDevMode ) )
346                 {
347                     DevModeContainer.cbBuf = pi2w->pDevMode->dmSize + pi2w->pDevMode->dmDriverExtra;
348                     DevModeContainer.pDevMode = (PBYTE)pi2w->pDevMode;
349                 }
350             }
351 
352             if ( pi2w->pSecurityDescriptor )
353             {
354                 sd = get_sd( pi2w->pSecurityDescriptor, &size );
355                 if ( sd )
356                 {
357                     SecurityContainer.cbBuf = size;
358                     SecurityContainer.pSecurity = (PBYTE)sd;
359                 }
360             }
361         }
362         else
363         {
364             SetLastError(ERROR_INVALID_PARAMETER);
365             return hHandle;
366         }
367     }
368 
369     PrinterContainer.PrinterInfo.pPrinterInfo1 = (WINSPOOL_PRINTER_INFO_1*)pPrinter;
370     PrinterContainer.Level = Level;
371 
372     // Do the RPC call
373     RpcTryExcept
374     {
375         dwErrorCode = _RpcAddPrinter( pName, &PrinterContainer, &DevModeContainer, &SecurityContainer, &hPrinter );
376     }
377     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
378     {
379         dwErrorCode = RpcExceptionCode();
380     }
381     RpcEndExcept;
382 
383     if (hPrinter)
384     {
385         // Create a new SPOOLER_HANDLE structure.
386         pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
387         if (!pHandle)
388         {
389             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
390             ERR("HeapAlloc failed!\n");
391             _RpcDeletePrinter(hPrinter);
392             _RpcClosePrinter(hPrinter);
393             goto Cleanup;
394         }
395 
396         pHandle->Sig = SPOOLER_HANDLE_SIG;
397         pHandle->hPrinter = hPrinter;
398         pHandle->hSPLFile = INVALID_HANDLE_VALUE;
399         pHandle->hSpoolFileHandle = INVALID_HANDLE_VALUE;
400         hHandle = (HANDLE)pHandle;
401     }
402 
403 Cleanup:
404     if ( sd ) HeapFree( GetProcessHeap(), 0, sd );
405 
406     SetLastError(dwErrorCode);
407     return hHandle;
408 }
409 
410 BOOL WINAPI
411 ClosePrinter(HANDLE hPrinter)
412 {
413     DWORD dwErrorCode;
414     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
415 
416     TRACE("ClosePrinter(%p)\n", hPrinter);
417 
418     // Sanity checks.
419     if ( IntProtectHandle( hPrinter, TRUE ) )
420     {
421         dwErrorCode = ERROR_INVALID_HANDLE;
422         goto Cleanup;
423     }
424 
425     // Do the RPC call.
426     RpcTryExcept
427     {
428         dwErrorCode = _RpcClosePrinter(&pHandle->hPrinter);
429     }
430     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
431     {
432         dwErrorCode = RpcExceptionCode();
433         ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode);
434     }
435     RpcEndExcept;
436 
437     // Close any open file handle.
438     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
439         CloseHandle(pHandle->hSPLFile);
440 
441     pHandle->Sig = -1;
442 
443     // Free the memory for the handle.
444     HeapFree(hProcessHeap, 0, pHandle);
445 
446 Cleanup:
447     SetLastError(dwErrorCode);
448     return (dwErrorCode == ERROR_SUCCESS);
449 }
450 
451 BOOL WINAPI
452 DeletePrinter(HANDLE hPrinter)
453 {
454     DWORD dwErrorCode;
455     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
456 
457     TRACE("DeletePrinter(%p)\n", hPrinter);
458 
459     // Sanity checks.
460     if (!pHandle)
461     {
462         dwErrorCode = ERROR_INVALID_HANDLE;
463         goto Cleanup;
464     }
465 
466     // Do the RPC call.
467     RpcTryExcept
468     {
469         dwErrorCode = _RpcDeletePrinter(&pHandle->hPrinter);
470     }
471     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
472     {
473         dwErrorCode = RpcExceptionCode();
474         ERR("_RpcDeletePrinter failed with exception code %lu!\n", dwErrorCode);
475     }
476     RpcEndExcept;
477 
478 Cleanup:
479     SetLastError(dwErrorCode);
480     return (dwErrorCode == ERROR_SUCCESS);
481 }
482 
483 //
484 // Based on GDI32:printdrv.c:IntGetPrinterDriver.
485 //
486 HMODULE
487 WINAPI
488 LoadPrinterDriver( HANDLE hspool )
489 {
490     INT iTries = 0;
491     DWORD Size = (sizeof(WCHAR) * MAX_PATH) * 2; // DRIVER_INFO_5W + plus strings.
492     PDRIVER_INFO_5W pdi = NULL;
493     HMODULE hLibrary = NULL;
494 
495     do
496     {
497         ++iTries;
498 
499         pdi = RtlAllocateHeap( GetProcessHeap(), 0, Size);
500 
501         if ( !pdi )
502             break;
503 
504         if ( GetPrinterDriverW(hspool, NULL, 5, (LPBYTE)pdi, Size, &Size) )
505         {
506             TRACE("Level 5 Size %d\n",Size);
507 
508             // Name and load configure library (for example, C:\DRIVERS\Pscrptui.dll). Not printui.dll!
509 
510             hLibrary = LoadLibrary(pdi->pConfigFile);
511 
512             FIXME("IGPD : Get Printer Driver Config File : %S\n",pdi->pConfigFile);
513 
514             RtlFreeHeap( GetProcessHeap(), 0, pdi);
515             return hLibrary;
516         }
517 
518         if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
519             ++iTries;
520 
521         RtlFreeHeap( GetProcessHeap(), 0, pdi);
522      }
523      while ( iTries < 2 );
524      ERR("No Printer Driver Error %d\n",GetLastError());
525      return NULL;
526 }
527 
528 DWORD WINAPI
529 DeviceCapabilitiesA(LPCSTR pDevice, LPCSTR pPort, WORD fwCapability, LPSTR pOutput, const DEVMODEA* pDevMode)
530 {
531     PWSTR pwszDeviceName = NULL;
532     PDEVMODEW pdmwInput = NULL;
533     BOOL bReturnValue = GDI_ERROR;
534     DWORD cch;
535 
536     FIXME("DeviceCapabilitiesA(%s, %s, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
537 
538     if (pDevice)
539     {
540         // Convert pName to a Unicode string pwszDeviceName.
541         cch = strlen(pDevice);
542 
543         pwszDeviceName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
544         if (!pwszDeviceName)
545         {
546             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
547             ERR("HeapAlloc failed!\n");
548             goto Cleanup;
549         }
550 
551         MultiByteToWideChar(CP_ACP, 0, pDevice, -1, pwszDeviceName, cch + 1);
552     }
553 
554     if (pDevMode)
555     {
556         RosConvertAnsiDevModeToUnicodeDevmode((PDEVMODEA)pDevMode, &pdmwInput);
557     }
558 
559     // pPort is ignored so no need to pass it.
560     bReturnValue = DeviceCapabilitiesW( pwszDeviceName, NULL, fwCapability, (LPWSTR)pOutput, (const DEVMODEW*) pdmwInput );
561 
562 Cleanup:
563     if(pwszDeviceName)
564         HeapFree(hProcessHeap, 0, pwszDeviceName);
565 
566     if (pdmwInput)
567         HeapFree(hProcessHeap, 0, pdmwInput);
568 
569     return bReturnValue;
570 }
571 
572 DWORD WINAPI
573 DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort, WORD fwCapability, LPWSTR pOutput, const DEVMODEW* pDevMode)
574 {
575     HANDLE hPrinter;
576     HMODULE hLibrary;
577     DWORD iDevCap = GDI_ERROR;
578 
579     FIXME("DeviceCapabilitiesW(%S, %S, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
580 
581     if ( pDevMode )
582     {
583         if (!IsValidDevmodeNoSizeW( (PDEVMODEW)pDevMode ) )
584         {
585             ERR("DeviceCapabilitiesW : Devode Invalid\n");
586             return -1;
587         }
588     }
589 
590     if ( OpenPrinterW( (LPWSTR)pDevice, &hPrinter, NULL ) )
591     {
592         hLibrary = LoadPrinterDriver( hPrinter );
593 
594         if ( hLibrary )
595         {
596             fpDeviceCapabilities = (PVOID)GetProcAddress( hLibrary, "DrvDeviceCapabilities" );
597 
598             if ( fpDeviceCapabilities )
599             {
600                 iDevCap = fpDeviceCapabilities( hPrinter, (PWSTR)pDevice, fwCapability, pOutput, (PDEVMODE)pDevMode );
601             }
602 
603             FreeLibrary(hLibrary);
604         }
605 
606         ClosePrinter( hPrinter );
607     }
608 
609     return iDevCap;
610 }
611 
612 BOOL
613 WINAPI
614 DevQueryPrint( HANDLE hPrinter, LPDEVMODEW pDevMode, DWORD *pResID)
615 {
616     HMODULE hLibrary;
617     BOOL Ret = FALSE;
618 
619     hLibrary = LoadPrinterDriver( hPrinter );
620 
621     if ( hLibrary )
622     {
623         fpDevQueryPrint = (PVOID)GetProcAddress( hLibrary, "DevQueryPrint" );
624 
625         if ( fpDevQueryPrint )
626         {
627             Ret = fpDevQueryPrint( hPrinter, pDevMode, pResID );
628         }
629 
630         FreeLibrary(hLibrary);
631     }
632     return Ret;
633 }
634 
635 BOOL WINAPI
636 DevQueryPrintEx( PDEVQUERYPRINT_INFO pDQPInfo )
637 {
638     HMODULE hLibrary;
639     BOOL Ret = FALSE;
640 
641     hLibrary = LoadPrinterDriver( pDQPInfo->hPrinter );
642 
643     if ( hLibrary )
644     {
645         fpDevQueryPrintEx = (PVOID)GetProcAddress( hLibrary, "DevQueryPrintEx" );
646 
647         if ( fpDevQueryPrintEx )
648         {
649             Ret = fpDevQueryPrintEx( pDQPInfo );
650         }
651 
652         FreeLibrary(hLibrary);
653     }
654     return Ret;
655 }
656 
657 INT WINAPI
658 DocumentEvent( HANDLE hPrinter, HDC hdc, int iEsc, ULONG cbIn, PVOID pvIn, ULONG cbOut, PVOID pvOut)
659 {
660     FIXME("DocumentEvent(%p, %p, %lu, %lu, %p, %lu, %p)\n", hPrinter, hdc, iEsc, cbIn, pvIn, cbOut, pvOut);
661     UNIMPLEMENTED;
662     return DOCUMENTEVENT_UNSUPPORTED;
663 }
664 
665 LONG WINAPI
666 DocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName, PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput, DWORD fMode)
667 {
668     PWSTR pwszDeviceName = NULL;
669     PDEVMODEW pdmwInput = NULL;
670     PDEVMODEW pdmwOutput = NULL;
671     LONG lReturnValue = -1;
672     DWORD cch;
673 
674     FIXME("DocumentPropertiesA(%p, %p, %s, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
675 
676     if (pDeviceName)
677     {
678         // Convert pName to a Unicode string pwszDeviceName.
679         cch = strlen(pDeviceName);
680 
681         pwszDeviceName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
682         if (!pwszDeviceName)
683         {
684             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
685             ERR("HeapAlloc failed!\n");
686             goto Cleanup;
687         }
688 
689         MultiByteToWideChar(CP_ACP, 0, pDeviceName, -1, pwszDeviceName, cch + 1);
690     }
691 
692     if (pDevModeInput)
693     {
694         // Create working buffer for input to DocumentPropertiesW.
695         RosConvertAnsiDevModeToUnicodeDevmode(pDevModeInput, &pdmwInput);
696     }
697 
698     if (pDevModeOutput)
699     {
700         // Create working buffer for output from DocumentPropertiesW.
701 
702         // Do it RIGHT! Get the F...ing Size!
703         LONG Size = DocumentPropertiesW( hWnd, hPrinter, pwszDeviceName, NULL, NULL, 0 );
704 
705         if ( Size < 0 )
706         {
707             goto Cleanup;
708         }
709 
710         pdmwOutput = HeapAlloc(hProcessHeap, 0, Size);
711         if (!pdmwOutput)
712         {
713             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
714             ERR("HeapAlloc failed!\n");
715             goto Cleanup;
716         }
717     }
718 
719     lReturnValue = DocumentPropertiesW(hWnd, hPrinter, pwszDeviceName, pdmwOutput, pdmwInput, fMode);
720     FIXME("lReturnValue from DocumentPropertiesW is '%ld'.\n", lReturnValue);
721 
722     if (lReturnValue < 0)
723     {
724         FIXME("DocumentPropertiesW failed!\n");
725         goto Cleanup;
726     }
727 
728     if (pdmwOutput)
729     {
730         RosConvertUnicodeDevModeToAnsiDevmode(pdmwOutput, pDevModeOutput);
731     }
732 
733 Cleanup:
734     if(pwszDeviceName)
735         HeapFree(hProcessHeap, 0, pwszDeviceName);
736 
737     if (pdmwInput)
738         HeapFree(hProcessHeap, 0, pdmwInput);
739 
740     if (pdmwOutput)
741         HeapFree(hProcessHeap, 0, pdmwOutput);
742 
743     return lReturnValue;
744 }
745 
746 PRINTER_INFO_9W * get_devmodeW(HANDLE hprn)
747 {
748     PRINTER_INFO_9W *pi9 = NULL;
749     DWORD needed = 0;
750     BOOL res;
751 
752     res = GetPrinterW(hprn, 9, NULL, 0, &needed);
753     if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
754     {
755         pi9 = HeapAlloc(hProcessHeap, 0, needed);
756         res = GetPrinterW(hprn, 9, (LPBYTE)pi9, needed, &needed);
757     }
758 
759     if (res)
760         return pi9;
761 
762     ERR("GetPrinterW failed with %u\n", GetLastError());
763     HeapFree(hProcessHeap, 0, pi9);
764     return NULL;
765 }
766 
767 BOOL
768 FASTCALL
769 CreateUIUserData( ULONG_PTR *puserdata, HANDLE hPrinter )
770 {
771     PCOMPUI_USERDATA pcui_ud = DllAllocSplMem( sizeof(COMPUI_USERDATA) );
772 
773     *puserdata = (ULONG_PTR)pcui_ud;
774     FIXME("CreateUIUserData\n");
775     if ( pcui_ud )
776     {
777         pcui_ud->hModule = LoadPrinterDriver( hPrinter );
778 
779         if ( !pcui_ud->hModule )
780         {
781             DllFreeSplMem( pcui_ud );
782             *puserdata = 0;
783         }
784     }
785     return *puserdata != 0;
786 }
787 
788 VOID
789 FASTCALL
790 DestroyUIUserData( ULONG_PTR *puserdata )
791 {
792     PCOMPUI_USERDATA pcui_ud = (PCOMPUI_USERDATA)*puserdata;
793     FIXME("DestroyUIUserData\n");
794     if ( pcui_ud )
795     {
796         if ( pcui_ud->hModule )
797         {
798             FreeLibrary( pcui_ud->hModule );
799             pcui_ud->hModule = NULL;
800         }
801 
802         if ( pcui_ud->pszPrinterName )
803         {
804             DllFreeSplMem( pcui_ud->pszPrinterName );
805             pcui_ud->pszPrinterName = NULL;
806         }
807 
808         DllFreeSplMem( pcui_ud );
809         *puserdata = 0;
810     }
811 }
812 
813 BOOL
814 FASTCALL
815 IntFixUpDevModeNames( PDOCUMENTPROPERTYHEADER pdphdr )
816 {
817     PRINTER_INFO_2W *pi2 = NULL;
818     DWORD needed = 0;
819     BOOL res;
820 
821     if (!(pdphdr->fMode & DM_OUT_BUFFER) ||
822          pdphdr->fMode & DM_NOPERMISSION || // Do not allow the user to modify properties on the displayed property sheet pages.
823         !pdphdr->pdmOut )
824     {
825         return FALSE;
826     }
827 
828     res = GetPrinterW( pdphdr->hPrinter, 2, NULL, 0, &needed);
829     if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
830     {
831         pi2 = HeapAlloc(hProcessHeap, 0, needed);
832         res = GetPrinterW( pdphdr->hPrinter, 2, (LPBYTE)pi2, needed, &needed);
833     }
834 
835     if (res)
836     {
837         FIXME("IFUDMN : Get Printer Name %S\n",pi2->pPrinterName);
838         StringCchCopyW( pdphdr->pdmOut->dmDeviceName, CCHDEVICENAME-1, pi2->pPrinterName );
839         pdphdr->pdmOut->dmDeviceName[CCHDEVICENAME-1] = 0;
840     }
841     else
842     {
843         ERR("IFUDMN : GetPrinterW failed with %u\n", GetLastError());
844     }
845     HeapFree(hProcessHeap, 0, pi2);
846     return res;
847 }
848 
849 LONG
850 WINAPI
851 CreatePrinterFriendlyName( PCOMPUI_USERDATA pcui_ud, LPWSTR pszPrinterName )
852 {
853     LONG Result = 0;
854     DWORD Size = 0;
855     HMODULE hLibrary = NULL;
856 
857     hLibrary = LoadLibraryA( "printui.dll" );
858 
859     if ( hLibrary )
860     {
861         fpConstructPrinterFriendlyName = (PVOID)GetProcAddress( hLibrary, "ConstructPrinterFriendlyName" );
862 
863         if ( fpConstructPrinterFriendlyName )
864         {
865              if ( !fpConstructPrinterFriendlyName( pszPrinterName, NULL, &Size ) )
866              {
867                  if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
868                  {
869                      PWSTR pwstr = DllAllocSplMem( (Size + 1) * sizeof(WCHAR) );
870 
871                      pcui_ud->pszPrinterName = pwstr;
872 
873                      if ( pwstr )
874                          Result = fpConstructPrinterFriendlyName( pszPrinterName, pwstr, &Size );
875                  }
876              }
877         }
878         FreeLibrary( hLibrary );
879     }
880 
881     if ( !Result )
882     {
883         DllFreeSplMem( pcui_ud->pszPrinterName );
884         pcui_ud->pszPrinterName = AllocSplStr( pszPrinterName );
885     }
886 
887     return Result;
888 }
889 
890 //
891 // Tested with XP CompstUI as a callback and works. Fails perfectly.
892 //
893 LONG
894 WINAPI
895 DocumentPropertySheets( PPROPSHEETUI_INFO pCPSUIInfo, LPARAM lparam )
896 {
897     LONG Result = -1;
898     PDOCUMENTPROPERTYHEADER pdphdr;
899 
900     FIXME("DocumentPropertySheets(%p, 0x%lx)\n", pCPSUIInfo, lparam);
901 
902     // If pPSUIInfo is NULL, and if either lParam -> fMode is zero or lParam -> pdmOut is NULL,
903     // this function should return the size, in bytes, of the printer's DEVMODEW structure.
904     if ( !pCPSUIInfo && lparam )
905     {
906         pdphdr = (PDOCUMENTPROPERTYHEADER)lparam;
907 
908         if ( pdphdr->cbSize >= sizeof(PDOCUMENTPROPERTYHEADER) &&
909             !(pdphdr->fMode & DM_PROMPT) )
910         {
911             HMODULE hLibrary = LoadPrinterDriver( pdphdr->hPrinter );
912 
913             if ( hLibrary )
914             {
915                 fpDocumentPropertySheets = (PVOID)GetProcAddress( hLibrary, "DrvDocumentPropertySheets" );
916 
917                 if ( fpDocumentPropertySheets )
918                 {
919                     FIXME("DPS : fpDocumentPropertySheets(%p, 0x%lx) pdmOut %p\n", pCPSUIInfo, lparam, pdphdr->pdmOut);
920                     Result = fpDocumentPropertySheets( pCPSUIInfo, lparam );
921                     FIXME("DPS : fpDocumentPropertySheets result %d cbOut %d\n",Result, pdphdr->cbOut);
922                 }
923                 else
924                 {
925                     //
926                     // ReactOS backup!!! Currently no supporting UI driver.
927                     //
928                     PRINTER_INFO_9W * pi9 = get_devmodeW( pdphdr->hPrinter );
929                     if ( pi9 )
930                     {
931                         Result = pi9->pDevMode->dmSize + pi9->pDevMode->dmDriverExtra;
932                         FIXME("IDPS : Using ReactOS backup!!! DevMode Size %d\n",Result);
933                         HeapFree(hProcessHeap, 0, pi9);
934                     }
935                 }
936 
937                 FreeLibrary(hLibrary);
938 
939                 if ( Result > 0 )
940                 {
941                     IntFixUpDevModeNames( pdphdr );
942                 }
943 
944                 return Result;
945             }
946             else
947             {
948                 SetLastError(ERROR_INVALID_HANDLE);
949             }
950         }
951         else
952         {
953             SetLastError(ERROR_INVALID_PARAMETER);
954         }
955         return Result;
956     }
957 
958     Result = 0;
959 
960     if ( pCPSUIInfo )
961     {
962         PSETRESULT_INFO psri;
963         PPROPSHEETUI_INFO_HEADER ppsuiihdr;
964         PCOMPUI_USERDATA pcui_ud;
965         pdphdr = (PDOCUMENTPROPERTYHEADER)pCPSUIInfo->lParamInit;
966 
967         if ( pdphdr->cbSize < sizeof(PDOCUMENTPROPERTYHEADER) )
968         {
969             SetLastError(ERROR_INVALID_PARAMETER);
970             return Result;
971         }
972 
973         switch ( pCPSUIInfo->Reason )
974         {
975             case PROPSHEETUI_REASON_INIT:
976             {
977                 FIXME("DocPS : PROPSHEETUI_REASON_INIT\n");
978                 if ( CreateUIUserData( &pCPSUIInfo->UserData, pdphdr->hPrinter ) )
979                 {
980                     pcui_ud = (PCOMPUI_USERDATA)pCPSUIInfo->UserData;
981 
982                     fpDocumentPropertySheets = (PVOID)GetProcAddress( pcui_ud->hModule, "DrvDocumentPropertySheets" );
983 
984                     if ( fpDocumentPropertySheets )
985                     {
986                         pCPSUIInfo->pfnComPropSheet( pCPSUIInfo->hComPropSheet,
987                                                      CPSFUNC_SET_FUSION_CONTEXT,
988                                                      -3,  // What type of handle is this?
989                                                      0 ); // Not used, must be zero.
990 
991                         Result = pCPSUIInfo->pfnComPropSheet( pCPSUIInfo->hComPropSheet,
992                                                               CPSFUNC_ADD_PFNPROPSHEETUIW,
993                                                              (LPARAM)fpDocumentPropertySheets,
994                                                               pCPSUIInfo->lParamInit );
995                         break;
996                     }
997                     FIXME("DocPS : PROPSHEETUI_REASON_INIT Fail\n");
998                     DestroyUIUserData( &pCPSUIInfo->UserData );
999                 }
1000             }
1001                 break;
1002 
1003             case PROPSHEETUI_REASON_GET_INFO_HEADER:
1004                 FIXME("DocPS : PROPSHEETUI_REASON_GET_INFO_HEADER\n");
1005 
1006                 ppsuiihdr = (PPROPSHEETUI_INFO_HEADER)lparam;
1007 
1008                 pcui_ud = (PCOMPUI_USERDATA)pCPSUIInfo->UserData;
1009 
1010                 CreatePrinterFriendlyName( pcui_ud, pdphdr->pszPrinterName );
1011 
1012                 ppsuiihdr->Flags  = PSUIHDRF_NOAPPLYNOW|PSUIHDRF_PROPTITLE;
1013                 ppsuiihdr->pTitle = pcui_ud->pszPrinterName;
1014                 ppsuiihdr->hInst  = hinstWinSpool;
1015                 ppsuiihdr->IconID = IDI_CPSUI_DOCUMENT;
1016 
1017                 Result = CPSUI_OK;
1018                 break;
1019 
1020             case PROPSHEETUI_REASON_DESTROY:
1021                 FIXME("DocPS : PROPSHEETUI_REASON_DESTROY\n");
1022                 DestroyUIUserData( &pCPSUIInfo->UserData );
1023                 Result = CPSUI_OK;
1024                 break;
1025 
1026             case PROPSHEETUI_REASON_SET_RESULT:
1027                 FIXME("DocPS : PROPSHEETUI_REASON_SET_RESULT\n");
1028 
1029                 psri = (PSETRESULT_INFO)lparam;
1030 
1031                 pCPSUIInfo->Result = psri->Result;
1032                 if ( pCPSUIInfo->Result > 0 )
1033                 {
1034                     IntFixUpDevModeNames( pdphdr );
1035                 }
1036                 Result = CPSUI_OK;
1037                 break;
1038         }
1039     }
1040     return Result;
1041 }
1042 
1043 LONG
1044 WINAPI
1045 DevicePropertySheets( PPROPSHEETUI_INFO pCPSUIInfo, LPARAM lparam )
1046 {
1047     LONG Result = 0;
1048     PDEVICEPROPERTYHEADER pdphdr;
1049 
1050     FIXME("DevicePropertySheets(%p, 0x%lx)\n", pCPSUIInfo, lparam);
1051 
1052     if ( pCPSUIInfo )
1053     {
1054         PSETRESULT_INFO psri;
1055         PPROPSHEETUI_INFO_HEADER ppsuiihdr;
1056         PCOMPUI_USERDATA pcui_ud;
1057         pdphdr = (PDEVICEPROPERTYHEADER)pCPSUIInfo->lParamInit;
1058 
1059         if ( pdphdr->cbSize < sizeof(DEVICEPROPERTYHEADER) )
1060         {
1061             SetLastError(ERROR_INVALID_PARAMETER);
1062             return Result;
1063         }
1064 
1065         switch ( pCPSUIInfo->Reason )
1066         {
1067             case PROPSHEETUI_REASON_INIT:
1068             {
1069                 FIXME("DevPS : PROPSHEETUI_REASON_INIT\n");
1070                 if ( CreateUIUserData( &pCPSUIInfo->UserData, pdphdr->hPrinter ) )
1071                 {
1072                     pcui_ud = (PCOMPUI_USERDATA)pCPSUIInfo->UserData;
1073 
1074                     fpDevicePropertySheets = (PVOID)GetProcAddress( pcui_ud->hModule, "DrvDevicePropertySheets" );
1075 
1076                     if ( fpDevicePropertySheets )
1077                     {
1078                         pCPSUIInfo->pfnComPropSheet( pCPSUIInfo->hComPropSheet,
1079                                                      CPSFUNC_SET_FUSION_CONTEXT,
1080                                                      -3,  // What type of handle is this?
1081                                                      0 ); // Not used, must be zero.
1082 
1083                         Result = pCPSUIInfo->pfnComPropSheet( pCPSUIInfo->hComPropSheet,
1084                                                               CPSFUNC_ADD_PFNPROPSHEETUIW,
1085                                                              (LPARAM)fpDevicePropertySheets,
1086                                                               pCPSUIInfo->lParamInit );
1087                         break;
1088                     }
1089                     FIXME("DevPS : PROPSHEETUI_REASON_INIT Fail\n");
1090                     DestroyUIUserData( &pCPSUIInfo->UserData );
1091                 }
1092             }
1093                 break;
1094 
1095             case PROPSHEETUI_REASON_GET_INFO_HEADER:
1096                 FIXME("DevPS : PROPSHEETUI_REASON_GET_INFO_HEADER\n");
1097 
1098                 ppsuiihdr = (PPROPSHEETUI_INFO_HEADER)lparam;
1099 
1100                 pcui_ud = (PCOMPUI_USERDATA)pCPSUIInfo->UserData;
1101 
1102                 CreatePrinterFriendlyName( pcui_ud, pdphdr->pszPrinterName );
1103 
1104                 ppsuiihdr->Flags  = PSUIHDRF_NOAPPLYNOW|PSUIHDRF_PROPTITLE;
1105                 ppsuiihdr->pTitle = pcui_ud->pszPrinterName;
1106                 ppsuiihdr->hInst  = hinstWinSpool;
1107                 ppsuiihdr->IconID = IDI_CPSUI_DOCUMENT;
1108 
1109                 Result = CPSUI_OK;
1110                 break;
1111 
1112             case PROPSHEETUI_REASON_DESTROY:
1113                 FIXME("DevPS : PROPSHEETUI_REASON_DESTROY\n");
1114                 DestroyUIUserData( &pCPSUIInfo->UserData );
1115                 Result = CPSUI_OK;
1116                 break;
1117 
1118             case PROPSHEETUI_REASON_SET_RESULT:
1119                 FIXME("DevPS : PROPSHEETUI_REASON_SET_RESULT\n");
1120                 psri = (PSETRESULT_INFO)lparam;
1121                 pCPSUIInfo->Result = psri->Result;
1122                 Result = CPSUI_OK;
1123                 break;
1124         }
1125     }
1126     return Result;
1127 }
1128 
1129 LONG
1130 WINAPI
1131 CallCommonPropertySheetUI(HWND hWnd, PFNPROPSHEETUI pfnPropSheetUI, LPARAM lparam, LPDWORD pResult)
1132 {
1133     HMODULE hLibrary = NULL;
1134     LONG Ret = ERR_CPSUI_GETLASTERROR;
1135 
1136     FIXME("CallCommonPropertySheetUI(%p, %p, 0x%lx, %p)\n", hWnd, pfnPropSheetUI, lparam, pResult);
1137 
1138     if ( ( hLibrary = LoadLibraryA( "compstui.dll" ) ) )
1139     {
1140         fpCommonPropertySheetUIW = (PVOID) GetProcAddress(hLibrary, "CommonPropertySheetUIW");
1141 
1142         if ( fpCommonPropertySheetUIW )
1143         {
1144             Ret = fpCommonPropertySheetUIW( hWnd, pfnPropSheetUI, lparam, pResult );
1145         }
1146 
1147         FreeLibrary(hLibrary);
1148     }
1149     return Ret;
1150 }
1151 
1152 LONG WINAPI
1153 DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput, DWORD fMode)
1154 {
1155     HANDLE hUseHandle = NULL;
1156     DOCUMENTPROPERTYHEADER docprophdr;
1157     LONG Result = IDOK;
1158 
1159     FIXME("DocumentPropertiesW(%p, %p, %S, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
1160 
1161     if (hPrinter)
1162     {
1163         hUseHandle = hPrinter;
1164     }
1165     else if (!OpenPrinterW(pDeviceName, &hUseHandle, NULL))
1166     {
1167         ERR("No handle, and no usable printer name passed in\n");
1168         return -1;
1169     }
1170 
1171     if ( !(fMode & DM_IN_BUFFER ) ||
1172          ( ( pDevModeInput && !IsValidDevmodeNoSizeW( (PDEVMODEW)pDevModeInput ) ) ) )
1173     {
1174         pDevModeInput = NULL;
1175         fMode &= ~DM_IN_BUFFER;
1176     }
1177 
1178     docprophdr.cbSize         = sizeof(DOCUMENTPROPERTYHEADER);
1179     docprophdr.Reserved       = 0;
1180     docprophdr.hPrinter       = hUseHandle;
1181     docprophdr.pszPrinterName = pDeviceName;
1182     docprophdr.cbOut          = 0;
1183 
1184     if ( pDevModeOutput )
1185     {
1186         docprophdr.pdmIn  = NULL;
1187         docprophdr.pdmOut = NULL;
1188         docprophdr.fMode  = 0;
1189         FIXME("DPW : Call DocumentPropertySheets with pDevModeOutput %p\n",pDevModeOutput);
1190         docprophdr.cbOut  = DocumentPropertySheets( NULL, (LPARAM)&docprophdr );
1191     }
1192 
1193     docprophdr.pdmIn  = pDevModeInput;
1194     docprophdr.pdmOut = pDevModeOutput;
1195     docprophdr.fMode  = fMode;
1196 
1197     if ( fMode & DM_IN_PROMPT )
1198     {
1199         Result = CPSUI_CANCEL;
1200 
1201         //
1202         // Now call the Property Sheet for Print > Properties.
1203         //
1204         if ( CallCommonPropertySheetUI( hWnd, (PFNPROPSHEETUI)DocumentPropertySheets, (LPARAM)&docprophdr, (LPDWORD)&Result ) < 0 )
1205         {
1206             FIXME("CallCommonPropertySheetUI return error\n");
1207             Result = ERR_CPSUI_GETLASTERROR;
1208         }
1209         else
1210             Result = (Result == CPSUI_OK) ? IDOK : IDCANCEL;
1211         FIXME("CallCommonPropertySheetUI returned\n");
1212     }
1213     else
1214     {
1215         FIXME("DPW : CallDocumentPropertySheets\n");
1216         Result = DocumentPropertySheets( NULL, (LPARAM)&docprophdr );
1217     }
1218 
1219     if ( Result != ERR_CPSUI_GETLASTERROR || Result != ERR_CPSUI_ALLOCMEM_FAILED )
1220     {
1221         if ( pDevModeOutput )
1222         {
1223             if ( !IsValidDevmodeNoSizeW( pDevModeOutput ) )
1224             {
1225                 ERR("DPW : Improper pDevModeOutput size.\n");
1226                 Result = -1;
1227             }
1228         }
1229         else
1230         {
1231             ERR("No pDevModeOutput\n");
1232         }
1233     }
1234 
1235     if (hUseHandle && !hPrinter)
1236         ClosePrinter(hUseHandle);
1237     return Result;
1238 }
1239 
1240 BOOL
1241 WINAPI
1242 PrinterProperties( HWND hWnd, HANDLE hPrinter )
1243 {
1244     PRINTER_INFO_2W *pi2 = NULL;
1245     DWORD needed = 0;
1246     LONG Ret, Result = 0;
1247     BOOL res;
1248     DEVICEPROPERTYHEADER devprophdr;
1249 
1250     FIXME("PrinterProperties(%p, %p)\n", hWnd, hPrinter);
1251 
1252     devprophdr.cbSize         = sizeof(DEVICEPROPERTYHEADER);
1253     devprophdr.Flags          = DPS_NOPERMISSION;
1254     devprophdr.hPrinter       = hPrinter;
1255     devprophdr.pszPrinterName = NULL;
1256 
1257     res = GetPrinterW( hPrinter, 2, NULL, 0, &needed);
1258     if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
1259     {
1260         pi2 = HeapAlloc(hProcessHeap, 0, needed);
1261         res = GetPrinterW(hPrinter, 2, (LPBYTE)pi2, needed, &needed);
1262     }
1263 
1264     //
1265     // Above can fail, still process w/o printer name.
1266     //
1267     if ( res ) devprophdr.pszPrinterName = pi2->pPrinterName;
1268 
1269     needed = 1;
1270 
1271     if ( ( SetPrinterDataW( hPrinter, L"PrinterPropertiesPermission", REG_DWORD, (LPBYTE)&needed, sizeof(DWORD) ) == ERROR_SUCCESS ) )
1272     {
1273         devprophdr.Flags &= ~DPS_NOPERMISSION;
1274     }
1275 
1276     Ret = CallCommonPropertySheetUI( hWnd, (PFNPROPSHEETUI)DevicePropertySheets, (LPARAM)&devprophdr, (LPDWORD)&Result );
1277 
1278     res = (Ret >= 0);
1279 
1280     if (!res)
1281     {
1282         FIXME("PrinterProperties fail ICPSUI\n");
1283     }
1284 
1285     if (pi2) HeapFree(hProcessHeap, 0, pi2);
1286 
1287     return res;
1288 }
1289 
1290 BOOL WINAPI
1291 EndDocPrinter(HANDLE hPrinter)
1292 {
1293     DWORD dwErrorCode;
1294     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1295 
1296     TRACE("EndDocPrinter(%p)\n", hPrinter);
1297 
1298     // Sanity checks.
1299     if (!pHandle)
1300     {
1301         dwErrorCode = ERROR_INVALID_HANDLE;
1302         goto Cleanup;
1303     }
1304 
1305     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
1306     {
1307         // For spooled jobs, the document is finished by calling _RpcScheduleJob.
1308         RpcTryExcept
1309         {
1310             dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, pHandle->dwJobID);
1311         }
1312         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1313         {
1314             dwErrorCode = RpcExceptionCode();
1315             ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
1316         }
1317         RpcEndExcept;
1318 
1319         // Close the spool file handle.
1320         CloseHandle(pHandle->hSPLFile);
1321     }
1322     else
1323     {
1324         // In all other cases, just call _RpcEndDocPrinter.
1325         RpcTryExcept
1326         {
1327             dwErrorCode = _RpcEndDocPrinter(pHandle->hPrinter);
1328         }
1329         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1330         {
1331             dwErrorCode = RpcExceptionCode();
1332             ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode);
1333         }
1334         RpcEndExcept;
1335     }
1336 
1337     // A new document can now be started again.
1338     pHandle->bTrayIcon = pHandle->bJob = pHandle->bStartedDoc = FALSE;
1339 
1340 Cleanup:
1341     SetLastError(dwErrorCode);
1342     return (dwErrorCode == ERROR_SUCCESS);
1343 }
1344 
1345 BOOL WINAPI
1346 EndPagePrinter(HANDLE hPrinter)
1347 {
1348     DWORD dwErrorCode;
1349     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1350 
1351     TRACE("EndPagePrinter(%p)\n", hPrinter);
1352 
1353     // Sanity checks.
1354     if (!pHandle)
1355     {
1356         dwErrorCode = ERROR_INVALID_HANDLE;
1357         goto Cleanup;
1358     }
1359 
1360     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
1361     {
1362         // For spooled jobs, we don't need to do anything.
1363         dwErrorCode = ERROR_SUCCESS;
1364     }
1365     else
1366     {
1367         // In all other cases, just call _RpcEndPagePrinter.
1368         RpcTryExcept
1369         {
1370             dwErrorCode = _RpcEndPagePrinter(pHandle->hPrinter);
1371         }
1372         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1373         {
1374             dwErrorCode = RpcExceptionCode();
1375             ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode);
1376         }
1377         RpcEndExcept;
1378     }
1379 
1380 Cleanup:
1381     SetLastError(dwErrorCode);
1382     return (dwErrorCode == ERROR_SUCCESS);
1383 }
1384 
1385 BOOL WINAPI
1386 EnumPrintersA(DWORD Flags, PSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
1387 {
1388     DWORD dwErrorCode;
1389     DWORD cch;
1390     PWSTR pwszName = NULL;
1391     PSTR pszPrinterName = NULL;
1392     PSTR pszServerName = NULL;
1393     PSTR pszDescription = NULL;
1394     PSTR pszName = NULL;
1395     PSTR pszComment = NULL;
1396     PSTR pszShareName = NULL;
1397     PSTR pszPortName = NULL;
1398     PSTR pszDriverName = NULL;
1399     PSTR pszLocation = NULL;
1400     PSTR pszSepFile = NULL;
1401     PSTR pszPrintProcessor = NULL;
1402     PSTR pszDatatype = NULL;
1403     PSTR pszParameters = NULL;
1404     DWORD i;
1405     PPRINTER_INFO_1W ppi1w = NULL;
1406     PPRINTER_INFO_1A ppi1a = NULL;
1407     PPRINTER_INFO_2W ppi2w = NULL;
1408     PPRINTER_INFO_2A ppi2a = NULL;
1409     PPRINTER_INFO_4W ppi4w = NULL;
1410     PPRINTER_INFO_4A ppi4a = NULL;
1411     PPRINTER_INFO_5W ppi5w = NULL;
1412     PPRINTER_INFO_5A ppi5a = NULL;
1413 
1414     TRACE("EnumPrintersA(%lu, %s, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
1415 
1416     // Check for invalid levels here for early error return. MSDN says that only 1, 2, 4, and 5 are allowable.
1417     if (Level !=  1 && Level != 2 && Level != 4 && Level != 5)
1418     {
1419         dwErrorCode = ERROR_INVALID_LEVEL;
1420         ERR("Invalid Level!\n");
1421         goto Cleanup;
1422     }
1423 
1424     if (Name)
1425     {
1426         // Convert pName to a Unicode string pwszName.
1427         cch = strlen(Name);
1428 
1429         pwszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1430         if (!pwszName)
1431         {
1432             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1433             ERR("HeapAlloc failed!\n");
1434             goto Cleanup;
1435         }
1436 
1437         MultiByteToWideChar(CP_ACP, 0, Name, -1, pwszName, cch + 1);
1438     }
1439 
1440     /* Ref: https://stackoverflow.com/questions/41147180/why-enumprintersa-and-enumprintersw-request-the-same-amount-of-memory */
1441     if (!EnumPrintersW(Flags, pwszName, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned))
1442     {
1443         dwErrorCode = GetLastError();
1444         goto Cleanup;
1445     }
1446 
1447     /* We are mapping multiple different pointers to the same pPrinterEnum pointer here so that */
1448     /* we can do in-place conversion. We read the Unicode response from the EnumPrintersW and */
1449     /* then we write back the ANSI conversion into the same buffer for our EnumPrintersA output */
1450 
1451     /* mapping to pPrinterEnum for Unicode (w) characters for Levels 1, 2, 4, and 5 */
1452     ppi1w = (PPRINTER_INFO_1W)pPrinterEnum;
1453     ppi2w = (PPRINTER_INFO_2W)pPrinterEnum;
1454     ppi4w = (PPRINTER_INFO_4W)pPrinterEnum;
1455     ppi5w = (PPRINTER_INFO_5W)pPrinterEnum;
1456     /* mapping to pPrinterEnum for ANSI (a) characters for Levels 1, 2, 4, and 5 */
1457     ppi1a = (PPRINTER_INFO_1A)pPrinterEnum;
1458     ppi2a = (PPRINTER_INFO_2A)pPrinterEnum;
1459     ppi4a = (PPRINTER_INFO_4A)pPrinterEnum;
1460     ppi5a = (PPRINTER_INFO_5A)pPrinterEnum;
1461 
1462     for (i = 0; i < *pcReturned; i++)
1463     {
1464         switch (Level)
1465         {
1466             case 1:
1467             {
1468                 if (ppi1w[i].pDescription)
1469                 {
1470                     // Convert Unicode pDescription to a ANSI string pszDescription.
1471                     cch = wcslen(ppi1w[i].pDescription);
1472 
1473                     pszDescription = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1474                     if (!pszDescription)
1475                     {
1476                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1477                         ERR("HeapAlloc failed!\n");
1478                         goto Cleanup;
1479                     }
1480 
1481                     WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pDescription, -1, pszDescription, cch + 1, NULL, NULL);
1482                     StringCchCopyA(ppi1a[i].pDescription, cch + 1, pszDescription);
1483 
1484                     HeapFree(hProcessHeap, 0, pszDescription);
1485                 }
1486 
1487                 if (ppi1w[i].pName)
1488                 {
1489                     // Convert Unicode pName to a ANSI string pszName.
1490                     cch = wcslen(ppi1w[i].pName);
1491 
1492                     pszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1493                     if (!pszName)
1494                     {
1495                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1496                         ERR("HeapAlloc failed!\n");
1497                         goto Cleanup;
1498                     }
1499 
1500                     WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pName, -1, pszName, cch + 1, NULL, NULL);
1501                     StringCchCopyA(ppi1a[i].pName, cch + 1, pszName);
1502 
1503                     HeapFree(hProcessHeap, 0, pszName);
1504                 }
1505 
1506                 if (ppi1w[i].pComment)
1507                 {
1508                     // Convert Unicode pComment to a ANSI string pszComment.
1509                     cch = wcslen(ppi1w[i].pComment);
1510 
1511                     pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1512                     if (!pszComment)
1513                     {
1514                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1515                         ERR("HeapAlloc failed!\n");
1516                         goto Cleanup;
1517                     }
1518 
1519                     WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pComment, -1, pszComment, cch + 1, NULL, NULL);
1520                     StringCchCopyA(ppi1a[i].pComment, cch + 1, pszComment);
1521 
1522                     HeapFree(hProcessHeap, 0, pszComment);
1523                 }
1524                 break;
1525             }
1526 
1527 
1528             case 2:
1529             {
1530                 if (ppi2w[i].pServerName)
1531                 {
1532                     // Convert Unicode pServerName to a ANSI string pszServerName.
1533                     cch = wcslen(ppi2w[i].pServerName);
1534 
1535                     pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1536                     if (!pszServerName)
1537                     {
1538                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1539                         ERR("HeapAlloc failed!\n");
1540                         goto Cleanup;
1541                     }
1542 
1543                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pServerName, -1, pszServerName, cch + 1, NULL, NULL);
1544                     StringCchCopyA(ppi2a[i].pServerName, cch + 1, pszServerName);
1545 
1546                     HeapFree(hProcessHeap, 0, pszServerName);
1547                 }
1548 
1549                 if (ppi2w[i].pPrinterName)
1550                 {
1551                     // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
1552                     cch = wcslen(ppi2w[i].pPrinterName);
1553 
1554                     pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1555                     if (!pszPrinterName)
1556                     {
1557                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1558                         ERR("HeapAlloc failed!\n");
1559                         goto Cleanup;
1560                     }
1561 
1562                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
1563                     StringCchCopyA(ppi2a[i].pPrinterName, cch + 1, pszPrinterName);
1564 
1565                     HeapFree(hProcessHeap, 0, pszPrinterName);
1566                 }
1567 
1568                 if (ppi2w[i].pShareName)
1569                 {
1570                     // Convert Unicode pShareName to a ANSI string pszShareName.
1571                     cch = wcslen(ppi2w[i].pShareName);
1572 
1573                     pszShareName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1574                     if (!pszShareName)
1575                     {
1576                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1577                         ERR("HeapAlloc failed!\n");
1578                         goto Cleanup;
1579                     }
1580 
1581                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pShareName, -1, pszShareName, cch + 1, NULL, NULL);
1582                     StringCchCopyA(ppi2a[i].pShareName, cch + 1, pszShareName);
1583 
1584                     HeapFree(hProcessHeap, 0, pszShareName);
1585                 }
1586 
1587                 if (ppi2w[i].pPortName)
1588                 {
1589                     // Convert Unicode pPortName to a ANSI string pszPortName.
1590                     cch = wcslen(ppi2w[i].pPortName);
1591 
1592                     pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1593                     if (!pszPortName)
1594                     {
1595                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1596                         ERR("HeapAlloc failed!\n");
1597                         goto Cleanup;
1598                     }
1599 
1600                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPortName, -1, pszPortName, cch + 1, NULL, NULL);
1601                     StringCchCopyA(ppi2a[i].pPortName, cch + 1, pszPortName);
1602 
1603                     HeapFree(hProcessHeap, 0, pszPortName);
1604                 }
1605 
1606                 if (ppi2w[i].pDriverName)
1607                 {
1608                     // Convert Unicode pDriverName to a ANSI string pszDriverName.
1609                     cch = wcslen(ppi2w[i].pDriverName);
1610 
1611                     pszDriverName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1612                     if (!pszDriverName)
1613                     {
1614                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1615                         ERR("HeapAlloc failed!\n");
1616                         goto Cleanup;
1617                     }
1618 
1619                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pDriverName, -1, pszDriverName, cch + 1, NULL, NULL);
1620                     StringCchCopyA(ppi2a[i].pDriverName, cch + 1, pszDriverName);
1621 
1622                     HeapFree(hProcessHeap, 0, pszDriverName);
1623                 }
1624 
1625                 if (ppi2w[i].pComment)
1626                 {
1627                     // Convert Unicode pComment to a ANSI string pszComment.
1628                     cch = wcslen(ppi2w[i].pComment);
1629 
1630                     pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1631                     if (!pszComment)
1632                     {
1633                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1634                         ERR("HeapAlloc failed!\n");
1635                         goto Cleanup;
1636                     }
1637 
1638                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pComment, -1, pszComment, cch + 1, NULL, NULL);
1639                     StringCchCopyA(ppi2a[i].pComment, cch + 1, pszComment);
1640 
1641                     HeapFree(hProcessHeap, 0, pszComment);
1642                 }
1643 
1644                 if (ppi2w[i].pLocation)
1645                 {
1646                     // Convert Unicode pLocation to a ANSI string pszLocation.
1647                     cch = wcslen(ppi2w[i].pLocation);
1648 
1649                     pszLocation = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1650                     if (!pszLocation)
1651                     {
1652                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1653                         ERR("HeapAlloc failed!\n");
1654                         goto Cleanup;
1655                     }
1656 
1657                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pLocation, -1, pszLocation, cch + 1, NULL, NULL);
1658                     StringCchCopyA(ppi2a[i].pLocation, cch + 1, pszLocation);
1659 
1660                     HeapFree(hProcessHeap, 0, pszLocation);
1661                 }
1662 
1663 
1664                 if (ppi2w[i].pSepFile)
1665                 {
1666                     // Convert Unicode pSepFile to a ANSI string pszSepFile.
1667                     cch = wcslen(ppi2w[i].pSepFile);
1668 
1669                     pszSepFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1670                     if (!pszSepFile)
1671                     {
1672                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1673                         ERR("HeapAlloc failed!\n");
1674                         goto Cleanup;
1675                     }
1676 
1677                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pSepFile, -1, pszSepFile, cch + 1, NULL, NULL);
1678                     StringCchCopyA(ppi2a[i].pSepFile, cch + 1, pszSepFile);
1679 
1680                     HeapFree(hProcessHeap, 0, pszSepFile);
1681                 }
1682 
1683                 if (ppi2w[i].pPrintProcessor)
1684                 {
1685                     // Convert Unicode pPrintProcessor to a ANSI string pszPrintProcessor.
1686                     cch = wcslen(ppi2w[i].pPrintProcessor);
1687 
1688                     pszPrintProcessor = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1689                     if (!pszPrintProcessor)
1690                     {
1691                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1692                         ERR("HeapAlloc failed!\n");
1693                         goto Cleanup;
1694                     }
1695 
1696                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPrintProcessor, -1, pszPrintProcessor, cch + 1, NULL, NULL);
1697                     StringCchCopyA(ppi2a[i].pPrintProcessor, cch + 1, pszPrintProcessor);
1698 
1699                     HeapFree(hProcessHeap, 0, pszPrintProcessor);
1700                 }
1701 
1702 
1703                 if (ppi2w[i].pDatatype)
1704                 {
1705                     // Convert Unicode pDatatype to a ANSI string pszDatatype.
1706                     cch = wcslen(ppi2w[i].pDatatype);
1707 
1708                     pszDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1709                     if (!pszDatatype)
1710                     {
1711                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1712                         ERR("HeapAlloc failed!\n");
1713                         goto Cleanup;
1714                     }
1715 
1716                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pDatatype, -1, pszDatatype, cch + 1, NULL, NULL);
1717                     StringCchCopyA(ppi2a[i].pDatatype, cch + 1, pszDatatype);
1718 
1719                     HeapFree(hProcessHeap, 0, pszDatatype);
1720                 }
1721 
1722                 if (ppi2w[i].pParameters)
1723                 {
1724                     // Convert Unicode pParameters to a ANSI string pszParameters.
1725                     cch = wcslen(ppi2w[i].pParameters);
1726 
1727                     pszParameters = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1728                     if (!pszParameters)
1729                     {
1730                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1731                         ERR("HeapAlloc failed!\n");
1732                         goto Cleanup;
1733                     }
1734 
1735                     WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pParameters, -1, pszParameters, cch + 1, NULL, NULL);
1736                     StringCchCopyA(ppi2a[i].pParameters, cch + 1, pszParameters);
1737 
1738                     HeapFree(hProcessHeap, 0, pszParameters);
1739                 }
1740                 if ( ppi2w[i].pDevMode )
1741                 {
1742                     RosConvertUnicodeDevModeToAnsiDevmode( ppi2w[i].pDevMode, ppi2a[i].pDevMode );
1743                 }
1744                 break;
1745             }
1746 
1747             case 4:
1748             {
1749                 if (ppi4w[i].pPrinterName)
1750                 {
1751                     // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
1752                     cch = wcslen(ppi4w[i].pPrinterName);
1753 
1754                     pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1755                     if (!pszPrinterName)
1756                     {
1757                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1758                         ERR("HeapAlloc failed!\n");
1759                         goto Cleanup;
1760                     }
1761 
1762                     WideCharToMultiByte(CP_ACP, 0, ppi4w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
1763                     StringCchCopyA(ppi4a[i].pPrinterName, cch + 1, pszPrinterName);
1764 
1765                     HeapFree(hProcessHeap, 0, pszPrinterName);
1766                 }
1767 
1768                 if (ppi4w[i].pServerName)
1769                 {
1770                     // Convert Unicode pServerName to a ANSI string pszServerName.
1771                     cch = wcslen(ppi4w[i].pServerName);
1772 
1773                     pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1774                     if (!pszServerName)
1775                     {
1776                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1777                         ERR("HeapAlloc failed!\n");
1778                         goto Cleanup;
1779                     }
1780 
1781                     WideCharToMultiByte(CP_ACP, 0, ppi4w[i].pServerName, -1, pszServerName, cch + 1, NULL, NULL);
1782                     StringCchCopyA(ppi4a[i].pServerName, cch + 1, pszServerName);
1783 
1784                     HeapFree(hProcessHeap, 0, pszServerName);
1785                 }
1786                 break;
1787             }
1788 
1789             case 5:
1790             {
1791                 if (ppi5w[i].pPrinterName)
1792                 {
1793                     // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
1794                     cch = wcslen(ppi5w[i].pPrinterName);
1795 
1796                     pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1797                     if (!pszPrinterName)
1798                     {
1799                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1800                         ERR("HeapAlloc failed!\n");
1801                         goto Cleanup;
1802                     }
1803 
1804                     WideCharToMultiByte(CP_ACP, 0, ppi5w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
1805                     StringCchCopyA(ppi5a[i].pPrinterName, cch + 1, pszPrinterName);
1806 
1807                     HeapFree(hProcessHeap, 0, pszPrinterName);
1808                 }
1809 
1810                 if (ppi5w[i].pPortName)
1811                 {
1812                     // Convert Unicode pPortName to a ANSI string pszPortName.
1813                     cch = wcslen(ppi5w[i].pPortName);
1814 
1815                     pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
1816                     if (!pszPortName)
1817                     {
1818                         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1819                         ERR("HeapAlloc failed!\n");
1820                         goto Cleanup;
1821                     }
1822 
1823                     WideCharToMultiByte(CP_ACP, 0, ppi5w[i].pPortName, -1, pszPortName, cch + 1, NULL, NULL);
1824                     StringCchCopyA(ppi5a[i].pPortName, cch + 1, pszPortName);
1825 
1826                     HeapFree(hProcessHeap, 0, pszPortName);
1827                 }
1828                 break;
1829             }
1830 
1831         }   // switch
1832     }       // for
1833 
1834     dwErrorCode = ERROR_SUCCESS;
1835 
1836 Cleanup:
1837     if (pwszName)
1838     {
1839         HeapFree(hProcessHeap, 0, pwszName);
1840     }
1841 
1842     SetLastError(dwErrorCode);
1843     return (dwErrorCode == ERROR_SUCCESS);
1844 }
1845 
1846 BOOL WINAPI
1847 EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
1848 {
1849     DWORD dwErrorCode;
1850 
1851     TRACE("EnumPrintersW(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
1852 
1853     // Dismiss invalid levels already at this point.
1854     if (Level == 3 || Level > 5)
1855     {
1856         dwErrorCode = ERROR_INVALID_LEVEL;
1857         goto Cleanup;
1858     }
1859 
1860     if (cbBuf && pPrinterEnum)
1861         ZeroMemory(pPrinterEnum, cbBuf);
1862 
1863     // Do the RPC call
1864     RpcTryExcept
1865     {
1866         dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
1867     }
1868     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1869     {
1870         dwErrorCode = RpcExceptionCode();
1871         ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
1872     }
1873     RpcEndExcept;
1874 
1875     if (dwErrorCode == ERROR_SUCCESS)
1876     {
1877         // Replace relative offset addresses in the output by absolute pointers.
1878         ASSERT(Level <= 9);
1879         MarshallUpStructuresArray(cbBuf, pPrinterEnum, *pcReturned, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
1880     }
1881 
1882 Cleanup:
1883     SetLastError(dwErrorCode);
1884     return (dwErrorCode == ERROR_SUCCESS);
1885 }
1886 
1887 BOOL WINAPI
1888 FlushPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten, DWORD cSleep)
1889 {
1890     TRACE("FlushPrinter(%p, %p, %lu, %p, %lu)\n", hPrinter, pBuf, cbBuf, pcWritten, cSleep);
1891     UNIMPLEMENTED;
1892     return FALSE;
1893 }
1894 
1895 BOOL WINAPI
1896 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
1897 {
1898     DWORD dwErrorCode;
1899     PWSTR pwszBuffer = NULL;
1900 
1901     TRACE("GetDefaultPrinterA(%p, %p)\n", pszBuffer, pcchBuffer);
1902 
1903     // Sanity check.
1904     if (!pcchBuffer)
1905     {
1906         dwErrorCode = ERROR_INVALID_PARAMETER;
1907         goto Cleanup;
1908     }
1909 
1910     // Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
1911     if (pszBuffer && *pcchBuffer)
1912     {
1913         pwszBuffer = HeapAlloc(hProcessHeap, 0, *pcchBuffer * sizeof(WCHAR));
1914         if (!pwszBuffer)
1915         {
1916             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1917             ERR("HeapAlloc failed!\n");
1918             goto Cleanup;
1919         }
1920     }
1921 
1922     if (!GetDefaultPrinterW(pwszBuffer, pcchBuffer))
1923     {
1924         dwErrorCode = GetLastError();
1925         goto Cleanup;
1926     }
1927 
1928     // We successfully got a string in pwszBuffer, so convert the Unicode string to ANSI.
1929     WideCharToMultiByte(CP_ACP, 0, pwszBuffer, -1, pszBuffer, *pcchBuffer, NULL, NULL);
1930 
1931     dwErrorCode = ERROR_SUCCESS;
1932 
1933 Cleanup:
1934     if (pwszBuffer)
1935         HeapFree(hProcessHeap, 0, pwszBuffer);
1936 
1937     SetLastError(dwErrorCode);
1938     return (dwErrorCode == ERROR_SUCCESS);
1939 }
1940 
1941 BOOL WINAPI
1942 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
1943 {
1944     DWORD cbNeeded;
1945     DWORD cchInputBuffer;
1946     DWORD dwErrorCode;
1947     HKEY hWindowsKey = NULL;
1948     PWSTR pwszDevice = NULL;
1949     PWSTR pwszComma;
1950 
1951     TRACE("GetDefaultPrinterW(%p, %p)\n", pszBuffer, pcchBuffer);
1952 
1953     // Sanity check.
1954     if (!pcchBuffer)
1955     {
1956         dwErrorCode = ERROR_INVALID_PARAMETER;
1957         goto Cleanup;
1958     }
1959 
1960     cchInputBuffer = *pcchBuffer;
1961 
1962     // Open the registry key where the default printer for the current user is stored.
1963     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_READ, &hWindowsKey);
1964     if (dwErrorCode != ERROR_SUCCESS)
1965     {
1966         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
1967         goto Cleanup;
1968     }
1969 
1970     // Determine the size of the required buffer.
1971     dwErrorCode = (DWORD)RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, NULL, &cbNeeded);
1972     if (dwErrorCode != ERROR_SUCCESS)
1973     {
1974         ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
1975         goto Cleanup;
1976     }
1977 
1978     // Allocate it.
1979     pwszDevice = HeapAlloc(hProcessHeap, 0, cbNeeded);
1980     if (!pwszDevice)
1981     {
1982         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1983         ERR("HeapAlloc failed!\n");
1984         goto Cleanup;
1985     }
1986 
1987     // Now get the actual value.
1988     dwErrorCode = RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, (PBYTE)pwszDevice, &cbNeeded);
1989     if (dwErrorCode != ERROR_SUCCESS)
1990     {
1991         ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
1992         goto Cleanup;
1993     }
1994 
1995     // We get a string "<Printer Name>,winspool,<Port>:".
1996     // Extract the printer name from it.
1997     pwszComma = wcschr(pwszDevice, L',');
1998     if (!pwszComma)
1999     {
2000         ERR("Found no or invalid default printer: %S!\n", pwszDevice);
2001         dwErrorCode = ERROR_INVALID_NAME;
2002         goto Cleanup;
2003     }
2004 
2005     // Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
2006     *pcchBuffer = pwszComma - pwszDevice + 1;
2007 
2008     // Check if the supplied buffer is large enough.
2009     if ( !pszBuffer || cchInputBuffer < *pcchBuffer)
2010     {
2011         dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
2012         goto Cleanup;
2013     }
2014 
2015     // Copy the default printer.
2016     *pwszComma = 0;
2017     CopyMemory(pszBuffer, pwszDevice, *pcchBuffer * sizeof(WCHAR));
2018 
2019     dwErrorCode = ERROR_SUCCESS;
2020 
2021 Cleanup:
2022     if (hWindowsKey)
2023         RegCloseKey(hWindowsKey);
2024 
2025     if (pwszDevice)
2026         HeapFree(hProcessHeap, 0, pwszDevice);
2027 
2028     SetLastError(dwErrorCode);
2029     return (dwErrorCode == ERROR_SUCCESS);
2030 }
2031 
2032 BOOL WINAPI
2033 GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
2034 {
2035     DWORD dwErrorCode;
2036     PPRINTER_INFO_1A ppi1a = (PPRINTER_INFO_1A)pPrinter;
2037     PPRINTER_INFO_1W ppi1w = (PPRINTER_INFO_1W)pPrinter;
2038     PPRINTER_INFO_2A ppi2a = (PPRINTER_INFO_2A)pPrinter;
2039     PPRINTER_INFO_2W ppi2w = (PPRINTER_INFO_2W)pPrinter;
2040     PPRINTER_INFO_4A ppi4a = (PPRINTER_INFO_4A)pPrinter;
2041     PPRINTER_INFO_4W ppi4w = (PPRINTER_INFO_4W)pPrinter;
2042     PPRINTER_INFO_5A ppi5a = (PPRINTER_INFO_5A)pPrinter;
2043     PPRINTER_INFO_5W ppi5w = (PPRINTER_INFO_5W)pPrinter;
2044     PPRINTER_INFO_7A ppi7a = (PPRINTER_INFO_7A)pPrinter;
2045     PPRINTER_INFO_7W ppi7w = (PPRINTER_INFO_7W)pPrinter;
2046     PPRINTER_INFO_9A ppi9a = (PPRINTER_INFO_9A)pPrinter;
2047     PPRINTER_INFO_9W ppi9w = (PPRINTER_INFO_9W)pPrinter;
2048     DWORD cch;
2049 
2050     TRACE("GetPrinterA(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
2051 
2052     // Check for invalid levels here for early error return. Should be 1-9.
2053     if (Level <  1 || Level > 9)
2054     {
2055         dwErrorCode = ERROR_INVALID_LEVEL;
2056         ERR("Invalid Level!\n");
2057         goto Cleanup;
2058     }
2059 
2060     if (!GetPrinterW(hPrinter, Level, pPrinter, cbBuf, pcbNeeded))
2061     {
2062         dwErrorCode = GetLastError();
2063         goto Cleanup;
2064     }
2065 
2066     switch (Level)
2067     {
2068         case 1:
2069         {
2070             if (ppi1w->pDescription)
2071             {
2072                 PSTR pszDescription;
2073 
2074                 // Convert Unicode pDescription to a ANSI string pszDescription.
2075                 cch = wcslen(ppi1w->pDescription);
2076 
2077                 pszDescription = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2078                 if (!pszDescription)
2079                 {
2080                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2081                     ERR("HeapAlloc failed!\n");
2082                     goto Cleanup;
2083                 }
2084 
2085                 WideCharToMultiByte(CP_ACP, 0, ppi1w->pDescription, -1, pszDescription, cch + 1, NULL, NULL);
2086                 StringCchCopyA(ppi1a->pDescription, cch + 1, pszDescription);
2087 
2088                 HeapFree(hProcessHeap, 0, pszDescription);
2089             }
2090 
2091             if (ppi1w->pName)
2092             {
2093                 PSTR pszName;
2094 
2095                 // Convert Unicode pName to a ANSI string pszName.
2096                 cch = wcslen(ppi1w->pName);
2097 
2098                 pszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2099                 if (!pszName)
2100                 {
2101                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2102                     ERR("HeapAlloc failed!\n");
2103                     goto Cleanup;
2104                 }
2105 
2106                 WideCharToMultiByte(CP_ACP, 0, ppi1w->pName, -1, pszName, cch + 1, NULL, NULL);
2107                 StringCchCopyA(ppi1a->pName, cch + 1, pszName);
2108 
2109                 HeapFree(hProcessHeap, 0, pszName);
2110             }
2111 
2112             if (ppi1w->pComment)
2113             {
2114                 PSTR pszComment;
2115 
2116                 // Convert Unicode pComment to a ANSI string pszComment.
2117                 cch = wcslen(ppi1w->pComment);
2118 
2119                 pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2120                 if (!pszComment)
2121                 {
2122                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2123                     ERR("HeapAlloc failed!\n");
2124                     goto Cleanup;
2125                 }
2126 
2127                 WideCharToMultiByte(CP_ACP, 0, ppi1w->pComment, -1, pszComment, cch + 1, NULL, NULL);
2128                 StringCchCopyA(ppi1a->pComment, cch + 1, pszComment);
2129 
2130                 HeapFree(hProcessHeap, 0, pszComment);
2131             }
2132             break;
2133         }
2134 
2135         case 2:
2136         {
2137             if (ppi2w->pServerName)
2138             {
2139                 PSTR pszServerName;
2140 
2141                 // Convert Unicode pServerName to a ANSI string pszServerName.
2142                 cch = wcslen(ppi2w->pServerName);
2143 
2144                 pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2145                 if (!pszServerName)
2146                 {
2147                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2148                     ERR("HeapAlloc failed!\n");
2149                     goto Cleanup;
2150                 }
2151 
2152                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pServerName, -1, pszServerName, cch + 1, NULL, NULL);
2153                 StringCchCopyA(ppi2a->pServerName, cch + 1, pszServerName);
2154 
2155                 HeapFree(hProcessHeap, 0, pszServerName);
2156             }
2157 
2158             if (ppi2w->pPrinterName)
2159             {
2160                 PSTR pszPrinterName;
2161 
2162                 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
2163                 cch = wcslen(ppi2w->pPrinterName);
2164 
2165                 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2166                 if (!pszPrinterName)
2167                 {
2168                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2169                     ERR("HeapAlloc failed!\n");
2170                     goto Cleanup;
2171                 }
2172 
2173                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
2174                 StringCchCopyA(ppi2a->pPrinterName, cch + 1, pszPrinterName);
2175 
2176                 HeapFree(hProcessHeap, 0, pszPrinterName);
2177             }
2178 
2179             if (ppi2w->pShareName)
2180             {
2181                 PSTR pszShareName;
2182 
2183                 // Convert Unicode pShareName to a ANSI string pszShareName.
2184                 cch = wcslen(ppi2w->pShareName);
2185 
2186                 pszShareName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2187                 if (!pszShareName)
2188                 {
2189                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2190                     ERR("HeapAlloc failed!\n");
2191                     goto Cleanup;
2192                 }
2193 
2194                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pShareName, -1, pszShareName, cch + 1, NULL, NULL);
2195                 StringCchCopyA(ppi2a->pShareName, cch + 1, pszShareName);
2196 
2197                 HeapFree(hProcessHeap, 0, pszShareName);
2198             }
2199 
2200             if (ppi2w->pPortName)
2201             {
2202                 PSTR pszPortName;
2203 
2204                 // Convert Unicode pPortName to a ANSI string pszPortName.
2205                 cch = wcslen(ppi2w->pPortName);
2206 
2207                 pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2208                 if (!pszPortName)
2209                 {
2210                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2211                     ERR("HeapAlloc failed!\n");
2212                     goto Cleanup;
2213                 }
2214 
2215                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pPortName, -1, pszPortName, cch + 1, NULL, NULL);
2216                 StringCchCopyA(ppi2a->pPortName, cch + 1, pszPortName);
2217 
2218                 HeapFree(hProcessHeap, 0, pszPortName);
2219             }
2220 
2221             if (ppi2w->pDriverName)
2222             {
2223                 PSTR pszDriverName;
2224 
2225                 // Convert Unicode pDriverName to a ANSI string pszDriverName.
2226                 cch = wcslen(ppi2w->pDriverName);
2227 
2228                 pszDriverName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2229                 if (!pszDriverName)
2230                 {
2231                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2232                     ERR("HeapAlloc failed!\n");
2233                     goto Cleanup;
2234                 }
2235 
2236                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pDriverName, -1, pszDriverName, cch + 1, NULL, NULL);
2237                 StringCchCopyA(ppi2a->pDriverName, cch + 1, pszDriverName);
2238 
2239                 HeapFree(hProcessHeap, 0, pszDriverName);
2240             }
2241 
2242             if (ppi2w->pComment)
2243             {
2244                 PSTR pszComment;
2245 
2246                 // Convert Unicode pComment to a ANSI string pszComment.
2247                 cch = wcslen(ppi2w->pComment);
2248 
2249                 pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2250                 if (!pszComment)
2251                 {
2252                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2253                     ERR("HeapAlloc failed!\n");
2254                     goto Cleanup;
2255                 }
2256 
2257                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pComment, -1, pszComment, cch + 1, NULL, NULL);
2258                 StringCchCopyA(ppi2a->pComment, cch + 1, pszComment);
2259 
2260                 HeapFree(hProcessHeap, 0, pszComment);
2261             }
2262 
2263             if (ppi2w->pLocation)
2264             {
2265                 PSTR pszLocation;
2266 
2267                 // Convert Unicode pLocation to a ANSI string pszLocation.
2268                 cch = wcslen(ppi2w->pLocation);
2269 
2270                 pszLocation = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2271                 if (!pszLocation)
2272                 {
2273                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2274                     ERR("HeapAlloc failed!\n");
2275                     goto Cleanup;
2276                 }
2277 
2278                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pLocation, -1, pszLocation, cch + 1, NULL, NULL);
2279                 StringCchCopyA(ppi2a->pLocation, cch + 1, pszLocation);
2280 
2281                 HeapFree(hProcessHeap, 0, pszLocation);
2282             }
2283 
2284             if (ppi2w->pSepFile)
2285             {
2286                 PSTR pszSepFile;
2287 
2288                 // Convert Unicode pSepFile to a ANSI string pszSepFile.
2289                 cch = wcslen(ppi2w->pSepFile);
2290 
2291                 pszSepFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2292                 if (!pszSepFile)
2293                 {
2294                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2295                     ERR("HeapAlloc failed!\n");
2296                     goto Cleanup;
2297                 }
2298 
2299                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pSepFile, -1, pszSepFile, cch + 1, NULL, NULL);
2300                 StringCchCopyA(ppi2a->pSepFile, cch + 1, pszSepFile);
2301 
2302                 HeapFree(hProcessHeap, 0, pszSepFile);
2303             }
2304 
2305             if (ppi2w->pPrintProcessor)
2306             {
2307                 PSTR pszPrintProcessor;
2308 
2309                 // Convert Unicode pPrintProcessor to a ANSI string pszPrintProcessor.
2310                 cch = wcslen(ppi2w->pPrintProcessor);
2311 
2312                 pszPrintProcessor = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2313                 if (!pszPrintProcessor)
2314                 {
2315                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2316                     ERR("HeapAlloc failed!\n");
2317                     goto Cleanup;
2318                 }
2319 
2320                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pPrintProcessor, -1, pszPrintProcessor, cch + 1, NULL, NULL);
2321                 StringCchCopyA(ppi2a->pPrintProcessor, cch + 1, pszPrintProcessor);
2322 
2323                 HeapFree(hProcessHeap, 0, pszPrintProcessor);
2324             }
2325 
2326             if (ppi2w->pDatatype)
2327             {
2328                 PSTR pszDatatype;
2329 
2330                 // Convert Unicode pDatatype to a ANSI string pszDatatype.
2331                 cch = wcslen(ppi2w->pDatatype);
2332 
2333                 pszDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2334                 if (!pszDatatype)
2335                 {
2336                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2337                     ERR("HeapAlloc failed!\n");
2338                     goto Cleanup;
2339                 }
2340 
2341                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pDatatype, -1, pszDatatype, cch + 1, NULL, NULL);
2342                 StringCchCopyA(ppi2a->pDatatype, cch + 1, pszDatatype);
2343 
2344                 HeapFree(hProcessHeap, 0, pszDatatype);
2345             }
2346 
2347             if (ppi2w->pParameters)
2348             {
2349                 PSTR pszParameters;
2350 
2351                 // Convert Unicode pParameters to a ANSI string pszParameters.
2352                 cch = wcslen(ppi2w->pParameters);
2353 
2354                 pszParameters = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2355                 if (!pszParameters)
2356                 {
2357                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2358                     ERR("HeapAlloc failed!\n");
2359                     goto Cleanup;
2360                 }
2361 
2362                 WideCharToMultiByte(CP_ACP, 0, ppi2w->pParameters, -1, pszParameters, cch + 1, NULL, NULL);
2363                 StringCchCopyA(ppi2a->pParameters, cch + 1, pszParameters);
2364 
2365                 HeapFree(hProcessHeap, 0, pszParameters);
2366             }
2367             if ( ppi2w->pDevMode )
2368             {
2369                 RosConvertUnicodeDevModeToAnsiDevmode( ppi2w->pDevMode, ppi2a->pDevMode );
2370             }
2371             break;
2372         }
2373 
2374         case 4:
2375         {
2376             if (ppi4w->pPrinterName)
2377             {
2378                 PSTR pszPrinterName;
2379 
2380                 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
2381                 cch = wcslen(ppi4w->pPrinterName);
2382 
2383                 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2384                 if (!pszPrinterName)
2385                 {
2386                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2387                     ERR("HeapAlloc failed!\n");
2388                     goto Cleanup;
2389                 }
2390 
2391                 WideCharToMultiByte(CP_ACP, 0, ppi4w->pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
2392                 StringCchCopyA(ppi4a->pPrinterName, cch + 1, pszPrinterName);
2393 
2394                 HeapFree(hProcessHeap, 0, pszPrinterName);
2395             }
2396 
2397             if (ppi4w->pServerName)
2398             {
2399                 PSTR pszServerName;
2400 
2401                 // Convert Unicode pServerName to a ANSI string pszServerName.
2402                 cch = wcslen(ppi4w->pServerName);
2403 
2404                 pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2405                 if (!pszServerName)
2406                 {
2407                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2408                     ERR("HeapAlloc failed!\n");
2409                     goto Cleanup;
2410                 }
2411 
2412                 WideCharToMultiByte(CP_ACP, 0, ppi4w->pServerName, -1, pszServerName, cch + 1, NULL, NULL);
2413                 StringCchCopyA(ppi4a->pServerName, cch + 1, pszServerName);
2414 
2415                 HeapFree(hProcessHeap, 0, pszServerName);
2416             }
2417             break;
2418         }
2419 
2420         case 5:
2421         {
2422             if (ppi5w->pPrinterName)
2423             {
2424                 PSTR pszPrinterName;
2425 
2426                 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
2427                 cch = wcslen(ppi5w->pPrinterName);
2428 
2429                 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2430                 if (!pszPrinterName)
2431                 {
2432                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2433                     ERR("HeapAlloc failed!\n");
2434                     goto Cleanup;
2435                 }
2436 
2437                 WideCharToMultiByte(CP_ACP, 0, ppi5w->pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
2438                 StringCchCopyA(ppi5a->pPrinterName, cch + 1, pszPrinterName);
2439 
2440                 HeapFree(hProcessHeap, 0, pszPrinterName);
2441             }
2442 
2443             if (ppi5w->pPortName)
2444             {
2445                 PSTR pszPortName;
2446 
2447                 // Convert Unicode pPortName to a ANSI string pszPortName.
2448                 cch = wcslen(ppi5w->pPortName);
2449 
2450                 pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2451                 if (!pszPortName)
2452                 {
2453                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2454                     ERR("HeapAlloc failed!\n");
2455                     goto Cleanup;
2456                 }
2457 
2458                 WideCharToMultiByte(CP_ACP, 0, ppi5w->pPortName, -1, pszPortName, cch + 1, NULL, NULL);
2459                 StringCchCopyA(ppi5a->pPortName, cch + 1, pszPortName);
2460 
2461                 HeapFree(hProcessHeap, 0, pszPortName);
2462             }
2463             break;
2464         }
2465 
2466         case 7:
2467         {
2468             if (ppi7w->pszObjectGUID)
2469             {
2470                 PSTR pszaObjectGUID;
2471 
2472                 // Convert Unicode pszObjectGUID to a ANSI string pszaObjectGUID.
2473                 cch = wcslen(ppi7w->pszObjectGUID);
2474 
2475                 pszaObjectGUID = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
2476                 if (!pszaObjectGUID)
2477                 {
2478                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2479                     ERR("HeapAlloc failed!\n");
2480                     goto Cleanup;
2481                 }
2482 
2483                 WideCharToMultiByte(CP_ACP, 0, ppi7w->pszObjectGUID, -1, pszaObjectGUID, cch + 1, NULL, NULL);
2484                 StringCchCopyA(ppi7a->pszObjectGUID, cch + 1, pszaObjectGUID);
2485 
2486                 HeapFree(hProcessHeap, 0, pszaObjectGUID);
2487             }
2488         }
2489             break;
2490         case 8:
2491         case 9:
2492             RosConvertUnicodeDevModeToAnsiDevmode(ppi9w->pDevMode, ppi9a->pDevMode);
2493             break;
2494     }       // switch
2495 
2496     dwErrorCode = ERROR_SUCCESS;
2497 
2498 Cleanup:
2499     SetLastError(dwErrorCode);
2500     return (dwErrorCode == ERROR_SUCCESS);
2501 }
2502 
2503 BOOL WINAPI
2504 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
2505 {
2506     DWORD dwErrorCode;
2507     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2508 
2509     TRACE("GetPrinterW(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
2510 
2511     // Sanity checks.
2512     if (!pHandle)
2513     {
2514         dwErrorCode = ERROR_INVALID_HANDLE;
2515         goto Cleanup;
2516     }
2517 
2518     // Dismiss invalid levels already at this point.
2519     if (Level > 9)
2520     {
2521         dwErrorCode = ERROR_INVALID_LEVEL;
2522         goto Cleanup;
2523     }
2524 
2525     if (cbBuf && pPrinter)
2526         ZeroMemory(pPrinter, cbBuf);
2527 
2528     // Do the RPC call
2529     RpcTryExcept
2530     {
2531         dwErrorCode = _RpcGetPrinter(pHandle->hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
2532     }
2533     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2534     {
2535         dwErrorCode = RpcExceptionCode();
2536         ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
2537     }
2538     RpcEndExcept;
2539 
2540     if (dwErrorCode == ERROR_SUCCESS)
2541     {
2542         // Replace relative offset addresses in the output by absolute pointers.
2543         ASSERT(Level <= 9);
2544         MarshallUpStructure(cbBuf, pPrinter, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
2545     }
2546 
2547 Cleanup:
2548     SetLastError(dwErrorCode);
2549     return (dwErrorCode == ERROR_SUCCESS);
2550 }
2551 
2552 BOOL WINAPI
2553 OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
2554 {
2555     BOOL bReturnValue = FALSE;
2556     DWORD cch;
2557     PWSTR pwszPrinterName = NULL;
2558     PRINTER_DEFAULTSW wDefault = { 0 };
2559 
2560     TRACE("OpenPrinterA(%s, %p, %p)\n", pPrinterName, phPrinter, pDefault);
2561 
2562     if (pPrinterName)
2563     {
2564         // Convert pPrinterName to a Unicode string pwszPrinterName
2565         cch = strlen(pPrinterName);
2566 
2567         pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2568         if (!pwszPrinterName)
2569         {
2570             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2571             ERR("HeapAlloc failed!\n");
2572             goto Cleanup;
2573         }
2574 
2575         MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
2576     }
2577 
2578     if (pDefault)
2579     {
2580         wDefault.DesiredAccess = pDefault->DesiredAccess;
2581 
2582         if (pDefault->pDatatype)
2583         {
2584             // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
2585             cch = strlen(pDefault->pDatatype);
2586 
2587             wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2588             if (!wDefault.pDatatype)
2589             {
2590                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2591                 ERR("HeapAlloc failed!\n");
2592                 goto Cleanup;
2593             }
2594 
2595             MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
2596         }
2597 
2598         if (pDefault->pDevMode)
2599             wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
2600     }
2601 
2602     bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
2603 
2604     if ( bReturnValue )
2605     {
2606         PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)*phPrinter;
2607         pHandle->bAnsi = TRUE;
2608     }
2609 
2610 Cleanup:
2611     if (wDefault.pDatatype)
2612         HeapFree(hProcessHeap, 0, wDefault.pDatatype);
2613 
2614     if (wDefault.pDevMode)
2615         HeapFree(hProcessHeap, 0, wDefault.pDevMode);
2616 
2617     if (pwszPrinterName)
2618         HeapFree(hProcessHeap, 0, pwszPrinterName);
2619 
2620     return bReturnValue;
2621 }
2622 
2623 BOOL WINAPI
2624 OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
2625 {
2626     DWORD dwErrorCode;
2627     HANDLE hPrinter;
2628     PSPOOLER_HANDLE pHandle;
2629     PWSTR pDatatype = NULL;
2630     WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
2631     ACCESS_MASK AccessRequired = 0;
2632 
2633     TRACE("OpenPrinterW(%S, %p, %p)\n", pPrinterName, phPrinter, pDefault);
2634 
2635     // Sanity check
2636     if (!phPrinter)
2637     {
2638         dwErrorCode = ERROR_INVALID_PARAMETER;
2639         goto Cleanup;
2640     }
2641 
2642     // Prepare the additional parameters in the format required by _RpcOpenPrinter
2643     if (pDefault)
2644     {
2645         pDatatype = pDefault->pDatatype;
2646         DevModeContainer.cbBuf = sizeof(DEVMODEW);
2647         DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
2648         AccessRequired = pDefault->DesiredAccess;
2649     }
2650 
2651     // Do the RPC call
2652     RpcTryExcept
2653     {
2654         dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
2655     }
2656     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2657     {
2658         dwErrorCode = RpcExceptionCode();
2659         ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
2660     }
2661     RpcEndExcept;
2662 
2663     if (dwErrorCode == ERROR_SUCCESS)
2664     {
2665         // Create a new SPOOLER_HANDLE structure.
2666         pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
2667         if (!pHandle)
2668         {
2669             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
2670             ERR("HeapAlloc failed!\n");
2671             goto Cleanup;
2672         }
2673 
2674         pHandle->Sig = SPOOLER_HANDLE_SIG;
2675         pHandle->hPrinter = hPrinter;
2676         pHandle->hSPLFile = INVALID_HANDLE_VALUE;
2677         pHandle->hSpoolFileHandle = INVALID_HANDLE_VALUE;
2678 
2679         // Return it as phPrinter.
2680         *phPrinter = (HANDLE)pHandle;
2681     }
2682 
2683 Cleanup:
2684     SetLastError(dwErrorCode);
2685     return (dwErrorCode == ERROR_SUCCESS);
2686 }
2687 
2688 //
2689 // Dead API.
2690 //
2691 DWORD WINAPI
2692 PrinterMessageBoxA(HANDLE hPrinter, DWORD Error, HWND hWnd, LPSTR pText, LPSTR pCaption, DWORD dwType)
2693 {
2694     return 50;
2695 }
2696 
2697 DWORD WINAPI
2698 PrinterMessageBoxW(HANDLE hPrinter, DWORD Error, HWND hWnd, LPWSTR pText, LPWSTR pCaption, DWORD dwType)
2699 {
2700     return 50;
2701 }
2702 
2703 BOOL WINAPI
2704 QueryColorProfile(
2705   HANDLE    hPrinter,
2706   PDEVMODEW pdevmode,
2707   ULONG     ulQueryMode,
2708   VOID      *pvProfileData,
2709   ULONG     *pcbProfileData,
2710   FLONG     *pflProfileData )
2711 {
2712     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2713     BOOL Ret = FALSE;
2714     HMODULE hLibrary;
2715 
2716     FIXME("QueryColorProfile(%p, %p, %l, %p, %p, %p)\n", hPrinter, pdevmode, ulQueryMode, pvProfileData, pcbProfileData, pflProfileData);
2717 
2718     if ( pHandle->bNoColorProfile )
2719     {
2720         Ret = (BOOL)SP_ERROR;
2721     }
2722     else
2723     {
2724 
2725         if ( pdevmode )
2726         {
2727             if (!IsValidDevmodeNoSizeW( pdevmode ) )
2728             {
2729                 ERR("DeviceCapabilitiesW : Devode Invalid");
2730                 return FALSE;
2731             }
2732         }
2733 
2734         hLibrary = LoadPrinterDriver( hPrinter );
2735 
2736         if ( hLibrary )
2737         {
2738             fpQueryColorProfile = (PVOID)GetProcAddress( hLibrary, "DrvQueryColorProfile" );
2739 
2740             if ( fpQueryColorProfile )
2741             {
2742                 Ret = fpQueryColorProfile( hPrinter, pdevmode, ulQueryMode, pvProfileData, pcbProfileData, pflProfileData );
2743             }
2744             else
2745             {
2746                 pHandle->bNoColorProfile = TRUE;
2747                 Ret = (BOOL)SP_ERROR;
2748             }
2749 
2750             FreeLibrary(hLibrary);
2751         }
2752     }
2753     return Ret;
2754 }
2755 
2756 // Note from GDI32:printdrv.c
2757 //
2758 //  QuerySpoolMode :
2759 //    BOOL return TRUE if successful.
2760 //    dlFont 0x0001 for Downloading fonts. 0x0002 unknown XPS_PASS?.
2761 //    dwVersion is version of EMFSPOOL. Must be 0x00010000. See [MS-EMFSPOOL] page 18.
2762 //
2763 
2764 #define QSM_DOWNLOADINGFONTS 0x0001
2765 
2766 /*
2767    Note from MSDN : "V4 print drivers using RAW mode to send PCL/Postscript have 0 byte spool file"
2768 
2769    Use XPS_PASS instead of RAW to pass information directly to the print filter pipeline in
2770    v4 and v3 XPSDrv drivers. Here's how to proceed with Windows 8:
2771 
2772     Call GetPrinterDriver to retrieve the DRIVER_INFO_8 structure.
2773     Check DRIVER_INFO_8::dwPrinterDriverAttributes for the PRINTER_DRIVER_XPS flag.
2774     Choose your datatype based on the presence or absence of the flag:
2775         If the flag is set, use XPS_PASS.
2776         If the flag isn't set, use RAW.
2777  */
2778 
2779 #define QSM_XPS_PASS         0x0002 // Guessing. PRINTER_DRIVER_XPS?
2780 
2781 BOOL WINAPI
2782 QuerySpoolMode( HANDLE hPrinter, PDWORD downloadFontsFlags, PDWORD dwVersion )
2783 {
2784     PRINTER_INFO_2W *pi2 = NULL;
2785     DWORD needed = 0;
2786     BOOL res;
2787 
2788     FIXME("QuerySpoolMode(%p, %p, %p)\n", hPrinter, downloadFontsFlags, dwVersion);
2789 
2790     res = GetPrinterW( hPrinter, 2, NULL, 0, &needed);
2791     if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
2792     {
2793         pi2 = HeapAlloc(hProcessHeap, 0, needed);
2794         res = GetPrinterW(hPrinter, 2, (LPBYTE)pi2, needed, &needed);
2795     }
2796 
2797     if ( res )
2798     {
2799         *dwVersion = 0x10000;
2800         *downloadFontsFlags = 0;
2801 
2802         if ( pi2->pServerName )
2803         {
2804             *downloadFontsFlags |= QSM_DOWNLOADINGFONTS;
2805         }
2806     }
2807 //
2808 //  Guessing,,,
2809 //  To do : Add GetPrinterDriver for DRIVER_INFO_8, test PRINTER_DRIVER_XPS flag,
2810 //          to set *downloadFontsFlags |= QSM_XPS_PASS;
2811 //
2812 //  Vista+ looks for QSM_XPS_PASS to be set in GDI32.
2813 //
2814     HeapFree(hProcessHeap, 0, pi2);
2815     return res;
2816 }
2817 
2818 //
2819 // This requires IC support.
2820 //
2821 DWORD WINAPI
2822 QueryRemoteFonts( HANDLE hPrinter, PUNIVERSAL_FONT_ID pufi, ULONG NumberOfUFIs )
2823 {
2824     HANDLE hIC;
2825     DWORD Result = -1, cOut, cIn = 0;
2826     PBYTE pOut;
2827 
2828     FIXME("QueryRemoteFonts(%p, %p, %lu)\n", hPrinter, pufi, NumberOfUFIs);
2829 
2830     hIC = CreatePrinterIC( hPrinter, NULL );
2831     if ( hIC )
2832     {
2833         cOut = (NumberOfUFIs * sizeof(UNIVERSAL_FONT_ID)) + sizeof(DWORD); // Include "DWORD" first part to return size.
2834 
2835         pOut = HeapAlloc( hProcessHeap, 0, cOut );
2836         if ( pOut )
2837         {
2838             if ( PlayGdiScriptOnPrinterIC( hIC, (LPBYTE)&cIn, sizeof(DWORD), pOut, cOut, 0 ) )
2839             {
2840                 cIn = *((PDWORD)pOut); // Fisrt part is the size of the UFID object.
2841 
2842                 Result = cIn; // Return the required size.
2843 
2844                 if( NumberOfUFIs < cIn )
2845                 {
2846                     cIn = NumberOfUFIs;
2847                 }
2848                 //     Copy whole object back to GDI32, exclude first DWORD part.
2849                 memcpy( pufi, pOut + sizeof(DWORD), cIn * sizeof(UNIVERSAL_FONT_ID) );
2850             }
2851             HeapFree( hProcessHeap, 0, pOut );
2852         }
2853         DeletePrinterIC( hIC );
2854     }
2855     return Result;
2856 }
2857 
2858 BOOL WINAPI
2859 ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
2860 {
2861     DWORD dwErrorCode;
2862     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2863 
2864     TRACE("ReadPrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pNoBytesRead);
2865 
2866     // Sanity checks.
2867     if (!pHandle)
2868     {
2869         dwErrorCode = ERROR_INVALID_HANDLE;
2870         goto Cleanup;
2871     }
2872 
2873     // Do the RPC call
2874     RpcTryExcept
2875     {
2876         dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
2877     }
2878     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2879     {
2880         dwErrorCode = RpcExceptionCode();
2881         ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
2882     }
2883     RpcEndExcept;
2884 
2885 Cleanup:
2886     SetLastError(dwErrorCode);
2887     return (dwErrorCode == ERROR_SUCCESS);
2888 }
2889 
2890 BOOL WINAPI
2891 ResetPrinterA(HANDLE hPrinter, PPRINTER_DEFAULTSA pDefault)
2892 {
2893     BOOL ret;
2894     UNICODE_STRING pNameW;
2895     PDEVMODEW pdmw = NULL;
2896     PPRINTER_DEFAULTSW pdw = (PPRINTER_DEFAULTSW)pDefault;
2897 
2898     TRACE("ResetPrinterA(%p, %p)\n", hPrinter, pDefault);
2899 
2900     if ( pDefault->pDatatype == (LPSTR)-1 )
2901     {
2902         pdw->pDatatype = (LPWSTR)-1;
2903     }
2904     else
2905     {
2906         pdw->pDatatype = AsciiToUnicode( &pNameW, pDefault->pDatatype );
2907     }
2908     if ( pDefault->pDevMode == (LPDEVMODEA)-1)
2909     {
2910         pdw->pDevMode = (LPDEVMODEW)-1;
2911     }
2912     else
2913     {
2914         if ( pDefault->pDevMode )//&& IsValidDevmodeNoSizeW( pDefault->pDevMode ) )
2915         {
2916             RosConvertAnsiDevModeToUnicodeDevmode( pDefault->pDevMode, &pdmw );
2917             pdw->pDevMode = pdmw;
2918         }
2919     }
2920 
2921     ret = ResetPrinterW( hPrinter, pdw );
2922 
2923     if (pdmw) HeapFree(hProcessHeap, 0, pdmw);
2924 
2925     RtlFreeUnicodeString( &pNameW );
2926 
2927     return ret;
2928 }
2929 
2930 BOOL WINAPI
2931 ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
2932 {
2933     TRACE("ResetPrinterW(%p, %p)\n", hPrinter, pDefault);
2934     UNIMPLEMENTED;
2935     return FALSE;
2936 }
2937 
2938 BOOL WINAPI
2939 SeekPrinter( HANDLE hPrinter, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER pliNewPointer, DWORD dwMoveMethod, BOOL bWrite )
2940 {
2941     DWORD dwErrorCode;
2942     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
2943 
2944     FIXME("SeekPrinter(%p, %I64u, %p, %lu, %d)\n", hPrinter, liDistanceToMove.QuadPart, pliNewPointer, dwMoveMethod, bWrite);
2945 
2946     // Sanity checks.
2947     if (!pHandle)
2948     {
2949         dwErrorCode = ERROR_INVALID_HANDLE;
2950         goto Cleanup;
2951     }
2952 
2953     // Do the RPC call
2954     RpcTryExcept
2955     {
2956         dwErrorCode = _RpcSeekPrinter(pHandle->hPrinter, liDistanceToMove, pliNewPointer, dwMoveMethod, bWrite);
2957     }
2958     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
2959     {
2960         dwErrorCode = RpcExceptionCode();
2961         ERR("_RpcSeekPrinter failed with exception code %lu!\n", dwErrorCode);
2962     }
2963     RpcEndExcept;
2964 
2965 Cleanup:
2966     SetLastError(dwErrorCode);
2967     return (dwErrorCode == ERROR_SUCCESS);
2968 }
2969 
2970 BOOL WINAPI
2971 SetDefaultPrinterA(LPCSTR pszPrinter)
2972 {
2973     BOOL bReturnValue = FALSE;
2974     DWORD cch;
2975     PWSTR pwszPrinter = NULL;
2976 
2977     TRACE("SetDefaultPrinterA(%s)\n", pszPrinter);
2978 
2979     if (pszPrinter)
2980     {
2981         // Convert pszPrinter to a Unicode string pwszPrinter
2982         cch = strlen(pszPrinter);
2983 
2984         pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
2985         if (!pwszPrinter)
2986         {
2987             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2988             ERR("HeapAlloc failed!\n");
2989             goto Cleanup;
2990         }
2991 
2992         MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, pwszPrinter, cch + 1);
2993     }
2994 
2995     bReturnValue = SetDefaultPrinterW(pwszPrinter);
2996 
2997 Cleanup:
2998     if (pwszPrinter)
2999         HeapFree(hProcessHeap, 0, pwszPrinter);
3000 
3001     return bReturnValue;
3002 }
3003 
3004 BOOL WINAPI
3005 SetDefaultPrinterW(LPCWSTR pszPrinter)
3006 {
3007     const WCHAR wszDevicesKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
3008 
3009     DWORD cbDeviceValueData;
3010     DWORD cbPrinterValueData = 0;
3011     DWORD cchPrinter;
3012     DWORD dwErrorCode;
3013     HKEY hDevicesKey = NULL;
3014     HKEY hWindowsKey = NULL;
3015     PWSTR pwszDeviceValueData = NULL;
3016     WCHAR wszPrinter[MAX_PRINTER_NAME + 1];
3017 
3018     TRACE("SetDefaultPrinterW(%S)\n", pszPrinter);
3019 
3020     // Open the Devices registry key.
3021     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszDevicesKey, 0, KEY_READ, &hDevicesKey);
3022     if (dwErrorCode != ERROR_SUCCESS)
3023     {
3024         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
3025         goto Cleanup;
3026     }
3027 
3028     // Did the caller give us a printer to set as default?
3029     if (pszPrinter && *pszPrinter)
3030     {
3031         // Check if the given printer exists and query the value data size.
3032         dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, NULL, &cbPrinterValueData);
3033         if (dwErrorCode == ERROR_FILE_NOT_FOUND)
3034         {
3035             dwErrorCode = ERROR_INVALID_PRINTER_NAME;
3036             goto Cleanup;
3037         }
3038         else if (dwErrorCode != ERROR_SUCCESS)
3039         {
3040             ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
3041             goto Cleanup;
3042         }
3043 
3044         cchPrinter = wcslen(pszPrinter);
3045     }
3046     else
3047     {
3048         // If there is already a default printer, we're done!
3049         cchPrinter = _countof(wszPrinter);
3050         if (GetDefaultPrinterW(wszPrinter, &cchPrinter))
3051         {
3052             dwErrorCode = ERROR_SUCCESS;
3053             goto Cleanup;
3054         }
3055 
3056         // Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
3057         cchPrinter = _countof(wszPrinter);
3058         dwErrorCode = (DWORD)RegEnumValueW(hDevicesKey, 0, wszPrinter, &cchPrinter, NULL, NULL, NULL, &cbPrinterValueData);
3059         if (dwErrorCode != ERROR_MORE_DATA)
3060             goto Cleanup;
3061 
3062         pszPrinter = wszPrinter;
3063     }
3064 
3065     // We now need to query the value data, which has the format "winspool,<Port>:"
3066     // and make "<Printer Name>,winspool,<Port>:" out of it.
3067     // Allocate a buffer large enough for the final data.
3068     cbDeviceValueData = (cchPrinter + 1) * sizeof(WCHAR) + cbPrinterValueData;
3069     pwszDeviceValueData = HeapAlloc(hProcessHeap, 0, cbDeviceValueData);
3070     if (!pwszDeviceValueData)
3071     {
3072         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
3073         ERR("HeapAlloc failed!\n");
3074         goto Cleanup;
3075     }
3076 
3077     // Copy the Printer Name and a comma into it.
3078     CopyMemory(pwszDeviceValueData, pszPrinter, cchPrinter * sizeof(WCHAR));
3079     pwszDeviceValueData[cchPrinter] = L',';
3080 
3081     // Append the value data, which has the format "winspool,<Port>:"
3082     dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, (PBYTE)&pwszDeviceValueData[cchPrinter + 1], &cbPrinterValueData);
3083     if (dwErrorCode != ERROR_SUCCESS)
3084         goto Cleanup;
3085 
3086     // Open the Windows registry key.
3087     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_SET_VALUE, &hWindowsKey);
3088     if (dwErrorCode != ERROR_SUCCESS)
3089     {
3090         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
3091         goto Cleanup;
3092     }
3093 
3094     // Store our new default printer.
3095     dwErrorCode = (DWORD)RegSetValueExW(hWindowsKey, wszDeviceValue, 0, REG_SZ, (PBYTE)pwszDeviceValueData, cbDeviceValueData);
3096     if (dwErrorCode != ERROR_SUCCESS)
3097     {
3098         ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
3099         goto Cleanup;
3100     }
3101 
3102 Cleanup:
3103     if (hDevicesKey)
3104         RegCloseKey(hDevicesKey);
3105 
3106     if (hWindowsKey)
3107         RegCloseKey(hWindowsKey);
3108 
3109     if (pwszDeviceValueData)
3110         HeapFree(hProcessHeap, 0, pwszDeviceValueData);
3111 
3112     SetLastError(dwErrorCode);
3113     return (dwErrorCode == ERROR_SUCCESS);
3114 }
3115 
3116 BOOL WINAPI
3117 SetPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
3118 {
3119     BOOL Ret = FALSE;
3120     UNICODE_STRING usBuffer;
3121     PPRINTER_INFO_STRESS ppisa = (PPRINTER_INFO_STRESS)pPrinter;
3122     PPRINTER_INFO_STRESS ppisw = (PPRINTER_INFO_STRESS)pPrinter;
3123     PPRINTER_INFO_2A ppi2a = (PPRINTER_INFO_2A)pPrinter;
3124     PPRINTER_INFO_2W ppi2w = (PPRINTER_INFO_2W)pPrinter;
3125     PPRINTER_INFO_7A ppi7a = (PPRINTER_INFO_7A)pPrinter;
3126     PPRINTER_INFO_7W ppi7w = (PPRINTER_INFO_7W)pPrinter;
3127     PPRINTER_INFO_9A ppi9a = (PPRINTER_INFO_9A)pPrinter;
3128     PPRINTER_INFO_9W ppi9w = (PPRINTER_INFO_9W)pPrinter;
3129     PWSTR pwszPrinterName = NULL;
3130     PWSTR pwszServerName = NULL;
3131     PWSTR pwszShareName = NULL;
3132     PWSTR pwszPortName = NULL;
3133     PWSTR pwszDriverName = NULL;
3134     PWSTR pwszComment = NULL;
3135     PWSTR pwszLocation = NULL;
3136     PWSTR pwszSepFile = NULL;
3137     PWSTR pwszPrintProcessor = NULL;
3138     PWSTR pwszDatatype = NULL;
3139     PWSTR pwszParameters = NULL;
3140     PDEVMODEW pdmw = NULL;
3141 
3142     FIXME("SetPrinterA(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
3143 
3144     switch ( Level )
3145     {
3146         case 0:
3147             if ( Command == 0 )
3148             {
3149                 if (ppisa->pPrinterName)
3150                 {
3151                     pwszPrinterName = AsciiToUnicode(&usBuffer, (LPCSTR)ppisa->pPrinterName);
3152                     if (!(ppisw->pPrinterName = pwszPrinterName)) goto Cleanup;
3153                 }
3154                 if (ppisa->pServerName)
3155                 {
3156                     pwszServerName = AsciiToUnicode(&usBuffer, (LPCSTR)ppisa->pServerName);
3157                     if (!(ppisw->pPrinterName = pwszServerName)) goto Cleanup;
3158                 }
3159             }
3160             if ( Command == PRINTER_CONTROL_SET_STATUS )
3161             {
3162                 // Set the pPrinter parameter to a pointer to a DWORD value that specifies the new printer status.
3163                 PRINTER_INFO_6 pi6;
3164                 pi6.dwStatus = (DWORD_PTR)pPrinter;
3165                 pPrinter = (LPBYTE)&pi6;
3166                 Level = 6;
3167                 Command = 0;
3168             }
3169             break;
3170         case 2:
3171             {
3172                 if (ppi2a->pShareName)
3173                 {
3174                     pwszShareName = AsciiToUnicode(&usBuffer, ppi2a->pShareName);
3175                     if (!(ppi2w->pShareName = pwszShareName)) goto Cleanup;
3176                 }
3177                 if (ppi2a->pPortName)
3178                 {
3179                     pwszPortName = AsciiToUnicode(&usBuffer, ppi2a->pPortName);
3180                     if (!(ppi2w->pPortName = pwszPortName)) goto Cleanup;
3181                 }
3182                 if (ppi2a->pDriverName)
3183                 {
3184                     pwszDriverName = AsciiToUnicode(&usBuffer, ppi2a->pDriverName);
3185                     if (!(ppi2w->pDriverName = pwszDriverName)) goto Cleanup;
3186                 }
3187                 if (ppi2a->pComment)
3188                 {
3189                     pwszComment = AsciiToUnicode(&usBuffer, ppi2a->pComment);
3190                     if (!(ppi2w->pComment = pwszComment)) goto Cleanup;
3191                 }
3192                 if (ppi2a->pLocation)
3193                 {
3194                     pwszLocation = AsciiToUnicode(&usBuffer, ppi2a->pLocation);
3195                     if (!(ppi2w->pLocation = pwszLocation)) goto Cleanup;
3196                 }
3197                 if (ppi2a->pSepFile)
3198                 {
3199                     pwszSepFile = AsciiToUnicode(&usBuffer, ppi2a->pSepFile);
3200                     if (!(ppi2w->pSepFile = pwszSepFile)) goto Cleanup;
3201                 }
3202                 if (ppi2a->pServerName)
3203                 {
3204                     pwszPrintProcessor = AsciiToUnicode(&usBuffer, ppi2a->pPrintProcessor);
3205                     if (!(ppi2w->pPrintProcessor = pwszPrintProcessor)) goto Cleanup;
3206                 }
3207                 if (ppi2a->pDatatype)
3208                 {
3209                     pwszDatatype = AsciiToUnicode(&usBuffer, ppi2a->pDatatype);
3210                     if (!(ppi2w->pDatatype = pwszDatatype)) goto Cleanup;
3211                 }
3212                 if (ppi2a->pParameters)
3213                 {
3214                     pwszParameters = AsciiToUnicode(&usBuffer, ppi2a->pParameters);
3215                     if (!(ppi2w->pParameters = pwszParameters)) goto Cleanup;
3216                 }
3217 
3218                 if ( ppi2a->pDevMode )
3219                 {
3220                     RosConvertAnsiDevModeToUnicodeDevmode( ppi2a->pDevMode, &pdmw );
3221                     ppi2w->pDevMode = pdmw;
3222                 }
3223             }
3224         //
3225         //  These two strings are relitive and common to these three Levels.
3226         //  Fall through...
3227         //
3228         case 4:
3229         case 5:
3230             {
3231                 if (ppi2a->pServerName) // 4 & 5 : pPrinterName.
3232                 {
3233                     pwszServerName = AsciiToUnicode(&usBuffer, ppi2a->pServerName);
3234                     if (!(ppi2w->pPrinterName = pwszServerName)) goto Cleanup;
3235                 }
3236                 if (ppi2a->pPrinterName) // 4 : pServerName, 5 : pPortName.
3237                 {
3238                     pwszPrinterName = AsciiToUnicode(&usBuffer, ppi2a->pPrinterName);
3239                     if (!(ppi2w->pPrinterName = pwszPrinterName)) goto Cleanup;
3240                 }
3241             }
3242             break;
3243         case 3:
3244         case 6:
3245             break;
3246         case 7:
3247             {
3248                 if (ppi7a->pszObjectGUID)
3249                 {
3250                     pwszPrinterName = AsciiToUnicode(&usBuffer, ppi7a->pszObjectGUID);
3251                     if (!(ppi7w->pszObjectGUID = pwszPrinterName)) goto Cleanup;
3252                 }
3253             }
3254             break;
3255 
3256         case 8:
3257         /* 8 is the global default printer info and 9 already sets it instead of the per-user one */
3258         /* still, PRINTER_INFO_8W is the same as PRINTER_INFO_9W */
3259         /* fall through */
3260         case 9:
3261             {
3262                 RosConvertAnsiDevModeToUnicodeDevmode( ppi9a->pDevMode, &pdmw );
3263                 ppi9w->pDevMode = pdmw;
3264             }
3265             break;
3266 
3267         default:
3268             FIXME( "Unsupported level %d\n", Level);
3269             SetLastError( ERROR_INVALID_LEVEL );
3270     }
3271 
3272     Ret = SetPrinterW( hPrinter, Level, pPrinter, Command );
3273 
3274 Cleanup:
3275     if (pdmw) HeapFree(hProcessHeap, 0, pdmw);
3276     if (pwszPrinterName) HeapFree(hProcessHeap, 0, pwszPrinterName);
3277     if (pwszServerName) HeapFree(hProcessHeap, 0, pwszServerName);
3278     if (pwszShareName) HeapFree(hProcessHeap, 0, pwszShareName);
3279     if (pwszPortName) HeapFree(hProcessHeap, 0, pwszPortName);
3280     if (pwszDriverName) HeapFree(hProcessHeap, 0, pwszDriverName);
3281     if (pwszComment) HeapFree(hProcessHeap, 0, pwszComment);
3282     if (pwszLocation) HeapFree(hProcessHeap, 0, pwszLocation);
3283     if (pwszSepFile) HeapFree(hProcessHeap, 0, pwszSepFile);
3284     if (pwszPrintProcessor) HeapFree(hProcessHeap, 0, pwszPrintProcessor);
3285     if (pwszDatatype) HeapFree(hProcessHeap, 0, pwszDatatype);
3286     if (pwszParameters) HeapFree(hProcessHeap, 0, pwszParameters);
3287     return Ret;
3288 }
3289 
3290 BOOL WINAPI
3291 SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
3292 {
3293     DWORD dwErrorCode;
3294     WINSPOOL_PRINTER_CONTAINER PrinterContainer;
3295     WINSPOOL_DEVMODE_CONTAINER DevModeContainer;
3296     WINSPOOL_SECURITY_CONTAINER SecurityContainer;
3297     SECURITY_DESCRIPTOR *sd = NULL;
3298     DWORD size;
3299     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
3300 
3301     FIXME("SetPrinterW(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
3302 
3303     // Sanity checks
3304     if (!pHandle)
3305         return ERROR_INVALID_HANDLE;
3306 
3307     DevModeContainer.cbBuf = 0;
3308     DevModeContainer.pDevMode = NULL;
3309 
3310     SecurityContainer.cbBuf = 0;
3311     SecurityContainer.pSecurity = NULL;
3312 
3313     switch ( Level )
3314     {
3315         case 0:
3316             if ( Command == PRINTER_CONTROL_SET_STATUS )
3317             {
3318                 // Set the pPrinter parameter to a pointer to a DWORD value that specifies the new printer status.
3319                 PRINTER_INFO_6 pi6;
3320                 pi6.dwStatus = (DWORD_PTR)pPrinter;
3321                 pPrinter = (LPBYTE)&pi6;
3322                 Level = 6;
3323                 Command = 0;
3324             }
3325             break;
3326         case 2:
3327             {
3328                 PPRINTER_INFO_2W pi2w = (PPRINTER_INFO_2W)pPrinter;
3329                 if ( pi2w )
3330                 {
3331                     if ( pi2w->pDevMode )
3332                     {
3333                          if ( IsValidDevmodeNoSizeW( pi2w->pDevMode ) )
3334                          {
3335                              DevModeContainer.cbBuf = pi2w->pDevMode->dmSize + pi2w->pDevMode->dmDriverExtra;
3336                              DevModeContainer.pDevMode = (PBYTE)pi2w->pDevMode;
3337                          }
3338                     }
3339 
3340                     if ( pi2w->pSecurityDescriptor )
3341                     {
3342                         sd = get_sd( pi2w->pSecurityDescriptor, &size );
3343                         if ( sd )
3344                         {
3345                             SecurityContainer.cbBuf = size;
3346                             SecurityContainer.pSecurity = (PBYTE)sd;
3347                         }
3348                     }
3349                 }
3350                 else
3351                 {
3352                     SetLastError(ERROR_INVALID_PARAMETER);
3353                     return FALSE;
3354                 }
3355             }
3356             break;
3357         case 3:
3358             {
3359                 PPRINTER_INFO_3 pi3 = (PPRINTER_INFO_3)pPrinter;
3360                 if ( pi3 )
3361                 {
3362                     if ( pi3->pSecurityDescriptor )
3363                     {
3364                         sd = get_sd( pi3->pSecurityDescriptor, &size );
3365                         if ( sd )
3366                         {
3367                             SecurityContainer.cbBuf = size;
3368                             SecurityContainer.pSecurity = (PBYTE)sd;
3369                         }
3370                     }
3371                 }
3372                 else
3373                 {
3374                     SetLastError(ERROR_INVALID_PARAMETER);
3375                     return FALSE;
3376                 }
3377             }
3378             break;
3379 
3380         case 4:
3381         case 5:
3382         case 6:
3383         case 7:
3384             if ( pPrinter == NULL )
3385             {
3386                 SetLastError(ERROR_INVALID_PARAMETER);
3387                 return FALSE;
3388             }
3389             break;
3390 
3391         case 8:
3392         /* 8 is the global default printer info and 9 already sets it instead of the per-user one */
3393         /* still, PRINTER_INFO_8W is the same as PRINTER_INFO_9W */
3394         /* fall through */
3395         case 9:
3396             {
3397                 PPRINTER_INFO_9W pi9w = (PPRINTER_INFO_9W)pPrinter;
3398                 if ( pi9w )
3399                 {
3400                     if ( pi9w->pDevMode )
3401                     {
3402                          if ( IsValidDevmodeNoSizeW( pi9w->pDevMode ) )
3403                          {
3404                              DevModeContainer.cbBuf = pi9w->pDevMode->dmSize + pi9w->pDevMode->dmDriverExtra;
3405                              DevModeContainer.pDevMode = (LPBYTE)pi9w->pDevMode;
3406                          }
3407                     }
3408                 }
3409             }
3410             break;
3411 
3412         default:
3413             FIXME( "Unsupported level %d\n", Level );
3414             SetLastError( ERROR_INVALID_LEVEL );
3415             return FALSE;
3416     }
3417 
3418     PrinterContainer.PrinterInfo.pPrinterInfo1 = (WINSPOOL_PRINTER_INFO_1*)pPrinter;
3419     PrinterContainer.Level = Level;
3420 
3421     // Do the RPC call
3422     RpcTryExcept
3423     {
3424         dwErrorCode = _RpcSetPrinter(pHandle->hPrinter, &PrinterContainer, &DevModeContainer, &SecurityContainer, Command);
3425     }
3426     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
3427     {
3428         dwErrorCode = RpcExceptionCode();
3429     }
3430     RpcEndExcept;
3431 
3432     if ( sd ) HeapFree( GetProcessHeap(), 0, sd );
3433 
3434     SetLastError(dwErrorCode);
3435     return (dwErrorCode == ERROR_SUCCESS);
3436 }
3437 
3438 BOOL WINAPI
3439 SplDriverUnloadComplete(LPWSTR pDriverFile)
3440 {
3441     TRACE("DriverUnloadComplete(%S)\n", pDriverFile);
3442     UNIMPLEMENTED;
3443     return TRUE; // return true for now.
3444 }
3445 BOOL WINAPI
3446 
3447 SpoolerPrinterEvent( LPWSTR pPrinterName, INT DriverEvent, DWORD Flags, LPARAM lParam )
3448 {
3449     HMODULE hLibrary;
3450     HANDLE hPrinter;
3451     BOOL Ret = FALSE;
3452 
3453     if ( OpenPrinterW( pPrinterName, &hPrinter, NULL ) )
3454     {
3455         hLibrary = LoadPrinterDriver( hPrinter );
3456 
3457         if ( hLibrary )
3458         {
3459             fpPrinterEvent = (PVOID)GetProcAddress( hLibrary, "DrvPrinterEvent" );
3460 
3461             if ( fpPrinterEvent )
3462             {
3463                 Ret = fpPrinterEvent( pPrinterName, DriverEvent, Flags, lParam );
3464             }
3465 
3466             FreeLibrary(hLibrary);
3467         }
3468 
3469         ClosePrinter( hPrinter );
3470     }
3471 
3472     return Ret;
3473 }
3474 
3475 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
3476 {
3477     LPWSTR filename;
3478 
3479     switch(msg)
3480     {
3481     case WM_INITDIALOG:
3482         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
3483         return TRUE;
3484 
3485     case WM_COMMAND:
3486         if(HIWORD(wparam) == BN_CLICKED)
3487         {
3488             if(LOWORD(wparam) == IDOK)
3489             {
3490                 HANDLE hf;
3491                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
3492                 LPWSTR *output;
3493 
3494                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
3495                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
3496 
3497                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
3498                 {
3499                     WCHAR caption[200], message[200];
3500                     int mb_ret;
3501 
3502                     LoadStringW(hinstWinSpool, IDS_CAPTION, caption, ARRAYSIZE(caption));
3503                     LoadStringW(hinstWinSpool, IDS_FILE_EXISTS, message, ARRAYSIZE(message));
3504                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
3505                     if(mb_ret == IDCANCEL)
3506                     {
3507                         HeapFree(GetProcessHeap(), 0, filename);
3508                         return TRUE;
3509                     }
3510                 }
3511                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3512                 if(hf == INVALID_HANDLE_VALUE)
3513                 {
3514                     WCHAR caption[200], message[200];
3515 
3516                     LoadStringW(hinstWinSpool, IDS_CAPTION, caption, ARRAYSIZE(caption));
3517                     LoadStringW(hinstWinSpool, IDS_CANNOT_OPEN, message, ARRAYSIZE(message));
3518                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
3519                     HeapFree(GetProcessHeap(), 0, filename);
3520                     return TRUE;
3521                 }
3522                 CloseHandle(hf);
3523                 DeleteFileW(filename);
3524                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
3525                 *output = filename;
3526                 EndDialog(hwnd, IDOK);
3527                 return TRUE;
3528             }
3529             if(LOWORD(wparam) == IDCANCEL)
3530             {
3531                 EndDialog(hwnd, IDCANCEL);
3532                 return TRUE;
3533             }
3534         }
3535         return FALSE;
3536     }
3537     return FALSE;
3538 }
3539 
3540 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
3541 
3542 LPWSTR WINAPI
3543 StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
3544 {
3545     LPWSTR ret = NULL;
3546     DWORD len, attr, retDlg;
3547 
3548     FIXME("StartDocDlgW(%p, %p)\n", hPrinter, doc);
3549 
3550     if (doc->lpszOutput == NULL) /* Check whether default port is FILE: */
3551     {
3552         PRINTER_INFO_5W *pi5;
3553         GetPrinterW(hPrinter, 5, NULL, 0, &len);
3554         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
3555             return NULL;
3556         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
3557         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
3558         if (!pi5->pPortName || wcsicmp(pi5->pPortName, FILE_Port))
3559         {
3560             HeapFree(GetProcessHeap(), 0, pi5);
3561             return NULL;
3562         }
3563         HeapFree(GetProcessHeap(), 0, pi5);
3564     }
3565 
3566     if (doc->lpszOutput == NULL || !wcsicmp(doc->lpszOutput, FILE_Port))
3567     {
3568         LPWSTR name;
3569 
3570         retDlg = DialogBoxParamW( hinstWinSpool,
3571                                   MAKEINTRESOURCEW(FILENAME_DIALOG),
3572                                   GetForegroundWindow(),
3573                                   file_dlg_proc,
3574                                  (LPARAM)&name );
3575 
3576         if ( retDlg == IDOK )
3577         {
3578             if (!(len = GetFullPathNameW(name, 0, NULL, NULL)))
3579             {
3580                 HeapFree(GetProcessHeap(), 0, name);
3581                 return NULL;
3582             }
3583             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3584             GetFullPathNameW(name, len, ret, NULL);
3585             HeapFree(GetProcessHeap(), 0, name);
3586         }
3587         else if ( retDlg == 0 ) // FALSE, some type of error occurred.
3588         {
3589             ret = (LPWSTR)SP_ERROR;
3590         }
3591         else if ( retDlg == IDCANCEL )
3592         {
3593             SetLastError( ERROR_CANCELLED );
3594             ret = (LPWSTR)SP_APPABORT;
3595         }
3596         return ret;
3597     }
3598 
3599     if (!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
3600         return NULL;
3601 
3602     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3603     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
3604 
3605     attr = GetFileAttributesW(ret);
3606     if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
3607     {
3608         HeapFree(GetProcessHeap(), 0, ret);
3609         ret = NULL;
3610     }
3611     return ret;
3612 }
3613 
3614 LPSTR WINAPI
3615 StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
3616 {
3617     UNICODE_STRING usBuffer;
3618     DOCINFOW docW = { 0 };
3619     LPWSTR retW;
3620     LPWSTR docnameW = NULL, outputW = NULL, datatypeW = NULL;
3621     LPSTR ret = NULL;
3622 
3623     docW.cbSize = sizeof(docW);
3624     if (doc->lpszDocName)
3625     {
3626         docnameW = AsciiToUnicode(&usBuffer, doc->lpszDocName);
3627         if (!(docW.lpszDocName = docnameW)) goto failed;
3628     }
3629     if (doc->lpszOutput)
3630     {
3631         outputW = AsciiToUnicode(&usBuffer, doc->lpszOutput);
3632         if (!(docW.lpszOutput = outputW)) goto failed;
3633     }
3634     if (doc->lpszDatatype)
3635     {
3636         datatypeW = AsciiToUnicode(&usBuffer, doc->lpszDatatype);
3637         if (!(docW.lpszDatatype = datatypeW)) goto failed;
3638     }
3639     docW.fwType = doc->fwType;
3640 
3641     retW = StartDocDlgW(hPrinter, &docW);
3642 
3643     if (retW)
3644     {
3645         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
3646         ret = HeapAlloc(GetProcessHeap(), 0, len);
3647         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
3648         HeapFree(GetProcessHeap(), 0, retW);
3649     }
3650 
3651 failed:
3652     if (datatypeW) HeapFree(GetProcessHeap(), 0, datatypeW);
3653     if (outputW) HeapFree(GetProcessHeap(), 0, outputW);
3654     if (docnameW) HeapFree(GetProcessHeap(), 0, docnameW);
3655 
3656     return ret;
3657 }
3658 
3659 DWORD WINAPI
3660 StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
3661 {
3662     DOC_INFO_1W wDocInfo1 = { 0 };
3663     DWORD cch;
3664     DWORD dwErrorCode;
3665     DWORD dwReturnValue = 0;
3666     PDOC_INFO_1A pDocInfo1 = (PDOC_INFO_1A)pDocInfo;
3667 
3668     TRACE("StartDocPrinterA(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
3669 
3670     // Only check the minimum required for accessing pDocInfo.
3671     // Additional sanity checks are done in StartDocPrinterW.
3672     if (!pDocInfo1)
3673     {
3674         dwErrorCode = ERROR_INVALID_PARAMETER;
3675         goto Cleanup;
3676     }
3677 
3678     if (Level != 1)
3679     {
3680         ERR("Level = %d, unsupported!\n", Level);
3681         dwErrorCode = ERROR_INVALID_LEVEL;
3682         goto Cleanup;
3683     }
3684 
3685     if (pDocInfo1->pDatatype)
3686     {
3687         // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
3688         cch = strlen(pDocInfo1->pDatatype);
3689 
3690         wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
3691         if (!wDocInfo1.pDatatype)
3692         {
3693             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
3694             ERR("HeapAlloc failed!\n");
3695             goto Cleanup;
3696         }
3697 
3698         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDatatype, -1, wDocInfo1.pDatatype, cch + 1);
3699     }
3700 
3701     if (pDocInfo1->pDocName)
3702     {
3703         // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
3704         cch = strlen(pDocInfo1->pDocName);
3705 
3706         wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
3707         if (!wDocInfo1.pDocName)
3708         {
3709             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
3710             ERR("HeapAlloc failed!\n");
3711             goto Cleanup;
3712         }
3713 
3714         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDocName, -1, wDocInfo1.pDocName, cch + 1);
3715     }
3716 
3717     if (pDocInfo1->pOutputFile)
3718     {
3719         // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
3720         cch = strlen(pDocInfo1->pOutputFile);
3721 
3722         wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
3723         if (!wDocInfo1.pOutputFile)
3724         {
3725             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
3726             ERR("HeapAlloc failed!\n");
3727             goto Cleanup;
3728         }
3729 
3730         MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pOutputFile, -1, wDocInfo1.pOutputFile, cch + 1);
3731     }
3732 
3733     dwReturnValue = StartDocPrinterW(hPrinter, Level, (PBYTE)&wDocInfo1);
3734     dwErrorCode = GetLastError();
3735 
3736 Cleanup:
3737     if (wDocInfo1.pDatatype)
3738         HeapFree(hProcessHeap, 0, wDocInfo1.pDatatype);
3739 
3740     if (wDocInfo1.pDocName)
3741         HeapFree(hProcessHeap, 0, wDocInfo1.pDocName);
3742 
3743     if (wDocInfo1.pOutputFile)
3744         HeapFree(hProcessHeap, 0, wDocInfo1.pOutputFile);
3745 
3746     SetLastError(dwErrorCode);
3747     return dwReturnValue;
3748 }
3749 
3750 DWORD WINAPI
3751 StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
3752 {
3753     DWORD cbAddJobInfo1;
3754     DWORD cbNeeded;
3755     DWORD dwErrorCode;
3756     DWORD dwReturnValue = 0;
3757     PADDJOB_INFO_1W pAddJobInfo1 = NULL;
3758     PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
3759     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
3760 
3761     TRACE("StartDocPrinterW(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
3762 
3763     // Sanity checks.
3764     if (!pHandle)
3765     {
3766         dwErrorCode = ERROR_INVALID_HANDLE;
3767         goto Cleanup;
3768     }
3769 
3770     if (!pDocInfo1)
3771     {
3772         dwErrorCode = ERROR_INVALID_PARAMETER;
3773         goto Cleanup;
3774     }
3775 
3776     if (Level != 1)
3777     {
3778         ERR("Level = %d, unsupported!\n", Level);
3779         dwErrorCode = ERROR_INVALID_LEVEL;
3780         goto Cleanup;
3781     }
3782 
3783     if (pHandle->bStartedDoc)
3784     {
3785         dwErrorCode = ERROR_INVALID_PRINTER_STATE;
3786         goto Cleanup;
3787     }
3788 
3789     // Check if we want to redirect output into a file.
3790     if (pDocInfo1->pOutputFile)
3791     {
3792         // Do a StartDocPrinter RPC call in this case.
3793         dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
3794     }
3795     else
3796     {
3797         // Allocate memory for the ADDJOB_INFO_1W structure and a path.
3798         cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
3799         pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
3800         if (!pAddJobInfo1)
3801         {
3802             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
3803             ERR("HeapAlloc failed!\n");
3804             goto Cleanup;
3805         }
3806 
3807         // Try to add a new job.
3808         // This only succeeds if the printer is set to do spooled printing.
3809         if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
3810         {
3811             // Do spooled printing.
3812             dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
3813         }
3814         else if (GetLastError() == ERROR_INVALID_ACCESS)
3815         {
3816             // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
3817             // In this case, we do a StartDocPrinter RPC call.
3818             dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
3819         }
3820         else
3821         {
3822             dwErrorCode = GetLastError();
3823             ERR("AddJobW failed with error %lu!\n", dwErrorCode);
3824             goto Cleanup;
3825         }
3826     }
3827 
3828     if (dwErrorCode == ERROR_SUCCESS)
3829     {
3830         pHandle->bStartedDoc = TRUE;
3831         dwReturnValue = pHandle->dwJobID;
3832         if ( !pHandle->bTrayIcon )
3833         {
3834             UpdateTrayIcon( hPrinter, pHandle->dwJobID );
3835         }
3836     }
3837 
3838 Cleanup:
3839     if (pAddJobInfo1)
3840         HeapFree(hProcessHeap, 0, pAddJobInfo1);
3841 
3842     SetLastError(dwErrorCode);
3843     return dwReturnValue;
3844 }
3845 
3846 BOOL WINAPI
3847 StartPagePrinter(HANDLE hPrinter)
3848 {
3849     DWORD dwErrorCode;
3850     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
3851 
3852     TRACE("StartPagePrinter(%p)\n", hPrinter);
3853 
3854     // Sanity checks.
3855     if (!pHandle)
3856     {
3857         dwErrorCode = ERROR_INVALID_HANDLE;
3858         goto Cleanup;
3859     }
3860 
3861     // Do the RPC call
3862     RpcTryExcept
3863     {
3864         dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
3865     }
3866     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
3867     {
3868         dwErrorCode = RpcExceptionCode();
3869         ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
3870     }
3871     RpcEndExcept;
3872 
3873 Cleanup:
3874     SetLastError(dwErrorCode);
3875     return (dwErrorCode == ERROR_SUCCESS);
3876 }
3877 
3878 BOOL WINAPI
3879 WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
3880 {
3881     DWORD dwErrorCode;
3882     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
3883 
3884     TRACE("WritePrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
3885 
3886     // Sanity checks.
3887     if (!pHandle)
3888     {
3889         dwErrorCode = ERROR_INVALID_HANDLE;
3890         goto Cleanup;
3891     }
3892 
3893     if (!pHandle->bStartedDoc)
3894     {
3895         dwErrorCode = ERROR_SPL_NO_STARTDOC;
3896         goto Cleanup;
3897     }
3898 
3899     if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
3900     {
3901         // Write to the spool file. This doesn't need an RPC request.
3902         if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
3903         {
3904             dwErrorCode = GetLastError();
3905             ERR("WriteFile failed with error %lu!\n", dwErrorCode);
3906             goto Cleanup;
3907         }
3908 
3909         dwErrorCode = ERROR_SUCCESS;
3910     }
3911     else
3912     {
3913         // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
3914         // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
3915 
3916         // Do the RPC call
3917         RpcTryExcept
3918         {
3919             dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
3920         }
3921         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
3922         {
3923             dwErrorCode = RpcExceptionCode();
3924             ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
3925         }
3926         RpcEndExcept;
3927     }
3928 
3929 Cleanup:
3930     SetLastError(dwErrorCode);
3931     return (dwErrorCode == ERROR_SUCCESS);
3932 }
3933 
3934 BOOL WINAPI
3935 XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
3936 {
3937     DWORD dwErrorCode, Bogus = 0;
3938     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hXcv;
3939 
3940     TRACE("XcvDataW(%p, %S, %p, %lu, %p, %lu, %p, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus);
3941 
3942     if ( pcbOutputNeeded == NULL )
3943     {
3944         dwErrorCode = ERROR_INVALID_PARAMETER;
3945         goto Cleanup;
3946     }
3947 
3948     // Sanity checks.
3949     if (!pHandle) // ( IntProtectHandle( hXcv, FALSE ) )
3950     {
3951         dwErrorCode = ERROR_INVALID_HANDLE;
3952         goto Cleanup;
3953     }
3954 
3955     //
3956     // Do fixups.
3957     //
3958     if ( pInputData == NULL )
3959     {
3960         if ( !cbInputData )
3961         {
3962              pInputData = (PBYTE)&Bogus;
3963         }
3964     }
3965 
3966     if ( pOutputData == NULL )
3967     {
3968         if ( !cbOutputData )
3969         {
3970             pOutputData = (PBYTE)&Bogus;
3971         }
3972     }
3973 
3974     // Do the RPC call
3975     RpcTryExcept
3976     {
3977         dwErrorCode = _RpcXcvData( pHandle->hPrinter,
3978                                    pszDataName,
3979                                    pInputData,
3980                                    cbInputData,
3981                                    pOutputData,
3982                                    cbOutputData,
3983                                    pcbOutputNeeded,
3984                                    pdwStatus );
3985     }
3986     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
3987     {
3988         dwErrorCode = RpcExceptionCode();
3989         ERR("_RpcXcvData failed with exception code %lu!\n", dwErrorCode);
3990     }
3991     RpcEndExcept;
3992 
3993     //IntUnprotectHandle( hXcv );
3994 
3995 Cleanup:
3996     SetLastError(dwErrorCode);
3997     return (dwErrorCode == ERROR_SUCCESS);
3998 }
3999