winspool: Document the monitor functions.
[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_UNISTD_H
35 # include <unistd.h>
36 #endif
37 #include <signal.h>
38 #ifdef HAVE_CUPS_CUPS_H
39 # include <cups/cups.h>
40 # ifndef SONAME_LIBCUPS
41 #  define SONAME_LIBCUPS "libcups.so"
42 # endif
43 #endif
44
45 #define NONAMELESSUNION
46 #define NONAMELESSSTRUCT
47 #include "wine/library.h"
48 #include "windef.h"
49 #include "winbase.h"
50 #include "winuser.h"
51 #include "winerror.h"
52 #include "winreg.h"
53 #include "wingdi.h"
54 #include "winspool.h"
55 #include "winternl.h"
56 #include "wine/windef16.h"
57 #include "wine/unicode.h"
58 #include "wine/debug.h"
59 #include "wine/list.h"
60 #include "heap.h"
61 #include "winnls.h"
62
63 #include "wspool.h"
64
65 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
66
67 static CRITICAL_SECTION printer_handles_cs;
68 static CRITICAL_SECTION_DEBUG printer_handles_cs_debug = 
69 {
70     0, 0, &printer_handles_cs,
71     { &printer_handles_cs_debug.ProcessLocksList, &printer_handles_cs_debug.ProcessLocksList },
72       0, 0, { (DWORD_PTR)(__FILE__ ": printer_handles_cs") }
73 };
74 static CRITICAL_SECTION printer_handles_cs = { &printer_handles_cs_debug, -1, 0, 0, 0, 0 };
75
76 typedef struct {
77     DWORD job_id;
78     HANDLE hf;
79 } started_doc_t;
80
81 typedef struct {
82     struct list jobs;
83     LONG ref;
84 } jobqueue_t;
85
86 typedef struct {
87     LPWSTR name;
88     jobqueue_t *queue;
89     started_doc_t *doc;
90 } opened_printer_t;
91
92 typedef struct {
93     struct list entry;
94     DWORD job_id;
95     WCHAR *filename;
96     WCHAR *document_title;
97 } job_t;
98
99 static opened_printer_t **printer_handles;
100 static int nb_printer_handles;
101 static LONG next_job_id = 1;
102
103 static DWORD (WINAPI *GDI_CallDeviceCapabilities16)( LPCSTR lpszDevice, LPCSTR lpszPort,
104                                                      WORD fwCapability, LPSTR lpszOutput,
105                                                      LPDEVMODEA lpdm );
106 static INT (WINAPI *GDI_CallExtDeviceMode16)( HWND hwnd, LPDEVMODEA lpdmOutput,
107                                               LPSTR lpszDevice, LPSTR lpszPort,
108                                               LPDEVMODEA lpdmInput, LPSTR lpszProfile,
109                                               DWORD fwMode );
110
111 static const char Printers[] =
112 "System\\CurrentControlSet\\control\\Print\\Printers\\";
113
114 static const WCHAR DriversW[] = { 'S','y','s','t','e','m','\\',
115                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
116                                   'c','o','n','t','r','o','l','\\',
117                                   'P','r','i','n','t','\\',
118                                   'E','n','v','i','r','o','n','m','e','n','t','s','\\',
119                                   '%','s','\\','D','r','i','v','e','r','s','\\',0 };
120
121 static const WCHAR user_default_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
122                                               'M','i','c','r','o','s','o','f','t','\\',
123                                               'W','i','n','d','o','w','s',' ','N','T','\\',
124                                               'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
125                                               'W','i','n','d','o','w','s',0};
126
127 static const WCHAR user_printers_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
128                                                'M','i','c','r','o','s','o','f','t','\\',
129                                                'W','i','n','d','o','w','s',' ','N','T','\\',
130                                                'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
131                                                'D','e','v','i','c','e','s',0};
132
133 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
134
135 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
136                                       'i','o','n',' ','F','i','l','e',0};
137 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
138 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
139 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v',
140                                    'M','o','d','e',0};
141 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F',
142                                    'i','l','e','s',0};
143 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
144 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
145 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
146 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
147 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
148 static const WCHAR NameW[] = {'N','a','m','e',0};
149 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
150 static const WCHAR PortW[] = {'P','o','r','t',0};
151 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e',
152                                    's','s','o','r',0};
153 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i',
154                                   'v','e','r',0};
155 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i',
156                                      'v','e','r','D','a','t','a',0};
157 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F',
158                                   'i','l','e',0};
159 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
160 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
161 static const WCHAR deviceW[]  = {'d','e','v','i','c','e',0};
162 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
163 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
164 static const WCHAR emptyStringW[] = {0};
165
166 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
167
168 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
169 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
170 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
171
172 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
173                                           'D','o','c','u','m','e','n','t',0};
174
175 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode);
176 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
177                                       DWORD Level, LPBYTE pDriverInfo,
178                                       DWORD cbBuf, LPDWORD pcbNeeded,
179                                       BOOL unicode);
180 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey);
181
182 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
183    if passed a NULL string. This returns NULLs to the result. 
184 */
185 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
186 {
187     if ( (src) )
188     {
189         RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
190         return usBufferPtr->Buffer;
191     }
192     usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
193     return NULL;
194 }
195             
196 static LPWSTR strdupW(LPCWSTR p)
197 {
198     LPWSTR ret;
199     DWORD len;
200
201     if(!p) return NULL;
202     len = (strlenW(p) + 1) * sizeof(WCHAR);
203     ret = HeapAlloc(GetProcessHeap(), 0, len);
204     memcpy(ret, p, len);
205     return ret;
206 }
207
208 static void
209 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name,BOOL force) {
210     char qbuf[200];
211
212     /* If forcing, or no profile string entry for device yet, set the entry
213      *
214      * The always change entry if not WINEPS yet is discussable.
215      */
216     if (force                                                           ||
217         !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf))    ||
218         !strcmp(qbuf,"*")                                               ||
219         !strstr(qbuf,"WINEPS.DRV")
220     ) {
221         char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
222         HKEY hkey;
223
224         sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
225         WriteProfileStringA("windows","device",buf);
226         if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
227             RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
228             RegCloseKey(hkey);
229         }
230         HeapFree(GetProcessHeap(),0,buf);
231     }
232 }
233
234 #ifdef HAVE_CUPS_CUPS_H
235 static typeof(cupsGetDests)  *pcupsGetDests;
236 static typeof(cupsGetPPD)    *pcupsGetPPD;
237 static typeof(cupsPrintFile) *pcupsPrintFile;
238 static void *cupshandle;
239
240 static BOOL CUPS_LoadPrinters(void)
241 {
242     int                   i, nrofdests;
243     BOOL                  hadprinter = FALSE;
244     cups_dest_t          *dests;
245     PRINTER_INFO_2A       pinfo2a;
246     char   *port,*devline;
247     HKEY hkeyPrinter, hkeyPrinters, hkey;
248
249     cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, NULL, 0);
250     if (!cupshandle) 
251         return FALSE;
252     TRACE("loaded %s\n", SONAME_LIBCUPS);
253
254 #define DYNCUPS(x)                                      \
255         p##x = wine_dlsym(cupshandle, #x, NULL,0);      \
256         if (!p##x) return FALSE;
257
258     DYNCUPS(cupsGetPPD);
259     DYNCUPS(cupsGetDests);
260     DYNCUPS(cupsPrintFile);
261 #undef DYNCUPS
262
263     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
264        ERROR_SUCCESS) {
265         ERR("Can't create Printers key\n");
266         return FALSE;
267     }
268
269     nrofdests = pcupsGetDests(&dests);
270     TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
271     for (i=0;i<nrofdests;i++) {
272         port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
273         sprintf(port,"LPR:%s",dests[i].name);
274         devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
275         sprintf(devline,"WINEPS.DRV,%s",port);
276         WriteProfileStringA("devices",dests[i].name,devline);
277         if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
278             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
279             RegCloseKey(hkey);
280         }
281         HeapFree(GetProcessHeap(),0,devline);
282
283         TRACE("Printer %d: %s\n", i, dests[i].name);
284         if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
285             /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
286                and continue */
287             TRACE("Printer already exists\n");
288             RegDeleteValueW(hkeyPrinter, May_Delete_Value);
289             RegCloseKey(hkeyPrinter);
290         } else {
291             memset(&pinfo2a,0,sizeof(pinfo2a));
292             pinfo2a.pPrinterName        = dests[i].name;
293             pinfo2a.pDatatype   = "RAW";
294             pinfo2a.pPrintProcessor     = "WinPrint";
295             pinfo2a.pDriverName = "PS Driver";
296             pinfo2a.pComment    = "WINEPS Printer using CUPS";
297             pinfo2a.pLocation   = "<physical location of printer>";
298             pinfo2a.pPortName   = port;
299             pinfo2a.pParameters = "<parameters?>";
300             pinfo2a.pShareName  = "<share name?>";
301             pinfo2a.pSepFile    = "<sep file?>";
302
303             if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
304                 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
305                     ERR("printer '%s' not added by AddPrinterA (error %ld)\n",dests[i].name,GetLastError());
306             }
307         }
308         HeapFree(GetProcessHeap(),0,port);
309
310         hadprinter = TRUE;
311         if (dests[i].is_default)
312             WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
313     }
314     RegCloseKey(hkeyPrinters);
315     return hadprinter;
316 }
317 #endif
318
319 static BOOL
320 PRINTCAP_ParseEntry(char *pent,BOOL isfirst) {
321     PRINTER_INFO_2A     pinfo2a;
322     char                *e,*s,*name,*prettyname,*devname;
323     BOOL                ret = FALSE, set_default = FALSE;
324     char                *port,*devline,*env_default;
325     HKEY                hkeyPrinter, hkeyPrinters, hkey;
326
327     while (isspace(*pent)) pent++;
328     s = strchr(pent,':');
329     if(s) *s='\0';
330     name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
331     strcpy(name,pent);
332     if(s) {
333         *s=':';
334         pent = s;
335     } else
336         pent = "";
337
338     TRACE("name=%s entry=%s\n",name, pent);
339
340     if(ispunct(*name)) { /* a tc entry, not a real printer */
341         TRACE("skipping tc entry\n");
342         goto end;
343     }
344
345     if(strstr(pent,":server")) { /* server only version so skip */
346         TRACE("skipping server entry\n");
347         goto end;
348     }
349
350     /* Determine whether this is a postscript printer. */
351
352     ret = TRUE;
353     env_default = getenv("PRINTER");
354     prettyname = name;
355     /* Get longest name, usually the one at the right for later display. */
356     while((s=strchr(prettyname,'|'))) {
357         *s = '\0';
358         e = s;
359         while(isspace(*--e)) *e = '\0';
360         TRACE("\t%s\n", debugstr_a(prettyname));
361         if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
362         for(prettyname = s+1; isspace(*prettyname); prettyname++)
363             ;
364     }
365     e = prettyname + strlen(prettyname);
366     while(isspace(*--e)) *e = '\0';
367     TRACE("\t%s\n", debugstr_a(prettyname));
368     if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
369
370     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
371      * if it is too long, we use it as comment below. */
372     devname = prettyname;
373     if (strlen(devname)>=CCHDEVICENAME-1)
374          devname = name;
375     if (strlen(devname)>=CCHDEVICENAME-1) {
376         ret = FALSE;
377         goto end;
378     }
379
380     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
381     sprintf(port,"LPR:%s",name);
382
383     devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
384     sprintf(devline,"WINEPS.DRV,%s",port);
385     WriteProfileStringA("devices",devname,devline);
386     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
387         RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
388         RegCloseKey(hkey);
389     }
390     HeapFree(GetProcessHeap(),0,devline);
391     
392     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
393        ERROR_SUCCESS) {
394         ERR("Can't create Printers key\n");
395         ret = FALSE;
396         goto end;
397     }
398     if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
399         /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
400            and continue */
401         TRACE("Printer already exists\n");
402         RegDeleteValueW(hkeyPrinter, May_Delete_Value);
403         RegCloseKey(hkeyPrinter);
404     } else {
405         memset(&pinfo2a,0,sizeof(pinfo2a));
406         pinfo2a.pPrinterName            = devname;
407         pinfo2a.pDatatype               = "RAW";
408         pinfo2a.pPrintProcessor         = "WinPrint";
409         pinfo2a.pDriverName             = "PS Driver";
410         pinfo2a.pComment                = "WINEPS Printer using LPR";
411         pinfo2a.pLocation               = prettyname;
412         pinfo2a.pPortName               = port;
413         pinfo2a.pParameters             = "<parameters?>";
414         pinfo2a.pShareName              = "<share name?>";
415         pinfo2a.pSepFile                = "<sep file?>";
416
417         if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
418             if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
419                 ERR("%s not added by AddPrinterA (%ld)\n",name,GetLastError());
420         }
421     }
422     RegCloseKey(hkeyPrinters);
423
424     if (isfirst || set_default)
425         WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
426
427     HeapFree(GetProcessHeap(), 0, port);
428  end:
429     HeapFree(GetProcessHeap(), 0, name);
430     return ret;
431 }
432
433 static BOOL
434 PRINTCAP_LoadPrinters(void) {
435     BOOL                hadprinter = FALSE;
436     char                buf[200];
437     FILE                *f;
438     char *pent = NULL;
439     BOOL had_bash = FALSE;
440
441     f = fopen("/etc/printcap","r");
442     if (!f)
443         return FALSE;
444
445     while(fgets(buf,sizeof(buf),f)) {
446         char *start, *end;
447
448         end=strchr(buf,'\n');
449         if (end) *end='\0';
450     
451         start = buf;
452         while(isspace(*start)) start++;
453         if(*start == '#' || *start == '\0')
454             continue;
455
456         if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
457             hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
458             HeapFree(GetProcessHeap(),0,pent);
459             pent = NULL;
460         }
461
462         if (end && *--end == '\\') {
463             *end = '\0';
464             had_bash = TRUE;
465         } else
466             had_bash = FALSE;
467
468         if (pent) {
469             pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
470             strcat(pent,start);
471         } else {
472             pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
473             strcpy(pent,start);
474         }
475
476     }
477     if(pent) {
478         hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
479         HeapFree(GetProcessHeap(),0,pent);
480     }
481     fclose(f);
482     return hadprinter;
483 }
484
485 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
486 {
487     if (value)
488         return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
489                    lstrlenW(value) * sizeof(WCHAR));
490     else
491         return ERROR_FILE_NOT_FOUND;
492 }
493
494 void WINSPOOL_LoadSystemPrinters(void)
495 {
496     HKEY                hkey, hkeyPrinters;
497     DRIVER_INFO_3A      di3a;
498     HANDLE              hprn;
499     DWORD               needed, num, i;
500     WCHAR               PrinterName[256];
501     BOOL                done = FALSE;
502
503     di3a.cVersion = 0x400;
504     di3a.pName = "PS Driver";
505     di3a.pEnvironment = NULL;   /* NULL means auto */
506     di3a.pDriverPath = "wineps16";
507     di3a.pDataFile = "<datafile?>";
508     di3a.pConfigFile = "wineps16";
509     di3a.pHelpFile = "<helpfile?>";
510     di3a.pDependentFiles = "<dependend files?>";
511     di3a.pMonitorName = "<monitor name?>";
512     di3a.pDefaultDataType = "RAW";
513
514     if (!AddPrinterDriverA(NULL,3,(LPBYTE)&di3a)) {
515         ERR("Failed adding PS Driver (%ld)\n",GetLastError());
516         return;
517     }
518
519     /* This ensures that all printer entries have a valid Name value.  If causes
520        problems later if they don't.  If one is found to be missed we create one
521        and set it equal to the name of the key */
522     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) == ERROR_SUCCESS) {
523         if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
524                             NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
525             for(i = 0; i < num; i++) {
526                 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) == ERROR_SUCCESS) {
527                     if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
528                         if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
529                             set_reg_szW(hkey, NameW, PrinterName);
530                         }
531                         RegCloseKey(hkey);
532                     }
533                 }
534             }
535         }
536         RegCloseKey(hkeyPrinters);
537     }
538
539     /* We want to avoid calling AddPrinter on printers as much as
540        possible, because on cups printers this will (eventually) lead
541        to a call to cupsGetPPD which takes forever, even with non-cups
542        printers AddPrinter takes a while.  So we'll tag all printers that
543        were automatically added last time around, if they still exist
544        we'll leave them be otherwise we'll delete them. */
545     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
546     if(needed) {
547         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
548         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
549             for(i = 0; i < num; i++) {
550                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
551                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
552                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
553                             DWORD dw = 1;
554                             RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
555                             RegCloseKey(hkey);
556                         }
557                         ClosePrinter(hprn);
558                     }
559                 }
560             }
561         }
562         HeapFree(GetProcessHeap(), 0, pi);
563     }
564
565
566 #ifdef HAVE_CUPS_CUPS_H
567     done = CUPS_LoadPrinters();
568 #endif
569
570     if(!done) { /* If we have any CUPS based printers, skip looking for printcap printers */
571         /* Check for [ppd] section in config file before parsing /etc/printcap */
572         /* @@ Wine registry key: HKCU\Software\Wine\Printing\PPD Files */
573         if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Printing\\PPD Files",
574                         &hkey) == ERROR_SUCCESS) {
575             RegCloseKey(hkey);
576             PRINTCAP_LoadPrinters();
577         }
578     }
579
580     /* Now enumerate the list again and delete any printers that a still tagged */
581     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
582     if(needed) {
583         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
584         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
585             for(i = 0; i < num; i++) {
586                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
587                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
588                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
589                             DWORD dw, type, size = sizeof(dw);
590                             if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
591                                 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
592                                 DeletePrinter(hprn);
593                             }
594                             RegCloseKey(hkey);
595                         }
596                         ClosePrinter(hprn);
597                     }
598                 }
599             }
600         }
601         HeapFree(GetProcessHeap(), 0, pi);
602     }
603
604     return;
605
606 }
607
608
609 /******************************************************************
610  *  get_opened_printer_entry
611  *  Get the first place empty in the opened printer table
612  */
613 static HANDLE get_opened_printer_entry( LPCWSTR name )
614 {
615     UINT_PTR handle = nb_printer_handles, i;
616     jobqueue_t *queue = NULL;
617     opened_printer_t *printer;
618
619     EnterCriticalSection(&printer_handles_cs);
620
621     for (i = 0; i < nb_printer_handles; i++)
622     {
623         if (!printer_handles[i])
624         {
625             if(handle == nb_printer_handles)
626                 handle = i;
627         }
628         else if(!queue && !strcmpW(name, printer_handles[i]->name))
629             queue = printer_handles[i]->queue;
630     }
631
632     if (handle >= nb_printer_handles)
633     {
634         opened_printer_t **new_array;
635         if (printer_handles)
636             new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
637                                      (nb_printer_handles + 16) * sizeof(*new_array) );
638         else
639             new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
640                                    (nb_printer_handles + 16) * sizeof(*new_array) );
641
642         if (!new_array)
643         {
644             handle = 0;
645             goto end;
646         }
647         printer_handles = new_array;
648         nb_printer_handles += 16;
649     }
650
651     if (!(printer = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer))))
652     {
653         handle = 0;
654         goto end;
655     }
656
657     printer->name = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
658     strcpyW(printer->name, name);
659     if(queue)
660         printer->queue = queue;
661     else
662     {
663         printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
664         list_init(&printer->queue->jobs);
665         printer->queue->ref = 0;
666     }
667     InterlockedIncrement(&printer->queue->ref);
668     printer->doc = NULL;
669
670     printer_handles[handle] = printer;
671     handle++;
672 end:
673     LeaveCriticalSection(&printer_handles_cs);
674
675     return (HANDLE)handle;
676 }
677
678 /******************************************************************
679  *  get_opened_printer
680  *  Get the pointer to the opened printer referred by the handle
681  */
682 static opened_printer_t *get_opened_printer(HANDLE hprn)
683 {
684     UINT_PTR idx = (UINT_PTR)hprn;
685     opened_printer_t *ret = NULL;
686
687     EnterCriticalSection(&printer_handles_cs);
688
689     if ((idx <= 0) || (idx > nb_printer_handles))
690         goto end;
691
692     ret = printer_handles[idx - 1];
693 end:
694     LeaveCriticalSection(&printer_handles_cs);
695     return ret;
696 }
697
698 /******************************************************************
699  *  get_opened_printer_name
700  *  Get the pointer to the opened printer name referred by the handle
701  */
702 static LPCWSTR get_opened_printer_name(HANDLE hprn)
703 {
704     opened_printer_t *printer = get_opened_printer(hprn);
705     if(!printer) return NULL;
706     return printer->name;
707 }
708
709 /******************************************************************
710  *  WINSPOOL_GetOpenedPrinterRegKey
711  *
712  */
713 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
714 {
715     LPCWSTR name = get_opened_printer_name(hPrinter);
716     DWORD ret;
717     HKEY hkeyPrinters;
718
719     if(!name) return ERROR_INVALID_HANDLE;
720
721     if((ret = RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters)) !=
722        ERROR_SUCCESS)
723         return ret;
724
725     if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
726     {
727         ERR("Can't find opened printer %s in registry\n",
728             debugstr_w(name));
729         RegCloseKey(hkeyPrinters);
730         return ERROR_INVALID_PRINTER_NAME; /* ? */
731     }
732     RegCloseKey(hkeyPrinters);
733     return ERROR_SUCCESS;
734 }
735
736 /******************************************************************
737  *                  get_job
738  *
739  *  Get the pointer to the specified job.
740  *  Should hold the printer_handles_cs before calling.
741  */
742 static job_t *get_job(HANDLE hprn, DWORD JobId)
743 {
744     opened_printer_t *printer = get_opened_printer(hprn);
745     job_t *job;
746
747     if(!printer) return NULL;
748     LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
749     {
750         if(job->job_id == JobId)
751             return job;
752     }
753     return NULL;
754 }
755
756 /***********************************************************
757  *      DEVMODEcpyAtoW
758  */
759 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
760 {
761     BOOL Formname;
762     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
763     DWORD size;
764
765     Formname = (dmA->dmSize > off_formname);
766     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
767     MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
768                         dmW->dmDeviceName, CCHDEVICENAME);
769     if(!Formname) {
770       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
771              dmA->dmSize - CCHDEVICENAME);
772     } else {
773       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
774              off_formname - CCHDEVICENAME);
775       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
776                           dmW->dmFormName, CCHFORMNAME);
777       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
778              (off_formname + CCHFORMNAME));
779     }
780     dmW->dmSize = size;
781     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
782            dmA->dmDriverExtra);
783     return dmW;
784 }
785
786 /***********************************************************
787  *      DEVMODEdupWtoA
788  * Creates an ascii copy of supplied devmode on heap
789  */
790 static LPDEVMODEA DEVMODEdupWtoA(HANDLE heap, const DEVMODEW *dmW)
791 {
792     LPDEVMODEA dmA;
793     DWORD size;
794     BOOL Formname;
795     ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;
796
797     if(!dmW) return NULL;
798     Formname = (dmW->dmSize > off_formname);
799     size = dmW->dmSize - CCHDEVICENAME - (Formname ? CCHFORMNAME : 0);
800     dmA = HeapAlloc(heap, HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
801     WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
802                         (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
803     if(!Formname) {
804       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
805              dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR));
806     } else {
807       memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
808              off_formname - CCHDEVICENAME * sizeof(WCHAR));
809       WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
810                           (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
811       memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
812              (off_formname + CCHFORMNAME * sizeof(WCHAR)));
813     }
814     dmA->dmSize = size;
815     memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
816            dmW->dmDriverExtra);
817     return dmA;
818 }
819
820 /***********************************************************
821  *             PRINTER_INFO_2AtoW
822  * Creates a unicode copy of PRINTER_INFO_2A on heap
823  */
824 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
825 {
826     LPPRINTER_INFO_2W piW;
827     UNICODE_STRING usBuffer;
828
829     if(!piA) return NULL;
830     piW = HeapAlloc(heap, 0, sizeof(*piW));
831     memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
832     
833     piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
834     piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
835     piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
836     piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
837     piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
838     piW->pComment = asciitounicode(&usBuffer,piA->pComment);
839     piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
840     piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
841     piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
842     piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
843     piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
844     piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
845     return piW;
846 }
847
848 /***********************************************************
849  *       FREE_PRINTER_INFO_2W
850  * Free PRINTER_INFO_2W and all strings
851  */
852 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
853 {
854     if(!piW) return;
855
856     HeapFree(heap,0,piW->pServerName);
857     HeapFree(heap,0,piW->pPrinterName);
858     HeapFree(heap,0,piW->pShareName);
859     HeapFree(heap,0,piW->pPortName);
860     HeapFree(heap,0,piW->pDriverName);
861     HeapFree(heap,0,piW->pComment);
862     HeapFree(heap,0,piW->pLocation);
863     HeapFree(heap,0,piW->pDevMode);
864     HeapFree(heap,0,piW->pSepFile);
865     HeapFree(heap,0,piW->pPrintProcessor);
866     HeapFree(heap,0,piW->pDatatype);
867     HeapFree(heap,0,piW->pParameters);
868     HeapFree(heap,0,piW);
869     return;
870 }
871
872 /******************************************************************
873  *              DeviceCapabilities     [WINSPOOL.@]
874  *              DeviceCapabilitiesA    [WINSPOOL.@]
875  *
876  */
877 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
878                                LPSTR pOutput, LPDEVMODEA lpdm)
879 {
880     INT ret;
881
882     if (!GDI_CallDeviceCapabilities16)
883     {
884         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
885                                                               (LPCSTR)104 );
886         if (!GDI_CallDeviceCapabilities16) return -1;
887     }
888     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
889
890     /* If DC_PAPERSIZE map POINT16s to POINTs */
891     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
892         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
893         POINT *pt = (POINT *)pOutput;
894         INT i;
895         memcpy(tmp, pOutput, ret * sizeof(POINT16));
896         for(i = 0; i < ret; i++, pt++)
897         {
898             pt->x = tmp[i].x;
899             pt->y = tmp[i].y;
900         }
901         HeapFree( GetProcessHeap(), 0, tmp );
902     }
903     return ret;
904 }
905
906
907 /*****************************************************************************
908  *          DeviceCapabilitiesW        [WINSPOOL.@]
909  *
910  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
911  *
912  */
913 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
914                                WORD fwCapability, LPWSTR pOutput,
915                                const DEVMODEW *pDevMode)
916 {
917     LPDEVMODEA dmA = DEVMODEdupWtoA(GetProcessHeap(), pDevMode);
918     LPSTR pDeviceA = HEAP_strdupWtoA(GetProcessHeap(),0,pDevice);
919     LPSTR pPortA = HEAP_strdupWtoA(GetProcessHeap(),0,pPort);
920     INT ret;
921
922     if(pOutput && (fwCapability == DC_BINNAMES ||
923                    fwCapability == DC_FILEDEPENDENCIES ||
924                    fwCapability == DC_PAPERNAMES)) {
925       /* These need A -> W translation */
926         INT size = 0, i;
927         LPSTR pOutputA;
928         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
929                                   dmA);
930         if(ret == -1)
931             return ret;
932         switch(fwCapability) {
933         case DC_BINNAMES:
934             size = 24;
935             break;
936         case DC_PAPERNAMES:
937         case DC_FILEDEPENDENCIES:
938             size = 64;
939             break;
940         }
941         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
942         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
943                                   dmA);
944         for(i = 0; i < ret; i++)
945             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
946                                 pOutput + (i * size), size);
947         HeapFree(GetProcessHeap(), 0, pOutputA);
948     } else {
949         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
950                                   (LPSTR)pOutput, dmA);
951     }
952     HeapFree(GetProcessHeap(),0,pPortA);
953     HeapFree(GetProcessHeap(),0,pDeviceA);
954     HeapFree(GetProcessHeap(),0,dmA);
955     return ret;
956 }
957
958 /******************************************************************
959  *              DocumentPropertiesA   [WINSPOOL.@]
960  *
961  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
962  */
963 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
964                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
965                                 LPDEVMODEA pDevModeInput,DWORD fMode )
966 {
967     LPSTR lpName = pDeviceName;
968     LONG ret;
969
970     TRACE("(%p,%p,%s,%p,%p,%ld)\n",
971         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
972     );
973
974     if(!pDeviceName) {
975         LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
976         if(!lpNameW) {
977                 ERR("no name from hPrinter?\n");
978                 SetLastError(ERROR_INVALID_HANDLE);
979                 return -1;
980         }
981         lpName = HEAP_strdupWtoA(GetProcessHeap(),0,lpNameW);
982     }
983
984     if (!GDI_CallExtDeviceMode16)
985     {
986         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
987                                                          (LPCSTR)102 );
988         if (!GDI_CallExtDeviceMode16) {
989                 ERR("No CallExtDeviceMode16?\n");
990                 return -1;
991         }
992     }
993     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, "LPT1:",
994                                   pDevModeInput, NULL, fMode);
995
996     if(!pDeviceName)
997         HeapFree(GetProcessHeap(),0,lpName);
998     return ret;
999 }
1000
1001
1002 /*****************************************************************************
1003  *          DocumentPropertiesW (WINSPOOL.@)
1004  *
1005  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1006  */
1007 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1008                                 LPWSTR pDeviceName,
1009                                 LPDEVMODEW pDevModeOutput,
1010                                 LPDEVMODEW pDevModeInput, DWORD fMode)
1011 {
1012
1013     LPSTR pDeviceNameA = HEAP_strdupWtoA(GetProcessHeap(),0,pDeviceName);
1014     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(GetProcessHeap(),pDevModeInput);
1015     LPDEVMODEA pDevModeOutputA = NULL;
1016     LONG ret;
1017
1018     TRACE("(%p,%p,%s,%p,%p,%ld)\n",
1019           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1020           fMode);
1021     if(pDevModeOutput) {
1022         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1023         if(ret < 0) return ret;
1024         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1025     }
1026     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1027                               pDevModeInputA, fMode);
1028     if(pDevModeOutput) {
1029         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1030         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1031     }
1032     if(fMode == 0 && ret > 0)
1033         ret += (CCHDEVICENAME + CCHFORMNAME);
1034     HeapFree(GetProcessHeap(),0,pDevModeInputA);
1035     HeapFree(GetProcessHeap(),0,pDeviceNameA);
1036     return ret;
1037 }
1038
1039 /******************************************************************
1040  *              OpenPrinterA        [WINSPOOL.@]
1041  *
1042  */
1043 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
1044                          LPPRINTER_DEFAULTSA pDefault)
1045 {
1046     UNICODE_STRING lpPrinterNameW;
1047     UNICODE_STRING usBuffer;
1048     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
1049     PWSTR pwstrPrinterNameW;
1050     BOOL ret;
1051
1052     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
1053
1054     if(pDefault) {
1055         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
1056         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
1057         DefaultW.DesiredAccess = pDefault->DesiredAccess;
1058         pDefaultW = &DefaultW;
1059     }
1060     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
1061     if(pDefault) {
1062         RtlFreeUnicodeString(&usBuffer);
1063         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
1064     }
1065     RtlFreeUnicodeString(&lpPrinterNameW);
1066     return ret;
1067 }
1068
1069 /******************************************************************
1070  *              OpenPrinterW        [WINSPOOL.@]
1071  *
1072  */
1073 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter,
1074                          LPPRINTER_DEFAULTSW pDefault)
1075 {
1076     HKEY hkeyPrinters, hkeyPrinter;
1077
1078     if (!lpPrinterName) {
1079        FIXME("(printerName: NULL, pDefault %p Ret: False\n", pDefault);
1080        SetLastError(ERROR_INVALID_PARAMETER);
1081        return FALSE;
1082     }
1083
1084     TRACE("(printerName: %s, pDefault %p)\n", debugstr_w(lpPrinterName),
1085           pDefault);
1086
1087     /* Check Printer exists */
1088     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
1089        ERROR_SUCCESS) {
1090         ERR("Can't create Printers key\n");
1091         SetLastError(ERROR_FILE_NOT_FOUND); /* ?? */
1092         return FALSE;
1093     }
1094
1095     if(lpPrinterName[0] == '\0' || /* explicitly exclude "" */
1096        RegOpenKeyW(hkeyPrinters, lpPrinterName, &hkeyPrinter)
1097        != ERROR_SUCCESS) {
1098         TRACE("Can't find printer %s in registry\n",
1099               debugstr_w(lpPrinterName));
1100         RegCloseKey(hkeyPrinters);
1101         SetLastError(ERROR_INVALID_PRINTER_NAME);
1102         return FALSE;
1103     }
1104     RegCloseKey(hkeyPrinter);
1105     RegCloseKey(hkeyPrinters);
1106
1107     if(!phPrinter) /* This seems to be what win95 does anyway */
1108         return TRUE;
1109
1110     /* Get the unique handle of the printer*/
1111     *phPrinter = get_opened_printer_entry( lpPrinterName );
1112
1113     if (pDefault != NULL)
1114         FIXME("Not handling pDefault\n");
1115
1116     return TRUE;
1117 }
1118
1119 /******************************************************************
1120  *              AddMonitorA        [WINSPOOL.@]
1121  *
1122  * See AddMonitorW.
1123  *
1124  */
1125 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
1126 {
1127     FIXME("(%s,0x%08lx,%p), stub!\n", debugstr_a(pName), Level, pMonitors);
1128     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1129     return FALSE;
1130 }
1131
1132 /******************************************************************************
1133  *              AddMonitorW        [WINSPOOL.@]
1134  *
1135  * Install a Printmonitor
1136  *
1137  * PARAMS
1138  *  pName       [I] Servername or NULL (local Computer)
1139  *  Level       [I] Structure-Level (Must be 2)
1140  *  pMonitors   [I] PTR to MONITOR_INFO_2
1141  *
1142  * RETURNS
1143  *  Success: TRUE
1144  *  Failure: FALSE
1145  *
1146  * NOTES
1147  *  All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1148  *
1149  * BUGS
1150  *  only a Stub
1151  *
1152  */
1153 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1154 {
1155     FIXME("(%s,0x%08lx,%p), stub!\n",debugstr_w(pName), Level, pMonitors);
1156     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1157     return FALSE;
1158 }
1159
1160 /******************************************************************
1161  *              DeletePrinterDriverA        [WINSPOOL.@]
1162  *
1163  */
1164 BOOL WINAPI
1165 DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
1166 {
1167     FIXME("(%s,%s,%s):stub\n",debugstr_a(pName),debugstr_a(pEnvironment),
1168           debugstr_a(pDriverName));
1169     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1170     return FALSE;
1171 }
1172
1173 /******************************************************************
1174  *              DeletePrinterDriverW        [WINSPOOL.@]
1175  *
1176  */
1177 BOOL WINAPI
1178 DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
1179 {
1180     FIXME("(%s,%s,%s):stub\n",debugstr_w(pName),debugstr_w(pEnvironment),
1181           debugstr_w(pDriverName));
1182     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1183     return FALSE;
1184 }
1185
1186 /******************************************************************
1187  *              DeleteMonitorA        [WINSPOOL.@]
1188  *
1189  * See DeleteMonitorW.
1190  *
1191  */
1192 BOOL WINAPI
1193 DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
1194 {
1195     FIXME("(%s,%s,%s):stub\n",debugstr_a(pName),debugstr_a(pEnvironment),
1196           debugstr_a(pMonitorName));
1197     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1198     return FALSE;
1199 }
1200
1201 /******************************************************************
1202  *              DeleteMonitorW        [WINSPOOL.@]
1203  *
1204  * Delete a specific Printmonitor from a Printing-Environment
1205  *
1206  * PARAMS
1207  *  pName        [I] Servername or NULL (local Computer)
1208  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1209  *  pMonitorName [I] Name of the Monitor, that should be deleted
1210  *
1211  * RETURNS
1212  *  Success: TRUE
1213  *  Failure: FALSE
1214  *
1215  * BUGS
1216  *  only a Stub
1217  *
1218  */
1219 BOOL WINAPI
1220 DeleteMonitorW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1221 {
1222     FIXME("(%s,%s,%s):stub\n",debugstr_w(pName),debugstr_w(pEnvironment),
1223           debugstr_w(pMonitorName));
1224     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1225     return FALSE;
1226 }
1227
1228 /******************************************************************
1229  *              DeletePortA        [WINSPOOL.@]
1230  *
1231  * See DeletePortW.
1232  *
1233  */
1234 BOOL WINAPI
1235 DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
1236 {
1237     FIXME("(%s,%p,%s):stub\n",debugstr_a(pName),hWnd,
1238           debugstr_a(pPortName));
1239     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1240     return FALSE;
1241 }
1242
1243 /******************************************************************
1244  *              DeletePortW        [WINSPOOL.@]
1245  *
1246  * Delete a specific Port
1247  *
1248  * PARAMS
1249  *  pName     [I] Servername or NULL (local Computer)
1250  *  hWnd      [I] Handle to parent Window for the Dialog-Box
1251  *  pPortName [I] Name of the Port, that should be deleted
1252  *
1253  * RETURNS
1254  *  Success: TRUE
1255  *  Failure: FALSE
1256  *
1257  * BUGS
1258  *  only a Stub
1259  *
1260  */
1261 BOOL WINAPI
1262 DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1263 {
1264     FIXME("(%s,%p,%s):stub\n",debugstr_w(pName),hWnd,
1265           debugstr_w(pPortName));
1266     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1267     return FALSE;
1268 }
1269
1270 /******************************************************************************
1271  *    SetPrinterW  [WINSPOOL.@]
1272  */
1273 BOOL WINAPI
1274 SetPrinterW(
1275   HANDLE  hPrinter,
1276   DWORD     Level,
1277   LPBYTE    pPrinter,
1278   DWORD     Command) {
1279
1280     FIXME("():stub\n");
1281     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1282     return FALSE;
1283 }
1284
1285 /******************************************************************************
1286  *    WritePrinter  [WINSPOOL.@]
1287  */
1288 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
1289 {
1290     opened_printer_t *printer;
1291     BOOL ret = FALSE;
1292
1293     TRACE("(%p, %p, %ld, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
1294
1295     EnterCriticalSection(&printer_handles_cs);
1296     printer = get_opened_printer(hPrinter);
1297     if(!printer)
1298     {
1299         SetLastError(ERROR_INVALID_HANDLE);
1300         goto end;
1301     }
1302
1303     if(!printer->doc)
1304     {
1305         SetLastError(ERROR_SPL_NO_STARTDOC);
1306         goto end;
1307     }
1308
1309     ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
1310 end:
1311     LeaveCriticalSection(&printer_handles_cs);
1312     return ret;
1313 }
1314
1315 /*****************************************************************************
1316  *          AddFormA  [WINSPOOL.@]
1317  */
1318 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1319 {
1320     FIXME("(%p,%ld,%p): stub\n", hPrinter, Level, pForm);
1321     return 1;
1322 }
1323
1324 /*****************************************************************************
1325  *          AddFormW  [WINSPOOL.@]
1326  */
1327 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
1328 {
1329     FIXME("(%p,%ld,%p): stub\n", hPrinter, Level, pForm);
1330     return 1;
1331 }
1332
1333 /*****************************************************************************
1334  *          AddJobA  [WINSPOOL.@]
1335  */
1336 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
1337 {
1338     BOOL ret;
1339     BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
1340     DWORD needed;
1341
1342     if(Level != 1) {
1343         SetLastError(ERROR_INVALID_LEVEL);
1344         return FALSE;
1345     }
1346
1347     ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
1348
1349     if(ret) {
1350         ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
1351         DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
1352         *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
1353         if(*pcbNeeded > cbBuf) {
1354             SetLastError(ERROR_INSUFFICIENT_BUFFER);
1355             ret = FALSE;
1356         } else {
1357             ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
1358             addjobA->JobId = addjobW->JobId;
1359             addjobA->Path = (char *)(addjobA + 1);
1360             WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
1361         }
1362     }
1363     return ret;
1364 }
1365
1366 /*****************************************************************************
1367  *          AddJobW  [WINSPOOL.@]
1368  */
1369 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
1370 {
1371     opened_printer_t *printer;
1372     job_t *job;
1373     BOOL ret = FALSE;
1374     static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
1375     static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
1376     WCHAR path[MAX_PATH], filename[MAX_PATH];
1377     DWORD len;
1378     ADDJOB_INFO_1W *addjob;
1379
1380     TRACE("(%p,%ld,%p,%ld,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
1381     
1382     EnterCriticalSection(&printer_handles_cs);
1383
1384     printer = get_opened_printer(hPrinter);
1385
1386     if(!printer) {
1387         SetLastError(ERROR_INVALID_HANDLE);
1388         goto end;
1389     }
1390
1391     if(Level != 1) {
1392         SetLastError(ERROR_INVALID_LEVEL);
1393         goto end;
1394     }
1395
1396     job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
1397     if(!job)
1398         goto end;
1399
1400     job->job_id = InterlockedIncrement(&next_job_id);
1401
1402     len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
1403     if(path[len - 1] != '\\')
1404         path[len++] = '\\';
1405     memcpy(path + len, spool_path, sizeof(spool_path));    
1406     sprintfW(filename, fmtW, path, job->job_id);
1407
1408     len = strlenW(filename);
1409     job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1410     memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
1411     job->document_title = strdupW(default_doc_title);
1412     list_add_tail(&printer->queue->jobs, &job->entry);
1413
1414     *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
1415     if(*pcbNeeded <= cbBuf) {
1416         addjob = (ADDJOB_INFO_1W*)pData;
1417         addjob->JobId = job->job_id;
1418         addjob->Path = (WCHAR *)(addjob + 1);
1419         memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
1420         ret = TRUE;
1421     } else 
1422         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1423
1424 end:
1425     LeaveCriticalSection(&printer_handles_cs);
1426     return ret;
1427 }
1428
1429 /*****************************************************************************
1430  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
1431  */
1432 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
1433                                         DWORD level,  LPBYTE Info,
1434                                         DWORD cbBuf, LPDWORD needed)
1435 {
1436     FIXME("(%s,%s,%ld,%p,0x%08lx): stub\n", debugstr_a(server), debugstr_a(env),
1437           level, Info, cbBuf);
1438     return 0;
1439 }
1440
1441 /*****************************************************************************
1442  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
1443  */
1444 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
1445                                         DWORD level,  LPBYTE Info,
1446                                         DWORD cbBuf, LPDWORD needed)
1447 {
1448     FIXME("(%s,%s,%ld,%p,0x%08lx): stub\n", debugstr_w(server), debugstr_w(env),
1449           level, Info, cbBuf);
1450     return 0;
1451 }
1452
1453 /*****************************************************************************
1454  *          WINSPOOL_OpenDriverReg [internal]
1455  *
1456  * opens the registry for the printer drivers depending on the given input
1457  * variable pEnvironment
1458  *
1459  * RETURNS:
1460  *    the opened hkey on success
1461  *    NULL on error
1462  */
1463 static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode)
1464 {   
1465     static const WCHAR WinNTW[] = { 'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0 };
1466     static const WCHAR Win40W[] = { 'W','i','n','d','o','w','s',' ','4','.','0',0 };
1467     HKEY  retval;
1468     LPWSTR lpKey, buffer = NULL;
1469     LPCWSTR pEnvW;
1470
1471     TRACE("%s\n",
1472           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment));
1473
1474     if(pEnvironment) {
1475         if (unicode) {
1476             pEnvW = pEnvironment;
1477         } else {
1478             INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
1479             buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1480             if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
1481             pEnvW = buffer;
1482         }
1483     } else {
1484         OSVERSIONINFOW ver;
1485         ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
1486
1487         if(!GetVersionExW( &ver))
1488             return 0;
1489
1490         switch (ver.dwPlatformId) {
1491              case VER_PLATFORM_WIN32s:
1492                   ERR("win32 style printing used with 16 bits app, try specifying 'win95' Windows version\n");
1493                   return 0;
1494              case VER_PLATFORM_WIN32_NT:
1495                   pEnvW = WinNTW;
1496                   break;
1497              default:
1498                   pEnvW = Win40W;
1499                   break;
1500         }
1501         TRACE("set environment to %s\n", debugstr_w(pEnvW));
1502     }
1503
1504     lpKey = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1505                        (strlenW(pEnvW) + strlenW(DriversW) + 1) * sizeof(WCHAR));
1506     wsprintfW( lpKey, DriversW, pEnvW);
1507
1508     TRACE("%s\n", debugstr_w(lpKey));
1509
1510     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, lpKey, &retval) != ERROR_SUCCESS)
1511        retval = 0;
1512
1513     HeapFree( GetProcessHeap(), 0, buffer);
1514     HeapFree( GetProcessHeap(), 0, lpKey);
1515
1516     return retval;
1517 }
1518
1519 /*****************************************************************************
1520  *          AddPrinterW  [WINSPOOL.@]
1521  */
1522 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
1523 {
1524     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
1525     LPDEVMODEA dmA;
1526     LPDEVMODEW dmW;
1527     HANDLE retval;
1528     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
1529     LONG size;
1530
1531     TRACE("(%s,%ld,%p)\n", debugstr_w(pName), Level, pPrinter);
1532
1533     if(pName != NULL) {
1534         ERR("pName = %s - unsupported\n", debugstr_w(pName));
1535         SetLastError(ERROR_INVALID_PARAMETER);
1536         return 0;
1537     }
1538     if(Level != 2) {
1539         ERR("Level = %ld, unsupported!\n", Level);
1540         SetLastError(ERROR_INVALID_LEVEL);
1541         return 0;
1542     }
1543     if (strlenW(pi->pPrinterName) >= CCHDEVICENAME) {
1544         ERR("Printername %s must not exceed length of DEVMODE.dmDeviceName !\n",
1545                 debugstr_w(pi->pPrinterName)
1546         );
1547         SetLastError(ERROR_INVALID_LEVEL);
1548         return 0;
1549     }
1550     if(!pPrinter) {
1551         SetLastError(ERROR_INVALID_PARAMETER);
1552         return 0;
1553     }
1554     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
1555        ERROR_SUCCESS) {
1556         ERR("Can't create Printers key\n");
1557         return 0;
1558     }
1559     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
1560         if (!RegQueryValueA(hkeyPrinter,"Attributes",NULL,NULL)) {
1561             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
1562             RegCloseKey(hkeyPrinter);
1563             RegCloseKey(hkeyPrinters);
1564             return 0;
1565         }
1566         RegCloseKey(hkeyPrinter);
1567     }
1568     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
1569     if(!hkeyDrivers) {
1570         ERR("Can't create Drivers key\n");
1571         RegCloseKey(hkeyPrinters);
1572         return 0;
1573     }
1574     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
1575        ERROR_SUCCESS) {
1576         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
1577         RegCloseKey(hkeyPrinters);
1578         RegCloseKey(hkeyDrivers);
1579         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
1580         return 0;
1581     }
1582     RegCloseKey(hkeyDriver);
1583     RegCloseKey(hkeyDrivers);
1584
1585     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
1586         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
1587         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
1588         RegCloseKey(hkeyPrinters);
1589         return 0;
1590     }
1591
1592     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
1593        ERROR_SUCCESS) {
1594         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
1595         SetLastError(ERROR_INVALID_PRINTER_NAME);
1596         RegCloseKey(hkeyPrinters);
1597         return 0;
1598     }
1599     RegSetValueExA(hkeyPrinter, "Attributes", 0, REG_DWORD,
1600                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
1601     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
1602
1603     /* See if we can load the driver.  We may need the devmode structure anyway
1604      *
1605      * FIXME:
1606      * Note that DocumentPropertiesW will briefly try to open the printer we
1607      * just create to find a DEVMODEA struct (it will use the WINEPS default
1608      * one in case it is not there, so we are ok).
1609      */
1610     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
1611
1612     if(size < 0) {
1613         FIXME("DocumentPropertiesW on printer '%s' fails\n", debugstr_w(pi->pPrinterName));
1614         size = sizeof(DEVMODEW);
1615     }
1616     if(pi->pDevMode)
1617         dmW = pi->pDevMode;
1618     else 
1619     {
1620             dmW = HeapAlloc(GetProcessHeap(), 0, size);
1621         ZeroMemory(dmW,size);
1622             dmW->dmSize = size;
1623             if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER)) 
1624         {
1625                 WARN("DocumentPropertiesW on printer '%s' failed!\n", debugstr_w(pi->pPrinterName));
1626             HeapFree(GetProcessHeap(),0,dmW);
1627             dmW=NULL;
1628             }
1629         else
1630         {
1631                 /* set devmode to printer name */
1632                 strcpyW(dmW->dmDeviceName,pi->pPrinterName);
1633         }
1634     }
1635
1636     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
1637        and we support these drivers.  NT writes DEVMODEW so somehow
1638        we'll need to distinguish between these when we support NT
1639        drivers */
1640     if (dmW)
1641     {
1642         dmA = DEVMODEdupWtoA(GetProcessHeap(), dmW);
1643         RegSetValueExA(hkeyPrinter, "Default DevMode", 0, REG_BINARY, 
1644                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
1645         HeapFree(GetProcessHeap(), 0, dmA);
1646         if(!pi->pDevMode)
1647             HeapFree(GetProcessHeap(), 0, dmW);
1648     }
1649     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
1650     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
1651     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
1652     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
1653
1654     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
1655     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
1656     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
1657     RegSetValueExA(hkeyPrinter, "Priority", 0, REG_DWORD,
1658                    (LPBYTE)&pi->Priority, sizeof(DWORD));
1659     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
1660     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
1661     RegSetValueExA(hkeyPrinter, "StartTime", 0, REG_DWORD,
1662                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
1663     RegSetValueExA(hkeyPrinter, "Status", 0, REG_DWORD,
1664                    (LPBYTE)&pi->Status, sizeof(DWORD));
1665     RegSetValueExA(hkeyPrinter, "UntilTime", 0, REG_DWORD,
1666                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
1667
1668     RegCloseKey(hkeyPrinter);
1669     RegCloseKey(hkeyPrinters);
1670     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
1671         ERR("OpenPrinter failing\n");
1672         return 0;
1673     }
1674     return retval;
1675 }
1676
1677 /*****************************************************************************
1678  *          AddPrinterA  [WINSPOOL.@]
1679  */
1680 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
1681 {
1682     UNICODE_STRING pNameW;
1683     PWSTR pwstrNameW;
1684     PRINTER_INFO_2W *piW;
1685     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
1686     HANDLE ret;
1687
1688     TRACE("(%s,%ld,%p): stub\n", debugstr_a(pName), Level, pPrinter);
1689     if(Level != 2) {
1690         ERR("Level = %ld, unsupported!\n", Level);
1691         SetLastError(ERROR_INVALID_LEVEL);
1692         return 0;
1693     }
1694     pwstrNameW = asciitounicode(&pNameW,pName);
1695     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
1696
1697     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
1698
1699     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
1700     RtlFreeUnicodeString(&pNameW);
1701     return ret;
1702 }
1703
1704
1705 /*****************************************************************************
1706  *          ClosePrinter  [WINSPOOL.@]
1707  */
1708 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
1709 {
1710     UINT_PTR i = (UINT_PTR)hPrinter;
1711     opened_printer_t *printer = NULL;
1712     BOOL ret = FALSE;
1713
1714     TRACE("Handle %p\n", hPrinter);
1715
1716     EnterCriticalSection(&printer_handles_cs);
1717
1718     if ((i > 0) && (i <= nb_printer_handles))
1719         printer = printer_handles[i - 1];
1720
1721     if(printer)
1722     {
1723         struct list *cursor, *cursor2;
1724
1725         if(printer->doc)
1726             EndDocPrinter(hPrinter);
1727
1728         if(InterlockedDecrement(&printer->queue->ref) == 0)
1729         {
1730             LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
1731             {
1732                 job_t *job = LIST_ENTRY(cursor, job_t, entry);
1733                 ScheduleJob(hPrinter, job->job_id);
1734             }
1735             HeapFree(GetProcessHeap(), 0, printer->queue);
1736         }
1737         HeapFree(GetProcessHeap(), 0, printer->name);
1738         HeapFree(GetProcessHeap(), 0, printer);
1739         printer_handles[i - 1] = NULL;
1740         ret = TRUE;
1741     }
1742     LeaveCriticalSection(&printer_handles_cs);
1743     return ret;
1744 }
1745
1746 /*****************************************************************************
1747  *          DeleteFormA  [WINSPOOL.@]
1748  */
1749 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
1750 {
1751     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
1752     return 1;
1753 }
1754
1755 /*****************************************************************************
1756  *          DeleteFormW  [WINSPOOL.@]
1757  */
1758 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
1759 {
1760     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
1761     return 1;
1762 }
1763
1764 /*****************************************************************************
1765  *   WINSPOOL_SHRegDeleteKey
1766  *
1767  *   Recursively delete subkeys.
1768  *   Cut & paste from shlwapi.
1769  * 
1770  */
1771 static DWORD WINSPOOL_SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey)
1772 {
1773   DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
1774   WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
1775   HKEY hSubKey = 0;
1776
1777   dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1778   if(!dwRet)
1779   {
1780     /* Find how many subkeys there are */
1781     dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1782                              &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
1783     if(!dwRet)
1784     {
1785       dwMaxSubkeyLen++;
1786       if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
1787         /* Name too big: alloc a buffer for it */
1788         lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
1789
1790       if(!lpszName)
1791         dwRet = ERROR_NOT_ENOUGH_MEMORY;
1792       else
1793       {
1794         /* Recursively delete all the subkeys */
1795         for(i = 0; i < dwKeyCount && !dwRet; i++)
1796         {
1797           dwSize = dwMaxSubkeyLen;
1798           dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL);
1799           if(!dwRet)
1800             dwRet = WINSPOOL_SHDeleteKeyW(hSubKey, lpszName);
1801         }
1802
1803         if (lpszName != szNameBuf)
1804           HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
1805       }
1806     }
1807
1808     RegCloseKey(hSubKey);
1809     if(!dwRet)
1810       dwRet = RegDeleteKeyW(hKey, lpszSubKey);
1811   }
1812   return dwRet;
1813 }
1814
1815 /*****************************************************************************
1816  *          DeletePrinter  [WINSPOOL.@]
1817  */
1818 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
1819 {
1820     LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1821     HKEY hkeyPrinters, hkey;
1822
1823     if(!lpNameW) {
1824         SetLastError(ERROR_INVALID_HANDLE);
1825         return FALSE;
1826     }
1827     if(RegOpenKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) == ERROR_SUCCESS) {
1828         WINSPOOL_SHDeleteKeyW(hkeyPrinters, lpNameW);
1829         RegCloseKey(hkeyPrinters);
1830     }
1831     WriteProfileStringW(devicesW, lpNameW, NULL);
1832     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
1833         RegDeleteValueW(hkey, lpNameW);
1834         RegCloseKey(hkey);
1835     }
1836     return TRUE;
1837 }
1838
1839 /*****************************************************************************
1840  *          SetPrinterA  [WINSPOOL.@]
1841  */
1842 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
1843                            DWORD Command)
1844 {
1845     FIXME("(%p,%ld,%p,%ld): stub\n",hPrinter,Level,pPrinter,Command);
1846     return FALSE;
1847 }
1848
1849 /*****************************************************************************
1850  *          SetJobA  [WINSPOOL.@]
1851  */
1852 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
1853                     LPBYTE pJob, DWORD Command)
1854 {
1855     BOOL ret;
1856     LPBYTE JobW;
1857     UNICODE_STRING usBuffer;
1858
1859     TRACE("(%p, %ld, %ld, %p, %ld)\n",hPrinter, JobId, Level, pJob, Command);
1860
1861     /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
1862        are all ignored by SetJob, so we don't bother copying them */
1863     switch(Level)
1864     {
1865     case 0:
1866         JobW = NULL;
1867         break;
1868     case 1:
1869       {
1870         JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
1871         JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
1872
1873         JobW = (LPBYTE)info1W;
1874         info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
1875         info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
1876         info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
1877         info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
1878         info1W->Status = info1A->Status;
1879         info1W->Priority = info1A->Priority;
1880         info1W->Position = info1A->Position;
1881         info1W->PagesPrinted = info1A->PagesPrinted;
1882         break;
1883       }
1884     case 2:
1885       {
1886         JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
1887         JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
1888
1889         JobW = (LPBYTE)info2W;
1890         info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
1891         info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
1892         info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
1893         info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
1894         info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
1895         info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
1896         info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
1897         info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
1898         info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
1899         info2W->Status = info2A->Status;
1900         info2W->Priority = info2A->Priority;
1901         info2W->Position = info2A->Position;
1902         info2W->StartTime = info2A->StartTime;
1903         info2W->UntilTime = info2A->UntilTime;
1904         info2W->PagesPrinted = info2A->PagesPrinted;
1905         break;
1906       }
1907     case 3:
1908         JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
1909         memcpy(JobW, pJob, sizeof(JOB_INFO_3));
1910         break;
1911     default:
1912         SetLastError(ERROR_INVALID_LEVEL);
1913         return FALSE;
1914     }
1915
1916     ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
1917
1918     switch(Level)
1919     {
1920     case 1:
1921       {
1922         JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
1923         HeapFree(GetProcessHeap(), 0, info1W->pUserName);
1924         HeapFree(GetProcessHeap(), 0, info1W->pDocument); 
1925         HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
1926         HeapFree(GetProcessHeap(), 0, info1W->pStatus);
1927         break;
1928       }
1929     case 2:
1930       {
1931         JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
1932         HeapFree(GetProcessHeap(), 0, info2W->pUserName);
1933         HeapFree(GetProcessHeap(), 0, info2W->pDocument); 
1934         HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
1935         HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
1936         HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
1937         HeapFree(GetProcessHeap(), 0, info2W->pParameters);
1938         HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
1939         HeapFree(GetProcessHeap(), 0, info2W->pStatus);
1940         break;
1941       }
1942     }
1943     HeapFree(GetProcessHeap(), 0, JobW);
1944
1945     return ret;
1946 }
1947
1948 /*****************************************************************************
1949  *          SetJobW  [WINSPOOL.@]
1950  */
1951 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
1952                     LPBYTE pJob, DWORD Command)
1953 {
1954     BOOL ret = FALSE;
1955     job_t *job;
1956
1957     TRACE("(%p, %ld, %ld, %p, %ld)\n", hPrinter, JobId, Level, pJob, Command);
1958     FIXME("Ignoring everything other than document title\n");
1959
1960     EnterCriticalSection(&printer_handles_cs);
1961     job = get_job(hPrinter, JobId);
1962     if(!job)
1963         goto end;
1964
1965     switch(Level)
1966     {
1967     case 0:
1968         break;
1969     case 1:
1970       {
1971         JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
1972         HeapFree(GetProcessHeap(), 0, job->document_title);
1973         job->document_title = strdupW(info1->pDocument);
1974         break;
1975       }
1976     case 2:
1977       {
1978         JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
1979         HeapFree(GetProcessHeap(), 0, job->document_title);
1980         job->document_title = strdupW(info2->pDocument);
1981         break;
1982       }
1983     case 3:
1984         break;
1985     default:
1986         SetLastError(ERROR_INVALID_LEVEL);
1987         goto end;
1988     }
1989     ret = TRUE;
1990 end:
1991     LeaveCriticalSection(&printer_handles_cs);
1992     return ret;
1993 }
1994
1995 /*****************************************************************************
1996  *          EndDocPrinter  [WINSPOOL.@]
1997  */
1998 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
1999 {
2000     opened_printer_t *printer;
2001     BOOL ret = FALSE;
2002     TRACE("(%p)\n", hPrinter);
2003
2004     EnterCriticalSection(&printer_handles_cs);
2005
2006     printer = get_opened_printer(hPrinter);
2007     if(!printer)
2008     {
2009         SetLastError(ERROR_INVALID_HANDLE);
2010         goto end;
2011     }
2012
2013     if(!printer->doc)    
2014     {
2015         SetLastError(ERROR_SPL_NO_STARTDOC);
2016         goto end;
2017     }
2018
2019     CloseHandle(printer->doc->hf);
2020     ScheduleJob(hPrinter, printer->doc->job_id);
2021     HeapFree(GetProcessHeap(), 0, printer->doc);
2022     printer->doc = NULL;
2023     ret = TRUE;
2024 end:
2025     LeaveCriticalSection(&printer_handles_cs);
2026     return ret;
2027 }
2028
2029 /*****************************************************************************
2030  *          EndPagePrinter  [WINSPOOL.@]
2031  */
2032 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
2033 {
2034     FIXME("(%p): stub\n", hPrinter);
2035     return TRUE;
2036 }
2037
2038 /*****************************************************************************
2039  *          StartDocPrinterA  [WINSPOOL.@]
2040  */
2041 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2042 {
2043     UNICODE_STRING usBuffer;
2044     DOC_INFO_2W doc2W;
2045     DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
2046     DWORD ret;
2047
2048     /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
2049        or one (DOC_INFO_3) extra DWORDs */
2050
2051     switch(Level) {
2052     case 2:
2053         doc2W.JobId = doc2->JobId;
2054         /* fall through */
2055     case 3:
2056         doc2W.dwMode = doc2->dwMode;
2057         /* fall through */
2058     case 1:
2059         doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
2060         doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
2061         doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
2062         break;
2063
2064     default:
2065         SetLastError(ERROR_INVALID_LEVEL);
2066         return FALSE;
2067     }
2068
2069     ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
2070
2071     HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
2072     HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
2073     HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
2074
2075     return ret;
2076 }
2077
2078 /*****************************************************************************
2079  *          StartDocPrinterW  [WINSPOOL.@]
2080  */
2081 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
2082 {
2083     DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
2084     opened_printer_t *printer;
2085     BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2086     ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
2087     JOB_INFO_1W job_info;
2088     DWORD needed, ret = 0;
2089     HANDLE hf;
2090     WCHAR *filename;
2091
2092     TRACE("(hPrinter = %p, Level = %ld, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
2093           hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
2094           debugstr_w(doc->pDatatype));
2095
2096     if(Level < 1 || Level > 3)
2097     {
2098         SetLastError(ERROR_INVALID_LEVEL);
2099         return 0;
2100     }
2101
2102     EnterCriticalSection(&printer_handles_cs);
2103     printer = get_opened_printer(hPrinter);
2104     if(!printer)
2105     {
2106         SetLastError(ERROR_INVALID_HANDLE);
2107         goto end;
2108     }
2109
2110     if(printer->doc)
2111     {
2112         SetLastError(ERROR_INVALID_PRINTER_STATE);
2113         goto end;
2114     }
2115
2116     /* Even if we're printing to a file we still add a print job, we'll
2117        just ignore the spool file name */
2118
2119     if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
2120     {
2121         ERR("AddJob failed gle %08lx\n", GetLastError());
2122         goto end;
2123     }
2124
2125     if(doc->pOutputFile)
2126         filename = doc->pOutputFile;
2127     else
2128         filename = addjob->Path;
2129
2130     hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2131     if(hf == INVALID_HANDLE_VALUE)
2132         goto end;
2133
2134     memset(&job_info, 0, sizeof(job_info));
2135     job_info.pDocument = doc->pDocName;
2136     SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
2137
2138     printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
2139     printer->doc->hf = hf;
2140     ret = printer->doc->job_id = addjob->JobId;
2141 end:
2142     LeaveCriticalSection(&printer_handles_cs);
2143
2144     return ret;
2145 }
2146
2147 /*****************************************************************************
2148  *          StartPagePrinter  [WINSPOOL.@]
2149  */
2150 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
2151 {
2152     FIXME("(%p): stub\n", hPrinter);
2153     return TRUE;
2154 }
2155
2156 /*****************************************************************************
2157  *          GetFormA  [WINSPOOL.@]
2158  */
2159 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
2160                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
2161 {
2162     FIXME("(%p,%s,%ld,%p,%ld,%p): stub\n",hPrinter,pFormName,
2163          Level,pForm,cbBuf,pcbNeeded);
2164     return FALSE;
2165 }
2166
2167 /*****************************************************************************
2168  *          GetFormW  [WINSPOOL.@]
2169  */
2170 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
2171                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
2172 {
2173     FIXME("(%p,%s,%ld,%p,%ld,%p): stub\n",hPrinter,
2174           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
2175     return FALSE;
2176 }
2177
2178 /*****************************************************************************
2179  *          SetFormA  [WINSPOOL.@]
2180  */
2181 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
2182                         LPBYTE pForm)
2183 {
2184     FIXME("(%p,%s,%ld,%p): stub\n",hPrinter,pFormName,Level,pForm);
2185     return FALSE;
2186 }
2187
2188 /*****************************************************************************
2189  *          SetFormW  [WINSPOOL.@]
2190  */
2191 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
2192                         LPBYTE pForm)
2193 {
2194     FIXME("(%p,%p,%ld,%p): stub\n",hPrinter,pFormName,Level,pForm);
2195     return FALSE;
2196 }
2197
2198 /*****************************************************************************
2199  *          ReadPrinter  [WINSPOOL.@]
2200  */
2201 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
2202                            LPDWORD pNoBytesRead)
2203 {
2204     FIXME("(%p,%p,%ld,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
2205     return FALSE;
2206 }
2207
2208 /*****************************************************************************
2209  *          ResetPrinterA  [WINSPOOL.@]
2210  */
2211 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
2212 {
2213     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
2214     return FALSE;
2215 }
2216
2217 /*****************************************************************************
2218  *          ResetPrinterW  [WINSPOOL.@]
2219  */
2220 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
2221 {
2222     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
2223     return FALSE;
2224 }
2225
2226 /*****************************************************************************
2227  *    WINSPOOL_GetDWORDFromReg
2228  *
2229  * Return DWORD associated with ValueName from hkey.
2230  */
2231 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
2232 {
2233     DWORD sz = sizeof(DWORD), type, value = 0;
2234     LONG ret;
2235
2236     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
2237
2238     if(ret != ERROR_SUCCESS) {
2239         WARN("Got ret = %ld on name %s\n", ret, ValueName);
2240         return 0;
2241     }
2242     if(type != REG_DWORD) {
2243         ERR("Got type %ld\n", type);
2244         return 0;
2245     }
2246     return value;
2247 }
2248
2249 /*****************************************************************************
2250  *    WINSPOOL_GetStringFromReg
2251  *
2252  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
2253  * String is stored either as unicode or ascii.
2254  * Bit of a hack here to get the ValueName if we want ascii.
2255  */
2256 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
2257                                       DWORD buflen, DWORD *needed,
2258                                       BOOL unicode)
2259 {
2260     DWORD sz = buflen, type;
2261     LONG ret;
2262
2263     if(unicode)
2264         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
2265     else {
2266         LPSTR ValueNameA = HEAP_strdupWtoA(GetProcessHeap(),0,ValueName);
2267         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
2268         HeapFree(GetProcessHeap(),0,ValueNameA);
2269     }
2270     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
2271         WARN("Got ret = %ld\n", ret);
2272         *needed = 0;
2273         return FALSE;
2274     }
2275     *needed = sz;
2276     return TRUE;
2277 }
2278
2279 /*****************************************************************************
2280  *    WINSPOOL_GetDefaultDevMode
2281  *
2282  * Get a default DevMode values for wineps.
2283  * FIXME - use ppd.
2284  */
2285
2286 static void WINSPOOL_GetDefaultDevMode(
2287         LPBYTE ptr,
2288         DWORD buflen, DWORD *needed,
2289         BOOL unicode)
2290 {
2291     DEVMODEA    dm;
2292     static const char szwps[] = "wineps.drv";
2293
2294         /* fill default DEVMODE - should be read from ppd... */
2295         ZeroMemory( &dm, sizeof(dm) );
2296         memcpy(dm.dmDeviceName,szwps,sizeof szwps);
2297         dm.dmSpecVersion = DM_SPECVERSION;
2298         dm.dmDriverVersion = 1;
2299         dm.dmSize = sizeof(DEVMODEA);
2300         dm.dmDriverExtra = 0;
2301         dm.dmFields =
2302                 DM_ORIENTATION | DM_PAPERSIZE |
2303                 DM_PAPERLENGTH | DM_PAPERWIDTH |
2304                 DM_SCALE |
2305                 DM_COPIES |
2306                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
2307                 DM_YRESOLUTION | DM_TTOPTION;
2308
2309         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
2310         dm.u1.s1.dmPaperSize = DMPAPER_A4;
2311         dm.u1.s1.dmPaperLength = 2970;
2312         dm.u1.s1.dmPaperWidth = 2100;
2313
2314         dm.dmScale = 100;
2315         dm.dmCopies = 1;
2316         dm.dmDefaultSource = DMBIN_AUTO;
2317         dm.dmPrintQuality = DMRES_MEDIUM;
2318         /* dm.dmColor */
2319         /* dm.dmDuplex */
2320         dm.dmYResolution = 300; /* 300dpi */
2321         dm.dmTTOption = DMTT_BITMAP;
2322         /* dm.dmCollate */
2323         /* dm.dmFormName */
2324         /* dm.dmLogPixels */
2325         /* dm.dmBitsPerPel */
2326         /* dm.dmPelsWidth */
2327         /* dm.dmPelsHeight */
2328         /* dm.dmDisplayFlags */
2329         /* dm.dmDisplayFrequency */
2330         /* dm.dmICMMethod */
2331         /* dm.dmICMIntent */
2332         /* dm.dmMediaType */
2333         /* dm.dmDitherType */
2334         /* dm.dmReserved1 */
2335         /* dm.dmReserved2 */
2336         /* dm.dmPanningWidth */
2337         /* dm.dmPanningHeight */
2338
2339     if(unicode) {
2340         if(buflen >= sizeof(DEVMODEW)) {
2341             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
2342             memcpy(ptr, pdmW, sizeof(DEVMODEW));
2343             HeapFree(GetProcessHeap(),0,pdmW);
2344         }
2345         *needed = sizeof(DEVMODEW);
2346     }
2347     else
2348     {
2349         if(buflen >= sizeof(DEVMODEA)) {
2350             memcpy(ptr, &dm, sizeof(DEVMODEA));
2351         }
2352         *needed = sizeof(DEVMODEA);
2353     }
2354 }
2355
2356 /*****************************************************************************
2357  *    WINSPOOL_GetDevModeFromReg
2358  *
2359  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
2360  * DevMode is stored either as unicode or ascii.
2361  */
2362 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
2363                                        LPBYTE ptr,
2364                                        DWORD buflen, DWORD *needed,
2365                                        BOOL unicode)
2366 {
2367     DWORD sz = buflen, type;
2368     LONG ret;
2369
2370     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
2371     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
2372     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
2373     if (sz < sizeof(DEVMODEA))
2374     {
2375         TRACE("corrupted registry for %s ( size %ld)\n",debugstr_w(ValueName),sz);
2376         return FALSE;
2377     }
2378     /* ensures that dmSize is not erratically bogus if registry is invalid */
2379     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
2380         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
2381     if(unicode) {
2382         sz += (CCHDEVICENAME + CCHFORMNAME);
2383         if(buflen >= sz) {
2384             DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
2385             memcpy(ptr, dmW, sz);
2386             HeapFree(GetProcessHeap(),0,dmW);
2387         }
2388     }
2389     *needed = sz;
2390     return TRUE;
2391 }
2392
2393 /*********************************************************************
2394  *    WINSPOOL_GetPrinter_2
2395  *
2396  * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
2397  * The strings are either stored as unicode or ascii.
2398  */
2399 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
2400                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2401                                   BOOL unicode)
2402 {
2403     DWORD size, left = cbBuf;
2404     BOOL space = (cbBuf > 0);
2405     LPBYTE ptr = buf;
2406
2407     *pcbNeeded = 0;
2408
2409     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2410                                  unicode)) {
2411         if(space && size <= left) {
2412             pi2->pPrinterName = (LPWSTR)ptr;
2413             ptr += size;
2414             left -= size;
2415         } else
2416             space = FALSE;
2417         *pcbNeeded += size;
2418     }
2419     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
2420                                  unicode)) {
2421         if(space && size <= left) {
2422             pi2->pShareName = (LPWSTR)ptr;
2423             ptr += size;
2424             left -= size;
2425         } else
2426             space = FALSE;
2427         *pcbNeeded += size;
2428     }
2429     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
2430                                  unicode)) {
2431         if(space && size <= left) {
2432             pi2->pPortName = (LPWSTR)ptr;
2433             ptr += size;
2434             left -= size;
2435         } else
2436             space = FALSE;
2437         *pcbNeeded += size;
2438     }
2439     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
2440                                  &size, unicode)) {
2441         if(space && size <= left) {
2442             pi2->pDriverName = (LPWSTR)ptr;
2443             ptr += size;
2444             left -= size;
2445         } else
2446             space = FALSE;
2447         *pcbNeeded += size;
2448     }
2449     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
2450                                  unicode)) {
2451         if(space && size <= left) {
2452             pi2->pComment = (LPWSTR)ptr;
2453             ptr += size;
2454             left -= size;
2455         } else
2456             space = FALSE;
2457         *pcbNeeded += size;
2458     }
2459     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
2460                                  unicode)) {
2461         if(space && size <= left) {
2462             pi2->pLocation = (LPWSTR)ptr;
2463             ptr += size;
2464             left -= size;
2465         } else
2466             space = FALSE;
2467         *pcbNeeded += size;
2468     }
2469     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
2470                                   &size, unicode)) {
2471         if(space && size <= left) {
2472             pi2->pDevMode = (LPDEVMODEW)ptr;
2473             ptr += size;
2474             left -= size;
2475         } else
2476             space = FALSE;
2477         *pcbNeeded += size;
2478     }
2479     else
2480     {
2481         WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
2482         if(space && size <= left) {
2483             pi2->pDevMode = (LPDEVMODEW)ptr;
2484             ptr += size;
2485             left -= size;
2486         } else
2487             space = FALSE;
2488         *pcbNeeded += size;
2489     }
2490     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
2491                                  &size, unicode)) {
2492         if(space && size <= left) {
2493             pi2->pSepFile = (LPWSTR)ptr;
2494             ptr += size;
2495             left -= size;
2496         } else
2497             space = FALSE;
2498         *pcbNeeded += size;
2499     }
2500     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
2501                                  &size, unicode)) {
2502         if(space && size <= left) {
2503             pi2->pPrintProcessor = (LPWSTR)ptr;
2504             ptr += size;
2505             left -= size;
2506         } else
2507             space = FALSE;
2508         *pcbNeeded += size;
2509     }
2510     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
2511                                  &size, unicode)) {
2512         if(space && size <= left) {
2513             pi2->pDatatype = (LPWSTR)ptr;
2514             ptr += size;
2515             left -= size;
2516         } else
2517             space = FALSE;
2518         *pcbNeeded += size;
2519     }
2520     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
2521                                  &size, unicode)) {
2522         if(space && size <= left) {
2523             pi2->pParameters = (LPWSTR)ptr;
2524             ptr += size;
2525             left -= size;
2526         } else
2527             space = FALSE;
2528         *pcbNeeded += size;
2529     }
2530     if(pi2) {
2531         pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
2532         pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
2533         pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
2534                                                         "Default Priority");
2535         pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
2536         pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
2537     }
2538
2539     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
2540         memset(pi2, 0, sizeof(*pi2));
2541
2542     return space;
2543 }
2544
2545 /*********************************************************************
2546  *    WINSPOOL_GetPrinter_4
2547  *
2548  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
2549  */
2550 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
2551                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2552                                   BOOL unicode)
2553 {
2554     DWORD size, left = cbBuf;
2555     BOOL space = (cbBuf > 0);
2556     LPBYTE ptr = buf;
2557
2558     *pcbNeeded = 0;
2559
2560     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2561                                  unicode)) {
2562         if(space && size <= left) {
2563             pi4->pPrinterName = (LPWSTR)ptr;
2564             ptr += size;
2565             left -= size;
2566         } else
2567             space = FALSE;
2568         *pcbNeeded += size;
2569     }
2570     if(pi4) {
2571         pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
2572     }
2573
2574     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
2575         memset(pi4, 0, sizeof(*pi4));
2576
2577     return space;
2578 }
2579
2580 /*********************************************************************
2581  *    WINSPOOL_GetPrinter_5
2582  *
2583  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
2584  */
2585 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
2586                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
2587                                   BOOL unicode)
2588 {
2589     DWORD size, left = cbBuf;
2590     BOOL space = (cbBuf > 0);
2591     LPBYTE ptr = buf;
2592
2593     *pcbNeeded = 0;
2594
2595     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
2596                                  unicode)) {
2597         if(space && size <= left) {
2598             pi5->pPrinterName = (LPWSTR)ptr;
2599             ptr += size;
2600             left -= size;
2601         } else
2602             space = FALSE;
2603         *pcbNeeded += size;
2604     }
2605     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
2606                                  unicode)) {
2607         if(space && size <= left) {
2608             pi5->pPortName = (LPWSTR)ptr;
2609             ptr += size;
2610             left -= size;
2611         } else
2612             space = FALSE;
2613         *pcbNeeded += size;
2614     }
2615     if(pi5) {
2616         pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
2617         pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
2618                                                                 "dnsTimeout");
2619         pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
2620                                                                  "txTimeout");
2621     }
2622
2623     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
2624         memset(pi5, 0, sizeof(*pi5));
2625
2626     return space;
2627 }
2628
2629 /*****************************************************************************
2630  *          WINSPOOL_GetPrinter
2631  *
2632  *    Implementation of GetPrinterA|W.  Relies on PRINTER_INFO_*W being
2633  *    essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
2634  *    just a collection of pointers to strings.
2635  */
2636 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2637                                 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
2638 {
2639     LPCWSTR name;
2640     DWORD size, needed = 0;
2641     LPBYTE ptr = NULL;
2642     HKEY hkeyPrinter, hkeyPrinters;
2643     BOOL ret;
2644
2645     TRACE("(%p,%ld,%p,%ld,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
2646
2647     if (!(name = get_opened_printer_name(hPrinter))) {
2648         SetLastError(ERROR_INVALID_HANDLE);
2649         return FALSE;
2650     }
2651
2652     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
2653        ERROR_SUCCESS) {
2654         ERR("Can't create Printers key\n");
2655         return FALSE;
2656     }
2657     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
2658     {
2659         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
2660         RegCloseKey(hkeyPrinters);
2661         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
2662         return FALSE;
2663     }
2664
2665     switch(Level) {
2666     case 2:
2667       {
2668         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
2669
2670         size = sizeof(PRINTER_INFO_2W);
2671         if(size <= cbBuf) {
2672             ptr = pPrinter + size;
2673             cbBuf -= size;
2674             memset(pPrinter, 0, size);
2675         } else {
2676             pi2 = NULL;
2677             cbBuf = 0;
2678         }
2679         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
2680                                     unicode);
2681         needed += size;
2682         break;
2683       }
2684
2685     case 4:
2686       {
2687         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
2688
2689         size = sizeof(PRINTER_INFO_4W);
2690         if(size <= cbBuf) {
2691             ptr = pPrinter + size;
2692             cbBuf -= size;
2693             memset(pPrinter, 0, size);
2694         } else {
2695             pi4 = NULL;
2696             cbBuf = 0;
2697         }
2698         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
2699                                     unicode);
2700         needed += size;
2701         break;
2702       }
2703
2704
2705     case 5:
2706       {
2707         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
2708
2709         size = sizeof(PRINTER_INFO_5W);
2710         if(size <= cbBuf) {
2711             ptr = pPrinter + size;
2712             cbBuf -= size;
2713             memset(pPrinter, 0, size);
2714         } else {
2715             pi5 = NULL;
2716             cbBuf = 0;
2717         }
2718
2719         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
2720                                     unicode);
2721         needed += size;
2722         break;
2723       }
2724
2725     default:
2726         FIXME("Unimplemented level %ld\n", Level);
2727         SetLastError(ERROR_INVALID_LEVEL);
2728         RegCloseKey(hkeyPrinters);
2729         RegCloseKey(hkeyPrinter);
2730         return FALSE;
2731     }
2732
2733     RegCloseKey(hkeyPrinter);
2734     RegCloseKey(hkeyPrinters);
2735
2736     TRACE("returning %d needed = %ld\n", ret, needed);
2737     if(pcbNeeded) *pcbNeeded = needed;
2738     if(!ret)
2739         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2740     return ret;
2741 }
2742
2743 /*****************************************************************************
2744  *          GetPrinterW  [WINSPOOL.@]
2745  */
2746 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2747                         DWORD cbBuf, LPDWORD pcbNeeded)
2748 {
2749     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
2750                                TRUE);
2751 }
2752
2753 /*****************************************************************************
2754  *          GetPrinterA  [WINSPOOL.@]
2755  */
2756 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2757                     DWORD cbBuf, LPDWORD pcbNeeded)
2758 {
2759     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
2760                                FALSE);
2761 }
2762
2763 /*****************************************************************************
2764  *          WINSPOOL_EnumPrinters
2765  *
2766  *    Implementation of EnumPrintersA|W
2767  */
2768 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
2769                                   DWORD dwLevel, LPBYTE lpbPrinters,
2770                                   DWORD cbBuf, LPDWORD lpdwNeeded,
2771                                   LPDWORD lpdwReturned, BOOL unicode)
2772
2773 {
2774     HKEY hkeyPrinters, hkeyPrinter;
2775     WCHAR PrinterName[255];
2776     DWORD needed = 0, number = 0;
2777     DWORD used, i, left;
2778     PBYTE pi, buf;
2779
2780     if(lpbPrinters)
2781         memset(lpbPrinters, 0, cbBuf);
2782     if(lpdwReturned)
2783         *lpdwReturned = 0;
2784     if(lpdwNeeded)
2785         *lpdwNeeded = 0;
2786
2787     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
2788     if(dwType == PRINTER_ENUM_DEFAULT)
2789         return TRUE;
2790
2791     if (dwType & PRINTER_ENUM_CONNECTIONS) {
2792         FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
2793         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
2794         if(!dwType) return TRUE;
2795     }
2796
2797     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
2798         FIXME("dwType = %08lx\n", dwType);
2799         SetLastError(ERROR_INVALID_FLAGS);
2800         return FALSE;
2801     }
2802
2803     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
2804        ERROR_SUCCESS) {
2805         ERR("Can't create Printers key\n");
2806         return FALSE;
2807     }
2808
2809     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
2810                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
2811         RegCloseKey(hkeyPrinters);
2812         ERR("Can't query Printers key\n");
2813         return FALSE;
2814     }
2815     TRACE("Found %ld printers\n", number);
2816
2817     switch(dwLevel) {
2818     case 1:
2819         RegCloseKey(hkeyPrinters);
2820         if (lpdwReturned)
2821             *lpdwReturned = number;
2822         return TRUE;
2823
2824     case 2:
2825         used = number * sizeof(PRINTER_INFO_2W);
2826         break;
2827     case 4:
2828         used = number * sizeof(PRINTER_INFO_4W);
2829         break;
2830     case 5:
2831         used = number * sizeof(PRINTER_INFO_5W);
2832         break;
2833
2834     default:
2835         SetLastError(ERROR_INVALID_LEVEL);
2836         RegCloseKey(hkeyPrinters);
2837         return FALSE;
2838     }
2839     pi = (used <= cbBuf) ? lpbPrinters : NULL;
2840
2841     for(i = 0; i < number; i++) {
2842         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) !=
2843            ERROR_SUCCESS) {
2844             ERR("Can't enum key number %ld\n", i);
2845             RegCloseKey(hkeyPrinters);
2846             return FALSE;
2847         }
2848         TRACE("Printer %ld is %s\n", i, debugstr_w(PrinterName));
2849         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
2850            ERROR_SUCCESS) {
2851             ERR("Can't open key %s\n", debugstr_w(PrinterName));
2852             RegCloseKey(hkeyPrinters);
2853             return FALSE;
2854         }
2855
2856         if(cbBuf > used) {
2857             buf = lpbPrinters + used;
2858             left = cbBuf - used;
2859         } else {
2860             buf = NULL;
2861             left = 0;
2862         }
2863
2864         switch(dwLevel) {
2865         case 2:
2866             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
2867                                   left, &needed, unicode);
2868             used += needed;
2869             if(pi) pi += sizeof(PRINTER_INFO_2W);
2870             break;
2871         case 4:
2872             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
2873                                   left, &needed, unicode);
2874             used += needed;
2875             if(pi) pi += sizeof(PRINTER_INFO_4W);
2876             break;
2877         case 5:
2878             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
2879                                   left, &needed, unicode);
2880             used += needed;
2881             if(pi) pi += sizeof(PRINTER_INFO_5W);
2882             break;
2883         default:
2884             ERR("Shouldn't be here!\n");
2885             RegCloseKey(hkeyPrinter);
2886             RegCloseKey(hkeyPrinters);
2887             return FALSE;
2888         }
2889         RegCloseKey(hkeyPrinter);
2890     }
2891     RegCloseKey(hkeyPrinters);
2892
2893     if(lpdwNeeded)
2894         *lpdwNeeded = used;
2895
2896     if(used > cbBuf) {
2897         if(lpbPrinters)
2898             memset(lpbPrinters, 0, cbBuf);
2899         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2900         return FALSE;
2901     }
2902     if(lpdwReturned)
2903         *lpdwReturned = number;
2904     SetLastError(ERROR_SUCCESS);
2905     return TRUE;
2906 }
2907
2908
2909 /******************************************************************
2910  *              EnumPrintersW        [WINSPOOL.@]
2911  *
2912  *    Enumerates the available printers, print servers and print
2913  *    providers, depending on the specified flags, name and level.
2914  *
2915  * RETURNS:
2916  *
2917  *    If level is set to 1:
2918  *      Not implemented yet!
2919  *      Returns TRUE with an empty list.
2920  *
2921  *    If level is set to 2:
2922  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
2923  *      Returns an array of PRINTER_INFO_2 data structures in the
2924  *      lpbPrinters buffer. Note that according to MSDN also an
2925  *      OpenPrinter should be performed on every remote printer.
2926  *
2927  *    If level is set to 4 (officially WinNT only):
2928  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
2929  *      Fast: Only the registry is queried to retrieve printer names,
2930  *      no connection to the driver is made.
2931  *      Returns an array of PRINTER_INFO_4 data structures in the
2932  *      lpbPrinters buffer.
2933  *
2934  *    If level is set to 5 (officially WinNT4/Win9x only):
2935  *      Fast: Only the registry is queried to retrieve printer names,
2936  *      no connection to the driver is made.
2937  *      Returns an array of PRINTER_INFO_5 data structures in the
2938  *      lpbPrinters buffer.
2939  *
2940  *    If level set to 3 or 6+:
2941  *          returns zero (failure!)
2942  *
2943  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
2944  *    for information.
2945  *
2946  * BUGS:
2947  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
2948  *    - Only levels 2, 4 and 5 are implemented at the moment.
2949  *    - 16-bit printer drivers are not enumerated.
2950  *    - Returned amount of bytes used/needed does not match the real Windoze
2951  *      implementation (as in this implementation, all strings are part
2952  *      of the buffer, whereas Win32 keeps them somewhere else)
2953  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
2954  *
2955  * NOTE:
2956  *    - In a regular Wine installation, no registry settings for printers
2957  *      exist, which makes this function return an empty list.
2958  */
2959 BOOL  WINAPI EnumPrintersW(
2960                 DWORD dwType,        /* [in] Types of print objects to enumerate */
2961                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
2962                 DWORD dwLevel,       /* [in] type of printer info structure */
2963                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
2964                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
2965                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
2966                 LPDWORD lpdwReturned /* [out] number of entries returned */
2967                 )
2968 {
2969     return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
2970                                  lpdwNeeded, lpdwReturned, TRUE);
2971 }
2972
2973 /******************************************************************
2974  *              EnumPrintersA        [WINSPOOL.@]
2975  *
2976  */
2977 BOOL WINAPI EnumPrintersA(DWORD dwType, LPSTR lpszName,
2978                           DWORD dwLevel, LPBYTE lpbPrinters,
2979                           DWORD cbBuf, LPDWORD lpdwNeeded,
2980                           LPDWORD lpdwReturned)
2981 {
2982     BOOL ret;
2983     UNICODE_STRING lpszNameW;
2984     PWSTR pwstrNameW;
2985     
2986     pwstrNameW = asciitounicode(&lpszNameW,lpszName);
2987     ret = WINSPOOL_EnumPrinters(dwType, pwstrNameW, dwLevel, lpbPrinters, cbBuf,
2988                                 lpdwNeeded, lpdwReturned, FALSE);
2989     RtlFreeUnicodeString(&lpszNameW);
2990     return ret;
2991 }
2992
2993 /*****************************************************************************
2994  *          WINSPOOL_GetDriverInfoFromReg [internal]
2995  *
2996  *    Enters the information from the registry into the DRIVER_INFO struct
2997  *
2998  * RETURNS
2999  *    zero if the printer driver does not exist in the registry
3000  *    (only if Level > 1) otherwise nonzero
3001  */
3002 static BOOL WINSPOOL_GetDriverInfoFromReg(
3003                             HKEY    hkeyDrivers,
3004                             LPWSTR  DriverName,
3005                             LPWSTR  pEnvironment,
3006                             DWORD   Level,
3007                             LPBYTE  ptr,            /* DRIVER_INFO */
3008                             LPBYTE  pDriverStrings, /* strings buffer */
3009                             DWORD   cbBuf,          /* size of string buffer */
3010                             LPDWORD pcbNeeded,      /* space needed for str. */
3011                             BOOL    unicode)        /* type of strings */
3012 {   DWORD  dw, size, tmp, type;
3013     HKEY   hkeyDriver;
3014     LPBYTE strPtr = pDriverStrings;
3015
3016     TRACE("%s,%s,%ld,%p,%p,%ld,%d\n",
3017           debugstr_w(DriverName), debugstr_w(pEnvironment),
3018           Level, ptr, pDriverStrings, cbBuf, unicode);
3019
3020     if(unicode) {
3021         *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
3022             if (*pcbNeeded <= cbBuf)
3023                strcpyW((LPWSTR)strPtr, DriverName);
3024     } else {
3025         *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0,
3026                                           NULL, NULL);
3027         if(*pcbNeeded <= cbBuf)
3028             WideCharToMultiByte(CP_ACP, 0, DriverName, -1,
3029                                 (LPSTR)strPtr, *pcbNeeded, NULL, NULL);
3030     }
3031     if(Level == 1) {
3032        if(ptr)
3033           ((PDRIVER_INFO_1W) ptr)->pName = (LPWSTR) strPtr;
3034        return TRUE;
3035     } else {
3036        if(ptr)
3037           ((PDRIVER_INFO_3W) ptr)->pName = (LPWSTR) strPtr;
3038        strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3039     }
3040
3041     if(!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
3042         ERR("Can't find driver '%s' in registry\n", debugstr_w(DriverName));
3043         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
3044         return FALSE;
3045     }
3046
3047     size = sizeof(dw);
3048     if(RegQueryValueExA(hkeyDriver, "Version", 0, &type, (PBYTE)&dw, &size) !=
3049         ERROR_SUCCESS)
3050          WARN("Can't get Version\n");
3051     else if(ptr)
3052          ((PDRIVER_INFO_3A) ptr)->cVersion = dw;
3053
3054     if(!pEnvironment)
3055         pEnvironment = (LPWSTR)DefaultEnvironmentW;
3056     if(unicode)
3057         size = (lstrlenW(pEnvironment) + 1) * sizeof(WCHAR);
3058     else
3059         size = WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1, NULL, 0,
3060                                    NULL, NULL);
3061     *pcbNeeded += size;
3062     if(*pcbNeeded <= cbBuf) {
3063         if(unicode)
3064             strcpyW((LPWSTR)strPtr, pEnvironment);
3065         else
3066             WideCharToMultiByte(CP_ACP, 0, pEnvironment, -1,
3067                                 (LPSTR)strPtr, size, NULL, NULL);
3068         if(ptr)
3069             ((PDRIVER_INFO_3W) ptr)->pEnvironment = (LPWSTR)strPtr;
3070         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3071     }
3072
3073     if(WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, 0, &size,
3074                                  unicode)) {
3075         *pcbNeeded += size;
3076         if(*pcbNeeded <= cbBuf)
3077             WINSPOOL_GetStringFromReg(hkeyDriver, DriverW, strPtr, size, &tmp,
3078                                       unicode);
3079         if(ptr)
3080             ((PDRIVER_INFO_3W) ptr)->pDriverPath = (LPWSTR)strPtr;
3081         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
3082     }
3083
3084     if(WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, 0, &size,
3085                                  unicode)) {
3086         *pcbNeeded += size;
3087         if(*pcbNeeded <= cbBuf)
3088             WINSPOOL_GetStringFromReg(hkeyDriver, Data_FileW, strPtr, size,
3089                                       &tmp, unicode);
3090         if(ptr)
3091             ((PDRIVER_INFO_3W) ptr)->pDataFile = (LPWSTR)strPtr;
3092         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3093     }
3094
3095     if(WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
3096                                  0, &size, unicode)) {
3097         *pcbNeeded += size;
3098         if(*pcbNeeded <= cbBuf)
3099             WINSPOOL_GetStringFromReg(hkeyDriver, Configuration_FileW, strPtr,
3100                                       size, &tmp, unicode);
3101         if(ptr)
3102             ((PDRIVER_INFO_3W) ptr)->pConfigFile = (LPWSTR)strPtr;
3103         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3104     }
3105
3106     if(Level == 2 ) {
3107         RegCloseKey(hkeyDriver);
3108         TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
3109         return TRUE;
3110     }
3111
3112     if(WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr, 0, &size,
3113                                  unicode)) {
3114         *pcbNeeded += size;
3115         if(*pcbNeeded <= cbBuf)
3116             WINSPOOL_GetStringFromReg(hkeyDriver, Help_FileW, strPtr,
3117                                       size, &tmp, unicode);
3118         if(ptr)
3119             ((PDRIVER_INFO_3W) ptr)->pHelpFile = (LPWSTR)strPtr;
3120         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3121     }
3122
3123     if(WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr, 0,
3124                              &size, unicode)) {
3125         *pcbNeeded += size;
3126         if(*pcbNeeded <= cbBuf)
3127             WINSPOOL_GetStringFromReg(hkeyDriver, Dependent_FilesW, strPtr,
3128                                       size, &tmp, unicode);
3129         if(ptr)
3130             ((PDRIVER_INFO_3W) ptr)->pDependentFiles = (LPWSTR)strPtr;
3131         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3132     }
3133
3134     if(WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size,
3135                                  unicode)) {
3136         *pcbNeeded += size;
3137         if(*pcbNeeded <= cbBuf)
3138             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr,
3139                                       size, &tmp, unicode);
3140         if(ptr)
3141             ((PDRIVER_INFO_3W) ptr)->pMonitorName = (LPWSTR)strPtr;
3142         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3143     }
3144
3145     if(WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size,
3146                                  unicode)) {
3147         *pcbNeeded += size;
3148         if(*pcbNeeded <= cbBuf)
3149             WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr,
3150                                       size, &tmp, unicode);
3151         if(ptr)
3152             ((PDRIVER_INFO_3W) ptr)->pDefaultDataType = (LPWSTR)strPtr;
3153         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
3154     }
3155
3156     TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
3157     RegCloseKey(hkeyDriver);
3158     return TRUE;
3159 }
3160
3161 /*****************************************************************************
3162  *          WINSPOOL_GetPrinterDriver
3163  */
3164 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
3165                                       DWORD Level, LPBYTE pDriverInfo,
3166                                       DWORD cbBuf, LPDWORD pcbNeeded,
3167                                       BOOL unicode)
3168 {
3169     LPCWSTR name;
3170     WCHAR DriverName[100];
3171     DWORD ret, type, size, needed = 0;
3172     LPBYTE ptr = NULL;
3173     HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
3174
3175     TRACE("(%p,%s,%ld,%p,%ld,%p)\n",hPrinter,debugstr_w(pEnvironment),
3176           Level,pDriverInfo,cbBuf, pcbNeeded);
3177
3178     ZeroMemory(pDriverInfo, cbBuf);
3179
3180     if (!(name = get_opened_printer_name(hPrinter))) {
3181         SetLastError(ERROR_INVALID_HANDLE);
3182         return FALSE;
3183     }
3184     if(Level < 1 || Level > 3) {
3185         SetLastError(ERROR_INVALID_LEVEL);
3186         return FALSE;
3187     }
3188     if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
3189        ERROR_SUCCESS) {
3190         ERR("Can't create Printers key\n");
3191         return FALSE;
3192     }
3193     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
3194        != ERROR_SUCCESS) {
3195         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
3196         RegCloseKey(hkeyPrinters);
3197         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
3198         return FALSE;
3199     }
3200     size = sizeof(DriverName);
3201     DriverName[0] = 0;
3202     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
3203                            (LPBYTE)DriverName, &size);
3204     RegCloseKey(hkeyPrinter);
3205     RegCloseKey(hkeyPrinters);
3206     if(ret != ERROR_SUCCESS) {
3207         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
3208         return FALSE;
3209     }
3210
3211     hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
3212     if(!hkeyDrivers) {
3213         ERR("Can't create Drivers key\n");
3214         return FALSE;
3215     }
3216
3217     switch(Level) {
3218     case 1:
3219         size = sizeof(DRIVER_INFO_1W);
3220         break;
3221     case 2:
3222         size = sizeof(DRIVER_INFO_2W);
3223         break;
3224     case 3:
3225         size = sizeof(DRIVER_INFO_3W);
3226         break;
3227     default:
3228         ERR("Invalid level\n");
3229         return FALSE;
3230     }
3231
3232     if(size <= cbBuf)
3233         ptr = pDriverInfo + size;
3234
3235     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
3236                          pEnvironment, Level, pDriverInfo,
3237                          (cbBuf < size) ? NULL : ptr,
3238                          (cbBuf < size) ? 0 : cbBuf - size,
3239                          &needed, unicode)) {
3240             RegCloseKey(hkeyDrivers);
3241             return FALSE;
3242     }
3243
3244     RegCloseKey(hkeyDrivers);
3245
3246     if(pcbNeeded) *pcbNeeded = size + needed;
3247     TRACE("buffer space %ld required %ld\n", cbBuf, *pcbNeeded);
3248     if(cbBuf >= needed) return TRUE;
3249     SetLastError(ERROR_INSUFFICIENT_BUFFER);
3250     return FALSE;
3251 }
3252
3253 /*****************************************************************************
3254  *          GetPrinterDriverA  [WINSPOOL.@]
3255  */
3256 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
3257                               DWORD Level, LPBYTE pDriverInfo,
3258                               DWORD cbBuf, LPDWORD pcbNeeded)
3259 {
3260     BOOL ret;
3261     UNICODE_STRING pEnvW;
3262     PWSTR pwstrEnvW;
3263     
3264     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
3265     ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
3266                                     cbBuf, pcbNeeded, FALSE);
3267     RtlFreeUnicodeString(&pEnvW);
3268     return ret;
3269 }
3270 /*****************************************************************************
3271  *          GetPrinterDriverW  [WINSPOOL.@]
3272  */
3273 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
3274                                   DWORD Level, LPBYTE pDriverInfo,
3275                                   DWORD cbBuf, LPDWORD pcbNeeded)
3276 {
3277     return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
3278                                      pDriverInfo, cbBuf, pcbNeeded, TRUE);
3279 }
3280
3281 /*****************************************************************************
3282  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
3283  *
3284  * Return the PATH for the Printer-Drivers (UNICODE)
3285  *
3286  * PARAMS
3287  *   pName            [I] Servername (NT only) or NULL (local Computer)
3288  *   pEnvironment     [I] Printing-Environment (see below) or NULL (Default)
3289  *   Level            [I] Structure-Level (must be 1)
3290  *   pDriverDirectory [O] PTR to Buffer that receives the Result
3291  *   cbBuf            [I] Size of Buffer at pDriverDirectory
3292  *   pcbNeeded        [O] PTR to DWORD that receives the size in Bytes used / 
3293  *                        required for pDriverDirectory
3294  *
3295  * RETURNS
3296  *   Success: TRUE  and in pcbNeeded the Bytes used in pDriverDirectory
3297  *   Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
3298  *   if cbBuf is too small
3299  * 
3300  *   Native Values returned in pDriverDirectory on Success:
3301  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\DRIVERS\\w32x86" 
3302  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\DRIVERS\\win40" 
3303  *|  win9x(Windows 4.0):  "%winsysdir%" 
3304  *
3305  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
3306  *
3307  * FIXME
3308  *-  pName != NULL not supported
3309  *-  pEnvironment != NULL not supported
3310  *-  Current Implementation returns always "%winsysdir%"
3311  *
3312  */
3313 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
3314                                        DWORD Level, LPBYTE pDriverDirectory,
3315                                        DWORD cbBuf, LPDWORD pcbNeeded)
3316 {
3317     DWORD needed;
3318
3319     TRACE("(%s, %s, %ld, %p, %ld, %p)\n", debugstr_w(pName), 
3320           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
3321     if(pName != NULL) {
3322         FIXME("pName = `%s' - unsupported\n", debugstr_w(pName));
3323         SetLastError(ERROR_INVALID_PARAMETER);
3324         return FALSE;
3325     }
3326     if(pEnvironment != NULL) {
3327         FIXME("pEnvironment = `%s' - unsupported\n", debugstr_w(pEnvironment));
3328         SetLastError(ERROR_INVALID_ENVIRONMENT);
3329         return FALSE;
3330     }
3331     if(Level != 1)  /* win95 ignores this so we just carry on */
3332         WARN("Level = %ld - assuming 1\n", Level);
3333
3334     /* FIXME should read from registry */
3335     needed = GetSystemDirectoryW( (LPWSTR)pDriverDirectory, cbBuf/sizeof(WCHAR));
3336     /* GetSystemDirectoryW returns number of TCHAR without '\0' 
3337      * adjust this now
3338      */
3339     needed++;
3340     needed*=sizeof(WCHAR);
3341
3342     if(pcbNeeded)
3343         *pcbNeeded = needed;
3344     TRACE("required <%08lx>\n", *pcbNeeded);
3345     if(needed > cbBuf) {
3346         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3347         return FALSE;
3348     }
3349     return TRUE;
3350 }
3351
3352
3353 /*****************************************************************************
3354  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
3355  *
3356  * Return the PATH for the Printer-Drivers (ANSI)
3357  *
3358  * See GetPrinterDriverDirectoryW.
3359  *
3360  * NOTES
3361  * On NT, pDriverDirectory need the same Size as the Unicode-Version
3362  *
3363  */
3364 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
3365                                        DWORD Level, LPBYTE pDriverDirectory,
3366                                        DWORD cbBuf, LPDWORD pcbNeeded)
3367 {
3368     UNICODE_STRING nameW, environmentW;
3369     BOOL ret;
3370     DWORD pcbNeededW;
3371     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
3372     WCHAR *driverDirectoryW = NULL;
3373
3374     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
3375
3376     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
3377     else nameW.Buffer = NULL;
3378     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
3379     else environmentW.Buffer = NULL;
3380
3381     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
3382                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
3383     if (ret) {
3384         DWORD needed;
3385         needed = 1 + WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
3386                                    (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
3387         if(pcbNeeded)
3388             *pcbNeeded = needed;
3389         ret = (needed <= cbBuf) ? TRUE : FALSE;
3390     } else 
3391         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
3392
3393     TRACE("provided<%ld> required <%ld>\n", cbBuf, *pcbNeeded);
3394
3395     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
3396     RtlFreeUnicodeString(&environmentW);
3397     RtlFreeUnicodeString(&nameW);
3398
3399     return ret;
3400 }
3401
3402 /*****************************************************************************
3403  *          AddPrinterDriverA  [WINSPOOL.@]
3404  */
3405 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
3406 {
3407     DRIVER_INFO_3A di3;
3408     HKEY hkeyDrivers, hkeyName;
3409
3410     TRACE("(%s,%ld,%p)\n",debugstr_a(pName),level,pDriverInfo);
3411
3412     if(level != 2 && level != 3) {
3413         SetLastError(ERROR_INVALID_LEVEL);
3414         return FALSE;
3415     }
3416     if(pName != NULL) {
3417         FIXME("pName= %s - unsupported\n", debugstr_a(pName));
3418         SetLastError(ERROR_INVALID_PARAMETER);
3419         return FALSE;
3420     }
3421     if(!pDriverInfo) {
3422         WARN("pDriverInfo == NULL\n");
3423         SetLastError(ERROR_INVALID_PARAMETER);
3424         return FALSE;
3425     }
3426
3427     if(level == 3)
3428         di3 = *(DRIVER_INFO_3A *)pDriverInfo;
3429     else {
3430         memset(&di3, 0, sizeof(di3));
3431         memcpy(&di3, pDriverInfo, sizeof(DRIVER_INFO_2A));
3432     }
3433
3434     if(!di3.pName || !di3.pDriverPath || !di3.pConfigFile ||
3435        !di3.pDataFile) {
3436         SetLastError(ERROR_INVALID_PARAMETER);
3437         return FALSE;
3438     }
3439     if(!di3.pDefaultDataType) di3.pDefaultDataType = "";
3440     if(!di3.pDependentFiles) di3.pDependentFiles = "\0";
3441     if(!di3.pHelpFile) di3.pHelpFile = "";
3442     if(!di3.pMonitorName) di3.pMonitorName = "";
3443
3444     hkeyDrivers = WINSPOOL_OpenDriverReg(di3.pEnvironment, FALSE);
3445
3446     if(!hkeyDrivers) {
3447         ERR("Can't create Drivers key\n");
3448         return FALSE;
3449     }
3450
3451     if(level == 2) { /* apparently can't overwrite with level2 */
3452         if(RegOpenKeyA(hkeyDrivers, di3.pName, &hkeyName) == ERROR_SUCCESS) {
3453             RegCloseKey(hkeyName);
3454             RegCloseKey(hkeyDrivers);
3455             WARN("Trying to create existing printer driver %s\n", debugstr_a(di3.pName));
3456             SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
3457             return FALSE;
3458         }
3459     }
3460     if(RegCreateKeyA(hkeyDrivers, di3.pName, &hkeyName) != ERROR_SUCCESS) {
3461         RegCloseKey(hkeyDrivers);
3462         ERR("Can't create Name key\n");
3463         return FALSE;
3464     }
3465     RegSetValueExA(hkeyName, "Configuration File", 0, REG_SZ, (LPBYTE) di3.pConfigFile,
3466                    0);
3467     RegSetValueExA(hkeyName, "Data File", 0, REG_SZ, (LPBYTE) di3.pDataFile, 0);
3468     RegSetValueExA(hkeyName, "Driver", 0, REG_SZ, (LPBYTE) di3.pDriverPath, 0);
3469     RegSetValueExA(hkeyName, "Version", 0, REG_DWORD, (LPBYTE) &di3.cVersion,
3470                    sizeof(DWORD));
3471     RegSetValueExA(hkeyName, "Datatype", 0, REG_SZ, (LPBYTE) di3.pDefaultDataType, 0);
3472     RegSetValueExA(hkeyName, "Dependent Files", 0, REG_MULTI_SZ,
3473                    (LPBYTE) di3.pDependentFiles, 0);
3474     RegSetValueExA(hkeyName, "Help File", 0, REG_SZ, (LPBYTE) di3.pHelpFile, 0);
3475     RegSetValueExA(hkeyName, "Monitor", 0, REG_SZ, (LPBYTE) di3.pMonitorName, 0);
3476     RegCloseKey(hkeyName);
3477     RegCloseKey(hkeyDrivers);
3478
3479     return TRUE;
3480 }
3481
3482 /*****************************************************************************
3483  *          AddPrinterDriverW  [WINSPOOL.@]
3484  */
3485 BOOL WINAPI AddPrinterDriverW(LPWSTR printerName,DWORD level,
3486                                    LPBYTE pDriverInfo)
3487 {
3488     FIXME("(%s,%ld,%p): stub\n",debugstr_w(printerName),
3489           level,pDriverInfo);
3490     return FALSE;
3491 }
3492
3493 /*****************************************************************************
3494  *          AddPrintProcessorA  [WINSPOOL.@]
3495  */
3496 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
3497                                LPSTR pPrintProcessorName)
3498 {
3499     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
3500           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
3501     return FALSE;
3502 }
3503
3504 /*****************************************************************************
3505  *          AddPrintProcessorW  [WINSPOOL.@]
3506  */
3507 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
3508                                LPWSTR pPrintProcessorName)
3509 {
3510     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
3511           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
3512     return FALSE;
3513 }
3514
3515 /*****************************************************************************
3516  *          AddPrintProvidorA  [WINSPOOL.@]
3517  */
3518 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
3519 {
3520     FIXME("(%s,0x%08lx,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
3521     return FALSE;
3522 }
3523
3524 /*****************************************************************************
3525  *          AddPrintProvidorW  [WINSPOOL.@]
3526  */
3527 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
3528 {
3529     FIXME("(%s,0x%08lx,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
3530     return FALSE;
3531 }
3532
3533 /*****************************************************************************
3534  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
3535  */
3536 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
3537                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
3538 {
3539     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
3540           pDevModeOutput, pDevModeInput);
3541     return 0;
3542 }
3543
3544 /*****************************************************************************
3545  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
3546  */
3547 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
3548                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
3549 {
3550     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
3551           pDevModeOutput, pDevModeInput);
3552     return 0;
3553 }
3554
3555 /*****************************************************************************
3556  *          PrinterProperties  [WINSPOOL.@]
3557  *
3558  *     Displays a dialog to set the properties of the printer.
3559  *
3560  * RETURNS
3561  *     nonzero on success or zero on failure
3562  *
3563  * BUGS
3564  *         implemented as stub only
3565  */
3566 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
3567                               HANDLE hPrinter /* [in] handle to printer object */
3568 ){
3569     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
3570     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3571     return FALSE;
3572 }
3573
3574 /*****************************************************************************
3575  *          EnumJobsA [WINSPOOL.@]
3576  *
3577  */
3578 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
3579                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
3580                       LPDWORD pcReturned)
3581 {
3582     FIXME("(%p,first=%ld,no=%ld,level=%ld,job=%p,cb=%ld,%p,%p), stub!\n",
3583         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
3584     );
3585     if(pcbNeeded) *pcbNeeded = 0;
3586     if(pcReturned) *pcReturned = 0;
3587     return FALSE;
3588 }
3589
3590
3591 /*****************************************************************************
3592  *          EnumJobsW [WINSPOOL.@]
3593  *
3594  */
3595 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
3596                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
3597                       LPDWORD pcReturned)
3598 {
3599     FIXME("(%p,first=%ld,no=%ld,level=%ld,job=%p,cb=%ld,%p,%p), stub!\n",
3600         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
3601     );
3602     if(pcbNeeded) *pcbNeeded = 0;
3603     if(pcReturned) *pcReturned = 0;
3604     return FALSE;
3605 }
3606
3607 /*****************************************************************************
3608  *          WINSPOOL_EnumPrinterDrivers [internal]
3609  *
3610  *    Delivers information about all printer drivers installed on the
3611  *    localhost or a given server
3612  *
3613  * RETURNS
3614  *    nonzero on success or zero on failure. If the buffer for the returned
3615  *    information is too small the function will return an error
3616  *
3617  * BUGS
3618  *    - only implemented for localhost, foreign hosts will return an error
3619  */
3620 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPWSTR pEnvironment,
3621                                         DWORD Level, LPBYTE pDriverInfo,
3622                                         DWORD cbBuf, LPDWORD pcbNeeded,
3623                                         LPDWORD pcReturned, BOOL unicode)
3624
3625 {   HKEY  hkeyDrivers;
3626     DWORD i, needed, number = 0, size = 0;
3627     WCHAR DriverNameW[255];
3628     PBYTE ptr;
3629
3630     TRACE("%s,%s,%ld,%p,%ld,%d\n",
3631           debugstr_w(pName), debugstr_w(pEnvironment),
3632           Level, pDriverInfo, cbBuf, unicode);
3633
3634     /* check for local drivers */
3635     if(pName) {
3636         ERR("remote drivers unsupported! Current remote host is %s\n",
3637              debugstr_w(pName));
3638         return FALSE;
3639     }
3640
3641     /* check input parameter */
3642     if((Level < 1) || (Level > 3)) {
3643         ERR("unsupported level %ld\n", Level);
3644         SetLastError(ERROR_INVALID_LEVEL);
3645         return FALSE;
3646     }
3647
3648     /* initialize return values */
3649     if(pDriverInfo)
3650         memset( pDriverInfo, 0, cbBuf);
3651     *pcbNeeded  = 0;
3652     *pcReturned = 0;
3653
3654     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
3655     if(!hkeyDrivers) {
3656         ERR("Can't open Drivers key\n");
3657         return FALSE;
3658     }
3659
3660     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
3661                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
3662         RegCloseKey(hkeyDrivers);
3663         ERR("Can't query Drivers key\n");
3664         return FALSE;
3665     }
3666     TRACE("Found %ld Drivers\n", number);
3667
3668     /* get size of single struct
3669      * unicode and ascii structure have the same size
3670      */
3671     switch (Level) {
3672         case 1:
3673             size = sizeof(DRIVER_INFO_1A);
3674             break;
3675         case 2:
3676             size = sizeof(DRIVER_INFO_2A);
3677             break;
3678         case 3:
3679             size = sizeof(DRIVER_INFO_3A);
3680             break;
3681     }
3682
3683     /* calculate required buffer size */
3684     *pcbNeeded = size * number;
3685
3686     for( i = 0,  ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
3687          i < number;
3688          i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
3689         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW))
3690                        != ERROR_SUCCESS) {
3691             ERR("Can't enum key number %ld\n", i);
3692             RegCloseKey(hkeyDrivers);
3693             return FALSE;
3694         }
3695         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
3696                          pEnvironment, Level, ptr,
3697                          (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
3698                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
3699                          &needed, unicode)) {
3700             RegCloseKey(hkeyDrivers);
3701             return FALSE;
3702         }
3703         (*pcbNeeded) += needed;
3704     }
3705
3706     RegCloseKey(hkeyDrivers);
3707
3708     if(cbBuf < *pcbNeeded){
3709         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3710         return FALSE;
3711     }
3712
3713     *pcReturned = number;
3714     return TRUE;
3715 }
3716
3717 /*****************************************************************************
3718  *          EnumPrinterDriversW  [WINSPOOL.@]
3719  *
3720  *    see function EnumPrinterDrivers for RETURNS, BUGS
3721  */
3722 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
3723                                 LPBYTE pDriverInfo, DWORD cbBuf,
3724                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
3725 {
3726     return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
3727                                        cbBuf, pcbNeeded, pcReturned, TRUE);
3728 }
3729
3730 /*****************************************************************************
3731  *          EnumPrinterDriversA  [WINSPOOL.@]
3732  *
3733  *    see function EnumPrinterDrivers for RETURNS, BUGS
3734  */
3735 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
3736                                 LPBYTE pDriverInfo, DWORD cbBuf,
3737                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
3738 {   BOOL ret;
3739     UNICODE_STRING pNameW, pEnvironmentW;
3740     PWSTR pwstrNameW, pwstrEnvironmentW;
3741
3742     pwstrNameW = asciitounicode(&pNameW, pName);
3743     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
3744
3745     ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
3746                                       Level, pDriverInfo, cbBuf, pcbNeeded,
3747                                       pcReturned, FALSE);
3748     RtlFreeUnicodeString(&pNameW);
3749     RtlFreeUnicodeString(&pEnvironmentW);
3750
3751     return ret;
3752 }
3753
3754 static CHAR PortMonitor[] = "Wine Port Monitor";
3755 static CHAR PortDescription[] = "Wine Port";
3756
3757 static BOOL WINSPOOL_ComPortExists( LPCSTR name )
3758 {
3759     HANDLE handle;
3760
3761     handle = CreateFileA( name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
3762                          NULL, OPEN_EXISTING, 0, NULL );
3763     if (handle == INVALID_HANDLE_VALUE)
3764         return FALSE;
3765     TRACE("Checking %s exists\n", name );
3766     CloseHandle( handle );
3767     return TRUE;
3768 }
3769
3770 static DWORD WINSPOOL_CountSerialPorts(void)
3771 {
3772     CHAR name[6];
3773     DWORD n = 0, i;
3774
3775     for (i=0; i<4; i++)
3776     {
3777         strcpy( name, "COMx:" );
3778         name[3] = '1' + i;
3779         if (WINSPOOL_ComPortExists( name ))
3780             n++;
3781     }
3782
3783     return n;
3784 }
3785
3786 /******************************************************************************
3787  *              EnumPortsA   (WINSPOOL.@)
3788  *
3789  * See EnumPortsW.
3790  *
3791  * BUGS
3792  *  ANSI-Version did not call the UNICODE-Version
3793  *
3794  */
3795 BOOL WINAPI EnumPortsA(LPSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
3796                        LPDWORD bufneeded,LPDWORD bufreturned)
3797 {
3798     CHAR portname[10];
3799     DWORD info_size, ofs, i, printer_count, serial_count, count, n, r;
3800     const LPCSTR szPrinterPortKey = "Software\\Wine\\Wine\\Config\\spooler";
3801     HKEY hkey_printer;
3802     BOOL retval = TRUE;
3803
3804     TRACE("(%s,%ld,%p,%ld,%p,%p)\n",
3805           debugstr_a(name),level,buffer,bufsize,bufneeded,bufreturned);
3806
3807     switch( level )
3808     {
3809     case 1:
3810         info_size = sizeof (PORT_INFO_1A);
3811         break;
3812     case 2:
3813         info_size = sizeof (PORT_INFO_2A);
3814         break;
3815     default:
3816         SetLastError(ERROR_INVALID_LEVEL);
3817         return FALSE;
3818     }
3819     
3820     /* see how many exist */
3821
3822     hkey_printer = 0;
3823     serial_count = WINSPOOL_CountSerialPorts();
3824     printer_count = 0;
3825
3826     r = RegOpenKeyA( HKEY_LOCAL_MACHINE, szPrinterPortKey, &hkey_printer);
3827     if ( r == ERROR_SUCCESS )
3828     {
3829         RegQueryInfoKeyA( hkey_printer, NULL, NULL, NULL, NULL, NULL, NULL,
3830             &printer_count, NULL, NULL, NULL, NULL);
3831     }
3832     count = serial_count + printer_count;
3833
3834     /* then fill in the structure info structure once
3835        we know the offset to the first string */
3836
3837     memset( buffer, 0, bufsize );
3838     n = 0;
3839     ofs = info_size*count; 
3840     for ( i=0; i<count; i++)
3841     {
3842         DWORD vallen = sizeof(portname) - 1;
3843
3844         /* get the serial port values, then the printer values */
3845         if ( i < serial_count )
3846         {
3847             strcpy( portname, "COMx:" );
3848             portname[3] = '1' + i;
3849             if (!WINSPOOL_ComPortExists( portname ))
3850                 continue;
3851
3852             TRACE("Found %s\n", portname );
3853             vallen = strlen( portname );
3854         }
3855         else
3856         {
3857             r = RegEnumValueA( hkey_printer, i-serial_count, 
3858                      portname, &vallen, NULL, NULL, NULL, 0 );
3859             if ( r )
3860                 continue;
3861         }
3862
3863         /* add a colon if necessary, and make it upper case */
3864         CharUpperBuffA(portname,vallen);
3865         if (strcasecmp(portname,"nul")!=0)
3866             if (vallen && (portname[vallen-1] != ':') )
3867                 lstrcatA(portname,":");
3868
3869         /* add the port info structure if we can fit it */
3870         if ( info_size*(n+1) < bufsize )
3871         {
3872             if ( level == 1)
3873             {
3874                 PORT_INFO_1A *info = (PORT_INFO_1A*) &buffer[info_size*n];
3875                 info->pName = (LPSTR) &buffer[ofs];
3876             }
3877             else if ( level == 2)
3878             {
3879                 PORT_INFO_2A *info = (PORT_INFO_2A*) &buffer[info_size*n];
3880                 info->pPortName = (LPSTR) &buffer[ofs];
3881                 /* FIXME: fill in more stuff here */
3882                 info->pMonitorName = PortMonitor;
3883                 info->pDescription = PortDescription;
3884                 info->fPortType = PORT_TYPE_WRITE|PORT_TYPE_READ;
3885             }
3886
3887             /* add the name of the port if we can fit it */
3888             if ( ofs < bufsize )
3889                 lstrcpynA((LPSTR)&buffer[ofs],portname,bufsize - ofs);
3890
3891             n++;
3892         }
3893         else
3894             retval = FALSE;
3895         ofs += lstrlenA(portname)+1;
3896     }
3897
3898     RegCloseKey(hkey_printer);
3899
3900     if(bufneeded)
3901         *bufneeded = ofs;
3902
3903     if(bufreturned)
3904         *bufreturned = n;
3905
3906     return retval;
3907 }
3908
3909 /******************************************************************************
3910  *      EnumPortsW   (WINSPOOL.@)
3911  *
3912  * Enumerate available Ports
3913  *
3914  * PARAMS
3915  *  name        [I] Servername or NULL (local Computer)
3916  *  level       [I] Structure-Level (1 or 2)
3917  *  buffer      [O] PTR to Buffer that receives the Result
3918  *  bufsize     [I] Size of Buffer at buffer
3919  *  bufneeded   [O] PTR to DWORD that receives the size in Bytes used / required for buffer
3920  *  bufreturned [O] PTR to DWORD that receives the number of Ports in buffer
3921  *
3922  * RETURNS
3923  *  Success: TRUE
3924  *  Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
3925  *
3926  * BUGS
3927  *  UNICODE-Version is a stub
3928  *
3929  */
3930 BOOL WINAPI EnumPortsW(LPWSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
3931                        LPDWORD bufneeded,LPDWORD bufreturned)
3932 {
3933     FIXME("(%s,%ld,%p,%ld,%p,%p) - stub\n",
3934           debugstr_w(name),level,buffer,bufsize,bufneeded,bufreturned);
3935     return FALSE;
3936 }
3937
3938 /******************************************************************************
3939  *              GetDefaultPrinterW   (WINSPOOL.@)
3940  *
3941  * FIXME
3942  *      This function must read the value from data 'device' of key
3943  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
3944  */
3945 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
3946 {
3947     BOOL  retval = TRUE;
3948     DWORD insize, len;
3949     WCHAR *buffer, *ptr;
3950
3951     if (!namesize)
3952     {
3953         SetLastError(ERROR_INVALID_PARAMETER);
3954         return FALSE;
3955     }
3956
3957     /* make the buffer big enough for the stuff from the profile/registry,
3958      * the content must fit into the local buffer to compute the correct
3959      * size even if the extern buffer is too small or not given.
3960      * (20 for ,driver,port) */
3961     insize = *namesize;
3962     len = max(100, (insize + 20));
3963     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
3964
3965     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
3966     {
3967         SetLastError (ERROR_FILE_NOT_FOUND);
3968         retval = FALSE;
3969         goto end;
3970     }
3971     TRACE("%s\n", debugstr_w(buffer));
3972
3973     if ((ptr = strchrW(buffer, ',')) == NULL)
3974     {
3975         SetLastError(ERROR_INVALID_NAME);
3976         retval = FALSE;
3977         goto end;
3978     }
3979
3980     *ptr = 0;
3981     *namesize = strlenW(buffer) + 1;
3982     if(!name || (*namesize > insize))
3983     {
3984         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3985         retval = FALSE;
3986         goto end;
3987     }
3988     strcpyW(name, buffer);
3989
3990 end:
3991     HeapFree( GetProcessHeap(), 0, buffer);
3992     return retval;
3993 }
3994
3995
3996 /******************************************************************************
3997  *              GetDefaultPrinterA   (WINSPOOL.@)
3998  */
3999 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
4000 {
4001     BOOL  retval = TRUE;
4002     DWORD insize = 0;
4003     WCHAR *bufferW = NULL;
4004
4005     if (!namesize)
4006     {
4007         SetLastError(ERROR_INVALID_PARAMETER);
4008         return FALSE;
4009     }
4010
4011     if(name && *namesize) {
4012         insize = *namesize;
4013         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
4014     }
4015
4016     if(!GetDefaultPrinterW( bufferW, namesize)) {
4017         retval = FALSE;
4018         goto end;
4019     }
4020
4021     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
4022                                     NULL, NULL);
4023     if (!*namesize)
4024     {
4025         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
4026         retval = FALSE;
4027     }
4028     TRACE("0x%08lx/0x%08lx:%s\n", *namesize, insize, debugstr_w(bufferW));
4029
4030 end:
4031     HeapFree( GetProcessHeap(), 0, bufferW);
4032     return retval;
4033 }
4034
4035
4036 /******************************************************************************
4037  *              SetPrinterDataExA   (WINSPOOL.@)
4038  */
4039 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
4040                                LPCSTR pValueName, DWORD Type,
4041                                LPBYTE pData, DWORD cbData)
4042 {
4043     HKEY hkeyPrinter, hkeySubkey;
4044     DWORD ret;
4045
4046     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_a(pKeyName),
4047           debugstr_a(pValueName), Type, pData, cbData);
4048
4049     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4050        != ERROR_SUCCESS)
4051         return ret;
4052
4053     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
4054        != ERROR_SUCCESS) {
4055         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
4056         RegCloseKey(hkeyPrinter);
4057         return ret;
4058     }
4059     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
4060     RegCloseKey(hkeySubkey);
4061     RegCloseKey(hkeyPrinter);
4062     return ret;
4063 }
4064
4065 /******************************************************************************
4066  *              SetPrinterDataExW   (WINSPOOL.@)
4067  */
4068 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4069                                LPCWSTR pValueName, DWORD Type,
4070                                LPBYTE pData, DWORD cbData)
4071 {
4072     HKEY hkeyPrinter, hkeySubkey;
4073     DWORD ret;
4074
4075     TRACE("(%p, %s, %s %08lx, %p, %08lx)\n", hPrinter, debugstr_w(pKeyName),
4076           debugstr_w(pValueName), Type, pData, cbData);
4077
4078     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4079        != ERROR_SUCCESS)
4080         return ret;
4081
4082     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
4083        != ERROR_SUCCESS) {
4084         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
4085         RegCloseKey(hkeyPrinter);
4086         return ret;
4087     }
4088     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
4089     RegCloseKey(hkeySubkey);
4090     RegCloseKey(hkeyPrinter);
4091     return ret;
4092 }
4093
4094 /******************************************************************************
4095  *              SetPrinterDataA   (WINSPOOL.@)
4096  */
4097 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
4098                                LPBYTE pData, DWORD cbData)
4099 {
4100     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
4101                              pData, cbData);
4102 }
4103
4104 /******************************************************************************
4105  *              SetPrinterDataW   (WINSPOOL.@)
4106  */
4107 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
4108                              LPBYTE pData, DWORD cbData)
4109 {
4110     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
4111                              pData, cbData);
4112 }
4113
4114 /******************************************************************************
4115  *              GetPrinterDataExA   (WINSPOOL.@)
4116  */
4117 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
4118                                LPCSTR pValueName, LPDWORD pType,
4119                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4120 {
4121     HKEY hkeyPrinter, hkeySubkey;
4122     DWORD ret;
4123
4124     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
4125           debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
4126           pcbNeeded);
4127
4128     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4129        != ERROR_SUCCESS)
4130         return ret;
4131
4132     if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
4133        != ERROR_SUCCESS) {
4134         WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
4135         RegCloseKey(hkeyPrinter);
4136         return ret;
4137     }
4138     *pcbNeeded = nSize;
4139     ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
4140     RegCloseKey(hkeySubkey);
4141     RegCloseKey(hkeyPrinter);
4142     return ret;
4143 }
4144
4145 /******************************************************************************
4146  *              GetPrinterDataExW   (WINSPOOL.@)
4147  */
4148 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4149                                LPCWSTR pValueName, LPDWORD pType,
4150                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4151 {
4152     HKEY hkeyPrinter, hkeySubkey;
4153     DWORD ret;
4154
4155     TRACE("(%p, %s, %s %p, %p, %08lx, %p)\n", hPrinter,
4156           debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
4157           pcbNeeded);
4158
4159     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
4160        != ERROR_SUCCESS)
4161         return ret;
4162
4163     if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
4164        != ERROR_SUCCESS) {
4165         WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
4166         RegCloseKey(hkeyPrinter);
4167         return ret;
4168     }
4169     *pcbNeeded = nSize;
4170     ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
4171     RegCloseKey(hkeySubkey);
4172     RegCloseKey(hkeyPrinter);
4173     return ret;
4174 }
4175
4176 /******************************************************************************
4177  *              GetPrinterDataA   (WINSPOOL.@)
4178  */
4179 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
4180                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4181 {
4182     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
4183                              pData, nSize, pcbNeeded);
4184 }
4185
4186 /******************************************************************************
4187  *              GetPrinterDataW   (WINSPOOL.@)
4188  */
4189 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
4190                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
4191 {
4192     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
4193                              pData, nSize, pcbNeeded);
4194 }
4195
4196 /*******************************************************************************
4197  *              EnumPrinterDataExW      [WINSPOOL.@]
4198  */
4199 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
4200                                 LPBYTE pEnumValues, DWORD cbEnumValues,
4201                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
4202 {
4203     HKEY                    hkPrinter, hkSubKey;
4204     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
4205                             cbValueNameLen, cbMaxValueLen, cbValueLen,
4206                             cbBufSize, dwType;
4207     LPWSTR                  lpValueName;
4208     HANDLE                  hHeap;
4209     PBYTE                   lpValue;
4210     PPRINTER_ENUM_VALUESW   ppev;
4211
4212     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
4213
4214     if (pKeyName == NULL || *pKeyName == 0)
4215         return ERROR_INVALID_PARAMETER;
4216
4217     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
4218     if (ret != ERROR_SUCCESS)
4219     {
4220         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %li\n",
4221                 hPrinter, ret);
4222         return ret;
4223     }
4224
4225     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
4226     if (ret != ERROR_SUCCESS)
4227     {
4228         r = RegCloseKey (hkPrinter);
4229         if (r != ERROR_SUCCESS)
4230             WARN ("RegCloseKey returned %li\n", r);
4231         TRACE ("RegOpenKeyExW (%p, %s) returned %li\n", hPrinter,
4232                 debugstr_w (pKeyName), ret);
4233         return ret;
4234     }
4235
4236     ret = RegCloseKey (hkPrinter);
4237     if (ret != ERROR_SUCCESS)
4238     {
4239         ERR ("RegCloseKey returned %li\n", ret);
4240         r = RegCloseKey (hkSubKey);
4241         if (r != ERROR_SUCCESS)
4242             WARN ("RegCloseKey returned %li\n", r);
4243         return ret;
4244     }
4245
4246     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
4247             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
4248     if (ret != ERROR_SUCCESS)
4249     {
4250         r = RegCloseKey (hkSubKey);
4251         if (r != ERROR_SUCCESS)
4252             WARN ("RegCloseKey returned %li\n", r);
4253         TRACE ("RegQueryInfoKeyW (%p) returned %li\n", hkSubKey, ret);
4254         return ret;
4255     }
4256
4257     TRACE ("RegQueryInfoKeyW returned cValues = %li, cbMaxValueNameLen = %li, "
4258             "cbMaxValueLen = %li\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
4259
4260     if (cValues == 0)                   /* empty key */
4261     {
4262         r = RegCloseKey (hkSubKey);
4263         if (r != ERROR_SUCCESS)
4264             WARN ("RegCloseKey returned %li\n", r);
4265         *pcbEnumValues = *pnEnumValues = 0;
4266         return ERROR_SUCCESS;
4267     }
4268
4269     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
4270
4271     hHeap = GetProcessHeap ();
4272     if (hHeap == NULL)
4273     {
4274         ERR ("GetProcessHeap failed\n");
4275         r = RegCloseKey (hkSubKey);
4276         if (r != ERROR_SUCCESS)
4277             WARN ("RegCloseKey returned %li\n", r);
4278         return ERROR_OUTOFMEMORY;
4279     }
4280
4281     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
4282     if (lpValueName == NULL)
4283     {
4284         ERR ("Failed to allocate %li bytes from process heap\n",
4285                 cbMaxValueNameLen * sizeof (WCHAR));
4286         r = RegCloseKey (hkSubKey);
4287         if (r != ERROR_SUCCESS)
4288             WARN ("RegCloseKey returned %li\n", r);
4289         return ERROR_OUTOFMEMORY;
4290     }
4291
4292     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
4293     if (lpValue == NULL)
4294     {
4295         ERR ("Failed to allocate %li bytes from process heap\n", cbMaxValueLen);
4296         if (HeapFree (hHeap, 0, lpValueName) == 0)
4297             WARN ("HeapFree failed with code %li\n", GetLastError ());
4298         r = RegCloseKey (hkSubKey);
4299         if (r != ERROR_SUCCESS)
4300             WARN ("RegCloseKey returned %li\n", r);
4301         return ERROR_OUTOFMEMORY;
4302     }
4303
4304     TRACE ("pass 1: calculating buffer required for all names and values\n");
4305
4306     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
4307
4308     TRACE ("%li bytes required for %li headers\n", cbBufSize, cValues);
4309
4310     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
4311     {
4312         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
4313         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
4314                 NULL, NULL, lpValue, &cbValueLen);
4315         if (ret != ERROR_SUCCESS)
4316         {
4317             if (HeapFree (hHeap, 0, lpValue) == 0)
4318                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4319             if (HeapFree (hHeap, 0, lpValueName) == 0)
4320                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4321             r = RegCloseKey (hkSubKey);
4322             if (r != ERROR_SUCCESS)
4323                 WARN ("RegCloseKey returned %li\n", r);
4324             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
4325             return ret;
4326         }
4327
4328         TRACE ("%s [%li]: name needs %li bytes, data needs %li bytes\n",
4329                 debugstr_w (lpValueName), dwIndex,
4330                 (cbValueNameLen + 1) * sizeof (WCHAR), cbValueLen);
4331
4332         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
4333         cbBufSize += cbValueLen;
4334     }
4335
4336     TRACE ("%li bytes required for all %li values\n", cbBufSize, cValues);
4337
4338     *pcbEnumValues = cbBufSize;
4339     *pnEnumValues = cValues;
4340
4341     if (cbEnumValues < cbBufSize)       /* buffer too small */
4342     {
4343         if (HeapFree (hHeap, 0, lpValue) == 0)
4344             WARN ("HeapFree failed with code %li\n", GetLastError ());
4345         if (HeapFree (hHeap, 0, lpValueName) == 0)
4346             WARN ("HeapFree failed with code %li\n", GetLastError ());
4347         r = RegCloseKey (hkSubKey);
4348         if (r != ERROR_SUCCESS)
4349             WARN ("RegCloseKey returned %li\n", r);
4350         TRACE ("%li byte buffer is not large enough\n", cbEnumValues);
4351         return ERROR_MORE_DATA;
4352     }
4353
4354     TRACE ("pass 2: copying all names and values to buffer\n");
4355
4356     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
4357     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
4358
4359     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
4360     {
4361         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
4362         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
4363                 NULL, &dwType, lpValue, &cbValueLen);
4364         if (ret != ERROR_SUCCESS)
4365         {
4366             if (HeapFree (hHeap, 0, lpValue) == 0)
4367                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4368             if (HeapFree (hHeap, 0, lpValueName) == 0)
4369                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4370             r = RegCloseKey (hkSubKey);
4371             if (r != ERROR_SUCCESS)
4372                 WARN ("RegCloseKey returned %li\n", r);
4373             TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret);
4374             return ret;
4375         }
4376
4377         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
4378         memcpy (pEnumValues, lpValueName, cbValueNameLen);
4379         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
4380         pEnumValues += cbValueNameLen;
4381
4382         /* return # of *bytes* (including trailing \0), not # of chars */
4383         ppev[dwIndex].cbValueName = cbValueNameLen;
4384
4385         ppev[dwIndex].dwType = dwType;
4386
4387         memcpy (pEnumValues, lpValue, cbValueLen);
4388         ppev[dwIndex].pData = pEnumValues;
4389         pEnumValues += cbValueLen;
4390
4391         ppev[dwIndex].cbData = cbValueLen;
4392
4393         TRACE ("%s [%li]: copied name (%li bytes) and data (%li bytes)\n",
4394                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
4395     }
4396
4397     if (HeapFree (hHeap, 0, lpValue) == 0)
4398     {
4399         ret = GetLastError ();
4400         ERR ("HeapFree failed with code %li\n", ret);
4401         if (HeapFree (hHeap, 0, lpValueName) == 0)
4402             WARN ("HeapFree failed with code %li\n", GetLastError ());
4403         r = RegCloseKey (hkSubKey);
4404         if (r != ERROR_SUCCESS)
4405             WARN ("RegCloseKey returned %li\n", r);
4406         return ret;
4407     }
4408
4409     if (HeapFree (hHeap, 0, lpValueName) == 0)
4410     {
4411         ret = GetLastError ();
4412         ERR ("HeapFree failed with code %li\n", ret);
4413         r = RegCloseKey (hkSubKey);
4414         if (r != ERROR_SUCCESS)
4415             WARN ("RegCloseKey returned %li\n", r);
4416         return ret;
4417     }
4418
4419     ret = RegCloseKey (hkSubKey);
4420     if (ret != ERROR_SUCCESS)
4421     {
4422         ERR ("RegCloseKey returned %li\n", ret);
4423         return ret;
4424     }
4425
4426     return ERROR_SUCCESS;
4427 }
4428
4429 /*******************************************************************************
4430  *              EnumPrinterDataExA      [WINSPOOL.@]
4431  *
4432  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
4433  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
4434  * what Windows 2000 SP1 does.
4435  *
4436  */
4437 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
4438                                 LPBYTE pEnumValues, DWORD cbEnumValues,
4439                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
4440 {
4441     INT     len;
4442     LPWSTR  pKeyNameW;
4443     DWORD   ret, dwIndex, dwBufSize;
4444     HANDLE  hHeap;
4445     LPSTR   pBuffer;
4446
4447     TRACE ("%p %s\n", hPrinter, pKeyName);
4448
4449     if (pKeyName == NULL || *pKeyName == 0)
4450         return ERROR_INVALID_PARAMETER;
4451
4452     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
4453     if (len == 0)
4454     {
4455         ret = GetLastError ();
4456         ERR ("MultiByteToWideChar failed with code %li\n", ret);
4457         return ret;
4458     }
4459
4460     hHeap = GetProcessHeap ();
4461     if (hHeap == NULL)
4462     {
4463         ERR ("GetProcessHeap failed\n");
4464         return ERROR_OUTOFMEMORY;
4465     }
4466
4467     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
4468     if (pKeyNameW == NULL)
4469     {
4470         ERR ("Failed to allocate %li bytes from process heap\n",
4471                 (LONG) len * sizeof (WCHAR));
4472         return ERROR_OUTOFMEMORY;
4473     }
4474
4475     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
4476     {
4477         ret = GetLastError ();
4478         ERR ("MultiByteToWideChar failed with code %li\n", ret);
4479         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
4480             WARN ("HeapFree failed with code %li\n", GetLastError ());
4481         return ret;
4482     }
4483
4484     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
4485             pcbEnumValues, pnEnumValues);
4486     if (ret != ERROR_SUCCESS)
4487     {
4488         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
4489             WARN ("HeapFree failed with code %li\n", GetLastError ());
4490         TRACE ("EnumPrinterDataExW returned %li\n", ret);
4491         return ret;
4492     }
4493
4494     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
4495     {
4496         ret = GetLastError ();
4497         ERR ("HeapFree failed with code %li\n", ret);
4498         return ret;
4499     }
4500
4501     if (*pnEnumValues == 0)     /* empty key */
4502         return ERROR_SUCCESS;
4503
4504     dwBufSize = 0;
4505     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
4506     {
4507         PPRINTER_ENUM_VALUESW ppev =
4508                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
4509
4510         if (dwBufSize < ppev->cbValueName)
4511             dwBufSize = ppev->cbValueName;
4512
4513         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
4514                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
4515             dwBufSize = ppev->cbData;
4516     }
4517
4518     TRACE ("Largest Unicode name or value is %li bytes\n", dwBufSize);
4519
4520     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
4521     if (pBuffer == NULL)
4522     {
4523         ERR ("Failed to allocate %li bytes from process heap\n", dwBufSize);
4524         return ERROR_OUTOFMEMORY;
4525     }
4526
4527     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
4528     {
4529         PPRINTER_ENUM_VALUESW ppev =
4530                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
4531
4532         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
4533                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
4534                 NULL);
4535         if (len == 0)
4536         {
4537             ret = GetLastError ();
4538             ERR ("WideCharToMultiByte failed with code %li\n", ret);
4539             if (HeapFree (hHeap, 0, pBuffer) == 0)
4540                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4541             return ret;
4542         }
4543
4544         memcpy (ppev->pValueName, pBuffer, len);
4545
4546         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
4547
4548         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
4549                 ppev->dwType != REG_MULTI_SZ)
4550             continue;
4551
4552         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
4553                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
4554         if (len == 0)
4555         {
4556             ret = GetLastError ();
4557             ERR ("WideCharToMultiByte failed with code %li\n", ret);
4558             if (HeapFree (hHeap, 0, pBuffer) == 0)
4559                 WARN ("HeapFree failed with code %li\n", GetLastError ());
4560             return ret;
4561         }
4562
4563         memcpy (ppev->pData, pBuffer, len);
4564
4565         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
4566         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
4567     }
4568
4569     if (HeapFree (hHeap, 0, pBuffer) == 0)
4570     {
4571         ret = GetLastError ();
4572         ERR ("HeapFree failed with code %li\n", ret);
4573         return ret;
4574     }
4575
4576     return ERROR_SUCCESS;
4577 }
4578
4579 /******************************************************************************
4580  *      AbortPrinter (WINSPOOL.@)
4581  */
4582 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
4583 {
4584     FIXME("(%p), stub!\n", hPrinter);
4585     return TRUE;
4586 }
4587
4588 /******************************************************************************
4589  *              AddPortA (WINSPOOL.@)
4590  *
4591  * See AddPortW.
4592  *
4593  */
4594 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
4595 {
4596     FIXME("(%s, %p, %s), stub!\n",debugstr_a(pName),hWnd,debugstr_a(pMonitorName));
4597     return FALSE;
4598 }
4599
4600 /******************************************************************************
4601  *      AddPortW (WINSPOOL.@)
4602  *
4603  * Add a Port for a specific Monitor
4604  *
4605  * PARAMS
4606  *  pName        [I] Servername or NULL (local Computer)
4607  *  hWnd         [I] Handle to parent Window for the Dialog-Box
4608  *  pMonitorName [I] Name of the Monitor that manage the Port
4609  *
4610  * RETURNS
4611  *  Success: TRUE
4612  *  Failure: FALSE
4613  *
4614  * BUGS
4615  *  only a Stub
4616  *
4617  */
4618 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
4619 {
4620     FIXME("(%s, %p, %s), stub!\n",debugstr_w(pName),hWnd,debugstr_w(pMonitorName));
4621     return FALSE;
4622 }
4623
4624 /******************************************************************************
4625  *             AddPortExA (WINSPOOL.@)
4626  *
4627  * See AddPortExW.
4628  *
4629  */
4630 BOOL WINAPI AddPortExA(HANDLE hMonitor, LPSTR pName, DWORD Level, LPBYTE lpBuffer, LPSTR lpMonitorName)
4631 {
4632     FIXME("(%p, %s, %ld, %p, %s), stub!\n",hMonitor, debugstr_a(pName), Level,
4633           lpBuffer, debugstr_a(lpMonitorName));
4634     return FALSE;
4635 }
4636
4637 /******************************************************************************
4638  *             AddPortExW (WINSPOOL.@)
4639  *
4640  * Add a Port for a specific Monitor, without presenting a user interface
4641  *
4642  * PARAMS
4643  *  hMonitor      [I] Handle from InitializePrintMonitor2()
4644  *  pName         [I] Servername or NULL (local Computer)
4645  *  Level         [I] Structure-Level (1 or 2) for lpBuffer
4646  *  lpBuffer      [I] PTR to: PORT_INFO_1 or PORT_INFO_2
4647  *  lpMonitorName [I] Name of the Monitor that manage the Port or NULL
4648  *
4649  * RETURNS
4650  *  Success: TRUE
4651  *  Failure: FALSE
4652  *
4653  * BUGS
4654  *  only a Stub
4655  *
4656  */
4657 BOOL WINAPI AddPortExW(HANDLE hMonitor, LPWSTR pName, DWORD Level, LPBYTE lpBuffer, LPWSTR lpMonitorName)
4658 {
4659     FIXME("(%p, %s, %ld, %p, %s), stub!\n", hMonitor, debugstr_w(pName), Level,
4660           lpBuffer, debugstr_w(lpMonitorName));
4661     return FALSE;
4662 }
4663
4664 /******************************************************************************
4665  *      AddPrinterConnectionA (WINSPOOL.@)
4666  */
4667 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
4668 {
4669     FIXME("%s\n", debugstr_a(pName));
4670     return FALSE;
4671 }
4672
4673 /******************************************************************************
4674  *      AddPrinterConnectionW (WINSPOOL.@)
4675  */
4676 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
4677 {
4678     FIXME("%s\n", debugstr_w(pName));
4679     return FALSE;
4680 }
4681
4682 /******************************************************************************
4683  *              AddPrinterDriverExW (WINSPOOL.@)
4684  */
4685 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD Level,
4686     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
4687 {
4688     FIXME("%s %ld %p %ld\n", debugstr_w(pName),
4689            Level, pDriverInfo, dwFileCopyFlags);
4690     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
4691     return FALSE;
4692 }
4693
4694 /******************************************************************************
4695  *              AddPrinterDriverExA (WINSPOOL.@)
4696  */
4697 BOOL WINAPI AddPrinterDriverExA( LPSTR pName, DWORD Level,
4698     LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
4699 {
4700     FIXME("%s %ld %p %ld\n", debugstr_a(pName),
4701            Level, pDriverInfo, dwFileCopyFlags);
4702     SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
4703     return FALSE;
4704 }
4705
4706 /******************************************************************************
4707  *      ConfigurePortA (WINSPOOL.@)
4708  *
4709  * See ConfigurePortW.
4710  *
4711  */
4712 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
4713 {
4714     FIXME("%s %p %s\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
4715     return FALSE;
4716 }
4717
4718 /******************************************************************************
4719  *      ConfigurePortW (WINSPOOL.@)
4720  *
4721  * Display the Configuration-Dialog for a specific Port
4722  *
4723  * PARAMS
4724  *  pName     [I] Servername or NULL (local Computer)
4725  *  hWnd      [I] Handle to parent Window for the Dialog-Box
4726  *  pPortName [I] Name of the Port, that should be configured
4727  *
4728  * RETURNS
4729  *  Success: TRUE
4730  *  Failure: FALSE
4731  *
4732  * BUGS
4733  *  only a Stub
4734  *
4735  */
4736 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
4737 {
4738     FIXME("%s %p %s\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
4739     return FALSE;
4740 }
4741
4742 /******************************************************************************
4743  *      ConnectToPrinterDlg (WINSPOOL.@)
4744  */
4745 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
4746 {
4747     FIXME("%p %lx\n", hWnd, Flags);
4748     return NULL;
4749 }
4750
4751 /******************************************************************************
4752  *      DeletePrinterConnectionA (WINSPOOL.@)
4753  */
4754 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
4755 {
4756     FIXME("%s\n", debugstr_a(pName));
4757     return TRUE;
4758 }
4759
4760 /******************************************************************************
4761  *      DeletePrinterConnectionW (WINSPOOL.@)
4762  */
4763 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
4764 {
4765     FIXME("%s\n", debugstr_w(pName));
4766     return TRUE;
4767 }
4768
4769 /******************************************************************************
4770  *              DeletePrinterDriverExW (WINSPOOL.@)
4771  */
4772 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
4773     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
4774 {
4775     FIXME("%s %s %s %lx %lx\n", debugstr_w(pName), debugstr_w(pEnvironment),
4776           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
4777     return TRUE;
4778 }
4779
4780 /******************************************************************************
4781  *              DeletePrinterDriverExA (WINSPOOL.@)
4782  */
4783 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
4784     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
4785 {
4786     FIXME("%s %s %s %lx %lx\n", debugstr_a(pName), debugstr_a(pEnvironment),
4787           debugstr_a(pDriverName), dwDeleteFlag, dwVersionFlag);
4788     return TRUE;
4789 }
4790
4791 /******************************************************************************
4792  *              DeletePrinterDataExW (WINSPOOL.@)
4793  */
4794 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
4795                                   LPCWSTR pValueName)
4796 {
4797     FIXME("%p %s %s\n", hPrinter, 
4798           debugstr_w(pKeyName), debugstr_w(pValueName));
4799     return ERROR_INVALID_PARAMETER;
4800 }
4801
4802 /******************************************************************************
4803  *              DeletePrinterDataExA (WINSPOOL.@)
4804  */
4805 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
4806                                   LPCSTR pValueName)
4807 {
4808     FIXME("%p %s %s\n", hPrinter, 
4809           debugstr_a(pKeyName), debugstr_a(pValueName));
4810     return ERROR_INVALID_PARAMETER;
4811 }
4812
4813 /******************************************************************************
4814  *      DeletePrintProcessorA (WINSPOOL.@)
4815  */
4816 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
4817 {
4818     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
4819           debugstr_a(pPrintProcessorName));
4820     return TRUE;
4821 }
4822
4823 /******************************************************************************
4824  *      DeletePrintProcessorW (WINSPOOL.@)
4825  */
4826 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
4827 {
4828     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
4829           debugstr_w(pPrintProcessorName));
4830     return TRUE;
4831 }
4832
4833 /******************************************************************************
4834  *      DeletePrintProvidorA (WINSPOOL.@)
4835  */
4836 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
4837 {
4838     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
4839           debugstr_a(pPrintProviderName));
4840     return TRUE;
4841 }
4842
4843 /******************************************************************************
4844  *      DeletePrintProvidorW (WINSPOOL.@)
4845  */
4846 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
4847 {
4848     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
4849           debugstr_w(pPrintProviderName));
4850     return TRUE;
4851 }
4852
4853 /******************************************************************************
4854  *      EnumFormsA (WINSPOOL.@)
4855  */
4856 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
4857     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
4858 {
4859     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
4860     return FALSE;
4861 }
4862
4863 /******************************************************************************
4864  *      EnumFormsW (WINSPOOL.@)
4865  */
4866 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
4867     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
4868 {
4869     FIXME("%p %lx %p %lx %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
4870     return FALSE;
4871 }
4872
4873 /*****************************************************************************
4874  *          EnumMonitorsA [WINSPOOL.@]
4875  *
4876  * See EnumMonitorsW.
4877  *
4878  */
4879 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
4880                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4881 {
4882     FIXME("%s,%ld,%p,%ld,%p,%p\n", debugstr_a(pName), Level, pMonitors,
4883           cbBuf, pcbNeeded, pcReturned);
4884     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4885     return 0;
4886 }
4887
4888 /*****************************************************************************
4889  *          EnumMonitorsW [WINSPOOL.@]
4890  *
4891  * Enumerate available Monitors
4892  *
4893  * PARAMS
4894  *  pName       [I] Servername or NULL (local Computer)
4895  *  Level       [I] Structure-Level (1:Win9x+NT or 2:NT only)
4896  *  pMonitors   [O] PTR to Buffer that receives the Result
4897  *  cbBuf       [I] Size of Buffer at pMonitors
4898  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
4899  *  pcReturned  [O] PTR to DWORD that receives the number of Monitors in pMonitors
4900  *
4901  * RETURNS
4902  *  Success: TRUE
4903  *  Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
4904  *
4905  * BUGS
4906  *  only a Stub
4907  *
4908  */
4909 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
4910                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4911 {
4912     FIXME("%s,%ld,%p,%ld,%p,%p\n", debugstr_w(pName), Level, pMonitors,
4913           cbBuf, pcbNeeded, pcReturned);
4914     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4915     return 0;
4916 }
4917
4918 /******************************************************************************
4919  *              XcvDataW (WINSPOOL.@)
4920  *
4921  * Notes:
4922  *  There doesn't seem to be an A version...
4923  */
4924 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
4925     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
4926     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
4927 {
4928     FIXME("%p %s %p %ld %p %ld %p %p\n", hXcv, debugstr_w(pszDataName), 
4929           pInputData, cbInputData, pOutputData,
4930           cbOutputData, pcbOutputNeeded, pdwStatus);
4931     return FALSE;
4932 }
4933
4934 /*****************************************************************************
4935  *          EnumPrinterDataA [WINSPOOL.@]
4936  *
4937  */
4938 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
4939     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
4940     DWORD cbData, LPDWORD pcbData )
4941 {
4942     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
4943           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
4944     return ERROR_NO_MORE_ITEMS;
4945 }
4946
4947 /*****************************************************************************
4948  *          EnumPrinterDataW [WINSPOOL.@]
4949  *
4950  */
4951 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
4952     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
4953     DWORD cbData, LPDWORD pcbData )
4954 {
4955     FIXME("%p %lx %p %lx %p %p %p %lx %p\n", hPrinter, dwIndex, pValueName,
4956           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
4957     return ERROR_NO_MORE_ITEMS;
4958 }
4959
4960 /*****************************************************************************
4961  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
4962  *
4963  */
4964 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
4965                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
4966                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
4967 {
4968     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_a(pName),
4969           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
4970           pcbNeeded, pcReturned);
4971     return FALSE;
4972 }
4973
4974 /*****************************************************************************
4975  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
4976  *
4977  */
4978 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
4979                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
4980                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
4981 {
4982     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
4983           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
4984           pcbNeeded, pcReturned);
4985     return FALSE;
4986 }
4987
4988 /*****************************************************************************
4989  *          EnumPrintProcessorsA [WINSPOOL.@]
4990  *
4991  */
4992 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
4993     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
4994 {
4995     FIXME("Stub: %s %s %ld %p %ld %p %p\n", pName, pEnvironment, Level,
4996         pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
4997     return FALSE;
4998 }
4999
5000 /*****************************************************************************
5001  *          EnumPrintProcessorsW [WINSPOOL.@]
5002  *
5003  */
5004 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5005     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
5006 {
5007     FIXME("Stub: %s %s %ld %p %ld %p %p\n", debugstr_w(pName),
5008         debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
5009         cbBuf, pcbNeeded, pcbReturned);
5010     return FALSE;
5011 }
5012
5013 /*****************************************************************************
5014  *          ExtDeviceMode [WINSPOOL.@]
5015  *
5016  */
5017 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
5018     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
5019     DWORD fMode)
5020 {
5021     FIXME("Stub: %p %p %p %s %s %p %s %lx\n", hWnd, hInst, pDevModeOutput,
5022           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
5023           debugstr_a(pProfile), fMode);
5024     return -1;
5025 }
5026
5027 /*****************************************************************************
5028  *          FindClosePrinterChangeNotification [WINSPOOL.@]
5029  *
5030  */
5031 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
5032 {
5033     FIXME("Stub: %p\n", hChange);
5034     return TRUE;
5035 }
5036
5037 /*****************************************************************************
5038  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
5039  *
5040  */
5041 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
5042     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
5043 {
5044     FIXME("Stub: %p %lx %lx %p\n",
5045           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
5046     return INVALID_HANDLE_VALUE;
5047 }
5048
5049 /*****************************************************************************
5050  *          FindNextPrinterChangeNotification [WINSPOOL.@]
5051  *
5052  */
5053 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
5054     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
5055 {
5056     FIXME("Stub: %p %p %p %p\n",
5057           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
5058     return FALSE;
5059 }
5060
5061 /*****************************************************************************
5062  *          FreePrinterNotifyInfo [WINSPOOL.@]
5063  *
5064  */
5065 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
5066 {
5067     FIXME("Stub: %p\n", pPrinterNotifyInfo);
5068     return TRUE;
5069 }
5070
5071 /*****************************************************************************
5072  *          string_to_buf
5073  *
5074  * Copies a unicode string into a buffer.  The buffer will either contain unicode or
5075  * ansi depending on the unicode parameter.
5076  */
5077 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
5078 {
5079     if(!str)
5080     {
5081         *size = 0;
5082         return TRUE;
5083     }
5084
5085     if(unicode)
5086     {
5087         *size = (strlenW(str) + 1) * sizeof(WCHAR);
5088         if(*size <= cb)
5089         {
5090             memcpy(ptr, str, *size);
5091             return TRUE;
5092         }
5093         return FALSE;
5094     }
5095     else
5096     {
5097         *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
5098         if(*size <= cb)
5099         {
5100             WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
5101             return TRUE;
5102         }
5103         return FALSE;
5104     }
5105 }
5106
5107 /*****************************************************************************
5108  *          get_job_info_1
5109  */
5110 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
5111                            LPDWORD pcbNeeded, BOOL unicode)
5112 {
5113     DWORD size, left = cbBuf;
5114     BOOL space = (cbBuf > 0);
5115     LPBYTE ptr = buf;
5116
5117     *pcbNeeded = 0;
5118
5119     if(space)
5120     {
5121         ji1->JobId = job->job_id;
5122     }
5123
5124     string_to_buf(job->document_title, ptr, left, &size, unicode);
5125     if(space && size <= left)
5126     {
5127         ji1->pDocument = (LPWSTR)ptr;
5128         ptr += size;
5129         left -= size;
5130     }
5131     else
5132         space = FALSE;
5133     *pcbNeeded += size;
5134
5135     return space;
5136 }
5137
5138 /*****************************************************************************
5139  *          get_job_info_2
5140  */
5141 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
5142                            LPDWORD pcbNeeded, BOOL unicode)
5143 {
5144     DWORD size, left = cbBuf;
5145     BOOL space = (cbBuf > 0);
5146     LPBYTE ptr = buf;
5147
5148     *pcbNeeded = 0;
5149
5150     if(space)
5151     {
5152         ji2->JobId = job->job_id;
5153     }
5154
5155     string_to_buf(job->document_title, ptr, left, &size, unicode);
5156     if(space && size <= left)
5157     {
5158         ji2->pDocument = (LPWSTR)ptr;
5159         ptr += size;
5160         left -= size;
5161     }
5162     else
5163         space = FALSE;
5164     *pcbNeeded += size;
5165
5166     return space;
5167 }
5168
5169 /*****************************************************************************
5170  *          get_job_info
5171  */
5172 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
5173                          DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
5174 {
5175     BOOL ret = FALSE;
5176     DWORD needed = 0, size;
5177     job_t *job;
5178     LPBYTE ptr = pJob;
5179
5180     TRACE("%p %ld %ld %p %ld %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
5181
5182     EnterCriticalSection(&printer_handles_cs);
5183     job = get_job(hPrinter, JobId);
5184     if(!job)
5185         goto end;
5186
5187     switch(Level)
5188     {
5189     case 1:
5190         size = sizeof(JOB_INFO_1W);
5191         if(cbBuf >= size)
5192         {
5193             cbBuf -= size;
5194             ptr += size;
5195             memset(pJob, 0, size);
5196         }
5197         else
5198             cbBuf = 0;
5199         ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
5200         needed += size;
5201         break;
5202
5203     case 2:
5204         size = sizeof(JOB_INFO_2W);
5205         if(cbBuf >= size)
5206         {
5207             cbBuf -= size;
5208             ptr += size;
5209             memset(pJob, 0, size);
5210         }
5211         else
5212             cbBuf = 0;
5213         ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
5214         needed += size;
5215         break;
5216
5217     case 3:
5218         size = sizeof(JOB_INFO_3);
5219         if(cbBuf >= size)
5220         {
5221             cbBuf -= size;
5222             memset(pJob, 0, size);
5223             ret = TRUE;
5224         }
5225         else
5226             cbBuf = 0;
5227         needed = size;
5228         break;
5229
5230     default:
5231         SetLastError(ERROR_INVALID_LEVEL);
5232         goto end;
5233     }
5234     if(pcbNeeded)
5235         *pcbNeeded = needed;
5236 end:
5237     LeaveCriticalSection(&printer_handles_cs);
5238     return ret;
5239 }
5240
5241 /*****************************************************************************
5242  *          GetJobA [WINSPOOL.@]
5243  *
5244  */
5245 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
5246                     DWORD cbBuf, LPDWORD pcbNeeded)
5247 {
5248     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
5249 }
5250
5251 /*****************************************************************************
5252  *          GetJobW [WINSPOOL.@]
5253  *
5254  */
5255 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
5256                     DWORD cbBuf, LPDWORD pcbNeeded)
5257 {
5258     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
5259 }
5260
5261 /*****************************************************************************
5262  *          schedule_lpr
5263  */
5264 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
5265 {
5266     char *unixname, *queue, *cmd;
5267     char fmt[] = "lpr -P%s %s";
5268     DWORD len;
5269
5270     if(!(unixname = wine_get_unix_file_name(filename)))
5271         return FALSE;
5272
5273     len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
5274     queue = HeapAlloc(GetProcessHeap(), 0, len);
5275     WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
5276
5277     cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
5278     sprintf(cmd, fmt, queue, unixname);
5279
5280     TRACE("printing with: %s\n", cmd);
5281     system(cmd);
5282
5283     HeapFree(GetProcessHeap(), 0, cmd);
5284     HeapFree(GetProcessHeap(), 0, queue);
5285     HeapFree(GetProcessHeap(), 0, unixname);
5286     return TRUE;
5287 }
5288
5289 /*****************************************************************************
5290  *          schedule_cups
5291  */
5292 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
5293 {
5294 #if HAVE_CUPS_CUPS_H
5295     if(pcupsPrintFile)
5296     {
5297         char *unixname, *queue, *doc_titleA;
5298         DWORD len;
5299         BOOL ret;
5300
5301         if(!(unixname = wine_get_unix_file_name(filename)))
5302             return FALSE;
5303
5304         len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
5305         queue = HeapAlloc(GetProcessHeap(), 0, len);
5306         WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
5307
5308         len = WideCharToMultiByte(CP_ACP, 0, document_title, -1, NULL, 0, NULL, NULL);
5309         doc_titleA = HeapAlloc(GetProcessHeap(), 0, len);
5310         WideCharToMultiByte(CP_ACP, 0, document_title, -1, doc_titleA, len, NULL, NULL);
5311
5312         TRACE("printing via cups\n");
5313         ret = pcupsPrintFile(queue, unixname, doc_titleA, 0, NULL);
5314         HeapFree(GetProcessHeap(), 0, doc_titleA);
5315         HeapFree(GetProcessHeap(), 0, queue);
5316         HeapFree(GetProcessHeap(), 0, unixname);
5317         return ret;
5318     }
5319     else
5320 #endif
5321     {
5322         return schedule_lpr(printer_name, filename);
5323     }
5324 }
5325
5326 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
5327 {
5328     LPWSTR filename;
5329
5330     switch(msg)
5331     {
5332     case WM_INITDIALOG:
5333         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
5334         return TRUE;
5335
5336     case WM_COMMAND:
5337         if(HIWORD(wparam) == BN_CLICKED)
5338         {
5339             if(LOWORD(wparam) == IDOK)
5340             {
5341                 HANDLE hf;
5342                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
5343                 LPWSTR *output;
5344
5345                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
5346                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
5347
5348                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
5349                 {
5350                     WCHAR caption[200], message[200];
5351                     int mb_ret;
5352
5353                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
5354                     LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
5355                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
5356                     if(mb_ret == IDCANCEL)
5357                     {
5358                         HeapFree(GetProcessHeap(), 0, filename);
5359                         return TRUE;
5360                     }
5361                 }
5362                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
5363                 if(hf == INVALID_HANDLE_VALUE)
5364                 {
5365                     WCHAR caption[200], message[200];
5366
5367                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
5368                     LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
5369                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
5370                     HeapFree(GetProcessHeap(), 0, filename);
5371                     return TRUE;
5372                 }
5373                 CloseHandle(hf);
5374                 DeleteFileW(filename);
5375                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
5376                 *output = filename;
5377                 EndDialog(hwnd, IDOK);
5378                 return TRUE;
5379             }
5380             if(LOWORD(wparam) == IDCANCEL)
5381             {
5382                 EndDialog(hwnd, IDCANCEL);
5383                 return TRUE;
5384             }
5385         }
5386         return FALSE;
5387     }
5388     return FALSE;
5389 }
5390
5391 /*****************************************************************************
5392  *          get_filename
5393  */
5394 static BOOL get_filename(LPWSTR *filename)
5395 {
5396     return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
5397                            file_dlg_proc, (LPARAM)filename) == IDOK;
5398 }
5399
5400 /*****************************************************************************
5401  *          schedule_file
5402  */
5403 static BOOL schedule_file(LPCWSTR filename)
5404 {
5405     LPWSTR output = NULL;
5406
5407     if(get_filename(&output))
5408     {
5409         TRACE("copy to %s\n", debugstr_w(output));
5410         CopyFileW(filename, output, FALSE);
5411         HeapFree(GetProcessHeap(), 0, output);
5412         return TRUE;
5413     }
5414     return FALSE;
5415 }
5416
5417 /*****************************************************************************
5418  *          schedule_pipe
5419  */
5420 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
5421 {
5422 #ifdef HAVE_FORK
5423     char *unixname, *cmdA;
5424     DWORD len;
5425     int fds[2] = {-1, -1}, file_fd = -1, no_read;
5426     BOOL ret = FALSE;
5427     char buf[1024];
5428
5429     if(!(unixname = wine_get_unix_file_name(filename)))
5430         return FALSE;
5431
5432     len = WideCharToMultiByte(CP_ACP, 0, cmd, -1, NULL, 0, NULL, NULL);
5433     cmdA = HeapAlloc(GetProcessHeap(), 0, len);
5434     WideCharToMultiByte(CP_ACP, 0, cmd, -1, cmdA, len, NULL, NULL);
5435
5436     TRACE("printing with: %s\n", cmdA);
5437
5438     if((file_fd = open(unixname, O_RDONLY)) == -1)
5439         goto end;
5440
5441     if (pipe(fds))
5442     {
5443         ERR("pipe() failed!\n"); 
5444         goto end;
5445     }
5446
5447     if (fork() == 0)
5448     {
5449         close(0);
5450         dup2(fds[0], 0);
5451         close(fds[1]);
5452
5453         /* reset signals that we previously set to SIG_IGN */
5454         signal(SIGPIPE, SIG_DFL);
5455         signal(SIGCHLD, SIG_DFL);
5456
5457         system(cmdA);
5458         exit(0);
5459     }
5460
5461     while((no_read = read(file_fd, buf, sizeof(buf))))
5462         write(fds[1], buf, no_read);
5463
5464     ret = TRUE;
5465
5466 end:
5467     if(file_fd != -1) close(file_fd);
5468     if(fds[0] != -1) close(fds[0]);
5469     if(fds[1] != -1) close(fds[1]);
5470
5471     HeapFree(GetProcessHeap(), 0, cmdA);
5472     HeapFree(GetProcessHeap(), 0, unixname);
5473     return ret;
5474 #else
5475     return FALSE;
5476 #endif
5477 }
5478
5479 /*****************************************************************************
5480  *          schedule_unixfile
5481  */
5482 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
5483 {
5484     int in_fd, out_fd, no_read;
5485     char buf[1024];
5486     BOOL ret = FALSE;
5487     char *unixname, *outputA;
5488     DWORD len;
5489
5490     if(!(unixname = wine_get_unix_file_name(filename)))
5491         return FALSE;
5492
5493     len = WideCharToMultiByte(CP_ACP, 0, output, -1, NULL, 0, NULL, NULL);
5494     outputA = HeapAlloc(GetProcessHeap(), 0, len);
5495     WideCharToMultiByte(CP_ACP, 0, output, -1, outputA, len, NULL, NULL);
5496     
5497     out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
5498     in_fd = open(unixname, O_RDONLY);
5499     if(out_fd == -1 || in_fd == -1)
5500         goto end;
5501
5502     while((no_read = read(in_fd, buf, sizeof(buf))))
5503         write(out_fd, buf, no_read);
5504
5505     ret = TRUE;
5506 end:
5507     if(in_fd != -1) close(in_fd);
5508     if(out_fd != -1) close(out_fd);
5509     HeapFree(GetProcessHeap(), 0, outputA);
5510     HeapFree(GetProcessHeap(), 0, unixname);
5511     return ret;
5512 }
5513
5514 /*****************************************************************************
5515  *          ScheduleJob [WINSPOOL.@]
5516  *
5517  */
5518 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
5519 {
5520     opened_printer_t *printer;
5521     BOOL ret = FALSE;
5522     struct list *cursor, *cursor2;
5523
5524     TRACE("(%p, %lx)\n", hPrinter, dwJobID);
5525     EnterCriticalSection(&printer_handles_cs);
5526     printer = get_opened_printer(hPrinter);
5527     if(!printer)
5528         goto end;
5529
5530     LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
5531     {
5532         job_t *job = LIST_ENTRY(cursor, job_t, entry);
5533         HANDLE hf;
5534
5535         if(job->job_id != dwJobID) continue;
5536
5537         hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
5538         if(hf != INVALID_HANDLE_VALUE)
5539         {
5540             PRINTER_INFO_5W *pi5;
5541             DWORD needed;
5542             HKEY hkey;
5543             WCHAR output[1024];
5544             static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
5545                                                 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
5546
5547             GetPrinterW(hPrinter, 5, NULL, 0, &needed);
5548             pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
5549             GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
5550             TRACE("need to schedule job %ld filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
5551                   debugstr_w(pi5->pPortName));
5552             
5553             output[0] = 0;
5554
5555             /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
5556             if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
5557             {
5558                 DWORD type, count = sizeof(output);
5559                 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
5560                 RegCloseKey(hkey);
5561             }
5562             if(output[0] == '|')
5563             {
5564                 schedule_pipe(output + 1, job->filename);
5565             }
5566             else if(output[0])
5567             {
5568                 schedule_unixfile(output, job->filename);
5569             }
5570             else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
5571             {
5572                 schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
5573             }
5574             else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
5575             {
5576                 schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
5577             }
5578             else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
5579             {
5580                 schedule_file(job->filename);
5581             }
5582             else
5583             {
5584                 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
5585             }
5586             HeapFree(GetProcessHeap(), 0, pi5);
5587             CloseHandle(hf);
5588             DeleteFileW(job->filename);
5589         }
5590         list_remove(cursor);
5591         HeapFree(GetProcessHeap(), 0, job->document_title);
5592         HeapFree(GetProcessHeap(), 0, job->filename);
5593         HeapFree(GetProcessHeap(), 0, job);
5594         ret = TRUE;
5595         break;
5596     }
5597 end:
5598     LeaveCriticalSection(&printer_handles_cs);
5599     return ret;
5600 }
5601
5602 /*****************************************************************************
5603  *          StartDocDlgA [WINSPOOL.@]
5604  */
5605  LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
5606 {
5607     UNICODE_STRING usBuffer;
5608     DOCINFOW docW;
5609     LPWSTR retW;
5610     LPSTR ret = NULL;
5611
5612     docW.cbSize = sizeof(docW);
5613     docW.lpszDocName = asciitounicode(&usBuffer, doc->lpszDocName);
5614     docW.lpszOutput = asciitounicode(&usBuffer, doc->lpszOutput);
5615     docW.lpszDatatype = asciitounicode(&usBuffer, doc->lpszDatatype);
5616     docW.fwType = doc->fwType;
5617
5618     retW = StartDocDlgW(hPrinter, &docW);
5619
5620     if(retW)
5621     {
5622         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
5623         ret = HeapAlloc(GetProcessHeap(), 0, len);
5624         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
5625         HeapFree(GetProcessHeap(), 0, retW);
5626     }
5627
5628     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszDatatype);
5629     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszOutput);
5630     HeapFree(GetProcessHeap(), 0, (LPWSTR)docW.lpszDocName);
5631
5632     return ret;
5633 }
5634
5635 /*****************************************************************************
5636  *          StartDocDlgW [WINSPOOL.@]
5637  *
5638  * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
5639  * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
5640  * port is "FILE:". Also returns the full path if passed a relative path.
5641  *
5642  * The caller should free the returned string from the process heap.
5643  */
5644 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
5645 {
5646     LPWSTR ret = NULL;
5647     DWORD len, attr;
5648
5649     if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
5650     {
5651         PRINTER_INFO_5W *pi5;
5652         GetPrinterW(hPrinter, 5, NULL, 0, &len);
5653         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
5654             return NULL;
5655         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
5656         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
5657         if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
5658         {
5659             HeapFree(GetProcessHeap(), 0, pi5);
5660             return NULL;
5661         }
5662         HeapFree(GetProcessHeap(), 0, pi5);
5663     }
5664
5665     if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
5666     {
5667         LPWSTR name;
5668         get_filename(&name);
5669         if(name)
5670         {
5671             if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
5672             {
5673                 HeapFree(GetProcessHeap(), 0, name);
5674                 return NULL;
5675             }
5676             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5677             GetFullPathNameW(name, len, ret, NULL);
5678             HeapFree(GetProcessHeap(), 0, name);
5679         }
5680         return ret;
5681     }
5682
5683     if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
5684         return NULL;
5685
5686     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5687     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
5688         
5689     attr = GetFileAttributesW(ret);
5690     if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
5691     {
5692         HeapFree(GetProcessHeap(), 0, ret);
5693         ret = NULL;
5694     }
5695     return ret;
5696 }