sane.ds: Add partial stub support for ICAP_PIXELTYPE, with tests. Fixes a first crash...
[wine] / dlls / winspool.drv / 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  * Copyright 2005-2009 Detlef Riekenberg
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25
26 #include "config.h"
27 #include "wine/port.h"
28
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <stddef.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <signal.h>
39 #ifdef HAVE_CUPS_CUPS_H
40 # include <cups/cups.h>
41 #endif
42
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
45 #include "wine/library.h"
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winuser.h"
49 #include "winerror.h"
50 #include "winreg.h"
51 #include "wingdi.h"
52 #include "winspool.h"
53 #include "winternl.h"
54 #include "wine/windef16.h"
55 #include "wine/unicode.h"
56 #include "wine/debug.h"
57 #include "wine/list.h"
58 #include "winnls.h"
59
60 #include "ddk/winsplp.h"
61 #include "wspool.h"
62
63 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
64
65 /* ############################### */
66
67 static CRITICAL_SECTION monitor_handles_cs;
68 static CRITICAL_SECTION_DEBUG monitor_handles_cs_debug = 
69 {
70     0, 0, &monitor_handles_cs,
71     { &monitor_handles_cs_debug.ProcessLocksList, &monitor_handles_cs_debug.ProcessLocksList },
72       0, 0, { (DWORD_PTR)(__FILE__ ": monitor_handles_cs") }
73 };
74 static CRITICAL_SECTION monitor_handles_cs = { &monitor_handles_cs_debug, -1, 0, 0, 0, 0 };
75
76
77 static CRITICAL_SECTION printer_handles_cs;
78 static CRITICAL_SECTION_DEBUG printer_handles_cs_debug = 
79 {
80     0, 0, &printer_handles_cs,
81     { &printer_handles_cs_debug.ProcessLocksList, &printer_handles_cs_debug.ProcessLocksList },
82       0, 0, { (DWORD_PTR)(__FILE__ ": printer_handles_cs") }
83 };
84 static CRITICAL_SECTION printer_handles_cs = { &printer_handles_cs_debug, -1, 0, 0, 0, 0 };
85
86 /* ############################### */
87
88 typedef struct {
89     struct list     entry;
90     LPWSTR          name;
91     LPWSTR          dllname;
92     PMONITORUI      monitorUI;
93     LPMONITOR       monitor;
94     HMODULE         hdll;
95     DWORD           refcount;
96     DWORD           dwMonitorSize;
97 } monitor_t;
98
99 typedef struct {
100     DWORD job_id;
101     HANDLE hf;
102 } started_doc_t;
103
104 typedef struct {
105     struct list jobs;
106     LONG ref;
107 } jobqueue_t;
108
109 typedef struct {
110     LPWSTR name;
111     LPWSTR printername;
112     monitor_t *pm;
113     HANDLE hXcv;
114     jobqueue_t *queue;
115     started_doc_t *doc;
116 } opened_printer_t;
117
118 typedef struct {
119     struct list entry;
120     DWORD job_id;
121     WCHAR *filename;
122     WCHAR *document_title;
123 } job_t;
124
125
126 typedef struct {
127     LPCWSTR  envname;
128     LPCWSTR  subdir;
129     DWORD    driverversion;
130     LPCWSTR  versionregpath;
131     LPCWSTR  versionsubdir;
132 } printenv_t;
133
134 /* ############################### */
135
136 static struct list monitor_handles = LIST_INIT( monitor_handles );
137 static monitor_t * pm_localport;
138
139 static opened_printer_t **printer_handles;
140 static UINT nb_printer_handles;
141 static LONG next_job_id = 1;
142
143 static DWORD (WINAPI *GDI_CallDeviceCapabilities16)( LPCSTR lpszDevice, LPCSTR lpszPort,
144                                                      WORD fwCapability, LPSTR lpszOutput,
145                                                      LPDEVMODEA lpdm );
146 static INT (WINAPI *GDI_CallExtDeviceMode16)( HWND hwnd, LPDEVMODEA lpdmOutput,
147                                               LPSTR lpszDevice, LPSTR lpszPort,
148                                               LPDEVMODEA lpdmInput, LPSTR lpszProfile,
149                                               DWORD fwMode );
150
151 static const WCHAR DriversW[] = { 'S','y','s','t','e','m','\\',
152                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
153                                   'c','o','n','t','r','o','l','\\',
154                                   'P','r','i','n','t','\\',
155                                   'E','n','v','i','r','o','n','m','e','n','t','s','\\',
156                                   '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
157
158 static const WCHAR MonitorsW[] =  { 'S','y','s','t','e','m','\\',
159                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
160                                   'C','o','n','t','r','o','l','\\',
161                                   'P','r','i','n','t','\\',
162                                   'M','o','n','i','t','o','r','s','\\',0};
163
164 static const WCHAR PrintersW[] = {'S','y','s','t','e','m','\\',
165                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
166                                   'C','o','n','t','r','o','l','\\',
167                                   'P','r','i','n','t','\\',
168                                   'P','r','i','n','t','e','r','s',0};
169
170 static const WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
171
172 static const WCHAR user_default_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
173                                               'M','i','c','r','o','s','o','f','t','\\',
174                                               'W','i','n','d','o','w','s',' ','N','T','\\',
175                                               'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
176                                               'W','i','n','d','o','w','s',0};
177
178 static const WCHAR user_printers_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
179                                                'M','i','c','r','o','s','o','f','t','\\',
180                                                'W','i','n','d','o','w','s',' ','N','T','\\',
181                                                'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
182                                                'D','e','v','i','c','e','s',0};
183
184 static const WCHAR WinNT_CV_PortsW[] = {'S','o','f','t','w','a','r','e','\\',
185                                         'M','i','c','r','o','s','o','f','t','\\',
186                                         'W','i','n','d','o','w','s',' ','N','T','\\',
187                                         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
188                                         'P','o','r','t','s',0};
189
190 static const WCHAR WinNT_CV_PrinterPortsW[] = { 'S','o','f','t','w','a','r','e','\\',
191                                                 'M','i','c','r','o','s','o','f','t','\\',
192                                                 'W','i','n','d','o','w','s',' ','N','T','\\',
193                                                 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
194                                                 'P','r','i','n','t','e','r','P','o','r','t','s',0};
195
196 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
197 static const WCHAR envname_win40W[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
198 static const WCHAR envname_x64W[] =   {'W','i','n','d','o','w','s',' ','x','6','4',0};
199 static const WCHAR envname_x86W[] =   {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
200 static const WCHAR subdir_win40W[] = {'w','i','n','4','0',0};
201 static const WCHAR subdir_x64W[] =   {'x','6','4',0};
202 static const WCHAR subdir_x86W[] =   {'w','3','2','x','8','6',0};
203 static const WCHAR Version0_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','0',0};
204 static const WCHAR Version0_SubdirW[] = {'\\','0',0};
205 static const WCHAR Version3_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
206 static const WCHAR Version3_SubdirW[] = {'\\','3',0};
207
208 static const WCHAR spooldriversW[] = {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\',0};
209 static const WCHAR spoolprtprocsW[] = {'\\','s','p','o','o','l','\\','p','r','t','p','r','o','c','s','\\',0};
210
211 static const WCHAR backslashW[] = {'\\',0};
212 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
213                                       'i','o','n',' ','F','i','l','e',0};
214 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
215 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
216 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0};
217 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
218 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
219 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
220 static const WCHAR HardwareIDW[] = {'H','a','r','d','w','a','r','e','I','D',0};
221 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
222 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
223 static const WCHAR ManufacturerW[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0};
224 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
225 static const WCHAR MonitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
226 static const WCHAR NameW[] = {'N','a','m','e',0};
227 static const WCHAR ObjectGUIDW[] = {'O','b','j','e','c','t','G','U','I','D',0};
228 static const WCHAR OEM_UrlW[] = {'O','E','M',' ','U','r','l',0};
229 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
230 static const WCHAR PortW[] = {'P','o','r','t',0};
231 static const WCHAR bs_Ports_bsW[] = {'\\','P','o','r','t','s','\\',0};
232 static const WCHAR Previous_NamesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
233 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e','s','s','o','r',0};
234 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i','v','e','r',0};
235 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i','v','e','r','D','a','t','a',0};
236 static const WCHAR PrinterPortsW[] = {'P','r','i','n','t','e','r','P','o','r','t','s',0};
237 static const WCHAR ProviderW[] = {'P','r','o','v','i','d','e','r',0};
238 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F','i','l','e',0};
239 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
240 static const WCHAR VersionW[] = {'V','e','r','s','i','o','n',0};
241 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
242 static const WCHAR deviceW[]  = {'d','e','v','i','c','e',0};
243 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
244 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
245 static const WCHAR emptyStringW[] = {0};
246 static const WCHAR XcvMonitorW[] = {',','X','c','v','M','o','n','i','t','o','r',' ',0};
247 static const WCHAR XcvPortW[] = {',','X','c','v','P','o','r','t',' ',0};
248
249 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
250
251 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
252 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
253 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
254
255 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
256                                           'D','o','c','u','m','e','n','t',0};
257
258 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
259                                      sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
260                                      sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
261                                   0, sizeof(DRIVER_INFO_8W)};
262
263
264 static const DWORD pi_sizeof[] = {0, sizeof(PRINTER_INFO_1W), sizeof(PRINTER_INFO_2W),
265                                      sizeof(PRINTER_INFO_3),  sizeof(PRINTER_INFO_4W),
266                                      sizeof(PRINTER_INFO_5W), sizeof(PRINTER_INFO_6),
267                                      sizeof(PRINTER_INFO_7W), sizeof(PRINTER_INFO_8W),
268                                      sizeof(PRINTER_INFO_9W)};
269
270 /******************************************************************
271  *  validate the user-supplied printing-environment [internal]
272  *
273  * PARAMS
274  *  env  [I] PTR to Environment-String or NULL
275  *
276  * RETURNS
277  *  Failure:  NULL
278  *  Success:  PTR to printenv_t
279  *
280  * NOTES
281  *  An empty string is handled the same way as NULL.
282  *  SetLastEror(ERROR_INVALID_ENVIRONMENT) is called on Failure
283  *  
284  */
285
286 static const  printenv_t * validate_envW(LPCWSTR env)
287 {
288     static const printenv_t env_x64 =   {envname_x64W, subdir_x64W,
289                                          3, Version3_RegPathW, Version3_SubdirW};
290     static const printenv_t env_x86 =   {envname_x86W, subdir_x86W,
291                                          3, Version3_RegPathW, Version3_SubdirW};
292     static const printenv_t env_win40 = {envname_win40W, subdir_win40W,
293                                          0, Version0_RegPathW, Version0_SubdirW};
294
295     static const printenv_t * const all_printenv[]={&env_x86, &env_x64, &env_win40};
296
297     const printenv_t *result = NULL;
298     unsigned int i;
299
300     TRACE("testing %s\n", debugstr_w(env));
301     if (env && env[0])
302     {
303         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
304         {
305             if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
306             {
307                 result = all_printenv[i];
308                 break;
309             }
310         }
311
312         if (result == NULL) {
313             FIXME("unsupported Environment: %s\n", debugstr_w(env));
314             SetLastError(ERROR_INVALID_ENVIRONMENT);
315         }
316         /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
317     }
318     else
319     {
320         result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
321     }
322     TRACE("using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
323
324     return result;
325 }
326
327
328 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
329    if passed a NULL string. This returns NULLs to the result. 
330 */
331 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
332 {
333     if ( (src) )
334     {
335         RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
336         return usBufferPtr->Buffer;
337     }
338     usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
339     return NULL;
340 }
341             
342 static LPWSTR strdupW(LPCWSTR p)
343 {
344     LPWSTR ret;
345     DWORD len;
346
347     if(!p) return NULL;
348     len = (strlenW(p) + 1) * sizeof(WCHAR);
349     ret = HeapAlloc(GetProcessHeap(), 0, len);
350     memcpy(ret, p, len);
351     return ret;
352 }
353
354 static LPSTR strdupWtoA( LPCWSTR str )
355 {
356     LPSTR ret;
357     INT len;
358
359     if (!str) return NULL;
360     len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
361     ret = HeapAlloc( GetProcessHeap(), 0, len );
362     if(ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
363     return ret;
364 }
365
366 /******************************************************************
367  * Return the number of bytes for an multi_sz string.
368  * The result includes all \0s
369  * (specifically the extra \0, that is needed as multi_sz terminator).
370  */
371 #if 0
372 static int multi_sz_lenW(const WCHAR *str)
373 {
374     const WCHAR *ptr = str;
375     if(!str) return 0;
376     do
377     {
378         ptr += lstrlenW(ptr) + 1;
379     } while(*ptr);
380
381     return (ptr - str + 1) * sizeof(WCHAR);
382 }
383 #endif
384 /* ################################ */
385
386 static int multi_sz_lenA(const char *str)
387 {
388     const char *ptr = str;
389     if(!str) return 0;
390     do
391     {
392         ptr += lstrlenA(ptr) + 1;
393     } while(*ptr);
394
395     return ptr - str + 1;
396 }
397
398 static void
399 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name, BOOL force) {
400     char qbuf[200];
401
402     /* If forcing, or no profile string entry for device yet, set the entry
403      *
404      * The always change entry if not WINEPS yet is discussable.
405      */
406     if (force                                                           ||
407         !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf))    ||
408         !strcmp(qbuf,"*")                                               ||
409         !strstr(qbuf,"WINEPS.DRV")
410     ) {
411         char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
412         HKEY hkey;
413
414         sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
415         WriteProfileStringA("windows","device",buf);
416         if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
417             RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
418             RegCloseKey(hkey);
419         }
420         HeapFree(GetProcessHeap(),0,buf);
421     }
422 }
423
424 static BOOL add_printer_driver(const char *name)
425 {
426     DRIVER_INFO_3A di3a;
427
428     static char driver_9x[]         = "wineps16.drv",
429                 driver_nt[]         = "wineps.drv",
430                 env_9x[]            = "Windows 4.0",
431                 env_nt[]            = "Windows NT x86",
432                 data_file[]         = "generic.ppd",
433                 default_data_type[] = "RAW";
434
435     ZeroMemory(&di3a, sizeof(DRIVER_INFO_3A));
436     di3a.cVersion         = 3;
437     di3a.pName            = (char *)name;
438     di3a.pEnvironment     = env_nt;
439     di3a.pDriverPath      = driver_nt;
440     di3a.pDataFile        = data_file;
441     di3a.pConfigFile      = driver_nt;
442     di3a.pDefaultDataType = default_data_type;
443
444     if (AddPrinterDriverA(NULL, 3, (LPBYTE)&di3a) ||
445         (GetLastError() ==  ERROR_PRINTER_DRIVER_ALREADY_INSTALLED ))
446     {
447         di3a.cVersion     = 0;
448         di3a.pEnvironment = env_9x;
449         di3a.pDriverPath  = driver_9x;
450         di3a.pConfigFile  = driver_9x;
451         if (AddPrinterDriverA(NULL, 3, (LPBYTE)&di3a) ||
452             (GetLastError() ==  ERROR_PRINTER_DRIVER_ALREADY_INSTALLED ))
453         {
454             return TRUE;
455         }
456     }
457     ERR("Failed adding driver %s (%s): %u\n", debugstr_a(di3a.pDriverPath),
458         debugstr_a(di3a.pEnvironment), GetLastError());
459     return FALSE;
460 }
461
462 #ifdef SONAME_LIBCUPS
463 static typeof(cupsGetDests)  *pcupsGetDests;
464 static typeof(cupsGetPPD)    *pcupsGetPPD;
465 static typeof(cupsPrintFile) *pcupsPrintFile;
466 static void *cupshandle;
467
468 static BOOL CUPS_LoadPrinters(void)
469 {
470     int                   i, nrofdests;
471     BOOL                  hadprinter = FALSE, haddefault = FALSE;
472     cups_dest_t          *dests;
473     PRINTER_INFO_2A       pinfo2a;
474     char   *port,*devline;
475     HKEY hkeyPrinter, hkeyPrinters, hkey;
476     char    loaderror[256];
477
478     cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, loaderror, sizeof(loaderror));
479     if (!cupshandle) {
480         TRACE("%s\n", loaderror);
481         return FALSE;
482     }
483     TRACE("%p: %s loaded\n", cupshandle, SONAME_LIBCUPS);
484
485 #define DYNCUPS(x)                                      \
486         p##x = wine_dlsym(cupshandle, #x, NULL,0);      \
487         if (!p##x) return FALSE;
488
489     DYNCUPS(cupsGetPPD);
490     DYNCUPS(cupsGetDests);
491     DYNCUPS(cupsPrintFile);
492 #undef DYNCUPS
493
494     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
495        ERROR_SUCCESS) {
496         ERR("Can't create Printers key\n");
497         return FALSE;
498     }
499
500     nrofdests = pcupsGetDests(&dests);
501     TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
502     for (i=0;i<nrofdests;i++) {
503         /* FIXME: replace "LPR:" with "CUPS:". Fix printing output first */
504         port = HeapAlloc(GetProcessHeap(), 0, strlen("LPR:") + strlen(dests[i].name)+1);
505         sprintf(port,"LPR:%s", dests[i].name);
506         /* FIXME: remove extension. Fix gdi32/drivers and comdlg32/printdlg first */
507         devline = HeapAlloc(GetProcessHeap(), 0, sizeof("WINEPS.DRV,,15,45") + strlen(port));
508         sprintf(devline, "WINEPS.DRV,%s", port);
509         WriteProfileStringA("devices", dests[i].name, devline);
510         if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
511             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
512             RegCloseKey(hkey);
513         }
514
515         lstrcatA(devline, ",15,45");
516         WriteProfileStringA("PrinterPorts", dests[i].name, devline);
517         if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
518             RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
519             RegCloseKey(hkey);
520         }
521
522         HeapFree(GetProcessHeap(), 0, devline);
523
524         TRACE("Printer %d: %s\n", i, dests[i].name);
525         if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
526             /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
527                and continue */
528             TRACE("Printer already exists\n");
529             RegDeleteValueW(hkeyPrinter, May_Delete_Value);
530             RegCloseKey(hkeyPrinter);
531         } else {
532             static CHAR data_type[] = "RAW",
533                     print_proc[]    = "WinPrint",
534                     comment[]       = "WINEPS Printer using CUPS",
535                     location[]      = "<physical location of printer>",
536                     params[]        = "<parameters?>",
537                     share_name[]    = "<share name?>",
538                     sep_file[]      = "<sep file?>";
539
540             add_printer_driver(dests[i].name);
541
542             memset(&pinfo2a,0,sizeof(pinfo2a));
543             pinfo2a.pPrinterName    = dests[i].name;
544             pinfo2a.pDatatype       = data_type;
545             pinfo2a.pPrintProcessor = print_proc;
546             pinfo2a.pDriverName     = dests[i].name;
547             pinfo2a.pComment        = comment;
548             pinfo2a.pLocation       = location;
549             pinfo2a.pPortName       = port;
550             pinfo2a.pParameters     = params;
551             pinfo2a.pShareName      = share_name;
552             pinfo2a.pSepFile        = sep_file;
553
554             if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
555                 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
556                     ERR("printer '%s' not added by AddPrinterA (error %d)\n",dests[i].name,GetLastError());
557             }
558         }
559         HeapFree(GetProcessHeap(),0,port);
560
561         hadprinter = TRUE;
562         if (dests[i].is_default) {
563             WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
564             haddefault = TRUE;
565         }
566     }
567     if (hadprinter & !haddefault)
568         WINSPOOL_SetDefaultPrinter(dests[0].name, dests[0].name, TRUE);
569     RegCloseKey(hkeyPrinters);
570     return hadprinter;
571 }
572 #endif
573
574 static BOOL
575 PRINTCAP_ParseEntry(const char *pent, BOOL isfirst) {
576     PRINTER_INFO_2A     pinfo2a;
577     char                *e,*s,*name,*prettyname,*devname;
578     BOOL                ret = FALSE, set_default = FALSE;
579     char                *port = NULL, *devline,*env_default;
580     HKEY                hkeyPrinter, hkeyPrinters, hkey;
581
582     while (isspace(*pent)) pent++;
583     s = strchr(pent,':');
584     if(s) *s='\0';
585     name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
586     strcpy(name,pent);
587     if(s) {
588         *s=':';
589         pent = s;
590     } else
591         pent = "";
592
593     TRACE("name=%s entry=%s\n",name, pent);
594
595     if(ispunct(*name)) { /* a tc entry, not a real printer */
596         TRACE("skipping tc entry\n");
597         goto end;
598     }
599
600     if(strstr(pent,":server")) { /* server only version so skip */
601         TRACE("skipping server entry\n");
602         goto end;
603     }
604
605     /* Determine whether this is a postscript printer. */
606
607     ret = TRUE;
608     env_default = getenv("PRINTER");
609     prettyname = name;
610     /* Get longest name, usually the one at the right for later display. */
611     while((s=strchr(prettyname,'|'))) {
612         *s = '\0';
613         e = s;
614         while(isspace(*--e)) *e = '\0';
615         TRACE("\t%s\n", debugstr_a(prettyname));
616         if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
617         for(prettyname = s+1; isspace(*prettyname); prettyname++)
618             ;
619     }
620     e = prettyname + strlen(prettyname);
621     while(isspace(*--e)) *e = '\0';
622     TRACE("\t%s\n", debugstr_a(prettyname));
623     if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
624
625     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
626      * if it is too long, we use it as comment below. */
627     devname = prettyname;
628     if (strlen(devname)>=CCHDEVICENAME-1)
629          devname = name;
630     if (strlen(devname)>=CCHDEVICENAME-1) {
631         ret = FALSE;
632         goto end;
633     }
634
635     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
636     sprintf(port,"LPR:%s",name);
637
638     /* FIXME: remove extension. Fix gdi32/drivers and comdlg32/printdlg first */
639     devline = HeapAlloc(GetProcessHeap(), 0, sizeof("WINEPS.DRV,,15,45") + strlen(port));
640     sprintf(devline, "WINEPS.DRV,%s", port);
641     WriteProfileStringA("devices", devname, devline);
642     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
643         RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
644         RegCloseKey(hkey);
645     }
646
647     lstrcatA(devline, ",15,45");
648     WriteProfileStringA("PrinterPorts", devname, devline);
649     if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
650         RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
651         RegCloseKey(hkey);
652     }
653
654     HeapFree(GetProcessHeap(),0,devline);
655     
656     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
657        ERROR_SUCCESS) {
658         ERR("Can't create Printers key\n");
659         ret = FALSE;
660         goto end;
661     }
662     if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
663         /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
664            and continue */
665         TRACE("Printer already exists\n");
666         RegDeleteValueW(hkeyPrinter, May_Delete_Value);
667         RegCloseKey(hkeyPrinter);
668     } else {
669         static CHAR data_type[]   = "RAW",
670                     print_proc[]  = "WinPrint",
671                     comment[]     = "WINEPS Printer using LPR",
672                     params[]      = "<parameters?>",
673                     share_name[]  = "<share name?>",
674                     sep_file[]    = "<sep file?>";
675
676         add_printer_driver(devname);
677
678         memset(&pinfo2a,0,sizeof(pinfo2a));
679         pinfo2a.pPrinterName    = devname;
680         pinfo2a.pDatatype       = data_type;
681         pinfo2a.pPrintProcessor = print_proc;
682         pinfo2a.pDriverName     = devname;
683         pinfo2a.pComment        = comment;
684         pinfo2a.pLocation       = prettyname;
685         pinfo2a.pPortName       = port;
686         pinfo2a.pParameters     = params;
687         pinfo2a.pShareName      = share_name;
688         pinfo2a.pSepFile        = sep_file;
689
690         if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
691             if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
692                 ERR("%s not added by AddPrinterA (%d)\n",name,GetLastError());
693         }
694     }
695     RegCloseKey(hkeyPrinters);
696
697     if (isfirst || set_default)
698         WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
699
700  end:
701     HeapFree(GetProcessHeap(), 0, port);
702     HeapFree(GetProcessHeap(), 0, name);
703     return ret;
704 }
705
706 static BOOL
707 PRINTCAP_LoadPrinters(void) {
708     BOOL                hadprinter = FALSE;
709     char                buf[200];
710     FILE                *f;
711     char *pent = NULL;
712     BOOL had_bash = FALSE;
713
714     f = fopen("/etc/printcap","r");
715     if (!f)
716         return FALSE;
717
718     while(fgets(buf,sizeof(buf),f)) {
719         char *start, *end;
720
721         end=strchr(buf,'\n');
722         if (end) *end='\0';
723     
724         start = buf;
725         while(isspace(*start)) start++;
726         if(*start == '#' || *start == '\0')
727             continue;
728
729         if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
730             hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
731             HeapFree(GetProcessHeap(),0,pent);
732             pent = NULL;
733         }
734
735         if (end && *--end == '\\') {
736             *end = '\0';
737             had_bash = TRUE;
738         } else
739             had_bash = FALSE;
740
741         if (pent) {
742             pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
743             strcat(pent,start);
744         } else {
745             pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
746             strcpy(pent,start);
747         }
748
749     }
750     if(pent) {
751         hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
752         HeapFree(GetProcessHeap(),0,pent);
753     }
754     fclose(f);
755     return hadprinter;
756 }
757
758 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
759 {
760     if (value)
761         return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
762                               (lstrlenW(value) + 1) * sizeof(WCHAR));
763     else
764         return ERROR_FILE_NOT_FOUND;
765 }
766
767 /******************************************************************
768  * monitor_unload [internal]
769  *
770  * release a printmonitor and unload it from memory, when needed
771  *
772  */
773 static void monitor_unload(monitor_t * pm)
774 {
775     if (pm == NULL) return;
776     TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
777
778     EnterCriticalSection(&monitor_handles_cs);
779
780     if (pm->refcount) pm->refcount--;
781
782     if (pm->refcount == 0) {
783         list_remove(&pm->entry);
784         FreeLibrary(pm->hdll);
785         HeapFree(GetProcessHeap(), 0, pm->name);
786         HeapFree(GetProcessHeap(), 0, pm->dllname);
787         HeapFree(GetProcessHeap(), 0, pm);
788     }
789     LeaveCriticalSection(&monitor_handles_cs);
790 }
791
792 /******************************************************************
793  * monitor_load [internal]
794  *
795  * load a printmonitor, get the dllname from the registry, when needed 
796  * initialize the monitor and dump found function-pointers
797  *
798  * On failure, SetLastError() is called and NULL is returned
799  */
800
801 static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname)
802 {
803     LPMONITOR2  (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
804     PMONITORUI  (WINAPI *pInitializePrintMonitorUI)(VOID);
805     LPMONITOREX (WINAPI *pInitializePrintMonitor)  (LPWSTR);
806     DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
807     DWORD (WINAPI *pInitializeMonitor)  (LPWSTR);
808
809     monitor_t * pm = NULL;
810     monitor_t * cursor;
811     LPWSTR  regroot = NULL;
812     LPWSTR  driver = dllname;
813
814     TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
815     /* Is the Monitor already loaded? */
816     EnterCriticalSection(&monitor_handles_cs);
817
818     if (name) {
819         LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
820         {
821             if (cursor->name && (lstrcmpW(name, cursor->name) == 0)) {
822                 pm = cursor;
823                 break;
824             }
825         }
826     }
827
828     if (pm == NULL) {
829         pm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(monitor_t));
830         if (pm == NULL) goto cleanup;
831         list_add_tail(&monitor_handles, &pm->entry);
832     }
833     pm->refcount++;
834
835     if (pm->name == NULL) {
836         /* Load the monitor */
837         LPMONITOREX pmonitorEx;
838         DWORD   len;
839
840         if (name) {
841             len = lstrlenW(MonitorsW) + lstrlenW(name) + 2;
842             regroot = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
843         }
844
845         if (regroot) {
846             lstrcpyW(regroot, MonitorsW);
847             lstrcatW(regroot, name);
848             /* Get the Driver from the Registry */
849             if (driver == NULL) {
850                 HKEY    hroot;
851                 DWORD   namesize;
852                 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
853                     if (RegQueryValueExW(hroot, DriverW, NULL, NULL, NULL,
854                                         &namesize) == ERROR_SUCCESS) {
855                         driver = HeapAlloc(GetProcessHeap(), 0, namesize);
856                         RegQueryValueExW(hroot, DriverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
857                     }
858                     RegCloseKey(hroot);
859                 }
860             }
861         }
862
863         pm->name = strdupW(name);
864         pm->dllname = strdupW(driver);
865
866         if ((name && (!regroot || !pm->name)) || !pm->dllname) {
867             monitor_unload(pm);
868             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
869             pm = NULL;
870             goto cleanup;
871         }
872
873         pm->hdll = LoadLibraryW(driver);
874         TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
875
876         if (pm->hdll == NULL) {
877             monitor_unload(pm);
878             SetLastError(ERROR_MOD_NOT_FOUND);
879             pm = NULL;
880             goto cleanup;
881         }
882
883         pInitializePrintMonitor2  = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
884         pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
885         pInitializePrintMonitor   = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
886         pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
887         pInitializeMonitor   = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
888
889
890         TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
891         TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
892         TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
893         TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
894         TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
895
896         if (pInitializePrintMonitorUI  != NULL) {
897             pm->monitorUI = pInitializePrintMonitorUI();
898             TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver)); 
899             if (pm->monitorUI) {
900                 TRACE(  "0x%08x: dwMonitorSize (%d)\n",
901                         pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize );
902
903             }
904         }
905
906         if (pInitializePrintMonitor && regroot) {
907             pmonitorEx = pInitializePrintMonitor(regroot);
908             TRACE(  "%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
909                     pmonitorEx, debugstr_w(driver), debugstr_w(regroot));
910
911             if (pmonitorEx) {
912                 pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
913                 pm->monitor = &(pmonitorEx->Monitor);
914             }
915         }
916
917         if (pm->monitor) {
918             TRACE(  "0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize );
919
920         }
921
922         if (!pm->monitor && regroot) {
923             if (pInitializePrintMonitor2 != NULL) {
924                 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
925             }
926             if (pInitializeMonitorEx != NULL) {
927                 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
928             }
929             if (pInitializeMonitor != NULL) {
930                 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
931             }
932         }
933         if (!pm->monitor && !pm->monitorUI) {
934             monitor_unload(pm);
935             SetLastError(ERROR_PROC_NOT_FOUND);
936             pm = NULL;
937         }
938     }
939 cleanup:
940     if ((pm_localport ==  NULL) && (pm != NULL) && (lstrcmpW(pm->name, LocalPortW) == 0)) {
941         pm->refcount++;
942         pm_localport = pm;
943     }
944     LeaveCriticalSection(&monitor_handles_cs);
945     if (driver != dllname) HeapFree(GetProcessHeap(), 0, driver);
946     HeapFree(GetProcessHeap(), 0, regroot);
947     TRACE("=> %p\n", pm);
948     return pm;
949 }
950
951 /******************************************************************
952  * monitor_loadui [internal]
953  *
954  * load the userinterface-dll for a given portmonitor
955  *
956  * On failure, NULL is returned
957  */
958
959 static monitor_t * monitor_loadui(monitor_t * pm)
960 {
961     monitor_t * pui = NULL;
962     LPWSTR  buffer[MAX_PATH];
963     HANDLE  hXcv;
964     DWORD   len;
965     DWORD   res;
966
967     if (pm == NULL) return NULL;
968     TRACE("(%p) => dllname: %s\n", pm, debugstr_w(pm->dllname));
969
970     /* Try the Portmonitor first; works for many monitors */
971     if (pm->monitorUI) {
972         EnterCriticalSection(&monitor_handles_cs);
973         pm->refcount++;
974         LeaveCriticalSection(&monitor_handles_cs);
975         return pm;
976     }
977
978     /* query the userinterface-dllname from the Portmonitor */
979     if ((pm->monitor) && (pm->monitor->pfnXcvDataPort)) {
980         /* building (",XcvMonitor %s",pm->name) not needed yet */
981         res = pm->monitor->pfnXcvOpenPort(emptyStringW, SERVER_ACCESS_ADMINISTER, &hXcv);
982         TRACE("got %u with %p\n", res, hXcv);
983         if (res) {
984             res = pm->monitor->pfnXcvDataPort(hXcv, MonitorUIW, NULL, 0, (BYTE *) buffer, sizeof(buffer), &len);
985             TRACE("got %u with %s\n", res, debugstr_w((LPWSTR) buffer));
986             if (res == ERROR_SUCCESS) pui = monitor_load(NULL, (LPWSTR) buffer);
987             pm->monitor->pfnXcvClosePort(hXcv);
988         }
989     }
990     return pui;
991 }
992
993
994 /******************************************************************
995  * monitor_load_by_port [internal]
996  *
997  * load a printmonitor for a given port
998  *
999  * On failure, NULL is returned
1000  */
1001
1002 static monitor_t * monitor_load_by_port(LPCWSTR portname)
1003 {
1004     HKEY    hroot;
1005     HKEY    hport;
1006     LPWSTR  buffer;
1007     monitor_t * pm = NULL;
1008     DWORD   registered = 0;
1009     DWORD   id = 0;
1010     DWORD   len;
1011
1012     TRACE("(%s)\n", debugstr_w(portname));
1013
1014     /* Try the Local Monitor first */
1015     if (RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot) == ERROR_SUCCESS) {
1016         if (RegQueryValueExW(hroot, portname, NULL, NULL, NULL, &len) == ERROR_SUCCESS) {
1017             /* found the portname */
1018             RegCloseKey(hroot);
1019             return monitor_load(LocalPortW, NULL);
1020         }
1021         RegCloseKey(hroot);
1022     }
1023
1024     len = MAX_PATH + lstrlenW(bs_Ports_bsW) + lstrlenW(portname) + 1;
1025     buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1026     if (buffer == NULL) return NULL;
1027
1028     if (RegOpenKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) == ERROR_SUCCESS) {
1029         EnterCriticalSection(&monitor_handles_cs);
1030         RegQueryInfoKeyW(hroot, NULL, NULL, NULL, &registered, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1031
1032         while ((pm == NULL) && (id < registered)) {
1033             buffer[0] = '\0';
1034             RegEnumKeyW(hroot, id, buffer, MAX_PATH);
1035             TRACE("testing %s\n", debugstr_w(buffer));
1036             len = lstrlenW(buffer);
1037             lstrcatW(buffer, bs_Ports_bsW);
1038             lstrcatW(buffer, portname);
1039             if (RegOpenKeyW(hroot, buffer, &hport) == ERROR_SUCCESS) {
1040                 RegCloseKey(hport);
1041                 buffer[len] = '\0';             /* use only the Monitor-Name */
1042                 pm = monitor_load(buffer, NULL);
1043             }
1044             id++;
1045         }
1046         LeaveCriticalSection(&monitor_handles_cs);
1047         RegCloseKey(hroot);
1048     }
1049     HeapFree(GetProcessHeap(), 0, buffer);
1050     return pm;
1051 }
1052
1053 /******************************************************************
1054  * get_servername_from_name  (internal)
1055  *
1056  * for an external server, a copy of the serverpart from the full name is returned
1057  *
1058  */
1059 static LPWSTR get_servername_from_name(LPCWSTR name)
1060 {
1061     LPWSTR  server;
1062     LPWSTR  ptr;
1063     WCHAR   buffer[MAX_PATH];
1064     DWORD   len;
1065
1066     if (name == NULL) return NULL;
1067     if ((name[0] != '\\') || (name[1] != '\\')) return NULL;
1068
1069     server = strdupW(&name[2]);     /* skip over both backslash */
1070     if (server == NULL) return NULL;
1071
1072     /* strip '\' and the printername */
1073     ptr = strchrW(server, '\\');
1074     if (ptr) ptr[0] = '\0';
1075
1076     TRACE("found %s\n", debugstr_w(server));
1077
1078     len = sizeof(buffer)/sizeof(buffer[0]);
1079     if (GetComputerNameW(buffer, &len)) {
1080         if (lstrcmpW(buffer, server) == 0) {
1081             /* The requested Servername is our computername */
1082             HeapFree(GetProcessHeap(), 0, server);
1083             return NULL;
1084         }
1085     }
1086     return server;
1087 }
1088
1089 /******************************************************************
1090  * get_basename_from_name  (internal)
1091  *
1092  * skip over the serverpart from the full name
1093  *
1094  */
1095 static LPCWSTR get_basename_from_name(LPCWSTR name)
1096 {
1097     if (name == NULL)  return NULL;
1098     if ((name[0] == '\\') && (name[1] == '\\')) {
1099         /* skip over the servername and search for the following '\'  */
1100         name = strchrW(&name[2], '\\');
1101         if ((name) && (name[1])) {
1102             /* found a separator ('\') followed by a name:
1103                skip over the separator and return the rest */
1104             name++;
1105         }
1106         else
1107         {
1108             /* no basename present (we found only a servername) */
1109             return NULL;
1110         }
1111     }
1112     return name;
1113 }
1114
1115 /******************************************************************
1116  *  get_opened_printer_entry
1117  *  Get the first place empty in the opened printer table
1118  *
1119  * ToDo:
1120  *  - pDefault is ignored
1121  */
1122 static HANDLE get_opened_printer_entry(LPCWSTR name, LPPRINTER_DEFAULTSW pDefault)
1123 {
1124     UINT_PTR handle = nb_printer_handles, i;
1125     jobqueue_t *queue = NULL;
1126     opened_printer_t *printer = NULL;
1127     LPWSTR  servername;
1128     LPCWSTR printername;
1129     HKEY    hkeyPrinters;
1130     HKEY    hkeyPrinter;
1131     DWORD   len;
1132
1133     servername = get_servername_from_name(name);
1134     if (servername) {
1135         FIXME("server %s not supported\n", debugstr_w(servername));
1136         HeapFree(GetProcessHeap(), 0, servername);
1137         SetLastError(ERROR_INVALID_PRINTER_NAME);
1138         return NULL;
1139     }
1140
1141     printername = get_basename_from_name(name);
1142     if (name != printername) TRACE("converted %s to %s\n", debugstr_w(name), debugstr_w(printername));
1143
1144     /* an empty printername is invalid */
1145     if (printername && (!printername[0])) {
1146         SetLastError(ERROR_INVALID_PARAMETER);
1147         return NULL;
1148     }
1149
1150     EnterCriticalSection(&printer_handles_cs);
1151
1152     for (i = 0; i < nb_printer_handles; i++)
1153     {
1154         if (!printer_handles[i])
1155         {
1156             if(handle == nb_printer_handles)
1157                 handle = i;
1158         }
1159         else
1160         {
1161             if(!queue && (name) && !lstrcmpW(name, printer_handles[i]->name))
1162                 queue = printer_handles[i]->queue;
1163         }
1164     }
1165
1166     if (handle >= nb_printer_handles)
1167     {
1168         opened_printer_t **new_array;
1169         if (printer_handles)
1170             new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
1171                                      (nb_printer_handles + 16) * sizeof(*new_array) );
1172         else
1173             new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1174                                    (nb_printer_handles + 16) * sizeof(*new_array) );
1175
1176         if (!new_array)
1177         {
1178             handle = 0;
1179             goto end;
1180         }
1181         printer_handles = new_array;
1182         nb_printer_handles += 16;
1183     }
1184
1185     if (!(printer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*printer))))
1186     {
1187         handle = 0;
1188         goto end;
1189     }
1190
1191
1192     /* clone the base name. This is NULL for the printserver */
1193     printer->printername = strdupW(printername);
1194
1195     /* clone the full name */
1196     printer->name = strdupW(name);
1197     if (name && (!printer->name)) {
1198         handle = 0;
1199         goto end;
1200     }
1201
1202     if (printername) {
1203         len = sizeof(XcvMonitorW)/sizeof(WCHAR) - 1;
1204         if (strncmpW(printername, XcvMonitorW, len) == 0) {
1205             /* OpenPrinter(",XcvMonitor " detected */
1206             TRACE(",XcvMonitor: %s\n", debugstr_w(&printername[len]));
1207             printer->pm = monitor_load(&printername[len], NULL);
1208             if (printer->pm == NULL) {
1209                 SetLastError(ERROR_UNKNOWN_PORT);
1210                 handle = 0;
1211                 goto end;
1212             }
1213         }
1214         else
1215         {
1216             len = sizeof(XcvPortW)/sizeof(WCHAR) - 1;
1217             if (strncmpW( printername, XcvPortW, len) == 0) {
1218                 /* OpenPrinter(",XcvPort " detected */
1219                 TRACE(",XcvPort: %s\n", debugstr_w(&printername[len]));
1220                 printer->pm = monitor_load_by_port(&printername[len]);
1221                 if (printer->pm == NULL) {
1222                     SetLastError(ERROR_UNKNOWN_PORT);
1223                     handle = 0;
1224                     goto end;
1225                 }
1226             }
1227         }
1228
1229         if (printer->pm) {
1230             if ((printer->pm->monitor) && (printer->pm->monitor->pfnXcvOpenPort)) {
1231                 printer->pm->monitor->pfnXcvOpenPort(&printername[len],
1232                                                     pDefault ? pDefault->DesiredAccess : 0,
1233                                                     &printer->hXcv);
1234             }
1235             if (printer->hXcv == NULL) {
1236                 SetLastError(ERROR_INVALID_PARAMETER);
1237                 handle = 0;
1238                 goto end;
1239             }
1240         }
1241         else
1242         {
1243             /* Does the Printer exist? */
1244             if (RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) != ERROR_SUCCESS) {
1245                 ERR("Can't create Printers key\n");
1246                 handle = 0;
1247                 goto end;
1248             }
1249             if (RegOpenKeyW(hkeyPrinters, printername, &hkeyPrinter) != ERROR_SUCCESS) {
1250                 WARN("Printer not found in Registry: %s\n", debugstr_w(printername));
1251                 RegCloseKey(hkeyPrinters);
1252                 SetLastError(ERROR_INVALID_PRINTER_NAME);
1253                 handle = 0;
1254                 goto end;
1255             }
1256             RegCloseKey(hkeyPrinter);
1257             RegCloseKey(hkeyPrinters);
1258         }
1259     }
1260     else
1261     {
1262         TRACE("using the local printserver\n");
1263     }
1264
1265     if(queue)
1266         printer->queue = queue;
1267     else
1268     {
1269         printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
1270         if (!printer->queue) {
1271             handle = 0;
1272             goto end;
1273         }
1274         list_init(&printer->queue->jobs);
1275         printer->queue->ref = 0;
1276     }
1277     InterlockedIncrement(&printer->queue->ref);
1278
1279     printer_handles[handle] = printer;
1280     handle++;
1281 end:
1282     LeaveCriticalSection(&printer_handles_cs);
1283     if (!handle && printer) {
1284         /* Something failed: Free all resources */
1285         if (printer->hXcv) printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
1286         monitor_unload(printer->pm);
1287         HeapFree(GetProcessHeap(), 0, printer->printername);
1288         HeapFree(GetProcessHeap(), 0, printer->name);
1289         if (!queue) HeapFree(GetProcessHeap(), 0, printer->queue);
1290         HeapFree(GetProcessHeap(), 0, printer);
1291     }
1292
1293     return (HANDLE)handle;
1294 }
1295
1296 /******************************************************************
1297  *  get_opened_printer
1298  *  Get the pointer to the opened printer referred by the handle
1299  */
1300 static opened_printer_t *get_opened_printer(HANDLE hprn)
1301 {
1302     UINT_PTR idx = (UINT_PTR)hprn;
1303     opened_printer_t *ret = NULL;
1304
1305     EnterCriticalSection(&printer_handles_cs);
1306
1307     if ((idx > 0) && (idx <= nb_printer_handles)) {
1308         ret = printer_handles[idx - 1];
1309     }
1310     LeaveCriticalSection(&printer_handles_cs);
1311     return ret;
1312 }
1313
1314 /******************************************************************
1315  *  get_opened_printer_name
1316  *  Get the pointer to the opened printer name referred by the handle
1317  */
1318 static LPCWSTR get_opened_printer_name(HANDLE hprn)
1319 {
1320     opened_printer_t *printer = get_opened_printer(hprn);
1321     if(!printer) return NULL;
1322     return printer->name;
1323 }
1324
1325 /******************************************************************
1326  *  WINSPOOL_GetOpenedPrinterRegKey
1327  *
1328  */
1329 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
1330 {
1331     LPCWSTR name = get_opened_printer_name(hPrinter);
1332     DWORD ret;
1333     HKEY hkeyPrinters;
1334
1335     if(!name) return ERROR_INVALID_HANDLE;
1336
1337     if((ret = RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters)) !=
1338        ERROR_SUCCESS)
1339         return ret;
1340
1341     if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
1342     {
1343         ERR("Can't find opened printer %s in registry\n",
1344             debugstr_w(name));
1345         RegCloseKey(hkeyPrinters);
1346         return ERROR_INVALID_PRINTER_NAME; /* ? */
1347     }
1348     RegCloseKey(hkeyPrinters);
1349     return ERROR_SUCCESS;
1350 }
1351
1352 void WINSPOOL_LoadSystemPrinters(void)
1353 {
1354     HKEY                hkey, hkeyPrinters;
1355     HANDLE              hprn;
1356     DWORD               needed, num, i;
1357     WCHAR               PrinterName[256];
1358     BOOL                done = FALSE;
1359
1360     /* This ensures that all printer entries have a valid Name value.  If causes
1361        problems later if they don't.  If one is found to be missed we create one
1362        and set it equal to the name of the key */
1363     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
1364         if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
1365                             NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
1366             for(i = 0; i < num; i++) {
1367                 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) == ERROR_SUCCESS) {
1368                     if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
1369                         if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
1370                             set_reg_szW(hkey, NameW, PrinterName);
1371                         }
1372                         RegCloseKey(hkey);
1373                     }
1374                 }
1375             }
1376         }
1377         RegCloseKey(hkeyPrinters);
1378     }
1379
1380     /* We want to avoid calling AddPrinter on printers as much as
1381        possible, because on cups printers this will (eventually) lead
1382        to a call to cupsGetPPD which takes forever, even with non-cups
1383        printers AddPrinter takes a while.  So we'll tag all printers that
1384        were automatically added last time around, if they still exist
1385        we'll leave them be otherwise we'll delete them. */
1386     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1387     if(needed) {
1388         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1389         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1390             for(i = 0; i < num; i++) {
1391                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1392                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1393                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1394                             DWORD dw = 1;
1395                             RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
1396                             RegCloseKey(hkey);
1397                         }
1398                         ClosePrinter(hprn);
1399                     }
1400                 }
1401             }
1402         }
1403         HeapFree(GetProcessHeap(), 0, pi);
1404     }
1405
1406
1407 #ifdef SONAME_LIBCUPS
1408     done = CUPS_LoadPrinters();
1409 #endif
1410
1411     if(!done) /* If we have any CUPS based printers, skip looking for printcap printers */
1412         PRINTCAP_LoadPrinters();
1413
1414     /* Now enumerate the list again and delete any printers that are still tagged */
1415     EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1416     if(needed) {
1417         PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1418         if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1419             for(i = 0; i < num; i++) {
1420                 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1421                     if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1422                         BOOL delete_driver = FALSE;
1423                         if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1424                             DWORD dw, type, size = sizeof(dw);
1425                             if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
1426                                 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
1427                                 DeletePrinter(hprn);
1428                                 delete_driver = TRUE;
1429                             }
1430                             RegCloseKey(hkey);
1431                         }
1432                         ClosePrinter(hprn);
1433                         if(delete_driver)
1434                             DeletePrinterDriverExA(NULL, NULL, pi[i].pPrinterName, 0, 0);
1435                     }
1436                 }
1437             }
1438         }
1439         HeapFree(GetProcessHeap(), 0, pi);
1440     }
1441
1442     return;
1443
1444 }
1445
1446 /******************************************************************
1447  *                  get_job
1448  *
1449  *  Get the pointer to the specified job.
1450  *  Should hold the printer_handles_cs before calling.
1451  */
1452 static job_t *get_job(HANDLE hprn, DWORD JobId)
1453 {
1454     opened_printer_t *printer = get_opened_printer(hprn);
1455     job_t *job;
1456
1457     if(!printer) return NULL;
1458     LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
1459     {
1460         if(job->job_id == JobId)
1461             return job;
1462     }
1463     return NULL;
1464 }
1465
1466 /***********************************************************
1467  *      DEVMODEcpyAtoW
1468  */
1469 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
1470 {
1471     BOOL Formname;
1472     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
1473     DWORD size;
1474
1475     Formname = (dmA->dmSize > off_formname);
1476     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
1477     MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
1478                         dmW->dmDeviceName, CCHDEVICENAME);
1479     if(!Formname) {
1480       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1481              dmA->dmSize - CCHDEVICENAME);
1482     } else {
1483       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1484              off_formname - CCHDEVICENAME);
1485       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
1486                           dmW->dmFormName, CCHFORMNAME);
1487       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
1488              (off_formname + CCHFORMNAME));
1489     }
1490     dmW->dmSize = size;
1491     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
1492            dmA->dmDriverExtra);
1493     return dmW;
1494 }
1495
1496 /***********************************************************
1497  * DEVMODEdupWtoA
1498  * Creates an ansi copy of supplied devmode
1499  */
1500 static LPDEVMODEA DEVMODEdupWtoA(const DEVMODEW *dmW)
1501 {
1502     LPDEVMODEA dmA;
1503     DWORD size;
1504
1505     if (!dmW) return NULL;
1506     size = dmW->dmSize - CCHDEVICENAME -
1507                         ((dmW->dmSize > FIELD_OFFSET(DEVMODEW, dmFormName)) ? CCHFORMNAME : 0);
1508
1509     dmA = HeapAlloc(GetProcessHeap(), 0, size + dmW->dmDriverExtra);
1510     if (!dmA) return NULL;
1511
1512     WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
1513                         (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
1514
1515     if (FIELD_OFFSET(DEVMODEW, dmFormName) >= dmW->dmSize) {
1516         memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1517                dmW->dmSize - FIELD_OFFSET(DEVMODEW, dmSpecVersion));
1518     }
1519     else
1520     {
1521         memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1522                FIELD_OFFSET(DEVMODEW, dmFormName) - FIELD_OFFSET(DEVMODEW, dmSpecVersion));
1523         WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
1524                             (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
1525
1526         memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize - FIELD_OFFSET(DEVMODEW, dmLogPixels));
1527     }
1528
1529     dmA->dmSize = size;
1530     memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize, dmW->dmDriverExtra);
1531     return dmA;
1532 }
1533
1534 /******************************************************************
1535  * convert_printerinfo_W_to_A [internal]
1536  *
1537  */
1538 static void convert_printerinfo_W_to_A(LPBYTE out, LPBYTE pPrintersW,
1539                                        DWORD level, DWORD outlen, DWORD numentries)
1540 {
1541     DWORD id = 0;
1542     LPSTR ptr;
1543     INT len;
1544
1545     TRACE("(%p, %p, %d, %u, %u)\n", out, pPrintersW, level, outlen, numentries);
1546
1547     len = pi_sizeof[level] * numentries;
1548     ptr = (LPSTR) out + len;
1549     outlen -= len;
1550
1551     /* copy the numbers of all PRINTER_INFO_* first */
1552     memcpy(out, pPrintersW, len);
1553
1554     while (id < numentries) {
1555         switch (level) {
1556             case 1:
1557                 {
1558                     PRINTER_INFO_1W * piW = (PRINTER_INFO_1W *) pPrintersW;
1559                     PRINTER_INFO_1A * piA = (PRINTER_INFO_1A *) out;
1560
1561                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pName));
1562                     if (piW->pDescription) {
1563                         piA->pDescription = ptr;
1564                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDescription, -1,
1565                                                   ptr, outlen, NULL, NULL);
1566                         ptr += len;
1567                         outlen -= len;
1568                     }
1569                     if (piW->pName) {
1570                         piA->pName = ptr;
1571                         len = WideCharToMultiByte(CP_ACP, 0, piW->pName, -1,
1572                                                   ptr, outlen, NULL, NULL);
1573                         ptr += len;
1574                         outlen -= len;
1575                     }
1576                     if (piW->pComment) {
1577                         piA->pComment = ptr;
1578                         len = WideCharToMultiByte(CP_ACP, 0, piW->pComment, -1,
1579                                                   ptr, outlen, NULL, NULL);
1580                         ptr += len;
1581                         outlen -= len;
1582                     }
1583                     break;
1584                 }
1585
1586             case 2:
1587                 {
1588                     PRINTER_INFO_2W * piW = (PRINTER_INFO_2W *) pPrintersW;
1589                     PRINTER_INFO_2A * piA = (PRINTER_INFO_2A *) out;
1590                     LPDEVMODEA dmA;
1591
1592                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1593                     if (piW->pServerName) {
1594                         piA->pServerName = ptr;
1595                         len = WideCharToMultiByte(CP_ACP, 0, piW->pServerName, -1,
1596                                                   ptr, outlen, NULL, NULL);
1597                         ptr += len;
1598                         outlen -= len;
1599                     }
1600                     if (piW->pPrinterName) {
1601                         piA->pPrinterName = ptr;
1602                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1603                                                   ptr, outlen, NULL, NULL);
1604                         ptr += len;
1605                         outlen -= len;
1606                     }
1607                     if (piW->pShareName) {
1608                         piA->pShareName = ptr;
1609                         len = WideCharToMultiByte(CP_ACP, 0, piW->pShareName, -1,
1610                                                   ptr, outlen, NULL, NULL);
1611                         ptr += len;
1612                         outlen -= len;
1613                     }
1614                     if (piW->pPortName) {
1615                         piA->pPortName = ptr;
1616                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPortName, -1,
1617                                                   ptr, outlen, NULL, NULL);
1618                         ptr += len;
1619                         outlen -= len;
1620                     }
1621                     if (piW->pDriverName) {
1622                         piA->pDriverName = ptr;
1623                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDriverName, -1,
1624                                                   ptr, outlen, NULL, NULL);
1625                         ptr += len;
1626                         outlen -= len;
1627                     }
1628                     if (piW->pComment) {
1629                         piA->pComment = ptr;
1630                         len = WideCharToMultiByte(CP_ACP, 0, piW->pComment, -1,
1631                                                   ptr, outlen, NULL, NULL);
1632                         ptr += len;
1633                         outlen -= len;
1634                     }
1635                     if (piW->pLocation) {
1636                         piA->pLocation = ptr;
1637                         len = WideCharToMultiByte(CP_ACP, 0, piW->pLocation, -1,
1638                                                   ptr, outlen, NULL, NULL);
1639                         ptr += len;
1640                         outlen -= len;
1641                     }
1642
1643                     dmA = DEVMODEdupWtoA(piW->pDevMode);
1644                     if (dmA) {
1645                         /* align DEVMODEA to a DWORD boundary */
1646                         len = (4 - ( (DWORD_PTR) ptr & 3)) & 3;
1647                         ptr += len;
1648                         outlen -= len;
1649
1650                         piA->pDevMode = (LPDEVMODEA) ptr;
1651                         len = dmA->dmSize + dmA->dmDriverExtra;
1652                         memcpy(ptr, dmA, len);
1653                         HeapFree(GetProcessHeap(), 0, dmA);
1654
1655                         ptr += len;
1656                         outlen -= len;
1657                     }
1658
1659                     if (piW->pSepFile) {
1660                         piA->pSepFile = ptr;
1661                         len = WideCharToMultiByte(CP_ACP, 0, piW->pSepFile, -1,
1662                                                   ptr, outlen, NULL, NULL);
1663                         ptr += len;
1664                         outlen -= len;
1665                     }
1666                     if (piW->pPrintProcessor) {
1667                         piA->pPrintProcessor = ptr;
1668                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrintProcessor, -1,
1669                                                   ptr, outlen, NULL, NULL);
1670                         ptr += len;
1671                         outlen -= len;
1672                     }
1673                     if (piW->pDatatype) {
1674                         piA->pDatatype = ptr;
1675                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDatatype, -1,
1676                                                   ptr, outlen, NULL, NULL);
1677                         ptr += len;
1678                         outlen -= len;
1679                     }
1680                     if (piW->pParameters) {
1681                         piA->pParameters = ptr;
1682                         len = WideCharToMultiByte(CP_ACP, 0, piW->pParameters, -1,
1683                                                   ptr, outlen, NULL, NULL);
1684                         ptr += len;
1685                         outlen -= len;
1686                     }
1687                     if (piW->pSecurityDescriptor) {
1688                         piA->pSecurityDescriptor = NULL;
1689                         FIXME("pSecurityDescriptor ignored: %s\n", debugstr_w(piW->pPrinterName));
1690                     }
1691                     break;
1692                 }
1693
1694             case 4:
1695                 {
1696                     PRINTER_INFO_4W * piW = (PRINTER_INFO_4W *) pPrintersW;
1697                     PRINTER_INFO_4A * piA = (PRINTER_INFO_4A *) out;
1698
1699                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1700
1701                     if (piW->pPrinterName) {
1702                         piA->pPrinterName = ptr;
1703                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1704                                                   ptr, outlen, NULL, NULL);
1705                         ptr += len;
1706                         outlen -= len;
1707                     }
1708                     if (piW->pServerName) {
1709                         piA->pServerName = ptr;
1710                         len = WideCharToMultiByte(CP_ACP, 0, piW->pServerName, -1,
1711                                                   ptr, outlen, NULL, NULL);
1712                         ptr += len;
1713                         outlen -= len;
1714                     }
1715                     break;
1716                 }
1717
1718             case 5:
1719                 {
1720                     PRINTER_INFO_5W * piW = (PRINTER_INFO_5W *) pPrintersW;
1721                     PRINTER_INFO_5A * piA = (PRINTER_INFO_5A *) out;
1722
1723                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1724
1725                     if (piW->pPrinterName) {
1726                         piA->pPrinterName = ptr;
1727                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1728                                                   ptr, outlen, NULL, NULL);
1729                         ptr += len;
1730                         outlen -= len;
1731                     }
1732                     if (piW->pPortName) {
1733                         piA->pPortName = ptr;
1734                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPortName, -1,
1735                                                   ptr, outlen, NULL, NULL);
1736                         ptr += len;
1737                         outlen -= len;
1738                     }
1739                     break;
1740                 }
1741
1742             default:
1743                 FIXME("for level %u\n", level);
1744         }
1745         pPrintersW += pi_sizeof[level];
1746         out += pi_sizeof[level];
1747         id++;
1748     }
1749 }
1750
1751 /***********************************************************
1752  *             PRINTER_INFO_2AtoW
1753  * Creates a unicode copy of PRINTER_INFO_2A on heap
1754  */
1755 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
1756 {
1757     LPPRINTER_INFO_2W piW;
1758     UNICODE_STRING usBuffer;
1759
1760     if(!piA) return NULL;
1761     piW = HeapAlloc(heap, 0, sizeof(*piW));
1762     memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
1763     
1764     piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
1765     piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
1766     piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
1767     piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
1768     piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
1769     piW->pComment = asciitounicode(&usBuffer,piA->pComment);
1770     piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
1771     piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
1772     piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
1773     piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
1774     piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
1775     piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
1776     return piW;
1777 }
1778
1779 /***********************************************************
1780  *       FREE_PRINTER_INFO_2W
1781  * Free PRINTER_INFO_2W and all strings
1782  */
1783 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
1784 {
1785     if(!piW) return;
1786
1787     HeapFree(heap,0,piW->pServerName);
1788     HeapFree(heap,0,piW->pPrinterName);
1789     HeapFree(heap,0,piW->pShareName);
1790     HeapFree(heap,0,piW->pPortName);
1791     HeapFree(heap,0,piW->pDriverName);
1792     HeapFree(heap,0,piW->pComment);
1793     HeapFree(heap,0,piW->pLocation);
1794     HeapFree(heap,0,piW->pDevMode);
1795     HeapFree(heap,0,piW->pSepFile);
1796     HeapFree(heap,0,piW->pPrintProcessor);
1797     HeapFree(heap,0,piW->pDatatype);
1798     HeapFree(heap,0,piW->pParameters);
1799     HeapFree(heap,0,piW);
1800     return;
1801 }
1802
1803 /******************************************************************
1804  *              DeviceCapabilities     [WINSPOOL.@]
1805  *              DeviceCapabilitiesA    [WINSPOOL.@]
1806  *
1807  */
1808 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
1809                                LPSTR pOutput, LPDEVMODEA lpdm)
1810 {
1811     INT ret;
1812
1813     if (!GDI_CallDeviceCapabilities16)
1814     {
1815         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1816                                                               (LPCSTR)104 );
1817         if (!GDI_CallDeviceCapabilities16) return -1;
1818     }
1819     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
1820
1821     /* If DC_PAPERSIZE map POINT16s to POINTs */
1822     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
1823         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
1824         POINT *pt = (POINT *)pOutput;
1825         INT i;
1826         memcpy(tmp, pOutput, ret * sizeof(POINT16));
1827         for(i = 0; i < ret; i++, pt++)
1828         {
1829             pt->x = tmp[i].x;
1830             pt->y = tmp[i].y;
1831         }
1832         HeapFree( GetProcessHeap(), 0, tmp );
1833     }
1834     return ret;
1835 }
1836
1837
1838 /*****************************************************************************
1839  *          DeviceCapabilitiesW        [WINSPOOL.@]
1840  *
1841  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
1842  *
1843  */
1844 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
1845                                WORD fwCapability, LPWSTR pOutput,
1846                                const DEVMODEW *pDevMode)
1847 {
1848     LPDEVMODEA dmA = DEVMODEdupWtoA(pDevMode);
1849     LPSTR pDeviceA = strdupWtoA(pDevice);
1850     LPSTR pPortA = strdupWtoA(pPort);
1851     INT ret;
1852
1853     if(pOutput && (fwCapability == DC_BINNAMES ||
1854                    fwCapability == DC_FILEDEPENDENCIES ||
1855                    fwCapability == DC_PAPERNAMES)) {
1856       /* These need A -> W translation */
1857         INT size = 0, i;
1858         LPSTR pOutputA;
1859         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
1860                                   dmA);
1861         if(ret == -1)
1862             return ret;
1863         switch(fwCapability) {
1864         case DC_BINNAMES:
1865             size = 24;
1866             break;
1867         case DC_PAPERNAMES:
1868         case DC_FILEDEPENDENCIES:
1869             size = 64;
1870             break;
1871         }
1872         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
1873         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
1874                                   dmA);
1875         for(i = 0; i < ret; i++)
1876             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
1877                                 pOutput + (i * size), size);
1878         HeapFree(GetProcessHeap(), 0, pOutputA);
1879     } else {
1880         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
1881                                   (LPSTR)pOutput, dmA);
1882     }
1883     HeapFree(GetProcessHeap(),0,pPortA);
1884     HeapFree(GetProcessHeap(),0,pDeviceA);
1885     HeapFree(GetProcessHeap(),0,dmA);
1886     return ret;
1887 }
1888
1889 /******************************************************************
1890  *              DocumentPropertiesA   [WINSPOOL.@]
1891  *
1892  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1893  */
1894 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
1895                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
1896                                 LPDEVMODEA pDevModeInput,DWORD fMode )
1897 {
1898     LPSTR lpName = pDeviceName;
1899     static CHAR port[] = "LPT1:";
1900     LONG ret;
1901
1902     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1903         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
1904     );
1905
1906     if(!pDeviceName) {
1907         LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1908         if(!lpNameW) {
1909                 ERR("no name from hPrinter?\n");
1910                 SetLastError(ERROR_INVALID_HANDLE);
1911                 return -1;
1912         }
1913         lpName = strdupWtoA(lpNameW);
1914     }
1915
1916     if (!GDI_CallExtDeviceMode16)
1917     {
1918         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1919                                                          (LPCSTR)102 );
1920         if (!GDI_CallExtDeviceMode16) {
1921                 ERR("No CallExtDeviceMode16?\n");
1922                 return -1;
1923         }
1924     }
1925     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, port,
1926                                   pDevModeInput, NULL, fMode);
1927
1928     if(!pDeviceName)
1929         HeapFree(GetProcessHeap(),0,lpName);
1930     return ret;
1931 }
1932
1933
1934 /*****************************************************************************
1935  *          DocumentPropertiesW (WINSPOOL.@)
1936  *
1937  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1938  */
1939 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1940                                 LPWSTR pDeviceName,
1941                                 LPDEVMODEW pDevModeOutput,
1942                                 LPDEVMODEW pDevModeInput, DWORD fMode)
1943 {
1944
1945     LPSTR pDeviceNameA = strdupWtoA(pDeviceName);
1946     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(pDevModeInput);
1947     LPDEVMODEA pDevModeOutputA = NULL;
1948     LONG ret;
1949
1950     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1951           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1952           fMode);
1953     if(pDevModeOutput) {
1954         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1955         if(ret < 0) return ret;
1956         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1957     }
1958     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1959                               pDevModeInputA, fMode);
1960     if(pDevModeOutput) {
1961         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1962         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1963     }
1964     if(fMode == 0 && ret > 0)
1965         ret += (CCHDEVICENAME + CCHFORMNAME);
1966     HeapFree(GetProcessHeap(),0,pDevModeInputA);
1967     HeapFree(GetProcessHeap(),0,pDeviceNameA);
1968     return ret;
1969 }
1970
1971 /******************************************************************
1972  *              OpenPrinterA        [WINSPOOL.@]
1973  *
1974  * See OpenPrinterW.
1975  *
1976  */
1977 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
1978                          LPPRINTER_DEFAULTSA pDefault)
1979 {
1980     UNICODE_STRING lpPrinterNameW;
1981     UNICODE_STRING usBuffer;
1982     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
1983     PWSTR pwstrPrinterNameW;
1984     BOOL ret;
1985
1986     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
1987
1988     if(pDefault) {
1989         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
1990         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
1991         DefaultW.DesiredAccess = pDefault->DesiredAccess;
1992         pDefaultW = &DefaultW;
1993     }
1994     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
1995     if(pDefault) {
1996         RtlFreeUnicodeString(&usBuffer);
1997         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
1998     }
1999     RtlFreeUnicodeString(&lpPrinterNameW);
2000     return ret;
2001 }
2002
2003 /******************************************************************
2004  *              OpenPrinterW        [WINSPOOL.@]
2005  *
2006  * Open a Printer / Printserver or a Printer-Object
2007  *
2008  * PARAMS
2009  *  lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
2010  *  phPrinter     [O] The resulting Handle is stored here
2011  *  pDefault      [I] PTR to Default Printer Settings or NULL
2012  *
2013  * RETURNS
2014  *  Success: TRUE
2015  *  Failure: FALSE
2016  *
2017  * NOTES
2018  *  lpPrinterName is one of:
2019  *|  Printserver (NT only): "Servername" or NULL for the local Printserver
2020  *|  Printer: "PrinterName"
2021  *|  Printer-Object: "PrinterName,Job xxx"
2022  *|  XcvMonitor: "Servername,XcvMonitor MonitorName"
2023  *|  XcvPort: "Servername,XcvPort PortName"
2024  *
2025  * BUGS
2026  *|  Printer-Object not supported
2027  *|  pDefaults is ignored
2028  *
2029  */
2030 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter, LPPRINTER_DEFAULTSW pDefault)
2031 {
2032
2033     TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), phPrinter, pDefault);
2034     if (pDefault) {
2035         FIXME("PRINTER_DEFAULTS ignored => %s,%p,0x%08x\n",
2036         debugstr_w(pDefault->pDatatype), pDefault->pDevMode, pDefault->DesiredAccess);
2037     }
2038
2039     if(!phPrinter) {
2040         /* NT: FALSE with ERROR_INVALID_PARAMETER, 9x: TRUE */
2041         SetLastError(ERROR_INVALID_PARAMETER);
2042         return FALSE;
2043     }
2044
2045     /* Get the unique handle of the printer or Printserver */
2046     *phPrinter = get_opened_printer_entry(lpPrinterName, pDefault);
2047     TRACE("returning %d with %u and %p\n", *phPrinter != NULL, GetLastError(), *phPrinter);
2048     return (*phPrinter != 0);
2049 }
2050
2051 /******************************************************************
2052  *              AddMonitorA        [WINSPOOL.@]
2053  *
2054  * See AddMonitorW.
2055  *
2056  */
2057 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
2058 {
2059     LPWSTR  nameW = NULL;
2060     INT     len;
2061     BOOL    res;
2062     LPMONITOR_INFO_2A mi2a;
2063     MONITOR_INFO_2W mi2w;
2064
2065     mi2a = (LPMONITOR_INFO_2A) pMonitors;
2066     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_a(pName), Level, pMonitors,
2067           debugstr_a(mi2a ? mi2a->pName : NULL),
2068           debugstr_a(mi2a ? mi2a->pEnvironment : NULL),
2069           debugstr_a(mi2a ? mi2a->pDLLName : NULL));
2070
2071     if  (Level != 2) {
2072         SetLastError(ERROR_INVALID_LEVEL);
2073         return FALSE;
2074     }
2075
2076     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2077     if (mi2a == NULL) {
2078         return FALSE;
2079     }
2080
2081     if (pName) {
2082         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2083         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2084         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2085     }
2086
2087     memset(&mi2w, 0, sizeof(MONITOR_INFO_2W));
2088     if (mi2a->pName) {
2089         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, NULL, 0);
2090         mi2w.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2091         MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, mi2w.pName, len);
2092     }
2093     if (mi2a->pEnvironment) {
2094         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, NULL, 0);
2095         mi2w.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2096         MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, mi2w.pEnvironment, len);
2097     }
2098     if (mi2a->pDLLName) {
2099         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, NULL, 0);
2100         mi2w.pDLLName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2101         MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, mi2w.pDLLName, len);
2102     }
2103
2104     res = AddMonitorW(nameW, Level, (LPBYTE) &mi2w);
2105
2106     HeapFree(GetProcessHeap(), 0, mi2w.pName); 
2107     HeapFree(GetProcessHeap(), 0, mi2w.pEnvironment); 
2108     HeapFree(GetProcessHeap(), 0, mi2w.pDLLName); 
2109
2110     HeapFree(GetProcessHeap(), 0, nameW); 
2111     return (res);
2112 }
2113
2114 /******************************************************************************
2115  *              AddMonitorW        [WINSPOOL.@]
2116  *
2117  * Install a Printmonitor
2118  *
2119  * PARAMS
2120  *  pName       [I] Servername or NULL (local Computer)
2121  *  Level       [I] Structure-Level (Must be 2)
2122  *  pMonitors   [I] PTR to MONITOR_INFO_2
2123  *
2124  * RETURNS
2125  *  Success: TRUE
2126  *  Failure: FALSE
2127  *
2128  * NOTES
2129  *  All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
2130  *
2131  */
2132 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
2133 {
2134     LPMONITOR_INFO_2W mi2w;
2135
2136     mi2w = (LPMONITOR_INFO_2W) pMonitors;
2137     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_w(pName), Level, pMonitors,
2138           debugstr_w(mi2w ? mi2w->pName : NULL),
2139           debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
2140           debugstr_w(mi2w ? mi2w->pDLLName : NULL));
2141
2142     if ((backend == NULL)  && !load_backend()) return FALSE;
2143
2144     if (Level != 2) {
2145         SetLastError(ERROR_INVALID_LEVEL);
2146         return FALSE;
2147     }
2148
2149     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2150     if (mi2w == NULL) {
2151         return FALSE;
2152     }
2153
2154     return backend->fpAddMonitor(pName, Level, pMonitors);
2155 }
2156
2157 /******************************************************************
2158  *              DeletePrinterDriverA        [WINSPOOL.@]
2159  *
2160  */
2161 BOOL WINAPI DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
2162 {
2163     return DeletePrinterDriverExA(pName, pEnvironment, pDriverName, 0, 0);
2164 }
2165
2166 /******************************************************************
2167  *              DeletePrinterDriverW        [WINSPOOL.@]
2168  *
2169  */
2170 BOOL WINAPI DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
2171 {
2172     return DeletePrinterDriverExW(pName, pEnvironment, pDriverName, 0, 0);
2173 }
2174
2175 /******************************************************************
2176  *              DeleteMonitorA        [WINSPOOL.@]
2177  *
2178  * See DeleteMonitorW.
2179  *
2180  */
2181 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
2182 {
2183     LPWSTR  nameW = NULL;
2184     LPWSTR  EnvironmentW = NULL;
2185     LPWSTR  MonitorNameW = NULL;
2186     BOOL    res;
2187     INT     len;
2188
2189     if (pName) {
2190         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2191         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2192         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2193     }
2194
2195     if (pEnvironment) {
2196         len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
2197         EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2198         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
2199     }
2200     if (pMonitorName) {
2201         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
2202         MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2203         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
2204     }
2205
2206     res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
2207
2208     HeapFree(GetProcessHeap(), 0, MonitorNameW); 
2209     HeapFree(GetProcessHeap(), 0, EnvironmentW);
2210     HeapFree(GetProcessHeap(), 0, nameW); 
2211     return (res);
2212 }
2213
2214 /******************************************************************
2215  *              DeleteMonitorW        [WINSPOOL.@]
2216  *
2217  * Delete a specific Printmonitor from a Printing-Environment
2218  *
2219  * PARAMS
2220  *  pName        [I] Servername or NULL (local Computer)
2221  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
2222  *  pMonitorName [I] Name of the Monitor, that should be deleted
2223  *
2224  * RETURNS
2225  *  Success: TRUE
2226  *  Failure: FALSE
2227  *
2228  * NOTES
2229  *  pEnvironment is ignored in Windows for the local Computer.
2230  *
2231  */
2232 BOOL WINAPI DeleteMonitorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
2233 {
2234
2235     TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
2236            debugstr_w(pMonitorName));
2237
2238     if ((backend == NULL)  && !load_backend()) return FALSE;
2239
2240     return backend->fpDeleteMonitor(pName, pEnvironment, pMonitorName);
2241 }
2242
2243
2244 /******************************************************************
2245  *              DeletePortA        [WINSPOOL.@]
2246  *
2247  * See DeletePortW.
2248  *
2249  */
2250 BOOL WINAPI DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
2251 {
2252     LPWSTR  nameW = NULL;
2253     LPWSTR  portW = NULL;
2254     INT     len;
2255     DWORD   res;
2256
2257     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
2258
2259     /* convert servername to unicode */
2260     if (pName) {
2261         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2262         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2263         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2264     }
2265
2266     /* convert portname to unicode */
2267     if (pPortName) {
2268         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
2269         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2270         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
2271     }
2272
2273     res = DeletePortW(nameW, hWnd, portW);
2274     HeapFree(GetProcessHeap(), 0, nameW);
2275     HeapFree(GetProcessHeap(), 0, portW);
2276     return res;
2277 }
2278
2279 /******************************************************************
2280  *              DeletePortW        [WINSPOOL.@]
2281  *
2282  * Delete a specific Port
2283  *
2284  * PARAMS
2285  *  pName     [I] Servername or NULL (local Computer)
2286  *  hWnd      [I] Handle to parent Window for the Dialog-Box
2287  *  pPortName [I] Name of the Port, that should be deleted
2288  *
2289  * RETURNS
2290  *  Success: TRUE
2291  *  Failure: FALSE
2292  *
2293  */
2294 BOOL WINAPI DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
2295 {
2296     monitor_t * pm;
2297     monitor_t * pui;
2298     DWORD       res;
2299
2300     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
2301
2302     if (pName && pName[0]) {
2303         SetLastError(ERROR_INVALID_PARAMETER);
2304         return FALSE;
2305     }
2306
2307     if (!pPortName) {
2308         SetLastError(RPC_X_NULL_REF_POINTER);
2309         return FALSE;
2310     }
2311
2312     /* an empty Portname is Invalid */
2313     if (!pPortName[0]) {
2314         SetLastError(ERROR_NOT_SUPPORTED);
2315         return FALSE;
2316     }
2317
2318     pm = monitor_load_by_port(pPortName);
2319     if (pm && pm->monitor && pm->monitor->pfnDeletePort) {
2320         TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
2321         res = pm->monitor->pfnDeletePort(pName, hWnd, pPortName);
2322         TRACE("got %d with %u\n", res, GetLastError());
2323     }
2324     else
2325     {
2326         pui = monitor_loadui(pm);
2327         if (pui && pui->monitorUI && pui->monitorUI->pfnDeletePortUI) {
2328             TRACE("use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
2329             res = pui->monitorUI->pfnDeletePortUI(pName, hWnd, pPortName);
2330             TRACE("got %d with %u\n", res, GetLastError());
2331         }
2332         else
2333         {
2334             FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
2335                   pm, debugstr_w(pm ? pm->dllname : NULL), pui, debugstr_w(pui ? pui->dllname : NULL));
2336
2337             /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
2338             SetLastError(ERROR_NOT_SUPPORTED);
2339             res = FALSE;
2340         }
2341         monitor_unload(pui);
2342     }
2343     monitor_unload(pm);
2344
2345     TRACE("returning %d with %u\n", res, GetLastError());
2346     return res;
2347 }
2348
2349 /******************************************************************************
2350  *    SetPrinterW  [WINSPOOL.@]
2351  */
2352 BOOL WINAPI SetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command)
2353 {
2354     FIXME("(%p, %d, %p, %d): stub\n", hPrinter, Level, pPrinter, Command);
2355     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2356     return FALSE;
2357 }
2358
2359 /******************************************************************************
2360  *    WritePrinter  [WINSPOOL.@]
2361  */
2362 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
2363 {
2364     opened_printer_t *printer;
2365     BOOL ret = FALSE;
2366
2367     TRACE("(%p, %p, %d, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
2368
2369     EnterCriticalSection(&printer_handles_cs);
2370     printer = get_opened_printer(hPrinter);
2371     if(!printer)
2372     {
2373         SetLastError(ERROR_INVALID_HANDLE);
2374         goto end;
2375     }
2376
2377     if(!printer->doc)
2378     {
2379         SetLastError(ERROR_SPL_NO_STARTDOC);
2380         goto end;
2381     }
2382
2383     ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
2384 end:
2385     LeaveCriticalSection(&printer_handles_cs);
2386     return ret;
2387 }
2388
2389 /*****************************************************************************
2390  *          AddFormA  [WINSPOOL.@]
2391  */
2392 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2393 {
2394     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2395     return 1;
2396 }
2397
2398 /*****************************************************************************
2399  *          AddFormW  [WINSPOOL.@]
2400  */
2401 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2402 {
2403     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2404     return 1;
2405 }
2406
2407 /*****************************************************************************
2408  *          AddJobA  [WINSPOOL.@]
2409  */
2410 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2411 {
2412     BOOL ret;
2413     BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2414     DWORD needed;
2415
2416     if(Level != 1) {
2417         SetLastError(ERROR_INVALID_LEVEL);
2418         return FALSE;
2419     }
2420
2421     ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
2422
2423     if(ret) {
2424         ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
2425         DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
2426         *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
2427         if(*pcbNeeded > cbBuf) {
2428             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2429             ret = FALSE;
2430         } else {
2431             ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
2432             addjobA->JobId = addjobW->JobId;
2433             addjobA->Path = (char *)(addjobA + 1);
2434             WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
2435         }
2436     }
2437     return ret;
2438 }
2439
2440 /*****************************************************************************
2441  *          AddJobW  [WINSPOOL.@]
2442  */
2443 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2444 {
2445     opened_printer_t *printer;
2446     job_t *job;
2447     BOOL ret = FALSE;
2448     static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
2449     static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
2450     WCHAR path[MAX_PATH], filename[MAX_PATH];
2451     DWORD len;
2452     ADDJOB_INFO_1W *addjob;
2453
2454     TRACE("(%p,%d,%p,%d,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
2455     
2456     EnterCriticalSection(&printer_handles_cs);
2457
2458     printer = get_opened_printer(hPrinter);
2459
2460     if(!printer) {
2461         SetLastError(ERROR_INVALID_HANDLE);
2462         goto end;
2463     }
2464
2465     if(Level != 1) {
2466         SetLastError(ERROR_INVALID_LEVEL);
2467         goto end;
2468     }
2469
2470     job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
2471     if(!job)
2472         goto end;
2473
2474     job->job_id = InterlockedIncrement(&next_job_id);
2475
2476     len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
2477     if(path[len - 1] != '\\')
2478         path[len++] = '\\';
2479     memcpy(path + len, spool_path, sizeof(spool_path));    
2480     sprintfW(filename, fmtW, path, job->job_id);
2481
2482     len = strlenW(filename);
2483     job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2484     memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
2485     job->document_title = strdupW(default_doc_title);
2486     list_add_tail(&printer->queue->jobs, &job->entry);
2487
2488     *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
2489     if(*pcbNeeded <= cbBuf) {
2490         addjob = (ADDJOB_INFO_1W*)pData;
2491         addjob->JobId = job->job_id;
2492         addjob->Path = (WCHAR *)(addjob + 1);
2493         memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
2494         ret = TRUE;
2495     } else 
2496         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2497
2498 end:
2499     LeaveCriticalSection(&printer_handles_cs);
2500     return ret;
2501 }
2502
2503 /*****************************************************************************
2504  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
2505  *
2506  * Return the PATH for the Print-Processors
2507  *
2508  * See GetPrintProcessorDirectoryW.
2509  *
2510  *
2511  */
2512 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
2513                                         DWORD level,  LPBYTE Info,
2514                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2515 {
2516     LPWSTR  serverW = NULL;
2517     LPWSTR  envW = NULL;
2518     BOOL    ret;
2519     INT     len;
2520
2521     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(server), 
2522           debugstr_a(env), level, Info, cbBuf, pcbNeeded);
2523  
2524
2525     if (server) {
2526         len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
2527         serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2528         MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
2529     }
2530
2531     if (env) {
2532         len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
2533         envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2534         MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
2535     }
2536
2537     /* NT requires the buffersize from GetPrintProcessorDirectoryW also
2538        for GetPrintProcessorDirectoryA and WC2MB is done in-place.
2539      */
2540     ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info, 
2541                                       cbBuf, pcbNeeded);
2542
2543     if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
2544                                        cbBuf, NULL, NULL) > 0;
2545
2546
2547     TRACE(" required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
2548     HeapFree(GetProcessHeap(), 0, envW);
2549     HeapFree(GetProcessHeap(), 0, serverW);
2550     return ret;
2551 }
2552
2553 /*****************************************************************************
2554  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
2555  *
2556  * Return the PATH for the Print-Processors
2557  *
2558  * PARAMS
2559  *   server     [I] Servername (NT only) or NULL (local Computer)
2560  *   env        [I] Printing-Environment (see below) or NULL (Default)
2561  *   level      [I] Structure-Level (must be 1)
2562  *   Info       [O] PTR to Buffer that receives the Result
2563  *   cbBuf      [I] Size of Buffer at "Info"
2564  *   pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / 
2565  *                  required for the Buffer at "Info"
2566  *
2567  * RETURNS
2568  *   Success: TRUE  and in pcbNeeded the Bytes used in Info
2569  *   Failure: FALSE and in pcbNeeded the Bytes required for Info,
2570  *   if cbBuf is too small
2571  * 
2572  *   Native Values returned in Info on Success:
2573  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\PRTPROCS\\w32x86" 
2574  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\PRTPROCS\\win40" 
2575  *|  win9x(Windows 4.0):  "%winsysdir%" 
2576  *
2577  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
2578  *
2579  * BUGS
2580  *  Only NULL or "" is supported for server
2581  *
2582  */
2583 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
2584                                         DWORD level,  LPBYTE Info,
2585                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2586 {
2587     DWORD needed;
2588     const printenv_t * env_t;
2589
2590     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(server),
2591             debugstr_w(env), level, Info, cbBuf, pcbNeeded);
2592
2593     if(server != NULL && server[0]) {
2594         FIXME("server not supported: %s\n", debugstr_w(server));
2595         SetLastError(ERROR_INVALID_PARAMETER);
2596         return FALSE;
2597     }
2598
2599     env_t = validate_envW(env);
2600     if(!env_t) return FALSE;  /* environment invalid or unsupported */
2601
2602     if(level != 1) {
2603         WARN("(Level: %d) is ignored in win9x\n", level);
2604         SetLastError(ERROR_INVALID_LEVEL);
2605         return FALSE;
2606     }
2607
2608     /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2609     needed = GetSystemDirectoryW(NULL, 0);
2610     /* add the Size for the Subdirectories */
2611     needed += lstrlenW(spoolprtprocsW);
2612     needed += lstrlenW(env_t->subdir);
2613     needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
2614
2615     if(pcbNeeded) *pcbNeeded = needed;
2616     TRACE ("required: 0x%x/%d\n", needed, needed);
2617     if (needed > cbBuf) {
2618         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2619         return FALSE;
2620     }
2621     if(pcbNeeded == NULL) {
2622         /* NT: RPC_X_NULL_REF_POINTER, 9x: ignored */
2623         WARN("(pcbNeeded == NULL) is ignored in win9x\n");
2624         SetLastError(RPC_X_NULL_REF_POINTER);
2625         return FALSE;
2626     }
2627     if(Info == NULL) {
2628         /* NT: RPC_X_NULL_REF_POINTER, 9x: ERROR_INVALID_PARAMETER */
2629         SetLastError(RPC_X_NULL_REF_POINTER);
2630         return FALSE;
2631     }
2632     
2633     GetSystemDirectoryW((LPWSTR) Info, cbBuf/sizeof(WCHAR));
2634     /* add the Subdirectories */
2635     lstrcatW((LPWSTR) Info, spoolprtprocsW);
2636     lstrcatW((LPWSTR) Info, env_t->subdir);
2637     TRACE(" => %s\n", debugstr_w((LPWSTR) Info));
2638     return TRUE;
2639 }
2640
2641 /*****************************************************************************
2642  *          WINSPOOL_OpenDriverReg [internal]
2643  *
2644  * opens the registry for the printer drivers depending on the given input
2645  * variable pEnvironment
2646  *
2647  * RETURNS:
2648  *    the opened hkey on success
2649  *    NULL on error
2650  */
2651 static HKEY WINSPOOL_OpenDriverReg( LPCVOID pEnvironment, BOOL unicode)
2652 {   
2653     HKEY  retval = NULL;
2654     LPWSTR buffer;
2655     const printenv_t * env;
2656
2657     TRACE("(%s, %d)\n",
2658           (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment), unicode);
2659
2660     if (!pEnvironment || unicode) {
2661         /* pEnvironment was NULL or a Unicode-String: use it direct */
2662         env = validate_envW(pEnvironment);
2663     }
2664     else
2665     {
2666         /* pEnvironment was an ANSI-String: convert to unicode first */
2667         LPWSTR  buffer;
2668         INT len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
2669         buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2670         if (buffer) MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, buffer, len);
2671         env = validate_envW(buffer);
2672         HeapFree(GetProcessHeap(), 0, buffer);
2673     }
2674     if (!env) return NULL;
2675
2676     buffer = HeapAlloc( GetProcessHeap(), 0,
2677                 (strlenW(DriversW) + strlenW(env->envname) + 
2678                  strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2679     if(buffer) {
2680         wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2681         RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2682         HeapFree(GetProcessHeap(), 0, buffer);
2683     }
2684     return retval;
2685 }
2686
2687 /*****************************************************************************
2688  *          AddPrinterW  [WINSPOOL.@]
2689  */
2690 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2691 {
2692     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2693     LPDEVMODEA dmA;
2694     LPDEVMODEW dmW;
2695     HANDLE retval;
2696     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2697     LONG size;
2698     static const WCHAR attributesW[]      = {'A','t','t','r','i','b','u','t','e','s',0},
2699                        default_devmodeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0},
2700                        priorityW[]        = {'P','r','i','o','r','i','t','y',0},
2701                        start_timeW[]      = {'S','t','a','r','t','T','i','m','e',0},
2702                        statusW[]          = {'S','t','a','t','u','s',0},
2703                        until_timeW[]      = {'U','n','t','i','l','T','i','m','e',0};
2704
2705     TRACE("(%s,%d,%p)\n", debugstr_w(pName), Level, pPrinter);
2706
2707     if(pName != NULL) {
2708         ERR("pName = %s - unsupported\n", debugstr_w(pName));
2709         SetLastError(ERROR_INVALID_PARAMETER);
2710         return 0;
2711     }
2712     if(Level != 2) {
2713         ERR("Level = %d, unsupported!\n", Level);
2714         SetLastError(ERROR_INVALID_LEVEL);
2715         return 0;
2716     }
2717     if(!pPrinter) {
2718         SetLastError(ERROR_INVALID_PARAMETER);
2719         return 0;
2720     }
2721     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2722        ERROR_SUCCESS) {
2723         ERR("Can't create Printers key\n");
2724         return 0;
2725     }
2726     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2727         if (!RegQueryValueW(hkeyPrinter, attributesW, NULL, NULL)) {
2728             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2729             RegCloseKey(hkeyPrinter);
2730             RegCloseKey(hkeyPrinters);
2731             return 0;
2732         }
2733         RegCloseKey(hkeyPrinter);
2734     }
2735     hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
2736     if(!hkeyDrivers) {
2737         ERR("Can't create Drivers key\n");
2738         RegCloseKey(hkeyPrinters);
2739         return 0;
2740     }
2741     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2742        ERROR_SUCCESS) {
2743         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2744         RegCloseKey(hkeyPrinters);
2745         RegCloseKey(hkeyDrivers);
2746         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2747         return 0;
2748     }
2749     RegCloseKey(hkeyDriver);
2750     RegCloseKey(hkeyDrivers);
2751
2752     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
2753         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2754         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2755         RegCloseKey(hkeyPrinters);
2756         return 0;
2757     }
2758
2759     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2760        ERROR_SUCCESS) {
2761         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2762         SetLastError(ERROR_INVALID_PRINTER_NAME);
2763         RegCloseKey(hkeyPrinters);
2764         return 0;
2765     }
2766     RegSetValueExW(hkeyPrinter, attributesW, 0, REG_DWORD,
2767                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
2768     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2769
2770     /* See if we can load the driver.  We may need the devmode structure anyway
2771      *
2772      * FIXME:
2773      * Note that DocumentPropertiesW will briefly try to open the printer we
2774      * just create to find a DEVMODEA struct (it will use the WINEPS default
2775      * one in case it is not there, so we are ok).
2776      */
2777     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2778
2779     if(size < 0) {
2780         FIXME("DocumentPropertiesW on printer %s fails\n", debugstr_w(pi->pPrinterName));
2781         size = sizeof(DEVMODEW);
2782     }
2783     if(pi->pDevMode)
2784         dmW = pi->pDevMode;
2785     else
2786     {
2787         dmW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
2788         dmW->dmSize = size;
2789         if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER))
2790         {
2791             WARN("DocumentPropertiesW on printer %s failed!\n", debugstr_w(pi->pPrinterName));
2792             HeapFree(GetProcessHeap(),0,dmW);
2793             dmW=NULL;
2794         }
2795         else
2796         {
2797             /* set devmode to printer name */
2798             lstrcpynW(dmW->dmDeviceName, pi->pPrinterName, CCHDEVICENAME);
2799         }
2800     }
2801
2802     /* Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
2803        and we support these drivers.  NT writes DEVMODEW so somehow
2804        we'll need to distinguish between these when we support NT
2805        drivers */
2806     if (dmW)
2807     {
2808         dmA = DEVMODEdupWtoA(dmW);
2809         RegSetValueExW(hkeyPrinter, default_devmodeW, 0, REG_BINARY,
2810                        (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
2811         HeapFree(GetProcessHeap(), 0, dmA);
2812         if(!pi->pDevMode)
2813             HeapFree(GetProcessHeap(), 0, dmW);
2814     }
2815     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
2816     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
2817     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
2818     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
2819
2820     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
2821     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
2822     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
2823     RegSetValueExW(hkeyPrinter, priorityW, 0, REG_DWORD,
2824                    (LPBYTE)&pi->Priority, sizeof(DWORD));
2825     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
2826     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
2827     RegSetValueExW(hkeyPrinter, start_timeW, 0, REG_DWORD,
2828                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
2829     RegSetValueExW(hkeyPrinter, statusW, 0, REG_DWORD,
2830                    (LPBYTE)&pi->Status, sizeof(DWORD));
2831     RegSetValueExW(hkeyPrinter, until_timeW, 0, REG_DWORD,
2832                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
2833
2834     RegCloseKey(hkeyPrinter);
2835     RegCloseKey(hkeyPrinters);
2836     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
2837         ERR("OpenPrinter failing\n");
2838         return 0;
2839     }
2840     return retval;
2841 }
2842
2843 /*****************************************************************************
2844  *          AddPrinterA  [WINSPOOL.@]
2845  */
2846 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
2847 {
2848     UNICODE_STRING pNameW;
2849     PWSTR pwstrNameW;
2850     PRINTER_INFO_2W *piW;
2851     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
2852     HANDLE ret;
2853
2854     TRACE("(%s,%d,%p): stub\n", debugstr_a(pName), Level, pPrinter);
2855     if(Level != 2) {
2856         ERR("Level = %d, unsupported!\n", Level);
2857         SetLastError(ERROR_INVALID_LEVEL);
2858         return 0;
2859     }
2860     pwstrNameW = asciitounicode(&pNameW,pName);
2861     piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
2862
2863     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
2864
2865     FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
2866     RtlFreeUnicodeString(&pNameW);
2867     return ret;
2868 }
2869
2870
2871 /*****************************************************************************
2872  *          ClosePrinter  [WINSPOOL.@]
2873  */
2874 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
2875 {
2876     UINT_PTR i = (UINT_PTR)hPrinter;
2877     opened_printer_t *printer = NULL;
2878     BOOL ret = FALSE;
2879
2880     TRACE("(%p)\n", hPrinter);
2881
2882     EnterCriticalSection(&printer_handles_cs);
2883
2884     if ((i > 0) && (i <= nb_printer_handles))
2885         printer = printer_handles[i - 1];
2886
2887
2888     if(printer)
2889     {
2890         struct list *cursor, *cursor2;
2891
2892         TRACE("%p: %s (hXcv: %p) for %s (doc: %p)\n", printer->pm,
2893                 debugstr_w(printer->pm ? printer->pm->dllname : NULL),
2894                 printer->hXcv, debugstr_w(printer->name), printer->doc );
2895
2896         if(printer->doc)
2897             EndDocPrinter(hPrinter);
2898
2899         if(InterlockedDecrement(&printer->queue->ref) == 0)
2900         {
2901             LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
2902             {
2903                 job_t *job = LIST_ENTRY(cursor, job_t, entry);
2904                 ScheduleJob(hPrinter, job->job_id);
2905             }
2906             HeapFree(GetProcessHeap(), 0, printer->queue);
2907         }
2908         if (printer->hXcv) printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
2909         monitor_unload(printer->pm);
2910         HeapFree(GetProcessHeap(), 0, printer->printername);
2911         HeapFree(GetProcessHeap(), 0, printer->name);
2912         HeapFree(GetProcessHeap(), 0, printer);
2913         printer_handles[i - 1] = NULL;
2914         ret = TRUE;
2915     }
2916     LeaveCriticalSection(&printer_handles_cs);
2917     return ret;
2918 }
2919
2920 /*****************************************************************************
2921  *          DeleteFormA  [WINSPOOL.@]
2922  */
2923 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
2924 {
2925     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
2926     return 1;
2927 }
2928
2929 /*****************************************************************************
2930  *          DeleteFormW  [WINSPOOL.@]
2931  */
2932 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
2933 {
2934     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
2935     return 1;
2936 }
2937
2938 /*****************************************************************************
2939  *          DeletePrinter  [WINSPOOL.@]
2940  */
2941 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
2942 {
2943     LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
2944     HKEY hkeyPrinters, hkey;
2945
2946     if(!lpNameW) {
2947         SetLastError(ERROR_INVALID_HANDLE);
2948         return FALSE;
2949     }
2950     if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
2951         RegDeleteTreeW(hkeyPrinters, lpNameW);
2952         RegCloseKey(hkeyPrinters);
2953     }
2954     WriteProfileStringW(devicesW, lpNameW, NULL);
2955     WriteProfileStringW(PrinterPortsW, lpNameW, NULL);
2956
2957     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
2958         RegDeleteValueW(hkey, lpNameW);
2959         RegCloseKey(hkey);
2960     }
2961
2962     if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
2963         RegDeleteValueW(hkey, lpNameW);
2964         RegCloseKey(hkey);
2965     }
2966     return TRUE;
2967 }
2968
2969 /*****************************************************************************
2970  *          SetPrinterA  [WINSPOOL.@]
2971  */
2972 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
2973                            DWORD Command)
2974 {
2975     FIXME("(%p,%d,%p,%d): stub\n",hPrinter,Level,pPrinter,Command);
2976     return FALSE;
2977 }
2978
2979 /*****************************************************************************
2980  *          SetJobA  [WINSPOOL.@]
2981  */
2982 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
2983                     LPBYTE pJob, DWORD Command)
2984 {
2985     BOOL ret;
2986     LPBYTE JobW;
2987     UNICODE_STRING usBuffer;
2988
2989     TRACE("(%p, %d, %d, %p, %d)\n",hPrinter, JobId, Level, pJob, Command);
2990
2991     /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
2992        are all ignored by SetJob, so we don't bother copying them */
2993     switch(Level)
2994     {
2995     case 0:
2996         JobW = NULL;
2997         break;
2998     case 1:
2999       {
3000         JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
3001         JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
3002
3003         JobW = (LPBYTE)info1W;
3004         info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
3005         info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
3006         info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
3007         info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
3008         info1W->Status = info1A->Status;
3009         info1W->Priority = info1A->Priority;
3010         info1W->Position = info1A->Position;
3011         info1W->PagesPrinted = info1A->PagesPrinted;
3012         break;
3013       }
3014     case 2:
3015       {
3016         JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
3017         JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
3018
3019         JobW = (LPBYTE)info2W;
3020         info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
3021         info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
3022         info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
3023         info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
3024         info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
3025         info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
3026         info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
3027         info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
3028         info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
3029         info2W->Status = info2A->Status;
3030         info2W->Priority = info2A->Priority;
3031         info2W->Position = info2A->Position;
3032         info2W->StartTime = info2A->StartTime;
3033         info2W->UntilTime = info2A->UntilTime;
3034         info2W->PagesPrinted = info2A->PagesPrinted;
3035         break;
3036       }
3037     case 3:
3038         JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
3039         memcpy(JobW, pJob, sizeof(JOB_INFO_3));
3040         break;
3041     default:
3042         SetLastError(ERROR_INVALID_LEVEL);
3043         return FALSE;
3044     }
3045
3046     ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
3047
3048     switch(Level)
3049     {
3050     case 1:
3051       {
3052         JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
3053         HeapFree(GetProcessHeap(), 0, info1W->pUserName);
3054         HeapFree(GetProcessHeap(), 0, info1W->pDocument); 
3055         HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
3056         HeapFree(GetProcessHeap(), 0, info1W->pStatus);
3057         break;
3058       }
3059     case 2:
3060       {
3061         JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
3062         HeapFree(GetProcessHeap(), 0, info2W->pUserName);
3063         HeapFree(GetProcessHeap(), 0, info2W->pDocument); 
3064         HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
3065         HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
3066         HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
3067         HeapFree(GetProcessHeap(), 0, info2W->pParameters);
3068         HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
3069         HeapFree(GetProcessHeap(), 0, info2W->pStatus);
3070         break;
3071       }
3072     }
3073     HeapFree(GetProcessHeap(), 0, JobW);
3074
3075     return ret;
3076 }
3077
3078 /*****************************************************************************
3079  *          SetJobW  [WINSPOOL.@]
3080  */
3081 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
3082                     LPBYTE pJob, DWORD Command)
3083 {
3084     BOOL ret = FALSE;
3085     job_t *job;
3086
3087     TRACE("(%p, %d, %d, %p, %d)\n", hPrinter, JobId, Level, pJob, Command);
3088     FIXME("Ignoring everything other than document title\n");
3089
3090     EnterCriticalSection(&printer_handles_cs);
3091     job = get_job(hPrinter, JobId);
3092     if(!job)
3093         goto end;
3094
3095     switch(Level)
3096     {
3097     case 0:
3098         break;
3099     case 1:
3100       {
3101         JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
3102         HeapFree(GetProcessHeap(), 0, job->document_title);
3103         job->document_title = strdupW(info1->pDocument);
3104         break;
3105       }
3106     case 2:
3107       {
3108         JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
3109         HeapFree(GetProcessHeap(), 0, job->document_title);
3110         job->document_title = strdupW(info2->pDocument);
3111         break;
3112       }
3113     case 3:
3114         break;
3115     default:
3116         SetLastError(ERROR_INVALID_LEVEL);
3117         goto end;
3118     }
3119     ret = TRUE;
3120 end:
3121     LeaveCriticalSection(&printer_handles_cs);
3122     return ret;
3123 }
3124
3125 /*****************************************************************************
3126  *          EndDocPrinter  [WINSPOOL.@]
3127  */
3128 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
3129 {
3130     opened_printer_t *printer;
3131     BOOL ret = FALSE;
3132     TRACE("(%p)\n", hPrinter);
3133
3134     EnterCriticalSection(&printer_handles_cs);
3135
3136     printer = get_opened_printer(hPrinter);
3137     if(!printer)
3138     {
3139         SetLastError(ERROR_INVALID_HANDLE);
3140         goto end;
3141     }
3142
3143     if(!printer->doc)    
3144     {
3145         SetLastError(ERROR_SPL_NO_STARTDOC);
3146         goto end;
3147     }
3148
3149     CloseHandle(printer->doc->hf);
3150     ScheduleJob(hPrinter, printer->doc->job_id);
3151     HeapFree(GetProcessHeap(), 0, printer->doc);
3152     printer->doc = NULL;
3153     ret = TRUE;
3154 end:
3155     LeaveCriticalSection(&printer_handles_cs);
3156     return ret;
3157 }
3158
3159 /*****************************************************************************
3160  *          EndPagePrinter  [WINSPOOL.@]
3161  */
3162 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
3163 {
3164     FIXME("(%p): stub\n", hPrinter);
3165     return TRUE;
3166 }
3167
3168 /*****************************************************************************
3169  *          StartDocPrinterA  [WINSPOOL.@]
3170  */
3171 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3172 {
3173     UNICODE_STRING usBuffer;
3174     DOC_INFO_2W doc2W;
3175     DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
3176     DWORD ret;
3177
3178     /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
3179        or one (DOC_INFO_3) extra DWORDs */
3180
3181     switch(Level) {
3182     case 2:
3183         doc2W.JobId = doc2->JobId;
3184         /* fall through */
3185     case 3:
3186         doc2W.dwMode = doc2->dwMode;
3187         /* fall through */
3188     case 1:
3189         doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
3190         doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
3191         doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
3192         break;
3193
3194     default:
3195         SetLastError(ERROR_INVALID_LEVEL);
3196         return FALSE;
3197     }
3198
3199     ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
3200
3201     HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
3202     HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
3203     HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
3204
3205     return ret;
3206 }
3207
3208 /*****************************************************************************
3209  *          StartDocPrinterW  [WINSPOOL.@]
3210  */
3211 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3212 {
3213     DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
3214     opened_printer_t *printer;
3215     BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
3216     ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
3217     JOB_INFO_1W job_info;
3218     DWORD needed, ret = 0;
3219     HANDLE hf;
3220     WCHAR *filename;
3221
3222     TRACE("(hPrinter = %p, Level = %d, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
3223           hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
3224           debugstr_w(doc->pDatatype));
3225
3226     if(Level < 1 || Level > 3)
3227     {
3228         SetLastError(ERROR_INVALID_LEVEL);
3229         return 0;
3230     }
3231
3232     EnterCriticalSection(&printer_handles_cs);
3233     printer = get_opened_printer(hPrinter);
3234     if(!printer)
3235     {
3236         SetLastError(ERROR_INVALID_HANDLE);
3237         goto end;
3238     }
3239
3240     if(printer->doc)
3241     {
3242         SetLastError(ERROR_INVALID_PRINTER_STATE);
3243         goto end;
3244     }
3245
3246     /* Even if we're printing to a file we still add a print job, we'll
3247        just ignore the spool file name */
3248
3249     if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
3250     {
3251         ERR("AddJob failed gle %u\n", GetLastError());
3252         goto end;
3253     }
3254
3255     if(doc->pOutputFile)
3256         filename = doc->pOutputFile;
3257     else
3258         filename = addjob->Path;
3259
3260     hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3261     if(hf == INVALID_HANDLE_VALUE)
3262         goto end;
3263
3264     memset(&job_info, 0, sizeof(job_info));
3265     job_info.pDocument = doc->pDocName;
3266     SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
3267
3268     printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
3269     printer->doc->hf = hf;
3270     ret = printer->doc->job_id = addjob->JobId;
3271 end:
3272     LeaveCriticalSection(&printer_handles_cs);
3273
3274     return ret;
3275 }
3276
3277 /*****************************************************************************
3278  *          StartPagePrinter  [WINSPOOL.@]
3279  */
3280 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
3281 {
3282     FIXME("(%p): stub\n", hPrinter);
3283     return TRUE;
3284 }
3285
3286 /*****************************************************************************
3287  *          GetFormA  [WINSPOOL.@]
3288  */
3289 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3290                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3291 {
3292     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,pFormName,
3293          Level,pForm,cbBuf,pcbNeeded);
3294     return FALSE;
3295 }
3296
3297 /*****************************************************************************
3298  *          GetFormW  [WINSPOOL.@]
3299  */
3300 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3301                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3302 {
3303     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,
3304           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
3305     return FALSE;
3306 }
3307
3308 /*****************************************************************************
3309  *          SetFormA  [WINSPOOL.@]
3310  */
3311 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3312                         LPBYTE pForm)
3313 {
3314     FIXME("(%p,%s,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3315     return FALSE;
3316 }
3317
3318 /*****************************************************************************
3319  *          SetFormW  [WINSPOOL.@]
3320  */
3321 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3322                         LPBYTE pForm)
3323 {
3324     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3325     return FALSE;
3326 }
3327
3328 /*****************************************************************************
3329  *          ReadPrinter  [WINSPOOL.@]
3330  */
3331 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
3332                            LPDWORD pNoBytesRead)
3333 {
3334     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
3335     return FALSE;
3336 }
3337
3338 /*****************************************************************************
3339  *          ResetPrinterA  [WINSPOOL.@]
3340  */
3341 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
3342 {
3343     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3344     return FALSE;
3345 }
3346
3347 /*****************************************************************************
3348  *          ResetPrinterW  [WINSPOOL.@]
3349  */
3350 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
3351 {
3352     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3353     return FALSE;
3354 }
3355
3356 /*****************************************************************************
3357  *    WINSPOOL_GetDWORDFromReg
3358  *
3359  * Return DWORD associated with ValueName from hkey.
3360  */
3361 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
3362 {
3363     DWORD sz = sizeof(DWORD), type, value = 0;
3364     LONG ret;
3365
3366     ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
3367
3368     if(ret != ERROR_SUCCESS) {
3369         WARN("Got ret = %d on name %s\n", ret, ValueName);
3370         return 0;
3371     }
3372     if(type != REG_DWORD) {
3373         ERR("Got type %d\n", type);
3374         return 0;
3375     }
3376     return value;
3377 }
3378
3379
3380 /*****************************************************************************
3381  * get_filename_from_reg [internal]
3382  *
3383  * Get ValueName from hkey storing result in out
3384  * when the Value in the registry has only a filename, use driverdir as prefix
3385  * outlen is space left in out
3386  * String is stored either as unicode or ascii
3387  *
3388  */
3389
3390 static BOOL get_filename_from_reg(HKEY hkey, LPCWSTR driverdir, DWORD dirlen, LPCWSTR ValueName,
3391                                   LPBYTE out, DWORD outlen, LPDWORD needed, BOOL unicode)
3392 {
3393     WCHAR   filename[MAX_PATH];
3394     DWORD   size;
3395     DWORD   type;
3396     LONG    ret;
3397     LPWSTR  buffer = filename;
3398     LPWSTR  ptr;
3399
3400     *needed = 0;
3401     size = sizeof(filename);
3402     buffer[0] = '\0';
3403     ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3404     if (ret == ERROR_MORE_DATA) {
3405         TRACE("need dynamic buffer: %u\n", size);
3406         buffer = HeapAlloc(GetProcessHeap(), 0, size);
3407         if (!buffer) {
3408             /* No Memory is bad */
3409             return FALSE;
3410         }
3411         buffer[0] = '\0';
3412         ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3413     }
3414
3415     if ((ret != ERROR_SUCCESS) || (!buffer[0])) {
3416         if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3417         return FALSE;
3418     }
3419
3420     ptr = buffer;
3421     while (ptr) {
3422         /* do we have a full path ? */
3423         ret = (((buffer[0] == '\\') && (buffer[1] == '\\')) ||
3424                 (buffer[0] && (buffer[1] == ':') && (buffer[2] == '\\')) );
3425
3426         if (!ret) {
3427             /* we must build the full Path */
3428             *needed += dirlen;
3429             if ((out) && (outlen > dirlen)) {
3430                 if (unicode) {
3431                     lstrcpyW((LPWSTR)out, driverdir);
3432                 }
3433                 else
3434                 {
3435                     WideCharToMultiByte(CP_ACP, 0, driverdir, -1, (LPSTR)out, outlen, NULL, NULL);
3436                 }
3437                 out += dirlen;
3438                 outlen -= dirlen;
3439             }
3440             else
3441                 out = NULL;
3442         }
3443
3444         /* write the filename */
3445         if (unicode) {
3446             size = (lstrlenW(ptr) + 1) * sizeof(WCHAR);
3447             if ((out) && (outlen >= size)) {
3448                 lstrcpyW((LPWSTR)out, ptr);
3449                 out += size;
3450                 outlen -= size;
3451             }
3452             else
3453                 out = NULL;
3454         }
3455         else
3456         {
3457             size = WideCharToMultiByte(CP_ACP, 0, ptr, -1, NULL, 0, NULL, NULL);
3458             if ((out) && (outlen >= size)) {
3459                 WideCharToMultiByte(CP_ACP, 0, ptr, -1, (LPSTR)out, outlen, NULL, NULL);
3460                 out += size;
3461                 outlen -= size;
3462             }
3463             else
3464                 out = NULL;
3465         }
3466         *needed += size;
3467         ptr +=  lstrlenW(ptr)+1;
3468         if ((type != REG_MULTI_SZ) || (!ptr[0]))  ptr = NULL;
3469     }
3470
3471     if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3472
3473     /* write the multisz-termination */
3474     if (type == REG_MULTI_SZ) {
3475         size = (unicode) ? sizeof(WCHAR) : 1;
3476
3477         *needed += size;
3478         if (out && (outlen >= size)) {
3479             memset (out, 0, size);
3480         }
3481     }
3482     return TRUE;
3483 }
3484
3485 /*****************************************************************************
3486  *    WINSPOOL_GetStringFromReg
3487  *
3488  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3489  * String is stored either as unicode or ascii.
3490  * Bit of a hack here to get the ValueName if we want ascii.
3491  */
3492 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
3493                                       DWORD buflen, DWORD *needed,
3494                                       BOOL unicode)
3495 {
3496     DWORD sz = buflen, type;
3497     LONG ret;
3498
3499     if(unicode)
3500         ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3501     else {
3502         LPSTR ValueNameA = strdupWtoA(ValueName);
3503         ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
3504         HeapFree(GetProcessHeap(),0,ValueNameA);
3505     }
3506     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
3507         WARN("Got ret = %d\n", ret);
3508         *needed = 0;
3509         return FALSE;
3510     }
3511     /* add space for terminating '\0' */
3512     sz += unicode ? sizeof(WCHAR) : 1;
3513     *needed = sz;
3514
3515     if (ptr)
3516         TRACE("%s: %s\n", debugstr_w(ValueName), unicode ? debugstr_w((LPCWSTR)ptr) : debugstr_a((LPCSTR)ptr));
3517
3518     return TRUE;
3519 }
3520
3521 /*****************************************************************************
3522  *    WINSPOOL_GetDefaultDevMode
3523  *
3524  * Get a default DevMode values for wineps.
3525  * FIXME - use ppd.
3526  */
3527
3528 static void WINSPOOL_GetDefaultDevMode(
3529         LPBYTE ptr,
3530         DWORD buflen, DWORD *needed,
3531         BOOL unicode)
3532 {
3533     DEVMODEA    dm;
3534     static const char szwps[] = "wineps.drv";
3535
3536         /* fill default DEVMODE - should be read from ppd... */
3537         ZeroMemory( &dm, sizeof(dm) );
3538         memcpy(dm.dmDeviceName,szwps,sizeof szwps);
3539         dm.dmSpecVersion = DM_SPECVERSION;
3540         dm.dmDriverVersion = 1;
3541         dm.dmSize = sizeof(DEVMODEA);
3542         dm.dmDriverExtra = 0;
3543         dm.dmFields =
3544                 DM_ORIENTATION | DM_PAPERSIZE |
3545                 DM_PAPERLENGTH | DM_PAPERWIDTH |
3546                 DM_SCALE |
3547                 DM_COPIES |
3548                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
3549                 DM_YRESOLUTION | DM_TTOPTION;
3550
3551         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
3552         dm.u1.s1.dmPaperSize = DMPAPER_A4;
3553         dm.u1.s1.dmPaperLength = 2970;
3554         dm.u1.s1.dmPaperWidth = 2100;
3555
3556         dm.u1.s1.dmScale = 100;
3557         dm.u1.s1.dmCopies = 1;
3558         dm.u1.s1.dmDefaultSource = DMBIN_AUTO;
3559         dm.u1.s1.dmPrintQuality = DMRES_MEDIUM;
3560         /* dm.dmColor */
3561         /* dm.dmDuplex */
3562         dm.dmYResolution = 300; /* 300dpi */
3563         dm.dmTTOption = DMTT_BITMAP;
3564         /* dm.dmCollate */
3565         /* dm.dmFormName */
3566         /* dm.dmLogPixels */
3567         /* dm.dmBitsPerPel */
3568         /* dm.dmPelsWidth */
3569         /* dm.dmPelsHeight */
3570         /* dm.u2.dmDisplayFlags */
3571         /* dm.dmDisplayFrequency */
3572         /* dm.dmICMMethod */
3573         /* dm.dmICMIntent */
3574         /* dm.dmMediaType */
3575         /* dm.dmDitherType */
3576         /* dm.dmReserved1 */
3577         /* dm.dmReserved2 */
3578         /* dm.dmPanningWidth */
3579         /* dm.dmPanningHeight */
3580
3581     if(unicode) {
3582         if(buflen >= sizeof(DEVMODEW)) {
3583             DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
3584             memcpy(ptr, pdmW, sizeof(DEVMODEW));
3585             HeapFree(GetProcessHeap(),0,pdmW);
3586         }
3587         *needed = sizeof(DEVMODEW);
3588     }
3589     else
3590     {
3591         if(buflen >= sizeof(DEVMODEA)) {
3592             memcpy(ptr, &dm, sizeof(DEVMODEA));
3593         }
3594         *needed = sizeof(DEVMODEA);
3595     }
3596 }
3597
3598 /*****************************************************************************
3599  *    WINSPOOL_GetDevModeFromReg
3600  *
3601  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3602  * DevMode is stored either as unicode or ascii.
3603  */
3604 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
3605                                        LPBYTE ptr,
3606                                        DWORD buflen, DWORD *needed,
3607                                        BOOL unicode)
3608 {
3609     DWORD sz = buflen, type;
3610     LONG ret;
3611
3612     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
3613     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3614     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
3615     if (sz < sizeof(DEVMODEA))
3616     {
3617         TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
3618         return FALSE;
3619     }
3620     /* ensures that dmSize is not erratically bogus if registry is invalid */
3621     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
3622         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
3623     if(unicode) {
3624         sz += (CCHDEVICENAME + CCHFORMNAME);
3625         if(buflen >= sz) {
3626             DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
3627             memcpy(ptr, dmW, sz);
3628             HeapFree(GetProcessHeap(),0,dmW);
3629         }
3630     }
3631     *needed = sz;
3632     return TRUE;
3633 }
3634
3635 /*********************************************************************
3636  *    WINSPOOL_GetPrinter_1
3637  *
3638  * Fills out a PRINTER_INFO_1A|W struct storing the strings in buf.
3639  * The strings are either stored as unicode or ascii.
3640  */
3641 static BOOL WINSPOOL_GetPrinter_1(HKEY hkeyPrinter, PRINTER_INFO_1W *pi1,
3642                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3643                                   BOOL unicode)
3644 {
3645     DWORD size, left = cbBuf;
3646     BOOL space = (cbBuf > 0);
3647     LPBYTE ptr = buf;
3648
3649     *pcbNeeded = 0;
3650
3651     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3652                                  unicode)) {
3653         if(space && size <= left) {
3654             pi1->pName = (LPWSTR)ptr;
3655             ptr += size;
3656             left -= size;
3657         } else
3658             space = FALSE;
3659         *pcbNeeded += size;
3660     }
3661
3662     /* FIXME: pDescription should be something like "Name,Driver_Name,". */
3663     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3664                                  unicode)) {
3665         if(space && size <= left) {
3666             pi1->pDescription = (LPWSTR)ptr;
3667             ptr += size;
3668             left -= size;
3669         } else
3670             space = FALSE;
3671         *pcbNeeded += size;
3672     }
3673
3674     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3675                                  unicode)) {
3676         if(space && size <= left) {
3677             pi1->pComment = (LPWSTR)ptr;
3678             ptr += size;
3679             left -= size;
3680         } else
3681             space = FALSE;
3682         *pcbNeeded += size;
3683     }
3684
3685     if(pi1) pi1->Flags = PRINTER_ENUM_ICON8; /* We're a printer */
3686
3687     if(!space && pi1) /* zero out pi1 if we can't completely fill buf */
3688         memset(pi1, 0, sizeof(*pi1));
3689
3690     return space;
3691 }
3692 /*********************************************************************
3693  *    WINSPOOL_GetPrinter_2
3694  *
3695  * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
3696  * The strings are either stored as unicode or ascii.
3697  */
3698 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
3699                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3700                                   BOOL unicode)
3701 {
3702     DWORD size, left = cbBuf;
3703     BOOL space = (cbBuf > 0);
3704     LPBYTE ptr = buf;
3705
3706     *pcbNeeded = 0;
3707
3708     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3709                                  unicode)) {
3710         if(space && size <= left) {
3711             pi2->pPrinterName = (LPWSTR)ptr;
3712             ptr += size;
3713             left -= size;
3714         } else
3715             space = FALSE;
3716         *pcbNeeded += size;
3717     }
3718     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
3719                                  unicode)) {
3720         if(space && size <= left) {
3721             pi2->pShareName = (LPWSTR)ptr;
3722             ptr += size;
3723             left -= size;
3724         } else
3725             space = FALSE;
3726         *pcbNeeded += size;
3727     }
3728     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3729                                  unicode)) {
3730         if(space && size <= left) {
3731             pi2->pPortName = (LPWSTR)ptr;
3732             ptr += size;
3733             left -= size;
3734         } else
3735             space = FALSE;
3736         *pcbNeeded += size;
3737     }
3738     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
3739                                  &size, unicode)) {
3740         if(space && size <= left) {
3741             pi2->pDriverName = (LPWSTR)ptr;
3742             ptr += size;
3743             left -= size;
3744         } else
3745             space = FALSE;
3746         *pcbNeeded += size;
3747     }
3748     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3749                                  unicode)) {
3750         if(space && size <= left) {
3751             pi2->pComment = (LPWSTR)ptr;
3752             ptr += size;
3753             left -= size;
3754         } else
3755             space = FALSE;
3756         *pcbNeeded += size;
3757     }
3758     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
3759                                  unicode)) {
3760         if(space && size <= left) {
3761             pi2->pLocation = (LPWSTR)ptr;
3762             ptr += size;
3763             left -= size;
3764         } else
3765             space = FALSE;
3766         *pcbNeeded += size;
3767     }
3768     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
3769                                   &size, unicode)) {
3770         if(space && size <= left) {
3771             pi2->pDevMode = (LPDEVMODEW)ptr;
3772             ptr += size;
3773             left -= size;
3774         } else
3775             space = FALSE;
3776         *pcbNeeded += size;
3777     }
3778     else
3779     {
3780         WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
3781         if(space && size <= left) {
3782             pi2->pDevMode = (LPDEVMODEW)ptr;
3783             ptr += size;
3784             left -= size;
3785         } else
3786             space = FALSE;
3787         *pcbNeeded += size;
3788     }
3789     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
3790                                  &size, unicode)) {
3791         if(space && size <= left) {
3792             pi2->pSepFile = (LPWSTR)ptr;
3793             ptr += size;
3794             left -= size;
3795         } else
3796             space = FALSE;
3797         *pcbNeeded += size;
3798     }
3799     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
3800                                  &size, unicode)) {
3801         if(space && size <= left) {
3802             pi2->pPrintProcessor = (LPWSTR)ptr;
3803             ptr += size;
3804             left -= size;
3805         } else
3806             space = FALSE;
3807         *pcbNeeded += size;
3808     }
3809     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
3810                                  &size, unicode)) {
3811         if(space && size <= left) {
3812             pi2->pDatatype = (LPWSTR)ptr;
3813             ptr += size;
3814             left -= size;
3815         } else
3816             space = FALSE;
3817         *pcbNeeded += size;
3818     }
3819     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
3820                                  &size, unicode)) {
3821         if(space && size <= left) {
3822             pi2->pParameters = (LPWSTR)ptr;
3823             ptr += size;
3824             left -= size;
3825         } else
3826             space = FALSE;
3827         *pcbNeeded += size;
3828     }
3829     if(pi2) {
3830         pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3831         pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
3832         pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3833                                                         "Default Priority");
3834         pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
3835         pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
3836     }
3837
3838     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
3839         memset(pi2, 0, sizeof(*pi2));
3840
3841     return space;
3842 }
3843
3844 /*********************************************************************
3845  *    WINSPOOL_GetPrinter_4
3846  *
3847  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
3848  */
3849 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
3850                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3851                                   BOOL unicode)
3852 {
3853     DWORD size, left = cbBuf;
3854     BOOL space = (cbBuf > 0);
3855     LPBYTE ptr = buf;
3856
3857     *pcbNeeded = 0;
3858
3859     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3860                                  unicode)) {
3861         if(space && size <= left) {
3862             pi4->pPrinterName = (LPWSTR)ptr;
3863             ptr += size;
3864             left -= size;
3865         } else
3866             space = FALSE;
3867         *pcbNeeded += size;
3868     }
3869     if(pi4) {
3870         pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3871     }
3872
3873     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
3874         memset(pi4, 0, sizeof(*pi4));
3875
3876     return space;
3877 }
3878
3879 /*********************************************************************
3880  *    WINSPOOL_GetPrinter_5
3881  *
3882  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
3883  */
3884 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
3885                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3886                                   BOOL unicode)
3887 {
3888     DWORD size, left = cbBuf;
3889     BOOL space = (cbBuf > 0);
3890     LPBYTE ptr = buf;
3891
3892     *pcbNeeded = 0;
3893
3894     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3895                                  unicode)) {
3896         if(space && size <= left) {
3897             pi5->pPrinterName = (LPWSTR)ptr;
3898             ptr += size;
3899             left -= size;
3900         } else
3901             space = FALSE;
3902         *pcbNeeded += size;
3903     }
3904     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3905                                  unicode)) {
3906         if(space && size <= left) {
3907             pi5->pPortName = (LPWSTR)ptr;
3908             ptr += size;
3909             left -= size;
3910         } else
3911             space = FALSE;
3912         *pcbNeeded += size;
3913     }
3914     if(pi5) {
3915         pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3916         pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3917                                                                 "dnsTimeout");
3918         pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3919                                                                  "txTimeout");
3920     }
3921
3922     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
3923         memset(pi5, 0, sizeof(*pi5));
3924
3925     return space;
3926 }
3927
3928 /*********************************************************************
3929  *    WINSPOOL_GetPrinter_7
3930  *
3931  * Fills out a PRINTER_INFO_7 struct storing the strings in buf.
3932  */
3933 static BOOL WINSPOOL_GetPrinter_7(HKEY hkeyPrinter, PRINTER_INFO_7W *pi7, LPBYTE buf,
3934                                   DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
3935 {
3936     DWORD size, left = cbBuf;
3937     BOOL space = (cbBuf > 0);
3938     LPBYTE ptr = buf;
3939
3940     *pcbNeeded = 0;
3941
3942     if (WINSPOOL_GetStringFromReg(hkeyPrinter, ObjectGUIDW, ptr, left, &size, unicode))
3943     {
3944         if (space && size <= left) {
3945             pi7->pszObjectGUID = (LPWSTR)ptr;
3946             ptr += size;
3947             left -= size;
3948         } else
3949             space = FALSE;
3950         *pcbNeeded += size;
3951     }
3952     if (pi7) {
3953         /* We do not have a Directory Service */
3954         pi7->dwAction = DSPRINT_UNPUBLISH;
3955     }
3956
3957     if (!space && pi7) /* zero out pi7 if we can't completely fill buf */
3958         memset(pi7, 0, sizeof(*pi7));
3959
3960     return space;
3961 }
3962
3963 /*********************************************************************
3964  *    WINSPOOL_GetPrinter_9
3965  *
3966  * Fills out a PRINTER_INFO_9A|W struct storing the strings in buf.
3967  * The strings are either stored as unicode or ascii.
3968  */
3969 static BOOL WINSPOOL_GetPrinter_9(HKEY hkeyPrinter, PRINTER_INFO_9W *pi9, LPBYTE buf,
3970                                   DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
3971 {
3972     DWORD size;
3973     BOOL space = (cbBuf > 0);
3974
3975     *pcbNeeded = 0;
3976
3977     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, buf, cbBuf, &size, unicode)) {
3978         if(space && size <= cbBuf) {
3979             pi9->pDevMode = (LPDEVMODEW)buf;
3980         } else
3981             space = FALSE;
3982         *pcbNeeded += size;
3983     }
3984     else
3985     {
3986         WINSPOOL_GetDefaultDevMode(buf, cbBuf, &size, unicode);
3987         if(space && size <= cbBuf) {
3988             pi9->pDevMode = (LPDEVMODEW)buf;
3989         } else
3990             space = FALSE;
3991         *pcbNeeded += size;
3992     }
3993
3994     if(!space && pi9) /* zero out pi9 if we can't completely fill buf */
3995         memset(pi9, 0, sizeof(*pi9));
3996
3997     return space;
3998 }
3999
4000 /*****************************************************************************
4001  *          WINSPOOL_GetPrinter
4002  *
4003  *    Implementation of GetPrinterA|W.  Relies on PRINTER_INFO_*W being
4004  *    essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
4005  *    just a collection of pointers to strings.
4006  */
4007 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4008                                 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
4009 {
4010     LPCWSTR name;
4011     DWORD size, needed = 0;
4012     LPBYTE ptr = NULL;
4013     HKEY hkeyPrinter, hkeyPrinters;
4014     BOOL ret;
4015
4016     TRACE("(%p,%d,%p,%d,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
4017
4018     if (!(name = get_opened_printer_name(hPrinter))) {
4019         SetLastError(ERROR_INVALID_HANDLE);
4020         return FALSE;
4021     }
4022
4023     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4024        ERROR_SUCCESS) {
4025         ERR("Can't create Printers key\n");
4026         return FALSE;
4027     }
4028     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
4029     {
4030         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4031         RegCloseKey(hkeyPrinters);
4032         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4033         return FALSE;
4034     }
4035
4036     switch(Level) {
4037     case 2:
4038       {
4039         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
4040
4041         size = sizeof(PRINTER_INFO_2W);
4042         if(size <= cbBuf) {
4043             ptr = pPrinter + size;
4044             cbBuf -= size;
4045             memset(pPrinter, 0, size);
4046         } else {
4047             pi2 = NULL;
4048             cbBuf = 0;
4049         }
4050         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
4051                                     unicode);
4052         needed += size;
4053         break;
4054       }
4055
4056     case 4:
4057       {
4058         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
4059
4060         size = sizeof(PRINTER_INFO_4W);
4061         if(size <= cbBuf) {
4062             ptr = pPrinter + size;
4063             cbBuf -= size;
4064             memset(pPrinter, 0, size);
4065         } else {
4066             pi4 = NULL;
4067             cbBuf = 0;
4068         }
4069         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
4070                                     unicode);
4071         needed += size;
4072         break;
4073       }
4074
4075
4076     case 5:
4077       {
4078         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
4079
4080         size = sizeof(PRINTER_INFO_5W);
4081         if(size <= cbBuf) {
4082             ptr = pPrinter + size;
4083             cbBuf -= size;
4084             memset(pPrinter, 0, size);
4085         } else {
4086             pi5 = NULL;
4087             cbBuf = 0;
4088         }
4089
4090         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
4091                                     unicode);
4092         needed += size;
4093         break;
4094       }
4095
4096
4097     case 6:
4098       {
4099         PRINTER_INFO_6 *pi6 = (PRINTER_INFO_6 *) pPrinter;
4100
4101         size = sizeof(PRINTER_INFO_6);
4102         if (size <= cbBuf) {
4103             /* FIXME: We do not update the status yet */
4104             pi6->dwStatus = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Status");
4105             ret = TRUE;
4106         } else {
4107             ret = FALSE;
4108         }
4109
4110         needed += size;
4111         break;
4112       }
4113
4114     case 7:
4115       {
4116         PRINTER_INFO_7W *pi7 = (PRINTER_INFO_7W *) pPrinter;
4117
4118         size = sizeof(PRINTER_INFO_7W);
4119         if (size <= cbBuf) {
4120             ptr = pPrinter + size;
4121             cbBuf -= size;
4122             memset(pPrinter, 0, size);
4123         } else {
4124             pi7 = NULL;
4125             cbBuf = 0;
4126         }
4127
4128         ret = WINSPOOL_GetPrinter_7(hkeyPrinter, pi7, ptr, cbBuf, &needed, unicode);
4129         needed += size;
4130         break;
4131       }
4132
4133
4134     case 9:
4135       {
4136         PRINTER_INFO_9W *pi9 = (PRINTER_INFO_9W *)pPrinter;
4137
4138         size = sizeof(PRINTER_INFO_9W);
4139         if(size <= cbBuf) {
4140             ptr = pPrinter + size;
4141             cbBuf -= size;
4142             memset(pPrinter, 0, size);
4143         } else {
4144             pi9 = NULL;
4145             cbBuf = 0;
4146         }
4147
4148         ret = WINSPOOL_GetPrinter_9(hkeyPrinter, pi9, ptr, cbBuf, &needed, unicode);
4149         needed += size;
4150         break;
4151       }
4152
4153
4154     default:
4155         FIXME("Unimplemented level %d\n", Level);
4156         SetLastError(ERROR_INVALID_LEVEL);
4157         RegCloseKey(hkeyPrinters);
4158         RegCloseKey(hkeyPrinter);
4159         return FALSE;
4160     }
4161
4162     RegCloseKey(hkeyPrinter);
4163     RegCloseKey(hkeyPrinters);
4164
4165     TRACE("returning %d needed = %d\n", ret, needed);
4166     if(pcbNeeded) *pcbNeeded = needed;
4167     if(!ret)
4168         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4169     return ret;
4170 }
4171
4172 /*****************************************************************************
4173  *          GetPrinterW  [WINSPOOL.@]
4174  */
4175 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4176                         DWORD cbBuf, LPDWORD pcbNeeded)
4177 {
4178     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4179                                TRUE);
4180 }
4181
4182 /*****************************************************************************
4183  *          GetPrinterA  [WINSPOOL.@]
4184  */
4185 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4186                     DWORD cbBuf, LPDWORD pcbNeeded)
4187 {
4188     return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4189                                FALSE);
4190 }
4191
4192 /*****************************************************************************
4193  *          WINSPOOL_EnumPrinters
4194  *
4195  *    Implementation of EnumPrintersA|W
4196  */
4197 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
4198                                   DWORD dwLevel, LPBYTE lpbPrinters,
4199                                   DWORD cbBuf, LPDWORD lpdwNeeded,
4200                                   LPDWORD lpdwReturned, BOOL unicode)
4201
4202 {
4203     HKEY hkeyPrinters, hkeyPrinter;
4204     WCHAR PrinterName[255];
4205     DWORD needed = 0, number = 0;
4206     DWORD used, i, left;
4207     PBYTE pi, buf;
4208
4209     if(lpbPrinters)
4210         memset(lpbPrinters, 0, cbBuf);
4211     if(lpdwReturned)
4212         *lpdwReturned = 0;
4213     if(lpdwNeeded)
4214         *lpdwNeeded = 0;
4215
4216     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
4217     if(dwType == PRINTER_ENUM_DEFAULT)
4218         return TRUE;
4219
4220     if (dwType & PRINTER_ENUM_CONNECTIONS) {
4221         TRACE("ignoring PRINTER_ENUM_CONNECTIONS\n");
4222         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
4223         if (!dwType) {
4224             FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
4225             *lpdwNeeded = 0;
4226             *lpdwReturned = 0;
4227             return TRUE;
4228         }
4229
4230     }
4231
4232     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
4233         FIXME("dwType = %08x\n", dwType);
4234         SetLastError(ERROR_INVALID_FLAGS);
4235         return FALSE;
4236     }
4237
4238     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4239        ERROR_SUCCESS) {
4240         ERR("Can't create Printers key\n");
4241         return FALSE;
4242     }
4243
4244     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
4245                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4246         RegCloseKey(hkeyPrinters);
4247         ERR("Can't query Printers key\n");
4248         return FALSE;
4249     }
4250     TRACE("Found %d printers\n", number);
4251
4252     switch(dwLevel) {
4253     case 1:
4254         used = number * sizeof(PRINTER_INFO_1W);
4255         break;
4256     case 2:
4257         used = number * sizeof(PRINTER_INFO_2W);
4258         break;
4259     case 4:
4260         used = number * sizeof(PRINTER_INFO_4W);
4261         break;
4262     case 5:
4263         used = number * sizeof(PRINTER_INFO_5W);
4264         break;
4265
4266     default:
4267         SetLastError(ERROR_INVALID_LEVEL);
4268         RegCloseKey(hkeyPrinters);
4269         return FALSE;
4270     }
4271     pi = (used <= cbBuf) ? lpbPrinters : NULL;
4272
4273     for(i = 0; i < number; i++) {
4274         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) !=
4275            ERROR_SUCCESS) {
4276             ERR("Can't enum key number %d\n", i);
4277             RegCloseKey(hkeyPrinters);
4278             return FALSE;
4279         }
4280         TRACE("Printer %d is %s\n", i, debugstr_w(PrinterName));
4281         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
4282            ERROR_SUCCESS) {
4283             ERR("Can't open key %s\n", debugstr_w(PrinterName));
4284             RegCloseKey(hkeyPrinters);
4285             return FALSE;
4286         }
4287
4288         if(cbBuf > used) {
4289             buf = lpbPrinters + used;
4290             left = cbBuf - used;
4291         } else {
4292             buf = NULL;
4293             left = 0;
4294         }
4295
4296         switch(dwLevel) {
4297         case 1:
4298             WINSPOOL_GetPrinter_1(hkeyPrinter, (PRINTER_INFO_1W *)pi, buf,
4299                                   left, &needed, unicode);
4300             used += needed;
4301             if(pi) pi += sizeof(PRINTER_INFO_1W);
4302             break;
4303         case 2:
4304             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
4305                                   left, &needed, unicode);
4306             used += needed;
4307             if(pi) pi += sizeof(PRINTER_INFO_2W);
4308             break;
4309         case 4:
4310             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
4311                                   left, &needed, unicode);
4312             used += needed;
4313             if(pi) pi += sizeof(PRINTER_INFO_4W);
4314             break;
4315         case 5:
4316             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
4317                                   left, &needed, unicode);
4318             used += needed;
4319             if(pi) pi += sizeof(PRINTER_INFO_5W);
4320             break;
4321         default:
4322             ERR("Shouldn't be here!\n");
4323             RegCloseKey(hkeyPrinter);
4324             RegCloseKey(hkeyPrinters);
4325             return FALSE;
4326         }
4327         RegCloseKey(hkeyPrinter);
4328     }
4329     RegCloseKey(hkeyPrinters);
4330
4331     if(lpdwNeeded)
4332         *lpdwNeeded = used;
4333
4334     if(used > cbBuf) {
4335         if(lpbPrinters)
4336             memset(lpbPrinters, 0, cbBuf);
4337         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4338         return FALSE;
4339     }
4340     if(lpdwReturned)
4341         *lpdwReturned = number;
4342     SetLastError(ERROR_SUCCESS);
4343     return TRUE;
4344 }
4345
4346
4347 /******************************************************************
4348  *              EnumPrintersW        [WINSPOOL.@]
4349  *
4350  *    Enumerates the available printers, print servers and print
4351  *    providers, depending on the specified flags, name and level.
4352  *
4353  * RETURNS:
4354  *
4355  *    If level is set to 1:
4356  *      Returns an array of PRINTER_INFO_1 data structures in the
4357  *      lpbPrinters buffer.
4358  *
4359  *    If level is set to 2:
4360  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4361  *      Returns an array of PRINTER_INFO_2 data structures in the
4362  *      lpbPrinters buffer. Note that according to MSDN also an
4363  *      OpenPrinter should be performed on every remote printer.
4364  *
4365  *    If level is set to 4 (officially WinNT only):
4366  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4367  *      Fast: Only the registry is queried to retrieve printer names,
4368  *      no connection to the driver is made.
4369  *      Returns an array of PRINTER_INFO_4 data structures in the
4370  *      lpbPrinters buffer.
4371  *
4372  *    If level is set to 5 (officially WinNT4/Win9x only):
4373  *      Fast: Only the registry is queried to retrieve printer names,
4374  *      no connection to the driver is made.
4375  *      Returns an array of PRINTER_INFO_5 data structures in the
4376  *      lpbPrinters buffer.
4377  *
4378  *    If level set to 3 or 6+:
4379  *          returns zero (failure!)
4380  *
4381  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
4382  *    for information.
4383  *
4384  * BUGS:
4385  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
4386  *    - Only levels 2, 4 and 5 are implemented at the moment.
4387  *    - 16-bit printer drivers are not enumerated.
4388  *    - Returned amount of bytes used/needed does not match the real Windoze
4389  *      implementation (as in this implementation, all strings are part
4390  *      of the buffer, whereas Win32 keeps them somewhere else)
4391  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
4392  *
4393  * NOTE:
4394  *    - In a regular Wine installation, no registry settings for printers
4395  *      exist, which makes this function return an empty list.
4396  */
4397 BOOL  WINAPI EnumPrintersW(
4398                 DWORD dwType,        /* [in] Types of print objects to enumerate */
4399                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
4400                 DWORD dwLevel,       /* [in] type of printer info structure */
4401                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
4402                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
4403                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
4404                 LPDWORD lpdwReturned /* [out] number of entries returned */
4405                 )
4406 {
4407     return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
4408                                  lpdwNeeded, lpdwReturned, TRUE);
4409 }
4410
4411 /******************************************************************
4412  * EnumPrintersA    [WINSPOOL.@]
4413  *
4414  * See EnumPrintersW
4415  *
4416  */
4417 BOOL WINAPI EnumPrintersA(DWORD flags, LPSTR pName, DWORD level, LPBYTE pPrinters,
4418                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4419 {
4420     BOOL ret;
4421     UNICODE_STRING pNameU;
4422     LPWSTR pNameW;
4423     LPBYTE pPrintersW;
4424
4425     TRACE("(0x%x, %s, %u, %p, %d, %p, %p)\n", flags, debugstr_a(pName), level,
4426                                               pPrinters, cbBuf, pcbNeeded, pcReturned);
4427
4428     pNameW = asciitounicode(&pNameU, pName);
4429
4430     /* Request a buffer with a size, that is big enough for EnumPrintersW.
4431        MS Office need this */
4432     pPrintersW = (pPrinters && cbBuf) ? HeapAlloc(GetProcessHeap(), 0, cbBuf) : NULL;
4433
4434     ret = EnumPrintersW(flags, pNameW, level, pPrintersW, cbBuf, pcbNeeded, pcReturned);
4435
4436     RtlFreeUnicodeString(&pNameU);
4437     if (ret) {
4438         convert_printerinfo_W_to_A(pPrinters, pPrintersW, level, *pcbNeeded, *pcReturned);
4439     }
4440     HeapFree(GetProcessHeap(), 0, pPrintersW);
4441     return ret;
4442 }
4443
4444 /*****************************************************************************
4445  *          WINSPOOL_GetDriverInfoFromReg [internal]
4446  *
4447  *    Enters the information from the registry into the DRIVER_INFO struct
4448  *
4449  * RETURNS
4450  *    zero if the printer driver does not exist in the registry
4451  *    (only if Level > 1) otherwise nonzero
4452  */
4453 static BOOL WINSPOOL_GetDriverInfoFromReg(
4454                             HKEY    hkeyDrivers,
4455                             LPWSTR  DriverName,
4456                             const printenv_t * env,
4457                             DWORD   Level,
4458                             LPBYTE  ptr,            /* DRIVER_INFO */
4459                             LPBYTE  pDriverStrings, /* strings buffer */
4460                             DWORD   cbBuf,          /* size of string buffer */
4461                             LPDWORD pcbNeeded,      /* space needed for str. */
4462                             BOOL    unicode)        /* type of strings */
4463 {
4464     DWORD  size, tmp;
4465     HKEY   hkeyDriver;
4466     WCHAR  driverdir[MAX_PATH];
4467     DWORD  dirlen;
4468     LPBYTE strPtr = pDriverStrings;
4469     LPDRIVER_INFO_8W di = (LPDRIVER_INFO_8W) ptr;
4470
4471     TRACE("(%p, %s, %p, %d, %p, %p, %d, %d)\n", hkeyDrivers,
4472           debugstr_w(DriverName), env,
4473           Level, di, pDriverStrings, cbBuf, unicode);
4474
4475     if (di) ZeroMemory(di, di_sizeof[Level]);
4476
4477     if (unicode) {
4478         *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
4479         if (*pcbNeeded <= cbBuf)
4480            strcpyW((LPWSTR)strPtr, DriverName);
4481     }
4482     else
4483     {
4484         *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0, NULL, NULL);
4485         if (*pcbNeeded <= cbBuf)
4486             WideCharToMultiByte(CP_ACP, 0, DriverName, -1, (LPSTR)strPtr, *pcbNeeded, NULL, NULL);
4487     }
4488
4489     /* pName for level 1 has a different offset! */
4490     if (Level == 1) {
4491        if (di) ((LPDRIVER_INFO_1W) di)->pName = (LPWSTR) strPtr;
4492        return TRUE;
4493     }
4494
4495     /* .cVersion and .pName for level > 1 */
4496     if (di) {
4497         di->cVersion = env->driverversion;
4498         di->pName = (LPWSTR) strPtr;
4499         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4500     }
4501
4502     /* Reserve Space for the largest subdir and a Backslash*/
4503     size = sizeof(driverdir) - sizeof(Version3_SubdirW) - sizeof(WCHAR);
4504     if (!GetPrinterDriverDirectoryW(NULL, (LPWSTR) env->envname, 1, (LPBYTE) driverdir, size, &size)) {
4505         /* Should never Fail */
4506         return FALSE;
4507     }
4508     lstrcatW(driverdir, env->versionsubdir);
4509     lstrcatW(driverdir, backslashW);
4510
4511     /* dirlen must not include the terminating zero */
4512     dirlen = (unicode) ? lstrlenW(driverdir) * sizeof(WCHAR) :
4513              WideCharToMultiByte(CP_ACP, 0, driverdir, -1, NULL, 0, NULL, NULL) -1;
4514
4515     if (!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
4516         ERR("Can't find driver %s in registry\n", debugstr_w(DriverName));
4517         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
4518         return FALSE;
4519     }
4520
4521     /* pEnvironment */
4522     if (unicode)
4523         size = (lstrlenW(env->envname) + 1) * sizeof(WCHAR);
4524     else
4525         size = WideCharToMultiByte(CP_ACP, 0, env->envname, -1, NULL, 0, NULL, NULL);
4526
4527     *pcbNeeded += size;
4528     if (*pcbNeeded <= cbBuf) {
4529         if (unicode) {
4530             lstrcpyW((LPWSTR)strPtr, env->envname);
4531         }
4532         else
4533         {
4534             WideCharToMultiByte(CP_ACP, 0, env->envname, -1, (LPSTR)strPtr, size, NULL, NULL);
4535         }
4536         if (di) di->pEnvironment = (LPWSTR)strPtr;
4537         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4538     }
4539
4540     /* .pDriverPath is the Graphics rendering engine.
4541         The full Path is required to avoid a crash in some apps */
4542     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, 0, &size, unicode)) {
4543         *pcbNeeded += size;
4544         if (*pcbNeeded <= cbBuf)
4545             get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, size, &tmp, unicode);
4546
4547         if (di) di->pDriverPath = (LPWSTR)strPtr;
4548         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4549     }
4550
4551     /* .pDataFile: For postscript-drivers, this is the ppd-file */
4552     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, 0, &size, unicode)) {
4553         *pcbNeeded += size;
4554         if (*pcbNeeded <= cbBuf)
4555             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, size, &size, unicode);
4556
4557         if (di) di->pDataFile = (LPWSTR)strPtr;
4558         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4559     }
4560
4561     /* .pConfigFile is the Driver user Interface */
4562     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, 0, &size, unicode)) {
4563         *pcbNeeded += size;
4564         if (*pcbNeeded <= cbBuf)
4565             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, size, &size, unicode);
4566
4567         if (di) di->pConfigFile = (LPWSTR)strPtr;
4568         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4569     }
4570
4571     if (Level == 2 ) {
4572         RegCloseKey(hkeyDriver);
4573         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4574         return TRUE;
4575     }
4576
4577     if (Level == 5 ) {
4578         RegCloseKey(hkeyDriver);
4579         FIXME("level 5: incomplete\n");
4580         return TRUE;
4581     }
4582
4583     /* .pHelpFile */
4584     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, 0, &size, unicode)) {
4585         *pcbNeeded += size;
4586         if (*pcbNeeded <= cbBuf)
4587             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, size, &size, unicode);
4588
4589         if (di) di->pHelpFile = (LPWSTR)strPtr;
4590         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4591     }
4592
4593     /* .pDependentFiles */
4594     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, 0, &size, unicode)) {
4595         *pcbNeeded += size;
4596         if (*pcbNeeded <= cbBuf)
4597             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, size, &size, unicode);
4598
4599         if (di) di->pDependentFiles = (LPWSTR)strPtr;
4600         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4601     }
4602     else if (GetVersion() & 0x80000000) {
4603         /* PowerPoint XP expects that pDependentFiles is always valid on win9x */
4604         size = 2 * ((unicode) ? sizeof(WCHAR) : 1);
4605         *pcbNeeded += size;
4606         if ((*pcbNeeded <= cbBuf) && strPtr) ZeroMemory(strPtr, size);
4607
4608         if (di) di->pDependentFiles = (LPWSTR)strPtr;
4609         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4610     }
4611
4612     /* .pMonitorName is the optional Language Monitor */
4613     if (WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size, unicode)) {
4614         *pcbNeeded += size;
4615         if (*pcbNeeded <= cbBuf)
4616             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, size, &size, unicode);
4617
4618         if (di) di->pMonitorName = (LPWSTR)strPtr;
4619         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4620     }
4621
4622     /* .pDefaultDataType */
4623     if (WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size, unicode)) {
4624         *pcbNeeded += size;
4625         if(*pcbNeeded <= cbBuf)
4626             WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, size, &size, unicode);
4627
4628         if (di) di->pDefaultDataType = (LPWSTR)strPtr;
4629         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4630     }
4631
4632     if (Level == 3 ) {
4633         RegCloseKey(hkeyDriver);
4634         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4635         return TRUE;
4636     }
4637
4638     /* .pszzPreviousNames */
4639     if (WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, 0, &size, unicode)) {
4640         *pcbNeeded += size;
4641         if(*pcbNeeded <= cbBuf)
4642             WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, size, &size, unicode);
4643
4644         if (di) di->pszzPreviousNames = (LPWSTR)strPtr;
4645         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4646     }
4647
4648     if (Level == 4 ) {
4649         RegCloseKey(hkeyDriver);
4650         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4651         return TRUE;
4652     }
4653
4654     /* support is missing, but not important enough for a FIXME */
4655     TRACE("%s: DriverDate + DriverVersion not supported\n", debugstr_w(DriverName));
4656
4657     /* .pszMfgName */
4658     if (WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, 0, &size, unicode)) {
4659         *pcbNeeded += size;
4660         if(*pcbNeeded <= cbBuf)
4661             WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, size, &size, unicode);
4662
4663         if (di) di->pszMfgName = (LPWSTR)strPtr;
4664         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4665     }
4666
4667     /* .pszOEMUrl */
4668     if (WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, 0, &size, unicode)) {
4669         *pcbNeeded += size;
4670         if(*pcbNeeded <= cbBuf)
4671             WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, size, &size, unicode);
4672
4673         if (di) di->pszOEMUrl = (LPWSTR)strPtr;
4674         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4675     }
4676
4677     /* .pszHardwareID */
4678     if (WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, 0, &size, unicode)) {
4679         *pcbNeeded += size;
4680         if(*pcbNeeded <= cbBuf)
4681             WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, size, &size, unicode);
4682
4683         if (di) di->pszHardwareID = (LPWSTR)strPtr;
4684         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4685     }
4686
4687     /* .pszProvider */
4688     if (WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, 0, &size, unicode)) {
4689         *pcbNeeded += size;
4690         if(*pcbNeeded <= cbBuf)
4691             WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, size, &size, unicode);
4692
4693         if (di) di->pszProvider = (LPWSTR)strPtr;
4694         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4695     }
4696
4697     if (Level == 6 ) {
4698         RegCloseKey(hkeyDriver);
4699         return TRUE;
4700     }
4701
4702     /* support is missing, but not important enough for a FIXME */
4703     TRACE("level 8: incomplete\n");
4704
4705     TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4706     RegCloseKey(hkeyDriver);
4707     return TRUE;
4708 }
4709
4710 /*****************************************************************************
4711  *          WINSPOOL_GetPrinterDriver
4712  */
4713 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPCWSTR pEnvironment,
4714                                       DWORD Level, LPBYTE pDriverInfo,
4715                                       DWORD cbBuf, LPDWORD pcbNeeded,
4716                                       BOOL unicode)
4717 {
4718     LPCWSTR name;
4719     WCHAR DriverName[100];
4720     DWORD ret, type, size, needed = 0;
4721     LPBYTE ptr = NULL;
4722     HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
4723     const printenv_t * env;
4724
4725     TRACE("(%p,%s,%d,%p,%d,%p)\n",hPrinter,debugstr_w(pEnvironment),
4726           Level,pDriverInfo,cbBuf, pcbNeeded);
4727
4728
4729     if (!(name = get_opened_printer_name(hPrinter))) {
4730         SetLastError(ERROR_INVALID_HANDLE);
4731         return FALSE;
4732     }
4733
4734     if (Level < 1 || Level == 7 || Level > 8) {
4735         SetLastError(ERROR_INVALID_LEVEL);
4736         return FALSE;
4737     }
4738
4739     env = validate_envW(pEnvironment);
4740     if (!env) return FALSE;     /* SetLastError() is in validate_envW */
4741
4742     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4743        ERROR_SUCCESS) {
4744         ERR("Can't create Printers key\n");
4745         return FALSE;
4746     }
4747     if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
4748        != ERROR_SUCCESS) {
4749         ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4750         RegCloseKey(hkeyPrinters);
4751         SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4752         return FALSE;
4753     }
4754     size = sizeof(DriverName);
4755     DriverName[0] = 0;
4756     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
4757                            (LPBYTE)DriverName, &size);
4758     RegCloseKey(hkeyPrinter);
4759     RegCloseKey(hkeyPrinters);
4760     if(ret != ERROR_SUCCESS) {
4761         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
4762         return FALSE;
4763     }
4764
4765     hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
4766     if(!hkeyDrivers) {
4767         ERR("Can't create Drivers key\n");
4768         return FALSE;
4769     }
4770
4771     size = di_sizeof[Level];
4772     if ((size <= cbBuf) && pDriverInfo)
4773         ptr = pDriverInfo + size;
4774
4775     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
4776                          env, Level, pDriverInfo, ptr,
4777                          (cbBuf < size) ? 0 : cbBuf - size,
4778                          &needed, unicode)) {
4779             RegCloseKey(hkeyDrivers);
4780             return FALSE;
4781     }
4782
4783     RegCloseKey(hkeyDrivers);
4784
4785     if(pcbNeeded) *pcbNeeded = size + needed;
4786     TRACE("buffer space %d required %d\n", cbBuf, size + needed);
4787     if(cbBuf >= needed) return TRUE;
4788     SetLastError(ERROR_INSUFFICIENT_BUFFER);
4789     return FALSE;
4790 }
4791
4792 /*****************************************************************************
4793  *          GetPrinterDriverA  [WINSPOOL.@]
4794  */
4795 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
4796                               DWORD Level, LPBYTE pDriverInfo,
4797                               DWORD cbBuf, LPDWORD pcbNeeded)
4798 {
4799     BOOL ret;
4800     UNICODE_STRING pEnvW;
4801     PWSTR pwstrEnvW;
4802     
4803     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
4804     ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
4805                                     cbBuf, pcbNeeded, FALSE);
4806     RtlFreeUnicodeString(&pEnvW);
4807     return ret;
4808 }
4809 /*****************************************************************************
4810  *          GetPrinterDriverW  [WINSPOOL.@]
4811  */
4812 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
4813                                   DWORD Level, LPBYTE pDriverInfo,
4814                                   DWORD cbBuf, LPDWORD pcbNeeded)
4815 {
4816     return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
4817                                      pDriverInfo, cbBuf, pcbNeeded, TRUE);
4818 }
4819
4820 /*****************************************************************************
4821  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
4822  *
4823  * Return the PATH for the Printer-Drivers (UNICODE)
4824  *
4825  * PARAMS
4826  *   pName            [I] Servername (NT only) or NULL (local Computer)
4827  *   pEnvironment     [I] Printing-Environment (see below) or NULL (Default)
4828  *   Level            [I] Structure-Level (must be 1)
4829  *   pDriverDirectory [O] PTR to Buffer that receives the Result
4830  *   cbBuf            [I] Size of Buffer at pDriverDirectory
4831  *   pcbNeeded        [O] PTR to DWORD that receives the size in Bytes used / 
4832  *                        required for pDriverDirectory
4833  *
4834  * RETURNS
4835  *   Success: TRUE  and in pcbNeeded the Bytes used in pDriverDirectory
4836  *   Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
4837  *   if cbBuf is too small
4838  * 
4839  *   Native Values returned in pDriverDirectory on Success:
4840  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\DRIVERS\\w32x86" 
4841  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\DRIVERS\\win40" 
4842  *|  win9x(Windows 4.0):  "%winsysdir%" 
4843  *
4844  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
4845  *
4846  * FIXME
4847  *-  Only NULL or "" is supported for pName
4848  *
4849  */
4850 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
4851                                        DWORD Level, LPBYTE pDriverDirectory,
4852                                        DWORD cbBuf, LPDWORD pcbNeeded)
4853 {
4854     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), 
4855           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4856
4857     if ((backend == NULL)  && !load_backend()) return FALSE;
4858
4859     if (Level != 1) {
4860         /* (Level != 1) is ignored in win9x */
4861         SetLastError(ERROR_INVALID_LEVEL);
4862         return FALSE;
4863     }
4864     if (pcbNeeded == NULL) {
4865         /* (pcbNeeded == NULL) is ignored in win9x */
4866         SetLastError(RPC_X_NULL_REF_POINTER);
4867         return FALSE;
4868     }
4869
4870     return backend->fpGetPrinterDriverDirectory(pName, pEnvironment, Level,
4871                                                 pDriverDirectory, cbBuf, pcbNeeded);
4872
4873 }
4874
4875
4876 /*****************************************************************************
4877  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
4878  *
4879  * Return the PATH for the Printer-Drivers (ANSI)
4880  *
4881  * See GetPrinterDriverDirectoryW.
4882  *
4883  * NOTES
4884  * On NT, pDriverDirectory need the same Size as the Unicode-Version
4885  *
4886  */
4887 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
4888                                        DWORD Level, LPBYTE pDriverDirectory,
4889                                        DWORD cbBuf, LPDWORD pcbNeeded)
4890 {
4891     UNICODE_STRING nameW, environmentW;
4892     BOOL ret;
4893     DWORD pcbNeededW;
4894     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
4895     WCHAR *driverDirectoryW = NULL;
4896
4897     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(pName), 
4898           debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4899  
4900     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
4901
4902     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
4903     else nameW.Buffer = NULL;
4904     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
4905     else environmentW.Buffer = NULL;
4906
4907     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
4908                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
4909     if (ret) {
4910         DWORD needed;
4911         needed =  WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
4912                                    (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
4913         if(pcbNeeded)
4914             *pcbNeeded = needed;
4915         ret = (needed <= cbBuf) ? TRUE : FALSE;
4916     } else 
4917         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
4918
4919     TRACE("required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
4920
4921     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
4922     RtlFreeUnicodeString(&environmentW);
4923     RtlFreeUnicodeString(&nameW);
4924
4925     return ret;
4926 }
4927
4928 /*****************************************************************************
4929  *          AddPrinterDriverA  [WINSPOOL.@]
4930  *
4931  * See AddPrinterDriverW.
4932  *
4933  */
4934 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
4935 {
4936     TRACE("(%s, %d, %p)\n", debugstr_a(pName), level, pDriverInfo);
4937     return AddPrinterDriverExA(pName, level, pDriverInfo, APD_COPY_NEW_FILES);
4938 }
4939
4940 /******************************************************************************
4941  *  AddPrinterDriverW (WINSPOOL.@)
4942  *
4943  * Install a Printer Driver
4944  *
4945  * PARAMS
4946  *  pName           [I] Servername or NULL (local Computer)
4947  *  level           [I] Level for the supplied DRIVER_INFO_*W struct
4948  *  pDriverInfo     [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
4949  *
4950  * RESULTS
4951  *  Success: TRUE
4952  *  Failure: FALSE
4953  *
4954  */
4955 BOOL WINAPI AddPrinterDriverW(LPWSTR pName, DWORD level, LPBYTE pDriverInfo)
4956 {
4957     TRACE("(%s, %d, %p)\n", debugstr_w(pName), level, pDriverInfo);
4958     return AddPrinterDriverExW(pName, level, pDriverInfo, APD_COPY_NEW_FILES);
4959 }
4960
4961 /*****************************************************************************
4962  *          AddPrintProcessorA  [WINSPOOL.@]
4963  */
4964 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
4965                                LPSTR pPrintProcessorName)
4966 {
4967     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
4968           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
4969     return FALSE;
4970 }
4971
4972 /*****************************************************************************
4973  *          AddPrintProcessorW  [WINSPOOL.@]
4974  */
4975 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
4976                                LPWSTR pPrintProcessorName)
4977 {
4978     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
4979           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
4980     return FALSE;
4981 }
4982
4983 /*****************************************************************************
4984  *          AddPrintProvidorA  [WINSPOOL.@]
4985  */
4986 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
4987 {
4988     FIXME("(%s,0x%08x,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
4989     return FALSE;
4990 }
4991
4992 /*****************************************************************************
4993  *          AddPrintProvidorW  [WINSPOOL.@]
4994  */
4995 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
4996 {
4997     FIXME("(%s,0x%08x,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
4998     return FALSE;
4999 }
5000
5001 /*****************************************************************************
5002  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
5003  */
5004 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
5005                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
5006 {
5007     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
5008           pDevModeOutput, pDevModeInput);
5009     return 0;
5010 }
5011
5012 /*****************************************************************************
5013  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
5014  */
5015 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
5016                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
5017 {
5018     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
5019           pDevModeOutput, pDevModeInput);
5020     return 0;
5021 }
5022
5023 /*****************************************************************************
5024  *          PrinterProperties  [WINSPOOL.@]
5025  *
5026  *     Displays a dialog to set the properties of the printer.
5027  *
5028  * RETURNS
5029  *     nonzero on success or zero on failure
5030  *
5031  * BUGS
5032  *         implemented as stub only
5033  */
5034 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
5035                               HANDLE hPrinter /* [in] handle to printer object */
5036 ){
5037     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
5038     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5039     return FALSE;
5040 }
5041
5042 /*****************************************************************************
5043  *          EnumJobsA [WINSPOOL.@]
5044  *
5045  */
5046 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
5047                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
5048                       LPDWORD pcReturned)
5049 {
5050     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
5051         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
5052     );
5053     if(pcbNeeded) *pcbNeeded = 0;
5054     if(pcReturned) *pcReturned = 0;
5055     return FALSE;
5056 }
5057
5058
5059 /*****************************************************************************
5060  *          EnumJobsW [WINSPOOL.@]
5061  *
5062  */
5063 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
5064                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
5065                       LPDWORD pcReturned)
5066 {
5067     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
5068         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
5069     );
5070     if(pcbNeeded) *pcbNeeded = 0;
5071     if(pcReturned) *pcReturned = 0;
5072     return FALSE;
5073 }
5074
5075 /*****************************************************************************
5076  *          WINSPOOL_EnumPrinterDrivers [internal]
5077  *
5078  *    Delivers information about all printer drivers installed on the
5079  *    localhost or a given server
5080  *
5081  * RETURNS
5082  *    nonzero on success or zero on failure. If the buffer for the returned
5083  *    information is too small the function will return an error
5084  *
5085  * BUGS
5086  *    - only implemented for localhost, foreign hosts will return an error
5087  */
5088 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPCWSTR pEnvironment,
5089                                         DWORD Level, LPBYTE pDriverInfo,
5090                                         DWORD cbBuf, LPDWORD pcbNeeded,
5091                                         LPDWORD pcReturned, BOOL unicode)
5092
5093 {   HKEY  hkeyDrivers;
5094     DWORD i, needed, number = 0, size = 0;
5095     WCHAR DriverNameW[255];
5096     PBYTE ptr;
5097     const printenv_t * env;
5098
5099     TRACE("%s,%s,%d,%p,%d,%d\n",
5100           debugstr_w(pName), debugstr_w(pEnvironment),
5101           Level, pDriverInfo, cbBuf, unicode);
5102
5103     /* check for local drivers */
5104     if((pName) && (pName[0])) {
5105         FIXME("remote drivers (%s) not supported!\n", debugstr_w(pName));
5106         SetLastError(ERROR_ACCESS_DENIED);
5107         return FALSE;
5108     }
5109
5110     env = validate_envW(pEnvironment);
5111     if (!env) return FALSE;     /* SetLastError() is in validate_envW */
5112
5113     /* check input parameter */
5114     if ((Level < 1) || (Level == 7) || (Level > 8)) {
5115         SetLastError(ERROR_INVALID_LEVEL);
5116         return FALSE;
5117     }
5118
5119     if ((pcbNeeded == NULL) || (pcReturned == NULL)) {
5120         SetLastError(RPC_X_NULL_REF_POINTER);
5121         return FALSE;
5122     }
5123
5124     /* initialize return values */
5125     if(pDriverInfo)
5126         memset( pDriverInfo, 0, cbBuf);
5127     *pcbNeeded  = 0;
5128     *pcReturned = 0;
5129
5130     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
5131     if(!hkeyDrivers) {
5132         ERR("Can't open Drivers key\n");
5133         return FALSE;
5134     }
5135
5136     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
5137                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
5138         RegCloseKey(hkeyDrivers);
5139         ERR("Can't query Drivers key\n");
5140         return FALSE;
5141     }
5142     TRACE("Found %d Drivers\n", number);
5143
5144     /* get size of single struct
5145      * unicode and ascii structure have the same size
5146      */
5147     size = di_sizeof[Level];
5148
5149     /* calculate required buffer size */
5150     *pcbNeeded = size * number;
5151
5152     for( i = 0,  ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
5153          i < number;
5154          i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
5155         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW)/sizeof(DriverNameW[0]))
5156                        != ERROR_SUCCESS) {
5157             ERR("Can't enum key number %d\n", i);
5158             RegCloseKey(hkeyDrivers);
5159             return FALSE;
5160         }
5161         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
5162                          env, Level, ptr,
5163                          (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
5164                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
5165                          &needed, unicode)) {
5166             RegCloseKey(hkeyDrivers);
5167             return FALSE;
5168         }
5169         (*pcbNeeded) += needed;
5170     }
5171
5172     RegCloseKey(hkeyDrivers);
5173
5174     if(cbBuf < *pcbNeeded){
5175         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5176         return FALSE;
5177     }
5178
5179     *pcReturned = number;
5180     return TRUE;
5181 }
5182
5183 /*****************************************************************************
5184  *          EnumPrinterDriversW  [WINSPOOL.@]
5185  *
5186  *    see function EnumPrinterDrivers for RETURNS, BUGS
5187  */
5188 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5189                                 LPBYTE pDriverInfo, DWORD cbBuf,
5190                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
5191 {
5192     return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
5193                                        cbBuf, pcbNeeded, pcReturned, TRUE);
5194 }
5195
5196 /*****************************************************************************
5197  *          EnumPrinterDriversA  [WINSPOOL.@]
5198  *
5199  *    see function EnumPrinterDrivers for RETURNS, BUGS
5200  */
5201 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
5202                                 LPBYTE pDriverInfo, DWORD cbBuf,
5203                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
5204 {   BOOL ret;
5205     UNICODE_STRING pNameW, pEnvironmentW;
5206     PWSTR pwstrNameW, pwstrEnvironmentW;
5207
5208     pwstrNameW = asciitounicode(&pNameW, pName);
5209     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
5210
5211     ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
5212                                       Level, pDriverInfo, cbBuf, pcbNeeded,
5213                                       pcReturned, FALSE);
5214     RtlFreeUnicodeString(&pNameW);
5215     RtlFreeUnicodeString(&pEnvironmentW);
5216
5217     return ret;
5218 }
5219
5220 /******************************************************************************
5221  *              EnumPortsA   (WINSPOOL.@)
5222  *
5223  * See EnumPortsW.
5224  *
5225  */
5226 BOOL WINAPI EnumPortsA( LPSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
5227                         LPDWORD pcbNeeded, LPDWORD pcReturned)
5228 {
5229     BOOL    res;
5230     LPBYTE  bufferW = NULL;
5231     LPWSTR  nameW = NULL;
5232     DWORD   needed = 0;
5233     DWORD   numentries = 0;
5234     INT     len;
5235
5236     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pPorts,
5237           cbBuf, pcbNeeded, pcReturned);
5238
5239     /* convert servername to unicode */
5240     if (pName) {
5241         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5242         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5243         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5244     }
5245     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the Ports */
5246     needed = cbBuf * sizeof(WCHAR);    
5247     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
5248     res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5249
5250     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
5251         if (pcbNeeded) needed = *pcbNeeded;
5252         /* HeapReAlloc return NULL, when bufferW was NULL */
5253         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
5254                               HeapAlloc(GetProcessHeap(), 0, needed);
5255
5256         /* Try again with the large Buffer */
5257         res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5258     }
5259     needed = pcbNeeded ? *pcbNeeded : 0;
5260     numentries = pcReturned ? *pcReturned : 0;
5261
5262     /*
5263        W2k require the buffersize from EnumPortsW also for EnumPortsA.
5264        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
5265      */
5266     if (res) {
5267         /* EnumPortsW collected all Data. Parse them to calculate ANSI-Size */
5268         DWORD   entrysize = 0;
5269         DWORD   index;
5270         LPSTR   ptr;
5271         LPPORT_INFO_2W pi2w;
5272         LPPORT_INFO_2A pi2a;
5273
5274         needed = 0;
5275         entrysize = (Level == 1) ? sizeof(PORT_INFO_1A) : sizeof(PORT_INFO_2A);
5276
5277         /* First pass: calculate the size for all Entries */
5278         pi2w = (LPPORT_INFO_2W) bufferW;
5279         pi2a = (LPPORT_INFO_2A) pPorts;
5280         index = 0;
5281         while (index < numentries) {
5282             index++;
5283             needed += entrysize;    /* PORT_INFO_?A */
5284             TRACE("%p: parsing #%d (%s)\n", pi2w, index, debugstr_w(pi2w->pPortName));
5285
5286             needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5287                                             NULL, 0, NULL, NULL);
5288             if (Level > 1) {
5289                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5290                                                 NULL, 0, NULL, NULL);
5291                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5292                                                 NULL, 0, NULL, NULL);
5293             }
5294             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5295             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5296             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5297         }
5298
5299         /* check for errors and quit on failure */
5300         if (cbBuf < needed) {
5301             SetLastError(ERROR_INSUFFICIENT_BUFFER);
5302             res = FALSE;
5303             goto cleanup;
5304         }
5305         len = entrysize * numentries;       /* room for all PORT_INFO_?A */
5306         ptr = (LPSTR) &pPorts[len];         /* room for strings */
5307         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
5308         pi2w = (LPPORT_INFO_2W) bufferW;
5309         pi2a = (LPPORT_INFO_2A) pPorts;
5310         index = 0;
5311         /* Second Pass: Fill the User Buffer (if we have one) */
5312         while ((index < numentries) && pPorts) {
5313             index++;
5314             TRACE("%p: writing PORT_INFO_%dA #%d\n", pi2a, Level, index);
5315             pi2a->pPortName = ptr;
5316             len = WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5317                                             ptr, cbBuf , NULL, NULL);
5318             ptr += len;
5319             cbBuf -= len;
5320             if (Level > 1) {
5321                 pi2a->pMonitorName = ptr;
5322                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5323                                             ptr, cbBuf, NULL, NULL);
5324                 ptr += len;
5325                 cbBuf -= len;
5326
5327                 pi2a->pDescription = ptr;
5328                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5329                                             ptr, cbBuf, NULL, NULL);
5330                 ptr += len;
5331                 cbBuf -= len;
5332
5333                 pi2a->fPortType = pi2w->fPortType;
5334                 pi2a->Reserved = 0;              /* documented: "must be zero" */
5335                 
5336             }
5337             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5338             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5339             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5340         }
5341     }
5342
5343 cleanup:
5344     if (pcbNeeded)  *pcbNeeded = needed;
5345     if (pcReturned) *pcReturned = (res) ? numentries : 0;
5346
5347     HeapFree(GetProcessHeap(), 0, nameW);
5348     HeapFree(GetProcessHeap(), 0, bufferW);
5349
5350     TRACE("returning %d with %d (%d byte for %d of %d entries)\n", 
5351             (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5352
5353     return (res);
5354
5355 }
5356
5357 /******************************************************************************
5358  *      EnumPortsW   (WINSPOOL.@)
5359  *
5360  * Enumerate available Ports
5361  *
5362  * PARAMS
5363  *  pName      [I] Servername or NULL (local Computer)
5364  *  Level      [I] Structure-Level (1 or 2)
5365  *  pPorts     [O] PTR to Buffer that receives the Result
5366  *  cbBuf      [I] Size of Buffer at pPorts
5367  *  pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
5368  *  pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
5369  *
5370  * RETURNS
5371  *  Success: TRUE
5372  *  Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
5373  *
5374  */
5375 BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5376 {
5377
5378     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
5379           cbBuf, pcbNeeded, pcReturned);
5380
5381     if ((backend == NULL)  && !load_backend()) return FALSE;
5382
5383     /* Level is not checked in win9x */
5384     if (!Level || (Level > 2)) {
5385         WARN("level (%d) is ignored in win9x\n", Level);
5386         SetLastError(ERROR_INVALID_LEVEL);
5387         return FALSE;
5388     }
5389     if (!pcbNeeded || (!pPorts && (cbBuf > 0))) {
5390         SetLastError(RPC_X_NULL_REF_POINTER);
5391         return FALSE;
5392     }
5393
5394     return backend->fpEnumPorts(pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
5395 }
5396
5397 /******************************************************************************
5398  *              GetDefaultPrinterW   (WINSPOOL.@)
5399  *
5400  * FIXME
5401  *      This function must read the value from data 'device' of key
5402  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
5403  */
5404 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
5405 {
5406     BOOL  retval = TRUE;
5407     DWORD insize, len;
5408     WCHAR *buffer, *ptr;
5409
5410     if (!namesize)
5411     {
5412         SetLastError(ERROR_INVALID_PARAMETER);
5413         return FALSE;
5414     }
5415
5416     /* make the buffer big enough for the stuff from the profile/registry,
5417      * the content must fit into the local buffer to compute the correct
5418      * size even if the extern buffer is too small or not given.
5419      * (20 for ,driver,port) */
5420     insize = *namesize;
5421     len = max(100, (insize + 20));
5422     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
5423
5424     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
5425     {
5426         SetLastError (ERROR_FILE_NOT_FOUND);
5427         retval = FALSE;
5428         goto end;
5429     }
5430     TRACE("%s\n", debugstr_w(buffer));
5431
5432     if ((ptr = strchrW(buffer, ',')) == NULL)
5433     {
5434         SetLastError(ERROR_INVALID_NAME);
5435         retval = FALSE;
5436         goto end;
5437     }
5438
5439     *ptr = 0;
5440     *namesize = strlenW(buffer) + 1;
5441     if(!name || (*namesize > insize))
5442     {
5443         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5444         retval = FALSE;
5445         goto end;
5446     }
5447     strcpyW(name, buffer);
5448
5449 end:
5450     HeapFree( GetProcessHeap(), 0, buffer);
5451     return retval;
5452 }
5453
5454
5455 /******************************************************************************
5456  *              GetDefaultPrinterA   (WINSPOOL.@)
5457  */
5458 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
5459 {
5460     BOOL  retval = TRUE;
5461     DWORD insize = 0;
5462     WCHAR *bufferW = NULL;
5463
5464     if (!namesize)
5465     {
5466         SetLastError(ERROR_INVALID_PARAMETER);
5467         return FALSE;
5468     }
5469
5470     if(name && *namesize) {
5471         insize = *namesize;
5472         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
5473     }
5474
5475     if(!GetDefaultPrinterW( bufferW, namesize)) {
5476         retval = FALSE;
5477         goto end;
5478     }
5479
5480     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
5481                                     NULL, NULL);
5482     if (!*namesize)
5483     {
5484         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
5485         retval = FALSE;
5486     }
5487     TRACE("0x%08x/0x%08x:%s\n", *namesize, insize, debugstr_w(bufferW));
5488
5489 end:
5490     HeapFree( GetProcessHeap(), 0, bufferW);
5491     return retval;
5492 }
5493
5494
5495 /******************************************************************************
5496  *              SetDefaultPrinterW   (WINSPOOL.204)
5497  *
5498  * Set the Name of the Default Printer
5499  *
5500  * PARAMS
5501  *  pszPrinter [I] Name of the Printer or NULL
5502  *
5503  * RETURNS
5504  *  Success:    True
5505  *  Failure:    FALSE
5506  *
5507  * NOTES
5508  *  When the Parameter is NULL or points to an Empty String and
5509  *  a Default Printer was already present, then this Function changes nothing.
5510  *  Without a Default Printer and NULL (or an Empty String) as Parameter,
5511  *  the First enumerated local Printer is used.
5512  *
5513  */
5514 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
5515 {
5516
5517     TRACE("(%s)\n", debugstr_w(pszPrinter));
5518
5519     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5520     return FALSE;
5521 }
5522
5523 /******************************************************************************
5524  *              SetDefaultPrinterA   (WINSPOOL.202)
5525  *
5526  * See SetDefaultPrinterW.
5527  *
5528  */
5529 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
5530 {
5531
5532     TRACE("(%s)\n", debugstr_a(pszPrinter));
5533
5534     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5535     return FALSE;
5536 }
5537
5538
5539 /******************************************************************************
5540  *              SetPrinterDataExA   (WINSPOOL.@)
5541  */
5542 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5543                                LPCSTR pValueName, DWORD Type,
5544                                LPBYTE pData, DWORD cbData)
5545 {
5546     HKEY hkeyPrinter, hkeySubkey;
5547     DWORD ret;
5548
5549     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_a(pKeyName),
5550           debugstr_a(pValueName), Type, pData, cbData);
5551
5552     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5553        != ERROR_SUCCESS)
5554         return ret;
5555
5556     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5557        != ERROR_SUCCESS) {
5558         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
5559         RegCloseKey(hkeyPrinter);
5560         return ret;
5561     }
5562     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
5563     RegCloseKey(hkeySubkey);
5564     RegCloseKey(hkeyPrinter);
5565     return ret;
5566 }
5567
5568 /******************************************************************************
5569  *              SetPrinterDataExW   (WINSPOOL.@)
5570  */
5571 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5572                                LPCWSTR pValueName, DWORD Type,
5573                                LPBYTE pData, DWORD cbData)
5574 {
5575     HKEY hkeyPrinter, hkeySubkey;
5576     DWORD ret;
5577
5578     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_w(pKeyName),
5579           debugstr_w(pValueName), Type, pData, cbData);
5580
5581     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5582        != ERROR_SUCCESS)
5583         return ret;
5584
5585     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5586        != ERROR_SUCCESS) {
5587         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
5588         RegCloseKey(hkeyPrinter);
5589         return ret;
5590     }
5591     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
5592     RegCloseKey(hkeySubkey);
5593     RegCloseKey(hkeyPrinter);
5594     return ret;
5595 }
5596
5597 /******************************************************************************
5598  *              SetPrinterDataA   (WINSPOOL.@)
5599  */
5600 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
5601                                LPBYTE pData, DWORD cbData)
5602 {
5603     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
5604                              pData, cbData);
5605 }
5606
5607 /******************************************************************************
5608  *              SetPrinterDataW   (WINSPOOL.@)
5609  */
5610 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
5611                              LPBYTE pData, DWORD cbData)
5612 {
5613     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
5614                              pData, cbData);
5615 }
5616
5617 /******************************************************************************
5618  *              GetPrinterDataExA   (WINSPOOL.@)
5619  */
5620 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5621                                LPCSTR pValueName, LPDWORD pType,
5622                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5623 {
5624     HKEY hkeyPrinter, hkeySubkey;
5625     DWORD ret;
5626
5627     TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5628           debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
5629           pcbNeeded);
5630
5631     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5632        != ERROR_SUCCESS)
5633         return ret;
5634
5635     if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5636        != ERROR_SUCCESS) {
5637         WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
5638         RegCloseKey(hkeyPrinter);
5639         return ret;
5640     }
5641     *pcbNeeded = nSize;
5642     ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5643     RegCloseKey(hkeySubkey);
5644     RegCloseKey(hkeyPrinter);
5645     return ret;
5646 }
5647
5648 /******************************************************************************
5649  *              GetPrinterDataExW   (WINSPOOL.@)
5650  */
5651 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5652                                LPCWSTR pValueName, LPDWORD pType,
5653                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5654 {
5655     HKEY hkeyPrinter, hkeySubkey;
5656     DWORD ret;
5657
5658     TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5659           debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
5660           pcbNeeded);
5661
5662     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5663        != ERROR_SUCCESS)
5664         return ret;
5665
5666     if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5667        != ERROR_SUCCESS) {
5668         WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
5669         RegCloseKey(hkeyPrinter);
5670         return ret;
5671     }
5672     *pcbNeeded = nSize;
5673     ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5674     RegCloseKey(hkeySubkey);
5675     RegCloseKey(hkeyPrinter);
5676     return ret;
5677 }
5678
5679 /******************************************************************************
5680  *              GetPrinterDataA   (WINSPOOL.@)
5681  */
5682 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
5683                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5684 {
5685     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
5686                              pData, nSize, pcbNeeded);
5687 }
5688
5689 /******************************************************************************
5690  *              GetPrinterDataW   (WINSPOOL.@)
5691  */
5692 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
5693                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5694 {
5695     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
5696                              pData, nSize, pcbNeeded);
5697 }
5698
5699 /*******************************************************************************
5700  *              EnumPrinterDataExW      [WINSPOOL.@]
5701  */
5702 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5703                                 LPBYTE pEnumValues, DWORD cbEnumValues,
5704                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5705 {
5706     HKEY                    hkPrinter, hkSubKey;
5707     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
5708                             cbValueNameLen, cbMaxValueLen, cbValueLen,
5709                             cbBufSize, dwType;
5710     LPWSTR                  lpValueName;
5711     HANDLE                  hHeap;
5712     PBYTE                   lpValue;
5713     PPRINTER_ENUM_VALUESW   ppev;
5714
5715     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
5716
5717     if (pKeyName == NULL || *pKeyName == 0)
5718         return ERROR_INVALID_PARAMETER;
5719
5720     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
5721     if (ret != ERROR_SUCCESS)
5722     {
5723         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %i\n",
5724                 hPrinter, ret);
5725         return ret;
5726     }
5727
5728     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
5729     if (ret != ERROR_SUCCESS)
5730     {
5731         r = RegCloseKey (hkPrinter);
5732         if (r != ERROR_SUCCESS)
5733             WARN ("RegCloseKey returned %i\n", r);
5734         TRACE ("RegOpenKeyExW (%p, %s) returned %i\n", hPrinter,
5735                 debugstr_w (pKeyName), ret);
5736         return ret;
5737     }
5738
5739     ret = RegCloseKey (hkPrinter);
5740     if (ret != ERROR_SUCCESS)
5741     {
5742         ERR ("RegCloseKey returned %i\n", ret);
5743         r = RegCloseKey (hkSubKey);
5744         if (r != ERROR_SUCCESS)
5745             WARN ("RegCloseKey returned %i\n", r);
5746         return ret;
5747     }
5748
5749     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
5750             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
5751     if (ret != ERROR_SUCCESS)
5752     {
5753         r = RegCloseKey (hkSubKey);
5754         if (r != ERROR_SUCCESS)
5755             WARN ("RegCloseKey returned %i\n", r);
5756         TRACE ("RegQueryInfoKeyW (%p) returned %i\n", hkSubKey, ret);
5757         return ret;
5758     }
5759
5760     TRACE ("RegQueryInfoKeyW returned cValues = %i, cbMaxValueNameLen = %i, "
5761             "cbMaxValueLen = %i\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
5762
5763     if (cValues == 0)                   /* empty key */
5764     {
5765         r = RegCloseKey (hkSubKey);
5766         if (r != ERROR_SUCCESS)
5767             WARN ("RegCloseKey returned %i\n", r);
5768         *pcbEnumValues = *pnEnumValues = 0;
5769         return ERROR_SUCCESS;
5770     }
5771
5772     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
5773
5774     hHeap = GetProcessHeap ();
5775     if (hHeap == NULL)
5776     {
5777         ERR ("GetProcessHeap failed\n");
5778         r = RegCloseKey (hkSubKey);
5779         if (r != ERROR_SUCCESS)
5780             WARN ("RegCloseKey returned %i\n", r);
5781         return ERROR_OUTOFMEMORY;
5782     }
5783
5784     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
5785     if (lpValueName == NULL)
5786     {
5787         ERR ("Failed to allocate %i WCHARs from process heap\n", cbMaxValueNameLen);
5788         r = RegCloseKey (hkSubKey);
5789         if (r != ERROR_SUCCESS)
5790             WARN ("RegCloseKey returned %i\n", r);
5791         return ERROR_OUTOFMEMORY;
5792     }
5793
5794     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
5795     if (lpValue == NULL)
5796     {
5797         ERR ("Failed to allocate %i bytes from process heap\n", cbMaxValueLen);
5798         if (HeapFree (hHeap, 0, lpValueName) == 0)
5799             WARN ("HeapFree failed with code %i\n", GetLastError ());
5800         r = RegCloseKey (hkSubKey);
5801         if (r != ERROR_SUCCESS)
5802             WARN ("RegCloseKey returned %i\n", r);
5803         return ERROR_OUTOFMEMORY;
5804     }
5805
5806     TRACE ("pass 1: calculating buffer required for all names and values\n");
5807
5808     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
5809
5810     TRACE ("%i bytes required for %i headers\n", cbBufSize, cValues);
5811
5812     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5813     {
5814         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5815         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5816                 NULL, NULL, lpValue, &cbValueLen);
5817         if (ret != ERROR_SUCCESS)
5818         {
5819             if (HeapFree (hHeap, 0, lpValue) == 0)
5820                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5821             if (HeapFree (hHeap, 0, lpValueName) == 0)
5822                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5823             r = RegCloseKey (hkSubKey);
5824             if (r != ERROR_SUCCESS)
5825                 WARN ("RegCloseKey returned %i\n", r);
5826             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5827             return ret;
5828         }
5829
5830         TRACE ("%s [%i]: name needs %i WCHARs, data needs %i bytes\n",
5831                 debugstr_w (lpValueName), dwIndex,
5832                 cbValueNameLen + 1, cbValueLen);
5833
5834         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
5835         cbBufSize += cbValueLen;
5836     }
5837
5838     TRACE ("%i bytes required for all %i values\n", cbBufSize, cValues);
5839
5840     *pcbEnumValues = cbBufSize;
5841     *pnEnumValues = cValues;
5842
5843     if (cbEnumValues < cbBufSize)       /* buffer too small */
5844     {
5845         if (HeapFree (hHeap, 0, lpValue) == 0)
5846             WARN ("HeapFree failed with code %i\n", GetLastError ());
5847         if (HeapFree (hHeap, 0, lpValueName) == 0)
5848             WARN ("HeapFree failed with code %i\n", GetLastError ());
5849         r = RegCloseKey (hkSubKey);
5850         if (r != ERROR_SUCCESS)
5851             WARN ("RegCloseKey returned %i\n", r);
5852         TRACE ("%i byte buffer is not large enough\n", cbEnumValues);
5853         return ERROR_MORE_DATA;
5854     }
5855
5856     TRACE ("pass 2: copying all names and values to buffer\n");
5857
5858     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
5859     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
5860
5861     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5862     {
5863         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5864         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5865                 NULL, &dwType, lpValue, &cbValueLen);
5866         if (ret != ERROR_SUCCESS)
5867         {
5868             if (HeapFree (hHeap, 0, lpValue) == 0)
5869                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5870             if (HeapFree (hHeap, 0, lpValueName) == 0)
5871                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5872             r = RegCloseKey (hkSubKey);
5873             if (r != ERROR_SUCCESS)
5874                 WARN ("RegCloseKey returned %i\n", r);
5875             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5876             return ret;
5877         }
5878
5879         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
5880         memcpy (pEnumValues, lpValueName, cbValueNameLen);
5881         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
5882         pEnumValues += cbValueNameLen;
5883
5884         /* return # of *bytes* (including trailing \0), not # of chars */
5885         ppev[dwIndex].cbValueName = cbValueNameLen;
5886
5887         ppev[dwIndex].dwType = dwType;
5888
5889         memcpy (pEnumValues, lpValue, cbValueLen);
5890         ppev[dwIndex].pData = pEnumValues;
5891         pEnumValues += cbValueLen;
5892
5893         ppev[dwIndex].cbData = cbValueLen;
5894
5895         TRACE ("%s [%i]: copied name (%i bytes) and data (%i bytes)\n",
5896                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
5897     }
5898
5899     if (HeapFree (hHeap, 0, lpValue) == 0)
5900     {
5901         ret = GetLastError ();
5902         ERR ("HeapFree failed with code %i\n", ret);
5903         if (HeapFree (hHeap, 0, lpValueName) == 0)
5904             WARN ("HeapFree failed with code %i\n", GetLastError ());
5905         r = RegCloseKey (hkSubKey);
5906         if (r != ERROR_SUCCESS)
5907             WARN ("RegCloseKey returned %i\n", r);
5908         return ret;
5909     }
5910
5911     if (HeapFree (hHeap, 0, lpValueName) == 0)
5912     {
5913         ret = GetLastError ();
5914         ERR ("HeapFree failed with code %i\n", ret);
5915         r = RegCloseKey (hkSubKey);
5916         if (r != ERROR_SUCCESS)
5917             WARN ("RegCloseKey returned %i\n", r);
5918         return ret;
5919     }
5920
5921     ret = RegCloseKey (hkSubKey);
5922     if (ret != ERROR_SUCCESS)
5923     {
5924         ERR ("RegCloseKey returned %i\n", ret);
5925         return ret;
5926     }
5927
5928     return ERROR_SUCCESS;
5929 }
5930
5931 /*******************************************************************************
5932  *              EnumPrinterDataExA      [WINSPOOL.@]
5933  *
5934  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
5935  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
5936  * what Windows 2000 SP1 does.
5937  *
5938  */
5939 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5940                                 LPBYTE pEnumValues, DWORD cbEnumValues,
5941                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5942 {
5943     INT     len;
5944     LPWSTR  pKeyNameW;
5945     DWORD   ret, dwIndex, dwBufSize;
5946     HANDLE  hHeap;
5947     LPSTR   pBuffer;
5948
5949     TRACE ("%p %s\n", hPrinter, pKeyName);
5950
5951     if (pKeyName == NULL || *pKeyName == 0)
5952         return ERROR_INVALID_PARAMETER;
5953
5954     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
5955     if (len == 0)
5956     {
5957         ret = GetLastError ();
5958         ERR ("MultiByteToWideChar failed with code %i\n", ret);
5959         return ret;
5960     }
5961
5962     hHeap = GetProcessHeap ();
5963     if (hHeap == NULL)
5964     {
5965         ERR ("GetProcessHeap failed\n");
5966         return ERROR_OUTOFMEMORY;
5967     }
5968
5969     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
5970     if (pKeyNameW == NULL)
5971     {
5972         ERR ("Failed to allocate %i bytes from process heap\n",
5973              (LONG)(len * sizeof (WCHAR)));
5974         return ERROR_OUTOFMEMORY;
5975     }
5976
5977     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
5978     {
5979         ret = GetLastError ();
5980         ERR ("MultiByteToWideChar failed with code %i\n", ret);
5981         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5982             WARN ("HeapFree failed with code %i\n", GetLastError ());
5983         return ret;
5984     }
5985
5986     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
5987             pcbEnumValues, pnEnumValues);
5988     if (ret != ERROR_SUCCESS)
5989     {
5990         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5991             WARN ("HeapFree failed with code %i\n", GetLastError ());
5992         TRACE ("EnumPrinterDataExW returned %i\n", ret);
5993         return ret;
5994     }
5995
5996     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5997     {
5998         ret = GetLastError ();
5999         ERR ("HeapFree failed with code %i\n", ret);
6000         return ret;
6001     }
6002
6003     if (*pnEnumValues == 0)     /* empty key */
6004         return ERROR_SUCCESS;
6005
6006     dwBufSize = 0;
6007     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6008     {
6009         PPRINTER_ENUM_VALUESW ppev =
6010                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6011
6012         if (dwBufSize < ppev->cbValueName)
6013             dwBufSize = ppev->cbValueName;
6014
6015         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
6016                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
6017             dwBufSize = ppev->cbData;
6018     }
6019
6020     TRACE ("Largest Unicode name or value is %i bytes\n", dwBufSize);
6021
6022     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
6023     if (pBuffer == NULL)
6024     {
6025         ERR ("Failed to allocate %i bytes from process heap\n", dwBufSize);
6026         return ERROR_OUTOFMEMORY;
6027     }
6028
6029     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6030     {
6031         PPRINTER_ENUM_VALUESW ppev =
6032                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6033
6034         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
6035                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
6036                 NULL);
6037         if (len == 0)
6038         {
6039             ret = GetLastError ();
6040             ERR ("WideCharToMultiByte failed with code %i\n", ret);
6041             if (HeapFree (hHeap, 0, pBuffer) == 0)
6042                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6043             return ret;
6044         }
6045
6046         memcpy (ppev->pValueName, pBuffer, len);
6047
6048         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6049
6050         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
6051                 ppev->dwType != REG_MULTI_SZ)
6052             continue;
6053
6054         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
6055                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
6056         if (len == 0)
6057         {
6058             ret = GetLastError ();
6059             ERR ("WideCharToMultiByte failed with code %i\n", ret);
6060             if (HeapFree (hHeap, 0, pBuffer) == 0)
6061                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6062             return ret;
6063         }
6064
6065         memcpy (ppev->pData, pBuffer, len);
6066
6067         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6068         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
6069     }
6070
6071     if (HeapFree (hHeap, 0, pBuffer) == 0)
6072     {
6073         ret = GetLastError ();
6074         ERR ("HeapFree failed with code %i\n", ret);
6075         return ret;
6076     }
6077
6078     return ERROR_SUCCESS;
6079 }
6080
6081 /******************************************************************************
6082  *      AbortPrinter (WINSPOOL.@)
6083  */
6084 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
6085 {
6086     FIXME("(%p), stub!\n", hPrinter);
6087     return TRUE;
6088 }
6089
6090 /******************************************************************************
6091  *              AddPortA (WINSPOOL.@)
6092  *
6093  * See AddPortW.
6094  *
6095  */
6096 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
6097 {
6098     LPWSTR  nameW = NULL;
6099     LPWSTR  monitorW = NULL;
6100     DWORD   len;
6101     BOOL    res;
6102
6103     TRACE("(%s, %p, %s)\n",debugstr_a(pName), hWnd, debugstr_a(pMonitorName));
6104
6105     if (pName) {
6106         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6107         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6108         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6109     }
6110
6111     if (pMonitorName) {
6112         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6113         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6114         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6115     }
6116     res = AddPortW(nameW, hWnd, monitorW);
6117     HeapFree(GetProcessHeap(), 0, nameW);
6118     HeapFree(GetProcessHeap(), 0, monitorW);
6119     return res;
6120 }
6121
6122 /******************************************************************************
6123  *      AddPortW (WINSPOOL.@)
6124  *
6125  * Add a Port for a specific Monitor
6126  *
6127  * PARAMS
6128  *  pName        [I] Servername or NULL (local Computer)
6129  *  hWnd         [I] Handle to parent Window for the Dialog-Box
6130  *  pMonitorName [I] Name of the Monitor that manage the Port
6131  *
6132  * RETURNS
6133  *  Success: TRUE
6134  *  Failure: FALSE
6135  *
6136  */
6137 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
6138 {
6139     monitor_t * pm;
6140     monitor_t * pui;
6141     DWORD       res;
6142
6143     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
6144
6145     if (pName && pName[0]) {
6146         SetLastError(ERROR_INVALID_PARAMETER);
6147         return FALSE;
6148     }
6149
6150     if (!pMonitorName) {
6151         SetLastError(RPC_X_NULL_REF_POINTER);
6152         return FALSE;
6153     }
6154
6155     /* an empty Monitorname is Invalid */
6156     if (!pMonitorName[0]) {
6157         SetLastError(ERROR_NOT_SUPPORTED);
6158         return FALSE;
6159     }
6160
6161     pm = monitor_load(pMonitorName, NULL);
6162     if (pm && pm->monitor && pm->monitor->pfnAddPort) {
6163         res = pm->monitor->pfnAddPort(pName, hWnd, pMonitorName);
6164         TRACE("got %d with %u\n", res, GetLastError());
6165         res = TRUE;
6166     }
6167     else
6168     {
6169         pui = monitor_loadui(pm);
6170         if (pui && pui->monitorUI && pui->monitorUI->pfnAddPortUI) {
6171             TRACE("use %p: %s\n", pui, debugstr_w(pui->dllname));
6172             res = pui->monitorUI->pfnAddPortUI(pName, hWnd, pMonitorName, NULL);
6173             TRACE("got %d with %u\n", res, GetLastError());
6174             res = TRUE;
6175         }
6176         else
6177         {
6178             FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pMonitorName),
6179                   pm, debugstr_w(pm ? pm->dllname : NULL), pui, debugstr_w(pui ? pui->dllname : NULL));
6180
6181             /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6182             SetLastError(ERROR_NOT_SUPPORTED);
6183             res = FALSE;
6184         }
6185         monitor_unload(pui);
6186     }
6187     monitor_unload(pm);
6188     TRACE("returning %d with %u\n", res, GetLastError());
6189     return res;
6190 }
6191
6192 /******************************************************************************
6193  *             AddPortExA (WINSPOOL.@)
6194  *
6195  * See AddPortExW.
6196  *
6197  */
6198 BOOL WINAPI AddPortExA(LPSTR pName, DWORD level, LPBYTE pBuffer, LPSTR pMonitorName)
6199 {
6200     PORT_INFO_2W   pi2W;
6201     PORT_INFO_2A * pi2A;
6202     LPWSTR  nameW = NULL;
6203     LPWSTR  monitorW = NULL;
6204     DWORD   len;
6205     BOOL    res;
6206
6207     pi2A = (PORT_INFO_2A *) pBuffer;
6208
6209     TRACE("(%s, %d, %p, %s): %s\n", debugstr_a(pName), level, pBuffer,
6210             debugstr_a(pMonitorName), debugstr_a(pi2A ? pi2A->pPortName : NULL));
6211
6212     if ((level < 1) || (level > 2)) {
6213         SetLastError(ERROR_INVALID_LEVEL);
6214         return FALSE;
6215     }
6216
6217     if (!pi2A) {
6218         SetLastError(ERROR_INVALID_PARAMETER);
6219         return FALSE;
6220     }
6221
6222     if (pName) {
6223         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6224         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6225         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6226     }
6227
6228     if (pMonitorName) {
6229         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6230         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6231         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6232     }
6233
6234     ZeroMemory(&pi2W, sizeof(PORT_INFO_2W));
6235
6236     if (pi2A->pPortName) {
6237         len = MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, NULL, 0);
6238         pi2W.pPortName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6239         MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, pi2W.pPortName, len);
6240     }
6241
6242     if (level > 1) {
6243         if (pi2A->pMonitorName) {
6244             len = MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, NULL, 0);
6245             pi2W.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6246             MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, pi2W.pMonitorName, len);
6247         }
6248
6249         if (pi2A->pDescription) {
6250             len = MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, NULL, 0);
6251             pi2W.pDescription = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6252             MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, pi2W.pDescription, len);
6253         }
6254         pi2W.fPortType = pi2A->fPortType;
6255         pi2W.Reserved = pi2A->Reserved;
6256     }
6257
6258     res = AddPortExW(nameW, level, (LPBYTE) &pi2W, monitorW);
6259
6260     HeapFree(GetProcessHeap(), 0, nameW);
6261     HeapFree(GetProcessHeap(), 0, monitorW);
6262     HeapFree(GetProcessHeap(), 0, pi2W.pPortName);
6263     HeapFree(GetProcessHeap(), 0, pi2W.pMonitorName);
6264     HeapFree(GetProcessHeap(), 0, pi2W.pDescription);
6265     return res;
6266
6267 }
6268
6269 /******************************************************************************
6270  *             AddPortExW (WINSPOOL.@)
6271  *
6272  * Add a Port for a specific Monitor, without presenting a user interface
6273  *
6274  * PARAMS
6275  *  pName         [I] Servername or NULL (local Computer)
6276  *  level         [I] Structure-Level (1 or 2) for pBuffer
6277  *  pBuffer       [I] PTR to: PORT_INFO_1 or PORT_INFO_2
6278  *  pMonitorName  [I] Name of the Monitor that manage the Port
6279  *
6280  * RETURNS
6281  *  Success: TRUE
6282  *  Failure: FALSE
6283  *
6284  */
6285 BOOL WINAPI AddPortExW(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
6286 {
6287     PORT_INFO_2W * pi2;
6288     monitor_t * pm;
6289     DWORD       res = FALSE;
6290
6291     pi2 = (PORT_INFO_2W *) pBuffer;
6292
6293     TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
6294             debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
6295             debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
6296             debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
6297
6298
6299     if ((level < 1) || (level > 2)) {
6300         SetLastError(ERROR_INVALID_LEVEL);
6301         return FALSE;
6302     }
6303
6304     if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
6305         SetLastError(ERROR_INVALID_PARAMETER);
6306         return FALSE;
6307     }
6308
6309     /* load the Monitor */
6310     pm = monitor_load(pMonitorName, NULL);
6311     if (!pm) {
6312         SetLastError(ERROR_INVALID_PARAMETER);
6313         return FALSE;
6314     }
6315
6316     if (pm->monitor && pm->monitor->pfnAddPortEx) {
6317         res = pm->monitor->pfnAddPortEx(pName, level, pBuffer, pMonitorName);
6318         TRACE("got %u with %u\n", res, GetLastError());
6319     }
6320     else
6321     {
6322         FIXME("not implemented for %s (%p)\n", debugstr_w(pMonitorName), pm->monitor);
6323     }
6324     monitor_unload(pm);
6325     return res;
6326 }
6327
6328 /******************************************************************************
6329  *      AddPrinterConnectionA (WINSPOOL.@)
6330  */
6331 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
6332 {
6333     FIXME("%s\n", debugstr_a(pName));
6334     return FALSE;
6335 }
6336
6337 /******************************************************************************
6338  *      AddPrinterConnectionW (WINSPOOL.@)
6339  */
6340 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
6341 {
6342     FIXME("%s\n", debugstr_w(pName));
6343     return FALSE;
6344 }
6345
6346 /******************************************************************************
6347  *  AddPrinterDriverExW (WINSPOOL.@)
6348  *
6349  * Install a Printer Driver with the Option to upgrade / downgrade the Files
6350  *
6351  * PARAMS
6352  *  pName           [I] Servername or NULL (local Computer)
6353  *  level           [I] Level for the supplied DRIVER_INFO_*W struct
6354  *  pDriverInfo     [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
6355  *  dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
6356  *
6357  * RESULTS
6358  *  Success: TRUE
6359  *  Failure: FALSE
6360  *
6361  */
6362 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6363 {
6364     TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
6365
6366     if ((backend == NULL)  && !load_backend()) return FALSE;
6367
6368     if (level < 2 || level == 5 || level == 7 || level > 8) {
6369         SetLastError(ERROR_INVALID_LEVEL);
6370         return FALSE;
6371     }
6372
6373     if (!pDriverInfo) {
6374         SetLastError(ERROR_INVALID_PARAMETER);
6375         return FALSE;
6376     }
6377
6378     return backend->fpAddPrinterDriverEx(pName, level, pDriverInfo, dwFileCopyFlags);
6379 }
6380
6381 /******************************************************************************
6382  *  AddPrinterDriverExA (WINSPOOL.@)
6383  *
6384  * See AddPrinterDriverExW.
6385  *
6386  */
6387 BOOL WINAPI AddPrinterDriverExA(LPSTR pName, DWORD Level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6388 {
6389     DRIVER_INFO_8A  *diA;
6390     DRIVER_INFO_8W   diW;
6391     LPWSTR  nameW = NULL;
6392     DWORD   lenA;
6393     DWORD   len;
6394     DWORD   res = FALSE;
6395
6396     TRACE("(%s, %d, %p, 0x%x)\n", debugstr_a(pName), Level, pDriverInfo, dwFileCopyFlags);
6397
6398     diA = (DRIVER_INFO_8A  *) pDriverInfo;
6399     ZeroMemory(&diW, sizeof(diW));
6400
6401     if (Level < 2 || Level == 5 || Level == 7 || Level > 8) {
6402         SetLastError(ERROR_INVALID_LEVEL);
6403         return FALSE;
6404     }
6405
6406     if (diA == NULL) {
6407         SetLastError(ERROR_INVALID_PARAMETER);
6408         return FALSE;
6409     }
6410
6411     /* convert servername to unicode */
6412     if (pName) {
6413         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6414         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6415         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6416     }
6417
6418     /* common fields */
6419     diW.cVersion = diA->cVersion;
6420
6421     if (diA->pName) {
6422         len = MultiByteToWideChar(CP_ACP, 0, diA->pName, -1, NULL, 0);
6423         diW.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6424         MultiByteToWideChar(CP_ACP, 0, diA->pName, -1, diW.pName, len);
6425     }
6426
6427     if (diA->pEnvironment) {
6428         len = MultiByteToWideChar(CP_ACP, 0, diA->pEnvironment, -1, NULL, 0);
6429         diW.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6430         MultiByteToWideChar(CP_ACP, 0, diA->pEnvironment, -1, diW.pEnvironment, len);
6431     }
6432
6433     if (diA->pDriverPath) {
6434         len = MultiByteToWideChar(CP_ACP, 0, diA->pDriverPath, -1, NULL, 0);
6435         diW.pDriverPath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6436         MultiByteToWideChar(CP_ACP, 0, diA->pDriverPath, -1, diW.pDriverPath, len);
6437     }
6438
6439     if (diA->pDataFile) {
6440         len = MultiByteToWideChar(CP_ACP, 0, diA->pDataFile, -1, NULL, 0);
6441         diW.pDataFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6442         MultiByteToWideChar(CP_ACP, 0, diA->pDataFile, -1, diW.pDataFile, len);
6443     }
6444
6445     if (diA->pConfigFile) {
6446         len = MultiByteToWideChar(CP_ACP, 0, diA->pConfigFile, -1, NULL, 0);
6447         diW.pConfigFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6448         MultiByteToWideChar(CP_ACP, 0, diA->pConfigFile, -1, diW.pConfigFile, len);
6449     }
6450
6451     if ((Level > 2) && diA->pDependentFiles) {
6452         lenA = multi_sz_lenA(diA->pDependentFiles);
6453         len = MultiByteToWideChar(CP_ACP, 0, diA->pDependentFiles, lenA, NULL, 0);
6454         diW.pDependentFiles = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6455         MultiByteToWideChar(CP_ACP, 0, diA->pDependentFiles, lenA, diW.pDependentFiles, len);
6456     }
6457
6458     if ((Level > 2) && diA->pMonitorName) {
6459         len = MultiByteToWideChar(CP_ACP, 0, diA->pMonitorName, -1, NULL, 0);
6460         diW.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6461         MultiByteToWideChar(CP_ACP, 0, diA->pMonitorName, -1, diW.pMonitorName, len);
6462     }
6463
6464     if ((Level > 3) && diA->pDefaultDataType) {
6465         len = MultiByteToWideChar(CP_ACP, 0, diA->pDefaultDataType, -1, NULL, 0);
6466         diW.pDefaultDataType = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6467         MultiByteToWideChar(CP_ACP, 0, diA->pDefaultDataType, -1, diW.pDefaultDataType, len);
6468     }
6469
6470     if ((Level > 3) && diA->pszzPreviousNames) {
6471         lenA = multi_sz_lenA(diA->pszzPreviousNames);
6472         len = MultiByteToWideChar(CP_ACP, 0, diA->pszzPreviousNames, lenA, NULL, 0);
6473         diW.pszzPreviousNames = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6474         MultiByteToWideChar(CP_ACP, 0, diA->pszzPreviousNames, lenA, diW.pszzPreviousNames, len);
6475     }
6476
6477     if ((Level > 5) && diA->pszMfgName) {
6478         len = MultiByteToWideChar(CP_ACP, 0, diA->pszMfgName, -1, NULL, 0);
6479         diW.pszMfgName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6480         MultiByteToWideChar(CP_ACP, 0, diA->pszMfgName, -1, diW.pszMfgName, len);
6481     }
6482
6483     if ((Level > 5) && diA->pszOEMUrl) {
6484         len = MultiByteToWideChar(CP_ACP, 0, diA->pszOEMUrl, -1, NULL, 0);
6485         diW.pszOEMUrl = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6486         MultiByteToWideChar(CP_ACP, 0, diA->pszOEMUrl, -1, diW.pszOEMUrl, len);
6487     }
6488
6489     if ((Level > 5) && diA->pszHardwareID) {
6490         len = MultiByteToWideChar(CP_ACP, 0, diA->pszHardwareID, -1, NULL, 0);
6491         diW.pszHardwareID = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6492         MultiByteToWideChar(CP_ACP, 0, diA->pszHardwareID, -1, diW.pszHardwareID, len);
6493     }
6494
6495     if ((Level > 5) && diA->pszProvider) {
6496         len = MultiByteToWideChar(CP_ACP, 0, diA->pszProvider, -1, NULL, 0);
6497         diW.pszProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6498         MultiByteToWideChar(CP_ACP, 0, diA->pszProvider, -1, diW.pszProvider, len);
6499     }
6500
6501     if (Level > 7) {
6502         FIXME("level %u is incomplete\n", Level);
6503     }
6504
6505     res = AddPrinterDriverExW(nameW, Level, (LPBYTE) &diW, dwFileCopyFlags);
6506     TRACE("got %u with %u\n", res, GetLastError());
6507     HeapFree(GetProcessHeap(), 0, nameW);
6508     HeapFree(GetProcessHeap(), 0, diW.pName);
6509     HeapFree(GetProcessHeap(), 0, diW.pEnvironment);
6510     HeapFree(GetProcessHeap(), 0, diW.pDriverPath);
6511     HeapFree(GetProcessHeap(), 0, diW.pDataFile);
6512     HeapFree(GetProcessHeap(), 0, diW.pConfigFile);
6513     HeapFree(GetProcessHeap(), 0, diW.pDependentFiles);
6514     HeapFree(GetProcessHeap(), 0, diW.pMonitorName);
6515     HeapFree(GetProcessHeap(), 0, diW.pDefaultDataType);
6516     HeapFree(GetProcessHeap(), 0, diW.pszzPreviousNames);
6517     HeapFree(GetProcessHeap(), 0, diW.pszMfgName);
6518     HeapFree(GetProcessHeap(), 0, diW.pszOEMUrl);
6519     HeapFree(GetProcessHeap(), 0, diW.pszHardwareID);
6520     HeapFree(GetProcessHeap(), 0, diW.pszProvider);
6521
6522     TRACE("=> %u with %u\n", res, GetLastError());
6523     return res;
6524 }
6525
6526 /******************************************************************************
6527  *      ConfigurePortA (WINSPOOL.@)
6528  *
6529  * See ConfigurePortW.
6530  *
6531  */
6532 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
6533 {
6534     LPWSTR  nameW = NULL;
6535     LPWSTR  portW = NULL;
6536     INT     len;
6537     DWORD   res;
6538
6539     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
6540
6541     /* convert servername to unicode */
6542     if (pName) {
6543         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6544         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6545         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6546     }
6547
6548     /* convert portname to unicode */
6549     if (pPortName) {
6550         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
6551         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6552         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
6553     }
6554
6555     res = ConfigurePortW(nameW, hWnd, portW);
6556     HeapFree(GetProcessHeap(), 0, nameW);
6557     HeapFree(GetProcessHeap(), 0, portW);
6558     return res;
6559 }
6560
6561 /******************************************************************************
6562  *      ConfigurePortW (WINSPOOL.@)
6563  *
6564  * Display the Configuration-Dialog for a specific Port
6565  *
6566  * PARAMS
6567  *  pName     [I] Servername or NULL (local Computer)
6568  *  hWnd      [I] Handle to parent Window for the Dialog-Box
6569  *  pPortName [I] Name of the Port, that should be configured
6570  *
6571  * RETURNS
6572  *  Success: TRUE
6573  *  Failure: FALSE
6574  *
6575  */
6576 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
6577 {
6578     monitor_t * pm;
6579     monitor_t * pui;
6580     DWORD       res;
6581
6582     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
6583
6584     if (pName && pName[0]) {
6585         SetLastError(ERROR_INVALID_PARAMETER);
6586         return FALSE;
6587     }
6588
6589     if (!pPortName) {
6590         SetLastError(RPC_X_NULL_REF_POINTER);
6591         return FALSE;
6592     }
6593
6594     /* an empty Portname is Invalid, but can popup a Dialog */
6595     if (!pPortName[0]) {
6596         SetLastError(ERROR_NOT_SUPPORTED);
6597         return FALSE;
6598     }
6599
6600     pm = monitor_load_by_port(pPortName);
6601     if (pm && pm->monitor && pm->monitor->pfnConfigurePort) {
6602         TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
6603         res = pm->monitor->pfnConfigurePort(pName, hWnd, pPortName);
6604         TRACE("got %d with %u\n", res, GetLastError());
6605     }
6606     else
6607     {
6608         pui = monitor_loadui(pm);
6609         if (pui && pui->monitorUI && pui->monitorUI->pfnConfigurePortUI) {
6610             TRACE("Use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
6611             res = pui->monitorUI->pfnConfigurePortUI(pName, hWnd, pPortName);
6612             TRACE("got %d with %u\n", res, GetLastError());
6613         }
6614         else
6615         {
6616             FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
6617                   pm, debugstr_w(pm ? pm->dllname : NULL), pui, debugstr_w(pui ? pui->dllname : NULL));
6618
6619             /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6620             SetLastError(ERROR_NOT_SUPPORTED);
6621             res = FALSE;
6622         }
6623         monitor_unload(pui);
6624     }
6625     monitor_unload(pm);
6626
6627     TRACE("returning %d with %u\n", res, GetLastError());
6628     return res;
6629 }
6630
6631 /******************************************************************************
6632  *      ConnectToPrinterDlg (WINSPOOL.@)
6633  */
6634 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
6635 {
6636     FIXME("%p %x\n", hWnd, Flags);
6637     return NULL;
6638 }
6639
6640 /******************************************************************************
6641  *      DeletePrinterConnectionA (WINSPOOL.@)
6642  */
6643 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
6644 {
6645     FIXME("%s\n", debugstr_a(pName));
6646     return TRUE;
6647 }
6648
6649 /******************************************************************************
6650  *      DeletePrinterConnectionW (WINSPOOL.@)
6651  */
6652 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
6653 {
6654     FIXME("%s\n", debugstr_w(pName));
6655     return TRUE;
6656 }
6657
6658 /******************************************************************************
6659  *              DeletePrinterDriverExW (WINSPOOL.@)
6660  */
6661 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
6662     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6663 {
6664     HKEY hkey_drivers;
6665     BOOL ret = FALSE;
6666
6667     TRACE("%s %s %s %x %x\n", debugstr_w(pName), debugstr_w(pEnvironment),
6668           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
6669
6670     if(pName && pName[0])
6671     {
6672         FIXME("pName = %s - unsupported\n", debugstr_w(pName));
6673         SetLastError(ERROR_INVALID_PARAMETER);
6674         return FALSE;
6675     }
6676
6677     if(dwDeleteFlag)
6678     {
6679         FIXME("dwDeleteFlag = %x - unsupported\n", dwDeleteFlag);
6680         SetLastError(ERROR_INVALID_PARAMETER);
6681         return FALSE;
6682     }
6683
6684     hkey_drivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
6685
6686     if(!hkey_drivers)
6687     {
6688         ERR("Can't open drivers key\n");
6689         return FALSE;
6690     }
6691
6692     if(RegDeleteTreeW(hkey_drivers, pDriverName) == ERROR_SUCCESS)
6693         ret = TRUE;
6694
6695     RegCloseKey(hkey_drivers);
6696
6697     return ret;
6698 }
6699
6700 /******************************************************************************
6701  *              DeletePrinterDriverExA (WINSPOOL.@)
6702  */
6703 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
6704     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6705 {
6706     UNICODE_STRING NameW, EnvW, DriverW;
6707     BOOL ret;
6708
6709     asciitounicode(&NameW, pName);
6710     asciitounicode(&EnvW, pEnvironment);
6711     asciitounicode(&DriverW, pDriverName);
6712
6713     ret = DeletePrinterDriverExW(NameW.Buffer, EnvW.Buffer, DriverW.Buffer, dwDeleteFlag, dwVersionFlag);
6714
6715     RtlFreeUnicodeString(&DriverW);
6716     RtlFreeUnicodeString(&EnvW);
6717     RtlFreeUnicodeString(&NameW);
6718
6719     return ret;
6720 }
6721
6722 /******************************************************************************
6723  *              DeletePrinterDataExW (WINSPOOL.@)
6724  */
6725 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
6726                                   LPCWSTR pValueName)
6727 {
6728     FIXME("%p %s %s\n", hPrinter, 
6729           debugstr_w(pKeyName), debugstr_w(pValueName));
6730     return ERROR_INVALID_PARAMETER;
6731 }
6732
6733 /******************************************************************************
6734  *              DeletePrinterDataExA (WINSPOOL.@)
6735  */
6736 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
6737                                   LPCSTR pValueName)
6738 {
6739     FIXME("%p %s %s\n", hPrinter, 
6740           debugstr_a(pKeyName), debugstr_a(pValueName));
6741     return ERROR_INVALID_PARAMETER;
6742 }
6743
6744 /******************************************************************************
6745  *      DeletePrintProcessorA (WINSPOOL.@)
6746  */
6747 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
6748 {
6749     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6750           debugstr_a(pPrintProcessorName));
6751     return TRUE;
6752 }
6753
6754 /******************************************************************************
6755  *      DeletePrintProcessorW (WINSPOOL.@)
6756  */
6757 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
6758 {
6759     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6760           debugstr_w(pPrintProcessorName));
6761     return TRUE;
6762 }
6763
6764 /******************************************************************************
6765  *      DeletePrintProvidorA (WINSPOOL.@)
6766  */
6767 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
6768 {
6769     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6770           debugstr_a(pPrintProviderName));
6771     return TRUE;
6772 }
6773
6774 /******************************************************************************
6775  *      DeletePrintProvidorW (WINSPOOL.@)
6776  */
6777 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
6778 {
6779     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6780           debugstr_w(pPrintProviderName));
6781     return TRUE;
6782 }
6783
6784 /******************************************************************************
6785  *      EnumFormsA (WINSPOOL.@)
6786  */
6787 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6788     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6789 {
6790     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6791     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6792     return FALSE;
6793 }
6794
6795 /******************************************************************************
6796  *      EnumFormsW (WINSPOOL.@)
6797  */
6798 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6799     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6800 {
6801     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6802     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6803     return FALSE;
6804 }
6805
6806 /*****************************************************************************
6807  *          EnumMonitorsA [WINSPOOL.@]
6808  *
6809  * See EnumMonitorsW.
6810  *
6811  */
6812 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
6813                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6814 {
6815     BOOL    res;
6816     LPBYTE  bufferW = NULL;
6817     LPWSTR  nameW = NULL;
6818     DWORD   needed = 0;
6819     DWORD   numentries = 0;
6820     INT     len;
6821
6822     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pMonitors,
6823           cbBuf, pcbNeeded, pcReturned);
6824
6825     /* convert servername to unicode */
6826     if (pName) {
6827         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6828         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6829         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6830     }
6831     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
6832     needed = cbBuf * sizeof(WCHAR);    
6833     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
6834     res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6835
6836     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
6837         if (pcbNeeded) needed = *pcbNeeded;
6838         /* HeapReAlloc return NULL, when bufferW was NULL */
6839         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
6840                               HeapAlloc(GetProcessHeap(), 0, needed);
6841
6842         /* Try again with the large Buffer */
6843         res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6844     }
6845     numentries = pcReturned ? *pcReturned : 0;
6846     needed = 0;
6847     /*
6848        W2k require the buffersize from EnumMonitorsW also for EnumMonitorsA.
6849        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
6850      */
6851     if (res) {
6852         /* EnumMonitorsW collected all Data. Parse them to calculate ANSI-Size */
6853         DWORD   entrysize = 0;
6854         DWORD   index;
6855         LPSTR   ptr;
6856         LPMONITOR_INFO_2W mi2w;
6857         LPMONITOR_INFO_2A mi2a;
6858
6859         /* MONITOR_INFO_*W and MONITOR_INFO_*A have the same size */
6860         entrysize = (Level == 1) ? sizeof(MONITOR_INFO_1A) : sizeof(MONITOR_INFO_2A);
6861
6862         /* First pass: calculate the size for all Entries */
6863         mi2w = (LPMONITOR_INFO_2W) bufferW;
6864         mi2a = (LPMONITOR_INFO_2A) pMonitors;
6865         index = 0;
6866         while (index < numentries) {
6867             index++;
6868             needed += entrysize;    /* MONITOR_INFO_?A */
6869             TRACE("%p: parsing #%d (%s)\n", mi2w, index, debugstr_w(mi2w->pName));
6870
6871             needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6872                                             NULL, 0, NULL, NULL);
6873             if (Level > 1) {
6874                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6875                                                 NULL, 0, NULL, NULL);
6876                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
6877                                                 NULL, 0, NULL, NULL);
6878             }
6879             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
6880             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
6881             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6882         }
6883
6884         /* check for errors and quit on failure */
6885         if (cbBuf < needed) {
6886             SetLastError(ERROR_INSUFFICIENT_BUFFER);
6887             res = FALSE;
6888             goto emA_cleanup;
6889         }
6890         len = entrysize * numentries;       /* room for all MONITOR_INFO_?A */
6891         ptr = (LPSTR) &pMonitors[len];      /* room for strings */
6892         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
6893         mi2w = (LPMONITOR_INFO_2W) bufferW;
6894         mi2a = (LPMONITOR_INFO_2A) pMonitors;
6895         index = 0;
6896         /* Second Pass: Fill the User Buffer (if we have one) */
6897         while ((index < numentries) && pMonitors) {
6898             index++;
6899             TRACE("%p: writing MONITOR_INFO_%dA #%d\n", mi2a, Level, index);
6900             mi2a->pName = ptr;
6901             len = WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6902                                             ptr, cbBuf , NULL, NULL);
6903             ptr += len;
6904             cbBuf -= len;
6905             if (Level > 1) {
6906                 mi2a->pEnvironment = ptr;
6907                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6908                                             ptr, cbBuf, NULL, NULL);
6909                 ptr += len;
6910                 cbBuf -= len;
6911
6912                 mi2a->pDLLName = ptr;
6913                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
6914                                             ptr, cbBuf, NULL, NULL);
6915                 ptr += len;
6916                 cbBuf -= len;
6917             }
6918             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
6919             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
6920             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6921         }
6922     }
6923 emA_cleanup:
6924     if (pcbNeeded)  *pcbNeeded = needed;
6925     if (pcReturned) *pcReturned = (res) ? numentries : 0;
6926
6927     HeapFree(GetProcessHeap(), 0, nameW);
6928     HeapFree(GetProcessHeap(), 0, bufferW);
6929
6930     TRACE("returning %d with %d (%d byte for %d entries)\n", 
6931             (res), GetLastError(), needed, numentries);
6932
6933     return (res);
6934
6935 }
6936
6937 /*****************************************************************************
6938  *          EnumMonitorsW [WINSPOOL.@]
6939  *
6940  * Enumerate available Port-Monitors
6941  *
6942  * PARAMS
6943  *  pName       [I] Servername or NULL (local Computer)
6944  *  Level       [I] Structure-Level (1:Win9x+NT or 2:NT only)
6945  *  pMonitors   [O] PTR to Buffer that receives the Result
6946  *  cbBuf       [I] Size of Buffer at pMonitors
6947  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
6948  *  pcReturned  [O] PTR to DWORD that receives the number of Monitors in pMonitors
6949  *
6950  * RETURNS
6951  *  Success: TRUE
6952  *  Failure: FALSE and in pcbNeeded the Bytes required for buffer, if cbBuf is too small
6953  *
6954  */
6955 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
6956                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6957 {
6958
6959     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
6960           cbBuf, pcbNeeded, pcReturned);
6961
6962     if ((backend == NULL)  && !load_backend()) return FALSE;
6963
6964     if (!pcbNeeded || !pcReturned || (!pMonitors && (cbBuf > 0))) {
6965         SetLastError(RPC_X_NULL_REF_POINTER);
6966         return FALSE;
6967     }
6968
6969     return backend->fpEnumMonitors(pName, Level, pMonitors, cbBuf, pcbNeeded, pcReturned);
6970 }
6971
6972 /******************************************************************************
6973  * SpoolerInit (WINSPOOL.@)
6974  *
6975  * Initialize the Spooler
6976  *
6977  * RETURNS
6978  *  Success: TRUE
6979  *  Failure: FALSE
6980  *
6981  * NOTES
6982  *  The function fails on windows, when the spooler service is not running
6983  *
6984  */
6985 BOOL WINAPI SpoolerInit(void)
6986 {
6987
6988     if ((backend == NULL)  && !load_backend()) return FALSE;
6989     return TRUE;
6990 }
6991
6992 /******************************************************************************
6993  *              XcvDataW (WINSPOOL.@)
6994  *
6995  * Execute commands in the Printmonitor DLL
6996  *
6997  * PARAMS
6998  *  hXcv            [i] Handle from OpenPrinter (with XcvMonitor or XcvPort)
6999  *  pszDataName     [i] Name of the command to execute
7000  *  pInputData      [i] Buffer for extra Input Data (needed only for some commands)
7001  *  cbInputData     [i] Size in Bytes of Buffer at pInputData
7002  *  pOutputData     [o] Buffer to receive additional Data (needed only for some commands)
7003  *  cbOutputData    [i] Size in Bytes of Buffer at pOutputData
7004  *  pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
7005  *  pdwStatus       [o] PTR to receive the win32 error code from the Printmonitor DLL
7006  *
7007  * RETURNS
7008  *  Success: TRUE
7009  *  Failure: FALSE
7010  *
7011  * NOTES
7012  *  Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
7013  *  The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
7014  *
7015  *  Minimal List of commands, that a Printmonitor DLL should support:
7016  *
7017  *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
7018  *| "AddPort"   : Add a Port
7019  *| "DeletePort": Delete a Port
7020  *
7021  *  Many Printmonitors support additional commands. Examples for localspl.dll:
7022  *  "GetDefaultCommConfig", "SetDefaultCommConfig",
7023  *  "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
7024  *
7025  */
7026 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
7027     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
7028     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
7029 {
7030     opened_printer_t *printer;
7031
7032     TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
7033           pInputData, cbInputData, pOutputData,
7034           cbOutputData, pcbOutputNeeded, pdwStatus);
7035
7036     printer = get_opened_printer(hXcv);
7037     if (!printer || (!printer->hXcv)) {
7038         SetLastError(ERROR_INVALID_HANDLE);
7039         return FALSE;
7040     }
7041
7042     if (!pcbOutputNeeded) {
7043         SetLastError(ERROR_INVALID_PARAMETER);
7044         return FALSE;
7045     }
7046
7047     if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
7048         SetLastError(RPC_X_NULL_REF_POINTER);
7049         return FALSE;
7050     }
7051
7052     *pcbOutputNeeded = 0;
7053
7054     *pdwStatus = printer->pm->monitor->pfnXcvDataPort(printer->hXcv, pszDataName,
7055             pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
7056
7057     return TRUE;
7058 }
7059
7060 /*****************************************************************************
7061  *          EnumPrinterDataA [WINSPOOL.@]
7062  *
7063  */
7064 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
7065     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7066     DWORD cbData, LPDWORD pcbData )
7067 {
7068     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7069           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7070     return ERROR_NO_MORE_ITEMS;
7071 }
7072
7073 /*****************************************************************************
7074  *          EnumPrinterDataW [WINSPOOL.@]
7075  *
7076  */
7077 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
7078     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7079     DWORD cbData, LPDWORD pcbData )
7080 {
7081     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7082           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7083     return ERROR_NO_MORE_ITEMS;
7084 }
7085
7086 /*****************************************************************************
7087  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
7088  *
7089  */
7090 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
7091                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7092                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
7093 {
7094     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_a(pName),
7095           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
7096           pcbNeeded, pcReturned);
7097     return FALSE;
7098 }
7099
7100 /*****************************************************************************
7101  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
7102  *
7103  */
7104 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
7105                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7106                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
7107 {
7108     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7109           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
7110           pcbNeeded, pcReturned);
7111     return FALSE;
7112 }
7113
7114 /*****************************************************************************
7115  *          EnumPrintProcessorsA [WINSPOOL.@]
7116  *
7117  */
7118 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
7119     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
7120 {
7121     FIXME("Stub: %s %s %d %p %d %p %p\n", pName, pEnvironment, Level,
7122         pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
7123     return FALSE;
7124 }
7125
7126 /*****************************************************************************
7127  *          EnumPrintProcessorsW [WINSPOOL.@]
7128  *
7129  */
7130 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
7131     LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
7132 {
7133     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7134         debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
7135         cbBuf, pcbNeeded, pcbReturned);
7136     return FALSE;
7137 }
7138
7139 /*****************************************************************************
7140  *          ExtDeviceMode [WINSPOOL.@]
7141  *
7142  */
7143 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
7144     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
7145     DWORD fMode)
7146 {
7147     FIXME("Stub: %p %p %p %s %s %p %s %x\n", hWnd, hInst, pDevModeOutput,
7148           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
7149           debugstr_a(pProfile), fMode);
7150     return -1;
7151 }
7152
7153 /*****************************************************************************
7154  *          FindClosePrinterChangeNotification [WINSPOOL.@]
7155  *
7156  */
7157 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
7158 {
7159     FIXME("Stub: %p\n", hChange);
7160     return TRUE;
7161 }
7162
7163 /*****************************************************************************
7164  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
7165  *
7166  */
7167 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
7168     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
7169 {
7170     FIXME("Stub: %p %x %x %p\n",
7171           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
7172     return INVALID_HANDLE_VALUE;
7173 }
7174
7175 /*****************************************************************************
7176  *          FindNextPrinterChangeNotification [WINSPOOL.@]
7177  *
7178  */
7179 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
7180     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
7181 {
7182     FIXME("Stub: %p %p %p %p\n",
7183           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
7184     return FALSE;
7185 }
7186
7187 /*****************************************************************************
7188  *          FreePrinterNotifyInfo [WINSPOOL.@]
7189  *
7190  */
7191 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
7192 {
7193     FIXME("Stub: %p\n", pPrinterNotifyInfo);
7194     return TRUE;
7195 }
7196
7197 /*****************************************************************************
7198  *          string_to_buf
7199  *
7200  * Copies a unicode string into a buffer.  The buffer will either contain unicode or
7201  * ansi depending on the unicode parameter.
7202  */
7203 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
7204 {
7205     if(!str)
7206     {
7207         *size = 0;
7208         return TRUE;
7209     }
7210
7211     if(unicode)
7212     {
7213         *size = (strlenW(str) + 1) * sizeof(WCHAR);
7214         if(*size <= cb)
7215         {
7216             memcpy(ptr, str, *size);
7217             return TRUE;
7218         }
7219         return FALSE;
7220     }
7221     else
7222     {
7223         *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
7224         if(*size <= cb)
7225         {
7226             WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
7227             return TRUE;
7228         }
7229         return FALSE;
7230     }
7231 }
7232
7233 /*****************************************************************************
7234  *          get_job_info_1
7235  */
7236 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
7237                            LPDWORD pcbNeeded, BOOL unicode)
7238 {
7239     DWORD size, left = cbBuf;
7240     BOOL space = (cbBuf > 0);
7241     LPBYTE ptr = buf;
7242
7243     *pcbNeeded = 0;
7244
7245     if(space)
7246     {
7247         ji1->JobId = job->job_id;
7248     }
7249
7250     string_to_buf(job->document_title, ptr, left, &size, unicode);
7251     if(space && size <= left)
7252     {
7253         ji1->pDocument = (LPWSTR)ptr;
7254         ptr += size;
7255         left -= size;
7256     }
7257     else
7258         space = FALSE;
7259     *pcbNeeded += size;
7260
7261     return space;
7262 }
7263
7264 /*****************************************************************************
7265  *          get_job_info_2
7266  */
7267 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
7268                            LPDWORD pcbNeeded, BOOL unicode)
7269 {
7270     DWORD size, left = cbBuf;
7271     BOOL space = (cbBuf > 0);
7272     LPBYTE ptr = buf;
7273
7274     *pcbNeeded = 0;
7275
7276     if(space)
7277     {
7278         ji2->JobId = job->job_id;
7279     }
7280
7281     string_to_buf(job->document_title, ptr, left, &size, unicode);
7282     if(space && size <= left)
7283     {
7284         ji2->pDocument = (LPWSTR)ptr;
7285         ptr += size;
7286         left -= size;
7287     }
7288     else
7289         space = FALSE;
7290     *pcbNeeded += size;
7291
7292     return space;
7293 }
7294
7295 /*****************************************************************************
7296  *          get_job_info
7297  */
7298 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7299                          DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
7300 {
7301     BOOL ret = FALSE;
7302     DWORD needed = 0, size;
7303     job_t *job;
7304     LPBYTE ptr = pJob;
7305
7306     TRACE("%p %d %d %p %d %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
7307
7308     EnterCriticalSection(&printer_handles_cs);
7309     job = get_job(hPrinter, JobId);
7310     if(!job)
7311         goto end;
7312
7313     switch(Level)
7314     {
7315     case 1:
7316         size = sizeof(JOB_INFO_1W);
7317         if(cbBuf >= size)
7318         {
7319             cbBuf -= size;
7320             ptr += size;
7321             memset(pJob, 0, size);
7322         }
7323         else
7324             cbBuf = 0;
7325         ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
7326         needed += size;
7327         break;
7328
7329     case 2:
7330         size = sizeof(JOB_INFO_2W);
7331         if(cbBuf >= size)
7332         {
7333             cbBuf -= size;
7334             ptr += size;
7335             memset(pJob, 0, size);
7336         }
7337         else
7338             cbBuf = 0;
7339         ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
7340         needed += size;
7341         break;
7342
7343     case 3:
7344         size = sizeof(JOB_INFO_3);
7345         if(cbBuf >= size)
7346         {
7347             cbBuf -= size;
7348             memset(pJob, 0, size);
7349             ret = TRUE;
7350         }
7351         else
7352             cbBuf = 0;
7353         needed = size;
7354         break;
7355
7356     default:
7357         SetLastError(ERROR_INVALID_LEVEL);
7358         goto end;
7359     }
7360     if(pcbNeeded)
7361         *pcbNeeded = needed;
7362 end:
7363     LeaveCriticalSection(&printer_handles_cs);
7364     return ret;
7365 }
7366
7367 /*****************************************************************************
7368  *          GetJobA [WINSPOOL.@]
7369  *
7370  */
7371 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7372                     DWORD cbBuf, LPDWORD pcbNeeded)
7373 {
7374     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
7375 }
7376
7377 /*****************************************************************************
7378  *          GetJobW [WINSPOOL.@]
7379  *
7380  */
7381 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7382                     DWORD cbBuf, LPDWORD pcbNeeded)
7383 {
7384     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
7385 }
7386
7387 /*****************************************************************************
7388  *          schedule_lpr
7389  */
7390 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
7391 {
7392     char *unixname, *queue, *cmd;
7393     char fmt[] = "lpr -P%s %s";
7394     DWORD len;
7395     int r;
7396
7397     if(!(unixname = wine_get_unix_file_name(filename)))
7398         return FALSE;
7399
7400     len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7401     queue = HeapAlloc(GetProcessHeap(), 0, len);
7402     WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7403
7404     cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
7405     sprintf(cmd, fmt, queue, unixname);
7406
7407     TRACE("printing with: %s\n", cmd);
7408     r = system(cmd);
7409
7410     HeapFree(GetProcessHeap(), 0, cmd);
7411     HeapFree(GetProcessHeap(), 0, queue);
7412     HeapFree(GetProcessHeap(), 0, unixname);
7413     return (r == 0);
7414 }
7415
7416 /*****************************************************************************
7417  *          schedule_cups
7418  */
7419 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
7420 {
7421 #ifdef SONAME_LIBCUPS
7422     if(pcupsPrintFile)
7423     {
7424         char *unixname, *queue, *unix_doc_title;
7425         DWORD len;
7426         BOOL ret;
7427
7428         if(!(unixname = wine_get_unix_file_name(filename)))
7429             return FALSE;
7430
7431         len = WideCharToMultiByte(CP_UNIXCP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7432         queue = HeapAlloc(GetProcessHeap(), 0, len);
7433         WideCharToMultiByte(CP_UNIXCP, 0, printer_name, -1, queue, len, NULL, NULL);
7434
7435         len = WideCharToMultiByte(CP_UNIXCP, 0, document_title, -1, NULL, 0, NULL, NULL);
7436         unix_doc_title = HeapAlloc(GetProcessHeap(), 0, len);
7437         WideCharToMultiByte(CP_UNIXCP, 0, document_title, -1, unix_doc_title, len, NULL, NULL);
7438
7439         TRACE("printing via cups\n");
7440         ret = pcupsPrintFile(queue, unixname, unix_doc_title, 0, NULL);
7441         HeapFree(GetProcessHeap(), 0, unix_doc_title);
7442         HeapFree(GetProcessHeap(), 0, queue);
7443         HeapFree(GetProcessHeap(), 0, unixname);
7444         return ret;
7445     }
7446     else
7447 #endif
7448     {
7449         return schedule_lpr(printer_name, filename);
7450     }
7451 }
7452
7453 static INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
7454 {
7455     LPWSTR filename;
7456
7457     switch(msg)
7458     {
7459     case WM_INITDIALOG:
7460         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
7461         return TRUE;
7462
7463     case WM_COMMAND:
7464         if(HIWORD(wparam) == BN_CLICKED)
7465         {
7466             if(LOWORD(wparam) == IDOK)
7467             {
7468                 HANDLE hf;
7469                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
7470                 LPWSTR *output;
7471
7472                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
7473                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
7474
7475                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
7476                 {
7477                     WCHAR caption[200], message[200];
7478                     int mb_ret;
7479
7480                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7481                     LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
7482                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
7483                     if(mb_ret == IDCANCEL)
7484                     {
7485                         HeapFree(GetProcessHeap(), 0, filename);
7486                         return TRUE;
7487                     }
7488                 }
7489                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
7490                 if(hf == INVALID_HANDLE_VALUE)
7491                 {
7492                     WCHAR caption[200], message[200];
7493
7494                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7495                     LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
7496                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
7497                     HeapFree(GetProcessHeap(), 0, filename);
7498                     return TRUE;
7499                 }
7500                 CloseHandle(hf);
7501                 DeleteFileW(filename);
7502                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
7503                 *output = filename;
7504                 EndDialog(hwnd, IDOK);
7505                 return TRUE;
7506             }
7507             if(LOWORD(wparam) == IDCANCEL)
7508             {
7509                 EndDialog(hwnd, IDCANCEL);
7510                 return TRUE;
7511             }
7512         }
7513         return FALSE;
7514     }
7515     return FALSE;
7516 }
7517
7518 /*****************************************************************************
7519  *          get_filename
7520  */
7521 static BOOL get_filename(LPWSTR *filename)
7522 {
7523     return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
7524                            file_dlg_proc, (LPARAM)filename) == IDOK;
7525 }
7526
7527 /*****************************************************************************
7528  *          schedule_file
7529  */
7530 static BOOL schedule_file(LPCWSTR filename)
7531 {
7532     LPWSTR output = NULL;
7533
7534     if(get_filename(&output))
7535     {
7536         BOOL r;
7537         TRACE("copy to %s\n", debugstr_w(output));
7538         r = CopyFileW(filename, output, FALSE);
7539         HeapFree(GetProcessHeap(), 0, output);
7540         return r;
7541     }
7542     return FALSE;
7543 }
7544
7545 /*****************************************************************************
7546  *          schedule_pipe
7547  */
7548 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
7549 {
7550 #ifdef HAVE_FORK
7551     char *unixname, *cmdA;
7552     DWORD len;
7553     int fds[2] = {-1, -1}, file_fd = -1, no_read;
7554     BOOL ret = FALSE;
7555     char buf[1024];
7556
7557     if(!(unixname = wine_get_unix_file_name(filename)))
7558         return FALSE;
7559
7560     len = WideCharToMultiByte(CP_UNIXCP, 0, cmd, -1, NULL, 0, NULL, NULL);
7561     cmdA = HeapAlloc(GetProcessHeap(), 0, len);
7562     WideCharToMultiByte(CP_UNIXCP, 0, cmd, -1, cmdA, len, NULL, NULL);
7563
7564     TRACE("printing with: %s\n", cmdA);
7565
7566     if((file_fd = open(unixname, O_RDONLY)) == -1)
7567         goto end;
7568
7569     if (pipe(fds))
7570     {
7571         ERR("pipe() failed!\n"); 
7572         goto end;
7573     }
7574
7575     if (fork() == 0)
7576     {
7577         close(0);
7578         dup2(fds[0], 0);
7579         close(fds[1]);
7580
7581         /* reset signals that we previously set to SIG_IGN */
7582         signal(SIGPIPE, SIG_DFL);
7583         signal(SIGCHLD, SIG_DFL);
7584
7585         execl("/bin/sh", "/bin/sh", "-c", cmdA, NULL);
7586         _exit(1);
7587     }
7588
7589     while((no_read = read(file_fd, buf, sizeof(buf))) > 0)
7590         write(fds[1], buf, no_read);
7591
7592     ret = TRUE;
7593
7594 end:
7595     if(file_fd != -1) close(file_fd);
7596     if(fds[0] != -1) close(fds[0]);
7597     if(fds[1] != -1) close(fds[1]);
7598
7599     HeapFree(GetProcessHeap(), 0, cmdA);
7600     HeapFree(GetProcessHeap(), 0, unixname);
7601     return ret;
7602 #else
7603     return FALSE;
7604 #endif
7605 }
7606
7607 /*****************************************************************************
7608  *          schedule_unixfile
7609  */
7610 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
7611 {
7612     int in_fd, out_fd, no_read;
7613     char buf[1024];
7614     BOOL ret = FALSE;
7615     char *unixname, *outputA;
7616     DWORD len;
7617
7618     if(!(unixname = wine_get_unix_file_name(filename)))
7619         return FALSE;
7620
7621     len = WideCharToMultiByte(CP_UNIXCP, 0, output, -1, NULL, 0, NULL, NULL);
7622     outputA = HeapAlloc(GetProcessHeap(), 0, len);
7623     WideCharToMultiByte(CP_UNIXCP, 0, output, -1, outputA, len, NULL, NULL);
7624     
7625     out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
7626     in_fd = open(unixname, O_RDONLY);
7627     if(out_fd == -1 || in_fd == -1)
7628         goto end;
7629
7630     while((no_read = read(in_fd, buf, sizeof(buf))) > 0)
7631         write(out_fd, buf, no_read);
7632
7633     ret = TRUE;
7634 end:
7635     if(in_fd != -1) close(in_fd);
7636     if(out_fd != -1) close(out_fd);
7637     HeapFree(GetProcessHeap(), 0, outputA);
7638     HeapFree(GetProcessHeap(), 0, unixname);
7639     return ret;
7640 }
7641
7642 /*****************************************************************************
7643  *          ScheduleJob [WINSPOOL.@]
7644  *
7645  */
7646 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
7647 {
7648     opened_printer_t *printer;
7649     BOOL ret = FALSE;
7650     struct list *cursor, *cursor2;
7651
7652     TRACE("(%p, %x)\n", hPrinter, dwJobID);
7653     EnterCriticalSection(&printer_handles_cs);
7654     printer = get_opened_printer(hPrinter);
7655     if(!printer)
7656         goto end;
7657
7658     LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
7659     {
7660         job_t *job = LIST_ENTRY(cursor, job_t, entry);
7661         HANDLE hf;
7662
7663         if(job->job_id != dwJobID) continue;
7664
7665         hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7666         if(hf != INVALID_HANDLE_VALUE)
7667         {
7668             PRINTER_INFO_5W *pi5;
7669             DWORD needed;
7670             HKEY hkey;
7671             WCHAR output[1024];
7672             static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
7673                                                 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
7674
7675             GetPrinterW(hPrinter, 5, NULL, 0, &needed);
7676             pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
7677             GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
7678             TRACE("need to schedule job %d filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
7679                   debugstr_w(pi5->pPortName));
7680             
7681             output[0] = 0;
7682
7683             /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
7684             if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
7685             {
7686                 DWORD type, count = sizeof(output);
7687                 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
7688                 RegCloseKey(hkey);
7689             }
7690             if(output[0] == '|')
7691             {
7692                 ret = schedule_pipe(output + 1, job->filename);
7693             }
7694             else if(output[0])
7695             {
7696                 ret = schedule_unixfile(output, job->filename);
7697             }
7698             else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
7699             {
7700                 ret = schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
7701             }
7702             else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
7703             {
7704                 ret = schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
7705             }
7706             else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
7707             {
7708                 ret = schedule_file(job->filename);
7709             }
7710             else
7711             {
7712                 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
7713             }
7714             HeapFree(GetProcessHeap(), 0, pi5);
7715             CloseHandle(hf);
7716             DeleteFileW(job->filename);
7717         }
7718         list_remove(cursor);
7719         HeapFree(GetProcessHeap(), 0, job->document_title);
7720         HeapFree(GetProcessHeap(), 0, job->filename);
7721         HeapFree(GetProcessHeap(), 0, job);
7722         break;
7723     }
7724 end:
7725     LeaveCriticalSection(&printer_handles_cs);
7726     return ret;
7727 }
7728
7729 /*****************************************************************************
7730  *          StartDocDlgA [WINSPOOL.@]
7731  */
7732 LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
7733 {
7734     UNICODE_STRING usBuffer;
7735     DOCINFOW docW;
7736     LPWSTR retW;
7737     LPWSTR docnameW = NULL, outputW = NULL, datatypeW = NULL;
7738     LPSTR ret = NULL;
7739
7740     docW.cbSize = sizeof(docW);
7741     if (doc->lpszDocName)
7742     {
7743         docnameW = asciitounicode(&usBuffer, doc->lpszDocName);
7744         if (!(docW.lpszDocName = docnameW)) return NULL;
7745     }
7746     if (doc->lpszOutput)
7747     {
7748         outputW = asciitounicode(&usBuffer, doc->lpszOutput);
7749         if (!(docW.lpszOutput = outputW)) return NULL;
7750     }
7751     if (doc->lpszDatatype)
7752     {
7753         datatypeW = asciitounicode(&usBuffer, doc->lpszDatatype);
7754         if (!(docW.lpszDatatype = datatypeW)) return NULL;
7755     }
7756     docW.fwType = doc->fwType;
7757
7758     retW = StartDocDlgW(hPrinter, &docW);
7759
7760     if(retW)
7761     {
7762         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
7763         ret = HeapAlloc(GetProcessHeap(), 0, len);
7764         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
7765         HeapFree(GetProcessHeap(), 0, retW);
7766     }
7767
7768     HeapFree(GetProcessHeap(), 0, datatypeW);
7769     HeapFree(GetProcessHeap(), 0, outputW);
7770     HeapFree(GetProcessHeap(), 0, docnameW);
7771
7772     return ret;
7773 }
7774
7775 /*****************************************************************************
7776  *          StartDocDlgW [WINSPOOL.@]
7777  *
7778  * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
7779  * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
7780  * port is "FILE:". Also returns the full path if passed a relative path.
7781  *
7782  * The caller should free the returned string from the process heap.
7783  */
7784 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
7785 {
7786     LPWSTR ret = NULL;
7787     DWORD len, attr;
7788
7789     if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
7790     {
7791         PRINTER_INFO_5W *pi5;
7792         GetPrinterW(hPrinter, 5, NULL, 0, &len);
7793         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
7794             return NULL;
7795         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
7796         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
7797         if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
7798         {
7799             HeapFree(GetProcessHeap(), 0, pi5);
7800             return NULL;
7801         }
7802         HeapFree(GetProcessHeap(), 0, pi5);
7803     }
7804
7805     if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
7806     {
7807         LPWSTR name;
7808
7809         if (get_filename(&name))
7810         {
7811             if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
7812             {
7813                 HeapFree(GetProcessHeap(), 0, name);
7814                 return NULL;
7815             }
7816             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7817             GetFullPathNameW(name, len, ret, NULL);
7818             HeapFree(GetProcessHeap(), 0, name);
7819         }
7820         return ret;
7821     }
7822
7823     if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
7824         return NULL;
7825
7826     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7827     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
7828         
7829     attr = GetFileAttributesW(ret);
7830     if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
7831     {
7832         HeapFree(GetProcessHeap(), 0, ret);
7833         ret = NULL;
7834     }
7835     return ret;
7836 }