winspool: Fix the character count passed into RegEnumKeyExW in get_local_monitors.
[wine.git] / dlls / winspool.drv / info.c
blob17cd5073b9794af670973e9a065eba4411f92037
1 /*
2 * WINSPOOL functions
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, 2006, 2007 Detlef Riekenberg
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.
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.
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
26 #include "config.h"
27 #include "wine/port.h"
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
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"
60 #include "ddk/winsplp.h"
61 #include "wspool.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
65 /* ############################### */
67 static CRITICAL_SECTION monitor_handles_cs;
68 static CRITICAL_SECTION_DEBUG monitor_handles_cs_debug =
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") }
74 static CRITICAL_SECTION monitor_handles_cs = { &monitor_handles_cs_debug, -1, 0, 0, 0, 0 };
77 static CRITICAL_SECTION printer_handles_cs;
78 static CRITICAL_SECTION_DEBUG printer_handles_cs_debug =
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") }
84 static CRITICAL_SECTION printer_handles_cs = { &printer_handles_cs_debug, -1, 0, 0, 0, 0 };
86 /* ############################### */
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;
99 typedef struct {
100 DWORD job_id;
101 HANDLE hf;
102 } started_doc_t;
104 typedef struct {
105 struct list jobs;
106 LONG ref;
107 } jobqueue_t;
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;
118 typedef struct {
119 struct list entry;
120 DWORD job_id;
121 WCHAR *filename;
122 WCHAR *document_title;
123 } job_t;
126 typedef struct {
127 LPCWSTR envname;
128 LPCWSTR subdir;
129 DWORD driverversion;
130 LPCWSTR versionregpath;
131 LPCWSTR versionsubdir;
132 } printenv_t;
134 /* ############################### */
136 static struct list monitor_handles = LIST_INIT( monitor_handles );
137 static monitor_t * pm_localport;
139 static opened_printer_t **printer_handles;
140 static UINT nb_printer_handles;
141 static LONG next_job_id = 1;
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 );
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 };
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};
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};
170 static const WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
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};
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};
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};
190 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
191 static const WCHAR envname_win40W[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
192 static const WCHAR envname_x86W[] = {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
193 static const WCHAR subdir_win40W[] = {'w','i','n','4','0',0};
194 static const WCHAR subdir_x86W[] = {'w','3','2','x','8','6',0};
195 static const WCHAR Version0_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','0',0};
196 static const WCHAR Version0_SubdirW[] = {'\\','0',0};
197 static const WCHAR Version3_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
198 static const WCHAR Version3_SubdirW[] = {'\\','3',0};
200 static const WCHAR spooldriversW[] = {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\',0};
201 static const WCHAR spoolprtprocsW[] = {'\\','s','p','o','o','l','\\','p','r','t','p','r','o','c','s','\\',0};
203 static const WCHAR backslashW[] = {'\\',0};
204 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
205 'i','o','n',' ','F','i','l','e',0};
206 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
207 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
208 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0};
209 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
210 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
211 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
212 static const WCHAR HardwareIDW[] = {'H','a','r','d','w','a','r','e','I','D',0};
213 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
214 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
215 static const WCHAR ManufacturerW[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0};
216 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
217 static const WCHAR MonitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
218 static const WCHAR NameW[] = {'N','a','m','e',0};
219 static const WCHAR OEM_UrlW[] = {'O','E','M',' ','U','r','l',0};
220 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
221 static const WCHAR PortW[] = {'P','o','r','t',0};
222 static const WCHAR bs_Ports_bsW[] = {'\\','P','o','r','t','s','\\',0};
223 static const WCHAR Previous_NamesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
224 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e','s','s','o','r',0};
225 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i','v','e','r',0};
226 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i','v','e','r','D','a','t','a',0};
227 static const WCHAR ProviderW[] = {'P','r','o','v','i','d','e','r',0};
228 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F','i','l','e',0};
229 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
230 static const WCHAR VersionW[] = {'V','e','r','s','i','o','n',0};
231 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
232 static const WCHAR deviceW[] = {'d','e','v','i','c','e',0};
233 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
234 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
235 static const WCHAR emptyStringW[] = {0};
236 static const WCHAR XcvMonitorW[] = {',','X','c','v','M','o','n','i','t','o','r',' ',0};
237 static const WCHAR XcvPortW[] = {',','X','c','v','P','o','r','t',' ',0};
239 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
241 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
242 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
243 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
245 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
246 'D','o','c','u','m','e','n','t',0};
248 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
249 sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
250 sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
251 0, sizeof(DRIVER_INFO_8W)};
253 /******************************************************************
254 * validate the user-supplied printing-environment [internal]
256 * PARAMS
257 * env [I] PTR to Environment-String or NULL
259 * RETURNS
260 * Failure: NULL
261 * Success: PTR to printenv_t
263 * NOTES
264 * An empty string is handled the same way as NULL.
265 * SetLastEror(ERROR_INVALID_ENVIRONMENT) is called on Failure
269 static const printenv_t * validate_envW(LPCWSTR env)
271 static const printenv_t env_x86 = {envname_x86W, subdir_x86W,
272 3, Version3_RegPathW, Version3_SubdirW};
273 static const printenv_t env_win40 = {envname_win40W, subdir_win40W,
274 0, Version0_RegPathW, Version0_SubdirW};
276 static const printenv_t * const all_printenv[]={&env_x86, &env_win40};
278 const printenv_t *result = NULL;
279 unsigned int i;
281 TRACE("testing %s\n", debugstr_w(env));
282 if (env && env[0])
284 for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
286 if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
288 result = all_printenv[i];
289 break;
293 if (result == NULL) {
294 FIXME("unsupported Environment: %s\n", debugstr_w(env));
295 SetLastError(ERROR_INVALID_ENVIRONMENT);
297 /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
299 else
301 result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
303 TRACE("using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
305 return result;
309 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
310 if passed a NULL string. This returns NULLs to the result.
312 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
314 if ( (src) )
316 RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
317 return usBufferPtr->Buffer;
319 usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
320 return NULL;
323 static LPWSTR strdupW(LPCWSTR p)
325 LPWSTR ret;
326 DWORD len;
328 if(!p) return NULL;
329 len = (strlenW(p) + 1) * sizeof(WCHAR);
330 ret = HeapAlloc(GetProcessHeap(), 0, len);
331 memcpy(ret, p, len);
332 return ret;
335 static LPSTR strdupWtoA( LPCWSTR str )
337 LPSTR ret;
338 INT len;
340 if (!str) return NULL;
341 len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
342 ret = HeapAlloc( GetProcessHeap(), 0, len );
343 if(ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
344 return ret;
347 /******************************************************************
348 * Return the number of bytes for an multi_sz string.
349 * The result includes all \0s
350 * (specifically the extra \0, that is needed as multi_sz terminator).
352 #if 0
353 static int multi_sz_lenW(const WCHAR *str)
355 const WCHAR *ptr = str;
356 if(!str) return 0;
359 ptr += lstrlenW(ptr) + 1;
360 } while(*ptr);
362 return (ptr - str + 1) * sizeof(WCHAR);
364 #endif
365 /* ################################ */
367 static int multi_sz_lenA(const char *str)
369 const char *ptr = str;
370 if(!str) return 0;
373 ptr += lstrlenA(ptr) + 1;
374 } while(*ptr);
376 return ptr - str + 1;
379 static void
380 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name, BOOL force) {
381 char qbuf[200];
383 /* If forcing, or no profile string entry for device yet, set the entry
385 * The always change entry if not WINEPS yet is discussable.
387 if (force ||
388 !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf)) ||
389 !strcmp(qbuf,"*") ||
390 !strstr(qbuf,"WINEPS.DRV")
392 char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
393 HKEY hkey;
395 sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
396 WriteProfileStringA("windows","device",buf);
397 if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
398 RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
399 RegCloseKey(hkey);
401 HeapFree(GetProcessHeap(),0,buf);
405 static BOOL add_printer_driver(const char *name)
407 DRIVER_INFO_3A di3a;
409 static char driver_9x[] = "wineps16.drv",
410 driver_nt[] = "wineps.drv",
411 env_9x[] = "Windows 4.0",
412 env_nt[] = "Windows NT x86",
413 data_file[] = "generic.ppd",
414 default_data_type[] = "RAW";
416 ZeroMemory(&di3a, sizeof(DRIVER_INFO_3A));
417 di3a.cVersion = 3;
418 di3a.pName = (char *)name;
419 di3a.pEnvironment = env_nt;
420 di3a.pDriverPath = driver_nt;
421 di3a.pDataFile = data_file;
422 di3a.pConfigFile = driver_nt;
423 di3a.pDefaultDataType = default_data_type;
425 if (AddPrinterDriverA(NULL, 3, (LPBYTE)&di3a) ||
426 (GetLastError() == ERROR_PRINTER_DRIVER_ALREADY_INSTALLED ))
428 di3a.cVersion = 0;
429 di3a.pEnvironment = env_9x;
430 di3a.pDriverPath = driver_9x;
431 di3a.pConfigFile = driver_9x;
432 if (AddPrinterDriverA(NULL, 3, (LPBYTE)&di3a) ||
433 (GetLastError() == ERROR_PRINTER_DRIVER_ALREADY_INSTALLED ))
435 return TRUE;
438 ERR("Failed adding driver %s (%s): %u\n", debugstr_a(di3a.pDriverPath),
439 debugstr_a(di3a.pEnvironment), GetLastError());
440 return FALSE;
443 #ifdef SONAME_LIBCUPS
444 static typeof(cupsGetDests) *pcupsGetDests;
445 static typeof(cupsGetPPD) *pcupsGetPPD;
446 static typeof(cupsPrintFile) *pcupsPrintFile;
447 static void *cupshandle;
449 static BOOL CUPS_LoadPrinters(void)
451 int i, nrofdests;
452 BOOL hadprinter = FALSE, haddefault = FALSE;
453 cups_dest_t *dests;
454 PRINTER_INFO_2A pinfo2a;
455 char *port,*devline;
456 HKEY hkeyPrinter, hkeyPrinters, hkey;
457 char loaderror[256];
459 cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, loaderror, sizeof(loaderror));
460 if (!cupshandle) {
461 TRACE("%s\n", loaderror);
462 return FALSE;
464 TRACE("%p: %s loaded\n", cupshandle, SONAME_LIBCUPS);
466 #define DYNCUPS(x) \
467 p##x = wine_dlsym(cupshandle, #x, NULL,0); \
468 if (!p##x) return FALSE;
470 DYNCUPS(cupsGetPPD);
471 DYNCUPS(cupsGetDests);
472 DYNCUPS(cupsPrintFile);
473 #undef DYNCUPS
475 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
476 ERROR_SUCCESS) {
477 ERR("Can't create Printers key\n");
478 return FALSE;
481 nrofdests = pcupsGetDests(&dests);
482 TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
483 for (i=0;i<nrofdests;i++) {
484 port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
485 sprintf(port,"LPR:%s",dests[i].name);
486 devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
487 sprintf(devline,"WINEPS.DRV,%s",port);
488 WriteProfileStringA("devices",dests[i].name,devline);
489 if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
490 RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
491 RegCloseKey(hkey);
493 HeapFree(GetProcessHeap(),0,devline);
495 TRACE("Printer %d: %s\n", i, dests[i].name);
496 if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
497 /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
498 and continue */
499 TRACE("Printer already exists\n");
500 RegDeleteValueW(hkeyPrinter, May_Delete_Value);
501 RegCloseKey(hkeyPrinter);
502 } else {
503 static CHAR data_type[] = "RAW",
504 print_proc[] = "WinPrint",
505 comment[] = "WINEPS Printer using CUPS",
506 location[] = "<physical location of printer>",
507 params[] = "<parameters?>",
508 share_name[] = "<share name?>",
509 sep_file[] = "<sep file?>";
511 add_printer_driver(dests[i].name);
513 memset(&pinfo2a,0,sizeof(pinfo2a));
514 pinfo2a.pPrinterName = dests[i].name;
515 pinfo2a.pDatatype = data_type;
516 pinfo2a.pPrintProcessor = print_proc;
517 pinfo2a.pDriverName = dests[i].name;
518 pinfo2a.pComment = comment;
519 pinfo2a.pLocation = location;
520 pinfo2a.pPortName = port;
521 pinfo2a.pParameters = params;
522 pinfo2a.pShareName = share_name;
523 pinfo2a.pSepFile = sep_file;
525 if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
526 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
527 ERR("printer '%s' not added by AddPrinterA (error %d)\n",dests[i].name,GetLastError());
530 HeapFree(GetProcessHeap(),0,port);
532 hadprinter = TRUE;
533 if (dests[i].is_default) {
534 WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
535 haddefault = TRUE;
538 if (hadprinter & !haddefault)
539 WINSPOOL_SetDefaultPrinter(dests[0].name, dests[0].name, TRUE);
540 RegCloseKey(hkeyPrinters);
541 return hadprinter;
543 #endif
545 static BOOL
546 PRINTCAP_ParseEntry(const char *pent, BOOL isfirst) {
547 PRINTER_INFO_2A pinfo2a;
548 char *e,*s,*name,*prettyname,*devname;
549 BOOL ret = FALSE, set_default = FALSE;
550 char *port = NULL, *devline,*env_default;
551 HKEY hkeyPrinter, hkeyPrinters, hkey;
553 while (isspace(*pent)) pent++;
554 s = strchr(pent,':');
555 if(s) *s='\0';
556 name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
557 strcpy(name,pent);
558 if(s) {
559 *s=':';
560 pent = s;
561 } else
562 pent = "";
564 TRACE("name=%s entry=%s\n",name, pent);
566 if(ispunct(*name)) { /* a tc entry, not a real printer */
567 TRACE("skipping tc entry\n");
568 goto end;
571 if(strstr(pent,":server")) { /* server only version so skip */
572 TRACE("skipping server entry\n");
573 goto end;
576 /* Determine whether this is a postscript printer. */
578 ret = TRUE;
579 env_default = getenv("PRINTER");
580 prettyname = name;
581 /* Get longest name, usually the one at the right for later display. */
582 while((s=strchr(prettyname,'|'))) {
583 *s = '\0';
584 e = s;
585 while(isspace(*--e)) *e = '\0';
586 TRACE("\t%s\n", debugstr_a(prettyname));
587 if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
588 for(prettyname = s+1; isspace(*prettyname); prettyname++)
591 e = prettyname + strlen(prettyname);
592 while(isspace(*--e)) *e = '\0';
593 TRACE("\t%s\n", debugstr_a(prettyname));
594 if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
596 /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
597 * if it is too long, we use it as comment below. */
598 devname = prettyname;
599 if (strlen(devname)>=CCHDEVICENAME-1)
600 devname = name;
601 if (strlen(devname)>=CCHDEVICENAME-1) {
602 ret = FALSE;
603 goto end;
606 port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
607 sprintf(port,"LPR:%s",name);
609 devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
610 sprintf(devline,"WINEPS.DRV,%s",port);
611 WriteProfileStringA("devices",devname,devline);
612 if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
613 RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
614 RegCloseKey(hkey);
616 HeapFree(GetProcessHeap(),0,devline);
618 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
619 ERROR_SUCCESS) {
620 ERR("Can't create Printers key\n");
621 ret = FALSE;
622 goto end;
624 if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
625 /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
626 and continue */
627 TRACE("Printer already exists\n");
628 RegDeleteValueW(hkeyPrinter, May_Delete_Value);
629 RegCloseKey(hkeyPrinter);
630 } else {
631 static CHAR data_type[] = "RAW",
632 print_proc[] = "WinPrint",
633 comment[] = "WINEPS Printer using LPR",
634 params[] = "<parameters?>",
635 share_name[] = "<share name?>",
636 sep_file[] = "<sep file?>";
638 add_printer_driver(devname);
640 memset(&pinfo2a,0,sizeof(pinfo2a));
641 pinfo2a.pPrinterName = devname;
642 pinfo2a.pDatatype = data_type;
643 pinfo2a.pPrintProcessor = print_proc;
644 pinfo2a.pDriverName = devname;
645 pinfo2a.pComment = comment;
646 pinfo2a.pLocation = prettyname;
647 pinfo2a.pPortName = port;
648 pinfo2a.pParameters = params;
649 pinfo2a.pShareName = share_name;
650 pinfo2a.pSepFile = sep_file;
652 if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
653 if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
654 ERR("%s not added by AddPrinterA (%d)\n",name,GetLastError());
657 RegCloseKey(hkeyPrinters);
659 if (isfirst || set_default)
660 WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
662 end:
663 HeapFree(GetProcessHeap(), 0, port);
664 HeapFree(GetProcessHeap(), 0, name);
665 return ret;
668 static BOOL
669 PRINTCAP_LoadPrinters(void) {
670 BOOL hadprinter = FALSE;
671 char buf[200];
672 FILE *f;
673 char *pent = NULL;
674 BOOL had_bash = FALSE;
676 f = fopen("/etc/printcap","r");
677 if (!f)
678 return FALSE;
680 while(fgets(buf,sizeof(buf),f)) {
681 char *start, *end;
683 end=strchr(buf,'\n');
684 if (end) *end='\0';
686 start = buf;
687 while(isspace(*start)) start++;
688 if(*start == '#' || *start == '\0')
689 continue;
691 if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
692 hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
693 HeapFree(GetProcessHeap(),0,pent);
694 pent = NULL;
697 if (end && *--end == '\\') {
698 *end = '\0';
699 had_bash = TRUE;
700 } else
701 had_bash = FALSE;
703 if (pent) {
704 pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
705 strcat(pent,start);
706 } else {
707 pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
708 strcpy(pent,start);
712 if(pent) {
713 hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
714 HeapFree(GetProcessHeap(),0,pent);
716 fclose(f);
717 return hadprinter;
720 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
722 if (value)
723 return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
724 (lstrlenW(value) + 1) * sizeof(WCHAR));
725 else
726 return ERROR_FILE_NOT_FOUND;
729 /*****************************************************************************
730 * enumerate the local monitors (INTERNAL)
732 * returns the needed size (in bytes) for pMonitors
733 * and *lpreturned is set to number of entries returned in pMonitors
736 static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
738 HKEY hroot = NULL;
739 HKEY hentry = NULL;
740 LPWSTR ptr;
741 LPMONITOR_INFO_2W mi;
742 WCHAR buffer[MAX_PATH];
743 WCHAR dllname[MAX_PATH];
744 DWORD dllsize;
745 DWORD len;
746 DWORD index = 0;
747 DWORD needed = 0;
748 DWORD numentries;
749 DWORD entrysize;
751 entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
753 numentries = *lpreturned; /* this is 0, when we scan the registry */
754 len = entrysize * numentries;
755 ptr = (LPWSTR) &pMonitors[len];
757 numentries = 0;
758 len = sizeof(buffer)/sizeof(buffer[0]);
759 buffer[0] = '\0';
761 /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
762 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) == ERROR_SUCCESS) {
763 /* Scan all Monitor-Registry-Keys */
764 while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
765 TRACE("Monitor_%d: %s\n", numentries, debugstr_w(buffer));
766 dllsize = sizeof(dllname);
767 dllname[0] = '\0';
769 /* The Monitor must have a Driver-DLL */
770 if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
771 if (RegQueryValueExW(hentry, DriverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
772 /* We found a valid DLL for this Monitor. */
773 TRACE("using Driver: %s\n", debugstr_w(dllname));
775 RegCloseKey(hentry);
778 /* Windows returns only Port-Monitors here, but to simplify our code,
779 we do no filtering for Language-Monitors */
780 if (dllname[0]) {
781 numentries++;
782 needed += entrysize;
783 needed += (len+1) * sizeof(WCHAR); /* len is lstrlenW(monitorname) */
784 if (level > 1) {
785 /* we install and return only monitors for "Windows NT x86" */
786 needed += (lstrlenW(envname_x86W) +1) * sizeof(WCHAR);
787 needed += dllsize;
790 /* required size is calculated. Now fill the user-buffer */
791 if (pMonitors && (cbBuf >= needed)){
792 mi = (LPMONITOR_INFO_2W) pMonitors;
793 pMonitors += entrysize;
795 TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi, level, numentries);
796 mi->pName = ptr;
797 lstrcpyW(ptr, buffer); /* Name of the Monitor */
798 ptr += (len+1); /* len is lstrlenW(monitorname) */
799 if (level > 1) {
800 mi->pEnvironment = ptr;
801 lstrcpyW(ptr, envname_x86W); /* fixed to "Windows NT x86" */
802 ptr += (lstrlenW(envname_x86W)+1);
804 mi->pDLLName = ptr;
805 lstrcpyW(ptr, dllname); /* Name of the Driver-DLL */
806 ptr += (dllsize / sizeof(WCHAR));
810 index++;
811 len = sizeof(buffer);
812 buffer[0] = '\0';
814 RegCloseKey(hroot);
816 *lpreturned = numentries;
817 TRACE("need %d byte for %d entries\n", needed, numentries);
818 return needed;
821 /******************************************************************
822 * monitor_unload [internal]
824 * release a printmonitor and unload it from memory, when needed
827 static void monitor_unload(monitor_t * pm)
829 if (pm == NULL) return;
830 TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
832 EnterCriticalSection(&monitor_handles_cs);
834 if (pm->refcount) pm->refcount--;
836 if (pm->refcount == 0) {
837 list_remove(&pm->entry);
838 FreeLibrary(pm->hdll);
839 HeapFree(GetProcessHeap(), 0, pm->name);
840 HeapFree(GetProcessHeap(), 0, pm->dllname);
841 HeapFree(GetProcessHeap(), 0, pm);
843 LeaveCriticalSection(&monitor_handles_cs);
846 /******************************************************************
847 * monitor_unloadall [internal]
849 * release all printmonitors and unload them from memory, when needed
852 static void monitor_unloadall(void)
854 monitor_t * pm;
855 monitor_t * next;
857 EnterCriticalSection(&monitor_handles_cs);
858 /* iterate through the list, with safety against removal */
859 LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
861 monitor_unload(pm);
863 LeaveCriticalSection(&monitor_handles_cs);
866 /******************************************************************
867 * monitor_load [internal]
869 * load a printmonitor, get the dllname from the registry, when needed
870 * initialize the monitor and dump found function-pointers
872 * On failure, SetLastError() is called and NULL is returned
875 static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname)
877 LPMONITOR2 (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
878 PMONITORUI (WINAPI *pInitializePrintMonitorUI)(VOID);
879 LPMONITOREX (WINAPI *pInitializePrintMonitor) (LPWSTR);
880 DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
881 DWORD (WINAPI *pInitializeMonitor) (LPWSTR);
883 monitor_t * pm = NULL;
884 monitor_t * cursor;
885 LPWSTR regroot = NULL;
886 LPWSTR driver = dllname;
888 TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
889 /* Is the Monitor already loaded? */
890 EnterCriticalSection(&monitor_handles_cs);
892 if (name) {
893 LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
895 if (cursor->name && (lstrcmpW(name, cursor->name) == 0)) {
896 pm = cursor;
897 break;
902 if (pm == NULL) {
903 pm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(monitor_t));
904 if (pm == NULL) goto cleanup;
905 list_add_tail(&monitor_handles, &pm->entry);
907 pm->refcount++;
909 if (pm->name == NULL) {
910 /* Load the monitor */
911 LPMONITOREX pmonitorEx;
912 DWORD len;
914 if (name) {
915 len = lstrlenW(MonitorsW) + lstrlenW(name) + 2;
916 regroot = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
919 if (regroot) {
920 lstrcpyW(regroot, MonitorsW);
921 lstrcatW(regroot, name);
922 /* Get the Driver from the Registry */
923 if (driver == NULL) {
924 HKEY hroot;
925 DWORD namesize;
926 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
927 if (RegQueryValueExW(hroot, DriverW, NULL, NULL, NULL,
928 &namesize) == ERROR_SUCCESS) {
929 driver = HeapAlloc(GetProcessHeap(), 0, namesize);
930 RegQueryValueExW(hroot, DriverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
932 RegCloseKey(hroot);
937 pm->name = strdupW(name);
938 pm->dllname = strdupW(driver);
940 if ((name && (!regroot || !pm->name)) || !pm->dllname) {
941 monitor_unload(pm);
942 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
943 pm = NULL;
944 goto cleanup;
947 pm->hdll = LoadLibraryW(driver);
948 TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
950 if (pm->hdll == NULL) {
951 monitor_unload(pm);
952 SetLastError(ERROR_MOD_NOT_FOUND);
953 pm = NULL;
954 goto cleanup;
957 pInitializePrintMonitor2 = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
958 pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
959 pInitializePrintMonitor = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
960 pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
961 pInitializeMonitor = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
964 TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
965 TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
966 TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
967 TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
968 TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
970 if (pInitializePrintMonitorUI != NULL) {
971 pm->monitorUI = pInitializePrintMonitorUI();
972 TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver));
973 if (pm->monitorUI) {
974 TRACE( "0x%08x: dwMonitorSize (%d)\n",
975 pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize );
980 if (pInitializePrintMonitor && regroot) {
981 pmonitorEx = pInitializePrintMonitor(regroot);
982 TRACE( "%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
983 pmonitorEx, debugstr_w(driver), debugstr_w(regroot));
985 if (pmonitorEx) {
986 pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
987 pm->monitor = &(pmonitorEx->Monitor);
991 if (pm->monitor) {
992 TRACE( "0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize );
996 if (!pm->monitor && regroot) {
997 if (pInitializePrintMonitor2 != NULL) {
998 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
1000 if (pInitializeMonitorEx != NULL) {
1001 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
1003 if (pInitializeMonitor != NULL) {
1004 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
1007 if (!pm->monitor && !pm->monitorUI) {
1008 monitor_unload(pm);
1009 SetLastError(ERROR_PROC_NOT_FOUND);
1010 pm = NULL;
1013 cleanup:
1014 if ((pm_localport == NULL) && (pm != NULL) && (lstrcmpW(pm->name, LocalPortW) == 0)) {
1015 pm->refcount++;
1016 pm_localport = pm;
1018 LeaveCriticalSection(&monitor_handles_cs);
1019 if (driver != dllname) HeapFree(GetProcessHeap(), 0, driver);
1020 HeapFree(GetProcessHeap(), 0, regroot);
1021 TRACE("=> %p\n", pm);
1022 return pm;
1025 /******************************************************************
1026 * monitor_loadall [internal]
1028 * Load all registered monitors
1031 static DWORD monitor_loadall(void)
1033 monitor_t * pm;
1034 DWORD registered = 0;
1035 DWORD loaded = 0;
1036 HKEY hmonitors;
1037 WCHAR buffer[MAX_PATH];
1038 DWORD id = 0;
1040 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hmonitors) == ERROR_SUCCESS) {
1041 RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, &registered, NULL, NULL,
1042 NULL, NULL, NULL, NULL, NULL);
1044 TRACE("%d monitors registered\n", registered);
1046 EnterCriticalSection(&monitor_handles_cs);
1047 while (id < registered) {
1048 buffer[0] = '\0';
1049 RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
1050 pm = monitor_load(buffer, NULL);
1051 if (pm) loaded++;
1052 id++;
1054 LeaveCriticalSection(&monitor_handles_cs);
1055 RegCloseKey(hmonitors);
1057 TRACE("%d monitors loaded\n", loaded);
1058 return loaded;
1061 /******************************************************************
1062 * monitor_loadui [internal]
1064 * load the userinterface-dll for a given portmonitor
1066 * On failure, NULL is returned
1069 static monitor_t * monitor_loadui(monitor_t * pm)
1071 monitor_t * pui = NULL;
1072 LPWSTR buffer[MAX_PATH];
1073 HANDLE hXcv;
1074 DWORD len;
1075 DWORD res;
1077 if (pm == NULL) return NULL;
1078 TRACE("(%p) => dllname: %s\n", pm, debugstr_w(pm->dllname));
1080 /* Try the Portmonitor first; works for many monitors */
1081 if (pm->monitorUI) {
1082 EnterCriticalSection(&monitor_handles_cs);
1083 pm->refcount++;
1084 LeaveCriticalSection(&monitor_handles_cs);
1085 return pm;
1088 /* query the userinterface-dllname from the Portmonitor */
1089 if ((pm->monitor) && (pm->monitor->pfnXcvDataPort)) {
1090 /* building (",XcvMonitor %s",pm->name) not needed yet */
1091 res = pm->monitor->pfnXcvOpenPort(emptyStringW, SERVER_ACCESS_ADMINISTER, &hXcv);
1092 TRACE("got %u with %p\n", res, hXcv);
1093 if (res) {
1094 res = pm->monitor->pfnXcvDataPort(hXcv, MonitorUIW, NULL, 0, (BYTE *) buffer, sizeof(buffer), &len);
1095 TRACE("got %u with %s\n", res, debugstr_w((LPWSTR) buffer));
1096 if (res == ERROR_SUCCESS) pui = monitor_load(NULL, (LPWSTR) buffer);
1097 pm->monitor->pfnXcvClosePort(hXcv);
1100 return pui;
1104 /******************************************************************
1105 * monitor_load_by_port [internal]
1107 * load a printmonitor for a given port
1109 * On failure, NULL is returned
1112 static monitor_t * monitor_load_by_port(LPCWSTR portname)
1114 HKEY hroot;
1115 HKEY hport;
1116 LPWSTR buffer;
1117 monitor_t * pm = NULL;
1118 DWORD registered = 0;
1119 DWORD id = 0;
1120 DWORD len;
1122 TRACE("(%s)\n", debugstr_w(portname));
1124 /* Try the Local Monitor first */
1125 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot) == ERROR_SUCCESS) {
1126 if (RegQueryValueExW(hroot, portname, NULL, NULL, NULL, &len) == ERROR_SUCCESS) {
1127 /* found the portname */
1128 RegCloseKey(hroot);
1129 return monitor_load(LocalPortW, NULL);
1131 RegCloseKey(hroot);
1134 len = MAX_PATH + lstrlenW(bs_Ports_bsW) + lstrlenW(portname) + 1;
1135 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1136 if (buffer == NULL) return NULL;
1138 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) == ERROR_SUCCESS) {
1139 EnterCriticalSection(&monitor_handles_cs);
1140 RegQueryInfoKeyW(hroot, NULL, NULL, NULL, &registered, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1142 while ((pm == NULL) && (id < registered)) {
1143 buffer[0] = '\0';
1144 RegEnumKeyW(hroot, id, buffer, MAX_PATH);
1145 TRACE("testing %s\n", debugstr_w(buffer));
1146 len = lstrlenW(buffer);
1147 lstrcatW(buffer, bs_Ports_bsW);
1148 lstrcatW(buffer, portname);
1149 if (RegOpenKeyW(hroot, buffer, &hport) == ERROR_SUCCESS) {
1150 RegCloseKey(hport);
1151 buffer[len] = '\0'; /* use only the Monitor-Name */
1152 pm = monitor_load(buffer, NULL);
1154 id++;
1156 LeaveCriticalSection(&monitor_handles_cs);
1157 RegCloseKey(hroot);
1159 HeapFree(GetProcessHeap(), 0, buffer);
1160 return pm;
1163 /******************************************************************
1164 * enumerate the local Ports from all loaded monitors (internal)
1166 * returns the needed size (in bytes) for pPorts
1167 * and *lpreturned is set to number of entries returned in pPorts
1170 static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
1172 monitor_t * pm;
1173 LPWSTR ptr;
1174 LPPORT_INFO_2W cache;
1175 LPPORT_INFO_2W out;
1176 LPBYTE pi_buffer = NULL;
1177 DWORD pi_allocated = 0;
1178 DWORD pi_needed;
1179 DWORD pi_index;
1180 DWORD pi_returned;
1181 DWORD res;
1182 DWORD outindex = 0;
1183 DWORD needed;
1184 DWORD numentries;
1185 DWORD entrysize;
1188 TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
1189 entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
1191 numentries = *lpreturned; /* this is 0, when we scan the registry */
1192 needed = entrysize * numentries;
1193 ptr = (LPWSTR) &pPorts[needed];
1195 numentries = 0;
1196 needed = 0;
1198 LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
1200 if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
1201 pi_needed = 0;
1202 pi_returned = 0;
1203 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
1204 if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
1205 /* Do not use HeapReAlloc (we do not need the old data in the buffer) */
1206 HeapFree(GetProcessHeap(), 0, pi_buffer);
1207 pi_buffer = HeapAlloc(GetProcessHeap(), 0, pi_needed);
1208 pi_allocated = (pi_buffer) ? pi_needed : 0;
1209 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
1211 TRACE( "(%s) got %d with %d (need %d byte for %d entries)\n",
1212 debugstr_w(pm->name), res, GetLastError(), pi_needed, pi_returned);
1214 numentries += pi_returned;
1215 needed += pi_needed;
1217 /* fill the output-buffer (pPorts), if we have one */
1218 if (pPorts && (cbBuf >= needed ) && pi_buffer) {
1219 pi_index = 0;
1220 while (pi_returned > pi_index) {
1221 cache = (LPPORT_INFO_2W) &pi_buffer[pi_index * entrysize];
1222 out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
1223 out->pPortName = ptr;
1224 lstrcpyW(ptr, cache->pPortName);
1225 ptr += (lstrlenW(ptr)+1);
1226 if (level > 1) {
1227 out->pMonitorName = ptr;
1228 lstrcpyW(ptr, cache->pMonitorName);
1229 ptr += (lstrlenW(ptr)+1);
1231 out->pDescription = ptr;
1232 lstrcpyW(ptr, cache->pDescription);
1233 ptr += (lstrlenW(ptr)+1);
1234 out->fPortType = cache->fPortType;
1235 out->Reserved = cache->Reserved;
1237 pi_index++;
1238 outindex++;
1243 /* the temporary portinfo-buffer is no longer needed */
1244 HeapFree(GetProcessHeap(), 0, pi_buffer);
1246 *lpreturned = numentries;
1247 TRACE("need %d byte for %d entries\n", needed, numentries);
1248 return needed;
1251 /******************************************************************
1252 * get_servername_from_name (internal)
1254 * for an external server, a copy of the serverpart from the full name is returned
1257 static LPWSTR get_servername_from_name(LPCWSTR name)
1259 LPWSTR server;
1260 LPWSTR ptr;
1261 WCHAR buffer[MAX_PATH];
1262 DWORD len;
1264 if (name == NULL) return NULL;
1265 if ((name[0] != '\\') || (name[1] != '\\')) return NULL;
1267 server = strdupW(&name[2]); /* skip over both backslash */
1268 if (server == NULL) return NULL;
1270 /* strip '\' and the printername */
1271 ptr = strchrW(server, '\\');
1272 if (ptr) ptr[0] = '\0';
1274 TRACE("found %s\n", debugstr_w(server));
1276 len = sizeof(buffer)/sizeof(buffer[0]);
1277 if (GetComputerNameW(buffer, &len)) {
1278 if (lstrcmpW(buffer, server) == 0) {
1279 /* The requested Servername is our computername */
1280 HeapFree(GetProcessHeap(), 0, server);
1281 return NULL;
1284 return server;
1287 /******************************************************************
1288 * get_basename_from_name (internal)
1290 * skip over the serverpart from the full name
1293 static LPCWSTR get_basename_from_name(LPCWSTR name)
1295 if (name == NULL) return NULL;
1296 if ((name[0] == '\\') && (name[1] == '\\')) {
1297 /* skip over the servername and search for the following '\' */
1298 name = strchrW(&name[2], '\\');
1299 if ((name) && (name[1])) {
1300 /* found a separator ('\') followed by a name:
1301 skip over the separator and return the rest */
1302 name++;
1304 else
1306 /* no basename present (we found only a servername) */
1307 return NULL;
1310 return name;
1313 /******************************************************************
1314 * get_opened_printer_entry
1315 * Get the first place empty in the opened printer table
1317 * ToDo:
1318 * - pDefault is ignored
1320 static HANDLE get_opened_printer_entry(LPCWSTR name, LPPRINTER_DEFAULTSW pDefault)
1322 UINT_PTR handle = nb_printer_handles, i;
1323 jobqueue_t *queue = NULL;
1324 opened_printer_t *printer = NULL;
1325 LPWSTR servername;
1326 LPCWSTR printername;
1327 HKEY hkeyPrinters;
1328 HKEY hkeyPrinter;
1329 DWORD len;
1331 servername = get_servername_from_name(name);
1332 if (servername) {
1333 FIXME("server %s not supported\n", debugstr_w(servername));
1334 HeapFree(GetProcessHeap(), 0, servername);
1335 SetLastError(ERROR_INVALID_PRINTER_NAME);
1336 return NULL;
1339 printername = get_basename_from_name(name);
1340 if (name != printername) TRACE("converted %s to %s\n", debugstr_w(name), debugstr_w(printername));
1342 /* an empty printername is invalid */
1343 if (printername && (!printername[0])) {
1344 SetLastError(ERROR_INVALID_PARAMETER);
1345 return NULL;
1348 EnterCriticalSection(&printer_handles_cs);
1350 for (i = 0; i < nb_printer_handles; i++)
1352 if (!printer_handles[i])
1354 if(handle == nb_printer_handles)
1355 handle = i;
1357 else
1359 if(!queue && (name) && !lstrcmpW(name, printer_handles[i]->name))
1360 queue = printer_handles[i]->queue;
1364 if (handle >= nb_printer_handles)
1366 opened_printer_t **new_array;
1367 if (printer_handles)
1368 new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
1369 (nb_printer_handles + 16) * sizeof(*new_array) );
1370 else
1371 new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1372 (nb_printer_handles + 16) * sizeof(*new_array) );
1374 if (!new_array)
1376 handle = 0;
1377 goto end;
1379 printer_handles = new_array;
1380 nb_printer_handles += 16;
1383 if (!(printer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*printer))))
1385 handle = 0;
1386 goto end;
1390 /* clone the base name. This is NULL for the printserver */
1391 printer->printername = strdupW(printername);
1393 /* clone the full name */
1394 printer->name = strdupW(name);
1395 if (name && (!printer->name)) {
1396 handle = 0;
1397 goto end;
1400 if (printername) {
1401 len = sizeof(XcvMonitorW)/sizeof(WCHAR) - 1;
1402 if (strncmpW(printername, XcvMonitorW, len) == 0) {
1403 /* OpenPrinter(",XcvMonitor " detected */
1404 TRACE(",XcvMonitor: %s\n", debugstr_w(&printername[len]));
1405 printer->pm = monitor_load(&printername[len], NULL);
1406 if (printer->pm == NULL) {
1407 SetLastError(ERROR_UNKNOWN_PORT);
1408 handle = 0;
1409 goto end;
1412 else
1414 len = sizeof(XcvPortW)/sizeof(WCHAR) - 1;
1415 if (strncmpW( printername, XcvPortW, len) == 0) {
1416 /* OpenPrinter(",XcvPort " detected */
1417 TRACE(",XcvPort: %s\n", debugstr_w(&printername[len]));
1418 printer->pm = monitor_load_by_port(&printername[len]);
1419 if (printer->pm == NULL) {
1420 SetLastError(ERROR_UNKNOWN_PORT);
1421 handle = 0;
1422 goto end;
1427 if (printer->pm) {
1428 if ((printer->pm->monitor) && (printer->pm->monitor->pfnXcvOpenPort)) {
1429 printer->pm->monitor->pfnXcvOpenPort(&printername[len],
1430 pDefault ? pDefault->DesiredAccess : 0,
1431 &printer->hXcv);
1433 if (printer->hXcv == NULL) {
1434 SetLastError(ERROR_INVALID_PARAMETER);
1435 handle = 0;
1436 goto end;
1439 else
1441 /* Does the Printer exist? */
1442 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) != ERROR_SUCCESS) {
1443 ERR("Can't create Printers key\n");
1444 handle = 0;
1445 goto end;
1447 if (RegOpenKeyW(hkeyPrinters, printername, &hkeyPrinter) != ERROR_SUCCESS) {
1448 WARN("Printer not found in Registry: %s\n", debugstr_w(printername));
1449 RegCloseKey(hkeyPrinters);
1450 SetLastError(ERROR_INVALID_PRINTER_NAME);
1451 handle = 0;
1452 goto end;
1454 RegCloseKey(hkeyPrinter);
1455 RegCloseKey(hkeyPrinters);
1458 else
1460 TRACE("using the local printserver\n");
1463 if(queue)
1464 printer->queue = queue;
1465 else
1467 printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
1468 if (!printer->queue) {
1469 handle = 0;
1470 goto end;
1472 list_init(&printer->queue->jobs);
1473 printer->queue->ref = 0;
1475 InterlockedIncrement(&printer->queue->ref);
1477 printer_handles[handle] = printer;
1478 handle++;
1479 end:
1480 LeaveCriticalSection(&printer_handles_cs);
1481 if (!handle && printer) {
1482 /* Something failed: Free all resources */
1483 if (printer->hXcv) printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
1484 monitor_unload(printer->pm);
1485 HeapFree(GetProcessHeap(), 0, printer->printername);
1486 HeapFree(GetProcessHeap(), 0, printer->name);
1487 if (!queue) HeapFree(GetProcessHeap(), 0, printer->queue);
1488 HeapFree(GetProcessHeap(), 0, printer);
1491 return (HANDLE)handle;
1494 /******************************************************************
1495 * get_opened_printer
1496 * Get the pointer to the opened printer referred by the handle
1498 static opened_printer_t *get_opened_printer(HANDLE hprn)
1500 UINT_PTR idx = (UINT_PTR)hprn;
1501 opened_printer_t *ret = NULL;
1503 EnterCriticalSection(&printer_handles_cs);
1505 if ((idx > 0) && (idx <= nb_printer_handles)) {
1506 ret = printer_handles[idx - 1];
1508 LeaveCriticalSection(&printer_handles_cs);
1509 return ret;
1512 /******************************************************************
1513 * get_opened_printer_name
1514 * Get the pointer to the opened printer name referred by the handle
1516 static LPCWSTR get_opened_printer_name(HANDLE hprn)
1518 opened_printer_t *printer = get_opened_printer(hprn);
1519 if(!printer) return NULL;
1520 return printer->name;
1523 /******************************************************************
1524 * WINSPOOL_GetOpenedPrinterRegKey
1527 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
1529 LPCWSTR name = get_opened_printer_name(hPrinter);
1530 DWORD ret;
1531 HKEY hkeyPrinters;
1533 if(!name) return ERROR_INVALID_HANDLE;
1535 if((ret = RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters)) !=
1536 ERROR_SUCCESS)
1537 return ret;
1539 if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
1541 ERR("Can't find opened printer %s in registry\n",
1542 debugstr_w(name));
1543 RegCloseKey(hkeyPrinters);
1544 return ERROR_INVALID_PRINTER_NAME; /* ? */
1546 RegCloseKey(hkeyPrinters);
1547 return ERROR_SUCCESS;
1550 void WINSPOOL_LoadSystemPrinters(void)
1552 HKEY hkey, hkeyPrinters;
1553 HANDLE hprn;
1554 DWORD needed, num, i;
1555 WCHAR PrinterName[256];
1556 BOOL done = FALSE;
1558 /* This ensures that all printer entries have a valid Name value. If causes
1559 problems later if they don't. If one is found to be missed we create one
1560 and set it equal to the name of the key */
1561 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
1562 if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
1563 NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
1564 for(i = 0; i < num; i++) {
1565 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) == ERROR_SUCCESS) {
1566 if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
1567 if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
1568 set_reg_szW(hkey, NameW, PrinterName);
1570 RegCloseKey(hkey);
1575 RegCloseKey(hkeyPrinters);
1578 /* We want to avoid calling AddPrinter on printers as much as
1579 possible, because on cups printers this will (eventually) lead
1580 to a call to cupsGetPPD which takes forever, even with non-cups
1581 printers AddPrinter takes a while. So we'll tag all printers that
1582 were automatically added last time around, if they still exist
1583 we'll leave them be otherwise we'll delete them. */
1584 EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1585 if(needed) {
1586 PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1587 if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1588 for(i = 0; i < num; i++) {
1589 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1590 if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1591 if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1592 DWORD dw = 1;
1593 RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
1594 RegCloseKey(hkey);
1596 ClosePrinter(hprn);
1601 HeapFree(GetProcessHeap(), 0, pi);
1605 #ifdef SONAME_LIBCUPS
1606 done = CUPS_LoadPrinters();
1607 #endif
1609 if(!done) /* If we have any CUPS based printers, skip looking for printcap printers */
1610 PRINTCAP_LoadPrinters();
1612 /* Now enumerate the list again and delete any printers that are still tagged */
1613 EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1614 if(needed) {
1615 PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1616 if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1617 for(i = 0; i < num; i++) {
1618 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1619 if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1620 BOOL delete_driver = FALSE;
1621 if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1622 DWORD dw, type, size = sizeof(dw);
1623 if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
1624 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
1625 DeletePrinter(hprn);
1626 delete_driver = TRUE;
1628 RegCloseKey(hkey);
1630 ClosePrinter(hprn);
1631 if(delete_driver)
1632 DeletePrinterDriverExA(NULL, NULL, pi[i].pPrinterName, 0, 0);
1637 HeapFree(GetProcessHeap(), 0, pi);
1640 return;
1644 /******************************************************************
1645 * get_job
1647 * Get the pointer to the specified job.
1648 * Should hold the printer_handles_cs before calling.
1650 static job_t *get_job(HANDLE hprn, DWORD JobId)
1652 opened_printer_t *printer = get_opened_printer(hprn);
1653 job_t *job;
1655 if(!printer) return NULL;
1656 LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
1658 if(job->job_id == JobId)
1659 return job;
1661 return NULL;
1664 /***********************************************************
1665 * DEVMODEcpyAtoW
1667 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
1669 BOOL Formname;
1670 ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
1671 DWORD size;
1673 Formname = (dmA->dmSize > off_formname);
1674 size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
1675 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
1676 dmW->dmDeviceName, CCHDEVICENAME);
1677 if(!Formname) {
1678 memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1679 dmA->dmSize - CCHDEVICENAME);
1680 } else {
1681 memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1682 off_formname - CCHDEVICENAME);
1683 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
1684 dmW->dmFormName, CCHFORMNAME);
1685 memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
1686 (off_formname + CCHFORMNAME));
1688 dmW->dmSize = size;
1689 memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
1690 dmA->dmDriverExtra);
1691 return dmW;
1694 /***********************************************************
1695 * DEVMODEdupWtoA
1696 * Creates an ascii copy of supplied devmode on heap
1698 static LPDEVMODEA DEVMODEdupWtoA(HANDLE heap, const DEVMODEW *dmW)
1700 LPDEVMODEA dmA;
1701 DWORD size;
1702 BOOL Formname;
1703 ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;
1705 if(!dmW) return NULL;
1706 Formname = (dmW->dmSize > off_formname);
1707 size = dmW->dmSize - CCHDEVICENAME - (Formname ? CCHFORMNAME : 0);
1708 dmA = HeapAlloc(heap, HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
1709 WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
1710 (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
1711 if(!Formname) {
1712 memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1713 dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR));
1714 } else {
1715 memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1716 off_formname - CCHDEVICENAME * sizeof(WCHAR));
1717 WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
1718 (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
1719 memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
1720 (off_formname + CCHFORMNAME * sizeof(WCHAR)));
1722 dmA->dmSize = size;
1723 memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
1724 dmW->dmDriverExtra);
1725 return dmA;
1728 /***********************************************************
1729 * PRINTER_INFO_2AtoW
1730 * Creates a unicode copy of PRINTER_INFO_2A on heap
1732 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
1734 LPPRINTER_INFO_2W piW;
1735 UNICODE_STRING usBuffer;
1737 if(!piA) return NULL;
1738 piW = HeapAlloc(heap, 0, sizeof(*piW));
1739 memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
1741 piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
1742 piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
1743 piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
1744 piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
1745 piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
1746 piW->pComment = asciitounicode(&usBuffer,piA->pComment);
1747 piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
1748 piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
1749 piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
1750 piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
1751 piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
1752 piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
1753 return piW;
1756 /***********************************************************
1757 * FREE_PRINTER_INFO_2W
1758 * Free PRINTER_INFO_2W and all strings
1760 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
1762 if(!piW) return;
1764 HeapFree(heap,0,piW->pServerName);
1765 HeapFree(heap,0,piW->pPrinterName);
1766 HeapFree(heap,0,piW->pShareName);
1767 HeapFree(heap,0,piW->pPortName);
1768 HeapFree(heap,0,piW->pDriverName);
1769 HeapFree(heap,0,piW->pComment);
1770 HeapFree(heap,0,piW->pLocation);
1771 HeapFree(heap,0,piW->pDevMode);
1772 HeapFree(heap,0,piW->pSepFile);
1773 HeapFree(heap,0,piW->pPrintProcessor);
1774 HeapFree(heap,0,piW->pDatatype);
1775 HeapFree(heap,0,piW->pParameters);
1776 HeapFree(heap,0,piW);
1777 return;
1780 /******************************************************************
1781 * DeviceCapabilities [WINSPOOL.@]
1782 * DeviceCapabilitiesA [WINSPOOL.@]
1785 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
1786 LPSTR pOutput, LPDEVMODEA lpdm)
1788 INT ret;
1790 if (!GDI_CallDeviceCapabilities16)
1792 GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1793 (LPCSTR)104 );
1794 if (!GDI_CallDeviceCapabilities16) return -1;
1796 ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
1798 /* If DC_PAPERSIZE map POINT16s to POINTs */
1799 if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
1800 POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
1801 POINT *pt = (POINT *)pOutput;
1802 INT i;
1803 memcpy(tmp, pOutput, ret * sizeof(POINT16));
1804 for(i = 0; i < ret; i++, pt++)
1806 pt->x = tmp[i].x;
1807 pt->y = tmp[i].y;
1809 HeapFree( GetProcessHeap(), 0, tmp );
1811 return ret;
1815 /*****************************************************************************
1816 * DeviceCapabilitiesW [WINSPOOL.@]
1818 * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
1821 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
1822 WORD fwCapability, LPWSTR pOutput,
1823 const DEVMODEW *pDevMode)
1825 LPDEVMODEA dmA = DEVMODEdupWtoA(GetProcessHeap(), pDevMode);
1826 LPSTR pDeviceA = strdupWtoA(pDevice);
1827 LPSTR pPortA = strdupWtoA(pPort);
1828 INT ret;
1830 if(pOutput && (fwCapability == DC_BINNAMES ||
1831 fwCapability == DC_FILEDEPENDENCIES ||
1832 fwCapability == DC_PAPERNAMES)) {
1833 /* These need A -> W translation */
1834 INT size = 0, i;
1835 LPSTR pOutputA;
1836 ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
1837 dmA);
1838 if(ret == -1)
1839 return ret;
1840 switch(fwCapability) {
1841 case DC_BINNAMES:
1842 size = 24;
1843 break;
1844 case DC_PAPERNAMES:
1845 case DC_FILEDEPENDENCIES:
1846 size = 64;
1847 break;
1849 pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
1850 ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
1851 dmA);
1852 for(i = 0; i < ret; i++)
1853 MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
1854 pOutput + (i * size), size);
1855 HeapFree(GetProcessHeap(), 0, pOutputA);
1856 } else {
1857 ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
1858 (LPSTR)pOutput, dmA);
1860 HeapFree(GetProcessHeap(),0,pPortA);
1861 HeapFree(GetProcessHeap(),0,pDeviceA);
1862 HeapFree(GetProcessHeap(),0,dmA);
1863 return ret;
1866 /******************************************************************
1867 * DocumentPropertiesA [WINSPOOL.@]
1869 * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1871 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
1872 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
1873 LPDEVMODEA pDevModeInput,DWORD fMode )
1875 LPSTR lpName = pDeviceName;
1876 static CHAR port[] = "LPT1:";
1877 LONG ret;
1879 TRACE("(%p,%p,%s,%p,%p,%d)\n",
1880 hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
1883 if(!pDeviceName) {
1884 LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1885 if(!lpNameW) {
1886 ERR("no name from hPrinter?\n");
1887 SetLastError(ERROR_INVALID_HANDLE);
1888 return -1;
1890 lpName = strdupWtoA(lpNameW);
1893 if (!GDI_CallExtDeviceMode16)
1895 GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1896 (LPCSTR)102 );
1897 if (!GDI_CallExtDeviceMode16) {
1898 ERR("No CallExtDeviceMode16?\n");
1899 return -1;
1902 ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, port,
1903 pDevModeInput, NULL, fMode);
1905 if(!pDeviceName)
1906 HeapFree(GetProcessHeap(),0,lpName);
1907 return ret;
1911 /*****************************************************************************
1912 * DocumentPropertiesW (WINSPOOL.@)
1914 * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1916 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1917 LPWSTR pDeviceName,
1918 LPDEVMODEW pDevModeOutput,
1919 LPDEVMODEW pDevModeInput, DWORD fMode)
1922 LPSTR pDeviceNameA = strdupWtoA(pDeviceName);
1923 LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(GetProcessHeap(),pDevModeInput);
1924 LPDEVMODEA pDevModeOutputA = NULL;
1925 LONG ret;
1927 TRACE("(%p,%p,%s,%p,%p,%d)\n",
1928 hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1929 fMode);
1930 if(pDevModeOutput) {
1931 ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1932 if(ret < 0) return ret;
1933 pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1935 ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1936 pDevModeInputA, fMode);
1937 if(pDevModeOutput) {
1938 DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1939 HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1941 if(fMode == 0 && ret > 0)
1942 ret += (CCHDEVICENAME + CCHFORMNAME);
1943 HeapFree(GetProcessHeap(),0,pDevModeInputA);
1944 HeapFree(GetProcessHeap(),0,pDeviceNameA);
1945 return ret;
1948 /******************************************************************
1949 * OpenPrinterA [WINSPOOL.@]
1951 * See OpenPrinterW.
1954 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
1955 LPPRINTER_DEFAULTSA pDefault)
1957 UNICODE_STRING lpPrinterNameW;
1958 UNICODE_STRING usBuffer;
1959 PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
1960 PWSTR pwstrPrinterNameW;
1961 BOOL ret;
1963 pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
1965 if(pDefault) {
1966 DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
1967 DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
1968 DefaultW.DesiredAccess = pDefault->DesiredAccess;
1969 pDefaultW = &DefaultW;
1971 ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
1972 if(pDefault) {
1973 RtlFreeUnicodeString(&usBuffer);
1974 HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
1976 RtlFreeUnicodeString(&lpPrinterNameW);
1977 return ret;
1980 /******************************************************************
1981 * OpenPrinterW [WINSPOOL.@]
1983 * Open a Printer / Printserver or a Printer-Object
1985 * PARAMS
1986 * lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
1987 * phPrinter [O] The resulting Handle is stored here
1988 * pDefault [I] PTR to Default Printer Settings or NULL
1990 * RETURNS
1991 * Success: TRUE
1992 * Failure: FALSE
1994 * NOTES
1995 * lpPrinterName is one of:
1996 *| Printserver (NT only): "Servername" or NULL for the local Printserver
1997 *| Printer: "PrinterName"
1998 *| Printer-Object: "PrinterName,Job xxx"
1999 *| XcvMonitor: "Servername,XcvMonitor MonitorName"
2000 *| XcvPort: "Servername,XcvPort PortName"
2002 * BUGS
2003 *| Printer-Object not supported
2004 *| pDefaults is ignored
2007 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter, LPPRINTER_DEFAULTSW pDefault)
2010 TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), phPrinter, pDefault);
2011 if (pDefault) {
2012 FIXME("PRINTER_DEFAULTS ignored => %s,%p,0x%08x\n",
2013 debugstr_w(pDefault->pDatatype), pDefault->pDevMode, pDefault->DesiredAccess);
2016 if(!phPrinter) {
2017 /* NT: FALSE with ERROR_INVALID_PARAMETER, 9x: TRUE */
2018 SetLastError(ERROR_INVALID_PARAMETER);
2019 return FALSE;
2022 /* Get the unique handle of the printer or Printserver */
2023 *phPrinter = get_opened_printer_entry(lpPrinterName, pDefault);
2024 TRACE("returning %d with %u and %p\n", *phPrinter != NULL, GetLastError(), *phPrinter);
2025 return (*phPrinter != 0);
2028 /******************************************************************
2029 * AddMonitorA [WINSPOOL.@]
2031 * See AddMonitorW.
2034 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
2036 LPWSTR nameW = NULL;
2037 INT len;
2038 BOOL res;
2039 LPMONITOR_INFO_2A mi2a;
2040 MONITOR_INFO_2W mi2w;
2042 mi2a = (LPMONITOR_INFO_2A) pMonitors;
2043 TRACE("(%s, %d, %p) : %s %s %s\n", debugstr_a(pName), Level, pMonitors,
2044 mi2a ? debugstr_a(mi2a->pName) : NULL,
2045 mi2a ? debugstr_a(mi2a->pEnvironment) : NULL,
2046 mi2a ? debugstr_a(mi2a->pDLLName) : NULL);
2048 if (Level != 2) {
2049 SetLastError(ERROR_INVALID_LEVEL);
2050 return FALSE;
2053 /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2054 if (mi2a == NULL) {
2055 return FALSE;
2058 if (pName) {
2059 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2060 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2061 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2064 memset(&mi2w, 0, sizeof(MONITOR_INFO_2W));
2065 if (mi2a->pName) {
2066 len = MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, NULL, 0);
2067 mi2w.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2068 MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, mi2w.pName, len);
2070 if (mi2a->pEnvironment) {
2071 len = MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, NULL, 0);
2072 mi2w.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2073 MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, mi2w.pEnvironment, len);
2075 if (mi2a->pDLLName) {
2076 len = MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, NULL, 0);
2077 mi2w.pDLLName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2078 MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, mi2w.pDLLName, len);
2081 res = AddMonitorW(nameW, Level, (LPBYTE) &mi2w);
2083 HeapFree(GetProcessHeap(), 0, mi2w.pName);
2084 HeapFree(GetProcessHeap(), 0, mi2w.pEnvironment);
2085 HeapFree(GetProcessHeap(), 0, mi2w.pDLLName);
2087 HeapFree(GetProcessHeap(), 0, nameW);
2088 return (res);
2091 /******************************************************************************
2092 * AddMonitorW [WINSPOOL.@]
2094 * Install a Printmonitor
2096 * PARAMS
2097 * pName [I] Servername or NULL (local Computer)
2098 * Level [I] Structure-Level (Must be 2)
2099 * pMonitors [I] PTR to MONITOR_INFO_2
2101 * RETURNS
2102 * Success: TRUE
2103 * Failure: FALSE
2105 * NOTES
2106 * All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
2109 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
2111 monitor_t * pm = NULL;
2112 LPMONITOR_INFO_2W mi2w;
2113 HKEY hroot = NULL;
2114 HKEY hentry = NULL;
2115 DWORD disposition;
2116 BOOL res = FALSE;
2118 mi2w = (LPMONITOR_INFO_2W) pMonitors;
2119 TRACE("(%s, %d, %p) : %s %s %s\n", debugstr_w(pName), Level, pMonitors,
2120 mi2w ? debugstr_w(mi2w->pName) : NULL,
2121 mi2w ? debugstr_w(mi2w->pEnvironment) : NULL,
2122 mi2w ? debugstr_w(mi2w->pDLLName) : NULL);
2124 if (Level != 2) {
2125 SetLastError(ERROR_INVALID_LEVEL);
2126 return FALSE;
2129 /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2130 if (mi2w == NULL) {
2131 return FALSE;
2134 if (pName && (pName[0])) {
2135 FIXME("for server %s not implemented\n", debugstr_w(pName));
2136 SetLastError(ERROR_ACCESS_DENIED);
2137 return FALSE;
2141 if (!mi2w->pName || (! mi2w->pName[0])) {
2142 WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
2143 SetLastError(ERROR_INVALID_PARAMETER);
2144 return FALSE;
2146 if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, envname_x86W)) {
2147 WARN("Environment %s requested (we support only %s)\n",
2148 debugstr_w(mi2w->pEnvironment), debugstr_w(envname_x86W));
2149 SetLastError(ERROR_INVALID_ENVIRONMENT);
2150 return FALSE;
2153 if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
2154 WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
2155 SetLastError(ERROR_INVALID_PARAMETER);
2156 return FALSE;
2159 /* Load and initialize the monitor. SetLastError() is called on failure */
2160 if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
2161 return FALSE;
2163 monitor_unload(pm);
2165 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
2166 ERR("unable to create key %s\n", debugstr_w(MonitorsW));
2167 return FALSE;
2170 if(RegCreateKeyExW( hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
2171 KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
2172 &disposition) == ERROR_SUCCESS) {
2174 /* Some installers set options for the port before calling AddMonitor.
2175 We query the "Driver" entry to verify that the monitor is installed,
2176 before we return an error.
2177 When a user installs two print monitors at the same time with the
2178 same name but with a different driver DLL and a task switch comes
2179 between RegQueryValueExW and RegSetValueExW, a race condition
2180 is possible but silently ignored. */
2182 DWORD namesize = 0;
2184 if ((disposition == REG_OPENED_EXISTING_KEY) &&
2185 (RegQueryValueExW(hentry, DriverW, NULL, NULL, NULL,
2186 &namesize) == ERROR_SUCCESS)) {
2187 TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
2188 /* NT: ERROR_PRINT_MONITOR_ALREADY_INSTALLED (3006)
2189 9x: ERROR_ALREADY_EXISTS (183) */
2190 SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
2192 else
2194 INT len;
2195 len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
2196 res = (RegSetValueExW(hentry, DriverW, 0,
2197 REG_SZ, (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
2199 RegCloseKey(hentry);
2202 RegCloseKey(hroot);
2203 return (res);
2206 /******************************************************************
2207 * DeletePrinterDriverA [WINSPOOL.@]
2210 BOOL WINAPI DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
2212 return DeletePrinterDriverExA(pName, pEnvironment, pDriverName, 0, 0);
2215 /******************************************************************
2216 * DeletePrinterDriverW [WINSPOOL.@]
2219 BOOL WINAPI DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
2221 return DeletePrinterDriverExW(pName, pEnvironment, pDriverName, 0, 0);
2224 /******************************************************************
2225 * DeleteMonitorA [WINSPOOL.@]
2227 * See DeleteMonitorW.
2230 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
2232 LPWSTR nameW = NULL;
2233 LPWSTR EnvironmentW = NULL;
2234 LPWSTR MonitorNameW = NULL;
2235 BOOL res;
2236 INT len;
2238 if (pName) {
2239 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2240 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2241 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2244 if (pEnvironment) {
2245 len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
2246 EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2247 MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
2249 if (pMonitorName) {
2250 len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
2251 MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2252 MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
2255 res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
2257 HeapFree(GetProcessHeap(), 0, MonitorNameW);
2258 HeapFree(GetProcessHeap(), 0, EnvironmentW);
2259 HeapFree(GetProcessHeap(), 0, nameW);
2260 return (res);
2263 /******************************************************************
2264 * DeleteMonitorW [WINSPOOL.@]
2266 * Delete a specific Printmonitor from a Printing-Environment
2268 * PARAMS
2269 * pName [I] Servername or NULL (local Computer)
2270 * pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
2271 * pMonitorName [I] Name of the Monitor, that should be deleted
2273 * RETURNS
2274 * Success: TRUE
2275 * Failure: FALSE
2277 * NOTES
2278 * pEnvironment is ignored in Windows for the local Computer.
2282 BOOL WINAPI DeleteMonitorW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
2284 HKEY hroot = NULL;
2286 TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
2287 debugstr_w(pMonitorName));
2289 if (pName && (pName[0])) {
2290 FIXME("for server %s not implemented\n", debugstr_w(pName));
2291 SetLastError(ERROR_ACCESS_DENIED);
2292 return FALSE;
2295 /* pEnvironment is ignored in Windows for the local Computer */
2297 if (!pMonitorName || !pMonitorName[0]) {
2298 WARN("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
2299 SetLastError(ERROR_INVALID_PARAMETER);
2300 return FALSE;
2303 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
2304 ERR("unable to create key %s\n", debugstr_w(MonitorsW));
2305 return FALSE;
2308 if(RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS) {
2309 TRACE("monitor %s deleted\n", debugstr_w(pMonitorName));
2310 RegCloseKey(hroot);
2311 return TRUE;
2314 WARN("monitor %s does not exist\n", debugstr_w(pMonitorName));
2315 RegCloseKey(hroot);
2317 /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
2318 SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
2319 return (FALSE);
2322 /******************************************************************
2323 * DeletePortA [WINSPOOL.@]
2325 * See DeletePortW.
2328 BOOL WINAPI DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
2330 LPWSTR nameW = NULL;
2331 LPWSTR portW = NULL;
2332 INT len;
2333 DWORD res;
2335 TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
2337 /* convert servername to unicode */
2338 if (pName) {
2339 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2340 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2341 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2344 /* convert portname to unicode */
2345 if (pPortName) {
2346 len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
2347 portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2348 MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
2351 res = DeletePortW(nameW, hWnd, portW);
2352 HeapFree(GetProcessHeap(), 0, nameW);
2353 HeapFree(GetProcessHeap(), 0, portW);
2354 return res;
2357 /******************************************************************
2358 * DeletePortW [WINSPOOL.@]
2360 * Delete a specific Port
2362 * PARAMS
2363 * pName [I] Servername or NULL (local Computer)
2364 * hWnd [I] Handle to parent Window for the Dialog-Box
2365 * pPortName [I] Name of the Port, that should be deleted
2367 * RETURNS
2368 * Success: TRUE
2369 * Failure: FALSE
2372 BOOL WINAPI DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
2374 monitor_t * pm;
2375 monitor_t * pui;
2376 DWORD res;
2378 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
2380 if (pName && pName[0]) {
2381 SetLastError(ERROR_INVALID_PARAMETER);
2382 return FALSE;
2385 if (!pPortName) {
2386 SetLastError(RPC_X_NULL_REF_POINTER);
2387 return FALSE;
2390 /* an empty Portname is Invalid */
2391 if (!pPortName[0]) {
2392 SetLastError(ERROR_NOT_SUPPORTED);
2393 return FALSE;
2396 pm = monitor_load_by_port(pPortName);
2397 if (pm && pm->monitor && pm->monitor->pfnDeletePort) {
2398 TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
2399 res = pm->monitor->pfnDeletePort(pName, hWnd, pPortName);
2400 TRACE("got %d with %u\n", res, GetLastError());
2402 else
2404 pui = monitor_loadui(pm);
2405 if (pui && pui->monitorUI && pui->monitorUI->pfnDeletePortUI) {
2406 TRACE("use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
2407 res = pui->monitorUI->pfnDeletePortUI(pName, hWnd, pPortName);
2408 TRACE("got %d with %u\n", res, GetLastError());
2410 else
2412 FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
2413 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
2415 /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
2416 SetLastError(ERROR_NOT_SUPPORTED);
2417 res = FALSE;
2419 monitor_unload(pui);
2421 monitor_unload(pm);
2423 TRACE("returning %d with %u\n", res, GetLastError());
2424 return res;
2427 /******************************************************************************
2428 * SetPrinterW [WINSPOOL.@]
2430 BOOL WINAPI SetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command)
2432 FIXME("(%p, %d, %p, %d): stub\n", hPrinter, Level, pPrinter, Command);
2433 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2434 return FALSE;
2437 /******************************************************************************
2438 * WritePrinter [WINSPOOL.@]
2440 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
2442 opened_printer_t *printer;
2443 BOOL ret = FALSE;
2445 TRACE("(%p, %p, %d, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
2447 EnterCriticalSection(&printer_handles_cs);
2448 printer = get_opened_printer(hPrinter);
2449 if(!printer)
2451 SetLastError(ERROR_INVALID_HANDLE);
2452 goto end;
2455 if(!printer->doc)
2457 SetLastError(ERROR_SPL_NO_STARTDOC);
2458 goto end;
2461 ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
2462 end:
2463 LeaveCriticalSection(&printer_handles_cs);
2464 return ret;
2467 /*****************************************************************************
2468 * AddFormA [WINSPOOL.@]
2470 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2472 FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2473 return 1;
2476 /*****************************************************************************
2477 * AddFormW [WINSPOOL.@]
2479 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2481 FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2482 return 1;
2485 /*****************************************************************************
2486 * AddJobA [WINSPOOL.@]
2488 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2490 BOOL ret;
2491 BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2492 DWORD needed;
2494 if(Level != 1) {
2495 SetLastError(ERROR_INVALID_LEVEL);
2496 return FALSE;
2499 ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
2501 if(ret) {
2502 ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
2503 DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
2504 *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
2505 if(*pcbNeeded > cbBuf) {
2506 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2507 ret = FALSE;
2508 } else {
2509 ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
2510 addjobA->JobId = addjobW->JobId;
2511 addjobA->Path = (char *)(addjobA + 1);
2512 WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
2515 return ret;
2518 /*****************************************************************************
2519 * AddJobW [WINSPOOL.@]
2521 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2523 opened_printer_t *printer;
2524 job_t *job;
2525 BOOL ret = FALSE;
2526 static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
2527 static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
2528 WCHAR path[MAX_PATH], filename[MAX_PATH];
2529 DWORD len;
2530 ADDJOB_INFO_1W *addjob;
2532 TRACE("(%p,%d,%p,%d,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
2534 EnterCriticalSection(&printer_handles_cs);
2536 printer = get_opened_printer(hPrinter);
2538 if(!printer) {
2539 SetLastError(ERROR_INVALID_HANDLE);
2540 goto end;
2543 if(Level != 1) {
2544 SetLastError(ERROR_INVALID_LEVEL);
2545 goto end;
2548 job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
2549 if(!job)
2550 goto end;
2552 job->job_id = InterlockedIncrement(&next_job_id);
2554 len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
2555 if(path[len - 1] != '\\')
2556 path[len++] = '\\';
2557 memcpy(path + len, spool_path, sizeof(spool_path));
2558 sprintfW(filename, fmtW, path, job->job_id);
2560 len = strlenW(filename);
2561 job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2562 memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
2563 job->document_title = strdupW(default_doc_title);
2564 list_add_tail(&printer->queue->jobs, &job->entry);
2566 *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
2567 if(*pcbNeeded <= cbBuf) {
2568 addjob = (ADDJOB_INFO_1W*)pData;
2569 addjob->JobId = job->job_id;
2570 addjob->Path = (WCHAR *)(addjob + 1);
2571 memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
2572 ret = TRUE;
2573 } else
2574 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2576 end:
2577 LeaveCriticalSection(&printer_handles_cs);
2578 return ret;
2581 /*****************************************************************************
2582 * GetPrintProcessorDirectoryA [WINSPOOL.@]
2584 * Return the PATH for the Print-Processors
2586 * See GetPrintProcessorDirectoryW.
2590 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
2591 DWORD level, LPBYTE Info,
2592 DWORD cbBuf, LPDWORD pcbNeeded)
2594 LPWSTR serverW = NULL;
2595 LPWSTR envW = NULL;
2596 BOOL ret;
2597 INT len;
2599 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(server),
2600 debugstr_a(env), level, Info, cbBuf, pcbNeeded);
2603 if (server) {
2604 len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
2605 serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2606 MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
2609 if (env) {
2610 len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
2611 envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2612 MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
2615 /* NT requires the buffersize from GetPrintProcessorDirectoryW also
2616 for GetPrintProcessorDirectoryA and WC2MB is done in-place.
2618 ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info,
2619 cbBuf, pcbNeeded);
2621 if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
2622 cbBuf, NULL, NULL) > 0;
2625 TRACE(" required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
2626 HeapFree(GetProcessHeap(), 0, envW);
2627 HeapFree(GetProcessHeap(), 0, serverW);
2628 return ret;
2631 /*****************************************************************************
2632 * GetPrintProcessorDirectoryW [WINSPOOL.@]
2634 * Return the PATH for the Print-Processors
2636 * PARAMS
2637 * server [I] Servername (NT only) or NULL (local Computer)
2638 * env [I] Printing-Environment (see below) or NULL (Default)
2639 * level [I] Structure-Level (must be 1)
2640 * Info [O] PTR to Buffer that receives the Result
2641 * cbBuf [I] Size of Buffer at "Info"
2642 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
2643 * required for the Buffer at "Info"
2645 * RETURNS
2646 * Success: TRUE and in pcbNeeded the Bytes used in Info
2647 * Failure: FALSE and in pcbNeeded the Bytes required for Info,
2648 * if cbBuf is too small
2650 * Native Values returned in Info on Success:
2651 *| NT(Windows NT x86): "%winsysdir%\\spool\\PRTPROCS\\w32x86"
2652 *| NT(Windows 4.0): "%winsysdir%\\spool\\PRTPROCS\\win40"
2653 *| win9x(Windows 4.0): "%winsysdir%"
2655 * "%winsysdir%" is the Value from GetSystemDirectoryW()
2657 * BUGS
2658 * Only NULL or "" is supported for server
2661 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
2662 DWORD level, LPBYTE Info,
2663 DWORD cbBuf, LPDWORD pcbNeeded)
2665 DWORD needed;
2666 const printenv_t * env_t;
2668 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(server),
2669 debugstr_w(env), level, Info, cbBuf, pcbNeeded);
2671 if(server != NULL && server[0]) {
2672 FIXME("server not supported: %s\n", debugstr_w(server));
2673 SetLastError(ERROR_INVALID_PARAMETER);
2674 return FALSE;
2677 env_t = validate_envW(env);
2678 if(!env_t) return FALSE; /* environment invalid or unsupported */
2680 if(level != 1) {
2681 WARN("(Level: %d) is ignored in win9x\n", level);
2682 SetLastError(ERROR_INVALID_LEVEL);
2683 return FALSE;
2686 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2687 needed = GetSystemDirectoryW(NULL, 0);
2688 /* add the Size for the Subdirectories */
2689 needed += lstrlenW(spoolprtprocsW);
2690 needed += lstrlenW(env_t->subdir);
2691 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
2693 if(pcbNeeded) *pcbNeeded = needed;
2694 TRACE ("required: 0x%x/%d\n", needed, needed);
2695 if (needed > cbBuf) {
2696 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2697 return FALSE;
2699 if(pcbNeeded == NULL) {
2700 /* NT: RPC_X_NULL_REF_POINTER, 9x: ignored */
2701 WARN("(pcbNeeded == NULL) is ignored in win9x\n");
2702 SetLastError(RPC_X_NULL_REF_POINTER);
2703 return FALSE;
2705 if(Info == NULL) {
2706 /* NT: RPC_X_NULL_REF_POINTER, 9x: ERROR_INVALID_PARAMETER */
2707 SetLastError(RPC_X_NULL_REF_POINTER);
2708 return FALSE;
2711 GetSystemDirectoryW((LPWSTR) Info, cbBuf/sizeof(WCHAR));
2712 /* add the Subdirectories */
2713 lstrcatW((LPWSTR) Info, spoolprtprocsW);
2714 lstrcatW((LPWSTR) Info, env_t->subdir);
2715 TRACE(" => %s\n", debugstr_w((LPWSTR) Info));
2716 return TRUE;
2719 /*****************************************************************************
2720 * WINSPOOL_OpenDriverReg [internal]
2722 * opens the registry for the printer drivers depending on the given input
2723 * variable pEnvironment
2725 * RETURNS:
2726 * the opened hkey on success
2727 * NULL on error
2729 static HKEY WINSPOOL_OpenDriverReg( LPCVOID pEnvironment, BOOL unicode)
2731 HKEY retval = NULL;
2732 LPWSTR buffer;
2733 const printenv_t * env;
2735 TRACE("(%s, %d)\n",
2736 (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment), unicode);
2738 if (!pEnvironment || unicode) {
2739 /* pEnvironment was NULL or an Unicode-String: use it direct */
2740 env = validate_envW(pEnvironment);
2742 else
2744 /* pEnvironment was an ANSI-String: convert to unicode first */
2745 LPWSTR buffer;
2746 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
2747 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2748 if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
2749 env = validate_envW(buffer);
2750 HeapFree(GetProcessHeap(), 0, buffer);
2752 if (!env) return NULL;
2754 buffer = HeapAlloc( GetProcessHeap(), 0,
2755 (strlenW(DriversW) + strlenW(env->envname) +
2756 strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2757 if(buffer) {
2758 wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2759 RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2760 HeapFree(GetProcessHeap(), 0, buffer);
2762 return retval;
2765 /*****************************************************************************
2766 * AddPrinterW [WINSPOOL.@]
2768 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2770 PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2771 LPDEVMODEA dmA;
2772 LPDEVMODEW dmW;
2773 HANDLE retval;
2774 HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2775 LONG size;
2776 static const WCHAR attributesW[] = {'A','t','t','r','i','b','u','t','e','s',0},
2777 default_devmodeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0},
2778 priorityW[] = {'P','r','i','o','r','i','t','y',0},
2779 start_timeW[] = {'S','t','a','r','t','T','i','m','e',0},
2780 statusW[] = {'S','t','a','t','u','s',0},
2781 until_timeW[] = {'U','n','t','i','l','T','i','m','e',0};
2783 TRACE("(%s,%d,%p)\n", debugstr_w(pName), Level, pPrinter);
2785 if(pName != NULL) {
2786 ERR("pName = %s - unsupported\n", debugstr_w(pName));
2787 SetLastError(ERROR_INVALID_PARAMETER);
2788 return 0;
2790 if(Level != 2) {
2791 ERR("Level = %d, unsupported!\n", Level);
2792 SetLastError(ERROR_INVALID_LEVEL);
2793 return 0;
2795 if(!pPrinter) {
2796 SetLastError(ERROR_INVALID_PARAMETER);
2797 return 0;
2799 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2800 ERROR_SUCCESS) {
2801 ERR("Can't create Printers key\n");
2802 return 0;
2804 if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2805 if (!RegQueryValueW(hkeyPrinter, attributesW, NULL, NULL)) {
2806 SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2807 RegCloseKey(hkeyPrinter);
2808 RegCloseKey(hkeyPrinters);
2809 return 0;
2811 RegCloseKey(hkeyPrinter);
2813 hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
2814 if(!hkeyDrivers) {
2815 ERR("Can't create Drivers key\n");
2816 RegCloseKey(hkeyPrinters);
2817 return 0;
2819 if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2820 ERROR_SUCCESS) {
2821 WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2822 RegCloseKey(hkeyPrinters);
2823 RegCloseKey(hkeyDrivers);
2824 SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2825 return 0;
2827 RegCloseKey(hkeyDriver);
2828 RegCloseKey(hkeyDrivers);
2830 if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) { /* FIXME */
2831 FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2832 SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2833 RegCloseKey(hkeyPrinters);
2834 return 0;
2837 if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2838 ERROR_SUCCESS) {
2839 FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2840 SetLastError(ERROR_INVALID_PRINTER_NAME);
2841 RegCloseKey(hkeyPrinters);
2842 return 0;
2844 RegSetValueExW(hkeyPrinter, attributesW, 0, REG_DWORD,
2845 (LPBYTE)&pi->Attributes, sizeof(DWORD));
2846 set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2848 /* See if we can load the driver. We may need the devmode structure anyway
2850 * FIXME:
2851 * Note that DocumentPropertiesW will briefly try to open the printer we
2852 * just create to find a DEVMODEA struct (it will use the WINEPS default
2853 * one in case it is not there, so we are ok).
2855 size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2857 if(size < 0) {
2858 FIXME("DocumentPropertiesW on printer %s fails\n", debugstr_w(pi->pPrinterName));
2859 size = sizeof(DEVMODEW);
2861 if(pi->pDevMode)
2862 dmW = pi->pDevMode;
2863 else
2865 dmW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
2866 dmW->dmSize = size;
2867 if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER))
2869 WARN("DocumentPropertiesW on printer %s failed!\n", debugstr_w(pi->pPrinterName));
2870 HeapFree(GetProcessHeap(),0,dmW);
2871 dmW=NULL;
2873 else
2875 /* set devmode to printer name */
2876 lstrcpynW(dmW->dmDeviceName, pi->pPrinterName, CCHDEVICENAME);
2880 /* Write DEVMODEA not DEVMODEW into reg. This is what win9x does
2881 and we support these drivers. NT writes DEVMODEW so somehow
2882 we'll need to distinguish between these when we support NT
2883 drivers */
2884 if (dmW)
2886 dmA = DEVMODEdupWtoA(GetProcessHeap(), dmW);
2887 RegSetValueExW(hkeyPrinter, default_devmodeW, 0, REG_BINARY,
2888 (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
2889 HeapFree(GetProcessHeap(), 0, dmA);
2890 if(!pi->pDevMode)
2891 HeapFree(GetProcessHeap(), 0, dmW);
2893 set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
2894 set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
2895 set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
2896 set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
2898 set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
2899 set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
2900 set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
2901 RegSetValueExW(hkeyPrinter, priorityW, 0, REG_DWORD,
2902 (LPBYTE)&pi->Priority, sizeof(DWORD));
2903 set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
2904 set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
2905 RegSetValueExW(hkeyPrinter, start_timeW, 0, REG_DWORD,
2906 (LPBYTE)&pi->StartTime, sizeof(DWORD));
2907 RegSetValueExW(hkeyPrinter, statusW, 0, REG_DWORD,
2908 (LPBYTE)&pi->Status, sizeof(DWORD));
2909 RegSetValueExW(hkeyPrinter, until_timeW, 0, REG_DWORD,
2910 (LPBYTE)&pi->UntilTime, sizeof(DWORD));
2912 RegCloseKey(hkeyPrinter);
2913 RegCloseKey(hkeyPrinters);
2914 if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
2915 ERR("OpenPrinter failing\n");
2916 return 0;
2918 return retval;
2921 /*****************************************************************************
2922 * AddPrinterA [WINSPOOL.@]
2924 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
2926 UNICODE_STRING pNameW;
2927 PWSTR pwstrNameW;
2928 PRINTER_INFO_2W *piW;
2929 PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
2930 HANDLE ret;
2932 TRACE("(%s,%d,%p): stub\n", debugstr_a(pName), Level, pPrinter);
2933 if(Level != 2) {
2934 ERR("Level = %d, unsupported!\n", Level);
2935 SetLastError(ERROR_INVALID_LEVEL);
2936 return 0;
2938 pwstrNameW = asciitounicode(&pNameW,pName);
2939 piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
2941 ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
2943 FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
2944 RtlFreeUnicodeString(&pNameW);
2945 return ret;
2949 /*****************************************************************************
2950 * ClosePrinter [WINSPOOL.@]
2952 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
2954 UINT_PTR i = (UINT_PTR)hPrinter;
2955 opened_printer_t *printer = NULL;
2956 BOOL ret = FALSE;
2958 TRACE("(%p)\n", hPrinter);
2960 EnterCriticalSection(&printer_handles_cs);
2962 if ((i > 0) && (i <= nb_printer_handles))
2963 printer = printer_handles[i - 1];
2966 if(printer)
2968 struct list *cursor, *cursor2;
2970 TRACE("%p: %s (hXcv: %p) for %s (doc: %p)\n", printer->pm,
2971 debugstr_w(printer->pm ? printer->pm->dllname : NULL),
2972 printer->hXcv, debugstr_w(printer->name), printer->doc );
2974 if(printer->doc)
2975 EndDocPrinter(hPrinter);
2977 if(InterlockedDecrement(&printer->queue->ref) == 0)
2979 LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
2981 job_t *job = LIST_ENTRY(cursor, job_t, entry);
2982 ScheduleJob(hPrinter, job->job_id);
2984 HeapFree(GetProcessHeap(), 0, printer->queue);
2986 if (printer->hXcv) printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
2987 monitor_unload(printer->pm);
2988 HeapFree(GetProcessHeap(), 0, printer->printername);
2989 HeapFree(GetProcessHeap(), 0, printer->name);
2990 HeapFree(GetProcessHeap(), 0, printer);
2991 printer_handles[i - 1] = NULL;
2992 ret = TRUE;
2994 LeaveCriticalSection(&printer_handles_cs);
2995 return ret;
2998 /*****************************************************************************
2999 * DeleteFormA [WINSPOOL.@]
3001 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
3003 FIXME("(%p,%s): stub\n", hPrinter, pFormName);
3004 return 1;
3007 /*****************************************************************************
3008 * DeleteFormW [WINSPOOL.@]
3010 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
3012 FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
3013 return 1;
3016 /*****************************************************************************
3017 * DeletePrinter [WINSPOOL.@]
3019 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
3021 LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
3022 HKEY hkeyPrinters, hkey;
3024 if(!lpNameW) {
3025 SetLastError(ERROR_INVALID_HANDLE);
3026 return FALSE;
3028 if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
3029 RegDeleteTreeW(hkeyPrinters, lpNameW);
3030 RegCloseKey(hkeyPrinters);
3032 WriteProfileStringW(devicesW, lpNameW, NULL);
3033 if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
3034 RegDeleteValueW(hkey, lpNameW);
3035 RegCloseKey(hkey);
3037 return TRUE;
3040 /*****************************************************************************
3041 * SetPrinterA [WINSPOOL.@]
3043 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3044 DWORD Command)
3046 FIXME("(%p,%d,%p,%d): stub\n",hPrinter,Level,pPrinter,Command);
3047 return FALSE;
3050 /*****************************************************************************
3051 * SetJobA [WINSPOOL.@]
3053 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
3054 LPBYTE pJob, DWORD Command)
3056 BOOL ret;
3057 LPBYTE JobW;
3058 UNICODE_STRING usBuffer;
3060 TRACE("(%p, %d, %d, %p, %d)\n",hPrinter, JobId, Level, pJob, Command);
3062 /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
3063 are all ignored by SetJob, so we don't bother copying them */
3064 switch(Level)
3066 case 0:
3067 JobW = NULL;
3068 break;
3069 case 1:
3071 JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
3072 JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
3074 JobW = (LPBYTE)info1W;
3075 info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
3076 info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
3077 info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
3078 info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
3079 info1W->Status = info1A->Status;
3080 info1W->Priority = info1A->Priority;
3081 info1W->Position = info1A->Position;
3082 info1W->PagesPrinted = info1A->PagesPrinted;
3083 break;
3085 case 2:
3087 JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
3088 JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
3090 JobW = (LPBYTE)info2W;
3091 info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
3092 info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
3093 info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
3094 info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
3095 info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
3096 info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
3097 info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
3098 info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
3099 info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
3100 info2W->Status = info2A->Status;
3101 info2W->Priority = info2A->Priority;
3102 info2W->Position = info2A->Position;
3103 info2W->StartTime = info2A->StartTime;
3104 info2W->UntilTime = info2A->UntilTime;
3105 info2W->PagesPrinted = info2A->PagesPrinted;
3106 break;
3108 case 3:
3109 JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
3110 memcpy(JobW, pJob, sizeof(JOB_INFO_3));
3111 break;
3112 default:
3113 SetLastError(ERROR_INVALID_LEVEL);
3114 return FALSE;
3117 ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
3119 switch(Level)
3121 case 1:
3123 JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
3124 HeapFree(GetProcessHeap(), 0, info1W->pUserName);
3125 HeapFree(GetProcessHeap(), 0, info1W->pDocument);
3126 HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
3127 HeapFree(GetProcessHeap(), 0, info1W->pStatus);
3128 break;
3130 case 2:
3132 JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
3133 HeapFree(GetProcessHeap(), 0, info2W->pUserName);
3134 HeapFree(GetProcessHeap(), 0, info2W->pDocument);
3135 HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
3136 HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
3137 HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
3138 HeapFree(GetProcessHeap(), 0, info2W->pParameters);
3139 HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
3140 HeapFree(GetProcessHeap(), 0, info2W->pStatus);
3141 break;
3144 HeapFree(GetProcessHeap(), 0, JobW);
3146 return ret;
3149 /*****************************************************************************
3150 * SetJobW [WINSPOOL.@]
3152 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
3153 LPBYTE pJob, DWORD Command)
3155 BOOL ret = FALSE;
3156 job_t *job;
3158 TRACE("(%p, %d, %d, %p, %d)\n", hPrinter, JobId, Level, pJob, Command);
3159 FIXME("Ignoring everything other than document title\n");
3161 EnterCriticalSection(&printer_handles_cs);
3162 job = get_job(hPrinter, JobId);
3163 if(!job)
3164 goto end;
3166 switch(Level)
3168 case 0:
3169 break;
3170 case 1:
3172 JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
3173 HeapFree(GetProcessHeap(), 0, job->document_title);
3174 job->document_title = strdupW(info1->pDocument);
3175 break;
3177 case 2:
3179 JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
3180 HeapFree(GetProcessHeap(), 0, job->document_title);
3181 job->document_title = strdupW(info2->pDocument);
3182 break;
3184 case 3:
3185 break;
3186 default:
3187 SetLastError(ERROR_INVALID_LEVEL);
3188 goto end;
3190 ret = TRUE;
3191 end:
3192 LeaveCriticalSection(&printer_handles_cs);
3193 return ret;
3196 /*****************************************************************************
3197 * EndDocPrinter [WINSPOOL.@]
3199 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
3201 opened_printer_t *printer;
3202 BOOL ret = FALSE;
3203 TRACE("(%p)\n", hPrinter);
3205 EnterCriticalSection(&printer_handles_cs);
3207 printer = get_opened_printer(hPrinter);
3208 if(!printer)
3210 SetLastError(ERROR_INVALID_HANDLE);
3211 goto end;
3214 if(!printer->doc)
3216 SetLastError(ERROR_SPL_NO_STARTDOC);
3217 goto end;
3220 CloseHandle(printer->doc->hf);
3221 ScheduleJob(hPrinter, printer->doc->job_id);
3222 HeapFree(GetProcessHeap(), 0, printer->doc);
3223 printer->doc = NULL;
3224 ret = TRUE;
3225 end:
3226 LeaveCriticalSection(&printer_handles_cs);
3227 return ret;
3230 /*****************************************************************************
3231 * EndPagePrinter [WINSPOOL.@]
3233 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
3235 FIXME("(%p): stub\n", hPrinter);
3236 return TRUE;
3239 /*****************************************************************************
3240 * StartDocPrinterA [WINSPOOL.@]
3242 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3244 UNICODE_STRING usBuffer;
3245 DOC_INFO_2W doc2W;
3246 DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
3247 DWORD ret;
3249 /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
3250 or one (DOC_INFO_3) extra DWORDs */
3252 switch(Level) {
3253 case 2:
3254 doc2W.JobId = doc2->JobId;
3255 /* fall through */
3256 case 3:
3257 doc2W.dwMode = doc2->dwMode;
3258 /* fall through */
3259 case 1:
3260 doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
3261 doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
3262 doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
3263 break;
3265 default:
3266 SetLastError(ERROR_INVALID_LEVEL);
3267 return FALSE;
3270 ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
3272 HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
3273 HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
3274 HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
3276 return ret;
3279 /*****************************************************************************
3280 * StartDocPrinterW [WINSPOOL.@]
3282 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3284 DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
3285 opened_printer_t *printer;
3286 BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
3287 ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
3288 JOB_INFO_1W job_info;
3289 DWORD needed, ret = 0;
3290 HANDLE hf;
3291 WCHAR *filename;
3293 TRACE("(hPrinter = %p, Level = %d, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
3294 hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
3295 debugstr_w(doc->pDatatype));
3297 if(Level < 1 || Level > 3)
3299 SetLastError(ERROR_INVALID_LEVEL);
3300 return 0;
3303 EnterCriticalSection(&printer_handles_cs);
3304 printer = get_opened_printer(hPrinter);
3305 if(!printer)
3307 SetLastError(ERROR_INVALID_HANDLE);
3308 goto end;
3311 if(printer->doc)
3313 SetLastError(ERROR_INVALID_PRINTER_STATE);
3314 goto end;
3317 /* Even if we're printing to a file we still add a print job, we'll
3318 just ignore the spool file name */
3320 if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
3322 ERR("AddJob failed gle %u\n", GetLastError());
3323 goto end;
3326 if(doc->pOutputFile)
3327 filename = doc->pOutputFile;
3328 else
3329 filename = addjob->Path;
3331 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3332 if(hf == INVALID_HANDLE_VALUE)
3333 goto end;
3335 memset(&job_info, 0, sizeof(job_info));
3336 job_info.pDocument = doc->pDocName;
3337 SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
3339 printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
3340 printer->doc->hf = hf;
3341 ret = printer->doc->job_id = addjob->JobId;
3342 end:
3343 LeaveCriticalSection(&printer_handles_cs);
3345 return ret;
3348 /*****************************************************************************
3349 * StartPagePrinter [WINSPOOL.@]
3351 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
3353 FIXME("(%p): stub\n", hPrinter);
3354 return TRUE;
3357 /*****************************************************************************
3358 * GetFormA [WINSPOOL.@]
3360 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3361 LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3363 FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,pFormName,
3364 Level,pForm,cbBuf,pcbNeeded);
3365 return FALSE;
3368 /*****************************************************************************
3369 * GetFormW [WINSPOOL.@]
3371 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3372 LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3374 FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,
3375 debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
3376 return FALSE;
3379 /*****************************************************************************
3380 * SetFormA [WINSPOOL.@]
3382 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3383 LPBYTE pForm)
3385 FIXME("(%p,%s,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3386 return FALSE;
3389 /*****************************************************************************
3390 * SetFormW [WINSPOOL.@]
3392 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3393 LPBYTE pForm)
3395 FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3396 return FALSE;
3399 /*****************************************************************************
3400 * ReadPrinter [WINSPOOL.@]
3402 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
3403 LPDWORD pNoBytesRead)
3405 FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
3406 return FALSE;
3409 /*****************************************************************************
3410 * ResetPrinterA [WINSPOOL.@]
3412 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
3414 FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3415 return FALSE;
3418 /*****************************************************************************
3419 * ResetPrinterW [WINSPOOL.@]
3421 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
3423 FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3424 return FALSE;
3427 /*****************************************************************************
3428 * WINSPOOL_GetDWORDFromReg
3430 * Return DWORD associated with ValueName from hkey.
3432 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
3434 DWORD sz = sizeof(DWORD), type, value = 0;
3435 LONG ret;
3437 ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
3439 if(ret != ERROR_SUCCESS) {
3440 WARN("Got ret = %d on name %s\n", ret, ValueName);
3441 return 0;
3443 if(type != REG_DWORD) {
3444 ERR("Got type %d\n", type);
3445 return 0;
3447 return value;
3451 /*****************************************************************************
3452 * get_filename_from_reg [internal]
3454 * Get ValueName from hkey storing result in out
3455 * when the Value in the registry has only a filename, use driverdir as prefix
3456 * outlen is space left in out
3457 * String is stored either as unicode or ascii
3461 static BOOL get_filename_from_reg(HKEY hkey, LPCWSTR driverdir, DWORD dirlen, LPCWSTR ValueName,
3462 LPBYTE out, DWORD outlen, LPDWORD needed, BOOL unicode)
3464 WCHAR filename[MAX_PATH];
3465 DWORD size;
3466 DWORD type;
3467 LONG ret;
3468 LPWSTR buffer = filename;
3469 LPWSTR ptr;
3471 *needed = 0;
3472 size = sizeof(filename);
3473 buffer[0] = '\0';
3474 ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3475 if (ret == ERROR_MORE_DATA) {
3476 TRACE("need dynamic buffer: %u\n", size);
3477 buffer = HeapAlloc(GetProcessHeap(), 0, size);
3478 if (!buffer) {
3479 /* No Memory is bad */
3480 return FALSE;
3482 buffer[0] = '\0';
3483 ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3486 if ((ret != ERROR_SUCCESS) || (!buffer[0])) {
3487 if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3488 return FALSE;
3491 ptr = buffer;
3492 while (ptr) {
3493 /* do we have a full path ? */
3494 ret = (((buffer[0] == '\\') && (buffer[1] == '\\')) ||
3495 (buffer[0] && (buffer[1] == ':') && (buffer[2] == '\\')) );
3497 if (!ret) {
3498 /* we must build the full Path */
3499 *needed += dirlen;
3500 if ((out) && (outlen > dirlen)) {
3501 if (unicode) {
3502 lstrcpyW((LPWSTR)out, driverdir);
3504 else
3506 WideCharToMultiByte(CP_ACP, 0, driverdir, -1, (LPSTR)out, outlen, NULL, NULL);
3508 out += dirlen;
3509 outlen -= dirlen;
3511 else
3512 out = NULL;
3515 /* write the filename */
3516 if (unicode) {
3517 size = (lstrlenW(ptr) + 1) * sizeof(WCHAR);
3518 if ((out) && (outlen >= size)) {
3519 lstrcpyW((LPWSTR)out, ptr);
3520 out += size;
3521 outlen -= size;
3523 else
3524 out = NULL;
3526 else
3528 size = WideCharToMultiByte(CP_ACP, 0, ptr, -1, NULL, 0, NULL, NULL);
3529 if ((out) && (outlen >= size)) {
3530 WideCharToMultiByte(CP_ACP, 0, ptr, -1, (LPSTR)out, outlen, NULL, NULL);
3531 out += size;
3532 outlen -= size;
3534 else
3535 out = NULL;
3537 *needed += size;
3538 ptr += lstrlenW(ptr)+1;
3539 if ((type != REG_MULTI_SZ) || (!ptr[0])) ptr = NULL;
3542 if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3544 /* write the multisz-termination */
3545 if (type == REG_MULTI_SZ) {
3546 size = (unicode) ? sizeof(WCHAR) : 1;
3548 *needed += size;
3549 if (out && (outlen >= size)) {
3550 memset (out, 0, size);
3553 return TRUE;
3556 /*****************************************************************************
3557 * WINSPOOL_GetStringFromReg
3559 * Get ValueName from hkey storing result in ptr. buflen is space left in ptr
3560 * String is stored either as unicode or ascii.
3561 * Bit of a hack here to get the ValueName if we want ascii.
3563 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
3564 DWORD buflen, DWORD *needed,
3565 BOOL unicode)
3567 DWORD sz = buflen, type;
3568 LONG ret;
3570 if(unicode)
3571 ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3572 else {
3573 LPSTR ValueNameA = strdupWtoA(ValueName);
3574 ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
3575 HeapFree(GetProcessHeap(),0,ValueNameA);
3577 if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
3578 WARN("Got ret = %d\n", ret);
3579 *needed = 0;
3580 return FALSE;
3582 /* add space for terminating '\0' */
3583 sz += unicode ? sizeof(WCHAR) : 1;
3584 *needed = sz;
3586 if (ptr)
3587 TRACE("%s: %s\n", debugstr_w(ValueName), unicode ? debugstr_w((LPCWSTR)ptr) : debugstr_a((LPCSTR)ptr));
3589 return TRUE;
3592 /*****************************************************************************
3593 * WINSPOOL_GetDefaultDevMode
3595 * Get a default DevMode values for wineps.
3596 * FIXME - use ppd.
3599 static void WINSPOOL_GetDefaultDevMode(
3600 LPBYTE ptr,
3601 DWORD buflen, DWORD *needed,
3602 BOOL unicode)
3604 DEVMODEA dm;
3605 static const char szwps[] = "wineps.drv";
3607 /* fill default DEVMODE - should be read from ppd... */
3608 ZeroMemory( &dm, sizeof(dm) );
3609 memcpy(dm.dmDeviceName,szwps,sizeof szwps);
3610 dm.dmSpecVersion = DM_SPECVERSION;
3611 dm.dmDriverVersion = 1;
3612 dm.dmSize = sizeof(DEVMODEA);
3613 dm.dmDriverExtra = 0;
3614 dm.dmFields =
3615 DM_ORIENTATION | DM_PAPERSIZE |
3616 DM_PAPERLENGTH | DM_PAPERWIDTH |
3617 DM_SCALE |
3618 DM_COPIES |
3619 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
3620 DM_YRESOLUTION | DM_TTOPTION;
3622 dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
3623 dm.u1.s1.dmPaperSize = DMPAPER_A4;
3624 dm.u1.s1.dmPaperLength = 2970;
3625 dm.u1.s1.dmPaperWidth = 2100;
3627 dm.u1.s1.dmScale = 100;
3628 dm.u1.s1.dmCopies = 1;
3629 dm.u1.s1.dmDefaultSource = DMBIN_AUTO;
3630 dm.u1.s1.dmPrintQuality = DMRES_MEDIUM;
3631 /* dm.dmColor */
3632 /* dm.dmDuplex */
3633 dm.dmYResolution = 300; /* 300dpi */
3634 dm.dmTTOption = DMTT_BITMAP;
3635 /* dm.dmCollate */
3636 /* dm.dmFormName */
3637 /* dm.dmLogPixels */
3638 /* dm.dmBitsPerPel */
3639 /* dm.dmPelsWidth */
3640 /* dm.dmPelsHeight */
3641 /* dm.u2.dmDisplayFlags */
3642 /* dm.dmDisplayFrequency */
3643 /* dm.dmICMMethod */
3644 /* dm.dmICMIntent */
3645 /* dm.dmMediaType */
3646 /* dm.dmDitherType */
3647 /* dm.dmReserved1 */
3648 /* dm.dmReserved2 */
3649 /* dm.dmPanningWidth */
3650 /* dm.dmPanningHeight */
3652 if(unicode) {
3653 if(buflen >= sizeof(DEVMODEW)) {
3654 DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
3655 memcpy(ptr, pdmW, sizeof(DEVMODEW));
3656 HeapFree(GetProcessHeap(),0,pdmW);
3658 *needed = sizeof(DEVMODEW);
3660 else
3662 if(buflen >= sizeof(DEVMODEA)) {
3663 memcpy(ptr, &dm, sizeof(DEVMODEA));
3665 *needed = sizeof(DEVMODEA);
3669 /*****************************************************************************
3670 * WINSPOOL_GetDevModeFromReg
3672 * Get ValueName from hkey storing result in ptr. buflen is space left in ptr
3673 * DevMode is stored either as unicode or ascii.
3675 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
3676 LPBYTE ptr,
3677 DWORD buflen, DWORD *needed,
3678 BOOL unicode)
3680 DWORD sz = buflen, type;
3681 LONG ret;
3683 if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
3684 ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3685 if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
3686 if (sz < sizeof(DEVMODEA))
3688 TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
3689 return FALSE;
3691 /* ensures that dmSize is not erratically bogus if registry is invalid */
3692 if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
3693 ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
3694 if(unicode) {
3695 sz += (CCHDEVICENAME + CCHFORMNAME);
3696 if(buflen >= sz) {
3697 DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
3698 memcpy(ptr, dmW, sz);
3699 HeapFree(GetProcessHeap(),0,dmW);
3702 *needed = sz;
3703 return TRUE;
3706 /*********************************************************************
3707 * WINSPOOL_GetPrinter_1
3709 * Fills out a PRINTER_INFO_1A|W struct storing the strings in buf.
3710 * The strings are either stored as unicode or ascii.
3712 static BOOL WINSPOOL_GetPrinter_1(HKEY hkeyPrinter, PRINTER_INFO_1W *pi1,
3713 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3714 BOOL unicode)
3716 DWORD size, left = cbBuf;
3717 BOOL space = (cbBuf > 0);
3718 LPBYTE ptr = buf;
3720 *pcbNeeded = 0;
3722 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3723 unicode)) {
3724 if(space && size <= left) {
3725 pi1->pName = (LPWSTR)ptr;
3726 ptr += size;
3727 left -= size;
3728 } else
3729 space = FALSE;
3730 *pcbNeeded += size;
3733 /* FIXME: pDescription should be something like "Name,Driver_Name,". */
3734 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3735 unicode)) {
3736 if(space && size <= left) {
3737 pi1->pDescription = (LPWSTR)ptr;
3738 ptr += size;
3739 left -= size;
3740 } else
3741 space = FALSE;
3742 *pcbNeeded += size;
3745 if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3746 unicode)) {
3747 if(space && size <= left) {
3748 pi1->pComment = (LPWSTR)ptr;
3749 ptr += size;
3750 left -= size;
3751 } else
3752 space = FALSE;
3753 *pcbNeeded += size;
3756 if(pi1) pi1->Flags = PRINTER_ENUM_ICON8; /* We're a printer */
3758 if(!space && pi1) /* zero out pi1 if we can't completely fill buf */
3759 memset(pi1, 0, sizeof(*pi1));
3761 return space;
3763 /*********************************************************************
3764 * WINSPOOL_GetPrinter_2
3766 * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
3767 * The strings are either stored as unicode or ascii.
3769 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
3770 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3771 BOOL unicode)
3773 DWORD size, left = cbBuf;
3774 BOOL space = (cbBuf > 0);
3775 LPBYTE ptr = buf;
3777 *pcbNeeded = 0;
3779 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3780 unicode)) {
3781 if(space && size <= left) {
3782 pi2->pPrinterName = (LPWSTR)ptr;
3783 ptr += size;
3784 left -= size;
3785 } else
3786 space = FALSE;
3787 *pcbNeeded += size;
3789 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
3790 unicode)) {
3791 if(space && size <= left) {
3792 pi2->pShareName = (LPWSTR)ptr;
3793 ptr += size;
3794 left -= size;
3795 } else
3796 space = FALSE;
3797 *pcbNeeded += size;
3799 if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3800 unicode)) {
3801 if(space && size <= left) {
3802 pi2->pPortName = (LPWSTR)ptr;
3803 ptr += size;
3804 left -= size;
3805 } else
3806 space = FALSE;
3807 *pcbNeeded += size;
3809 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
3810 &size, unicode)) {
3811 if(space && size <= left) {
3812 pi2->pDriverName = (LPWSTR)ptr;
3813 ptr += size;
3814 left -= size;
3815 } else
3816 space = FALSE;
3817 *pcbNeeded += size;
3819 if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3820 unicode)) {
3821 if(space && size <= left) {
3822 pi2->pComment = (LPWSTR)ptr;
3823 ptr += size;
3824 left -= size;
3825 } else
3826 space = FALSE;
3827 *pcbNeeded += size;
3829 if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
3830 unicode)) {
3831 if(space && size <= left) {
3832 pi2->pLocation = (LPWSTR)ptr;
3833 ptr += size;
3834 left -= size;
3835 } else
3836 space = FALSE;
3837 *pcbNeeded += size;
3839 if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
3840 &size, unicode)) {
3841 if(space && size <= left) {
3842 pi2->pDevMode = (LPDEVMODEW)ptr;
3843 ptr += size;
3844 left -= size;
3845 } else
3846 space = FALSE;
3847 *pcbNeeded += size;
3849 else
3851 WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
3852 if(space && size <= left) {
3853 pi2->pDevMode = (LPDEVMODEW)ptr;
3854 ptr += size;
3855 left -= size;
3856 } else
3857 space = FALSE;
3858 *pcbNeeded += size;
3860 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
3861 &size, unicode)) {
3862 if(space && size <= left) {
3863 pi2->pSepFile = (LPWSTR)ptr;
3864 ptr += size;
3865 left -= size;
3866 } else
3867 space = FALSE;
3868 *pcbNeeded += size;
3870 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
3871 &size, unicode)) {
3872 if(space && size <= left) {
3873 pi2->pPrintProcessor = (LPWSTR)ptr;
3874 ptr += size;
3875 left -= size;
3876 } else
3877 space = FALSE;
3878 *pcbNeeded += size;
3880 if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
3881 &size, unicode)) {
3882 if(space && size <= left) {
3883 pi2->pDatatype = (LPWSTR)ptr;
3884 ptr += size;
3885 left -= size;
3886 } else
3887 space = FALSE;
3888 *pcbNeeded += size;
3890 if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
3891 &size, unicode)) {
3892 if(space && size <= left) {
3893 pi2->pParameters = (LPWSTR)ptr;
3894 ptr += size;
3895 left -= size;
3896 } else
3897 space = FALSE;
3898 *pcbNeeded += size;
3900 if(pi2) {
3901 pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3902 pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
3903 pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3904 "Default Priority");
3905 pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
3906 pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
3909 if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
3910 memset(pi2, 0, sizeof(*pi2));
3912 return space;
3915 /*********************************************************************
3916 * WINSPOOL_GetPrinter_4
3918 * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
3920 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
3921 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3922 BOOL unicode)
3924 DWORD size, left = cbBuf;
3925 BOOL space = (cbBuf > 0);
3926 LPBYTE ptr = buf;
3928 *pcbNeeded = 0;
3930 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3931 unicode)) {
3932 if(space && size <= left) {
3933 pi4->pPrinterName = (LPWSTR)ptr;
3934 ptr += size;
3935 left -= size;
3936 } else
3937 space = FALSE;
3938 *pcbNeeded += size;
3940 if(pi4) {
3941 pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3944 if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
3945 memset(pi4, 0, sizeof(*pi4));
3947 return space;
3950 /*********************************************************************
3951 * WINSPOOL_GetPrinter_5
3953 * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
3955 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
3956 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3957 BOOL unicode)
3959 DWORD size, left = cbBuf;
3960 BOOL space = (cbBuf > 0);
3961 LPBYTE ptr = buf;
3963 *pcbNeeded = 0;
3965 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3966 unicode)) {
3967 if(space && size <= left) {
3968 pi5->pPrinterName = (LPWSTR)ptr;
3969 ptr += size;
3970 left -= size;
3971 } else
3972 space = FALSE;
3973 *pcbNeeded += size;
3975 if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3976 unicode)) {
3977 if(space && size <= left) {
3978 pi5->pPortName = (LPWSTR)ptr;
3979 ptr += size;
3980 left -= size;
3981 } else
3982 space = FALSE;
3983 *pcbNeeded += size;
3985 if(pi5) {
3986 pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3987 pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3988 "dnsTimeout");
3989 pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3990 "txTimeout");
3993 if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
3994 memset(pi5, 0, sizeof(*pi5));
3996 return space;
3999 /*****************************************************************************
4000 * WINSPOOL_GetPrinter
4002 * Implementation of GetPrinterA|W. Relies on PRINTER_INFO_*W being
4003 * essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
4004 * just a collection of pointers to strings.
4006 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4007 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
4009 LPCWSTR name;
4010 DWORD size, needed = 0;
4011 LPBYTE ptr = NULL;
4012 HKEY hkeyPrinter, hkeyPrinters;
4013 BOOL ret;
4015 TRACE("(%p,%d,%p,%d,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
4017 if (!(name = get_opened_printer_name(hPrinter))) {
4018 SetLastError(ERROR_INVALID_HANDLE);
4019 return FALSE;
4022 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4023 ERROR_SUCCESS) {
4024 ERR("Can't create Printers key\n");
4025 return FALSE;
4027 if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
4029 ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4030 RegCloseKey(hkeyPrinters);
4031 SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4032 return FALSE;
4035 switch(Level) {
4036 case 2:
4038 PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
4040 size = sizeof(PRINTER_INFO_2W);
4041 if(size <= cbBuf) {
4042 ptr = pPrinter + size;
4043 cbBuf -= size;
4044 memset(pPrinter, 0, size);
4045 } else {
4046 pi2 = NULL;
4047 cbBuf = 0;
4049 ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
4050 unicode);
4051 needed += size;
4052 break;
4055 case 4:
4057 PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
4059 size = sizeof(PRINTER_INFO_4W);
4060 if(size <= cbBuf) {
4061 ptr = pPrinter + size;
4062 cbBuf -= size;
4063 memset(pPrinter, 0, size);
4064 } else {
4065 pi4 = NULL;
4066 cbBuf = 0;
4068 ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
4069 unicode);
4070 needed += size;
4071 break;
4075 case 5:
4077 PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
4079 size = sizeof(PRINTER_INFO_5W);
4080 if(size <= cbBuf) {
4081 ptr = pPrinter + size;
4082 cbBuf -= size;
4083 memset(pPrinter, 0, size);
4084 } else {
4085 pi5 = NULL;
4086 cbBuf = 0;
4089 ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
4090 unicode);
4091 needed += size;
4092 break;
4095 default:
4096 FIXME("Unimplemented level %d\n", Level);
4097 SetLastError(ERROR_INVALID_LEVEL);
4098 RegCloseKey(hkeyPrinters);
4099 RegCloseKey(hkeyPrinter);
4100 return FALSE;
4103 RegCloseKey(hkeyPrinter);
4104 RegCloseKey(hkeyPrinters);
4106 TRACE("returning %d needed = %d\n", ret, needed);
4107 if(pcbNeeded) *pcbNeeded = needed;
4108 if(!ret)
4109 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4110 return ret;
4113 /*****************************************************************************
4114 * GetPrinterW [WINSPOOL.@]
4116 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4117 DWORD cbBuf, LPDWORD pcbNeeded)
4119 return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4120 TRUE);
4123 /*****************************************************************************
4124 * GetPrinterA [WINSPOOL.@]
4126 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4127 DWORD cbBuf, LPDWORD pcbNeeded)
4129 return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4130 FALSE);
4133 /*****************************************************************************
4134 * WINSPOOL_EnumPrinters
4136 * Implementation of EnumPrintersA|W
4138 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
4139 DWORD dwLevel, LPBYTE lpbPrinters,
4140 DWORD cbBuf, LPDWORD lpdwNeeded,
4141 LPDWORD lpdwReturned, BOOL unicode)
4144 HKEY hkeyPrinters, hkeyPrinter;
4145 WCHAR PrinterName[255];
4146 DWORD needed = 0, number = 0;
4147 DWORD used, i, left;
4148 PBYTE pi, buf;
4150 if(lpbPrinters)
4151 memset(lpbPrinters, 0, cbBuf);
4152 if(lpdwReturned)
4153 *lpdwReturned = 0;
4154 if(lpdwNeeded)
4155 *lpdwNeeded = 0;
4157 /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
4158 if(dwType == PRINTER_ENUM_DEFAULT)
4159 return TRUE;
4161 if (dwType & PRINTER_ENUM_CONNECTIONS) {
4162 TRACE("ignoring PRINTER_ENUM_CONNECTIONS\n");
4163 dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
4164 if (!dwType) {
4165 FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
4166 *lpdwNeeded = 0;
4167 *lpdwReturned = 0;
4168 return TRUE;
4173 if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
4174 FIXME("dwType = %08x\n", dwType);
4175 SetLastError(ERROR_INVALID_FLAGS);
4176 return FALSE;
4179 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4180 ERROR_SUCCESS) {
4181 ERR("Can't create Printers key\n");
4182 return FALSE;
4185 if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
4186 NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4187 RegCloseKey(hkeyPrinters);
4188 ERR("Can't query Printers key\n");
4189 return FALSE;
4191 TRACE("Found %d printers\n", number);
4193 switch(dwLevel) {
4194 case 1:
4195 used = number * sizeof(PRINTER_INFO_1W);
4196 break;
4197 case 2:
4198 used = number * sizeof(PRINTER_INFO_2W);
4199 break;
4200 case 4:
4201 used = number * sizeof(PRINTER_INFO_4W);
4202 break;
4203 case 5:
4204 used = number * sizeof(PRINTER_INFO_5W);
4205 break;
4207 default:
4208 SetLastError(ERROR_INVALID_LEVEL);
4209 RegCloseKey(hkeyPrinters);
4210 return FALSE;
4212 pi = (used <= cbBuf) ? lpbPrinters : NULL;
4214 for(i = 0; i < number; i++) {
4215 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) !=
4216 ERROR_SUCCESS) {
4217 ERR("Can't enum key number %d\n", i);
4218 RegCloseKey(hkeyPrinters);
4219 return FALSE;
4221 TRACE("Printer %d is %s\n", i, debugstr_w(PrinterName));
4222 if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
4223 ERROR_SUCCESS) {
4224 ERR("Can't open key %s\n", debugstr_w(PrinterName));
4225 RegCloseKey(hkeyPrinters);
4226 return FALSE;
4229 if(cbBuf > used) {
4230 buf = lpbPrinters + used;
4231 left = cbBuf - used;
4232 } else {
4233 buf = NULL;
4234 left = 0;
4237 switch(dwLevel) {
4238 case 1:
4239 WINSPOOL_GetPrinter_1(hkeyPrinter, (PRINTER_INFO_1W *)pi, buf,
4240 left, &needed, unicode);
4241 used += needed;
4242 if(pi) pi += sizeof(PRINTER_INFO_1W);
4243 break;
4244 case 2:
4245 WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
4246 left, &needed, unicode);
4247 used += needed;
4248 if(pi) pi += sizeof(PRINTER_INFO_2W);
4249 break;
4250 case 4:
4251 WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
4252 left, &needed, unicode);
4253 used += needed;
4254 if(pi) pi += sizeof(PRINTER_INFO_4W);
4255 break;
4256 case 5:
4257 WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
4258 left, &needed, unicode);
4259 used += needed;
4260 if(pi) pi += sizeof(PRINTER_INFO_5W);
4261 break;
4262 default:
4263 ERR("Shouldn't be here!\n");
4264 RegCloseKey(hkeyPrinter);
4265 RegCloseKey(hkeyPrinters);
4266 return FALSE;
4268 RegCloseKey(hkeyPrinter);
4270 RegCloseKey(hkeyPrinters);
4272 if(lpdwNeeded)
4273 *lpdwNeeded = used;
4275 if(used > cbBuf) {
4276 if(lpbPrinters)
4277 memset(lpbPrinters, 0, cbBuf);
4278 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4279 return FALSE;
4281 if(lpdwReturned)
4282 *lpdwReturned = number;
4283 SetLastError(ERROR_SUCCESS);
4284 return TRUE;
4288 /******************************************************************
4289 * EnumPrintersW [WINSPOOL.@]
4291 * Enumerates the available printers, print servers and print
4292 * providers, depending on the specified flags, name and level.
4294 * RETURNS:
4296 * If level is set to 1:
4297 * Returns an array of PRINTER_INFO_1 data structures in the
4298 * lpbPrinters buffer.
4300 * If level is set to 2:
4301 * Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4302 * Returns an array of PRINTER_INFO_2 data structures in the
4303 * lpbPrinters buffer. Note that according to MSDN also an
4304 * OpenPrinter should be performed on every remote printer.
4306 * If level is set to 4 (officially WinNT only):
4307 * Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4308 * Fast: Only the registry is queried to retrieve printer names,
4309 * no connection to the driver is made.
4310 * Returns an array of PRINTER_INFO_4 data structures in the
4311 * lpbPrinters buffer.
4313 * If level is set to 5 (officially WinNT4/Win9x only):
4314 * Fast: Only the registry is queried to retrieve printer names,
4315 * no connection to the driver is made.
4316 * Returns an array of PRINTER_INFO_5 data structures in the
4317 * lpbPrinters buffer.
4319 * If level set to 3 or 6+:
4320 * returns zero (failure!)
4322 * Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
4323 * for information.
4325 * BUGS:
4326 * - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
4327 * - Only levels 2, 4 and 5 are implemented at the moment.
4328 * - 16-bit printer drivers are not enumerated.
4329 * - Returned amount of bytes used/needed does not match the real Windoze
4330 * implementation (as in this implementation, all strings are part
4331 * of the buffer, whereas Win32 keeps them somewhere else)
4332 * - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
4334 * NOTE:
4335 * - In a regular Wine installation, no registry settings for printers
4336 * exist, which makes this function return an empty list.
4338 BOOL WINAPI EnumPrintersW(
4339 DWORD dwType, /* [in] Types of print objects to enumerate */
4340 LPWSTR lpszName, /* [in] name of objects to enumerate */
4341 DWORD dwLevel, /* [in] type of printer info structure */
4342 LPBYTE lpbPrinters, /* [out] buffer which receives info */
4343 DWORD cbBuf, /* [in] max size of buffer in bytes */
4344 LPDWORD lpdwNeeded, /* [out] pointer to var: # bytes used/needed */
4345 LPDWORD lpdwReturned /* [out] number of entries returned */
4348 return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
4349 lpdwNeeded, lpdwReturned, TRUE);
4352 /******************************************************************
4353 * EnumPrintersA [WINSPOOL.@]
4356 BOOL WINAPI EnumPrintersA(DWORD dwType, LPSTR lpszName,
4357 DWORD dwLevel, LPBYTE lpbPrinters,
4358 DWORD cbBuf, LPDWORD lpdwNeeded,
4359 LPDWORD lpdwReturned)
4361 BOOL ret, unicode = FALSE;
4362 UNICODE_STRING lpszNameW;
4363 PWSTR pwstrNameW;
4365 pwstrNameW = asciitounicode(&lpszNameW,lpszName);
4366 if(!cbBuf) unicode = TRUE; /* return a buffer that's big enough for the unicode version */
4367 ret = WINSPOOL_EnumPrinters(dwType, pwstrNameW, dwLevel, lpbPrinters, cbBuf,
4368 lpdwNeeded, lpdwReturned, unicode);
4369 RtlFreeUnicodeString(&lpszNameW);
4370 return ret;
4373 /*****************************************************************************
4374 * WINSPOOL_GetDriverInfoFromReg [internal]
4376 * Enters the information from the registry into the DRIVER_INFO struct
4378 * RETURNS
4379 * zero if the printer driver does not exist in the registry
4380 * (only if Level > 1) otherwise nonzero
4382 static BOOL WINSPOOL_GetDriverInfoFromReg(
4383 HKEY hkeyDrivers,
4384 LPWSTR DriverName,
4385 const printenv_t * env,
4386 DWORD Level,
4387 LPBYTE ptr, /* DRIVER_INFO */
4388 LPBYTE pDriverStrings, /* strings buffer */
4389 DWORD cbBuf, /* size of string buffer */
4390 LPDWORD pcbNeeded, /* space needed for str. */
4391 BOOL unicode) /* type of strings */
4393 DWORD size, tmp;
4394 HKEY hkeyDriver;
4395 WCHAR driverdir[MAX_PATH];
4396 DWORD dirlen;
4397 LPBYTE strPtr = pDriverStrings;
4398 LPDRIVER_INFO_8W di = (LPDRIVER_INFO_8W) ptr;
4400 TRACE("(%p, %s, %p, %d, %p, %p, %d, %d)\n", hkeyDrivers,
4401 debugstr_w(DriverName), env,
4402 Level, di, pDriverStrings, cbBuf, unicode);
4404 if (di) ZeroMemory(di, di_sizeof[Level]);
4406 if (unicode) {
4407 *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
4408 if (*pcbNeeded <= cbBuf)
4409 strcpyW((LPWSTR)strPtr, DriverName);
4411 else
4413 *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0, NULL, NULL);
4414 if (*pcbNeeded <= cbBuf)
4415 WideCharToMultiByte(CP_ACP, 0, DriverName, -1, (LPSTR)strPtr, *pcbNeeded, NULL, NULL);
4418 /* pName for level 1 has a different offset! */
4419 if (Level == 1) {
4420 if (di) ((LPDRIVER_INFO_1W) di)->pName = (LPWSTR) strPtr;
4421 return TRUE;
4424 /* .cVersion and .pName for level > 1 */
4425 if (di) {
4426 di->cVersion = env->driverversion;
4427 di->pName = (LPWSTR) strPtr;
4428 strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4431 /* Reserve Space for the largest subdir and a Backslash*/
4432 size = sizeof(driverdir) - sizeof(Version3_SubdirW) - sizeof(WCHAR);
4433 if (!GetPrinterDriverDirectoryW(NULL, (LPWSTR) env->envname, 1, (LPBYTE) driverdir, size, &size)) {
4434 /* Should never Fail */
4435 return FALSE;
4437 lstrcatW(driverdir, env->versionsubdir);
4438 lstrcatW(driverdir, backslashW);
4440 /* dirlen must not include the terminating zero */
4441 dirlen = (unicode) ? lstrlenW(driverdir) * sizeof(WCHAR) :
4442 WideCharToMultiByte(CP_ACP, 0, driverdir, -1, NULL, 0, NULL, NULL) -1;
4444 if (!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
4445 ERR("Can't find driver %s in registry\n", debugstr_w(DriverName));
4446 SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
4447 return FALSE;
4450 /* pEnvironment */
4451 if (unicode)
4452 size = (lstrlenW(env->envname) + 1) * sizeof(WCHAR);
4453 else
4454 size = WideCharToMultiByte(CP_ACP, 0, env->envname, -1, NULL, 0, NULL, NULL);
4456 *pcbNeeded += size;
4457 if (*pcbNeeded <= cbBuf) {
4458 if (unicode) {
4459 lstrcpyW((LPWSTR)strPtr, env->envname);
4461 else
4463 WideCharToMultiByte(CP_ACP, 0, env->envname, -1, (LPSTR)strPtr, size, NULL, NULL);
4465 if (di) di->pEnvironment = (LPWSTR)strPtr;
4466 strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4469 /* .pDriverPath is the Graphics rendering engine.
4470 The full Path is required to avoid a crash in some apps */
4471 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, 0, &size, unicode)) {
4472 *pcbNeeded += size;
4473 if (*pcbNeeded <= cbBuf)
4474 get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, size, &tmp, unicode);
4476 if (di) di->pDriverPath = (LPWSTR)strPtr;
4477 strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4480 /* .pDataFile: For postscript-drivers, this is the ppd-file */
4481 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, 0, &size, unicode)) {
4482 *pcbNeeded += size;
4483 if (*pcbNeeded <= cbBuf)
4484 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, size, &size, unicode);
4486 if (di) di->pDataFile = (LPWSTR)strPtr;
4487 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4490 /* .pConfigFile is the Driver user Interface */
4491 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, 0, &size, unicode)) {
4492 *pcbNeeded += size;
4493 if (*pcbNeeded <= cbBuf)
4494 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, size, &size, unicode);
4496 if (di) di->pConfigFile = (LPWSTR)strPtr;
4497 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4500 if (Level == 2 ) {
4501 RegCloseKey(hkeyDriver);
4502 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4503 return TRUE;
4506 if (Level == 5 ) {
4507 RegCloseKey(hkeyDriver);
4508 FIXME("level 5: incomplete\n");
4509 return TRUE;
4512 /* .pHelpFile */
4513 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, 0, &size, unicode)) {
4514 *pcbNeeded += size;
4515 if (*pcbNeeded <= cbBuf)
4516 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, size, &size, unicode);
4518 if (di) di->pHelpFile = (LPWSTR)strPtr;
4519 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4522 /* .pDependentFiles */
4523 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, 0, &size, unicode)) {
4524 *pcbNeeded += size;
4525 if (*pcbNeeded <= cbBuf)
4526 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, size, &size, unicode);
4528 if (di) di->pDependentFiles = (LPWSTR)strPtr;
4529 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4531 else if (GetVersion() & 0x80000000) {
4532 /* PowerPoint XP expects that pDependentFiles is always valid on win9x */
4533 size = 2 * ((unicode) ? sizeof(WCHAR) : 1);
4534 *pcbNeeded += size;
4535 if ((*pcbNeeded <= cbBuf) && strPtr) ZeroMemory(strPtr, size);
4537 if (di) di->pDependentFiles = (LPWSTR)strPtr;
4538 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4541 /* .pMonitorName is the optional Language Monitor */
4542 if (WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size, unicode)) {
4543 *pcbNeeded += size;
4544 if (*pcbNeeded <= cbBuf)
4545 WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, size, &size, unicode);
4547 if (di) di->pMonitorName = (LPWSTR)strPtr;
4548 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4551 /* .pDefaultDataType */
4552 if (WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size, unicode)) {
4553 *pcbNeeded += size;
4554 if(*pcbNeeded <= cbBuf)
4555 WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, size, &size, unicode);
4557 if (di) di->pDefaultDataType = (LPWSTR)strPtr;
4558 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4561 if (Level == 3 ) {
4562 RegCloseKey(hkeyDriver);
4563 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4564 return TRUE;
4567 /* .pszzPreviousNames */
4568 if (WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, 0, &size, unicode)) {
4569 *pcbNeeded += size;
4570 if(*pcbNeeded <= cbBuf)
4571 WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, size, &size, unicode);
4573 if (di) di->pszzPreviousNames = (LPWSTR)strPtr;
4574 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4577 if (Level == 4 ) {
4578 RegCloseKey(hkeyDriver);
4579 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4580 return TRUE;
4583 /* support is missing, but not important enough for a FIXME */
4584 TRACE("%s: DriverDate + DriverVersion not supported\n", debugstr_w(DriverName));
4586 /* .pszMfgName */
4587 if (WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, 0, &size, unicode)) {
4588 *pcbNeeded += size;
4589 if(*pcbNeeded <= cbBuf)
4590 WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, size, &size, unicode);
4592 if (di) di->pszMfgName = (LPWSTR)strPtr;
4593 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4596 /* .pszOEMUrl */
4597 if (WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, 0, &size, unicode)) {
4598 *pcbNeeded += size;
4599 if(*pcbNeeded <= cbBuf)
4600 WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, size, &size, unicode);
4602 if (di) di->pszOEMUrl = (LPWSTR)strPtr;
4603 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4606 /* .pszHardwareID */
4607 if (WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, 0, &size, unicode)) {
4608 *pcbNeeded += size;
4609 if(*pcbNeeded <= cbBuf)
4610 WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, size, &size, unicode);
4612 if (di) di->pszHardwareID = (LPWSTR)strPtr;
4613 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4616 /* .pszProvider */
4617 if (WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, 0, &size, unicode)) {
4618 *pcbNeeded += size;
4619 if(*pcbNeeded <= cbBuf)
4620 WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, size, &size, unicode);
4622 if (di) di->pszProvider = (LPWSTR)strPtr;
4623 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4626 if (Level == 6 ) {
4627 RegCloseKey(hkeyDriver);
4628 return TRUE;
4631 /* support is missing, but not important enough for a FIXME */
4632 TRACE("level 8: incomplete\n");
4634 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4635 RegCloseKey(hkeyDriver);
4636 return TRUE;
4639 /*****************************************************************************
4640 * WINSPOOL_GetPrinterDriver
4642 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPCWSTR pEnvironment,
4643 DWORD Level, LPBYTE pDriverInfo,
4644 DWORD cbBuf, LPDWORD pcbNeeded,
4645 BOOL unicode)
4647 LPCWSTR name;
4648 WCHAR DriverName[100];
4649 DWORD ret, type, size, needed = 0;
4650 LPBYTE ptr = NULL;
4651 HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
4652 const printenv_t * env;
4654 TRACE("(%p,%s,%d,%p,%d,%p)\n",hPrinter,debugstr_w(pEnvironment),
4655 Level,pDriverInfo,cbBuf, pcbNeeded);
4658 if (!(name = get_opened_printer_name(hPrinter))) {
4659 SetLastError(ERROR_INVALID_HANDLE);
4660 return FALSE;
4663 if (Level < 1 || Level == 7 || Level > 8) {
4664 SetLastError(ERROR_INVALID_LEVEL);
4665 return FALSE;
4668 env = validate_envW(pEnvironment);
4669 if (!env) return FALSE; /* SetLastError() is in validate_envW */
4671 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4672 ERROR_SUCCESS) {
4673 ERR("Can't create Printers key\n");
4674 return FALSE;
4676 if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
4677 != ERROR_SUCCESS) {
4678 ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4679 RegCloseKey(hkeyPrinters);
4680 SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4681 return FALSE;
4683 size = sizeof(DriverName);
4684 DriverName[0] = 0;
4685 ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
4686 (LPBYTE)DriverName, &size);
4687 RegCloseKey(hkeyPrinter);
4688 RegCloseKey(hkeyPrinters);
4689 if(ret != ERROR_SUCCESS) {
4690 ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
4691 return FALSE;
4694 hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
4695 if(!hkeyDrivers) {
4696 ERR("Can't create Drivers key\n");
4697 return FALSE;
4700 size = di_sizeof[Level];
4701 if ((size <= cbBuf) && pDriverInfo)
4702 ptr = pDriverInfo + size;
4704 if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
4705 env, Level, pDriverInfo, ptr,
4706 (cbBuf < size) ? 0 : cbBuf - size,
4707 &needed, unicode)) {
4708 RegCloseKey(hkeyDrivers);
4709 return FALSE;
4712 RegCloseKey(hkeyDrivers);
4714 if(pcbNeeded) *pcbNeeded = size + needed;
4715 TRACE("buffer space %d required %d\n", cbBuf, size + needed);
4716 if(cbBuf >= needed) return TRUE;
4717 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4718 return FALSE;
4721 /*****************************************************************************
4722 * GetPrinterDriverA [WINSPOOL.@]
4724 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
4725 DWORD Level, LPBYTE pDriverInfo,
4726 DWORD cbBuf, LPDWORD pcbNeeded)
4728 BOOL ret;
4729 UNICODE_STRING pEnvW;
4730 PWSTR pwstrEnvW;
4732 pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
4733 ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
4734 cbBuf, pcbNeeded, FALSE);
4735 RtlFreeUnicodeString(&pEnvW);
4736 return ret;
4738 /*****************************************************************************
4739 * GetPrinterDriverW [WINSPOOL.@]
4741 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
4742 DWORD Level, LPBYTE pDriverInfo,
4743 DWORD cbBuf, LPDWORD pcbNeeded)
4745 return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
4746 pDriverInfo, cbBuf, pcbNeeded, TRUE);
4749 /*****************************************************************************
4750 * GetPrinterDriverDirectoryW [WINSPOOL.@]
4752 * Return the PATH for the Printer-Drivers (UNICODE)
4754 * PARAMS
4755 * pName [I] Servername (NT only) or NULL (local Computer)
4756 * pEnvironment [I] Printing-Environment (see below) or NULL (Default)
4757 * Level [I] Structure-Level (must be 1)
4758 * pDriverDirectory [O] PTR to Buffer that receives the Result
4759 * cbBuf [I] Size of Buffer at pDriverDirectory
4760 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
4761 * required for pDriverDirectory
4763 * RETURNS
4764 * Success: TRUE and in pcbNeeded the Bytes used in pDriverDirectory
4765 * Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
4766 * if cbBuf is too small
4768 * Native Values returned in pDriverDirectory on Success:
4769 *| NT(Windows NT x86): "%winsysdir%\\spool\\DRIVERS\\w32x86"
4770 *| NT(Windows 4.0): "%winsysdir%\\spool\\DRIVERS\\win40"
4771 *| win9x(Windows 4.0): "%winsysdir%"
4773 * "%winsysdir%" is the Value from GetSystemDirectoryW()
4775 * FIXME
4776 *- Only NULL or "" is supported for pName
4779 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
4780 DWORD Level, LPBYTE pDriverDirectory,
4781 DWORD cbBuf, LPDWORD pcbNeeded)
4783 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName),
4784 debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4786 if ((backend == NULL) && !load_backend()) return FALSE;
4788 if (Level != 1) {
4789 /* (Level != 1) is ignored in win9x */
4790 SetLastError(ERROR_INVALID_LEVEL);
4791 return FALSE;
4793 if (pcbNeeded == NULL) {
4794 /* (pcbNeeded == NULL) is ignored in win9x */
4795 SetLastError(RPC_X_NULL_REF_POINTER);
4796 return FALSE;
4799 return backend->fpGetPrinterDriverDirectory(pName, pEnvironment, Level,
4800 pDriverDirectory, cbBuf, pcbNeeded);
4805 /*****************************************************************************
4806 * GetPrinterDriverDirectoryA [WINSPOOL.@]
4808 * Return the PATH for the Printer-Drivers (ANSI)
4810 * See GetPrinterDriverDirectoryW.
4812 * NOTES
4813 * On NT, pDriverDirectory need the same Size as the Unicode-Version
4816 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
4817 DWORD Level, LPBYTE pDriverDirectory,
4818 DWORD cbBuf, LPDWORD pcbNeeded)
4820 UNICODE_STRING nameW, environmentW;
4821 BOOL ret;
4822 DWORD pcbNeededW;
4823 INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
4824 WCHAR *driverDirectoryW = NULL;
4826 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(pName),
4827 debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4829 if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
4831 if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
4832 else nameW.Buffer = NULL;
4833 if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
4834 else environmentW.Buffer = NULL;
4836 ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
4837 (LPBYTE)driverDirectoryW, len, &pcbNeededW );
4838 if (ret) {
4839 DWORD needed;
4840 needed = WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1,
4841 (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
4842 if(pcbNeeded)
4843 *pcbNeeded = needed;
4844 ret = (needed <= cbBuf) ? TRUE : FALSE;
4845 } else
4846 if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
4848 TRACE("required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
4850 HeapFree( GetProcessHeap(), 0, driverDirectoryW );
4851 RtlFreeUnicodeString(&environmentW);
4852 RtlFreeUnicodeString(&nameW);
4854 return ret;
4857 /*****************************************************************************
4858 * AddPrinterDriverA [WINSPOOL.@]
4860 * See AddPrinterDriverW.
4863 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
4865 TRACE("(%s, %d, %p)\n", debugstr_a(pName), level, pDriverInfo);
4866 return AddPrinterDriverExA(pName, level, pDriverInfo, APD_COPY_NEW_FILES);
4869 /******************************************************************************
4870 * AddPrinterDriverW (WINSPOOL.@)
4872 * Install a Printer Driver
4874 * PARAMS
4875 * pName [I] Servername or NULL (local Computer)
4876 * level [I] Level for the supplied DRIVER_INFO_*W struct
4877 * pDriverInfo [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
4879 * RESULTS
4880 * Success: TRUE
4881 * Failure: FALSE
4884 BOOL WINAPI AddPrinterDriverW(LPWSTR pName, DWORD level, LPBYTE pDriverInfo)
4886 TRACE("(%s, %d, %p)\n", debugstr_w(pName), level, pDriverInfo);
4887 return AddPrinterDriverExW(pName, level, pDriverInfo, APD_COPY_NEW_FILES);
4890 /*****************************************************************************
4891 * AddPrintProcessorA [WINSPOOL.@]
4893 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
4894 LPSTR pPrintProcessorName)
4896 FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
4897 debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
4898 return FALSE;
4901 /*****************************************************************************
4902 * AddPrintProcessorW [WINSPOOL.@]
4904 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
4905 LPWSTR pPrintProcessorName)
4907 FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
4908 debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
4909 return FALSE;
4912 /*****************************************************************************
4913 * AddPrintProvidorA [WINSPOOL.@]
4915 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
4917 FIXME("(%s,0x%08x,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
4918 return FALSE;
4921 /*****************************************************************************
4922 * AddPrintProvidorW [WINSPOOL.@]
4924 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
4926 FIXME("(%s,0x%08x,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
4927 return FALSE;
4930 /*****************************************************************************
4931 * AdvancedDocumentPropertiesA [WINSPOOL.@]
4933 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
4934 PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
4936 FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
4937 pDevModeOutput, pDevModeInput);
4938 return 0;
4941 /*****************************************************************************
4942 * AdvancedDocumentPropertiesW [WINSPOOL.@]
4944 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
4945 PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
4947 FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
4948 pDevModeOutput, pDevModeInput);
4949 return 0;
4952 /*****************************************************************************
4953 * PrinterProperties [WINSPOOL.@]
4955 * Displays a dialog to set the properties of the printer.
4957 * RETURNS
4958 * nonzero on success or zero on failure
4960 * BUGS
4961 * implemented as stub only
4963 BOOL WINAPI PrinterProperties(HWND hWnd, /* [in] handle to parent window */
4964 HANDLE hPrinter /* [in] handle to printer object */
4966 FIXME("(%p,%p): stub\n", hWnd, hPrinter);
4967 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4968 return FALSE;
4971 /*****************************************************************************
4972 * EnumJobsA [WINSPOOL.@]
4975 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
4976 DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
4977 LPDWORD pcReturned)
4979 FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
4980 hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
4982 if(pcbNeeded) *pcbNeeded = 0;
4983 if(pcReturned) *pcReturned = 0;
4984 return FALSE;
4988 /*****************************************************************************
4989 * EnumJobsW [WINSPOOL.@]
4992 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
4993 DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
4994 LPDWORD pcReturned)
4996 FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
4997 hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
4999 if(pcbNeeded) *pcbNeeded = 0;
5000 if(pcReturned) *pcReturned = 0;
5001 return FALSE;
5004 /*****************************************************************************
5005 * WINSPOOL_EnumPrinterDrivers [internal]
5007 * Delivers information about all printer drivers installed on the
5008 * localhost or a given server
5010 * RETURNS
5011 * nonzero on success or zero on failure. If the buffer for the returned
5012 * information is too small the function will return an error
5014 * BUGS
5015 * - only implemented for localhost, foreign hosts will return an error
5017 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPCWSTR pEnvironment,
5018 DWORD Level, LPBYTE pDriverInfo,
5019 DWORD cbBuf, LPDWORD pcbNeeded,
5020 LPDWORD pcReturned, BOOL unicode)
5022 { HKEY hkeyDrivers;
5023 DWORD i, needed, number = 0, size = 0;
5024 WCHAR DriverNameW[255];
5025 PBYTE ptr;
5026 const printenv_t * env;
5028 TRACE("%s,%s,%d,%p,%d,%d\n",
5029 debugstr_w(pName), debugstr_w(pEnvironment),
5030 Level, pDriverInfo, cbBuf, unicode);
5032 /* check for local drivers */
5033 if((pName) && (pName[0])) {
5034 FIXME("remote drivers (%s) not supported!\n", debugstr_w(pName));
5035 SetLastError(ERROR_ACCESS_DENIED);
5036 return FALSE;
5039 env = validate_envW(pEnvironment);
5040 if (!env) return FALSE; /* SetLastError() is in validate_envW */
5042 /* check input parameter */
5043 if ((Level < 1) || (Level == 7) || (Level > 8)) {
5044 SetLastError(ERROR_INVALID_LEVEL);
5045 return FALSE;
5048 if ((pcbNeeded == NULL) || (pcReturned == NULL)) {
5049 SetLastError(RPC_X_NULL_REF_POINTER);
5050 return FALSE;
5053 /* initialize return values */
5054 if(pDriverInfo)
5055 memset( pDriverInfo, 0, cbBuf);
5056 *pcbNeeded = 0;
5057 *pcReturned = 0;
5059 hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
5060 if(!hkeyDrivers) {
5061 ERR("Can't open Drivers key\n");
5062 return FALSE;
5065 if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
5066 NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
5067 RegCloseKey(hkeyDrivers);
5068 ERR("Can't query Drivers key\n");
5069 return FALSE;
5071 TRACE("Found %d Drivers\n", number);
5073 /* get size of single struct
5074 * unicode and ascii structure have the same size
5076 size = di_sizeof[Level];
5078 /* calculate required buffer size */
5079 *pcbNeeded = size * number;
5081 for( i = 0, ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
5082 i < number;
5083 i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
5084 if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW)/sizeof(DriverNameW[0]))
5085 != ERROR_SUCCESS) {
5086 ERR("Can't enum key number %d\n", i);
5087 RegCloseKey(hkeyDrivers);
5088 return FALSE;
5090 if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
5091 env, Level, ptr,
5092 (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
5093 (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
5094 &needed, unicode)) {
5095 RegCloseKey(hkeyDrivers);
5096 return FALSE;
5098 (*pcbNeeded) += needed;
5101 RegCloseKey(hkeyDrivers);
5103 if(cbBuf < *pcbNeeded){
5104 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5105 return FALSE;
5108 *pcReturned = number;
5109 return TRUE;
5112 /*****************************************************************************
5113 * EnumPrinterDriversW [WINSPOOL.@]
5115 * see function EnumPrinterDrivers for RETURNS, BUGS
5117 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5118 LPBYTE pDriverInfo, DWORD cbBuf,
5119 LPDWORD pcbNeeded, LPDWORD pcReturned)
5121 return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
5122 cbBuf, pcbNeeded, pcReturned, TRUE);
5125 /*****************************************************************************
5126 * EnumPrinterDriversA [WINSPOOL.@]
5128 * see function EnumPrinterDrivers for RETURNS, BUGS
5130 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
5131 LPBYTE pDriverInfo, DWORD cbBuf,
5132 LPDWORD pcbNeeded, LPDWORD pcReturned)
5133 { BOOL ret;
5134 UNICODE_STRING pNameW, pEnvironmentW;
5135 PWSTR pwstrNameW, pwstrEnvironmentW;
5137 pwstrNameW = asciitounicode(&pNameW, pName);
5138 pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
5140 ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
5141 Level, pDriverInfo, cbBuf, pcbNeeded,
5142 pcReturned, FALSE);
5143 RtlFreeUnicodeString(&pNameW);
5144 RtlFreeUnicodeString(&pEnvironmentW);
5146 return ret;
5149 /******************************************************************************
5150 * EnumPortsA (WINSPOOL.@)
5152 * See EnumPortsW.
5155 BOOL WINAPI EnumPortsA( LPSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
5156 LPDWORD pcbNeeded, LPDWORD pcReturned)
5158 BOOL res;
5159 LPBYTE bufferW = NULL;
5160 LPWSTR nameW = NULL;
5161 DWORD needed = 0;
5162 DWORD numentries = 0;
5163 INT len;
5165 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pPorts,
5166 cbBuf, pcbNeeded, pcReturned);
5168 /* convert servername to unicode */
5169 if (pName) {
5170 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5171 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5172 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5174 /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the Ports */
5175 needed = cbBuf * sizeof(WCHAR);
5176 if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
5177 res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5179 if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
5180 if (pcbNeeded) needed = *pcbNeeded;
5181 /* HeapReAlloc return NULL, when bufferW was NULL */
5182 bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
5183 HeapAlloc(GetProcessHeap(), 0, needed);
5185 /* Try again with the large Buffer */
5186 res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5188 needed = pcbNeeded ? *pcbNeeded : 0;
5189 numentries = pcReturned ? *pcReturned : 0;
5192 W2k require the buffersize from EnumPortsW also for EnumPortsA.
5193 We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
5195 if (res) {
5196 /* EnumPortsW collected all Data. Parse them to calculate ANSI-Size */
5197 DWORD entrysize = 0;
5198 DWORD index;
5199 LPSTR ptr;
5200 LPPORT_INFO_2W pi2w;
5201 LPPORT_INFO_2A pi2a;
5203 needed = 0;
5204 entrysize = (Level == 1) ? sizeof(PORT_INFO_1A) : sizeof(PORT_INFO_2A);
5206 /* First pass: calculate the size for all Entries */
5207 pi2w = (LPPORT_INFO_2W) bufferW;
5208 pi2a = (LPPORT_INFO_2A) pPorts;
5209 index = 0;
5210 while (index < numentries) {
5211 index++;
5212 needed += entrysize; /* PORT_INFO_?A */
5213 TRACE("%p: parsing #%d (%s)\n", pi2w, index, debugstr_w(pi2w->pPortName));
5215 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5216 NULL, 0, NULL, NULL);
5217 if (Level > 1) {
5218 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5219 NULL, 0, NULL, NULL);
5220 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5221 NULL, 0, NULL, NULL);
5223 /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5224 pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5225 pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5228 /* check for errors and quit on failure */
5229 if (cbBuf < needed) {
5230 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5231 res = FALSE;
5232 goto cleanup;
5234 len = entrysize * numentries; /* room for all PORT_INFO_?A */
5235 ptr = (LPSTR) &pPorts[len]; /* room for strings */
5236 cbBuf -= len ; /* free Bytes in the user-Buffer */
5237 pi2w = (LPPORT_INFO_2W) bufferW;
5238 pi2a = (LPPORT_INFO_2A) pPorts;
5239 index = 0;
5240 /* Second Pass: Fill the User Buffer (if we have one) */
5241 while ((index < numentries) && pPorts) {
5242 index++;
5243 TRACE("%p: writing PORT_INFO_%dA #%d\n", pi2a, Level, index);
5244 pi2a->pPortName = ptr;
5245 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5246 ptr, cbBuf , NULL, NULL);
5247 ptr += len;
5248 cbBuf -= len;
5249 if (Level > 1) {
5250 pi2a->pMonitorName = ptr;
5251 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5252 ptr, cbBuf, NULL, NULL);
5253 ptr += len;
5254 cbBuf -= len;
5256 pi2a->pDescription = ptr;
5257 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5258 ptr, cbBuf, NULL, NULL);
5259 ptr += len;
5260 cbBuf -= len;
5262 pi2a->fPortType = pi2w->fPortType;
5263 pi2a->Reserved = 0; /* documented: "must be zero" */
5266 /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5267 pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5268 pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5272 cleanup:
5273 if (pcbNeeded) *pcbNeeded = needed;
5274 if (pcReturned) *pcReturned = (res) ? numentries : 0;
5276 HeapFree(GetProcessHeap(), 0, nameW);
5277 HeapFree(GetProcessHeap(), 0, bufferW);
5279 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
5280 (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5282 return (res);
5286 /******************************************************************************
5287 * EnumPortsW (WINSPOOL.@)
5289 * Enumerate available Ports
5291 * PARAMS
5292 * name [I] Servername or NULL (local Computer)
5293 * level [I] Structure-Level (1 or 2)
5294 * buffer [O] PTR to Buffer that receives the Result
5295 * bufsize [I] Size of Buffer at buffer
5296 * bufneeded [O] PTR to DWORD that receives the size in Bytes used / required for buffer
5297 * bufreturned [O] PTR to DWORD that receives the number of Ports in buffer
5299 * RETURNS
5300 * Success: TRUE
5301 * Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
5305 BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5307 DWORD needed = 0;
5308 DWORD numentries = 0;
5309 BOOL res = FALSE;
5311 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
5312 cbBuf, pcbNeeded, pcReturned);
5314 if (pName && (pName[0])) {
5315 FIXME("not implemented for Server %s\n", debugstr_w(pName));
5316 SetLastError(ERROR_ACCESS_DENIED);
5317 goto emP_cleanup;
5320 /* Level is not checked in win9x */
5321 if (!Level || (Level > 2)) {
5322 WARN("level (%d) is ignored in win9x\n", Level);
5323 SetLastError(ERROR_INVALID_LEVEL);
5324 goto emP_cleanup;
5326 if (!pcbNeeded) {
5327 SetLastError(RPC_X_NULL_REF_POINTER);
5328 goto emP_cleanup;
5331 EnterCriticalSection(&monitor_handles_cs);
5332 monitor_loadall();
5334 /* Scan all local Ports */
5335 numentries = 0;
5336 needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
5338 /* we calculated the needed buffersize. now do the error-checks */
5339 if (cbBuf < needed) {
5340 monitor_unloadall();
5341 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5342 goto emP_cleanup_cs;
5344 else if (!pPorts || !pcReturned) {
5345 monitor_unloadall();
5346 SetLastError(RPC_X_NULL_REF_POINTER);
5347 goto emP_cleanup_cs;
5350 /* Fill the Buffer */
5351 needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
5352 res = TRUE;
5353 monitor_unloadall();
5355 emP_cleanup_cs:
5356 LeaveCriticalSection(&monitor_handles_cs);
5358 emP_cleanup:
5359 if (pcbNeeded) *pcbNeeded = needed;
5360 if (pcReturned) *pcReturned = (res) ? numentries : 0;
5362 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
5363 (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5365 return (res);
5368 /******************************************************************************
5369 * GetDefaultPrinterW (WINSPOOL.@)
5371 * FIXME
5372 * This function must read the value from data 'device' of key
5373 * HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
5375 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
5377 BOOL retval = TRUE;
5378 DWORD insize, len;
5379 WCHAR *buffer, *ptr;
5381 if (!namesize)
5383 SetLastError(ERROR_INVALID_PARAMETER);
5384 return FALSE;
5387 /* make the buffer big enough for the stuff from the profile/registry,
5388 * the content must fit into the local buffer to compute the correct
5389 * size even if the extern buffer is too small or not given.
5390 * (20 for ,driver,port) */
5391 insize = *namesize;
5392 len = max(100, (insize + 20));
5393 buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
5395 if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
5397 SetLastError (ERROR_FILE_NOT_FOUND);
5398 retval = FALSE;
5399 goto end;
5401 TRACE("%s\n", debugstr_w(buffer));
5403 if ((ptr = strchrW(buffer, ',')) == NULL)
5405 SetLastError(ERROR_INVALID_NAME);
5406 retval = FALSE;
5407 goto end;
5410 *ptr = 0;
5411 *namesize = strlenW(buffer) + 1;
5412 if(!name || (*namesize > insize))
5414 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5415 retval = FALSE;
5416 goto end;
5418 strcpyW(name, buffer);
5420 end:
5421 HeapFree( GetProcessHeap(), 0, buffer);
5422 return retval;
5426 /******************************************************************************
5427 * GetDefaultPrinterA (WINSPOOL.@)
5429 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
5431 BOOL retval = TRUE;
5432 DWORD insize = 0;
5433 WCHAR *bufferW = NULL;
5435 if (!namesize)
5437 SetLastError(ERROR_INVALID_PARAMETER);
5438 return FALSE;
5441 if(name && *namesize) {
5442 insize = *namesize;
5443 bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
5446 if(!GetDefaultPrinterW( bufferW, namesize)) {
5447 retval = FALSE;
5448 goto end;
5451 *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
5452 NULL, NULL);
5453 if (!*namesize)
5455 *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
5456 retval = FALSE;
5458 TRACE("0x%08x/0x%08x:%s\n", *namesize, insize, debugstr_w(bufferW));
5460 end:
5461 HeapFree( GetProcessHeap(), 0, bufferW);
5462 return retval;
5466 /******************************************************************************
5467 * SetDefaultPrinterW (WINSPOOL.204)
5469 * Set the Name of the Default Printer
5471 * PARAMS
5472 * pszPrinter [I] Name of the Printer or NULL
5474 * RETURNS
5475 * Success: True
5476 * Failure: FALSE
5478 * NOTES
5479 * When the Parameter is NULL or points to an Empty String and
5480 * a Default Printer was already present, then this Function changes nothing.
5481 * Without a Default Printer and NULL (or an Empty String) as Parameter,
5482 * the First enumerated local Printer is used.
5485 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
5488 TRACE("(%s)\n", debugstr_w(pszPrinter));
5490 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5491 return FALSE;
5494 /******************************************************************************
5495 * SetDefaultPrinterA (WINSPOOL.202)
5497 * See SetDefaultPrinterW.
5500 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
5503 TRACE("(%s)\n", debugstr_a(pszPrinter));
5505 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5506 return FALSE;
5510 /******************************************************************************
5511 * SetPrinterDataExA (WINSPOOL.@)
5513 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5514 LPCSTR pValueName, DWORD Type,
5515 LPBYTE pData, DWORD cbData)
5517 HKEY hkeyPrinter, hkeySubkey;
5518 DWORD ret;
5520 TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_a(pKeyName),
5521 debugstr_a(pValueName), Type, pData, cbData);
5523 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5524 != ERROR_SUCCESS)
5525 return ret;
5527 if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5528 != ERROR_SUCCESS) {
5529 ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
5530 RegCloseKey(hkeyPrinter);
5531 return ret;
5533 ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
5534 RegCloseKey(hkeySubkey);
5535 RegCloseKey(hkeyPrinter);
5536 return ret;
5539 /******************************************************************************
5540 * SetPrinterDataExW (WINSPOOL.@)
5542 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5543 LPCWSTR pValueName, DWORD Type,
5544 LPBYTE pData, DWORD cbData)
5546 HKEY hkeyPrinter, hkeySubkey;
5547 DWORD ret;
5549 TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_w(pKeyName),
5550 debugstr_w(pValueName), Type, pData, cbData);
5552 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5553 != ERROR_SUCCESS)
5554 return ret;
5556 if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5557 != ERROR_SUCCESS) {
5558 ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
5559 RegCloseKey(hkeyPrinter);
5560 return ret;
5562 ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
5563 RegCloseKey(hkeySubkey);
5564 RegCloseKey(hkeyPrinter);
5565 return ret;
5568 /******************************************************************************
5569 * SetPrinterDataA (WINSPOOL.@)
5571 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
5572 LPBYTE pData, DWORD cbData)
5574 return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
5575 pData, cbData);
5578 /******************************************************************************
5579 * SetPrinterDataW (WINSPOOL.@)
5581 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
5582 LPBYTE pData, DWORD cbData)
5584 return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
5585 pData, cbData);
5588 /******************************************************************************
5589 * GetPrinterDataExA (WINSPOOL.@)
5591 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5592 LPCSTR pValueName, LPDWORD pType,
5593 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5595 HKEY hkeyPrinter, hkeySubkey;
5596 DWORD ret;
5598 TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5599 debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
5600 pcbNeeded);
5602 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5603 != ERROR_SUCCESS)
5604 return ret;
5606 if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5607 != ERROR_SUCCESS) {
5608 WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
5609 RegCloseKey(hkeyPrinter);
5610 return ret;
5612 *pcbNeeded = nSize;
5613 ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5614 RegCloseKey(hkeySubkey);
5615 RegCloseKey(hkeyPrinter);
5616 return ret;
5619 /******************************************************************************
5620 * GetPrinterDataExW (WINSPOOL.@)
5622 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5623 LPCWSTR pValueName, LPDWORD pType,
5624 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5626 HKEY hkeyPrinter, hkeySubkey;
5627 DWORD ret;
5629 TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5630 debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
5631 pcbNeeded);
5633 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5634 != ERROR_SUCCESS)
5635 return ret;
5637 if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5638 != ERROR_SUCCESS) {
5639 WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
5640 RegCloseKey(hkeyPrinter);
5641 return ret;
5643 *pcbNeeded = nSize;
5644 ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5645 RegCloseKey(hkeySubkey);
5646 RegCloseKey(hkeyPrinter);
5647 return ret;
5650 /******************************************************************************
5651 * GetPrinterDataA (WINSPOOL.@)
5653 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
5654 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5656 return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
5657 pData, nSize, pcbNeeded);
5660 /******************************************************************************
5661 * GetPrinterDataW (WINSPOOL.@)
5663 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
5664 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5666 return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
5667 pData, nSize, pcbNeeded);
5670 /*******************************************************************************
5671 * EnumPrinterDataExW [WINSPOOL.@]
5673 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5674 LPBYTE pEnumValues, DWORD cbEnumValues,
5675 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5677 HKEY hkPrinter, hkSubKey;
5678 DWORD r, ret, dwIndex, cValues, cbMaxValueNameLen,
5679 cbValueNameLen, cbMaxValueLen, cbValueLen,
5680 cbBufSize, dwType;
5681 LPWSTR lpValueName;
5682 HANDLE hHeap;
5683 PBYTE lpValue;
5684 PPRINTER_ENUM_VALUESW ppev;
5686 TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
5688 if (pKeyName == NULL || *pKeyName == 0)
5689 return ERROR_INVALID_PARAMETER;
5691 ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
5692 if (ret != ERROR_SUCCESS)
5694 TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %i\n",
5695 hPrinter, ret);
5696 return ret;
5699 ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
5700 if (ret != ERROR_SUCCESS)
5702 r = RegCloseKey (hkPrinter);
5703 if (r != ERROR_SUCCESS)
5704 WARN ("RegCloseKey returned %i\n", r);
5705 TRACE ("RegOpenKeyExW (%p, %s) returned %i\n", hPrinter,
5706 debugstr_w (pKeyName), ret);
5707 return ret;
5710 ret = RegCloseKey (hkPrinter);
5711 if (ret != ERROR_SUCCESS)
5713 ERR ("RegCloseKey returned %i\n", ret);
5714 r = RegCloseKey (hkSubKey);
5715 if (r != ERROR_SUCCESS)
5716 WARN ("RegCloseKey returned %i\n", r);
5717 return ret;
5720 ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
5721 &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
5722 if (ret != ERROR_SUCCESS)
5724 r = RegCloseKey (hkSubKey);
5725 if (r != ERROR_SUCCESS)
5726 WARN ("RegCloseKey returned %i\n", r);
5727 TRACE ("RegQueryInfoKeyW (%p) returned %i\n", hkSubKey, ret);
5728 return ret;
5731 TRACE ("RegQueryInfoKeyW returned cValues = %i, cbMaxValueNameLen = %i, "
5732 "cbMaxValueLen = %i\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
5734 if (cValues == 0) /* empty key */
5736 r = RegCloseKey (hkSubKey);
5737 if (r != ERROR_SUCCESS)
5738 WARN ("RegCloseKey returned %i\n", r);
5739 *pcbEnumValues = *pnEnumValues = 0;
5740 return ERROR_SUCCESS;
5743 ++cbMaxValueNameLen; /* allow for trailing '\0' */
5745 hHeap = GetProcessHeap ();
5746 if (hHeap == NULL)
5748 ERR ("GetProcessHeap failed\n");
5749 r = RegCloseKey (hkSubKey);
5750 if (r != ERROR_SUCCESS)
5751 WARN ("RegCloseKey returned %i\n", r);
5752 return ERROR_OUTOFMEMORY;
5755 lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
5756 if (lpValueName == NULL)
5758 ERR ("Failed to allocate %i WCHARs from process heap\n", cbMaxValueNameLen);
5759 r = RegCloseKey (hkSubKey);
5760 if (r != ERROR_SUCCESS)
5761 WARN ("RegCloseKey returned %i\n", r);
5762 return ERROR_OUTOFMEMORY;
5765 lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
5766 if (lpValue == NULL)
5768 ERR ("Failed to allocate %i bytes from process heap\n", cbMaxValueLen);
5769 if (HeapFree (hHeap, 0, lpValueName) == 0)
5770 WARN ("HeapFree failed with code %i\n", GetLastError ());
5771 r = RegCloseKey (hkSubKey);
5772 if (r != ERROR_SUCCESS)
5773 WARN ("RegCloseKey returned %i\n", r);
5774 return ERROR_OUTOFMEMORY;
5777 TRACE ("pass 1: calculating buffer required for all names and values\n");
5779 cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
5781 TRACE ("%i bytes required for %i headers\n", cbBufSize, cValues);
5783 for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5785 cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5786 ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5787 NULL, NULL, lpValue, &cbValueLen);
5788 if (ret != ERROR_SUCCESS)
5790 if (HeapFree (hHeap, 0, lpValue) == 0)
5791 WARN ("HeapFree failed with code %i\n", GetLastError ());
5792 if (HeapFree (hHeap, 0, lpValueName) == 0)
5793 WARN ("HeapFree failed with code %i\n", GetLastError ());
5794 r = RegCloseKey (hkSubKey);
5795 if (r != ERROR_SUCCESS)
5796 WARN ("RegCloseKey returned %i\n", r);
5797 TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5798 return ret;
5801 TRACE ("%s [%i]: name needs %i WCHARs, data needs %i bytes\n",
5802 debugstr_w (lpValueName), dwIndex,
5803 cbValueNameLen + 1, cbValueLen);
5805 cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
5806 cbBufSize += cbValueLen;
5809 TRACE ("%i bytes required for all %i values\n", cbBufSize, cValues);
5811 *pcbEnumValues = cbBufSize;
5812 *pnEnumValues = cValues;
5814 if (cbEnumValues < cbBufSize) /* buffer too small */
5816 if (HeapFree (hHeap, 0, lpValue) == 0)
5817 WARN ("HeapFree failed with code %i\n", GetLastError ());
5818 if (HeapFree (hHeap, 0, lpValueName) == 0)
5819 WARN ("HeapFree failed with code %i\n", GetLastError ());
5820 r = RegCloseKey (hkSubKey);
5821 if (r != ERROR_SUCCESS)
5822 WARN ("RegCloseKey returned %i\n", r);
5823 TRACE ("%i byte buffer is not large enough\n", cbEnumValues);
5824 return ERROR_MORE_DATA;
5827 TRACE ("pass 2: copying all names and values to buffer\n");
5829 ppev = (PPRINTER_ENUM_VALUESW) pEnumValues; /* array of structs */
5830 pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
5832 for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5834 cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5835 ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5836 NULL, &dwType, lpValue, &cbValueLen);
5837 if (ret != ERROR_SUCCESS)
5839 if (HeapFree (hHeap, 0, lpValue) == 0)
5840 WARN ("HeapFree failed with code %i\n", GetLastError ());
5841 if (HeapFree (hHeap, 0, lpValueName) == 0)
5842 WARN ("HeapFree failed with code %i\n", GetLastError ());
5843 r = RegCloseKey (hkSubKey);
5844 if (r != ERROR_SUCCESS)
5845 WARN ("RegCloseKey returned %i\n", r);
5846 TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5847 return ret;
5850 cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
5851 memcpy (pEnumValues, lpValueName, cbValueNameLen);
5852 ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
5853 pEnumValues += cbValueNameLen;
5855 /* return # of *bytes* (including trailing \0), not # of chars */
5856 ppev[dwIndex].cbValueName = cbValueNameLen;
5858 ppev[dwIndex].dwType = dwType;
5860 memcpy (pEnumValues, lpValue, cbValueLen);
5861 ppev[dwIndex].pData = pEnumValues;
5862 pEnumValues += cbValueLen;
5864 ppev[dwIndex].cbData = cbValueLen;
5866 TRACE ("%s [%i]: copied name (%i bytes) and data (%i bytes)\n",
5867 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
5870 if (HeapFree (hHeap, 0, lpValue) == 0)
5872 ret = GetLastError ();
5873 ERR ("HeapFree failed with code %i\n", ret);
5874 if (HeapFree (hHeap, 0, lpValueName) == 0)
5875 WARN ("HeapFree failed with code %i\n", GetLastError ());
5876 r = RegCloseKey (hkSubKey);
5877 if (r != ERROR_SUCCESS)
5878 WARN ("RegCloseKey returned %i\n", r);
5879 return ret;
5882 if (HeapFree (hHeap, 0, lpValueName) == 0)
5884 ret = GetLastError ();
5885 ERR ("HeapFree failed with code %i\n", ret);
5886 r = RegCloseKey (hkSubKey);
5887 if (r != ERROR_SUCCESS)
5888 WARN ("RegCloseKey returned %i\n", r);
5889 return ret;
5892 ret = RegCloseKey (hkSubKey);
5893 if (ret != ERROR_SUCCESS)
5895 ERR ("RegCloseKey returned %i\n", ret);
5896 return ret;
5899 return ERROR_SUCCESS;
5902 /*******************************************************************************
5903 * EnumPrinterDataExA [WINSPOOL.@]
5905 * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
5906 * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers. This is
5907 * what Windows 2000 SP1 does.
5910 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5911 LPBYTE pEnumValues, DWORD cbEnumValues,
5912 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5914 INT len;
5915 LPWSTR pKeyNameW;
5916 DWORD ret, dwIndex, dwBufSize;
5917 HANDLE hHeap;
5918 LPSTR pBuffer;
5920 TRACE ("%p %s\n", hPrinter, pKeyName);
5922 if (pKeyName == NULL || *pKeyName == 0)
5923 return ERROR_INVALID_PARAMETER;
5925 len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
5926 if (len == 0)
5928 ret = GetLastError ();
5929 ERR ("MultiByteToWideChar failed with code %i\n", ret);
5930 return ret;
5933 hHeap = GetProcessHeap ();
5934 if (hHeap == NULL)
5936 ERR ("GetProcessHeap failed\n");
5937 return ERROR_OUTOFMEMORY;
5940 pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
5941 if (pKeyNameW == NULL)
5943 ERR ("Failed to allocate %i bytes from process heap\n",
5944 (LONG)(len * sizeof (WCHAR)));
5945 return ERROR_OUTOFMEMORY;
5948 if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
5950 ret = GetLastError ();
5951 ERR ("MultiByteToWideChar failed with code %i\n", ret);
5952 if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5953 WARN ("HeapFree failed with code %i\n", GetLastError ());
5954 return ret;
5957 ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
5958 pcbEnumValues, pnEnumValues);
5959 if (ret != ERROR_SUCCESS)
5961 if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5962 WARN ("HeapFree failed with code %i\n", GetLastError ());
5963 TRACE ("EnumPrinterDataExW returned %i\n", ret);
5964 return ret;
5967 if (HeapFree (hHeap, 0, pKeyNameW) == 0)
5969 ret = GetLastError ();
5970 ERR ("HeapFree failed with code %i\n", ret);
5971 return ret;
5974 if (*pnEnumValues == 0) /* empty key */
5975 return ERROR_SUCCESS;
5977 dwBufSize = 0;
5978 for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
5980 PPRINTER_ENUM_VALUESW ppev =
5981 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
5983 if (dwBufSize < ppev->cbValueName)
5984 dwBufSize = ppev->cbValueName;
5986 if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
5987 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
5988 dwBufSize = ppev->cbData;
5991 TRACE ("Largest Unicode name or value is %i bytes\n", dwBufSize);
5993 pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
5994 if (pBuffer == NULL)
5996 ERR ("Failed to allocate %i bytes from process heap\n", dwBufSize);
5997 return ERROR_OUTOFMEMORY;
6000 for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6002 PPRINTER_ENUM_VALUESW ppev =
6003 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6005 len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
6006 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
6007 NULL);
6008 if (len == 0)
6010 ret = GetLastError ();
6011 ERR ("WideCharToMultiByte failed with code %i\n", ret);
6012 if (HeapFree (hHeap, 0, pBuffer) == 0)
6013 WARN ("HeapFree failed with code %i\n", GetLastError ());
6014 return ret;
6017 memcpy (ppev->pValueName, pBuffer, len);
6019 TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6021 if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
6022 ppev->dwType != REG_MULTI_SZ)
6023 continue;
6025 len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
6026 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
6027 if (len == 0)
6029 ret = GetLastError ();
6030 ERR ("WideCharToMultiByte failed with code %i\n", ret);
6031 if (HeapFree (hHeap, 0, pBuffer) == 0)
6032 WARN ("HeapFree failed with code %i\n", GetLastError ());
6033 return ret;
6036 memcpy (ppev->pData, pBuffer, len);
6038 TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6039 TRACE (" (only first string of REG_MULTI_SZ printed)\n");
6042 if (HeapFree (hHeap, 0, pBuffer) == 0)
6044 ret = GetLastError ();
6045 ERR ("HeapFree failed with code %i\n", ret);
6046 return ret;
6049 return ERROR_SUCCESS;
6052 /******************************************************************************
6053 * AbortPrinter (WINSPOOL.@)
6055 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
6057 FIXME("(%p), stub!\n", hPrinter);
6058 return TRUE;
6061 /******************************************************************************
6062 * AddPortA (WINSPOOL.@)
6064 * See AddPortW.
6067 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
6069 LPWSTR nameW = NULL;
6070 LPWSTR monitorW = NULL;
6071 DWORD len;
6072 BOOL res;
6074 TRACE("(%s, %p, %s)\n",debugstr_a(pName), hWnd, debugstr_a(pMonitorName));
6076 if (pName) {
6077 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6078 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6079 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6082 if (pMonitorName) {
6083 len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6084 monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6085 MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6087 res = AddPortW(nameW, hWnd, monitorW);
6088 HeapFree(GetProcessHeap(), 0, nameW);
6089 HeapFree(GetProcessHeap(), 0, monitorW);
6090 return res;
6093 /******************************************************************************
6094 * AddPortW (WINSPOOL.@)
6096 * Add a Port for a specific Monitor
6098 * PARAMS
6099 * pName [I] Servername or NULL (local Computer)
6100 * hWnd [I] Handle to parent Window for the Dialog-Box
6101 * pMonitorName [I] Name of the Monitor that manage the Port
6103 * RETURNS
6104 * Success: TRUE
6105 * Failure: FALSE
6108 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
6110 monitor_t * pm;
6111 monitor_t * pui;
6112 DWORD res;
6114 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
6116 if (pName && pName[0]) {
6117 SetLastError(ERROR_INVALID_PARAMETER);
6118 return FALSE;
6121 if (!pMonitorName) {
6122 SetLastError(RPC_X_NULL_REF_POINTER);
6123 return FALSE;
6126 /* an empty Monitorname is Invalid */
6127 if (!pMonitorName[0]) {
6128 SetLastError(ERROR_NOT_SUPPORTED);
6129 return FALSE;
6132 pm = monitor_load(pMonitorName, NULL);
6133 if (pm && pm->monitor && pm->monitor->pfnAddPort) {
6134 res = pm->monitor->pfnAddPort(pName, hWnd, pMonitorName);
6135 TRACE("got %d with %u\n", res, GetLastError());
6136 res = TRUE;
6138 else
6140 pui = monitor_loadui(pm);
6141 if (pui && pui->monitorUI && pui->monitorUI->pfnAddPortUI) {
6142 TRACE("use %p: %s\n", pui, debugstr_w(pui->dllname));
6143 res = pui->monitorUI->pfnAddPortUI(pName, hWnd, pMonitorName, NULL);
6144 TRACE("got %d with %u\n", res, GetLastError());
6145 res = TRUE;
6147 else
6149 FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pMonitorName),
6150 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
6152 /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6153 SetLastError(ERROR_NOT_SUPPORTED);
6154 res = FALSE;
6156 monitor_unload(pui);
6158 monitor_unload(pm);
6159 TRACE("returning %d with %u\n", res, GetLastError());
6160 return res;
6163 /******************************************************************************
6164 * AddPortExA (WINSPOOL.@)
6166 * See AddPortExW.
6169 BOOL WINAPI AddPortExA(LPSTR pName, DWORD level, LPBYTE pBuffer, LPSTR pMonitorName)
6171 PORT_INFO_2W pi2W;
6172 PORT_INFO_2A * pi2A;
6173 LPWSTR nameW = NULL;
6174 LPWSTR monitorW = NULL;
6175 DWORD len;
6176 BOOL res;
6178 pi2A = (PORT_INFO_2A *) pBuffer;
6180 TRACE("(%s, %d, %p, %s): %s\n", debugstr_a(pName), level, pBuffer,
6181 debugstr_a(pMonitorName), debugstr_a(pi2A ? pi2A->pPortName : NULL));
6183 if ((level < 1) || (level > 2)) {
6184 SetLastError(ERROR_INVALID_LEVEL);
6185 return FALSE;
6188 if (!pi2A) {
6189 SetLastError(ERROR_INVALID_PARAMETER);
6190 return FALSE;
6193 if (pName) {
6194 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6195 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6196 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6199 if (pMonitorName) {
6200 len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6201 monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6202 MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6205 ZeroMemory(&pi2W, sizeof(PORT_INFO_2W));
6207 if (pi2A->pPortName) {
6208 len = MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, NULL, 0);
6209 pi2W.pPortName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6210 MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, pi2W.pPortName, len);
6213 if (level > 1) {
6214 if (pi2A->pMonitorName) {
6215 len = MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, NULL, 0);
6216 pi2W.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6217 MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, pi2W.pMonitorName, len);
6220 if (pi2A->pDescription) {
6221 len = MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, NULL, 0);
6222 pi2W.pDescription = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6223 MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, pi2W.pDescription, len);
6225 pi2W.fPortType = pi2A->fPortType;
6226 pi2W.Reserved = pi2A->Reserved;
6229 res = AddPortExW(nameW, level, (LPBYTE) &pi2W, monitorW);
6231 HeapFree(GetProcessHeap(), 0, nameW);
6232 HeapFree(GetProcessHeap(), 0, monitorW);
6233 HeapFree(GetProcessHeap(), 0, pi2W.pPortName);
6234 HeapFree(GetProcessHeap(), 0, pi2W.pMonitorName);
6235 HeapFree(GetProcessHeap(), 0, pi2W.pDescription);
6236 return res;
6240 /******************************************************************************
6241 * AddPortExW (WINSPOOL.@)
6243 * Add a Port for a specific Monitor, without presenting a user interface
6245 * PARAMS
6246 * pName [I] Servername or NULL (local Computer)
6247 * level [I] Structure-Level (1 or 2) for pBuffer
6248 * pBuffer [I] PTR to: PORT_INFO_1 or PORT_INFO_2
6249 * pMonitorName [I] Name of the Monitor that manage the Port
6251 * RETURNS
6252 * Success: TRUE
6253 * Failure: FALSE
6256 BOOL WINAPI AddPortExW(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
6258 PORT_INFO_2W * pi2;
6259 monitor_t * pm;
6260 DWORD res = FALSE;
6262 pi2 = (PORT_INFO_2W *) pBuffer;
6264 TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
6265 debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
6266 debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
6267 debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
6270 if ((level < 1) || (level > 2)) {
6271 SetLastError(ERROR_INVALID_LEVEL);
6272 return FALSE;
6275 if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
6276 SetLastError(ERROR_INVALID_PARAMETER);
6277 return FALSE;
6280 /* load the Monitor */
6281 pm = monitor_load(pMonitorName, NULL);
6282 if (!pm) {
6283 SetLastError(ERROR_INVALID_PARAMETER);
6284 return FALSE;
6287 if (pm->monitor && pm->monitor->pfnAddPortEx) {
6288 res = pm->monitor->pfnAddPortEx(pName, level, pBuffer, pMonitorName);
6289 TRACE("got %u with %u\n", res, GetLastError());
6291 else
6293 FIXME("not implemented for %s (%p)\n", debugstr_w(pMonitorName), pm->monitor);
6295 monitor_unload(pm);
6296 return res;
6299 /******************************************************************************
6300 * AddPrinterConnectionA (WINSPOOL.@)
6302 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
6304 FIXME("%s\n", debugstr_a(pName));
6305 return FALSE;
6308 /******************************************************************************
6309 * AddPrinterConnectionW (WINSPOOL.@)
6311 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
6313 FIXME("%s\n", debugstr_w(pName));
6314 return FALSE;
6317 /******************************************************************************
6318 * AddPrinterDriverExW (WINSPOOL.@)
6320 * Install a Printer Driver with the Option to upgrade / downgrade the Files
6322 * PARAMS
6323 * pName [I] Servername or NULL (local Computer)
6324 * level [I] Level for the supplied DRIVER_INFO_*W struct
6325 * pDriverInfo [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
6326 * dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
6328 * RESULTS
6329 * Success: TRUE
6330 * Failure: FALSE
6333 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6335 TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
6337 if ((backend == NULL) && !load_backend()) return FALSE;
6339 if (level < 2 || level == 5 || level == 7 || level > 8) {
6340 SetLastError(ERROR_INVALID_LEVEL);
6341 return FALSE;
6344 if (!pDriverInfo) {
6345 SetLastError(ERROR_INVALID_PARAMETER);
6346 return FALSE;
6349 return backend->fpAddPrinterDriverEx(pName, level, pDriverInfo, dwFileCopyFlags);
6352 /******************************************************************************
6353 * AddPrinterDriverExA (WINSPOOL.@)
6355 * See AddPrinterDriverExW.
6358 BOOL WINAPI AddPrinterDriverExA(LPSTR pName, DWORD Level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6360 DRIVER_INFO_8A *diA;
6361 DRIVER_INFO_8W diW;
6362 LPWSTR nameW = NULL;
6363 DWORD lenA;
6364 DWORD len;
6365 DWORD res = FALSE;
6367 TRACE("(%s, %d, %p, 0x%x)\n", debugstr_a(pName), Level, pDriverInfo, dwFileCopyFlags);
6369 diA = (DRIVER_INFO_8A *) pDriverInfo;
6370 ZeroMemory(&diW, sizeof(diW));
6372 if (Level < 2 || Level == 5 || Level == 7 || Level > 8) {
6373 SetLastError(ERROR_INVALID_LEVEL);
6374 return FALSE;
6377 if (diA == NULL) {
6378 SetLastError(ERROR_INVALID_PARAMETER);
6379 return FALSE;
6382 /* convert servername to unicode */
6383 if (pName) {
6384 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6385 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6386 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6389 /* common fields */
6390 diW.cVersion = diA->cVersion;
6392 if (diA->pName) {
6393 len = MultiByteToWideChar(CP_ACP, 0, diA->pName, -1, NULL, 0);
6394 diW.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6395 MultiByteToWideChar(CP_ACP, 0, diA->pName, -1, diW.pName, len);
6398 if (diA->pEnvironment) {
6399 len = MultiByteToWideChar(CP_ACP, 0, diA->pEnvironment, -1, NULL, 0);
6400 diW.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6401 MultiByteToWideChar(CP_ACP, 0, diA->pEnvironment, -1, diW.pEnvironment, len);
6404 if (diA->pDriverPath) {
6405 len = MultiByteToWideChar(CP_ACP, 0, diA->pDriverPath, -1, NULL, 0);
6406 diW.pDriverPath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6407 MultiByteToWideChar(CP_ACP, 0, diA->pDriverPath, -1, diW.pDriverPath, len);
6410 if (diA->pDataFile) {
6411 len = MultiByteToWideChar(CP_ACP, 0, diA->pDataFile, -1, NULL, 0);
6412 diW.pDataFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6413 MultiByteToWideChar(CP_ACP, 0, diA->pDataFile, -1, diW.pDataFile, len);
6416 if (diA->pConfigFile) {
6417 len = MultiByteToWideChar(CP_ACP, 0, diA->pConfigFile, -1, NULL, 0);
6418 diW.pConfigFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6419 MultiByteToWideChar(CP_ACP, 0, diA->pConfigFile, -1, diW.pConfigFile, len);
6422 if ((Level > 2) && diA->pDependentFiles) {
6423 lenA = multi_sz_lenA(diA->pDependentFiles);
6424 len = MultiByteToWideChar(CP_ACP, 0, diA->pDependentFiles, lenA, NULL, 0);
6425 diW.pDependentFiles = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6426 MultiByteToWideChar(CP_ACP, 0, diA->pDependentFiles, lenA, diW.pDependentFiles, len);
6429 if ((Level > 2) && diA->pMonitorName) {
6430 len = MultiByteToWideChar(CP_ACP, 0, diA->pMonitorName, -1, NULL, 0);
6431 diW.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6432 MultiByteToWideChar(CP_ACP, 0, diA->pMonitorName, -1, diW.pMonitorName, len);
6435 if ((Level > 3) && diA->pDefaultDataType) {
6436 len = MultiByteToWideChar(CP_ACP, 0, diA->pDefaultDataType, -1, NULL, 0);
6437 diW.pDefaultDataType = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6438 MultiByteToWideChar(CP_ACP, 0, diA->pDefaultDataType, -1, diW.pDefaultDataType, len);
6441 if ((Level > 3) && diA->pszzPreviousNames) {
6442 lenA = multi_sz_lenA(diA->pszzPreviousNames);
6443 len = MultiByteToWideChar(CP_ACP, 0, diA->pszzPreviousNames, lenA, NULL, 0);
6444 diW.pszzPreviousNames = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6445 MultiByteToWideChar(CP_ACP, 0, diA->pszzPreviousNames, lenA, diW.pszzPreviousNames, len);
6448 if ((Level > 5) && diA->pszMfgName) {
6449 len = MultiByteToWideChar(CP_ACP, 0, diA->pszMfgName, -1, NULL, 0);
6450 diW.pszMfgName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6451 MultiByteToWideChar(CP_ACP, 0, diA->pszMfgName, -1, diW.pszMfgName, len);
6454 if ((Level > 5) && diA->pszOEMUrl) {
6455 len = MultiByteToWideChar(CP_ACP, 0, diA->pszOEMUrl, -1, NULL, 0);
6456 diW.pszOEMUrl = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6457 MultiByteToWideChar(CP_ACP, 0, diA->pszOEMUrl, -1, diW.pszOEMUrl, len);
6460 if ((Level > 5) && diA->pszHardwareID) {
6461 len = MultiByteToWideChar(CP_ACP, 0, diA->pszHardwareID, -1, NULL, 0);
6462 diW.pszHardwareID = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6463 MultiByteToWideChar(CP_ACP, 0, diA->pszHardwareID, -1, diW.pszHardwareID, len);
6466 if ((Level > 5) && diA->pszProvider) {
6467 len = MultiByteToWideChar(CP_ACP, 0, diA->pszProvider, -1, NULL, 0);
6468 diW.pszProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6469 MultiByteToWideChar(CP_ACP, 0, diA->pszProvider, -1, diW.pszProvider, len);
6472 if (Level > 7) {
6473 FIXME("level %u is incomplete\n", Level);
6476 res = AddPrinterDriverExW(nameW, Level, (LPBYTE) &diW, dwFileCopyFlags);
6477 TRACE("got %u with %u\n", res, GetLastError());
6478 HeapFree(GetProcessHeap(), 0, nameW);
6479 HeapFree(GetProcessHeap(), 0, diW.pName);
6480 HeapFree(GetProcessHeap(), 0, diW.pEnvironment);
6481 HeapFree(GetProcessHeap(), 0, diW.pDriverPath);
6482 HeapFree(GetProcessHeap(), 0, diW.pDataFile);
6483 HeapFree(GetProcessHeap(), 0, diW.pConfigFile);
6484 HeapFree(GetProcessHeap(), 0, diW.pDependentFiles);
6485 HeapFree(GetProcessHeap(), 0, diW.pMonitorName);
6486 HeapFree(GetProcessHeap(), 0, diW.pDefaultDataType);
6487 HeapFree(GetProcessHeap(), 0, diW.pszzPreviousNames);
6488 HeapFree(GetProcessHeap(), 0, diW.pszMfgName);
6489 HeapFree(GetProcessHeap(), 0, diW.pszOEMUrl);
6490 HeapFree(GetProcessHeap(), 0, diW.pszHardwareID);
6491 HeapFree(GetProcessHeap(), 0, diW.pszProvider);
6493 TRACE("=> %u with %u\n", res, GetLastError());
6494 return res;
6497 /******************************************************************************
6498 * ConfigurePortA (WINSPOOL.@)
6500 * See ConfigurePortW.
6503 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
6505 LPWSTR nameW = NULL;
6506 LPWSTR portW = NULL;
6507 INT len;
6508 DWORD res;
6510 TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
6512 /* convert servername to unicode */
6513 if (pName) {
6514 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6515 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6516 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6519 /* convert portname to unicode */
6520 if (pPortName) {
6521 len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
6522 portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6523 MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
6526 res = ConfigurePortW(nameW, hWnd, portW);
6527 HeapFree(GetProcessHeap(), 0, nameW);
6528 HeapFree(GetProcessHeap(), 0, portW);
6529 return res;
6532 /******************************************************************************
6533 * ConfigurePortW (WINSPOOL.@)
6535 * Display the Configuration-Dialog for a specific Port
6537 * PARAMS
6538 * pName [I] Servername or NULL (local Computer)
6539 * hWnd [I] Handle to parent Window for the Dialog-Box
6540 * pPortName [I] Name of the Port, that should be configured
6542 * RETURNS
6543 * Success: TRUE
6544 * Failure: FALSE
6547 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
6549 monitor_t * pm;
6550 monitor_t * pui;
6551 DWORD res;
6553 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
6555 if (pName && pName[0]) {
6556 SetLastError(ERROR_INVALID_PARAMETER);
6557 return FALSE;
6560 if (!pPortName) {
6561 SetLastError(RPC_X_NULL_REF_POINTER);
6562 return FALSE;
6565 /* an empty Portname is Invalid, but can popup a Dialog */
6566 if (!pPortName[0]) {
6567 SetLastError(ERROR_NOT_SUPPORTED);
6568 return FALSE;
6571 pm = monitor_load_by_port(pPortName);
6572 if (pm && pm->monitor && pm->monitor->pfnConfigurePort) {
6573 TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
6574 res = pm->monitor->pfnConfigurePort(pName, hWnd, pPortName);
6575 TRACE("got %d with %u\n", res, GetLastError());
6577 else
6579 pui = monitor_loadui(pm);
6580 if (pui && pui->monitorUI && pui->monitorUI->pfnConfigurePortUI) {
6581 TRACE("Use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
6582 res = pui->monitorUI->pfnConfigurePortUI(pName, hWnd, pPortName);
6583 TRACE("got %d with %u\n", res, GetLastError());
6585 else
6587 FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
6588 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
6590 /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6591 SetLastError(ERROR_NOT_SUPPORTED);
6592 res = FALSE;
6594 monitor_unload(pui);
6596 monitor_unload(pm);
6598 TRACE("returning %d with %u\n", res, GetLastError());
6599 return res;
6602 /******************************************************************************
6603 * ConnectToPrinterDlg (WINSPOOL.@)
6605 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
6607 FIXME("%p %x\n", hWnd, Flags);
6608 return NULL;
6611 /******************************************************************************
6612 * DeletePrinterConnectionA (WINSPOOL.@)
6614 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
6616 FIXME("%s\n", debugstr_a(pName));
6617 return TRUE;
6620 /******************************************************************************
6621 * DeletePrinterConnectionW (WINSPOOL.@)
6623 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
6625 FIXME("%s\n", debugstr_w(pName));
6626 return TRUE;
6629 /******************************************************************************
6630 * DeletePrinterDriverExW (WINSPOOL.@)
6632 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
6633 LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6635 HKEY hkey_drivers;
6636 BOOL ret = FALSE;
6638 TRACE("%s %s %s %x %x\n", debugstr_w(pName), debugstr_w(pEnvironment),
6639 debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
6641 if(pName && pName[0])
6643 FIXME("pName = %s - unsupported\n", debugstr_w(pName));
6644 SetLastError(ERROR_INVALID_PARAMETER);
6645 return FALSE;
6648 if(dwDeleteFlag)
6650 FIXME("dwDeleteFlag = %x - unsupported\n", dwDeleteFlag);
6651 SetLastError(ERROR_INVALID_PARAMETER);
6652 return FALSE;
6655 hkey_drivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
6657 if(!hkey_drivers)
6659 ERR("Can't open drivers key\n");
6660 return FALSE;
6663 if(RegDeleteTreeW(hkey_drivers, pDriverName) == ERROR_SUCCESS)
6664 ret = TRUE;
6666 RegCloseKey(hkey_drivers);
6668 return ret;
6671 /******************************************************************************
6672 * DeletePrinterDriverExA (WINSPOOL.@)
6674 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
6675 LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6677 UNICODE_STRING NameW, EnvW, DriverW;
6678 BOOL ret;
6680 asciitounicode(&NameW, pName);
6681 asciitounicode(&EnvW, pEnvironment);
6682 asciitounicode(&DriverW, pDriverName);
6684 ret = DeletePrinterDriverExW(NameW.Buffer, EnvW.Buffer, DriverW.Buffer, dwDeleteFlag, dwVersionFlag);
6686 RtlFreeUnicodeString(&DriverW);
6687 RtlFreeUnicodeString(&EnvW);
6688 RtlFreeUnicodeString(&NameW);
6690 return ret;
6693 /******************************************************************************
6694 * DeletePrinterDataExW (WINSPOOL.@)
6696 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
6697 LPCWSTR pValueName)
6699 FIXME("%p %s %s\n", hPrinter,
6700 debugstr_w(pKeyName), debugstr_w(pValueName));
6701 return ERROR_INVALID_PARAMETER;
6704 /******************************************************************************
6705 * DeletePrinterDataExA (WINSPOOL.@)
6707 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
6708 LPCSTR pValueName)
6710 FIXME("%p %s %s\n", hPrinter,
6711 debugstr_a(pKeyName), debugstr_a(pValueName));
6712 return ERROR_INVALID_PARAMETER;
6715 /******************************************************************************
6716 * DeletePrintProcessorA (WINSPOOL.@)
6718 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
6720 FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6721 debugstr_a(pPrintProcessorName));
6722 return TRUE;
6725 /******************************************************************************
6726 * DeletePrintProcessorW (WINSPOOL.@)
6728 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
6730 FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6731 debugstr_w(pPrintProcessorName));
6732 return TRUE;
6735 /******************************************************************************
6736 * DeletePrintProvidorA (WINSPOOL.@)
6738 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
6740 FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6741 debugstr_a(pPrintProviderName));
6742 return TRUE;
6745 /******************************************************************************
6746 * DeletePrintProvidorW (WINSPOOL.@)
6748 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
6750 FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6751 debugstr_w(pPrintProviderName));
6752 return TRUE;
6755 /******************************************************************************
6756 * EnumFormsA (WINSPOOL.@)
6758 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6759 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6761 FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6762 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6763 return FALSE;
6766 /******************************************************************************
6767 * EnumFormsW (WINSPOOL.@)
6769 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6770 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6772 FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6773 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6774 return FALSE;
6777 /*****************************************************************************
6778 * EnumMonitorsA [WINSPOOL.@]
6780 * See EnumMonitorsW.
6783 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
6784 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6786 BOOL res;
6787 LPBYTE bufferW = NULL;
6788 LPWSTR nameW = NULL;
6789 DWORD needed = 0;
6790 DWORD numentries = 0;
6791 INT len;
6793 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pMonitors,
6794 cbBuf, pcbNeeded, pcReturned);
6796 /* convert servername to unicode */
6797 if (pName) {
6798 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6799 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6800 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6802 /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
6803 needed = cbBuf * sizeof(WCHAR);
6804 if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
6805 res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6807 if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
6808 if (pcbNeeded) needed = *pcbNeeded;
6809 /* HeapReAlloc return NULL, when bufferW was NULL */
6810 bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
6811 HeapAlloc(GetProcessHeap(), 0, needed);
6813 /* Try again with the large Buffer */
6814 res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6816 numentries = pcReturned ? *pcReturned : 0;
6817 needed = 0;
6819 W2k require the buffersize from EnumMonitorsW also for EnumMonitorsA.
6820 We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
6822 if (res) {
6823 /* EnumMonitorsW collected all Data. Parse them to calculate ANSI-Size */
6824 DWORD entrysize = 0;
6825 DWORD index;
6826 LPSTR ptr;
6827 LPMONITOR_INFO_2W mi2w;
6828 LPMONITOR_INFO_2A mi2a;
6830 /* MONITOR_INFO_*W and MONITOR_INFO_*A have the same size */
6831 entrysize = (Level == 1) ? sizeof(MONITOR_INFO_1A) : sizeof(MONITOR_INFO_2A);
6833 /* First pass: calculate the size for all Entries */
6834 mi2w = (LPMONITOR_INFO_2W) bufferW;
6835 mi2a = (LPMONITOR_INFO_2A) pMonitors;
6836 index = 0;
6837 while (index < numentries) {
6838 index++;
6839 needed += entrysize; /* MONITOR_INFO_?A */
6840 TRACE("%p: parsing #%d (%s)\n", mi2w, index, debugstr_w(mi2w->pName));
6842 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6843 NULL, 0, NULL, NULL);
6844 if (Level > 1) {
6845 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6846 NULL, 0, NULL, NULL);
6847 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
6848 NULL, 0, NULL, NULL);
6850 /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
6851 mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
6852 mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6855 /* check for errors and quit on failure */
6856 if (cbBuf < needed) {
6857 SetLastError(ERROR_INSUFFICIENT_BUFFER);
6858 res = FALSE;
6859 goto emA_cleanup;
6861 len = entrysize * numentries; /* room for all MONITOR_INFO_?A */
6862 ptr = (LPSTR) &pMonitors[len]; /* room for strings */
6863 cbBuf -= len ; /* free Bytes in the user-Buffer */
6864 mi2w = (LPMONITOR_INFO_2W) bufferW;
6865 mi2a = (LPMONITOR_INFO_2A) pMonitors;
6866 index = 0;
6867 /* Second Pass: Fill the User Buffer (if we have one) */
6868 while ((index < numentries) && pMonitors) {
6869 index++;
6870 TRACE("%p: writing MONITOR_INFO_%dA #%d\n", mi2a, Level, index);
6871 mi2a->pName = ptr;
6872 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6873 ptr, cbBuf , NULL, NULL);
6874 ptr += len;
6875 cbBuf -= len;
6876 if (Level > 1) {
6877 mi2a->pEnvironment = ptr;
6878 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6879 ptr, cbBuf, NULL, NULL);
6880 ptr += len;
6881 cbBuf -= len;
6883 mi2a->pDLLName = ptr;
6884 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
6885 ptr, cbBuf, NULL, NULL);
6886 ptr += len;
6887 cbBuf -= len;
6889 /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
6890 mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
6891 mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6894 emA_cleanup:
6895 if (pcbNeeded) *pcbNeeded = needed;
6896 if (pcReturned) *pcReturned = (res) ? numentries : 0;
6898 HeapFree(GetProcessHeap(), 0, nameW);
6899 HeapFree(GetProcessHeap(), 0, bufferW);
6901 TRACE("returning %d with %d (%d byte for %d entries)\n",
6902 (res), GetLastError(), needed, numentries);
6904 return (res);
6908 /*****************************************************************************
6909 * EnumMonitorsW [WINSPOOL.@]
6911 * Enumerate available Port-Monitors
6913 * PARAMS
6914 * pName [I] Servername or NULL (local Computer)
6915 * Level [I] Structure-Level (1:Win9x+NT or 2:NT only)
6916 * pMonitors [O] PTR to Buffer that receives the Result
6917 * cbBuf [I] Size of Buffer at pMonitors
6918 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
6919 * pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
6921 * RETURNS
6922 * Success: TRUE
6923 * Failure: FALSE and in pcbNeeded the Bytes required for buffer, if cbBuf is too small
6925 * NOTES
6926 * Windows reads the Registry once and cache the Results.
6928 *| Language-Monitors are also installed in the same Registry-Location but
6929 *| they are filtered in Windows (not returned by EnumMonitors).
6930 *| We do no filtering to simplify our Code.
6933 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
6934 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6936 DWORD needed = 0;
6937 DWORD numentries = 0;
6938 BOOL res = FALSE;
6940 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
6941 cbBuf, pcbNeeded, pcReturned);
6943 if (pName && (lstrlenW(pName))) {
6944 FIXME("for Server %s not implemented\n", debugstr_w(pName));
6945 SetLastError(ERROR_ACCESS_DENIED);
6946 goto emW_cleanup;
6949 /* Level is not checked in win9x */
6950 if (!Level || (Level > 2)) {
6951 WARN("level (%d) is ignored in win9x\n", Level);
6952 SetLastError(ERROR_INVALID_LEVEL);
6953 goto emW_cleanup;
6955 if (!pcbNeeded) {
6956 SetLastError(RPC_X_NULL_REF_POINTER);
6957 goto emW_cleanup;
6960 /* Scan all Monitor-Keys */
6961 numentries = 0;
6962 needed = get_local_monitors(Level, NULL, 0, &numentries);
6964 /* we calculated the needed buffersize. now do the error-checks */
6965 if (cbBuf < needed) {
6966 SetLastError(ERROR_INSUFFICIENT_BUFFER);
6967 goto emW_cleanup;
6969 else if (!pMonitors || !pcReturned) {
6970 SetLastError(RPC_X_NULL_REF_POINTER);
6971 goto emW_cleanup;
6974 /* fill the Buffer with the Monitor-Keys */
6975 needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
6976 res = TRUE;
6978 emW_cleanup:
6979 if (pcbNeeded) *pcbNeeded = needed;
6980 if (pcReturned) *pcReturned = numentries;
6982 TRACE("returning %d with %d (%d byte for %d entries)\n",
6983 res, GetLastError(), needed, numentries);
6985 return (res);
6988 /******************************************************************************
6989 * XcvDataW (WINSPOOL.@)
6991 * Execute commands in the Printmonitor DLL
6993 * PARAMS
6994 * hXcv [i] Handle from OpenPrinter (with XcvMonitor or XcvPort)
6995 * pszDataName [i] Name of the command to execute
6996 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
6997 * cbInputData [i] Size in Bytes of Buffer at pInputData
6998 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
6999 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
7000 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
7001 * pdwStatus [o] PTR to receive the win32 error code from the Printmonitor DLL
7003 * RETURNS
7004 * Success: TRUE
7005 * Failure: FALSE
7007 * NOTES
7008 * Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
7009 * The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
7011 * Minimal List of commands, that a Printmonitor DLL should support:
7013 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
7014 *| "AddPort" : Add a Port
7015 *| "DeletePort": Delete a Port
7017 * Many Printmonitors support additional commands. Examples for localspl.dll:
7018 * "GetDefaultCommConfig", "SetDefaultCommConfig",
7019 * "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
7022 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
7023 DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
7024 PDWORD pcbOutputNeeded, PDWORD pdwStatus)
7026 opened_printer_t *printer;
7028 TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
7029 pInputData, cbInputData, pOutputData,
7030 cbOutputData, pcbOutputNeeded, pdwStatus);
7032 printer = get_opened_printer(hXcv);
7033 if (!printer || (!printer->hXcv)) {
7034 SetLastError(ERROR_INVALID_HANDLE);
7035 return FALSE;
7038 if (!pcbOutputNeeded) {
7039 SetLastError(ERROR_INVALID_PARAMETER);
7040 return FALSE;
7043 if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
7044 SetLastError(RPC_X_NULL_REF_POINTER);
7045 return FALSE;
7048 *pcbOutputNeeded = 0;
7050 *pdwStatus = printer->pm->monitor->pfnXcvDataPort(printer->hXcv, pszDataName,
7051 pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
7053 return TRUE;
7056 /*****************************************************************************
7057 * EnumPrinterDataA [WINSPOOL.@]
7060 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
7061 DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7062 DWORD cbData, LPDWORD pcbData )
7064 FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7065 cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7066 return ERROR_NO_MORE_ITEMS;
7069 /*****************************************************************************
7070 * EnumPrinterDataW [WINSPOOL.@]
7073 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
7074 DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7075 DWORD cbData, LPDWORD pcbData )
7077 FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7078 cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7079 return ERROR_NO_MORE_ITEMS;
7082 /*****************************************************************************
7083 * EnumPrintProcessorDatatypesA [WINSPOOL.@]
7086 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
7087 DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7088 LPDWORD pcbNeeded, LPDWORD pcReturned)
7090 FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_a(pName),
7091 debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
7092 pcbNeeded, pcReturned);
7093 return FALSE;
7096 /*****************************************************************************
7097 * EnumPrintProcessorDatatypesW [WINSPOOL.@]
7100 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
7101 DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7102 LPDWORD pcbNeeded, LPDWORD pcReturned)
7104 FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7105 debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
7106 pcbNeeded, pcReturned);
7107 return FALSE;
7110 /*****************************************************************************
7111 * EnumPrintProcessorsA [WINSPOOL.@]
7114 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
7115 LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
7117 FIXME("Stub: %s %s %d %p %d %p %p\n", pName, pEnvironment, Level,
7118 pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
7119 return FALSE;
7122 /*****************************************************************************
7123 * EnumPrintProcessorsW [WINSPOOL.@]
7126 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
7127 LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
7129 FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7130 debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
7131 cbBuf, pcbNeeded, pcbReturned);
7132 return FALSE;
7135 /*****************************************************************************
7136 * ExtDeviceMode [WINSPOOL.@]
7139 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
7140 LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
7141 DWORD fMode)
7143 FIXME("Stub: %p %p %p %s %s %p %s %x\n", hWnd, hInst, pDevModeOutput,
7144 debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
7145 debugstr_a(pProfile), fMode);
7146 return -1;
7149 /*****************************************************************************
7150 * FindClosePrinterChangeNotification [WINSPOOL.@]
7153 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
7155 FIXME("Stub: %p\n", hChange);
7156 return TRUE;
7159 /*****************************************************************************
7160 * FindFirstPrinterChangeNotification [WINSPOOL.@]
7163 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
7164 DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
7166 FIXME("Stub: %p %x %x %p\n",
7167 hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
7168 return INVALID_HANDLE_VALUE;
7171 /*****************************************************************************
7172 * FindNextPrinterChangeNotification [WINSPOOL.@]
7175 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
7176 LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
7178 FIXME("Stub: %p %p %p %p\n",
7179 hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
7180 return FALSE;
7183 /*****************************************************************************
7184 * FreePrinterNotifyInfo [WINSPOOL.@]
7187 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
7189 FIXME("Stub: %p\n", pPrinterNotifyInfo);
7190 return TRUE;
7193 /*****************************************************************************
7194 * string_to_buf
7196 * Copies a unicode string into a buffer. The buffer will either contain unicode or
7197 * ansi depending on the unicode parameter.
7199 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
7201 if(!str)
7203 *size = 0;
7204 return TRUE;
7207 if(unicode)
7209 *size = (strlenW(str) + 1) * sizeof(WCHAR);
7210 if(*size <= cb)
7212 memcpy(ptr, str, *size);
7213 return TRUE;
7215 return FALSE;
7217 else
7219 *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
7220 if(*size <= cb)
7222 WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
7223 return TRUE;
7225 return FALSE;
7229 /*****************************************************************************
7230 * get_job_info_1
7232 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
7233 LPDWORD pcbNeeded, BOOL unicode)
7235 DWORD size, left = cbBuf;
7236 BOOL space = (cbBuf > 0);
7237 LPBYTE ptr = buf;
7239 *pcbNeeded = 0;
7241 if(space)
7243 ji1->JobId = job->job_id;
7246 string_to_buf(job->document_title, ptr, left, &size, unicode);
7247 if(space && size <= left)
7249 ji1->pDocument = (LPWSTR)ptr;
7250 ptr += size;
7251 left -= size;
7253 else
7254 space = FALSE;
7255 *pcbNeeded += size;
7257 return space;
7260 /*****************************************************************************
7261 * get_job_info_2
7263 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
7264 LPDWORD pcbNeeded, BOOL unicode)
7266 DWORD size, left = cbBuf;
7267 BOOL space = (cbBuf > 0);
7268 LPBYTE ptr = buf;
7270 *pcbNeeded = 0;
7272 if(space)
7274 ji2->JobId = job->job_id;
7277 string_to_buf(job->document_title, ptr, left, &size, unicode);
7278 if(space && size <= left)
7280 ji2->pDocument = (LPWSTR)ptr;
7281 ptr += size;
7282 left -= size;
7284 else
7285 space = FALSE;
7286 *pcbNeeded += size;
7288 return space;
7291 /*****************************************************************************
7292 * get_job_info
7294 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7295 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
7297 BOOL ret = FALSE;
7298 DWORD needed = 0, size;
7299 job_t *job;
7300 LPBYTE ptr = pJob;
7302 TRACE("%p %d %d %p %d %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
7304 EnterCriticalSection(&printer_handles_cs);
7305 job = get_job(hPrinter, JobId);
7306 if(!job)
7307 goto end;
7309 switch(Level)
7311 case 1:
7312 size = sizeof(JOB_INFO_1W);
7313 if(cbBuf >= size)
7315 cbBuf -= size;
7316 ptr += size;
7317 memset(pJob, 0, size);
7319 else
7320 cbBuf = 0;
7321 ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
7322 needed += size;
7323 break;
7325 case 2:
7326 size = sizeof(JOB_INFO_2W);
7327 if(cbBuf >= size)
7329 cbBuf -= size;
7330 ptr += size;
7331 memset(pJob, 0, size);
7333 else
7334 cbBuf = 0;
7335 ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
7336 needed += size;
7337 break;
7339 case 3:
7340 size = sizeof(JOB_INFO_3);
7341 if(cbBuf >= size)
7343 cbBuf -= size;
7344 memset(pJob, 0, size);
7345 ret = TRUE;
7347 else
7348 cbBuf = 0;
7349 needed = size;
7350 break;
7352 default:
7353 SetLastError(ERROR_INVALID_LEVEL);
7354 goto end;
7356 if(pcbNeeded)
7357 *pcbNeeded = needed;
7358 end:
7359 LeaveCriticalSection(&printer_handles_cs);
7360 return ret;
7363 /*****************************************************************************
7364 * GetJobA [WINSPOOL.@]
7367 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7368 DWORD cbBuf, LPDWORD pcbNeeded)
7370 return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
7373 /*****************************************************************************
7374 * GetJobW [WINSPOOL.@]
7377 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7378 DWORD cbBuf, LPDWORD pcbNeeded)
7380 return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
7383 /*****************************************************************************
7384 * schedule_lpr
7386 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
7388 char *unixname, *queue, *cmd;
7389 char fmt[] = "lpr -P%s %s";
7390 DWORD len;
7392 if(!(unixname = wine_get_unix_file_name(filename)))
7393 return FALSE;
7395 len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7396 queue = HeapAlloc(GetProcessHeap(), 0, len);
7397 WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7399 cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
7400 sprintf(cmd, fmt, queue, unixname);
7402 TRACE("printing with: %s\n", cmd);
7403 system(cmd);
7405 HeapFree(GetProcessHeap(), 0, cmd);
7406 HeapFree(GetProcessHeap(), 0, queue);
7407 HeapFree(GetProcessHeap(), 0, unixname);
7408 return TRUE;
7411 /*****************************************************************************
7412 * schedule_cups
7414 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
7416 #ifdef SONAME_LIBCUPS
7417 if(pcupsPrintFile)
7419 char *unixname, *queue, *doc_titleA;
7420 DWORD len;
7421 BOOL ret;
7423 if(!(unixname = wine_get_unix_file_name(filename)))
7424 return FALSE;
7426 len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7427 queue = HeapAlloc(GetProcessHeap(), 0, len);
7428 WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7430 len = WideCharToMultiByte(CP_ACP, 0, document_title, -1, NULL, 0, NULL, NULL);
7431 doc_titleA = HeapAlloc(GetProcessHeap(), 0, len);
7432 WideCharToMultiByte(CP_ACP, 0, document_title, -1, doc_titleA, len, NULL, NULL);
7434 TRACE("printing via cups\n");
7435 ret = pcupsPrintFile(queue, unixname, doc_titleA, 0, NULL);
7436 HeapFree(GetProcessHeap(), 0, doc_titleA);
7437 HeapFree(GetProcessHeap(), 0, queue);
7438 HeapFree(GetProcessHeap(), 0, unixname);
7439 return ret;
7441 else
7442 #endif
7444 return schedule_lpr(printer_name, filename);
7448 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
7450 LPWSTR filename;
7452 switch(msg)
7454 case WM_INITDIALOG:
7455 SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
7456 return TRUE;
7458 case WM_COMMAND:
7459 if(HIWORD(wparam) == BN_CLICKED)
7461 if(LOWORD(wparam) == IDOK)
7463 HANDLE hf;
7464 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
7465 LPWSTR *output;
7467 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
7468 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
7470 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
7472 WCHAR caption[200], message[200];
7473 int mb_ret;
7475 LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7476 LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
7477 mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
7478 if(mb_ret == IDCANCEL)
7480 HeapFree(GetProcessHeap(), 0, filename);
7481 return TRUE;
7484 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
7485 if(hf == INVALID_HANDLE_VALUE)
7487 WCHAR caption[200], message[200];
7489 LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7490 LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
7491 MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
7492 HeapFree(GetProcessHeap(), 0, filename);
7493 return TRUE;
7495 CloseHandle(hf);
7496 DeleteFileW(filename);
7497 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
7498 *output = filename;
7499 EndDialog(hwnd, IDOK);
7500 return TRUE;
7502 if(LOWORD(wparam) == IDCANCEL)
7504 EndDialog(hwnd, IDCANCEL);
7505 return TRUE;
7508 return FALSE;
7510 return FALSE;
7513 /*****************************************************************************
7514 * get_filename
7516 static BOOL get_filename(LPWSTR *filename)
7518 return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
7519 file_dlg_proc, (LPARAM)filename) == IDOK;
7522 /*****************************************************************************
7523 * schedule_file
7525 static BOOL schedule_file(LPCWSTR filename)
7527 LPWSTR output = NULL;
7529 if(get_filename(&output))
7531 TRACE("copy to %s\n", debugstr_w(output));
7532 CopyFileW(filename, output, FALSE);
7533 HeapFree(GetProcessHeap(), 0, output);
7534 return TRUE;
7536 return FALSE;
7539 /*****************************************************************************
7540 * schedule_pipe
7542 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
7544 #ifdef HAVE_FORK
7545 char *unixname, *cmdA;
7546 DWORD len;
7547 int fds[2] = {-1, -1}, file_fd = -1, no_read;
7548 BOOL ret = FALSE;
7549 char buf[1024];
7551 if(!(unixname = wine_get_unix_file_name(filename)))
7552 return FALSE;
7554 len = WideCharToMultiByte(CP_ACP, 0, cmd, -1, NULL, 0, NULL, NULL);
7555 cmdA = HeapAlloc(GetProcessHeap(), 0, len);
7556 WideCharToMultiByte(CP_ACP, 0, cmd, -1, cmdA, len, NULL, NULL);
7558 TRACE("printing with: %s\n", cmdA);
7560 if((file_fd = open(unixname, O_RDONLY)) == -1)
7561 goto end;
7563 if (pipe(fds))
7565 ERR("pipe() failed!\n");
7566 goto end;
7569 if (fork() == 0)
7571 close(0);
7572 dup2(fds[0], 0);
7573 close(fds[1]);
7575 /* reset signals that we previously set to SIG_IGN */
7576 signal(SIGPIPE, SIG_DFL);
7577 signal(SIGCHLD, SIG_DFL);
7579 execl("/bin/sh", "/bin/sh", "-c", cmdA, (char*)0);
7580 _exit(1);
7583 while((no_read = read(file_fd, buf, sizeof(buf))) > 0)
7584 write(fds[1], buf, no_read);
7586 ret = TRUE;
7588 end:
7589 if(file_fd != -1) close(file_fd);
7590 if(fds[0] != -1) close(fds[0]);
7591 if(fds[1] != -1) close(fds[1]);
7593 HeapFree(GetProcessHeap(), 0, cmdA);
7594 HeapFree(GetProcessHeap(), 0, unixname);
7595 return ret;
7596 #else
7597 return FALSE;
7598 #endif
7601 /*****************************************************************************
7602 * schedule_unixfile
7604 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
7606 int in_fd, out_fd, no_read;
7607 char buf[1024];
7608 BOOL ret = FALSE;
7609 char *unixname, *outputA;
7610 DWORD len;
7612 if(!(unixname = wine_get_unix_file_name(filename)))
7613 return FALSE;
7615 len = WideCharToMultiByte(CP_ACP, 0, output, -1, NULL, 0, NULL, NULL);
7616 outputA = HeapAlloc(GetProcessHeap(), 0, len);
7617 WideCharToMultiByte(CP_ACP, 0, output, -1, outputA, len, NULL, NULL);
7619 out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
7620 in_fd = open(unixname, O_RDONLY);
7621 if(out_fd == -1 || in_fd == -1)
7622 goto end;
7624 while((no_read = read(in_fd, buf, sizeof(buf))) > 0)
7625 write(out_fd, buf, no_read);
7627 ret = TRUE;
7628 end:
7629 if(in_fd != -1) close(in_fd);
7630 if(out_fd != -1) close(out_fd);
7631 HeapFree(GetProcessHeap(), 0, outputA);
7632 HeapFree(GetProcessHeap(), 0, unixname);
7633 return ret;
7636 /*****************************************************************************
7637 * ScheduleJob [WINSPOOL.@]
7640 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
7642 opened_printer_t *printer;
7643 BOOL ret = FALSE;
7644 struct list *cursor, *cursor2;
7646 TRACE("(%p, %x)\n", hPrinter, dwJobID);
7647 EnterCriticalSection(&printer_handles_cs);
7648 printer = get_opened_printer(hPrinter);
7649 if(!printer)
7650 goto end;
7652 LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
7654 job_t *job = LIST_ENTRY(cursor, job_t, entry);
7655 HANDLE hf;
7657 if(job->job_id != dwJobID) continue;
7659 hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7660 if(hf != INVALID_HANDLE_VALUE)
7662 PRINTER_INFO_5W *pi5;
7663 DWORD needed;
7664 HKEY hkey;
7665 WCHAR output[1024];
7666 static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
7667 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
7669 GetPrinterW(hPrinter, 5, NULL, 0, &needed);
7670 pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
7671 GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
7672 TRACE("need to schedule job %d filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
7673 debugstr_w(pi5->pPortName));
7675 output[0] = 0;
7677 /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
7678 if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
7680 DWORD type, count = sizeof(output);
7681 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
7682 RegCloseKey(hkey);
7684 if(output[0] == '|')
7686 schedule_pipe(output + 1, job->filename);
7688 else if(output[0])
7690 schedule_unixfile(output, job->filename);
7692 else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
7694 schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
7696 else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
7698 schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
7700 else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
7702 schedule_file(job->filename);
7704 else
7706 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
7708 HeapFree(GetProcessHeap(), 0, pi5);
7709 CloseHandle(hf);
7710 DeleteFileW(job->filename);
7712 list_remove(cursor);
7713 HeapFree(GetProcessHeap(), 0, job->document_title);
7714 HeapFree(GetProcessHeap(), 0, job->filename);
7715 HeapFree(GetProcessHeap(), 0, job);
7716 ret = TRUE;
7717 break;
7719 end:
7720 LeaveCriticalSection(&printer_handles_cs);
7721 return ret;
7724 /*****************************************************************************
7725 * StartDocDlgA [WINSPOOL.@]
7727 LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
7729 UNICODE_STRING usBuffer;
7730 DOCINFOW docW;
7731 LPWSTR retW;
7732 LPWSTR docnameW = NULL, outputW = NULL, datatypeW = NULL;
7733 LPSTR ret = NULL;
7735 docW.cbSize = sizeof(docW);
7736 if (doc->lpszDocName)
7738 docnameW = asciitounicode(&usBuffer, doc->lpszDocName);
7739 if (!(docW.lpszDocName = docnameW)) return NULL;
7741 if (doc->lpszOutput)
7743 outputW = asciitounicode(&usBuffer, doc->lpszOutput);
7744 if (!(docW.lpszOutput = outputW)) return NULL;
7746 if (doc->lpszDatatype)
7748 datatypeW = asciitounicode(&usBuffer, doc->lpszDatatype);
7749 if (!(docW.lpszDatatype = datatypeW)) return NULL;
7751 docW.fwType = doc->fwType;
7753 retW = StartDocDlgW(hPrinter, &docW);
7755 if(retW)
7757 DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
7758 ret = HeapAlloc(GetProcessHeap(), 0, len);
7759 WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
7760 HeapFree(GetProcessHeap(), 0, retW);
7763 HeapFree(GetProcessHeap(), 0, datatypeW);
7764 HeapFree(GetProcessHeap(), 0, outputW);
7765 HeapFree(GetProcessHeap(), 0, docnameW);
7767 return ret;
7770 /*****************************************************************************
7771 * StartDocDlgW [WINSPOOL.@]
7773 * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
7774 * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
7775 * port is "FILE:". Also returns the full path if passed a relative path.
7777 * The caller should free the returned string from the process heap.
7779 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
7781 LPWSTR ret = NULL;
7782 DWORD len, attr;
7784 if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
7786 PRINTER_INFO_5W *pi5;
7787 GetPrinterW(hPrinter, 5, NULL, 0, &len);
7788 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
7789 return NULL;
7790 pi5 = HeapAlloc(GetProcessHeap(), 0, len);
7791 GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
7792 if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
7794 HeapFree(GetProcessHeap(), 0, pi5);
7795 return NULL;
7797 HeapFree(GetProcessHeap(), 0, pi5);
7800 if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
7802 LPWSTR name;
7804 if (get_filename(&name))
7806 if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
7808 HeapFree(GetProcessHeap(), 0, name);
7809 return NULL;
7811 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7812 GetFullPathNameW(name, len, ret, NULL);
7813 HeapFree(GetProcessHeap(), 0, name);
7815 return ret;
7818 if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
7819 return NULL;
7821 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7822 GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
7824 attr = GetFileAttributesW(ret);
7825 if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
7827 HeapFree(GetProcessHeap(), 0, ret);
7828 ret = NULL;
7830 return ret;