Added magic comments to all Wine-specific registry accesses to make
[wine] / dlls / winspool / info.c
1 /*
2  * WINSPOOL functions
3  *
4  * Copyright 1996 John Harvey
5  * Copyright 1998 Andreas Mohr
6  * Copyright 1999 Klaas van Gend
7  * Copyright 1999, 2000 Huw D M Davies
8  * Copyright 2001 Marcus Meissner
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include "config.h"
26 #include "wine/port.h"
27
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <stddef.h>
34 #ifdef HAVE_CUPS_CUPS_H
35 # include <cups/cups.h>
36 # ifndef SONAME_LIBCUPS
37 #  define SONAME_LIBCUPS "libcups.so"
38 # endif
39 #endif
40
41 #define NONAMELESSUNION
42 #define NONAMELESSSTRUCT
43 #include "wine/library.h"
44 #include "windef.h"
45 #include "winbase.h"
46 #include "winuser.h"
47 #include "winerror.h"
48 #include "winreg.h"
49 #include "wingdi.h"
50 #include "winspool.h"
51 #include "winternl.h"
52 #include "wine/windef16.h"
53 #include "wine/unicode.h"
54 #include "wine/debug.h"
55 #include "heap.h"
56 #include "winnls.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
59
60 static LPWSTR *printer_array;
61 static int nb_printers;
62
63 static DWORD (WINAPI *GDI_CallDeviceCapabilities16)( LPCSTR lpszDevice, LPCSTR lpszPort,
64                                                      WORD fwCapability, LPSTR lpszOutput,
65                                                      LPDEVMODEA lpdm );
66 static INT (WINAPI *GDI_CallExtDeviceMode16)( HWND hwnd, LPDEVMODEA lpdmOutput,
67                                               LPSTR lpszDevice, LPSTR lpszPort,
68                                               LPDEVMODEA lpdmInput, LPSTR lpszProfile,
69                                               DWORD fwMode );
70
71 static const char Printers[] =
72 "System\\CurrentControlSet\\control\\Print\\Printers\\";
73
74 static const WCHAR DriversW[] = { 'S','y','s','t','e','m','\\',
75                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
76                                   'c','o','n','t','r','o','l','\\',
77                                   'P','r','i','n','t','\\',
78                                   'E','n','v','i','r','o','n','m','e','n','t','s','\\',
79                                   '%','s','\\','D','r','i','v','e','r','s','\\',0 };
80
81 static const WCHAR user_default_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
82                                               'M','i','c','r','o','s','o','f','t','\\',
83                                               'W','i','n','d','o','w','s',' ','N','T','\\',
84                                               'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
85                                               'W','i','n','d','o','w','s',0};
86
87 static const WCHAR user_printers_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
88                                                'M','i','c','r','o','s','o','f','t','\\',
89                                                'W','i','n','d','o','w','s',' ','N','T','\\',
90                                                'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
91                                                'D','e','v','i','c','e','s',0};
92
93 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
94
95 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
96                                       'i','o','n',' ','F','i','l','e',0};
97 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
98 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
99 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v',
100                                    'M','o','d','e',0};
101 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F',
102                                    'i','l','e','s',0};
103 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
104 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
105 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
106 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
107 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
108 static const WCHAR NameW[] = {'N','a','m','e',0};
109 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
110 static const WCHAR PortW[] = {'P','o','r','t',0};
111 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e',
112                                    's','s','o','r',0};
113 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i',
114                                   'v','e','r',0};
115 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i',
116                                      'v','e','r','D','a','t','a',0};
117 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F',
118                                   'i','l','e',0};
119 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
120 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
121 static const WCHAR deviceW[]  = {'d','e','v','i','c','e',0};
122 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
123 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
124 static const WCHAR emptyStringW[] = {0};
125
126 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
127
128 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode);
129 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
130                                       DWORD Level, LPBYTE pDriverInfo,
131                                       DWORD cbBuf, LPDWORD pcbNeeded,
132                                       BOOL unicode);
133 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey);
134
135 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
136    if passed a NULL string. This returns NULLs to the result. 
137 */
138 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
139 {
140     if ( (src) )
141     {
142         RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
143         return usBufferPtr->Buffer;
144     }
145     usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
146     return NULL;
147 }
148             
149 static void
150 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name,BOOL force) {
151     char qbuf[200];
152
153     /* If forcing, or no profile string entry for device yet, set the entry
154      *
155      * The always change entry if not WINEPS yet is discussable.
156      */
157     if (force                                                           ||
158         !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf))    ||
159         !strcmp(qbuf,"*")                                               ||
160         !strstr(qbuf,"WINEPS.DRV")
161     ) {
162         char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
163         HKEY hkey;
164
165         sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
166         WriteProfileStringA("windows","device",buf);
167         if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
168             RegSetValueExA(hkey, "Device", 0, REG_SZ, buf, strlen(buf) + 1);
169             RegCloseKey(hkey);
170         }
171         HeapFree(GetProcessHeap(),0,buf);
172     }
173 }
174
175 #ifdef HAVE_CUPS_CUPS_H
176 static BOOL CUPS_LoadPrinters(void)
177 {
178     typeof(cupsGetDests) *pcupsGetDests = NULL;
179     typeof(cupsGetPPD)   *pcupsGetPPD = NULL;
180     int                   i, nrofdests;
181     BOOL                  hadprinter = FALSE;
182     cups_dest_t          *dests;
183     PRINTER_INFO_2A       pinfo2a;
184     void *cupshandle = NULL;
185     char   *port,*devline;
186     HKEY hkeyPrinter, hkeyPrinters, hkey;
187
188     cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, NULL, 0);
189     if (!cupshandle) 
190         return FALSE;
191     TRACE("loaded %s\n", SONAME_LIBCUPS);
192
193 #define DYNCUPS(x)                                      \
194         p##x = wine_dlsym(cupshandle, #x, NULL,0);      \
195         if (!p##x) return FALSE;
196
197     DYNCUPS(cupsGetPPD);
198     DYNCUPS(cupsGetDests);
199 #undef DYNCUPS
200
201     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
202        ERROR_SUCCESS) {
203         ERR("Can't create Printers key\n");
204         return FALSE;
205     }
206
207     nrofdests = pcupsGetDests(&dests);
208     TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
209     for (i=0;i<nrofdests;i++) {
210         port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
211         sprintf(port,"LPR:%s",dests[i].name);
212         devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
213         sprintf(devline,"WINEPS.DRV,%s",port);
214         WriteProfileStringA("devices",dests[i].name,devline);
215         if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
216             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, devline, strlen(devline) + 1);
217             RegCloseKey(hkey);
218         }
219         HeapFree(GetProcessHeap(),0,devline);
220
221         TRACE("Printer %d: %s\n", i, dests[i].name);
222         if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
223             /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
224                and continue */
225             TRACE("Printer already exists\n");
226             RegDeleteValueW(hkeyPrinter, May_Delete_Value);
227             RegCloseKey(hkeyPrinter);
228         } else {
229             memset(&pinfo2a,0,sizeof(pinfo2a));
230             pinfo2a.pPrinterName        = dests[i].name;
231             pinfo2a.pDatatype   = "RAW";
232             pinfo2a.pPrintProcessor     = "WinPrint";
233             pinfo2a.pDriverName = "PS Driver";
234             pinfo2a.pComment    = "WINEPS Printer using CUPS";
235             pinfo2a.pLocation   = "<physical location of printer>";
236             pinfo2a.pPortName   = port;
237             pinfo2a.pParameters = "<parameters?>";
238             pinfo2a.pShareName  = "<share name?>";
239             pinfo2a.pSepFile    = "<sep file?>";
240
241             if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
242                 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
243                     ERR("printer '%s' not added by AddPrinterA (error %ld)\n",dests[i].name,GetLastError());
244             }
245         }
246         HeapFree(GetProcessHeap(),0,port);
247
248         hadprinter = TRUE;
249         if (dests[i].is_default)
250             WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
251     }
252     RegCloseKey(hkeyPrinters);
253     wine_dlclose(cupshandle, NULL, 0);
254     return hadprinter;
255 }
256 #endif
257
258 static BOOL
259 PRINTCAP_ParseEntry(char *pent,BOOL isfirst) {
260     PRINTER_INFO_2A     pinfo2a;
261     char                *e,*s,*name,*prettyname,*devname;
262     BOOL                ret = FALSE, set_default = FALSE;
263     char                *port,*devline,*env_default;
264     HKEY                hkeyPrinter, hkeyPrinters, hkey;
265
266     while (isspace(*pent)) pent++;
267     s = strchr(pent,':');
268     if(s) *s='\0';
269     name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
270     strcpy(name,pent);
271     if(s) {
272         *s=':';
273         pent = s;
274     } else
275         pent = "";
276
277     TRACE("name=%s entry=%s\n",name, pent);
278
279     if(ispunct(*name)) { /* a tc entry, not a real printer */
280         TRACE("skipping tc entry\n");
281         goto end;
282     }
283
284     if(strstr(pent,":server")) { /* server only version so skip */
285         TRACE("skipping server entry\n");
286         goto end;
287     }
288
289     /* Determine whether this is a postscript printer. */
290
291     ret = TRUE;
292     env_default = getenv("PRINTER");
293     prettyname = name;
294     /* Get longest name, usually the one at the right for later display. */
295     while((s=strchr(prettyname,'|'))) {
296         *s = '\0';
297         e = s;
298         while(isspace(*--e)) *e = '\0';
299         TRACE("\t%s\n", debugstr_a(prettyname));
300         if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
301         for(prettyname = s+1; isspace(*prettyname); prettyname++)
302             ;
303     }
304     e = prettyname + strlen(prettyname);
305     while(isspace(*--e)) *e = '\0';
306     TRACE("\t%s\n", debugstr_a(prettyname));
307     if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
308
309     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
310      * if it is too long, we use it as comment below. */
311     devname = prettyname;
312     if (strlen(devname)>=CCHDEVICENAME-1)
313          devname = name;
314     if (strlen(devname)>=CCHDEVICENAME-1) {
315         ret = FALSE;
316         goto end;
317     }
318
319     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
320     sprintf(port,"LPR:%s",name);
321
322     devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
323     sprintf(devline,"WINEPS.DRV,%s",port);
324     WriteProfileStringA("devices",devname,devline);
325     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
326         RegSetValueExA(hkey, devname, 0, REG_SZ, devline, strlen(devline) + 1);
327         RegCloseKey(hkey);
328     }
329     HeapFree(GetProcessHeap(),0,devline);
330     
331     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
332        ERROR_SUCCESS) {
333         ERR("Can't create Printers key\n");
334         ret = FALSE;
335         goto end;
336     }
337     if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
338         /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
339            and continue */
340         TRACE("Printer already exists\n");
341         RegDeleteValueW(hkeyPrinter, May_Delete_Value);
342         RegCloseKey(hkeyPrinter);
343     } else {
344         memset(&pinfo2a,0,sizeof(pinfo2a));
345         pinfo2a.pPrinterName            = devname;
346         pinfo2a.pDatatype               = "RAW";
347         pinfo2a.pPrintProcessor         = "WinPrint";
348         pinfo2a.pDriverName             = "PS Driver";
349         pinfo2a.pComment                = "WINEPS Printer using LPR";
350         pinfo2a.pLocation               = prettyname;
351         pinfo2a.pPortName               = port;
352         pinfo2a.pParameters             = "<parameters?>";
353         pinfo2a.pShareName              = "<share name?>";
354         pinfo2a.pSepFile                = "<sep file?>";
355
356         if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
357             if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
358                 ERR("%s not added by AddPrinterA (%ld)\n",name,GetLastError());
359         }
360     }
361     RegCloseKey(hkeyPrinters);
362
363     if (isfirst || set_default)
364         WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
365
366     HeapFree(GetProcessHeap(), 0, port);
367  end:
368     HeapFree(GetProcessHeap(), 0, name);
369     return ret;
370 }
371
372 static BOOL
373 PRINTCAP_LoadPrinters(void) {
374     BOOL                hadprinter = FALSE;
375     char                buf[200];
376     FILE                *f;
377     char *pent = NULL;
378     BOOL had_bash = FALSE;
379
380     f = fopen("/etc/printcap","r");
381     if (!f)
382         return FALSE;
383
384     while(fgets(buf,sizeof(buf),f)) {
385         char *start, *end;
386
387         end=strchr(buf,'\n');
388         if (end) *end='\0';
389     
390         start = buf;
391         while(isspace(*start)) start++;
392         if(*start == '#' || *start == '\0')
393             continue;
394
395         if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
396             hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
397             HeapFree(GetProcessHeap(),0,pent);
398             pent = NULL;
399         }
400
401         if (end && *--end == '\\') {
402             *end = '\0';
403             had_bash = TRUE;
404         } else
405             had_bash = FALSE;
406
407         if (pent) {
408             pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
409             strcat(pent,start);
410         } else {
411             pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
412             strcpy(pent,start);
413         }
414
415     }
416     if(pent) {
417         hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
418         HeapFree(GetProcessHeap(),0,pent);
419     }
420     fclose(f);
421     return hadprinter;
422 }
423
424 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
425 {
426     if (value)
427         return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
428                    lstrlenW(value) * sizeof(WCHAR));
429     else
430         return ERROR_FILE_NOT_FOUND;
431 }
432
433 void WINSPOOL_LoadSystemPrinters(void)
434 {
435     HKEY                hkey, hkeyPrinters;
436     DRIVER_INFO_3A      di3a;
437     HANDLE              hprn;
438     DWORD               needed, num, i;
439     WCHAR               PrinterName[256];
440     BOOL                done = FALSE;
441
442     di3a.cVersion = 0x400;
443     di3a.pName = "PS Driver";
444     di3a.pEnvironment = NULL;   /* NULL means auto */
445     di3a.pDriverPath = "wineps16";
446     di3a.pDataFile = "<datafile?>";
447     di3a.pConfigFile = "wineps16";
448     di3a.pHelpFile = "<helpfile?>";
449     di3a.pDependentFiles = "<dependend files?>";
450     di3a.pMonitorName = "<monitor name?>";
451     di3a.pDefaultDataType = "RAW";
452
453     if (!AddPrinterDriverA(NULL,3,(LPBYTE)&di3a)) {
454         ERR("Failed adding PS Driver (%ld)\n",GetLastError());
455         return;
456     }
457
458     /* This ensures that all printer entries have a valid Name value.  If causes
459        problems later if they don't.  If one is found to be missed we create one
460        and set it equal to the name of the key */
461     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) == ERROR_SUCCESS) {
462         if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
463                             NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
464             for(i = 0; i < num; i++) {
465                 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) == ERROR_SUCCESS) {
466                     if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
467                         if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
468                             set_reg_szW(hkey, NameW, PrinterName);
469                         }
470                         RegCloseKey(hkey);
471                     }
472                 }
473             }
474         }
475         RegCloseKey(hkeyPrinters);
476     }
477
478     /* We want to avoid calling AddPrinter on printers as much as
479        possible, because on cups printers this will (eventually) lead
480        to a call to cupsGetPPD which takes forever, even with non-cups
481        printers AddPrinter takes a while.  So we'll tag all printers that
482        were automatically added last time around, if they still exist
483        we'll leave them be otherwise we'll delete them. */
484     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
485     if(needed) {
486         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
487         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
488             for(i = 0; i < num; i++) {
489                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
490                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
491                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
492                             DWORD dw = 1;
493                             RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
494                             RegCloseKey(hkey);
495                         }
496                         ClosePrinter(hprn);
497                     }
498                 }
499             }
500         }
501         HeapFree(GetProcessHeap(), 0, pi);
502     }
503
504
505 #ifdef HAVE_CUPS_CUPS_H
506     done = CUPS_LoadPrinters();
507 #endif
508
509     if(!done) { /* If we have any CUPS based printers, skip looking for printcap printers */
510         /* Check for [ppd] section in config file before parsing /etc/printcap */
511         /* @@ Wine registry key: HKLM\Software\Wine\Wine\Config\ppd */
512         if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ppd",
513                         &hkey) == ERROR_SUCCESS) {
514             RegCloseKey(hkey);
515             PRINTCAP_LoadPrinters();
516         }
517     }
518
519     /* Now enumerate the list again and delete any printers that a still tagged */
520     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
521     if(needed) {
522         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
523         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
524             for(i = 0; i < num; i++) {
525                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
526                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
527                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
528                             DWORD dw, type, size = sizeof(dw);
529                             if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
530                                 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
531                                 DeletePrinter(hprn);
532                             }
533                             RegCloseKey(hkey);
534                         }
535                         ClosePrinter(hprn);
536                     }
537                 }
538             }
539         }
540         HeapFree(GetProcessHeap(), 0, pi);
541     }
542
543     return;
544
545 }
546
547
548 /******************************************************************
549  *  WINSPOOL_GetOpenedPrinterEntry
550  *  Get the first place empty in the opened printer table
551  */
552 static HANDLE WINSPOOL_GetOpenedPrinterEntry( LPCWSTR name )
553 {
554     int i;
555
556     for (i = 0; i < nb_printers; i++) if (!printer_array[i]) break;
557
558     if (i >= nb_printers)
559     {
560         LPWSTR *new_array;
561         if (printer_array) 
562             new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_array,
563                                          (nb_printers + 16) * sizeof(*new_array) );
564         else 
565             new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
566                                          (nb_printers + 16) * sizeof(*new_array) );
567
568         if (!new_array) return 0;
569         printer_array = new_array;
570         nb_printers += 16;
571     }
572
573     if ((printer_array[i] = HeapAlloc( GetProcessHeap(), 0, (strlenW(name)+1)*sizeof(WCHAR) )))
574     {
575         strcpyW( printer_array[i], name );
576         return (HANDLE)(i + 1);
577     }
578     return 0;
579 }
580
581 /******************************************************************
582  *  WINSPOOL_GetOpenedPrinter
583  *  Get the pointer to the opened printer referred by the handle
584  */
585 static LPCWSTR WINSPOOL_GetOpenedPrinter(HANDLE printerHandle)
586 {
587     int idx = (int)printerHandle;
588     if ((idx <= 0) || (idx > nb_printers))
589     {
590         SetLastError(ERROR_INVALID_HANDLE);
591         return NULL;
592     }
593     return printer_array[idx - 1];
594 }
595
596 /******************************************************************
597  *  WINSPOOL_GetOpenedPrinterRegKey
598  *
599  */
600 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
601 {
602     LPCWSTR name = WINSPOOL_GetOpenedPrinter(hPrinter);
603     DWORD ret;
604     HKEY hkeyPrinters;
605
606     if(!name) return ERROR_INVALID_HANDLE;
607
608     if((ret = RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters)) !=
609        ERROR_SUCCESS)
610         return ret;
611
612     if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
613     {
614         ERR("Can't find opened printer %s in registry\n",
615             debugstr_w(name));
616         RegCloseKey(hkeyPrinters);
617         return ERROR_INVALID_PRINTER_NAME; /* ? */
618     }
619     RegCloseKey(hkeyPrinters);
620     return ERROR_SUCCESS;
621 }
622
623 /***********************************************************
624  *      DEVMODEcpyAtoW
625  */
626 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
627 {
628     BOOL Formname;
629     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
630     DWORD size;
631
632     Formname = (dmA->dmSize > off_formname);
633     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
634     MultiByteToWideChar(CP_ACP, 0, dmA->dmDeviceName, -1, dmW->dmDeviceName,
635                         CCHDEVICENAME);
636     if(!Formname) {
637       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
638              dmA->dmSize - CCHDEVICENAME);
639     } else {
640       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
641              off_formname - CCHDEVICENAME);
642       MultiByteToWideChar(CP_ACP, 0, dmA->dmFormName, -1, dmW->dmFormName,
643                           CCHFORMNAME);
644       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
645              (off_formname + CCHFORMNAME));
646     }
647     dmW->dmSize = size;
648     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
649            dmA->dmDriverExtra);
650     return dmW;
651 }
652
653 /***********************************************************
654  *      DEVMODEdupWtoA
655  * Creates an ascii copy of supplied devmode on heap
656  */
657 static LPDEVMODEA DEVMODEdupWtoA(HANDLE heap, const DEVMODEW *dmW)
658 {
659     LPDEVMODEA dmA;
660     DWORD size;
661     BOOL Formname;
662     ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;
663
664     if(!dmW) return NULL;
665     Formname = (dmW->dmSize > off_formname);
666     size = dmW->dmSize - CCHDEVICENAME - (Formname ? CCHFORMNAME : 0);
667     dmA = HeapAlloc(heap, HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
668     WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1, dmA->dmDeviceName,
669                         CCHDEVICENAME, NULL, NULL);
670     if(!Formname) {
671       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
672              dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR));
673     } else {
674       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
675              off_formname - CCHDEVICENAME * sizeof(WCHAR));
676       WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1, dmA->dmFormName,
677                           CCHFORMNAME, NULL, NULL);
678       memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
679              (off_formname + CCHFORMNAME * sizeof(WCHAR)));
680     }
681     dmA->dmSize = size;
682     memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
683            dmW->dmDriverExtra);
684     return dmA;
685 }
686
687 /***********************************************************
688  *             PRINTER_INFO_2AtoW
689  * Creates a unicode copy of PRINTER_INFO_2A on heap
690  */
691 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
692 {
693     LPPRINTER_INFO_2W piW;
694     UNICODE_STRING usBuffer;
695
696     if(!piA) return NULL;
697     piW = HeapAlloc(heap, 0, sizeof(*piW));
698     memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
699     
700     piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
701     piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
702     piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
703     piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
704     piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
705     piW->pComment = asciitounicode(&usBuffer,piA->pComment);
706     piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
707     piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
708     piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
709     piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
710     piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
711     piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
712     return piW;
713 }
714
715 /***********************************************************
716  *       FREE_PRINTER_INFO_2W
717  * Free PRINTER_INFO_2W and all strings
718  */
719 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
720 {
721     if(!piW) return;
722
723     HeapFree(heap,0,piW->pServerName);
724     HeapFree(heap,0,piW->pPrinterName);
725     HeapFree(heap,0,piW->pShareName);
726     HeapFree(heap,0,piW->pPortName);
727     HeapFree(heap,0,piW->pDriverName);
728     HeapFree(heap,0,piW->pComment);
729     HeapFree(heap,0,piW->pLocation);
730     HeapFree(heap,0,piW->pDevMode);
731     HeapFree(heap,0,piW->pSepFile);
732     HeapFree(heap,0,piW->pPrintProcessor);
733     HeapFree(heap,0,piW->pDatatype);
734     HeapFree(heap,0,piW->pParameters);
735     HeapFree(heap,0,piW);
736     return;
737 }
738
739 /******************************************************************
740  *              DeviceCapabilities     [WINSPOOL.@]
741  *              DeviceCapabilitiesA    [WINSPOOL.@]
742  *
743  */
744 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
745                                LPSTR pOutput, LPDEVMODEA lpdm)
746 {
747     INT ret;
748
749     if (!GDI_CallDeviceCapabilities16)
750     {
751         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
752                                                               (LPCSTR)104 );
753         if (!GDI_CallDeviceCapabilities16) return -1;
754     }
755     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
756
757     /* If DC_PAPERSIZE map POINT16s to POINTs */
758     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
759         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
760         POINT *pt = (POINT *)pOutput;
761         INT i;
762         memcpy(tmp, pOutput, ret * sizeof(POINT16));
763         for(i = 0; i < ret; i++, pt++)
764         {
765             pt->x = tmp[i].x;
766             pt->y = tmp[i].y;
767         }
768         HeapFree( GetProcessHeap(), 0, tmp );
769     }
770     return ret;
771 }
772
773
774 /*****************************************************************************
775  *          DeviceCapabilitiesW        [WINSPOOL.@]
776  *
777  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
778  *
779  */
780 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
781                                WORD fwCapability, LPWSTR pOutput,
782                                const DEVMODEW *pDevMode)
783 {
784     LPDEVMODEA dmA = DEVMODEdupWtoA(GetProcessHeap(), pDevMode);
785     LPSTR pDeviceA = HEAP_strdupWtoA(GetProcessHeap(),0,pDevice);
786     LPSTR pPortA = HEAP_strdupWtoA(GetProcessHeap(),0,pPort);
787     INT ret;
788
789     if(pOutput && (fwCapability == DC_BINNAMES ||
790                    fwCapability == DC_FILEDEPENDENCIES ||
791                    fwCapability == DC_PAPERNAMES)) {
792       /* These need A -> W translation */
793         INT size = 0, i;
794         LPSTR pOutputA;
795         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
796                                   dmA);
797         if(ret == -1)
798             return ret;
799         switch(fwCapability) {
800         case DC_BINNAMES:
801             size = 24;
802             break;
803         case DC_PAPERNAMES:
804         case DC_FILEDEPENDENCIES:
805             size = 64;
806             break;
807         }
808         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
809         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
810                                   dmA);
811         for(i = 0; i < ret; i++)
812             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
813                                 pOutput + (i * size), size);
814         HeapFree(GetProcessHeap(), 0, pOutputA);
815     } else {
816         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
817                                   (LPSTR)pOutput, dmA);
818     }
819     HeapFree(GetProcessHeap(),0,pPortA);
820     HeapFree(GetProcessHeap(),0,pDeviceA);
821     HeapFree(GetProcessHeap(),0,dmA);
822     return ret;
823 }
824
825 /******************************************************************
826  *              DocumentPropertiesA   [WINSPOOL.@]
827  *
828  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
829  */
830 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
831                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
832                                 LPDEVMODEA pDevModeInput,DWORD fMode )
833 {
834     LPSTR lpName = pDeviceName;
835     LONG ret;
836
837     TRACE("(%p,%p,%s,%p,%p,%ld)\n",
838         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
839     );
840
841     if(!pDeviceName) {
842         LPCWSTR lpNameW = WINSPOOL_GetOpenedPrinter(hPrinter);
843         if(!lpNameW) {
844                 ERR("no name from hPrinter?\n");
845                 return -1;
846         }
847         lpName = HEAP_strdupWtoA(GetProcessHeap(),0,lpNameW);
848     }
849
850     if (!GDI_CallExtDeviceMode16)
851     {
852         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
853                                                          (LPCSTR)102 );
854         if (!GDI_CallExtDeviceMode16) {
855                 ERR("No CallExtDeviceMode16?\n");
856                 return -1;
857         }
858     }
859     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, "LPT1:",
860                                   pDevModeInput, NULL, fMode);
861
862     if(!pDeviceName)
863         HeapFree(GetProcessHeap(),0,lpName);
864     return ret;
865 }
866
867
868 /*****************************************************************************
869  *          DocumentPropertiesW (WINSPOOL.@)
870  *
871  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
872  */
873 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
874                                 LPWSTR pDeviceName,
875                                 LPDEVMODEW pDevModeOutput,
876                                 LPDEVMODEW pDevModeInput, DWORD fMode)
877 {
878
879     LPSTR pDeviceNameA = HEAP_strdupWtoA(GetProcessHeap(),0,pDeviceName);
880     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(GetProcessHeap(),pDevModeInput);
881     LPDEVMODEA pDevModeOutputA = NULL;
882     LONG ret;
883
884     TRACE("(%p,%p,%s,%p,%p,%ld)\n",
885           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
886           fMode);
887     if(pDevModeOutput) {
888         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
889         if(ret < 0) return ret;
890         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
891     }
892     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
893                               pDevModeInputA, fMode);
894     if(pDevModeOutput) {
895         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
896         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
897     }
898     if(fMode == 0 && ret > 0)
899         ret += (CCHDEVICENAME + CCHFORMNAME);
900     HeapFree(GetProcessHeap(),0,pDevModeInputA);
901     HeapFree(GetProcessHeap(),0,pDeviceNameA);
902     return ret;
903 }
904
905 /******************************************************************
906  *              OpenPrinterA        [WINSPOOL.@]
907  *
908  */
909 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
910                          LPPRINTER_DEFAULTSA pDefault)
911 {
912     UNICODE_STRING lpPrinterNameW;
913     UNICODE_STRING usBuffer;
914     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
915     PWSTR pwstrPrinterNameW;
916     BOOL ret;
917
918     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
919
920     if(pDefault) {
921         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
922         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
923         DefaultW.DesiredAccess = pDefault->DesiredAccess;
924         pDefaultW = &DefaultW;
925     }
926     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
927     if(pDefault) {
928         RtlFreeUnicodeString(&usBuffer);
929         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
930     }
931     RtlFreeUnicodeString(&lpPrinterNameW);
932     return ret;
933 }
934
935 /******************************************************************
936  *              OpenPrinterW        [WINSPOOL.@]
937  *
938  */
939 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter,
940                          LPPRINTER_DEFAULTSW pDefault)
941 {
942     HKEY hkeyPrinters, hkeyPrinter;
943
944     if (!lpPrinterName) {
945        FIXME("(printerName: NULL, pDefault %p Ret: False\n", pDefault);
946        SetLastError(ERROR_INVALID_PARAMETER);
947        return FALSE;
948     }
949
950     TRACE("(printerName: %s, pDefault %p)\n", debugstr_w(lpPrinterName),
951           pDefault);
952
953     /* Check Printer exists */
954     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
955        ERROR_SUCCESS) {
956         ERR("Can't create Printers key\n");
957         SetLastError(ERROR_FILE_NOT_FOUND); /* ?? */
958         return FALSE;
959     }
960
961     if(lpPrinterName[0] == '\0' || /* explicitly exclude "" */
962        RegOpenKeyW(hkeyPrinters, lpPrinterName, &hkeyPrinter)
963        != ERROR_SUCCESS) {
964         TRACE("Can't find printer %s in registry\n",
965               debugstr_w(lpPrinterName));
966         RegCloseKey(hkeyPrinters);
967         SetLastError(ERROR_INVALID_PRINTER_NAME);
968         return FALSE;
969     }
970     RegCloseKey(hkeyPrinter);
971     RegCloseKey(hkeyPrinters);
972
973     if(!phPrinter) /* This seems to be what win95 does anyway */
974         return TRUE;
975
976     /* Get the unique handle of the printer*/
977     *phPrinter = WINSPOOL_GetOpenedPrinterEntry( lpPrinterName );
978
979     if (pDefault != NULL)
980         FIXME("Not handling pDefault\n");
981
982     return TRUE;
983 }
984
985 /******************************************************************
986  *              AddMonitorA        [WINSPOOL.@]
987  *
988  */
989 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
990 {
991     FIXME("(%s,0x%08lx,%p), stub!\n", debugstr_a(pName), Level, pMonitors);
992     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
993     return FALSE;
994 }
995
996 /******************************************************************************
997  *              AddMonitorW        [WINSPOOL.@]
998  */
999 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1000 {
1001     FIXME("(%s,0x%08lx,%p), stub!\n",debugstr_w(pName), Level, pMonitors);
1002     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1003     return FALSE;
1004 }
1005
1006 /******************************************************************
1007  *              DeletePrinterDriverA        [WINSPOOL.@]
1008  *
1009  */
1010 BOOL WINAPI
1011 DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
1012 {
1013     FIXME("(%s,%s,%s):stub\n",debugstr_a(pName),debugstr_a(pEnvironment),
1014           debugstr_a(pDriverName));
1015     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1016     return FALSE;
1017 }
1018
1019 /******************************************************************
1020  *              DeletePrinterDriverW        [WINSPOOL.@]
1021  *
1022  */
1023 BOOL WINAPI
1024 DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
1025 {
1026     FIXME("(%s,%s,%s):stub\n",debugstr_w(pName),debugstr_w(pEnvironment),
1027           debugstr_w(pDriverName));
1028     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1029     return FALSE;
1030 }
1031
1032 /******************************************************************
1033  *              DeleteMonitorA        [WINSPOOL.@]
1034  *
1035  */
1036 BOOL WINAPI
1037 DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
1038 {
1039     FIXME("(%s,%s,%s):stub\n",debugstr_a(pName),debugstr_a(pEnvironment),
1040           debugstr_a(pMonitorName));
1041     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1042     return FALSE;
1043 }
1044
1045 /******************************************************************
1046  *              DeleteMonitorW        [WINSPOOL.@]
1047  *
1048  */
1049 BOOL WINAPI
1050 DeleteMonitorW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1051 {
1052     FIXME("(%s,%s,%s):stub\n",debugstr_w(pName),debugstr_w(pEnvironment),
1053           debugstr_w(pMonitorName));
1054     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1055     return FALSE;
1056 }
1057
1058 /******************************************************************
1059  *              DeletePortA        [WINSPOOL.@]
1060  *
1061  */
1062 BOOL WINAPI
1063 DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
1064 {
1065     FIXME("(%s,%p,%s):stub\n",debugstr_a(pName),hWnd,
1066           debugstr_a(pPortName));
1067     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1068     return FALSE;
1069 }
1070
1071 /******************************************************************
1072  *              DeletePortW        [WINSPOOL.@]
1073  *
1074  */
1075 BOOL WINAPI
1076 DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1077 {
1078     FIXME("(%s,%p,%s):stub\n",debugstr_w(pName),hWnd,
1079           debugstr_w(pPortName));
1080     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1081     return FALSE;
1082 }
1083
1084 /******************************************************************************
1085  *    SetPrinterW  [WINSPOOL.@]
1086  */
1087 BOOL WINAPI
1088 SetPrinterW(
1089   HANDLE  hPrinter,
1090   DWORD     Level,
1091   LPBYTE    pPrinter,
1092   DWORD     Command) {
1093
1094     FIXME("():stub\n");
1095     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1096     return FALSE;
1097 }
1098
1099 /******************************************************************************
1100  *    WritePrinter  [WINSPOOL.@]
1101  */
1102 BOOL WINAPI
1103 WritePrinter(
1104   HANDLE  hPrinter,
1105   LPVOID  pBuf,
1106   DWORD   cbBuf,
1107   LPDWORD pcWritten) {
1108
1109     FIXME("():stub\n");
1110     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1111     return FALSE;
1112 }
1113
1114 /*****************************************************************************
1115  *          AddFormA  [WINSPOOL.@]
1116  */
1117 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1118 {
1119     FIXME("(%p,%ld,%p): stub\n", hPrinter, Level, pForm);
1120     return 1;
1121 }
1122
1123 /*****************************************************************************
1124  *          AddFormW  [WINSPOOL.@]
1125  */
1126 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1127 {
1128     FIXME("(%p,%ld,%p): stub\n", hPrinter, Level, pForm);
1129     return 1;
1130 }
1131
1132 /*****************************************************************************
1133  *          AddJobA  [WINSPOOL.@]
1134  */
1135 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData,
1136                         DWORD cbBuf, LPDWORD pcbNeeded)
1137 {
1138     FIXME("(%p,%ld,%p,%ld,%p): stub\n", hPrinter, Level, pData, cbBuf,
1139           pcbNeeded);
1140     return 1;
1141 }
1142
1143 /*****************************************************************************
1144  *          AddJobW  [WINSPOOL.@]
1145  */
1146 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf,
1147                         LPDWORD pcbNeeded)
1148 {
1149     FIXME("(%p,%ld,%p,%ld,%p): stub\n", hPrinter, Level, pData, cbBuf,
1150           pcbNeeded);
1151     return 1;
1152 }
1153
1154 /*****************************************************************************
1155  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
1156  */
1157 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
1158                                         DWORD level,  LPBYTE Info,
1159                                         DWORD cbBuf, LPDWORD needed)
1160 {
1161     FIXME("(%s,%s,%ld,%p,0x%08lx): stub\n", debugstr_a(server), debugstr_a(env),
1162           level, Info, cbBuf);
1163     return 0;
1164 }
1165
1166 /*****************************************************************************
1167  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
1168  */
1169 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
1170                                         DWORD level,  LPBYTE Info,
1171                                         DWORD cbBuf, LPDWORD needed)
1172 {
1173     FIXME("(%s,%s,%ld,%p,0x%08lx): stub\n", debugstr_w(server), debugstr_w(env),
1174           level, Info, cbBuf);
1175     return 0;
1176 }
1177
1178 /*****************************************************************************
1179  *          WINSPOOL_OpenDriverReg [internal]
1180  *
1181  * opens the registry for the printer drivers depending on the given input
1182  * variable pEnvironment
1183  *
1184  * RETURNS:
1185  *    the opened hkey on success
1186  *    NULL on error
1187  */
1188 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode)
1189 {   
1190     static const WCHAR WinNTW[] = { 'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0 };
1191     static const WCHAR Win40W[] = { 'W','i','n','d','o','w','s',' ','4','.','0',0 };
1192     HKEY  retval;
1193     LPWSTR lpKey, buffer = NULL;
1194     LPCWSTR pEnvW;
1195
1196     TRACE("%s\n",
1197           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment));
1198
1199     if(pEnvironment) {
1200         if (unicode) {
1201             pEnvW = pEnvironment;
1202         } else {
1203             INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
1204             buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1205             if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
1206             pEnvW = buffer;
1207         }
1208     } else {
1209         OSVERSIONINFOW ver;
1210         ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
1211
1212         if(!GetVersionExW( &ver))
1213             return 0;
1214
1215         switch (ver.dwPlatformId) {
1216              case VER_PLATFORM_WIN32s:
1217                   ERR("win32 style printing used with 16 bits app, try specifying 'win95' Windows version\n");
1218                   return 0;
1219              case VER_PLATFORM_WIN32_NT:
1220                   pEnvW = WinNTW;
1221                   break;
1222              default:
1223                   pEnvW = Win40W;
1224                   break;
1225         }
1226         TRACE("set environment to %s\n", debugstr_w(pEnvW));
1227     }
1228
1229     lpKey = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1230                        (strlenW(pEnvW) + strlenW(DriversW) + 1) * sizeof(WCHAR));
1231     wsprintfW( lpKey, DriversW, pEnvW);
1232
1233     TRACE("%s\n", debugstr_w(lpKey));
1234
1235     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, lpKey, &retval) != ERROR_SUCCESS)
1236        retval = 0;
1237
1238     HeapFree( GetProcessHeap(), 0, buffer);
1239     HeapFree( GetProcessHeap(), 0, lpKey);
1240
1241     return retval;
1242 }
1243
1244 /*****************************************************************************
1245  *          AddPrinterW  [WINSPOOL.@]
1246  */
1247 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
1248 {
1249     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
1250     LPDEVMODEA dmA;
1251     LPDEVMODEW dmW;
1252     HANDLE retval;
1253     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
1254     LONG size;
1255
1256     TRACE("(%s,%ld,%p)\n", debugstr_w(pName), Level, pPrinter);
1257
1258     if(pName != NULL) {
1259         ERR("pName = %s - unsupported\n", debugstr_w(pName));
1260         SetLastError(ERROR_INVALID_PARAMETER);
1261         return 0;
1262     }
1263     if(Level != 2) {
1264         ERR("Level = %ld, unsupported!\n", Level);
1265         SetLastError(ERROR_INVALID_LEVEL);
1266         return 0;
1267     }
1268     if (strlenW(pi->pPrinterName) >= CCHDEVICENAME) {
1269         ERR("Printername %s must not exceed length of DEVMODE.dmDeviceName !\n",
1270                 debugstr_w(pi->pPrinterName)
1271         );
1272         SetLastError(ERROR_INVALID_LEVEL);
1273         return 0;
1274     }
1275     if(!pPrinter) {
1276         SetLastError(ERROR_INVALID_PARAMETER);
1277         return 0;
1278     }
1279     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
1280        ERROR_SUCCESS) {
1281         ERR("Can't create Printers key\n");
1282         return 0;
1283     }
1284     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
1285         if (!RegQueryValueA(hkeyPrinter,"Attributes",NULL,NULL)) {
1286             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
1287             RegCloseKey(hkeyPrinter);
1288             RegCloseKey(hkeyPrinters);
1289             return 0;
1290         }
1291         RegCloseKey(hkeyPrinter);
1292     }
1293     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
1294     if(!hkeyDrivers) {
1295         ERR("Can't create Drivers key\n");
1296         RegCloseKey(hkeyPrinters);
1297         return 0;
1298     }
1299     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
1300        ERROR_SUCCESS) {
1301         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
1302         RegCloseKey(hkeyPrinters);
1303         RegCloseKey(hkeyDrivers);
1304         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
1305         return 0;
1306     }
1307     RegCloseKey(hkeyDriver);
1308     RegCloseKey(hkeyDrivers);
1309
1310     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
1311         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
1312         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
1313         RegCloseKey(hkeyPrinters);
1314         return 0;
1315     }
1316
1317     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
1318        ERROR_SUCCESS) {
1319         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
1320         SetLastError(ERROR_INVALID_PRINTER_NAME);
1321         RegCloseKey(hkeyPrinters);
1322         return 0;
1323     }
1324     RegSetValueExA(hkeyPrinter, "Attributes", 0, REG_DWORD,
1325                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
1326     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
1327
1328     /* See if we can load the driver.  We may need the devmode structure anyway
1329      *
1330      * FIXME:
1331      * Note that DocumentPropertiesW will briefly try to open the printer we
1332      * just create to find a DEVMODEA struct (it will use the WINEPS default
1333      * one in case it is not there, so we are ok).
1334      */
1335     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
1336
1337     if(size < 0) {
1338         FIXME("DocumentPropertiesW on printer '%s' fails\n", debugstr_w(pi->pPrinterName));
1339         size = sizeof(DEVMODEW);
1340     }
1341     if(pi->pDevMode)
1342         dmW = pi->pDevMode;
1343     else 
1344     {
1345             dmW = HeapAlloc(GetProcessHeap(), 0, size);
1346         ZeroMemory(dmW,size);
1347             dmW->dmSize = size;
1348             if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER)) 
1349         {
1350                 WARN("DocumentPropertiesW on printer '%s' failed!\n", debugstr_w(pi->pPrinterName));
1351             HeapFree(GetProcessHeap(),0,dmW);
1352             dmW=NULL;
1353             }
1354         else
1355         {
1356                 /* set devmode to printer name */
1357                 strcpyW(dmW->dmDeviceName,pi->pPrinterName);
1358         }
1359     }
1360
1361     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
1362        and we support these drivers.  NT writes DEVMODEW so somehow
1363        we'll need to distinguish between these when we support NT
1364        drivers */
1365     if (dmW)
1366     {
1367         dmA = DEVMODEdupWtoA(GetProcessHeap(), dmW);
1368         RegSetValueExA(hkeyPrinter, "Default DevMode", 0, REG_BINARY, 
1369                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
1370         HeapFree(GetProcessHeap(), 0, dmA);
1371         if(!pi->pDevMode)
1372             HeapFree(GetProcessHeap(), 0, dmW);
1373     }
1374     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
1375     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
1376     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
1377     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
1378
1379     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
1380     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
1381     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
1382     RegSetValueExA(hkeyPrinter, "Priority", 0, REG_DWORD,
1383                    (LPBYTE)&pi->Priority, sizeof(DWORD));
1384     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
1385     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
1386     RegSetValueExA(hkeyPrinter, "StartTime", 0, REG_DWORD,
1387                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
1388     RegSetValueExA(hkeyPrinter, "Status", 0, REG_DWORD,
1389                    (LPBYTE)&pi->Status, sizeof(DWORD));
1390     RegSetValueExA(hkeyPrinter, "UntilTime", 0, REG_DWORD,
1391                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
1392
1393     RegCloseKey(hkeyPrinter);
1394     RegCloseKey(hkeyPrinters);
1395     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
1396         ERR("OpenPrinter failing\n");
1397         return 0;
1398     }
1399     return retval;
1400 }
1401
1402 /*****************************************************************************
1403  *          AddPrinterA  [WINSPOOL.@]
1404  */
1405 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
1406 {
1407     UNICODE_STRING pNameW;
1408     PWSTR pwstrNameW;
1409     PRINTER_INFO_2W *piW;
1410     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
1411     HANDLE ret;
1412
1413     TRACE("(%s,%ld,%p): stub\n", debugstr_a(pName), Level, pPrinter);
1414     if(Level != 2) {
1415         ERR("Level = %ld, unsupported!\n", Level);
1416         SetLastError(ERROR_INVALID_LEVEL);
1417         return 0;
1418     }
1419     pwstrNameW = asciitounicode(&pNameW,pName);
1420     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
1421
1422     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
1423
1424     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
1425     RtlFreeUnicodeString(&pNameW);
1426     return ret;
1427 }
1428
1429
1430 /*****************************************************************************
1431  *          ClosePrinter  [WINSPOOL.@]
1432  */
1433 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
1434 {
1435     int i = (int)hPrinter;
1436
1437     TRACE("Handle %p\n", hPrinter);
1438
1439     if ((i <= 0) || (i > nb_printers)) return FALSE;
1440     HeapFree( GetProcessHeap(), 0, printer_array[i - 1] );
1441     printer_array[i - 1] = NULL;
1442     return TRUE;
1443 }
1444
1445 /*****************************************************************************
1446  *          DeleteFormA  [WINSPOOL.@]
1447  */
1448 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
1449 {
1450     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
1451     return 1;
1452 }
1453
1454 /*****************************************************************************
1455  *          DeleteFormW  [WINSPOOL.@]
1456  */
1457 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
1458 {
1459     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
1460     return 1;
1461 }
1462
1463 /*****************************************************************************
1464  *   WINSPOOL_SHRegDeleteKey
1465  *
1466  *   Recursively delete subkeys.
1467  *   Cut & paste from shlwapi.
1468  * 
1469  */
1470 static DWORD WINSPOOL_SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey)
1471 {
1472   DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
1473   WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
1474   HKEY hSubKey = 0;
1475
1476   dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1477   if(!dwRet)
1478   {
1479     /* Find how many subkeys there are */
1480     dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1481                              &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
1482     if(!dwRet)
1483     {
1484       dwMaxSubkeyLen++;
1485       if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
1486         /* Name too big: alloc a buffer for it */
1487         lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
1488
1489       if(!lpszName)
1490         dwRet = ERROR_NOT_ENOUGH_MEMORY;
1491       else
1492       {
1493         /* Recursively delete all the subkeys */
1494         for(i = 0; i < dwKeyCount && !dwRet; i++)
1495         {
1496           dwSize = dwMaxSubkeyLen;
1497           dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL);
1498           if(!dwRet)
1499             dwRet = WINSPOOL_SHDeleteKeyW(hSubKey, lpszName);
1500         }
1501
1502         if (lpszName != szNameBuf)
1503           HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
1504       }
1505     }
1506
1507     RegCloseKey(hSubKey);
1508     if(!dwRet)
1509       dwRet = RegDeleteKeyW(hKey, lpszSubKey);
1510   }
1511   return dwRet;
1512 }
1513
1514 /*****************************************************************************
1515  *          DeletePrinter  [WINSPOOL.@]
1516  */
1517 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
1518 {
1519     LPCWSTR lpNameW = WINSPOOL_GetOpenedPrinter(hPrinter);
1520     HKEY hkeyPrinters, hkey;
1521
1522     if(!lpNameW) return FALSE;
1523     if(RegOpenKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) == ERROR_SUCCESS) {
1524         WINSPOOL_SHDeleteKeyW(hkeyPrinters, lpNameW);
1525         RegCloseKey(hkeyPrinters);
1526     }
1527     WriteProfileStringW(devicesW, lpNameW, NULL);
1528     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
1529         RegDeleteValueW(hkey, lpNameW);
1530         RegCloseKey(hkey);
1531     }
1532     return TRUE;
1533 }
1534
1535 /*****************************************************************************
1536  *          SetPrinterA  [WINSPOOL.@]
1537  */
1538 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
1539                            DWORD Command)
1540 {
1541     FIXME("(%p,%ld,%p,%ld): stub\n",hPrinter,Level,pPrinter,Command);
1542     return FALSE;
1543 }
1544
1545 /*****************************************************************************
1546  *          SetJobA  [WINSPOOL.@]
1547  */
1548 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
1549                        LPBYTE pJob, DWORD Command)
1550 {
1551     FIXME("(%p,%ld,%ld,%p,%ld): stub\n",hPrinter,JobId,Level,pJob,
1552          Command);
1553     return FALSE;
1554 }
1555
1556 /*****************************************************************************
1557  *          SetJobW  [WINSPOOL.@]
1558  */
1559 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
1560                        LPBYTE pJob, DWORD Command)
1561 {
1562     FIXME("(%p,%ld,%ld,%p,%ld): stub\n",hPrinter,JobId,Level,pJob,
1563          Command);
1564     return FALSE;
1565 }
1566
1567 /*****************************************************************************
1568  *          EndDocPrinter  [WINSPOOL.@]
1569  */
1570 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
1571 {
1572     FIXME("(hPrinter=%p): stub\n", hPrinter);
1573     return FALSE;
1574 }
1575
1576 /*****************************************************************************
1577  *          EndPagePrinter  [WINSPOOL.@]
1578  */
1579 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
1580 {
1581     FIXME("(hPrinter=%p): stub\n", hPrinter);
1582     return FALSE;
1583 }
1584
1585 /*****************************************************************************
1586  *          StartDocPrinterA  [WINSPOOL.@]
1587  */
1588 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
1589 {
1590     FIXME("(hPrinter=%p, Level=0x%lx, pDocInfo=%p): stub\n", hPrinter, Level, pDocInfo);
1591     return FALSE;
1592 }
1593
1594 /*****************************************************************************
1595  *          StartDocPrinterW  [WINSPOOL.@]
1596  */
1597 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
1598 {
1599     FIXME("(hPrinter=%p, Level=0x%lx, pDocInfo=%p): stub\n", hPrinter, Level, pDocInfo);
1600     return FALSE;
1601 }
1602
1603 /*****************************************************************************
1604  *          StartPagePrinter  [WINSPOOL.@]
1605  */
1606 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
1607 {
1608     FIXME("(hPrinter=%p): stub\n", hPrinter);
1609     return FALSE;
1610 }
1611
1612 /*****************************************************************************
1613  *          GetFormA  [WINSPOOL.@]
1614  */
1615 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
1616                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
1617 {
1618     FIXME("(%p,%s,%ld,%p,%ld,%p): stub\n",hPrinter,pFormName,
1619          Level,pForm,cbBuf,pcbNeeded);
1620     return FALSE;
1621 }
1622
1623 /*****************************************************************************
1624  *          GetFormW  [WINSPOOL.@]
1625  */
1626 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
1627                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
1628 {
1629     FIXME("(%p,%s,%ld,%p,%ld,%p): stub\n",hPrinter,
1630           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
1631     return FALSE;
1632 }
1633
1634 /*****************************************************************************
1635  *          SetFormA  [WINSPOOL.@]
1636  */
1637 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
1638                         LPBYTE pForm)
1639 {
1640     FIXME("(%p,%s,%ld,%p): stub\n",hPrinter,pFormName,Level,pForm);
1641     return FALSE;
1642 }
1643
1644 /*****************************************************************************
1645  *          SetFormW  [WINSPOOL.@]
1646  */
1647 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
1648                         LPBYTE pForm)
1649 {
1650     FIXME("(%p,%p,%ld,%p): stub\n",hPrinter,pFormName,Level,pForm);
1651     return FALSE;
1652 }
1653
1654 /*****************************************************************************
1655  *          ReadPrinter  [WINSPOOL.@]
1656  */
1657 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
1658                            LPDWORD pNoBytesRead)
1659 {
1660     FIXME("(%p,%p,%ld,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
1661     return FALSE;
1662 }
1663
1664 /*****************************************************************************
1665  *          ResetPrinterA  [WINSPOOL.@]
1666  */
1667 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
1668 {
1669     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
1670     return FALSE;
1671 }
1672
1673 /*****************************************************************************
1674  *          ResetPrinterW  [WINSPOOL.@]
1675  */
1676 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
1677 {
1678     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
1679     return FALSE;
1680 }
1681
1682 /*****************************************************************************
1683  *    WINSPOOL_GetDWORDFromReg
1684  *
1685  * Return DWORD associated with ValueName from hkey.
1686  */
1687 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
1688 {
1689     DWORD sz = sizeof(DWORD), type, value = 0;
1690     LONG ret;
1691
1692     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
1693
1694     if(ret != ERROR_SUCCESS) {
1695         WARN("Got ret = %ld on name %s\n", ret, ValueName);
1696         return 0;
1697     }
1698     if(type != REG_DWORD) {
1699         ERR("Got type %ld\n", type);
1700         return 0;
1701     }
1702     return value;
1703 }
1704
1705 /*****************************************************************************
1706  *    WINSPOOL_GetStringFromReg
1707  *
1708  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
1709  * String is stored either as unicode or ascii.
1710  * Bit of a hack here to get the ValueName if we want ascii.
1711  */
1712 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
1713                                       DWORD buflen, DWORD *needed,
1714                                       BOOL unicode)
1715 {
1716     DWORD sz = buflen, type;
1717     LONG ret;
1718
1719     if(unicode)
1720         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
1721     else {
1722         LPSTR ValueNameA = HEAP_strdupWtoA(GetProcessHeap(),0,ValueName);
1723         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
1724         HeapFree(GetProcessHeap(),0,ValueNameA);
1725     }
1726     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
1727         WARN("Got ret = %ld\n", ret);
1728         *needed = 0;
1729         return FALSE;
1730     }
1731     *needed = sz;
1732     return TRUE;
1733 }
1734
1735 /*****************************************************************************
1736  *    WINSPOOL_GetDefaultDevMode
1737  *
1738  * Get a default DevMode values for wineps.
1739  * FIXME - use ppd.
1740  */
1741
1742 static void WINSPOOL_GetDefaultDevMode(
1743         LPBYTE ptr,
1744         DWORD buflen, DWORD *needed,
1745         BOOL unicode)
1746 {
1747     DEVMODEA    dm;
1748
1749         /* fill default DEVMODE - should be read from ppd... */
1750         ZeroMemory( &dm, sizeof(dm) );
1751         strcpy(dm.dmDeviceName,"wineps.drv");
1752         dm.dmSpecVersion = DM_SPECVERSION;
1753         dm.dmDriverVersion = 1;
1754         dm.dmSize = sizeof(DEVMODEA);
1755         dm.dmDriverExtra = 0;
1756         dm.dmFields =
1757                 DM_ORIENTATION | DM_PAPERSIZE |
1758                 DM_PAPERLENGTH | DM_PAPERWIDTH |
1759                 DM_SCALE |
1760                 DM_COPIES |
1761                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
1762                 DM_YRESOLUTION | DM_TTOPTION;
1763
1764         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
1765         dm.u1.s1.dmPaperSize = DMPAPER_A4;
1766         dm.u1.s1.dmPaperLength = 2970;
1767         dm.u1.s1.dmPaperWidth = 2100;
1768
1769         dm.dmScale = 100;
1770         dm.dmCopies = 1;
1771         dm.dmDefaultSource = DMBIN_AUTO;
1772         dm.dmPrintQuality = DMRES_MEDIUM;
1773         /* dm.dmColor */
1774         /* dm.dmDuplex */
1775         dm.dmYResolution = 300; /* 300dpi */
1776         dm.dmTTOption = DMTT_BITMAP;
1777         /* dm.dmCollate */
1778         /* dm.dmFormName */
1779         /* dm.dmLogPixels */
1780         /* dm.dmBitsPerPel */
1781         /* dm.dmPelsWidth */
1782         /* dm.dmPelsHeight */
1783         /* dm.dmDisplayFlags */
1784         /* dm.dmDisplayFrequency */
1785         /* dm.dmICMMethod */
1786         /* dm.dmICMIntent */
1787         /* dm.dmMediaType */
1788         /* dm.dmDitherType */
1789         /* dm.dmReserved1 */
1790         /* dm.dmReserved2 */
1791         /* dm.dmPanningWidth */
1792         /* dm.dmPanningHeight */
1793
1794     if(unicode) {
1795         if(buflen >= sizeof(DEVMODEW)) {
1796             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
1797             memcpy(ptr, pdmW, sizeof(DEVMODEW));
1798             HeapFree(GetProcessHeap(),0,pdmW);
1799         }
1800         *needed = sizeof(DEVMODEW);
1801     }
1802     else
1803     {
1804         if(buflen >= sizeof(DEVMODEA)) {
1805             memcpy(ptr, &dm, sizeof(DEVMODEA));
1806         }
1807         *needed = sizeof(DEVMODEA);
1808     }
1809 }
1810
1811 /*****************************************************************************
1812  *    WINSPOOL_GetDevModeFromReg
1813  *
1814  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
1815  * DevMode is stored either as unicode or ascii.
1816  */
1817 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
1818                                        LPBYTE ptr,
1819                                        DWORD buflen, DWORD *needed,
1820                                        BOOL unicode)
1821 {
1822     DWORD sz = buflen, type;
1823     LONG ret;
1824
1825     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
1826     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
1827     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
1828     if (sz < sizeof(DEVMODEA))
1829     {
1830         TRACE("corrupted registry for %s ( size %ld)\n",debugstr_w(ValueName),sz);
1831         return FALSE;
1832     }
1833     /* ensures that dmSize is not erratically bogus if registry is invalid */
1834     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
1835         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
1836     if(unicode) {
1837         sz += (CCHDEVICENAME + CCHFORMNAME);
1838         if(buflen >= sz) {
1839             DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
1840             memcpy(ptr, dmW, sz);
1841             HeapFree(GetProcessHeap(),0,dmW);
1842         }
1843     }
1844     *needed = sz;
1845     return TRUE;
1846 }
1847
1848 /*********************************************************************
1849  *    WINSPOOL_GetPrinter_2
1850  *
1851  * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
1852  * The strings are either stored as unicode or ascii.
1853  */
1854 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
1855                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
1856                                   BOOL unicode)
1857 {
1858     DWORD size, left = cbBuf;
1859     BOOL space = (cbBuf > 0);
1860     LPBYTE ptr = buf;
1861
1862     *pcbNeeded = 0;
1863
1864     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
1865                                  unicode)) {
1866         if(space && size <= left) {
1867             pi2->pPrinterName = (LPWSTR)ptr;
1868             ptr += size;
1869             left -= size;
1870         } else
1871             space = FALSE;
1872         *pcbNeeded += size;
1873     }
1874     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
1875                                  unicode)) {
1876         if(space && size <= left) {
1877             pi2->pShareName = (LPWSTR)ptr;
1878             ptr += size;
1879             left -= size;
1880         } else
1881             space = FALSE;
1882         *pcbNeeded += size;
1883     }
1884     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
1885                                  unicode)) {
1886         if(space && size <= left) {
1887             pi2->pPortName = (LPWSTR)ptr;
1888             ptr += size;
1889             left -= size;
1890         } else
1891             space = FALSE;
1892         *pcbNeeded += size;
1893     }
1894     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
1895                                  &size, unicode)) {
1896         if(space && size <= left) {
1897             pi2->pDriverName = (LPWSTR)ptr;
1898             ptr += size;
1899             left -= size;
1900         } else
1901             space = FALSE;
1902         *pcbNeeded += size;
1903     }
1904     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
1905                                  unicode)) {
1906         if(space && size <= left) {
1907             pi2->pComment = (LPWSTR)ptr;
1908             ptr += size;
1909             left -= size;
1910         } else
1911             space = FALSE;
1912         *pcbNeeded += size;
1913     }
1914     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
1915                                  unicode)) {
1916         if(space && size <= left) {
1917             pi2->pLocation = (LPWSTR)ptr;
1918             ptr += size;
1919             left -= size;
1920         } else
1921             space = FALSE;
1922         *pcbNeeded += size;
1923     }
1924     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
1925                                   &size, unicode)) {
1926         if(space && size <= left) {
1927             pi2->pDevMode = (LPDEVMODEW)ptr;
1928             ptr += size;
1929             left -= size;
1930         } else
1931             space = FALSE;
1932         *pcbNeeded += size;
1933     }
1934     else
1935     {
1936         WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
1937         if(space && size <= left) {
1938             pi2->pDevMode = (LPDEVMODEW)ptr;
1939             ptr += size;
1940             left -= size;
1941         } else
1942             space = FALSE;
1943         *pcbNeeded += size;
1944     }
1945     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
1946                                  &size, unicode)) {
1947         if(space && size <= left) {
1948             pi2->pSepFile = (LPWSTR)ptr;
1949             ptr += size;
1950             left -= size;
1951         } else
1952             space = FALSE;
1953         *pcbNeeded += size;
1954     }
1955     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
1956                                  &size, unicode)) {
1957         if(space && size <= left) {
1958             pi2->pPrintProcessor = (LPWSTR)ptr;
1959             ptr += size;
1960             left -= size;
1961         } else
1962             space = FALSE;
1963         *pcbNeeded += size;
1964     }
1965     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
1966                                  &size, unicode)) {
1967         if(space && size <= left) {
1968             pi2->pDatatype = (LPWSTR)ptr;
1969             ptr += size;
1970             left -= size;
1971         } else
1972             space = FALSE;
1973         *pcbNeeded += size;
1974     }
1975     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
1976                                  &size, unicode)) {
1977         if(space && size <= left) {
1978             pi2->pParameters = (LPWSTR)ptr;
1979             ptr += size;
1980             left -= size;
1981         } else
1982             space = FALSE;
1983         *pcbNeeded += size;
1984     }
1985     if(pi2) {
1986         pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
1987         pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
1988         pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
1989                                                         "Default Priority");
1990         pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
1991         pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
1992     }
1993
1994     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
1995         memset(pi2, 0, sizeof(*pi2));
1996
1997     return space;
1998 }
1999
2000 /*********************************************************************
2001  *    WINSPOOL_GetPrinter_4
2002  *
2003  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
2004  */
2005 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
2006                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2007                                   BOOL unicode)
2008 {
2009     DWORD size, left = cbBuf;
2010     BOOL space = (cbBuf > 0);
2011     LPBYTE ptr = buf;
2012
2013     *pcbNeeded = 0;
2014
2015     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2016                                  unicode)) {
2017         if(space && size <= left) {
2018             pi4->pPrinterName = (LPWSTR)ptr;
2019             ptr += size;
2020             left -= size;
2021         } else
2022             space = FALSE;
2023         *pcbNeeded += size;
2024     }
2025     if(pi4) {
2026         pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
2027     }
2028
2029     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
2030         memset(pi4, 0, sizeof(*pi4));
2031
2032     return space;
2033 }
2034
2035 /*********************************************************************
2036  *    WINSPOOL_GetPrinter_5
2037  *
2038  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
2039  */
2040 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
2041                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2042                                   BOOL unicode)
2043 {
2044     DWORD size, left = cbBuf;
2045     BOOL space = (cbBuf > 0);
2046     LPBYTE ptr = buf;
2047
2048     *pcbNeeded = 0;
2049
2050     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2051                                  unicode)) {
2052         if(space && size <= left) {
2053             pi5->pPrinterName = (LPWSTR)ptr;
2054             ptr += size;
2055             left -= size;
2056         } else
2057             space = FALSE;
2058         *pcbNeeded += size;
2059     }
2060     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
2061                                  unicode)) {
2062         if(space && size <= left) {
2063             pi5->pPortName = (LPWSTR)ptr;
2064             ptr += size;
2065             left -= size;
2066         } else
2067             space = FALSE;
2068         *pcbNeeded += size;
2069     }
2070     if(pi5) {
2071         pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
2072         pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
2073                                                                 "dnsTimeout");
2074         pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
2075                                                                  "txTimeout");
2076     }
2077
2078     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
2079         memset(pi5, 0, sizeof(*pi5));
2080
2081     return space;
2082 }
2083
2084 /*****************************************************************************
2085  *          WINSPOOL_GetPrinter
2086  *
2087  *    Implementation of GetPrinterA|W.  Relies on PRINTER_INFO_*W being
2088  *    essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
2089  *    just a collection of pointers to strings.
2090  */
2091 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2092                                 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
2093 {
2094     LPCWSTR name;
2095     DWORD size, needed = 0;
2096     LPBYTE ptr = NULL;
2097     HKEY hkeyPrinter, hkeyPrinters;
2098     BOOL ret;
2099
2100     TRACE("(%p,%ld,%p,%ld,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
2101
2102     if (!(name = WINSPOOL_GetOpenedPrinter(hPrinter))) return FALSE;
2103
2104     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
2105        ERROR_SUCCESS) {
2106         ERR("Can't create Printers key\n");
2107         return FALSE;
2108     }
2109     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
2110     {
2111         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
2112         RegCloseKey(hkeyPrinters);
2113         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
2114         return FALSE;
2115     }
2116
2117     switch(Level) {
2118     case 2:
2119       {
2120         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
2121
2122         size = sizeof(PRINTER_INFO_2W);
2123         if(size <= cbBuf) {
2124             ptr = pPrinter + size;
2125             cbBuf -= size;
2126             memset(pPrinter, 0, size);
2127         } else {
2128             pi2 = NULL;
2129             cbBuf = 0;
2130         }
2131         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
2132                                     unicode);
2133         needed += size;
2134         break;
2135       }
2136
2137     case 4:
2138       {
2139         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
2140
2141         size = sizeof(PRINTER_INFO_4W);
2142         if(size <= cbBuf) {
2143             ptr = pPrinter + size;
2144             cbBuf -= size;
2145             memset(pPrinter, 0, size);
2146         } else {
2147             pi4 = NULL;
2148             cbBuf = 0;
2149         }
2150         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
2151                                     unicode);
2152         needed += size;
2153         break;
2154       }
2155
2156
2157     case 5:
2158       {
2159         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
2160
2161         size = sizeof(PRINTER_INFO_5W);
2162         if(size <= cbBuf) {
2163             ptr = pPrinter + size;
2164             cbBuf -= size;
2165             memset(pPrinter, 0, size);
2166         } else {
2167             pi5 = NULL;
2168             cbBuf = 0;
2169         }
2170
2171         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
2172                                     unicode);
2173         needed += size;
2174         break;
2175       }
2176
2177     default:
2178         FIXME("Unimplemented level %ld\n", Level);
2179         SetLastError(ERROR_INVALID_LEVEL);
2180         RegCloseKey(hkeyPrinters);
2181         RegCloseKey(hkeyPrinter);
2182         return FALSE;
2183     }
2184
2185     RegCloseKey(hkeyPrinter);
2186     RegCloseKey(hkeyPrinters);
2187
2188     TRACE("returning %d needed = %ld\n", ret, needed);
2189     if(pcbNeeded) *pcbNeeded = needed;
2190     if(!ret)
2191         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2192     return ret;
2193 }
2194
2195 /*****************************************************************************
2196  *          GetPrinterW  [WINSPOOL.@]
2197  */
2198 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2199                         DWORD cbBuf, LPDWORD pcbNeeded)
2200 {
2201     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
2202                                TRUE);
2203 }
2204
2205 /*****************************************************************************
2206  *          GetPrinterA  [WINSPOOL.@]
2207  */
2208 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2209                     DWORD cbBuf, LPDWORD pcbNeeded)
2210 {
2211     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
2212                                FALSE);
2213 }
2214
2215 /*****************************************************************************
2216  *          WINSPOOL_EnumPrinters
2217  *
2218  *    Implementation of EnumPrintersA|W
2219  */
2220 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
2221                                   DWORD dwLevel, LPBYTE lpbPrinters,
2222                                   DWORD cbBuf, LPDWORD lpdwNeeded,
2223                                   LPDWORD lpdwReturned, BOOL unicode)
2224
2225 {
2226     HKEY hkeyPrinters, hkeyPrinter;
2227     WCHAR PrinterName[255];
2228     DWORD needed = 0, number = 0;
2229     DWORD used, i, left;
2230     PBYTE pi, buf;
2231
2232     if(lpbPrinters)
2233         memset(lpbPrinters, 0, cbBuf);
2234     if(lpdwReturned)
2235         *lpdwReturned = 0;
2236     if(lpdwNeeded)
2237         *lpdwNeeded = 0;
2238
2239     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
2240     if(dwType == PRINTER_ENUM_DEFAULT)
2241         return TRUE;
2242
2243     if (dwType & PRINTER_ENUM_CONNECTIONS) {
2244         FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
2245         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
2246         if(!dwType) return TRUE;
2247     }
2248
2249     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
2250         FIXME("dwType = %08lx\n", dwType);
2251         SetLastError(ERROR_INVALID_FLAGS);
2252         return FALSE;
2253     }
2254
2255     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
2256        ERROR_SUCCESS) {
2257         ERR("Can't create Printers key\n");
2258         return FALSE;
2259     }
2260
2261     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
2262                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
2263         RegCloseKey(hkeyPrinters);
2264         ERR("Can't query Printers key\n");
2265         return FALSE;
2266     }
2267     TRACE("Found %ld printers\n", number);
2268
2269     switch(dwLevel) {
2270     case 1:
2271         RegCloseKey(hkeyPrinters);
2272         if (lpdwReturned)
2273             *lpdwReturned = number;
2274         return TRUE;
2275
2276     case 2:
2277         used = number * sizeof(PRINTER_INFO_2W);
2278         break;
2279     case 4:
2280         used = number * sizeof(PRINTER_INFO_4W);
2281         break;
2282     case 5:
2283         used = number * sizeof(PRINTER_INFO_5W);
2284         break;
2285
2286     default:
2287         SetLastError(ERROR_INVALID_LEVEL);
2288         RegCloseKey(hkeyPrinters);
2289         return FALSE;
2290     }
2291     pi = (used <= cbBuf) ? lpbPrinters : NULL;
2292
2293     for(i = 0; i < number; i++) {
2294         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) !=
2295            ERROR_SUCCESS) {
2296             ERR("Can't enum key number %ld\n", i);
2297             RegCloseKey(hkeyPrinters);
2298             return FALSE;
2299         }
2300         TRACE("Printer %ld is %s\n", i, debugstr_w(PrinterName));
2301         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
2302            ERROR_SUCCESS) {
2303             ERR("Can't open key %s\n", debugstr_w(PrinterName));
2304             RegCloseKey(hkeyPrinters);
2305             return FALSE;
2306         }
2307
2308         if(cbBuf > used) {
2309             buf = lpbPrinters + used;
2310             left = cbBuf - used;
2311         } else {
2312             buf = NULL;
2313             left = 0;
2314         }
2315
2316         switch(dwLevel) {
2317         case 2:
2318             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
2319                                   left, &needed, unicode);
2320             used += needed;
2321             if(pi) pi += sizeof(PRINTER_INFO_2W);
2322             break;
2323         case 4:
2324             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
2325                                   left, &needed, unicode);
2326             used += needed;
2327             if(pi) pi += sizeof(PRINTER_INFO_4W);
2328             break;
2329         case 5:
2330             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
2331                                   left, &needed, unicode);
2332             used += needed;
2333             if(pi) pi += sizeof(PRINTER_INFO_5W);
2334             break;
2335         default:
2336             ERR("Shouldn't be here!\n");
2337             RegCloseKey(hkeyPrinter);
2338             RegCloseKey(hkeyPrinters);
2339             return FALSE;
2340         }
2341         RegCloseKey(hkeyPrinter);
2342     }
2343     RegCloseKey(hkeyPrinters);
2344
2345     if(lpdwNeeded)
2346         *lpdwNeeded = used;
2347
2348     if(used > cbBuf) {
2349         if(lpbPrinters)
2350             memset(lpbPrinters, 0, cbBuf);
2351         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2352         return FALSE;
2353     }
2354     if(lpdwReturned)
2355         *lpdwReturned = number;
2356     SetLastError(ERROR_SUCCESS);
2357     return TRUE;
2358 }
2359
2360
2361 /******************************************************************
2362  *              EnumPrintersW        [WINSPOOL.@]
2363  *
2364  *    Enumerates the available printers, print servers and print
2365  *    providers, depending on the specified flags, name and level.
2366  *
2367  * RETURNS:
2368  *
2369  *    If level is set to 1:
2370  *      Not implemented yet!
2371  *      Returns TRUE with an empty list.
2372  *
2373  *    If level is set to 2:
2374  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
2375  *      Returns an array of PRINTER_INFO_2 data structures in the
2376  *      lpbPrinters buffer. Note that according to MSDN also an
2377  *      OpenPrinter should be performed on every remote printer.
2378  *
2379  *    If level is set to 4 (officially WinNT only):
2380  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
2381  *      Fast: Only the registry is queried to retrieve printer names,
2382  *      no connection to the driver is made.
2383  *      Returns an array of PRINTER_INFO_4 data structures in the
2384  *      lpbPrinters buffer.
2385  *
2386  *    If level is set to 5 (officially WinNT4/Win9x only):
2387  *      Fast: Only the registry is queried to retrieve printer names,
2388  *      no connection to the driver is made.
2389  *      Returns an array of PRINTER_INFO_5 data structures in the
2390  *      lpbPrinters buffer.
2391  *
2392  *    If level set to 3 or 6+:
2393  *          returns zero (failure!)
2394  *
2395  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
2396  *    for information.
2397  *
2398  * BUGS:
2399  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
2400  *    - Only levels 2, 4 and 5 are implemented at the moment.
2401  *    - 16-bit printer drivers are not enumerated.
2402  *    - Returned amount of bytes used/needed does not match the real Windoze
2403  *      implementation (as in this implementation, all strings are part
2404  *      of the buffer, whereas Win32 keeps them somewhere else)
2405  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
2406  *
2407  * NOTE:
2408  *    - In a regular Wine installation, no registry settings for printers
2409  *      exist, which makes this function return an empty list.
2410  */
2411 BOOL  WINAPI EnumPrintersW(
2412                 DWORD dwType,        /* [in] Types of print objects to enumerate */
2413                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
2414                 DWORD dwLevel,       /* [in] type of printer info structure */
2415                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
2416                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
2417                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
2418                 LPDWORD lpdwReturned /* [out] number of entries returned */
2419                 )
2420 {
2421     return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
2422                                  lpdwNeeded, lpdwReturned, TRUE);
2423 }
2424
2425 /******************************************************************
2426  *              EnumPrintersA        [WINSPOOL.@]
2427  *
2428  */
2429 BOOL WINAPI EnumPrintersA(DWORD dwType, LPSTR lpszName,
2430                           DWORD dwLevel, LPBYTE lpbPrinters,
2431                           DWORD cbBuf, LPDWORD lpdwNeeded,
2432                           LPDWORD lpdwReturned)
2433 {
2434     BOOL ret;
2435     UNICODE_STRING lpszNameW;
2436     PWSTR pwstrNameW;
2437     
2438     pwstrNameW = asciitounicode(&lpszNameW,lpszName);
2439     ret = WINSPOOL_EnumPrinters(dwType, pwstrNameW, dwLevel, lpbPrinters, cbBuf,
2440                                 lpdwNeeded, lpdwReturned, FALSE);
2441     RtlFreeUnicodeString(&lpszNameW);
2442     return ret;
2443 }
2444
2445 /*****************************************************************************
2446  *          WINSPOOL_GetDriverInfoFromReg [internal]
2447  *
2448  *    Enters the information from the registry into the DRIVER_INFO struct
2449  *
2450  * RETURNS
2451  *    zero if the printer driver does not exist in the registry
2452  *    (only if Level > 1) otherwise nonzero
2453  */
2454 static BOOL WINSPOOL_GetDriverInfoFromReg(
2455                             HKEY    hkeyDrivers,
2456                             LPWSTR  DriverName,
2457                             LPWSTR  pEnvironment,
2458                             DWORD   Level,
2459                             LPBYTE  ptr,            /* DRIVER_INFO */
2460                             LPBYTE  pDriverStrings, /* strings buffer */
2461                             DWORD   cbBuf,          /* size of string buffer */
2462                             LPDWORD pcbNeeded,      /* space needed for str. */
2463                             BOOL    unicode)        /* type of strings */
2464 {   DWORD  dw, size, tmp, type;
2465     HKEY   hkeyDriver;
2466     LPBYTE strPtr = pDriverStrings;
2467
2468     TRACE("%s,%s,%ld,%p,%p,%ld,%d\n",
2469           debugstr_w(DriverName), debugstr_w(pEnvironment),
2470           Level, ptr, pDriverStrings, cbBuf, unicode);
2471
2472     if(unicode) {
2473         *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
2474             if (*pcbNeeded <= cbBuf)
2475                strcpyW((LPWSTR)strPtr, DriverName);
2476     } else {
2477         *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0,
2478                                           NULL, NULL);
2479         if(*pcbNeeded <= cbBuf)
2480             WideCharToMultiByte(CP_ACP, 0, DriverName, -1, strPtr, *pcbNeeded,
2481                                 NULL, NULL);
2482     }
2483     if(Level == 1) {
2484        if(ptr)
2485           ((PDRIVER_INFO_1W) ptr)->pName = (LPWSTR) strPtr;
2486        return TRUE;
2487     } else {
2488        if(ptr)
2489           ((PDRIVER_INFO_3W) ptr)->pName = (LPWSTR) strPtr;
2490        strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
2491     }
2492
2493     if(!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
2494         ERR("Can't find driver '%s' in registry\n", debugstr_w(DriverName));
2495         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
2496         return FALSE;
2497     }
2498
2499     size = sizeof(dw);
2500     if(RegQueryValueExA(hkeyDriver, "Version", 0, &type, (PBYTE)&dw, &size) !=
2501         ERROR_SUCCESS)
2502          WARN("Can't get Version\n");
2503     else if(ptr)
2504          ((PDRIVER_INFO_3A) ptr)->cVersion = dw;
2505
2506     if(!pEnvironment)
2507         pEnvironment = (LPWSTR)DefaultEnvironmentW;
2508     if(unicode)
2509         size = (lstrlenW(pEnvironment) + 1) * sizeof(WCHAR);
2510     else
2511         size = WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1, NULL, 0,
2512                                    NULL, NULL);
2513     *pcbNeeded += size;
2514     if(*pcbNeeded <= cbBuf) {
2515         if(unicode)
2516             strcpyW((LPWSTR)strPtr, pEnvironment);
2517         else
2518             WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1, strPtr, size,
2519                                 NULL, NULL);
2520         if(ptr)
2521             ((PDRIVER_INFO_3W) ptr)->pEnvironment = (LPWSTR)strPtr;
2522         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
2523     }
2524
2525     if(WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, 0, &size,
2526                                  unicode)) {
2527         *pcbNeeded += size;
2528         if(*pcbNeeded <= cbBuf)
2529             WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, size, &tmp,
2530                                       unicode);
2531         if(ptr)
2532             ((PDRIVER_INFO_3W) ptr)->pDriverPath = (LPWSTR)strPtr;
2533         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
2534     }
2535
2536     if(WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, 0, &size,
2537                                  unicode)) {
2538         *pcbNeeded += size;
2539         if(*pcbNeeded <= cbBuf)
2540             WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, size,
2541                                       &tmp, unicode);
2542         if(ptr)
2543             ((PDRIVER_INFO_3W) ptr)->pDataFile = (LPWSTR)strPtr;
2544         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2545     }
2546
2547     if(WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
2548                                  0, &size, unicode)) {
2549         *pcbNeeded += size;
2550         if(*pcbNeeded <= cbBuf)
2551             WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
2552                                       size, &tmp, unicode);
2553         if(ptr)
2554             ((PDRIVER_INFO_3W) ptr)->pConfigFile = (LPWSTR)strPtr;
2555         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2556     }
2557
2558     if(Level == 2 ) {
2559         RegCloseKey(hkeyDriver);
2560         TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
2561         return TRUE;
2562     }
2563
2564     if(WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr, 0, &size,
2565                                  unicode)) {
2566         *pcbNeeded += size;
2567         if(*pcbNeeded <= cbBuf)
2568             WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr,
2569                                       size, &tmp, unicode);
2570         if(ptr)
2571             ((PDRIVER_INFO_3W) ptr)->pHelpFile = (LPWSTR)strPtr;
2572         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2573     }
2574
2575     if(WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr, 0,
2576                              &size, unicode)) {
2577         *pcbNeeded += size;
2578         if(*pcbNeeded <= cbBuf)
2579             WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr,
2580                                       size, &tmp, unicode);
2581         if(ptr)
2582             ((PDRIVER_INFO_3W) ptr)->pDependentFiles = (LPWSTR)strPtr;
2583         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2584     }
2585
2586     if(WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size,
2587                                  unicode)) {
2588         *pcbNeeded += size;
2589         if(*pcbNeeded <= cbBuf)
2590             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr,
2591                                       size, &tmp, unicode);
2592         if(ptr)
2593             ((PDRIVER_INFO_3W) ptr)->pMonitorName = (LPWSTR)strPtr;
2594         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2595     }
2596
2597     if(WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size,
2598                                  unicode)) {
2599         *pcbNeeded += size;
2600         if(*pcbNeeded <= cbBuf)
2601             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr,
2602                                       size, &tmp, unicode);
2603         if(ptr)
2604             ((PDRIVER_INFO_3W) ptr)->pDefaultDataType = (LPWSTR)strPtr;
2605         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
2606     }
2607
2608     TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
2609     RegCloseKey(hkeyDriver);
2610     return TRUE;
2611 }
2612
2613 /*****************************************************************************
2614  *          WINSPOOL_GetPrinterDriver
2615  */
2616 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
2617                                       DWORD Level, LPBYTE pDriverInfo,
2618                                       DWORD cbBuf, LPDWORD pcbNeeded,
2619                                       BOOL unicode)
2620 {
2621     LPCWSTR name;
2622     WCHAR DriverName[100];
2623     DWORD ret, type, size, needed = 0;
2624     LPBYTE ptr = NULL;
2625     HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
2626
2627     TRACE("(%p,%s,%ld,%p,%ld,%p)\n",hPrinter,debugstr_w(pEnvironment),
2628           Level,pDriverInfo,cbBuf, pcbNeeded);
2629
2630     ZeroMemory(pDriverInfo, cbBuf);
2631
2632     if (!(name = WINSPOOL_GetOpenedPrinter(hPrinter))) return FALSE;
2633
2634     if(Level < 1 || Level > 3) {
2635         SetLastError(ERROR_INVALID_LEVEL);
2636         return FALSE;
2637     }
2638     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
2639        ERROR_SUCCESS) {
2640         ERR("Can't create Printers key\n");
2641         return FALSE;
2642     }
2643     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
2644        != ERROR_SUCCESS) {
2645         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
2646         RegCloseKey(hkeyPrinters);
2647         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
2648         return FALSE;
2649     }
2650     size = sizeof(DriverName);
2651     DriverName[0] = 0;
2652     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
2653                            (LPBYTE)DriverName, &size);
2654     RegCloseKey(hkeyPrinter);
2655     RegCloseKey(hkeyPrinters);
2656     if(ret != ERROR_SUCCESS) {
2657         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
2658         return FALSE;
2659     }
2660
2661     hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
2662     if(!hkeyDrivers) {
2663         ERR("Can't create Drivers key\n");
2664         return FALSE;
2665     }
2666
2667     switch(Level) {
2668     case 1:
2669         size = sizeof(DRIVER_INFO_1W);
2670         break;
2671     case 2:
2672         size = sizeof(DRIVER_INFO_2W);
2673         break;
2674     case 3:
2675         size = sizeof(DRIVER_INFO_3W);
2676         break;
2677     default:
2678         ERR("Invalid level\n");
2679         return FALSE;
2680     }
2681
2682     if(size <= cbBuf)
2683         ptr = pDriverInfo + size;
2684
2685     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
2686                          pEnvironment, Level, pDriverInfo,
2687                          (cbBuf < size) ? NULL : ptr,
2688                          (cbBuf < size) ? 0 : cbBuf - size,
2689                          &needed, unicode)) {
2690             RegCloseKey(hkeyDrivers);
2691             return FALSE;
2692     }
2693
2694     RegCloseKey(hkeyDrivers);
2695
2696     if(pcbNeeded) *pcbNeeded = size + needed;
2697     TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
2698     if(cbBuf >= needed) return TRUE;
2699     SetLastError(ERROR_INSUFFICIENT_BUFFER);
2700     return FALSE;
2701 }
2702
2703 /*****************************************************************************
2704  *          GetPrinterDriverA  [WINSPOOL.@]
2705  */
2706 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
2707                               DWORD Level, LPBYTE pDriverInfo,
2708                               DWORD cbBuf, LPDWORD pcbNeeded)
2709 {
2710     BOOL ret;
2711     UNICODE_STRING pEnvW;
2712     PWSTR pwstrEnvW;
2713     
2714     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
2715     ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
2716                                     cbBuf, pcbNeeded, FALSE);
2717     RtlFreeUnicodeString(&pEnvW);
2718     return ret;
2719 }
2720 /*****************************************************************************
2721  *          GetPrinterDriverW  [WINSPOOL.@]
2722  */
2723 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
2724                                   DWORD Level, LPBYTE pDriverInfo,
2725                                   DWORD cbBuf, LPDWORD pcbNeeded)
2726 {
2727     return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
2728                                      pDriverInfo, cbBuf, pcbNeeded, TRUE);
2729 }
2730
2731 /*****************************************************************************
2732  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
2733  */
2734 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
2735                                        DWORD Level, LPBYTE pDriverDirectory,
2736                                        DWORD cbBuf, LPDWORD pcbNeeded)
2737 {
2738     DWORD needed;
2739
2740     TRACE("(%s, %s, %ld, %p, %ld, %p)\n", debugstr_w(pName), 
2741           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
2742     if(pName != NULL) {
2743         FIXME("pName = `%s' - unsupported\n", debugstr_w(pName));
2744         SetLastError(ERROR_INVALID_PARAMETER);
2745         return FALSE;
2746     }
2747     if(pEnvironment != NULL) {
2748         FIXME("pEnvironment = `%s' - unsupported\n", debugstr_w(pEnvironment));
2749         SetLastError(ERROR_INVALID_ENVIRONMENT);
2750         return FALSE;
2751     }
2752     if(Level != 1)  /* win95 ignores this so we just carry on */
2753         WARN("Level = %ld - assuming 1\n", Level);
2754
2755     /* FIXME should read from registry */
2756     needed = GetSystemDirectoryW( (LPWSTR)pDriverDirectory, cbBuf/sizeof(WCHAR));
2757     /* GetSystemDirectoryW returns number of TCHAR without '\0' 
2758      * adjust this now
2759      */
2760     needed++;
2761     needed*=sizeof(WCHAR);
2762
2763     if(pcbNeeded)
2764         *pcbNeeded = needed;
2765     TRACE("required <%08lx>\n", *pcbNeeded);
2766     if(needed > cbBuf) {
2767         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2768         return FALSE;
2769     }
2770     return TRUE;
2771 }
2772
2773
2774 /*****************************************************************************
2775  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
2776  */
2777 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
2778                                        DWORD Level, LPBYTE pDriverDirectory,
2779                                        DWORD cbBuf, LPDWORD pcbNeeded)
2780 {
2781     UNICODE_STRING nameW, environmentW;
2782     BOOL ret;
2783     DWORD pcbNeededW;
2784     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
2785     WCHAR *driverDirectoryW = NULL;
2786
2787     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
2788
2789     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
2790     else nameW.Buffer = NULL;
2791     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
2792     else environmentW.Buffer = NULL;
2793
2794     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
2795                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
2796     if (ret) {
2797         DWORD needed;
2798         needed = 1 + WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
2799                                    pDriverDirectory, cbBuf, NULL, NULL);
2800         if(pcbNeeded)
2801             *pcbNeeded = needed;
2802         ret = (needed <= cbBuf) ? TRUE : FALSE;
2803     } else 
2804         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
2805
2806     TRACE("provided<%ld> required <%ld>\n", cbBuf, *pcbNeeded);
2807
2808     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
2809     RtlFreeUnicodeString(&environmentW);
2810     RtlFreeUnicodeString(&nameW);
2811
2812     return ret;
2813 }
2814
2815 /*****************************************************************************
2816  *          AddPrinterDriverA  [WINSPOOL.@]
2817  */
2818 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
2819 {
2820     DRIVER_INFO_3A di3;
2821     HKEY hkeyDrivers, hkeyName;
2822
2823     TRACE("(%s,%ld,%p)\n",debugstr_a(pName),level,pDriverInfo);
2824
2825     if(level != 2 && level != 3) {
2826         SetLastError(ERROR_INVALID_LEVEL);
2827         return FALSE;
2828     }
2829     if(pName != NULL) {
2830         FIXME("pName= %s - unsupported\n", debugstr_a(pName));
2831         SetLastError(ERROR_INVALID_PARAMETER);
2832         return FALSE;
2833     }
2834     if(!pDriverInfo) {
2835         WARN("pDriverInfo == NULL\n");
2836         SetLastError(ERROR_INVALID_PARAMETER);
2837         return FALSE;
2838     }
2839
2840     if(level == 3)
2841         di3 = *(DRIVER_INFO_3A *)pDriverInfo;
2842     else {
2843         memset(&di3, 0, sizeof(di3));
2844         memcpy(&di3, pDriverInfo, sizeof(DRIVER_INFO_2A));
2845     }
2846
2847     if(!di3.pName || !di3.pDriverPath || !di3.pConfigFile ||
2848        !di3.pDataFile) {
2849         SetLastError(ERROR_INVALID_PARAMETER);
2850         return FALSE;
2851     }
2852     if(!di3.pDefaultDataType) di3.pDefaultDataType = "";
2853     if(!di3.pDependentFiles) di3.pDependentFiles = "\0";
2854     if(!di3.pHelpFile) di3.pHelpFile = "";
2855     if(!di3.pMonitorName) di3.pMonitorName = "";
2856
2857     hkeyDrivers = WINSPOOL_OpenDriverReg(di3.pEnvironment, FALSE);
2858
2859     if(!hkeyDrivers) {
2860         ERR("Can't create Drivers key\n");
2861         return FALSE;
2862     }
2863
2864     if(level == 2) { /* apparently can't overwrite with level2 */
2865         if(RegOpenKeyA(hkeyDrivers, di3.pName, &hkeyName) == ERROR_SUCCESS) {
2866             RegCloseKey(hkeyName);
2867             RegCloseKey(hkeyDrivers);
2868             WARN("Trying to create existing printer driver %s\n", debugstr_a(di3.pName));
2869             SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
2870             return FALSE;
2871         }
2872     }
2873     if(RegCreateKeyA(hkeyDrivers, di3.pName, &hkeyName) != ERROR_SUCCESS) {
2874         RegCloseKey(hkeyDrivers);
2875         ERR("Can't create Name key\n");
2876         return FALSE;
2877     }
2878     RegSetValueExA(hkeyName, "Configuration File", 0, REG_SZ, di3.pConfigFile,
2879                    0);
2880     RegSetValueExA(hkeyName, "Data File", 0, REG_SZ, di3.pDataFile, 0);
2881     RegSetValueExA(hkeyName, "Driver", 0, REG_SZ, di3.pDriverPath, 0);
2882     RegSetValueExA(hkeyName, "Version", 0, REG_DWORD, (LPSTR)&di3.cVersion,
2883                    sizeof(DWORD));
2884     RegSetValueExA(hkeyName, "Datatype", 0, REG_SZ, di3.pDefaultDataType, 0);
2885     RegSetValueExA(hkeyName, "Dependent Files", 0, REG_MULTI_SZ,
2886                    di3.pDependentFiles, 0);
2887     RegSetValueExA(hkeyName, "Help File", 0, REG_SZ, di3.pHelpFile, 0);
2888     RegSetValueExA(hkeyName, "Monitor", 0, REG_SZ, di3.pMonitorName, 0);
2889     RegCloseKey(hkeyName);
2890     RegCloseKey(hkeyDrivers);
2891
2892     return TRUE;
2893 }
2894
2895 /*****************************************************************************
2896  *          AddPrinterDriverW  [WINSPOOL.@]
2897  */
2898 BOOL WINAPI AddPrinterDriverW(LPWSTR printerName,DWORD level,
2899                                    LPBYTE pDriverInfo)
2900 {
2901     FIXME("(%s,%ld,%p): stub\n",debugstr_w(printerName),
2902           level,pDriverInfo);
2903     return FALSE;
2904 }
2905
2906 /*****************************************************************************
2907  *          AddPrintProcessorA  [WINSPOOL.@]
2908  */
2909 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
2910                                LPSTR pPrintProcessorName)
2911 {
2912     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
2913           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
2914     return FALSE;
2915 }
2916
2917 /*****************************************************************************
2918  *          AddPrintProcessorW  [WINSPOOL.@]
2919  */
2920 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
2921                                LPWSTR pPrintProcessorName)
2922 {
2923     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
2924           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
2925     return FALSE;
2926 }
2927
2928 /*****************************************************************************
2929  *          AddPrintProvidorA  [WINSPOOL.@]
2930  */
2931 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
2932 {
2933     FIXME("(%s,0x%08lx,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
2934     return FALSE;
2935 }
2936
2937 /*****************************************************************************
2938  *          AddPrintProvidorW  [WINSPOOL.@]
2939  */
2940 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
2941 {
2942     FIXME("(%s,0x%08lx,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
2943     return FALSE;
2944 }
2945
2946 /*****************************************************************************
2947  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
2948  */
2949 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
2950                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
2951 {
2952     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
2953           pDevModeOutput, pDevModeInput);
2954     return 0;
2955 }
2956
2957 /*****************************************************************************
2958  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
2959  */
2960 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
2961                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
2962 {
2963     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
2964           pDevModeOutput, pDevModeInput);
2965     return 0;
2966 }
2967
2968 /*****************************************************************************
2969  *          PrinterProperties  [WINSPOOL.@]
2970  *
2971  *     Displays a dialog to set the properties of the printer.
2972  *
2973  * RETURNS
2974  *     nonzero on success or zero on failure
2975  *
2976  * BUGS
2977  *         implemented as stub only
2978  */
2979 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
2980                               HANDLE hPrinter /* [in] handle to printer object */
2981 ){
2982     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
2983     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2984     return FALSE;
2985 }
2986
2987 /*****************************************************************************
2988  *          EnumJobsA [WINSPOOL.@]
2989  *
2990  */
2991 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
2992                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
2993                       LPDWORD pcReturned)
2994 {
2995     FIXME("(%p,first=%ld,no=%ld,level=%ld,job=%p,cb=%ld,%p,%p), stub!\n",
2996         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
2997     );
2998     if(pcbNeeded) *pcbNeeded = 0;
2999     if(pcReturned) *pcReturned = 0;
3000     return FALSE;
3001 }
3002
3003
3004 /*****************************************************************************
3005  *          EnumJobsW [WINSPOOL.@]
3006  *
3007  */
3008 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
3009                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
3010                       LPDWORD pcReturned)
3011 {
3012     FIXME("(%p,first=%ld,no=%ld,level=%ld,job=%p,cb=%ld,%p,%p), stub!\n",
3013         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
3014     );
3015     if(pcbNeeded) *pcbNeeded = 0;
3016     if(pcReturned) *pcReturned = 0;
3017     return FALSE;
3018 }
3019
3020 /*****************************************************************************
3021  *          WINSPOOL_EnumPrinterDrivers [internal]
3022  *
3023  *    Delivers information about all printer drivers installed on the
3024  *    localhost or a given server
3025  *
3026  * RETURNS
3027  *    nonzero on success or zero on failure. If the buffer for the returned
3028  *    information is too small the function will return an error
3029  *
3030  * BUGS
3031  *    - only implemented for localhost, foreign hosts will return an error
3032  */
3033 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPWSTR pEnvironment,
3034                                         DWORD Level, LPBYTE pDriverInfo,
3035                                         DWORD cbBuf, LPDWORD pcbNeeded,
3036                                         LPDWORD pcReturned, BOOL unicode)
3037
3038 {   HKEY  hkeyDrivers;
3039     DWORD i, needed, number = 0, size = 0;
3040     WCHAR DriverNameW[255];
3041     PBYTE ptr;
3042
3043     TRACE("%s,%s,%ld,%p,%ld,%d\n",
3044           debugstr_w(pName), debugstr_w(pEnvironment),
3045           Level, pDriverInfo, cbBuf, unicode);
3046
3047     /* check for local drivers */
3048     if(pName) {
3049         ERR("remote drivers unsupported! Current remote host is %s\n",
3050              debugstr_w(pName));
3051         return FALSE;
3052     }
3053
3054     /* check input parameter */
3055     if((Level < 1) || (Level > 3)) {
3056         ERR("unsupported level %ld\n", Level);
3057         SetLastError(ERROR_INVALID_LEVEL);
3058         return FALSE;
3059     }
3060
3061     /* initialize return values */
3062     if(pDriverInfo)
3063         memset( pDriverInfo, 0, cbBuf);
3064     *pcbNeeded  = 0;
3065     *pcReturned = 0;
3066
3067     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
3068     if(!hkeyDrivers) {
3069         ERR("Can't open Drivers key\n");
3070         return FALSE;
3071     }
3072
3073     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
3074                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
3075         RegCloseKey(hkeyDrivers);
3076         ERR("Can't query Drivers key\n");
3077         return FALSE;
3078     }
3079     TRACE("Found %ld Drivers\n", number);
3080
3081     /* get size of single struct
3082      * unicode and ascii structure have the same size
3083      */
3084     switch (Level) {
3085         case 1:
3086             size = sizeof(DRIVER_INFO_1A);
3087             break;
3088         case 2:
3089             size = sizeof(DRIVER_INFO_2A);
3090             break;
3091         case 3:
3092             size = sizeof(DRIVER_INFO_3A);
3093             break;
3094     }
3095
3096     /* calculate required buffer size */
3097     *pcbNeeded = size * number;
3098
3099     for( i = 0,  ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
3100          i < number;
3101          i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
3102         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW))
3103                        != ERROR_SUCCESS) {
3104             ERR("Can't enum key number %ld\n", i);
3105             RegCloseKey(hkeyDrivers);
3106             return FALSE;
3107         }
3108         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
3109                          pEnvironment, Level, ptr,
3110                          (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
3111                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
3112                          &needed, unicode)) {
3113             RegCloseKey(hkeyDrivers);
3114             return FALSE;
3115         }
3116         (*pcbNeeded) += needed;
3117     }
3118
3119     RegCloseKey(hkeyDrivers);
3120
3121     if(cbBuf < *pcbNeeded){
3122         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3123         return FALSE;
3124     }
3125
3126     return TRUE;
3127 }
3128
3129 /*****************************************************************************
3130  *          EnumPrinterDriversW  [WINSPOOL.@]
3131  *
3132  *    see function EnumPrinterDrivers for RETURNS, BUGS
3133  */
3134 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
3135                                 LPBYTE pDriverInfo, DWORD cbBuf,
3136                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
3137 {
3138     return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
3139                                        cbBuf, pcbNeeded, pcReturned, TRUE);
3140 }
3141
3142 /*****************************************************************************
3143  *          EnumPrinterDriversA  [WINSPOOL.@]
3144  *
3145  *    see function EnumPrinterDrivers for RETURNS, BUGS
3146  */
3147 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
3148                                 LPBYTE pDriverInfo, DWORD cbBuf,
3149                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
3150 {   BOOL ret;
3151     UNICODE_STRING pNameW, pEnvironmentW;
3152     PWSTR pwstrNameW, pwstrEnvironmentW;
3153
3154     pwstrNameW = asciitounicode(&pNameW, pName);
3155     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
3156
3157     ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
3158                                       Level, pDriverInfo, cbBuf, pcbNeeded,
3159                                       pcReturned, FALSE);
3160     RtlFreeUnicodeString(&pNameW);
3161     RtlFreeUnicodeString(&pEnvironmentW);
3162
3163     return ret;
3164 }
3165
3166 static CHAR PortMonitor[] = "Wine Port Monitor";
3167 static CHAR PortDescription[] = "Wine Port";
3168
3169 static BOOL WINSPOOL_ComPortExists( LPCSTR name )
3170 {
3171     HANDLE handle;
3172
3173     handle = CreateFileA( name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
3174                          NULL, OPEN_EXISTING, 0, NULL );
3175     if (handle == INVALID_HANDLE_VALUE)
3176         return FALSE;
3177     TRACE("Checking %s exists\n", name );
3178     CloseHandle( handle );
3179     return TRUE;
3180 }
3181
3182 static DWORD WINSPOOL_CountSerialPorts()
3183 {
3184     CHAR name[6];
3185     DWORD n = 0, i;
3186
3187     for (i=0; i<4; i++)
3188     {
3189         strcpy( name, "COMx:" );
3190         name[3] = '1' + i;
3191         if (WINSPOOL_ComPortExists( name ))
3192             n++;
3193     }
3194
3195     return n;
3196 }
3197
3198 /******************************************************************************
3199  *              EnumPortsA   (WINSPOOL.@)
3200  */
3201 BOOL WINAPI EnumPortsA(LPSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
3202                        LPDWORD bufneeded,LPDWORD bufreturned)
3203 {
3204     CHAR portname[10];
3205     DWORD info_size, ofs, i, printer_count, serial_count, count, n, r;
3206     const LPCSTR szPrinterPortKey = "Software\\Wine\\Wine\\Config\\spooler";
3207     HKEY hkey_printer;
3208     BOOL retval = TRUE;
3209
3210     TRACE("(%s,%ld,%p,%ld,%p,%p)\n",
3211           debugstr_a(name),level,buffer,bufsize,bufneeded,bufreturned);
3212
3213     switch( level )
3214     {
3215     case 1:
3216         info_size = sizeof (PORT_INFO_1A);
3217         break;
3218     case 2:
3219         info_size = sizeof (PORT_INFO_2A);
3220         break;
3221     default:
3222         SetLastError(ERROR_INVALID_LEVEL);
3223         return FALSE;
3224     }
3225     
3226     /* see how many exist */
3227
3228     hkey_printer = 0;
3229     serial_count = WINSPOOL_CountSerialPorts();
3230     printer_count = 0;
3231
3232     r = RegOpenKeyA( HKEY_LOCAL_MACHINE, szPrinterPortKey, &hkey_printer);
3233     if ( r == ERROR_SUCCESS )
3234     {
3235         RegQueryInfoKeyA( hkey_printer, NULL, NULL, NULL, NULL, NULL, NULL,
3236             &printer_count, NULL, NULL, NULL, NULL);
3237     }
3238     count = serial_count + printer_count;
3239
3240     /* then fill in the structure info structure once
3241        we know the offset to the first string */
3242
3243     memset( buffer, 0, bufsize );
3244     n = 0;
3245     ofs = info_size*count; 
3246     for ( i=0; i<count; i++)
3247     {
3248         DWORD vallen = sizeof(portname) - 1;
3249
3250         /* get the serial port values, then the printer values */
3251         if ( i < serial_count )
3252         {
3253             strcpy( portname, "COMx:" );
3254             portname[3] = '1' + i;
3255             if (!WINSPOOL_ComPortExists( portname ))
3256                 continue;
3257
3258             TRACE("Found %s\n", portname );
3259             vallen = strlen( portname );
3260         }
3261         else
3262         {
3263             r = RegEnumValueA( hkey_printer, i-serial_count, 
3264                      portname, &vallen, NULL, NULL, NULL, 0 );
3265             if ( r )
3266                 continue;
3267         }
3268
3269         /* add a colon if necessary, and make it upper case */
3270         CharUpperBuffA(portname,vallen);
3271         if (strcasecmp(portname,"nul")!=0)
3272             if (vallen && (portname[vallen-1] != ':') )
3273                 lstrcatA(portname,":");
3274
3275         /* add the port info structure if we can fit it */
3276         if ( info_size*(n+1) < bufsize )
3277         {
3278             if ( level == 1)
3279             {
3280                 PORT_INFO_1A *info = (PORT_INFO_1A*) &buffer[info_size*n];
3281                 info->pName = (LPSTR) &buffer[ofs];
3282             }
3283             else if ( level == 2)
3284             {
3285                 PORT_INFO_2A *info = (PORT_INFO_2A*) &buffer[info_size*n];
3286                 info->pPortName = (LPSTR) &buffer[ofs];
3287                 /* FIXME: fill in more stuff here */
3288                 info->pMonitorName = PortMonitor;
3289                 info->pDescription = PortDescription;
3290                 info->fPortType = PORT_TYPE_WRITE|PORT_TYPE_READ;
3291             }
3292
3293             /* add the name of the port if we can fit it */
3294             if ( ofs < bufsize )
3295                 lstrcpynA(&buffer[ofs],portname,bufsize - ofs);
3296
3297             n++;
3298         }
3299         else
3300             retval = FALSE;
3301         ofs += lstrlenA(portname)+1;
3302     }
3303
3304     RegCloseKey(hkey_printer);
3305
3306     if(bufneeded)
3307         *bufneeded = ofs;
3308
3309     if(bufreturned)
3310         *bufreturned = n;
3311
3312     return retval;
3313 }
3314
3315 /******************************************************************************
3316  *      EnumPortsW   (WINSPOOL.@)
3317  */
3318 BOOL WINAPI EnumPortsW(LPWSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
3319                        LPDWORD bufneeded,LPDWORD bufreturned)
3320 {
3321     FIXME("(%s,%ld,%p,%ld,%p,%p) - stub\n",
3322           debugstr_w(name),level,buffer,bufsize,bufneeded,bufreturned);
3323     return FALSE;
3324 }
3325
3326 /******************************************************************************
3327  *              GetDefaultPrinterW   (WINSPOOL.@)
3328  *
3329  * FIXME
3330  *      This function must read the value from data 'device' of key
3331  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
3332  */
3333 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
3334 {
3335     BOOL  retval = TRUE;
3336     DWORD insize, len;
3337     WCHAR *buffer, *ptr;
3338
3339     if (!namesize)
3340     {
3341         SetLastError(ERROR_INVALID_PARAMETER);
3342         return FALSE;
3343     }
3344
3345     /* make the buffer big enough for the stuff from the profile/registry,
3346      * the content must fit into the local buffer to compute the correct
3347      * size even if the extern buffer is too small or not given.
3348      * (20 for ,driver,port) */
3349     insize = *namesize;
3350     len = max(100, (insize + 20));
3351     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
3352
3353     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
3354     {
3355         SetLastError (ERROR_FILE_NOT_FOUND);
3356         retval = FALSE;
3357         goto end;
3358     }
3359     TRACE("%s\n", debugstr_w(buffer));
3360
3361     if ((ptr = strchrW(buffer, ',')) == NULL)
3362     {
3363         SetLastError(ERROR_INVALID_NAME);
3364         retval = FALSE;
3365         goto end;
3366     }
3367
3368     *ptr = 0;
3369     *namesize = strlenW(buffer) + 1;
3370     if(!name || (*namesize > insize))
3371     {
3372         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3373         retval = FALSE;
3374         goto end;
3375     }
3376     strcpyW(name, buffer);
3377
3378 end:
3379     HeapFree( GetProcessHeap(), 0, buffer);
3380     return retval;
3381 }
3382
3383
3384 /******************************************************************************
3385  *              GetDefaultPrinterA   (WINSPOOL.@)
3386  */
3387 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
3388 {
3389     BOOL  retval = TRUE;
3390     DWORD insize = 0;
3391     WCHAR *bufferW = NULL;
3392
3393     if (!namesize)
3394     {
3395         SetLastError(ERROR_INVALID_PARAMETER);
3396         return FALSE;
3397     }
3398
3399     if(name && *namesize) {
3400         insize = *namesize;
3401         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
3402     }
3403
3404     if(!GetDefaultPrinterW( bufferW, namesize)) {
3405         retval = FALSE;
3406         goto end;
3407     }
3408
3409     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
3410                                     NULL, NULL);
3411     if (!*namesize)
3412     {
3413         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
3414         retval = FALSE;
3415     }
3416     TRACE("0x%08lx/0x%08lx:%s\n", *namesize, insize, debugstr_w(bufferW));
3417
3418 end:
3419     HeapFree( GetProcessHeap(), 0, bufferW);
3420     return retval;
3421 }
3422
3423
3424 /******************************************************************************
3425  *              SetPrinterDataExA   (WINSPOOL.@)
3426  */
3427 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
3428                                LPCSTR pValueName, DWORD Type,
3429                                LPBYTE pData, DWORD cbData)
3430 {
3431     HKEY hkeyPrinter, hkeySubkey;
3432     DWORD ret;
3433
3434     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_a(pKeyName),
3435           debugstr_a(pValueName), Type, pData, cbData);
3436
3437     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
3438        != ERROR_SUCCESS)
3439         return ret;
3440
3441     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
3442        != ERROR_SUCCESS) {
3443         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
3444         RegCloseKey(hkeyPrinter);
3445         return ret;
3446     }
3447     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
3448     RegCloseKey(hkeySubkey);
3449     RegCloseKey(hkeyPrinter);
3450     return ret;
3451 }
3452
3453 /******************************************************************************
3454  *              SetPrinterDataExW   (WINSPOOL.@)
3455  */
3456 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
3457                                LPCWSTR pValueName, DWORD Type,
3458                                LPBYTE pData, DWORD cbData)
3459 {
3460     HKEY hkeyPrinter, hkeySubkey;
3461     DWORD ret;
3462
3463     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_w(pKeyName),
3464           debugstr_w(pValueName), Type, pData, cbData);
3465
3466     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
3467        != ERROR_SUCCESS)
3468         return ret;
3469
3470     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
3471        != ERROR_SUCCESS) {
3472         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
3473         RegCloseKey(hkeyPrinter);
3474         return ret;
3475     }
3476     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
3477     RegCloseKey(hkeySubkey);
3478     RegCloseKey(hkeyPrinter);
3479     return ret;
3480 }
3481
3482 /******************************************************************************
3483  *              SetPrinterDataA   (WINSPOOL.@)
3484  */
3485 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
3486                                LPBYTE pData, DWORD cbData)
3487 {
3488     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
3489                              pData, cbData);
3490 }
3491
3492 /******************************************************************************
3493  *              SetPrinterDataW   (WINSPOOL.@)
3494  */
3495 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
3496                              LPBYTE pData, DWORD cbData)
3497 {
3498     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
3499                              pData, cbData);
3500 }
3501
3502 /******************************************************************************
3503  *              GetPrinterDataExA   (WINSPOOL.@)
3504  */
3505 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
3506                                LPCSTR pValueName, LPDWORD pType,
3507                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
3508 {
3509     HKEY hkeyPrinter, hkeySubkey;
3510     DWORD ret;
3511
3512     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
3513           debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
3514           pcbNeeded);
3515
3516     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
3517        != ERROR_SUCCESS)
3518         return ret;
3519
3520     if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
3521        != ERROR_SUCCESS) {
3522         WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
3523         RegCloseKey(hkeyPrinter);
3524         return ret;
3525     }
3526     *pcbNeeded = nSize;
3527     ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
3528     RegCloseKey(hkeySubkey);
3529     RegCloseKey(hkeyPrinter);
3530     return ret;
3531 }
3532
3533 /******************************************************************************
3534  *              GetPrinterDataExW   (WINSPOOL.@)
3535  */
3536 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
3537                                LPCWSTR pValueName, LPDWORD pType,
3538                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
3539 {
3540     HKEY hkeyPrinter, hkeySubkey;
3541     DWORD ret;
3542
3543     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
3544           debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
3545           pcbNeeded);
3546
3547     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
3548        != ERROR_SUCCESS)
3549         return ret;
3550
3551     if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
3552        != ERROR_SUCCESS) {
3553         WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
3554         RegCloseKey(hkeyPrinter);
3555         return ret;
3556     }
3557     *pcbNeeded = nSize;
3558     ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
3559     RegCloseKey(hkeySubkey);
3560     RegCloseKey(hkeyPrinter);
3561     return ret;
3562 }
3563
3564 /******************************************************************************
3565  *              GetPrinterDataA   (WINSPOOL.@)
3566  */
3567 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
3568                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
3569 {
3570     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
3571                              pData, nSize, pcbNeeded);
3572 }
3573
3574 /******************************************************************************
3575  *              GetPrinterDataW   (WINSPOOL.@)
3576  */
3577 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
3578                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
3579 {
3580     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
3581                              pData, nSize, pcbNeeded);
3582 }
3583
3584 /*******************************************************************************
3585  *              EnumPrinterDataExW      [WINSPOOL.@]
3586  */
3587 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
3588                                 LPBYTE pEnumValues, DWORD cbEnumValues,
3589                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
3590 {
3591     HKEY                    hkPrinter, hkSubKey;
3592     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
3593                             cbValueNameLen, cbMaxValueLen, cbValueLen,
3594                             cbBufSize, dwType;
3595     LPWSTR                  lpValueName;
3596     HANDLE                  hHeap;
3597     PBYTE                   lpValue;
3598     PPRINTER_ENUM_VALUESW   ppev;
3599
3600     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
3601
3602     if (pKeyName == NULL || *pKeyName == 0)
3603         return ERROR_INVALID_PARAMETER;
3604
3605     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
3606     if (ret != ERROR_SUCCESS)
3607     {
3608         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %li\n",
3609                 hPrinter, ret);
3610         return ret;
3611     }
3612
3613     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
3614     if (ret != ERROR_SUCCESS)
3615     {
3616         r = RegCloseKey (hkPrinter);
3617         if (r != ERROR_SUCCESS)
3618             WARN ("RegCloseKey returned %li\n", r);
3619         TRACE ("RegOpenKeyExW (%p, %s) returned %li\n", hPrinter,
3620                 debugstr_w (pKeyName), ret);
3621         return ret;
3622     }
3623
3624     ret = RegCloseKey (hkPrinter);
3625     if (ret != ERROR_SUCCESS)
3626     {
3627         ERR ("RegCloseKey returned %li\n", ret);
3628         r = RegCloseKey (hkSubKey);
3629         if (r != ERROR_SUCCESS)
3630             WARN ("RegCloseKey returned %li\n", r);
3631         return ret;
3632     }
3633
3634     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
3635             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
3636     if (ret != ERROR_SUCCESS)
3637     {
3638         r = RegCloseKey (hkSubKey);
3639         if (r != ERROR_SUCCESS)
3640             WARN ("RegCloseKey returned %li\n", r);
3641         TRACE ("RegQueryInfoKeyW (%p) returned %li\n", hkSubKey, ret);
3642         return ret;
3643     }
3644
3645     TRACE ("RegQueryInfoKeyW returned cValues = %li, cbMaxValueNameLen = %li, "
3646             "cbMaxValueLen = %li\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
3647
3648     if (cValues == 0)                   /* empty key */
3649     {
3650         r = RegCloseKey (hkSubKey);
3651         if (r != ERROR_SUCCESS)
3652             WARN ("RegCloseKey returned %li\n", r);
3653         *pcbEnumValues = *pnEnumValues = 0;
3654         return ERROR_SUCCESS;
3655     }
3656
3657     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
3658
3659     hHeap = GetProcessHeap ();
3660     if (hHeap == NULL)
3661     {
3662         ERR ("GetProcessHeap failed\n");
3663         r = RegCloseKey (hkSubKey);
3664         if (r != ERROR_SUCCESS)
3665             WARN ("RegCloseKey returned %li\n", r);
3666         return ERROR_OUTOFMEMORY;
3667     }
3668
3669     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
3670     if (lpValueName == NULL)
3671     {
3672         ERR ("Failed to allocate %li bytes from process heap\n",
3673                 cbMaxValueNameLen * sizeof (WCHAR));
3674         r = RegCloseKey (hkSubKey);
3675         if (r != ERROR_SUCCESS)
3676             WARN ("RegCloseKey returned %li\n", r);
3677         return ERROR_OUTOFMEMORY;
3678     }
3679
3680     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
3681     if (lpValue == NULL)
3682     {
3683         ERR ("Failed to allocate %li bytes from process heap\n", cbMaxValueLen);
3684         if (HeapFree (hHeap, 0, lpValueName) == 0)
3685             WARN ("HeapFree failed with code %li\n", GetLastError ());
3686         r = RegCloseKey (hkSubKey);
3687         if (r != ERROR_SUCCESS)
3688             WARN ("RegCloseKey returned %li\n", r);
3689         return ERROR_OUTOFMEMORY;
3690     }
3691
3692     TRACE ("pass 1: calculating buffer required for all names and values\n");
3693
3694     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
3695
3696     TRACE ("%li bytes required for %li headers\n", cbBufSize, cValues);
3697
3698     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
3699     {
3700         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
3701         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
3702                 NULL, NULL, lpValue, &cbValueLen);
3703         if (ret != ERROR_SUCCESS)
3704         {
3705             if (HeapFree (hHeap, 0, lpValue) == 0)
3706                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3707             if (HeapFree (hHeap, 0, lpValueName) == 0)
3708                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3709             r = RegCloseKey (hkSubKey);
3710             if (r != ERROR_SUCCESS)
3711                 WARN ("RegCloseKey returned %li\n", r);
3712             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
3713             return ret;
3714         }
3715
3716         TRACE ("%s [%li]: name needs %li bytes, data needs %li bytes\n",
3717                 debugstr_w (lpValueName), dwIndex,
3718                 (cbValueNameLen + 1) * sizeof (WCHAR), cbValueLen);
3719
3720         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
3721         cbBufSize += cbValueLen;
3722     }
3723
3724     TRACE ("%li bytes required for all %li values\n", cbBufSize, cValues);
3725
3726     *pcbEnumValues = cbBufSize;
3727     *pnEnumValues = cValues;
3728
3729     if (cbEnumValues < cbBufSize)       /* buffer too small */
3730     {
3731         if (HeapFree (hHeap, 0, lpValue) == 0)
3732             WARN ("HeapFree failed with code %li\n", GetLastError ());
3733         if (HeapFree (hHeap, 0, lpValueName) == 0)
3734             WARN ("HeapFree failed with code %li\n", GetLastError ());
3735         r = RegCloseKey (hkSubKey);
3736         if (r != ERROR_SUCCESS)
3737             WARN ("RegCloseKey returned %li\n", r);
3738         TRACE ("%li byte buffer is not large enough\n", cbEnumValues);
3739         return ERROR_MORE_DATA;
3740     }
3741
3742     TRACE ("pass 2: copying all names and values to buffer\n");
3743
3744     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
3745     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
3746
3747     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
3748     {
3749         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
3750         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
3751                 NULL, &dwType, lpValue, &cbValueLen);
3752         if (ret != ERROR_SUCCESS)
3753         {
3754             if (HeapFree (hHeap, 0, lpValue) == 0)
3755                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3756             if (HeapFree (hHeap, 0, lpValueName) == 0)
3757                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3758             r = RegCloseKey (hkSubKey);
3759             if (r != ERROR_SUCCESS)
3760                 WARN ("RegCloseKey returned %li\n", r);
3761             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
3762             return ret;
3763         }
3764
3765         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
3766         memcpy (pEnumValues, lpValueName, cbValueNameLen);
3767         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
3768         pEnumValues += cbValueNameLen;
3769
3770         /* return # of *bytes* (including trailing \0), not # of chars */
3771         ppev[dwIndex].cbValueName = cbValueNameLen;
3772
3773         ppev[dwIndex].dwType = dwType;
3774
3775         memcpy (pEnumValues, lpValue, cbValueLen);
3776         ppev[dwIndex].pData = pEnumValues;
3777         pEnumValues += cbValueLen;
3778
3779         ppev[dwIndex].cbData = cbValueLen;
3780
3781         TRACE ("%s [%li]: copied name (%li bytes) and data (%li bytes)\n",
3782                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
3783     }
3784
3785     if (HeapFree (hHeap, 0, lpValue) == 0)
3786     {
3787         ret = GetLastError ();
3788         ERR ("HeapFree failed with code %li\n", ret);
3789         if (HeapFree (hHeap, 0, lpValueName) == 0)
3790             WARN ("HeapFree failed with code %li\n", GetLastError ());
3791         r = RegCloseKey (hkSubKey);
3792         if (r != ERROR_SUCCESS)
3793             WARN ("RegCloseKey returned %li\n", r);
3794         return ret;
3795     }
3796
3797     if (HeapFree (hHeap, 0, lpValueName) == 0)
3798     {
3799         ret = GetLastError ();
3800         ERR ("HeapFree failed with code %li\n", ret);
3801         r = RegCloseKey (hkSubKey);
3802         if (r != ERROR_SUCCESS)
3803             WARN ("RegCloseKey returned %li\n", r);
3804         return ret;
3805     }
3806
3807     ret = RegCloseKey (hkSubKey);
3808     if (ret != ERROR_SUCCESS)
3809     {
3810         ERR ("RegCloseKey returned %li\n", ret);
3811         return ret;
3812     }
3813
3814     return ERROR_SUCCESS;
3815 }
3816
3817 /*******************************************************************************
3818  *              EnumPrinterDataExA      [WINSPOOL.@]
3819  *
3820  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
3821  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
3822  * what Windows 2000 SP1 does.
3823  *
3824  */
3825 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
3826                                 LPBYTE pEnumValues, DWORD cbEnumValues,
3827                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
3828 {
3829     INT     len;
3830     LPWSTR  pKeyNameW;
3831     DWORD   ret, dwIndex, dwBufSize;
3832     HANDLE  hHeap;
3833     LPSTR   pBuffer;
3834
3835     TRACE ("%p %s\n", hPrinter, pKeyName);
3836
3837     if (pKeyName == NULL || *pKeyName == 0)
3838         return ERROR_INVALID_PARAMETER;
3839
3840     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
3841     if (len == 0)
3842     {
3843         ret = GetLastError ();
3844         ERR ("MultiByteToWideChar failed with code %li\n", ret);
3845         return ret;
3846     }
3847
3848     hHeap = GetProcessHeap ();
3849     if (hHeap == NULL)
3850     {
3851         ERR ("GetProcessHeap failed\n");
3852         return ERROR_OUTOFMEMORY;
3853     }
3854
3855     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
3856     if (pKeyNameW == NULL)
3857     {
3858         ERR ("Failed to allocate %li bytes from process heap\n",
3859                 (LONG) len * sizeof (WCHAR));
3860         return ERROR_OUTOFMEMORY;
3861     }
3862
3863     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
3864     {
3865         ret = GetLastError ();
3866         ERR ("MultiByteToWideChar failed with code %li\n", ret);
3867         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
3868             WARN ("HeapFree failed with code %li\n", GetLastError ());
3869         return ret;
3870     }
3871
3872     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
3873             pcbEnumValues, pnEnumValues);
3874     if (ret != ERROR_SUCCESS)
3875     {
3876         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
3877             WARN ("HeapFree failed with code %li\n", GetLastError ());
3878         TRACE ("EnumPrinterDataExW returned %li\n", ret);
3879         return ret;
3880     }
3881
3882     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
3883     {
3884         ret = GetLastError ();
3885         ERR ("HeapFree failed with code %li\n", ret);
3886         return ret;
3887     }
3888
3889     if (*pnEnumValues == 0)     /* empty key */
3890         return ERROR_SUCCESS;
3891
3892     dwBufSize = 0;
3893     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
3894     {
3895         PPRINTER_ENUM_VALUESW ppev =
3896                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
3897
3898         if (dwBufSize < ppev->cbValueName)
3899             dwBufSize = ppev->cbValueName;
3900
3901         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
3902                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
3903             dwBufSize = ppev->cbData;
3904     }
3905
3906     TRACE ("Largest Unicode name or value is %li bytes\n", dwBufSize);
3907
3908     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
3909     if (pBuffer == NULL)
3910     {
3911         ERR ("Failed to allocate %li bytes from process heap\n", dwBufSize);
3912         return ERROR_OUTOFMEMORY;
3913     }
3914
3915     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
3916     {
3917         PPRINTER_ENUM_VALUESW ppev =
3918                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
3919
3920         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
3921                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
3922                 NULL);
3923         if (len == 0)
3924         {
3925             ret = GetLastError ();
3926             ERR ("WideCharToMultiByte failed with code %li\n", ret);
3927             if (HeapFree (hHeap, 0, pBuffer) == 0)
3928                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3929             return ret;
3930         }
3931
3932         memcpy (ppev->pValueName, pBuffer, len);
3933
3934         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
3935
3936         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
3937                 ppev->dwType != REG_MULTI_SZ)
3938             continue;
3939
3940         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
3941                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
3942         if (len == 0)
3943         {
3944             ret = GetLastError ();
3945             ERR ("WideCharToMultiByte failed with code %li\n", ret);
3946             if (HeapFree (hHeap, 0, pBuffer) == 0)
3947                 WARN ("HeapFree failed with code %li\n", GetLastError ());
3948             return ret;
3949         }
3950
3951         memcpy (ppev->pData, pBuffer, len);
3952
3953         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
3954         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
3955     }
3956
3957     if (HeapFree (hHeap, 0, pBuffer) == 0)
3958     {
3959         ret = GetLastError ();
3960         ERR ("HeapFree failed with code %li\n", ret);
3961         return ret;
3962     }
3963
3964     return ERROR_SUCCESS;
3965 }
3966
3967 /******************************************************************************
3968  *      AbortPrinter (WINSPOOL.@)
3969  */
3970 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
3971 {
3972     FIXME("(%p), stub!\n", hPrinter);
3973     return TRUE;
3974 }
3975
3976 /******************************************************************************
3977  *              AddPortA (WINSPOOL.@)
3978  */
3979 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
3980 {
3981     FIXME("(%s, %p, %s), stub!\n",debugstr_a(pName),hWnd,debugstr_a(pMonitorName));
3982     return FALSE;
3983 }
3984
3985 /******************************************************************************
3986  *      AddPortW (WINSPOOL.@)
3987  */
3988 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
3989 {
3990     FIXME("(%s, %p, %s), stub!\n",debugstr_w(pName),hWnd,debugstr_w(pMonitorName));
3991     return FALSE;
3992 }
3993
3994 /******************************************************************************
3995  *             AddPortExA (WINSPOOL.@)
3996  *
3997  * Adds a print spooler port without presenting a user interface.
3998  */
3999 BOOL WINAPI AddPortExA(HANDLE hMonitor, LPSTR pName, DWORD Level, LPBYTE lpBuffer, LPSTR lpMonitorName)
4000 {
4001     FIXME("(%p, %s, %ld, %p, %s), stub!\n",hMonitor, debugstr_a(pName), Level,
4002           lpBuffer, debugstr_a(lpMonitorName));
4003     return FALSE;
4004 }
4005
4006 /******************************************************************************
4007  *             AddPortExW (WINSPOOL.@)
4008  *
4009  * See AddPortExW.
4010  */
4011 BOOL WINAPI AddPortExW(HANDLE hMonitor, LPWSTR pName, DWORD Level, LPBYTE lpBuffer, LPWSTR lpMonitorName)
4012 {
4013     FIXME("(%p, %s, %ld, %p, %s), stub!\n", hMonitor, debugstr_w(pName), Level,
4014           lpBuffer, debugstr_w(lpMonitorName));
4015     return FALSE;
4016 }
4017
4018 /******************************************************************************
4019  *      AddPrinterConnectionA (WINSPOOL.@)
4020  */
4021 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
4022 {
4023     FIXME("%s\n", debugstr_a(pName));
4024     return FALSE;
4025 }
4026
4027 /******************************************************************************
4028  *      AddPrinterConnectionW (WINSPOOL.@)
4029  */
4030 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
4031 {
4032     FIXME("%s\n", debugstr_w(pName));
4033     return FALSE;
4034 }
4035
4036 /******************************************************************************
4037  *              AddPrinterDriverExW (WINSPOOL.@)
4038  */
4039 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD Level,
4040     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
4041 {
4042     FIXME("%s %ld %p %ld\n", debugstr_w(pName),
4043            Level, pDriverInfo, dwFileCopyFlags);
4044     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
4045     return FALSE;
4046 }
4047
4048 /******************************************************************************
4049  *              AddPrinterDriverExA (WINSPOOL.@)
4050  */
4051 BOOL WINAPI AddPrinterDriverExA( LPSTR pName, DWORD Level,
4052     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
4053 {
4054     FIXME("%s %ld %p %ld\n", debugstr_a(pName),
4055            Level, pDriverInfo, dwFileCopyFlags);
4056     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
4057     return FALSE;
4058 }
4059
4060 /******************************************************************************
4061  *      ConfigurePortA (WINSPOOL.@)
4062  */
4063 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
4064 {
4065     FIXME("%s %p %s\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
4066     return FALSE;
4067 }
4068
4069 /******************************************************************************
4070  *      ConfigurePortW (WINSPOOL.@)
4071  */
4072 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
4073 {
4074     FIXME("%s %p %s\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
4075     return FALSE;
4076 }
4077
4078 /******************************************************************************
4079  *      ConnectToPrinterDlg (WINSPOOL.@)
4080  */
4081 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
4082 {
4083     FIXME("%p %lx\n", hWnd, Flags);
4084     return NULL;
4085 }
4086
4087 /******************************************************************************
4088  *      DeletePrinterConnectionA (WINSPOOL.@)
4089  */
4090 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
4091 {
4092     FIXME("%s\n", debugstr_a(pName));
4093     return TRUE;
4094 }
4095
4096 /******************************************************************************
4097  *      DeletePrinterConnectionW (WINSPOOL.@)
4098  */
4099 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
4100 {
4101     FIXME("%s\n", debugstr_w(pName));
4102     return TRUE;
4103 }
4104
4105 /******************************************************************************
4106  *              DeletePrinterDriverExW (WINSPOOL.@)
4107  */
4108 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
4109     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
4110 {
4111     FIXME("%s %s %s %lx %lx\n", debugstr_w(pName), debugstr_w(pEnvironment),
4112           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
4113     return TRUE;
4114 }
4115
4116 /******************************************************************************
4117  *              DeletePrinterDriverExA (WINSPOOL.@)
4118  */
4119 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
4120     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
4121 {
4122     FIXME("%s %s %s %lx %lx\n", debugstr_a(pName), debugstr_a(pEnvironment),
4123           debugstr_a(pDriverName), dwDeleteFlag, dwVersionFlag);
4124     return TRUE;
4125 }
4126
4127 /******************************************************************************
4128  *              DeletePrinterDataExW (WINSPOOL.@)
4129  */
4130 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
4131                                   LPCWSTR pValueName)
4132 {
4133     FIXME("%p %s %s\n", hPrinter, 
4134           debugstr_w(pKeyName), debugstr_w(pValueName));
4135     return ERROR_INVALID_PARAMETER;
4136 }
4137
4138 /******************************************************************************
4139  *              DeletePrinterDataExA (WINSPOOL.@)
4140  */
4141 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
4142                                   LPCSTR pValueName)
4143 {
4144     FIXME("%p %s %s\n", hPrinter, 
4145           debugstr_a(pKeyName), debugstr_a(pValueName));
4146     return ERROR_INVALID_PARAMETER;
4147 }
4148
4149 /******************************************************************************
4150  *      DeletePrintProcessorA (WINSPOOL.@)
4151  */
4152 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
4153 {
4154     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
4155           debugstr_a(pPrintProcessorName));
4156     return TRUE;
4157 }
4158
4159 /******************************************************************************
4160  *      DeletePrintProcessorW (WINSPOOL.@)
4161  */
4162 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
4163 {
4164     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
4165           debugstr_w(pPrintProcessorName));
4166     return TRUE;
4167 }
4168
4169 /******************************************************************************
4170  *      DeletePrintProvidorA (WINSPOOL.@)
4171  */
4172 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
4173 {
4174     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
4175           debugstr_a(pPrintProviderName));
4176     return TRUE;
4177 }
4178
4179 /******************************************************************************
4180  *      DeletePrintProvidorW (WINSPOOL.@)
4181  */
4182 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
4183 {
4184     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
4185           debugstr_w(pPrintProviderName));
4186     return TRUE;
4187 }
4188
4189 /******************************************************************************
4190  *      EnumFormsA (WINSPOOL.@)
4191  */
4192 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
4193     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
4194 {
4195     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
4196     return FALSE;
4197 }
4198
4199 /******************************************************************************
4200  *      EnumFormsW (WINSPOOL.@)
4201  */
4202 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
4203     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
4204 {
4205     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
4206     return FALSE;
4207 }
4208
4209 /*****************************************************************************
4210  *          EnumMonitorsA [WINSPOOL.@]
4211  *
4212  */
4213 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
4214                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4215 {
4216     FIXME("%s,%ld,%p,%ld,%p,%p\n", debugstr_a(pName), Level, pMonitors,
4217           cbBuf, pcbNeeded, pcReturned);
4218     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4219     return 0;
4220 }
4221
4222 /*****************************************************************************
4223  *          EnumMonitorsW [WINSPOOL.@]
4224  *
4225  */
4226 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
4227                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4228 {
4229     FIXME("%s,%ld,%p,%ld,%p,%p\n", debugstr_w(pName), Level, pMonitors,
4230           cbBuf, pcbNeeded, pcReturned);
4231     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4232     return 0;
4233 }
4234
4235 /******************************************************************************
4236  *              XcvDataW (WINSPOOL.@)
4237  *
4238  * Notes:
4239  *  There doesn't seem to be an A version...
4240  */
4241 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
4242     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
4243     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
4244 {
4245     FIXME("%p %s %p %ld %p %ld %p %p\n", hXcv, debugstr_w(pszDataName), 
4246           pInputData, cbInputData, pOutputData,
4247           cbOutputData, pcbOutputNeeded, pdwStatus);
4248     return FALSE;
4249 }
4250
4251 /*****************************************************************************
4252  *          EnumPrinterDataA [WINSPOOL.@]
4253  *
4254  */
4255 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
4256     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
4257     DWORD cbData, LPDWORD pcbData )
4258 {
4259     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
4260           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
4261     return ERROR_NO_MORE_ITEMS;
4262 }
4263
4264 /*****************************************************************************
4265  *          EnumPrinterDataW [WINSPOOL.@]
4266  *
4267  */
4268 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
4269     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
4270     DWORD cbData, LPDWORD pcbData )
4271 {
4272     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
4273           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
4274     return ERROR_NO_MORE_ITEMS;
4275 }
4276
4277 /*****************************************************************************
4278  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
4279  *
4280  */
4281 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
4282                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
4283                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
4284 {
4285     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_a(pName),
4286           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
4287           pcbNeeded, pcReturned);
4288     return FALSE;
4289 }
4290
4291 /*****************************************************************************
4292  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
4293  *
4294  */
4295 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
4296                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
4297                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
4298 {
4299     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
4300           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
4301           pcbNeeded, pcReturned);
4302     return FALSE;
4303 }
4304
4305 /*****************************************************************************
4306  *          EnumPrintProcessorsA [WINSPOOL.@]
4307  *
4308  */
4309 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
4310     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
4311 {
4312     FIXME("Stub: %s %s %ld %p %ld %p %p\n", pName, pEnvironment, Level,
4313         pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
4314     return FALSE;
4315 }
4316
4317 /*****************************************************************************
4318  *          EnumPrintProcessorsW [WINSPOOL.@]
4319  *
4320  */
4321 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
4322     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
4323 {
4324     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
4325         debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
4326         cbBuf, pcbNeeded, pcbReturned);
4327     return FALSE;
4328 }
4329
4330 /*****************************************************************************
4331  *          ExtDeviceMode [WINSPOOL.@]
4332  *
4333  */
4334 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
4335     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
4336     DWORD fMode)
4337 {
4338     FIXME("Stub: %p %p %p %s %s %p %s %lx\n", hWnd, hInst, pDevModeOutput,
4339           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
4340           debugstr_a(pProfile), fMode);
4341     return -1;
4342 }
4343
4344 /*****************************************************************************
4345  *          FindClosePrinterChangeNotification [WINSPOOL.@]
4346  *
4347  */
4348 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
4349 {
4350     FIXME("Stub: %p\n", hChange);
4351     return TRUE;
4352 }
4353
4354 /*****************************************************************************
4355  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
4356  *
4357  */
4358 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
4359     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
4360 {
4361     FIXME("Stub: %p %lx %lx %p\n",
4362           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
4363     return INVALID_HANDLE_VALUE;
4364 }
4365
4366 /*****************************************************************************
4367  *          FindNextPrinterChangeNotification [WINSPOOL.@]
4368  *
4369  */
4370 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
4371     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
4372 {
4373     FIXME("Stub: %p %p %p %p\n",
4374           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
4375     return FALSE;
4376 }
4377
4378 /*****************************************************************************
4379  *          FreePrinterNotifyInfo [WINSPOOL.@]
4380  *
4381  */
4382 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
4383 {
4384     FIXME("Stub: %p\n", pPrinterNotifyInfo);
4385     return TRUE;
4386 }
4387
4388 /*****************************************************************************
4389  *          GetJobA [WINSPOOL.@]
4390  *
4391  */
4392 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
4393                            DWORD cbBuf, LPDWORD pcbNeeded)
4394 {
4395     FIXME("Stub: %p %ld %ld %p %ld %p\n", hPrinter, JobId, Level, pJob,
4396           cbBuf, pcbNeeded);
4397     return FALSE;
4398 }
4399
4400 /*****************************************************************************
4401  *          GetJobW [WINSPOOL.@]
4402  *
4403  */
4404 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
4405                            DWORD cbBuf, LPDWORD pcbNeeded)
4406 {
4407     FIXME("Stub: %p %ld %ld %p %ld %p\n", hPrinter, JobId, Level, pJob,
4408           cbBuf, pcbNeeded);
4409     return FALSE;
4410 }
4411
4412 /*****************************************************************************
4413  *          ScheduleJob [WINSPOOL.@]
4414  *
4415  */
4416 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
4417 {
4418     FIXME("Stub: %p %lx\n", hPrinter, dwJobID);
4419     return FALSE;
4420 }