winspool: Add a TRACE when wine_dlopen for SONAME_LIBCUPS failed.
[wine/wine-kai.git] / dlls / winspool.drv / info.c
blob7ac322ddd5ac6ea279185dace9a3b5c493a34064
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 int 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 WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
231 static const WCHAR deviceW[] = {'d','e','v','i','c','e',0};
232 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
233 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
234 static const WCHAR emptyStringW[] = {0};
235 static const WCHAR XcvMonitorW[] = {',','X','c','v','M','o','n','i','t','o','r',' ',0};
236 static const WCHAR XcvPortW[] = {',','X','c','v','P','o','r','t',' ',0};
238 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
240 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
241 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
242 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
244 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
245 'D','o','c','u','m','e','n','t',0};
247 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
248 sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
249 sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
250 0, sizeof(DRIVER_INFO_8W)};
252 /******************************************************************
253 * validate the user-supplied printing-environment [internal]
255 * PARAMS
256 * env [I] PTR to Environment-String or NULL
258 * RETURNS
259 * Failure: NULL
260 * Success: PTR to printenv_t
262 * NOTES
263 * An empty string is handled the same way as NULL.
264 * SetLastEror(ERROR_INVALID_ENVIRONMENT) is called on Failure
268 static const printenv_t * validate_envW(LPCWSTR env)
270 static const printenv_t env_x86 = {envname_x86W, subdir_x86W,
271 3, Version3_RegPathW, Version3_SubdirW};
272 static const printenv_t env_win40 = {envname_win40W, subdir_win40W,
273 0, Version0_RegPathW, Version0_SubdirW};
275 static const printenv_t * const all_printenv[]={&env_x86, &env_win40};
277 const printenv_t *result = NULL;
278 unsigned int i;
280 TRACE("testing %s\n", debugstr_w(env));
281 if (env && env[0])
283 for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
285 if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
287 result = all_printenv[i];
288 break;
292 if (result == NULL) {
293 FIXME("unsupported Environment: %s\n", debugstr_w(env));
294 SetLastError(ERROR_INVALID_ENVIRONMENT);
296 /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
298 else
300 result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
302 TRACE("using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
304 return result;
308 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
309 if passed a NULL string. This returns NULLs to the result.
311 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
313 if ( (src) )
315 RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
316 return usBufferPtr->Buffer;
318 usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
319 return NULL;
322 static LPWSTR strdupW(LPCWSTR p)
324 LPWSTR ret;
325 DWORD len;
327 if(!p) return NULL;
328 len = (strlenW(p) + 1) * sizeof(WCHAR);
329 ret = HeapAlloc(GetProcessHeap(), 0, len);
330 memcpy(ret, p, len);
331 return ret;
334 static LPSTR strdupWtoA( LPCWSTR str )
336 LPSTR ret;
337 INT len;
339 if (!str) return NULL;
340 len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
341 ret = HeapAlloc( GetProcessHeap(), 0, len );
342 if(ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
343 return ret;
346 /* Returns the number of bytes in an ansi \0\0 terminated string (multi_sz).
347 The result includes all \0s (specifically the last two). */
348 static int multi_sz_lenA(const char *str)
350 const char *ptr = str;
351 if(!str) return 0;
354 ptr += lstrlenA(ptr) + 1;
355 } while(*ptr);
357 return ptr - str + 1;
360 static void
361 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name, BOOL force) {
362 char qbuf[200];
364 /* If forcing, or no profile string entry for device yet, set the entry
366 * The always change entry if not WINEPS yet is discussable.
368 if (force ||
369 !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf)) ||
370 !strcmp(qbuf,"*") ||
371 !strstr(qbuf,"WINEPS.DRV")
373 char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
374 HKEY hkey;
376 sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
377 WriteProfileStringA("windows","device",buf);
378 if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
379 RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
380 RegCloseKey(hkey);
382 HeapFree(GetProcessHeap(),0,buf);
386 static BOOL add_printer_driver(const char *name)
388 DRIVER_INFO_3A di3a;
390 static char driver_path[] = "wineps16",
391 data_file[] = "<datafile?>",
392 config_file[] = "wineps16",
393 help_file[] = "<helpfile?>",
394 dep_file[] = "<dependent files?>\0",
395 monitor_name[] = "<monitor name?>",
396 default_data_type[] = "RAW";
398 di3a.cVersion = (GetVersion() & 0x80000000) ? 0 : 3; /* FIXME: add 1, 2 */
399 di3a.pName = (char *)name;
400 di3a.pEnvironment = NULL; /* NULL means auto */
401 di3a.pDriverPath = driver_path;
402 di3a.pDataFile = data_file;
403 di3a.pConfigFile = config_file;
404 di3a.pHelpFile = help_file;
405 di3a.pDependentFiles = dep_file;
406 di3a.pMonitorName = monitor_name;
407 di3a.pDefaultDataType = default_data_type;
409 if (!AddPrinterDriverA(NULL, 3, (LPBYTE)&di3a))
411 ERR("Failed adding driver (%d)\n", GetLastError());
412 return FALSE;
414 return TRUE;
417 #ifdef SONAME_LIBCUPS
418 static typeof(cupsGetDests) *pcupsGetDests;
419 static typeof(cupsGetPPD) *pcupsGetPPD;
420 static typeof(cupsPrintFile) *pcupsPrintFile;
421 static void *cupshandle;
423 static BOOL CUPS_LoadPrinters(void)
425 int i, nrofdests;
426 BOOL hadprinter = FALSE, haddefault = FALSE;
427 cups_dest_t *dests;
428 PRINTER_INFO_2A pinfo2a;
429 char *port,*devline;
430 HKEY hkeyPrinter, hkeyPrinters, hkey;
431 char loaderror[256];
433 cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, loaderror, sizeof(loaderror));
434 if (!cupshandle) {
435 TRACE("%s\n", loaderror);
436 return FALSE;
438 TRACE("%p: %s loaded\n", cupshandle, SONAME_LIBCUPS);
440 #define DYNCUPS(x) \
441 p##x = wine_dlsym(cupshandle, #x, NULL,0); \
442 if (!p##x) return FALSE;
444 DYNCUPS(cupsGetPPD);
445 DYNCUPS(cupsGetDests);
446 DYNCUPS(cupsPrintFile);
447 #undef DYNCUPS
449 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
450 ERROR_SUCCESS) {
451 ERR("Can't create Printers key\n");
452 return FALSE;
455 nrofdests = pcupsGetDests(&dests);
456 TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
457 for (i=0;i<nrofdests;i++) {
458 port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
459 sprintf(port,"LPR:%s",dests[i].name);
460 devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
461 sprintf(devline,"WINEPS.DRV,%s",port);
462 WriteProfileStringA("devices",dests[i].name,devline);
463 if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
464 RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
465 RegCloseKey(hkey);
467 HeapFree(GetProcessHeap(),0,devline);
469 TRACE("Printer %d: %s\n", i, dests[i].name);
470 if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
471 /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
472 and continue */
473 TRACE("Printer already exists\n");
474 RegDeleteValueW(hkeyPrinter, May_Delete_Value);
475 RegCloseKey(hkeyPrinter);
476 } else {
477 static CHAR data_type[] = "RAW",
478 print_proc[] = "WinPrint",
479 comment[] = "WINEPS Printer using CUPS",
480 location[] = "<physical location of printer>",
481 params[] = "<parameters?>",
482 share_name[] = "<share name?>",
483 sep_file[] = "<sep file?>";
485 add_printer_driver(dests[i].name);
487 memset(&pinfo2a,0,sizeof(pinfo2a));
488 pinfo2a.pPrinterName = dests[i].name;
489 pinfo2a.pDatatype = data_type;
490 pinfo2a.pPrintProcessor = print_proc;
491 pinfo2a.pDriverName = dests[i].name;
492 pinfo2a.pComment = comment;
493 pinfo2a.pLocation = location;
494 pinfo2a.pPortName = port;
495 pinfo2a.pParameters = params;
496 pinfo2a.pShareName = share_name;
497 pinfo2a.pSepFile = sep_file;
499 if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
500 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
501 ERR("printer '%s' not added by AddPrinterA (error %d)\n",dests[i].name,GetLastError());
504 HeapFree(GetProcessHeap(),0,port);
506 hadprinter = TRUE;
507 if (dests[i].is_default) {
508 WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
509 haddefault = TRUE;
512 if (hadprinter & !haddefault)
513 WINSPOOL_SetDefaultPrinter(dests[0].name, dests[0].name, TRUE);
514 RegCloseKey(hkeyPrinters);
515 return hadprinter;
517 #endif
519 static BOOL
520 PRINTCAP_ParseEntry(const char *pent, BOOL isfirst) {
521 PRINTER_INFO_2A pinfo2a;
522 char *e,*s,*name,*prettyname,*devname;
523 BOOL ret = FALSE, set_default = FALSE;
524 char *port,*devline,*env_default;
525 HKEY hkeyPrinter, hkeyPrinters, hkey;
527 while (isspace(*pent)) pent++;
528 s = strchr(pent,':');
529 if(s) *s='\0';
530 name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
531 strcpy(name,pent);
532 if(s) {
533 *s=':';
534 pent = s;
535 } else
536 pent = "";
538 TRACE("name=%s entry=%s\n",name, pent);
540 if(ispunct(*name)) { /* a tc entry, not a real printer */
541 TRACE("skipping tc entry\n");
542 goto end;
545 if(strstr(pent,":server")) { /* server only version so skip */
546 TRACE("skipping server entry\n");
547 goto end;
550 /* Determine whether this is a postscript printer. */
552 ret = TRUE;
553 env_default = getenv("PRINTER");
554 prettyname = name;
555 /* Get longest name, usually the one at the right for later display. */
556 while((s=strchr(prettyname,'|'))) {
557 *s = '\0';
558 e = s;
559 while(isspace(*--e)) *e = '\0';
560 TRACE("\t%s\n", debugstr_a(prettyname));
561 if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
562 for(prettyname = s+1; isspace(*prettyname); prettyname++)
565 e = prettyname + strlen(prettyname);
566 while(isspace(*--e)) *e = '\0';
567 TRACE("\t%s\n", debugstr_a(prettyname));
568 if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
570 /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
571 * if it is too long, we use it as comment below. */
572 devname = prettyname;
573 if (strlen(devname)>=CCHDEVICENAME-1)
574 devname = name;
575 if (strlen(devname)>=CCHDEVICENAME-1) {
576 ret = FALSE;
577 goto end;
580 port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
581 sprintf(port,"LPR:%s",name);
583 devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
584 sprintf(devline,"WINEPS.DRV,%s",port);
585 WriteProfileStringA("devices",devname,devline);
586 if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
587 RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
588 RegCloseKey(hkey);
590 HeapFree(GetProcessHeap(),0,devline);
592 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
593 ERROR_SUCCESS) {
594 ERR("Can't create Printers key\n");
595 ret = FALSE;
596 goto end;
598 if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
599 /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
600 and continue */
601 TRACE("Printer already exists\n");
602 RegDeleteValueW(hkeyPrinter, May_Delete_Value);
603 RegCloseKey(hkeyPrinter);
604 } else {
605 static CHAR data_type[] = "RAW",
606 print_proc[] = "WinPrint",
607 comment[] = "WINEPS Printer using LPR",
608 params[] = "<parameters?>",
609 share_name[] = "<share name?>",
610 sep_file[] = "<sep file?>";
612 add_printer_driver(devname);
614 memset(&pinfo2a,0,sizeof(pinfo2a));
615 pinfo2a.pPrinterName = devname;
616 pinfo2a.pDatatype = data_type;
617 pinfo2a.pPrintProcessor = print_proc;
618 pinfo2a.pDriverName = devname;
619 pinfo2a.pComment = comment;
620 pinfo2a.pLocation = prettyname;
621 pinfo2a.pPortName = port;
622 pinfo2a.pParameters = params;
623 pinfo2a.pShareName = share_name;
624 pinfo2a.pSepFile = sep_file;
626 if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
627 if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
628 ERR("%s not added by AddPrinterA (%d)\n",name,GetLastError());
631 RegCloseKey(hkeyPrinters);
633 if (isfirst || set_default)
634 WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
636 HeapFree(GetProcessHeap(), 0, port);
637 end:
638 HeapFree(GetProcessHeap(), 0, name);
639 return ret;
642 static BOOL
643 PRINTCAP_LoadPrinters(void) {
644 BOOL hadprinter = FALSE;
645 char buf[200];
646 FILE *f;
647 char *pent = NULL;
648 BOOL had_bash = FALSE;
650 f = fopen("/etc/printcap","r");
651 if (!f)
652 return FALSE;
654 while(fgets(buf,sizeof(buf),f)) {
655 char *start, *end;
657 end=strchr(buf,'\n');
658 if (end) *end='\0';
660 start = buf;
661 while(isspace(*start)) start++;
662 if(*start == '#' || *start == '\0')
663 continue;
665 if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
666 hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
667 HeapFree(GetProcessHeap(),0,pent);
668 pent = NULL;
671 if (end && *--end == '\\') {
672 *end = '\0';
673 had_bash = TRUE;
674 } else
675 had_bash = FALSE;
677 if (pent) {
678 pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
679 strcat(pent,start);
680 } else {
681 pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
682 strcpy(pent,start);
686 if(pent) {
687 hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
688 HeapFree(GetProcessHeap(),0,pent);
690 fclose(f);
691 return hadprinter;
694 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
696 if (value)
697 return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
698 (lstrlenW(value) + 1) * sizeof(WCHAR));
699 else
700 return ERROR_FILE_NOT_FOUND;
703 /*****************************************************************************
704 * enumerate the local monitors (INTERNAL)
706 * returns the needed size (in bytes) for pMonitors
707 * and *lpreturned is set to number of entries returned in pMonitors
710 static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
712 HKEY hroot = NULL;
713 HKEY hentry = NULL;
714 LPWSTR ptr;
715 LPMONITOR_INFO_2W mi;
716 WCHAR buffer[MAX_PATH];
717 WCHAR dllname[MAX_PATH];
718 DWORD dllsize;
719 DWORD len;
720 DWORD index = 0;
721 DWORD needed = 0;
722 DWORD numentries;
723 DWORD entrysize;
725 entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
727 numentries = *lpreturned; /* this is 0, when we scan the registry */
728 len = entrysize * numentries;
729 ptr = (LPWSTR) &pMonitors[len];
731 numentries = 0;
732 len = sizeof(buffer);
733 buffer[0] = '\0';
735 /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
736 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) == ERROR_SUCCESS) {
737 /* Scan all Monitor-Registry-Keys */
738 while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
739 TRACE("Monitor_%d: %s\n", numentries, debugstr_w(buffer));
740 dllsize = sizeof(dllname);
741 dllname[0] = '\0';
743 /* The Monitor must have a Driver-DLL */
744 if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
745 if (RegQueryValueExW(hentry, DriverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
746 /* We found a valid DLL for this Monitor. */
747 TRACE("using Driver: %s\n", debugstr_w(dllname));
749 RegCloseKey(hentry);
752 /* Windows returns only Port-Monitors here, but to simplify our code,
753 we do no filtering for Language-Monitors */
754 if (dllname[0]) {
755 numentries++;
756 needed += entrysize;
757 needed += (len+1) * sizeof(WCHAR); /* len is lstrlenW(monitorname) */
758 if (level > 1) {
759 /* we install and return only monitors for "Windows NT x86" */
760 needed += (lstrlenW(envname_x86W) +1) * sizeof(WCHAR);
761 needed += dllsize;
764 /* required size is calculated. Now fill the user-buffer */
765 if (pMonitors && (cbBuf >= needed)){
766 mi = (LPMONITOR_INFO_2W) pMonitors;
767 pMonitors += entrysize;
769 TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi, level, numentries);
770 mi->pName = ptr;
771 lstrcpyW(ptr, buffer); /* Name of the Monitor */
772 ptr += (len+1); /* len is lstrlenW(monitorname) */
773 if (level > 1) {
774 mi->pEnvironment = ptr;
775 lstrcpyW(ptr, envname_x86W); /* fixed to "Windows NT x86" */
776 ptr += (lstrlenW(envname_x86W)+1);
778 mi->pDLLName = ptr;
779 lstrcpyW(ptr, dllname); /* Name of the Driver-DLL */
780 ptr += (dllsize / sizeof(WCHAR));
784 index++;
785 len = sizeof(buffer);
786 buffer[0] = '\0';
788 RegCloseKey(hroot);
790 *lpreturned = numentries;
791 TRACE("need %d byte for %d entries\n", needed, numentries);
792 return needed;
795 /******************************************************************
796 * monitor_unload [internal]
798 * release a printmonitor and unload it from memory, when needed
801 static void monitor_unload(monitor_t * pm)
803 if (pm == NULL) return;
804 TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
806 EnterCriticalSection(&monitor_handles_cs);
808 if (pm->refcount) pm->refcount--;
810 if (pm->refcount == 0) {
811 list_remove(&pm->entry);
812 FreeLibrary(pm->hdll);
813 HeapFree(GetProcessHeap(), 0, pm->name);
814 HeapFree(GetProcessHeap(), 0, pm->dllname);
815 HeapFree(GetProcessHeap(), 0, pm);
817 LeaveCriticalSection(&monitor_handles_cs);
820 /******************************************************************
821 * monitor_unloadall [internal]
823 * release all printmonitors and unload them from memory, when needed
826 static void monitor_unloadall(void)
828 monitor_t * pm;
829 monitor_t * next;
831 EnterCriticalSection(&monitor_handles_cs);
832 /* iterate through the list, with safety against removal */
833 LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
835 monitor_unload(pm);
837 LeaveCriticalSection(&monitor_handles_cs);
840 /******************************************************************
841 * monitor_load [internal]
843 * load a printmonitor, get the dllname from the registry, when needed
844 * initialize the monitor and dump found function-pointers
846 * On failure, SetLastError() is called and NULL is returned
849 static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname)
851 LPMONITOR2 (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
852 PMONITORUI (WINAPI *pInitializePrintMonitorUI)(VOID);
853 LPMONITOREX (WINAPI *pInitializePrintMonitor) (LPWSTR);
854 DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
855 DWORD (WINAPI *pInitializeMonitor) (LPWSTR);
857 monitor_t * pm = NULL;
858 monitor_t * cursor;
859 LPWSTR regroot = NULL;
860 LPWSTR driver = dllname;
862 TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
863 /* Is the Monitor already loaded? */
864 EnterCriticalSection(&monitor_handles_cs);
866 if (name) {
867 LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
869 if (cursor->name && (lstrcmpW(name, cursor->name) == 0)) {
870 pm = cursor;
871 break;
876 if (pm == NULL) {
877 pm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(monitor_t));
878 if (pm == NULL) goto cleanup;
879 list_add_tail(&monitor_handles, &pm->entry);
881 pm->refcount++;
883 if (pm->name == NULL) {
884 /* Load the monitor */
885 LPMONITOREX pmonitorEx;
886 DWORD len;
888 if (name) {
889 len = lstrlenW(MonitorsW) + lstrlenW(name) + 2;
890 regroot = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
893 if (regroot) {
894 lstrcpyW(regroot, MonitorsW);
895 lstrcatW(regroot, name);
896 /* Get the Driver from the Registry */
897 if (driver == NULL) {
898 HKEY hroot;
899 DWORD namesize;
900 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
901 if (RegQueryValueExW(hroot, DriverW, NULL, NULL, NULL,
902 &namesize) == ERROR_SUCCESS) {
903 driver = HeapAlloc(GetProcessHeap(), 0, namesize);
904 RegQueryValueExW(hroot, DriverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
906 RegCloseKey(hroot);
911 pm->name = strdupW(name);
912 pm->dllname = strdupW(driver);
914 if ((name && (!regroot || !pm->name)) || !pm->dllname) {
915 monitor_unload(pm);
916 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
917 pm = NULL;
918 goto cleanup;
921 pm->hdll = LoadLibraryW(driver);
922 TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
924 if (pm->hdll == NULL) {
925 monitor_unload(pm);
926 SetLastError(ERROR_MOD_NOT_FOUND);
927 pm = NULL;
928 goto cleanup;
931 pInitializePrintMonitor2 = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
932 pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
933 pInitializePrintMonitor = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
934 pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
935 pInitializeMonitor = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
938 TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
939 TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
940 TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
941 TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
942 TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
944 if (pInitializePrintMonitorUI != NULL) {
945 pm->monitorUI = pInitializePrintMonitorUI();
946 TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver));
947 if (pm->monitorUI) {
948 TRACE( "0x%08x: dwMonitorSize (%d)\n",
949 pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize );
954 if (pInitializePrintMonitor && regroot) {
955 pmonitorEx = pInitializePrintMonitor(regroot);
956 TRACE( "%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
957 pmonitorEx, debugstr_w(driver), debugstr_w(regroot));
959 if (pmonitorEx) {
960 pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
961 pm->monitor = &(pmonitorEx->Monitor);
965 if (pm->monitor) {
966 TRACE( "0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize );
970 if (!pm->monitor && regroot) {
971 if (pInitializePrintMonitor2 != NULL) {
972 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
974 if (pInitializeMonitorEx != NULL) {
975 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
977 if (pInitializeMonitor != NULL) {
978 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
981 if (!pm->monitor && !pm->monitorUI) {
982 monitor_unload(pm);
983 SetLastError(ERROR_PROC_NOT_FOUND);
984 pm = NULL;
987 cleanup:
988 if ((pm_localport == NULL) && (pm != NULL) && (lstrcmpW(pm->name, LocalPortW) == 0)) {
989 pm->refcount++;
990 pm_localport = pm;
992 LeaveCriticalSection(&monitor_handles_cs);
993 if (driver != dllname) HeapFree(GetProcessHeap(), 0, driver);
994 HeapFree(GetProcessHeap(), 0, regroot);
995 TRACE("=> %p\n", pm);
996 return pm;
999 /******************************************************************
1000 * monitor_loadall [internal]
1002 * Load all registered monitors
1005 static DWORD monitor_loadall(void)
1007 monitor_t * pm;
1008 DWORD registered = 0;
1009 DWORD loaded = 0;
1010 HKEY hmonitors;
1011 WCHAR buffer[MAX_PATH];
1012 DWORD id = 0;
1014 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hmonitors) == ERROR_SUCCESS) {
1015 RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, &registered, NULL, NULL,
1016 NULL, NULL, NULL, NULL, NULL);
1018 TRACE("%d monitors registered\n", registered);
1020 EnterCriticalSection(&monitor_handles_cs);
1021 while (id < registered) {
1022 buffer[0] = '\0';
1023 RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
1024 pm = monitor_load(buffer, NULL);
1025 if (pm) loaded++;
1026 id++;
1028 LeaveCriticalSection(&monitor_handles_cs);
1029 RegCloseKey(hmonitors);
1031 TRACE("%d monitors loaded\n", loaded);
1032 return loaded;
1035 /******************************************************************
1036 * monitor_loadui [internal]
1038 * load the userinterface-dll for a given portmonitor
1040 * On failure, NULL is returned
1043 static monitor_t * monitor_loadui(monitor_t * pm)
1045 monitor_t * pui = NULL;
1046 LPWSTR buffer[MAX_PATH];
1047 HANDLE hXcv;
1048 DWORD len;
1049 DWORD res;
1051 if (pm == NULL) return NULL;
1052 TRACE("(%p) => dllname: %s\n", pm, debugstr_w(pm->dllname));
1054 /* Try the Portmonitor first; works for many monitors */
1055 if (pm->monitorUI) {
1056 EnterCriticalSection(&monitor_handles_cs);
1057 pm->refcount++;
1058 LeaveCriticalSection(&monitor_handles_cs);
1059 return pm;
1062 /* query the userinterface-dllname from the Portmonitor */
1063 if ((pm->monitor) && (pm->monitor->pfnXcvDataPort)) {
1064 /* building (",XcvMonitor %s",pm->name) not needed yet */
1065 res = pm->monitor->pfnXcvOpenPort(emptyStringW, SERVER_ACCESS_ADMINISTER, &hXcv);
1066 TRACE("got %u with %p\n", res, hXcv);
1067 if (res) {
1068 res = pm->monitor->pfnXcvDataPort(hXcv, MonitorUIW, NULL, 0, (BYTE *) buffer, sizeof(buffer), &len);
1069 TRACE("got %u with %s\n", res, debugstr_w((LPWSTR) buffer));
1070 if (res == ERROR_SUCCESS) pui = monitor_load(NULL, (LPWSTR) buffer);
1071 pm->monitor->pfnXcvClosePort(hXcv);
1074 return pui;
1078 /******************************************************************
1079 * monitor_load_by_port [internal]
1081 * load a printmonitor for a given port
1083 * On failure, NULL is returned
1086 static monitor_t * monitor_load_by_port(LPCWSTR portname)
1088 HKEY hroot;
1089 HKEY hport;
1090 LPWSTR buffer;
1091 monitor_t * pm = NULL;
1092 DWORD registered = 0;
1093 DWORD id = 0;
1094 DWORD len;
1096 TRACE("(%s)\n", debugstr_w(portname));
1098 /* Try the Local Monitor first */
1099 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot) == ERROR_SUCCESS) {
1100 if (RegQueryValueExW(hroot, portname, NULL, NULL, NULL, &len) == ERROR_SUCCESS) {
1101 /* found the portname */
1102 RegCloseKey(hroot);
1103 return monitor_load(LocalPortW, NULL);
1105 RegCloseKey(hroot);
1108 len = MAX_PATH + lstrlenW(bs_Ports_bsW) + lstrlenW(portname) + 1;
1109 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1110 if (buffer == NULL) return NULL;
1112 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) == ERROR_SUCCESS) {
1113 EnterCriticalSection(&monitor_handles_cs);
1114 RegQueryInfoKeyW(hroot, NULL, NULL, NULL, &registered, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1116 while ((pm == NULL) && (id < registered)) {
1117 buffer[0] = '\0';
1118 RegEnumKeyW(hroot, id, buffer, MAX_PATH);
1119 TRACE("testing %s\n", debugstr_w(buffer));
1120 len = lstrlenW(buffer);
1121 lstrcatW(buffer, bs_Ports_bsW);
1122 lstrcatW(buffer, portname);
1123 if (RegOpenKeyW(hroot, buffer, &hport) == ERROR_SUCCESS) {
1124 RegCloseKey(hport);
1125 buffer[len] = '\0'; /* use only the Monitor-Name */
1126 pm = monitor_load(buffer, NULL);
1128 id++;
1130 LeaveCriticalSection(&monitor_handles_cs);
1131 RegCloseKey(hroot);
1133 HeapFree(GetProcessHeap(), 0, buffer);
1134 return pm;
1137 /******************************************************************
1138 * enumerate the local Ports from all loaded monitors (internal)
1140 * returns the needed size (in bytes) for pPorts
1141 * and *lpreturned is set to number of entries returned in pPorts
1144 static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
1146 monitor_t * pm;
1147 LPWSTR ptr;
1148 LPPORT_INFO_2W cache;
1149 LPPORT_INFO_2W out;
1150 LPBYTE pi_buffer = NULL;
1151 DWORD pi_allocated = 0;
1152 DWORD pi_needed;
1153 DWORD pi_index;
1154 DWORD pi_returned;
1155 DWORD res;
1156 DWORD outindex = 0;
1157 DWORD needed;
1158 DWORD numentries;
1159 DWORD entrysize;
1162 TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
1163 entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
1165 numentries = *lpreturned; /* this is 0, when we scan the registry */
1166 needed = entrysize * numentries;
1167 ptr = (LPWSTR) &pPorts[needed];
1169 numentries = 0;
1170 needed = 0;
1172 LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
1174 if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
1175 pi_needed = 0;
1176 pi_returned = 0;
1177 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
1178 if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
1179 /* Do not use HeapReAlloc (we do not need the old data in the buffer) */
1180 HeapFree(GetProcessHeap(), 0, pi_buffer);
1181 pi_buffer = HeapAlloc(GetProcessHeap(), 0, pi_needed);
1182 pi_allocated = (pi_buffer) ? pi_needed : 0;
1183 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
1185 TRACE( "(%s) got %d with %d (need %d byte for %d entries)\n",
1186 debugstr_w(pm->name), res, GetLastError(), pi_needed, pi_returned);
1188 numentries += pi_returned;
1189 needed += pi_needed;
1191 /* fill the output-buffer (pPorts), if we have one */
1192 if (pPorts && (cbBuf >= needed ) && pi_buffer) {
1193 pi_index = 0;
1194 while (pi_returned > pi_index) {
1195 cache = (LPPORT_INFO_2W) &pi_buffer[pi_index * entrysize];
1196 out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
1197 out->pPortName = ptr;
1198 lstrcpyW(ptr, cache->pPortName);
1199 ptr += (lstrlenW(ptr)+1);
1200 if (level > 1) {
1201 out->pMonitorName = ptr;
1202 lstrcpyW(ptr, cache->pMonitorName);
1203 ptr += (lstrlenW(ptr)+1);
1205 out->pDescription = ptr;
1206 lstrcpyW(ptr, cache->pDescription);
1207 ptr += (lstrlenW(ptr)+1);
1208 out->fPortType = cache->fPortType;
1209 out->Reserved = cache->Reserved;
1211 pi_index++;
1212 outindex++;
1217 /* the temporary portinfo-buffer is no longer needed */
1218 HeapFree(GetProcessHeap(), 0, pi_buffer);
1220 *lpreturned = numentries;
1221 TRACE("need %d byte for %d entries\n", needed, numentries);
1222 return needed;
1225 /******************************************************************
1226 * get_servername_from_name (internal)
1228 * for an external server, a copy of the serverpart from the full name is returned
1231 static LPWSTR get_servername_from_name(LPCWSTR name)
1233 LPWSTR server;
1234 LPWSTR ptr;
1235 WCHAR buffer[MAX_PATH];
1236 DWORD len;
1238 if (name == NULL) return NULL;
1239 if ((name[0] != '\\') || (name[1] != '\\')) return NULL;
1241 server = strdupW(&name[2]); /* skip over both backslash */
1242 if (server == NULL) return NULL;
1244 /* strip '\' and the printername */
1245 ptr = strchrW(server, '\\');
1246 if (ptr) ptr[0] = '\0';
1248 TRACE("found %s\n", debugstr_w(server));
1250 len = sizeof(buffer)/sizeof(buffer[0]);
1251 if (GetComputerNameW(buffer, &len)) {
1252 if (lstrcmpW(buffer, server) == 0) {
1253 /* The requested Servername is our computername */
1254 HeapFree(GetProcessHeap(), 0, server);
1255 return NULL;
1258 return server;
1261 /******************************************************************
1262 * get_basename_from_name (internal)
1264 * skip over the serverpart from the full name
1267 static LPCWSTR get_basename_from_name(LPCWSTR name)
1269 if (name == NULL) return NULL;
1270 if ((name[0] == '\\') && (name[1] == '\\')) {
1271 /* skip over the servername and search for the following '\' */
1272 name = strchrW(&name[2], '\\');
1273 if ((name) && (name[1])) {
1274 /* found a separator ('\') followed by a name:
1275 skip over the separator and return the rest */
1276 name++;
1278 else
1280 /* no basename present (we found only a servername) */
1281 return NULL;
1284 return name;
1287 /******************************************************************
1288 * get_opened_printer_entry
1289 * Get the first place empty in the opened printer table
1291 * ToDo:
1292 * - pDefault is ignored
1294 static HANDLE get_opened_printer_entry(LPCWSTR name, LPPRINTER_DEFAULTSW pDefault)
1296 UINT_PTR handle = nb_printer_handles, i;
1297 jobqueue_t *queue = NULL;
1298 opened_printer_t *printer = NULL;
1299 LPWSTR servername;
1300 LPCWSTR printername;
1301 HKEY hkeyPrinters;
1302 HKEY hkeyPrinter;
1303 DWORD len;
1305 servername = get_servername_from_name(name);
1306 if (servername) {
1307 FIXME("server %s not supported\n", debugstr_w(servername));
1308 HeapFree(GetProcessHeap(), 0, servername);
1309 SetLastError(ERROR_INVALID_PRINTER_NAME);
1310 return NULL;
1313 printername = get_basename_from_name(name);
1314 if (name != printername) TRACE("converted %s to %s\n", debugstr_w(name), debugstr_w(printername));
1316 /* an empty printername is invalid */
1317 if (printername && (!printername[0])) {
1318 SetLastError(ERROR_INVALID_PARAMETER);
1319 return NULL;
1322 EnterCriticalSection(&printer_handles_cs);
1324 for (i = 0; i < nb_printer_handles; i++)
1326 if (!printer_handles[i])
1328 if(handle == nb_printer_handles)
1329 handle = i;
1331 else
1333 if(!queue && (name) && !lstrcmpW(name, printer_handles[i]->name))
1334 queue = printer_handles[i]->queue;
1338 if (handle >= nb_printer_handles)
1340 opened_printer_t **new_array;
1341 if (printer_handles)
1342 new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
1343 (nb_printer_handles + 16) * sizeof(*new_array) );
1344 else
1345 new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1346 (nb_printer_handles + 16) * sizeof(*new_array) );
1348 if (!new_array)
1350 handle = 0;
1351 goto end;
1353 printer_handles = new_array;
1354 nb_printer_handles += 16;
1357 if (!(printer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*printer))))
1359 handle = 0;
1360 goto end;
1364 /* clone the base name. This is NULL for the printserver */
1365 printer->printername = strdupW(printername);
1367 /* clone the full name */
1368 printer->name = strdupW(name);
1369 if (name && (!printer->name)) {
1370 handle = 0;
1371 goto end;
1374 if (printername) {
1375 len = sizeof(XcvMonitorW)/sizeof(WCHAR) - 1;
1376 if (strncmpW(printername, XcvMonitorW, len) == 0) {
1377 /* OpenPrinter(",XcvMonitor " detected */
1378 TRACE(",XcvMonitor: %s\n", debugstr_w(&printername[len]));
1379 printer->pm = monitor_load(&printername[len], NULL);
1380 if (printer->pm == NULL) {
1381 SetLastError(ERROR_UNKNOWN_PORT);
1382 handle = 0;
1383 goto end;
1386 else
1388 len = sizeof(XcvPortW)/sizeof(WCHAR) - 1;
1389 if (strncmpW( printername, XcvPortW, len) == 0) {
1390 /* OpenPrinter(",XcvPort " detected */
1391 TRACE(",XcvPort: %s\n", debugstr_w(&printername[len]));
1392 printer->pm = monitor_load_by_port(&printername[len]);
1393 if (printer->pm == NULL) {
1394 SetLastError(ERROR_UNKNOWN_PORT);
1395 handle = 0;
1396 goto end;
1401 if (printer->pm) {
1402 if ((printer->pm->monitor) && (printer->pm->monitor->pfnXcvOpenPort)) {
1403 printer->pm->monitor->pfnXcvOpenPort(&printername[len],
1404 pDefault ? pDefault->DesiredAccess : 0,
1405 &printer->hXcv);
1407 if (printer->hXcv == NULL) {
1408 SetLastError(ERROR_INVALID_PARAMETER);
1409 handle = 0;
1410 goto end;
1413 else
1415 /* Does the Printer exist? */
1416 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) != ERROR_SUCCESS) {
1417 ERR("Can't create Printers key\n");
1418 handle = 0;
1419 goto end;
1421 if (RegOpenKeyW(hkeyPrinters, printername, &hkeyPrinter) != ERROR_SUCCESS) {
1422 WARN("Printer not found in Registry: %s\n", debugstr_w(printername));
1423 RegCloseKey(hkeyPrinters);
1424 SetLastError(ERROR_INVALID_PRINTER_NAME);
1425 handle = 0;
1426 goto end;
1428 RegCloseKey(hkeyPrinter);
1429 RegCloseKey(hkeyPrinters);
1432 else
1434 TRACE("using the local printserver\n");
1437 if(queue)
1438 printer->queue = queue;
1439 else
1441 printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
1442 if (!printer->queue) {
1443 handle = 0;
1444 goto end;
1446 list_init(&printer->queue->jobs);
1447 printer->queue->ref = 0;
1449 InterlockedIncrement(&printer->queue->ref);
1451 printer_handles[handle] = printer;
1452 handle++;
1453 end:
1454 LeaveCriticalSection(&printer_handles_cs);
1455 if (!handle && printer) {
1456 /* Something failed: Free all resources */
1457 if (printer->hXcv) printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
1458 monitor_unload(printer->pm);
1459 HeapFree(GetProcessHeap(), 0, printer->printername);
1460 HeapFree(GetProcessHeap(), 0, printer->name);
1461 if (!queue) HeapFree(GetProcessHeap(), 0, printer->queue);
1462 HeapFree(GetProcessHeap(), 0, printer);
1465 return (HANDLE)handle;
1468 /******************************************************************
1469 * get_opened_printer
1470 * Get the pointer to the opened printer referred by the handle
1472 static opened_printer_t *get_opened_printer(HANDLE hprn)
1474 UINT_PTR idx = (UINT_PTR)hprn;
1475 opened_printer_t *ret = NULL;
1477 EnterCriticalSection(&printer_handles_cs);
1479 if ((idx <= 0) || (idx > nb_printer_handles))
1480 goto end;
1482 ret = printer_handles[idx - 1];
1483 end:
1484 LeaveCriticalSection(&printer_handles_cs);
1485 return ret;
1488 /******************************************************************
1489 * get_opened_printer_name
1490 * Get the pointer to the opened printer name referred by the handle
1492 static LPCWSTR get_opened_printer_name(HANDLE hprn)
1494 opened_printer_t *printer = get_opened_printer(hprn);
1495 if(!printer) return NULL;
1496 return printer->name;
1499 /******************************************************************
1500 * WINSPOOL_GetOpenedPrinterRegKey
1503 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
1505 LPCWSTR name = get_opened_printer_name(hPrinter);
1506 DWORD ret;
1507 HKEY hkeyPrinters;
1509 if(!name) return ERROR_INVALID_HANDLE;
1511 if((ret = RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters)) !=
1512 ERROR_SUCCESS)
1513 return ret;
1515 if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
1517 ERR("Can't find opened printer %s in registry\n",
1518 debugstr_w(name));
1519 RegCloseKey(hkeyPrinters);
1520 return ERROR_INVALID_PRINTER_NAME; /* ? */
1522 RegCloseKey(hkeyPrinters);
1523 return ERROR_SUCCESS;
1526 void WINSPOOL_LoadSystemPrinters(void)
1528 HKEY hkey, hkeyPrinters;
1529 HANDLE hprn;
1530 DWORD needed, num, i;
1531 WCHAR PrinterName[256];
1532 BOOL done = FALSE;
1534 /* This ensures that all printer entries have a valid Name value. If causes
1535 problems later if they don't. If one is found to be missed we create one
1536 and set it equal to the name of the key */
1537 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
1538 if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
1539 NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
1540 for(i = 0; i < num; i++) {
1541 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) == ERROR_SUCCESS) {
1542 if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
1543 if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
1544 set_reg_szW(hkey, NameW, PrinterName);
1546 RegCloseKey(hkey);
1551 RegCloseKey(hkeyPrinters);
1554 /* We want to avoid calling AddPrinter on printers as much as
1555 possible, because on cups printers this will (eventually) lead
1556 to a call to cupsGetPPD which takes forever, even with non-cups
1557 printers AddPrinter takes a while. So we'll tag all printers that
1558 were automatically added last time around, if they still exist
1559 we'll leave them be otherwise we'll delete them. */
1560 EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1561 if(needed) {
1562 PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1563 if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1564 for(i = 0; i < num; i++) {
1565 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1566 if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1567 if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1568 DWORD dw = 1;
1569 RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
1570 RegCloseKey(hkey);
1572 ClosePrinter(hprn);
1577 HeapFree(GetProcessHeap(), 0, pi);
1581 #ifdef SONAME_LIBCUPS
1582 done = CUPS_LoadPrinters();
1583 #endif
1585 if(!done) /* If we have any CUPS based printers, skip looking for printcap printers */
1586 PRINTCAP_LoadPrinters();
1588 /* Now enumerate the list again and delete any printers that a still tagged */
1589 EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1590 if(needed) {
1591 PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1592 if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1593 for(i = 0; i < num; i++) {
1594 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1595 if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1596 BOOL delete_driver = FALSE;
1597 if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1598 DWORD dw, type, size = sizeof(dw);
1599 if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
1600 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
1601 DeletePrinter(hprn);
1602 delete_driver = TRUE;
1604 RegCloseKey(hkey);
1606 ClosePrinter(hprn);
1607 if(delete_driver)
1608 DeletePrinterDriverExA(NULL, NULL, pi[i].pPrinterName, 0, 0);
1613 HeapFree(GetProcessHeap(), 0, pi);
1616 return;
1620 /******************************************************************
1621 * get_job
1623 * Get the pointer to the specified job.
1624 * Should hold the printer_handles_cs before calling.
1626 static job_t *get_job(HANDLE hprn, DWORD JobId)
1628 opened_printer_t *printer = get_opened_printer(hprn);
1629 job_t *job;
1631 if(!printer) return NULL;
1632 LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
1634 if(job->job_id == JobId)
1635 return job;
1637 return NULL;
1640 /***********************************************************
1641 * DEVMODEcpyAtoW
1643 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
1645 BOOL Formname;
1646 ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
1647 DWORD size;
1649 Formname = (dmA->dmSize > off_formname);
1650 size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
1651 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
1652 dmW->dmDeviceName, CCHDEVICENAME);
1653 if(!Formname) {
1654 memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1655 dmA->dmSize - CCHDEVICENAME);
1656 } else {
1657 memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1658 off_formname - CCHDEVICENAME);
1659 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
1660 dmW->dmFormName, CCHFORMNAME);
1661 memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
1662 (off_formname + CCHFORMNAME));
1664 dmW->dmSize = size;
1665 memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
1666 dmA->dmDriverExtra);
1667 return dmW;
1670 /***********************************************************
1671 * DEVMODEdupWtoA
1672 * Creates an ascii copy of supplied devmode on heap
1674 static LPDEVMODEA DEVMODEdupWtoA(HANDLE heap, const DEVMODEW *dmW)
1676 LPDEVMODEA dmA;
1677 DWORD size;
1678 BOOL Formname;
1679 ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;
1681 if(!dmW) return NULL;
1682 Formname = (dmW->dmSize > off_formname);
1683 size = dmW->dmSize - CCHDEVICENAME - (Formname ? CCHFORMNAME : 0);
1684 dmA = HeapAlloc(heap, HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
1685 WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
1686 (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
1687 if(!Formname) {
1688 memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1689 dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR));
1690 } else {
1691 memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1692 off_formname - CCHDEVICENAME * sizeof(WCHAR));
1693 WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
1694 (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
1695 memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
1696 (off_formname + CCHFORMNAME * sizeof(WCHAR)));
1698 dmA->dmSize = size;
1699 memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
1700 dmW->dmDriverExtra);
1701 return dmA;
1704 /***********************************************************
1705 * PRINTER_INFO_2AtoW
1706 * Creates a unicode copy of PRINTER_INFO_2A on heap
1708 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
1710 LPPRINTER_INFO_2W piW;
1711 UNICODE_STRING usBuffer;
1713 if(!piA) return NULL;
1714 piW = HeapAlloc(heap, 0, sizeof(*piW));
1715 memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
1717 piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
1718 piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
1719 piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
1720 piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
1721 piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
1722 piW->pComment = asciitounicode(&usBuffer,piA->pComment);
1723 piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
1724 piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
1725 piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
1726 piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
1727 piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
1728 piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
1729 return piW;
1732 /***********************************************************
1733 * FREE_PRINTER_INFO_2W
1734 * Free PRINTER_INFO_2W and all strings
1736 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
1738 if(!piW) return;
1740 HeapFree(heap,0,piW->pServerName);
1741 HeapFree(heap,0,piW->pPrinterName);
1742 HeapFree(heap,0,piW->pShareName);
1743 HeapFree(heap,0,piW->pPortName);
1744 HeapFree(heap,0,piW->pDriverName);
1745 HeapFree(heap,0,piW->pComment);
1746 HeapFree(heap,0,piW->pLocation);
1747 HeapFree(heap,0,piW->pDevMode);
1748 HeapFree(heap,0,piW->pSepFile);
1749 HeapFree(heap,0,piW->pPrintProcessor);
1750 HeapFree(heap,0,piW->pDatatype);
1751 HeapFree(heap,0,piW->pParameters);
1752 HeapFree(heap,0,piW);
1753 return;
1756 /******************************************************************
1757 * DeviceCapabilities [WINSPOOL.@]
1758 * DeviceCapabilitiesA [WINSPOOL.@]
1761 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
1762 LPSTR pOutput, LPDEVMODEA lpdm)
1764 INT ret;
1766 if (!GDI_CallDeviceCapabilities16)
1768 GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1769 (LPCSTR)104 );
1770 if (!GDI_CallDeviceCapabilities16) return -1;
1772 ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
1774 /* If DC_PAPERSIZE map POINT16s to POINTs */
1775 if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
1776 POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
1777 POINT *pt = (POINT *)pOutput;
1778 INT i;
1779 memcpy(tmp, pOutput, ret * sizeof(POINT16));
1780 for(i = 0; i < ret; i++, pt++)
1782 pt->x = tmp[i].x;
1783 pt->y = tmp[i].y;
1785 HeapFree( GetProcessHeap(), 0, tmp );
1787 return ret;
1791 /*****************************************************************************
1792 * DeviceCapabilitiesW [WINSPOOL.@]
1794 * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
1797 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
1798 WORD fwCapability, LPWSTR pOutput,
1799 const DEVMODEW *pDevMode)
1801 LPDEVMODEA dmA = DEVMODEdupWtoA(GetProcessHeap(), pDevMode);
1802 LPSTR pDeviceA = strdupWtoA(pDevice);
1803 LPSTR pPortA = strdupWtoA(pPort);
1804 INT ret;
1806 if(pOutput && (fwCapability == DC_BINNAMES ||
1807 fwCapability == DC_FILEDEPENDENCIES ||
1808 fwCapability == DC_PAPERNAMES)) {
1809 /* These need A -> W translation */
1810 INT size = 0, i;
1811 LPSTR pOutputA;
1812 ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
1813 dmA);
1814 if(ret == -1)
1815 return ret;
1816 switch(fwCapability) {
1817 case DC_BINNAMES:
1818 size = 24;
1819 break;
1820 case DC_PAPERNAMES:
1821 case DC_FILEDEPENDENCIES:
1822 size = 64;
1823 break;
1825 pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
1826 ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
1827 dmA);
1828 for(i = 0; i < ret; i++)
1829 MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
1830 pOutput + (i * size), size);
1831 HeapFree(GetProcessHeap(), 0, pOutputA);
1832 } else {
1833 ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
1834 (LPSTR)pOutput, dmA);
1836 HeapFree(GetProcessHeap(),0,pPortA);
1837 HeapFree(GetProcessHeap(),0,pDeviceA);
1838 HeapFree(GetProcessHeap(),0,dmA);
1839 return ret;
1842 /******************************************************************
1843 * DocumentPropertiesA [WINSPOOL.@]
1845 * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1847 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
1848 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
1849 LPDEVMODEA pDevModeInput,DWORD fMode )
1851 LPSTR lpName = pDeviceName;
1852 static CHAR port[] = "LPT1:";
1853 LONG ret;
1855 TRACE("(%p,%p,%s,%p,%p,%d)\n",
1856 hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
1859 if(!pDeviceName) {
1860 LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1861 if(!lpNameW) {
1862 ERR("no name from hPrinter?\n");
1863 SetLastError(ERROR_INVALID_HANDLE);
1864 return -1;
1866 lpName = strdupWtoA(lpNameW);
1869 if (!GDI_CallExtDeviceMode16)
1871 GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1872 (LPCSTR)102 );
1873 if (!GDI_CallExtDeviceMode16) {
1874 ERR("No CallExtDeviceMode16?\n");
1875 return -1;
1878 ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, port,
1879 pDevModeInput, NULL, fMode);
1881 if(!pDeviceName)
1882 HeapFree(GetProcessHeap(),0,lpName);
1883 return ret;
1887 /*****************************************************************************
1888 * DocumentPropertiesW (WINSPOOL.@)
1890 * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1892 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1893 LPWSTR pDeviceName,
1894 LPDEVMODEW pDevModeOutput,
1895 LPDEVMODEW pDevModeInput, DWORD fMode)
1898 LPSTR pDeviceNameA = strdupWtoA(pDeviceName);
1899 LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(GetProcessHeap(),pDevModeInput);
1900 LPDEVMODEA pDevModeOutputA = NULL;
1901 LONG ret;
1903 TRACE("(%p,%p,%s,%p,%p,%d)\n",
1904 hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1905 fMode);
1906 if(pDevModeOutput) {
1907 ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1908 if(ret < 0) return ret;
1909 pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1911 ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1912 pDevModeInputA, fMode);
1913 if(pDevModeOutput) {
1914 DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1915 HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1917 if(fMode == 0 && ret > 0)
1918 ret += (CCHDEVICENAME + CCHFORMNAME);
1919 HeapFree(GetProcessHeap(),0,pDevModeInputA);
1920 HeapFree(GetProcessHeap(),0,pDeviceNameA);
1921 return ret;
1924 /******************************************************************
1925 * OpenPrinterA [WINSPOOL.@]
1927 * See OpenPrinterW.
1930 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
1931 LPPRINTER_DEFAULTSA pDefault)
1933 UNICODE_STRING lpPrinterNameW;
1934 UNICODE_STRING usBuffer;
1935 PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
1936 PWSTR pwstrPrinterNameW;
1937 BOOL ret;
1939 pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
1941 if(pDefault) {
1942 DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
1943 DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
1944 DefaultW.DesiredAccess = pDefault->DesiredAccess;
1945 pDefaultW = &DefaultW;
1947 ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
1948 if(pDefault) {
1949 RtlFreeUnicodeString(&usBuffer);
1950 HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
1952 RtlFreeUnicodeString(&lpPrinterNameW);
1953 return ret;
1956 /******************************************************************
1957 * OpenPrinterW [WINSPOOL.@]
1959 * Open a Printer / Printserver or a Printer-Object
1961 * PARAMS
1962 * lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
1963 * phPrinter [O] The resulting Handle is stored here
1964 * pDefault [I] PTR to Default Printer Settings or NULL
1966 * RETURNS
1967 * Success: TRUE
1968 * Failure: FALSE
1970 * NOTES
1971 * lpPrinterName is one of:
1972 *| Printserver (NT only): "Servername" or NULL for the local Printserver
1973 *| Printer: "PrinterName"
1974 *| Printer-Object: "PrinterName,Job xxx"
1975 *| XcvMonitor: "Servername,XcvMonitor MonitorName"
1976 *| XcvPort: "Servername,XcvPort PortName"
1978 * BUGS
1979 *| Printer-Object not supported
1980 *| pDefaults is ignored
1983 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter, LPPRINTER_DEFAULTSW pDefault)
1986 TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), phPrinter, pDefault);
1987 if (pDefault) {
1988 FIXME("PRINTER_DEFAULTS ignored => %s,%p,0x%08x\n",
1989 debugstr_w(pDefault->pDatatype), pDefault->pDevMode, pDefault->DesiredAccess);
1992 if(!phPrinter) {
1993 /* NT: FALSE with ERROR_INVALID_PARAMETER, 9x: TRUE */
1994 SetLastError(ERROR_INVALID_PARAMETER);
1995 return FALSE;
1998 /* Get the unique handle of the printer or Printserver */
1999 *phPrinter = get_opened_printer_entry(lpPrinterName, pDefault);
2000 TRACE("returning %d with %u and %p\n", *phPrinter != NULL, GetLastError(), *phPrinter);
2001 return (*phPrinter != 0);
2004 /******************************************************************
2005 * AddMonitorA [WINSPOOL.@]
2007 * See AddMonitorW.
2010 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
2012 LPWSTR nameW = NULL;
2013 INT len;
2014 BOOL res;
2015 LPMONITOR_INFO_2A mi2a;
2016 MONITOR_INFO_2W mi2w;
2018 mi2a = (LPMONITOR_INFO_2A) pMonitors;
2019 TRACE("(%s, %d, %p) : %s %s %s\n", debugstr_a(pName), Level, pMonitors,
2020 mi2a ? debugstr_a(mi2a->pName) : NULL,
2021 mi2a ? debugstr_a(mi2a->pEnvironment) : NULL,
2022 mi2a ? debugstr_a(mi2a->pDLLName) : NULL);
2024 if (Level != 2) {
2025 SetLastError(ERROR_INVALID_LEVEL);
2026 return FALSE;
2029 /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2030 if (mi2a == NULL) {
2031 return FALSE;
2034 if (pName) {
2035 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2036 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2037 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2040 memset(&mi2w, 0, sizeof(MONITOR_INFO_2W));
2041 if (mi2a->pName) {
2042 len = MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, NULL, 0);
2043 mi2w.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2044 MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, mi2w.pName, len);
2046 if (mi2a->pEnvironment) {
2047 len = MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, NULL, 0);
2048 mi2w.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2049 MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, mi2w.pEnvironment, len);
2051 if (mi2a->pDLLName) {
2052 len = MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, NULL, 0);
2053 mi2w.pDLLName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2054 MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, mi2w.pDLLName, len);
2057 res = AddMonitorW(nameW, Level, (LPBYTE) &mi2w);
2059 HeapFree(GetProcessHeap(), 0, mi2w.pName);
2060 HeapFree(GetProcessHeap(), 0, mi2w.pEnvironment);
2061 HeapFree(GetProcessHeap(), 0, mi2w.pDLLName);
2063 HeapFree(GetProcessHeap(), 0, nameW);
2064 return (res);
2067 /******************************************************************************
2068 * AddMonitorW [WINSPOOL.@]
2070 * Install a Printmonitor
2072 * PARAMS
2073 * pName [I] Servername or NULL (local Computer)
2074 * Level [I] Structure-Level (Must be 2)
2075 * pMonitors [I] PTR to MONITOR_INFO_2
2077 * RETURNS
2078 * Success: TRUE
2079 * Failure: FALSE
2081 * NOTES
2082 * All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
2085 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
2087 monitor_t * pm = NULL;
2088 LPMONITOR_INFO_2W mi2w;
2089 HKEY hroot = NULL;
2090 HKEY hentry = NULL;
2091 DWORD disposition;
2092 BOOL res = FALSE;
2094 mi2w = (LPMONITOR_INFO_2W) pMonitors;
2095 TRACE("(%s, %d, %p) : %s %s %s\n", debugstr_w(pName), Level, pMonitors,
2096 mi2w ? debugstr_w(mi2w->pName) : NULL,
2097 mi2w ? debugstr_w(mi2w->pEnvironment) : NULL,
2098 mi2w ? debugstr_w(mi2w->pDLLName) : NULL);
2100 if (Level != 2) {
2101 SetLastError(ERROR_INVALID_LEVEL);
2102 return FALSE;
2105 /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2106 if (mi2w == NULL) {
2107 return FALSE;
2110 if (pName && (pName[0])) {
2111 FIXME("for server %s not implemented\n", debugstr_w(pName));
2112 SetLastError(ERROR_ACCESS_DENIED);
2113 return FALSE;
2117 if (!mi2w->pName || (! mi2w->pName[0])) {
2118 WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
2119 SetLastError(ERROR_INVALID_PARAMETER);
2120 return FALSE;
2122 if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, envname_x86W)) {
2123 WARN("Environment %s requested (we support only %s)\n",
2124 debugstr_w(mi2w->pEnvironment), debugstr_w(envname_x86W));
2125 SetLastError(ERROR_INVALID_ENVIRONMENT);
2126 return FALSE;
2129 if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
2130 WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
2131 SetLastError(ERROR_INVALID_PARAMETER);
2132 return FALSE;
2135 /* Load and initialize the monitor. SetLastError() is called on failure */
2136 if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
2137 return FALSE;
2139 monitor_unload(pm);
2141 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
2142 ERR("unable to create key %s\n", debugstr_w(MonitorsW));
2143 return FALSE;
2146 if(RegCreateKeyExW( hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
2147 KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
2148 &disposition) == ERROR_SUCCESS) {
2150 /* Some installers set options for the port before calling AddMonitor.
2151 We query the "Driver" entry to verify that the monitor is installed,
2152 before we return an error.
2153 When a user installs two print monitors at the same time with the
2154 same name but with a different driver DLL and a task switch comes
2155 between RegQueryValueExW and RegSetValueExW, a race condition
2156 is possible but silently ignored. */
2158 DWORD namesize = 0;
2160 if ((disposition == REG_OPENED_EXISTING_KEY) &&
2161 (RegQueryValueExW(hentry, DriverW, NULL, NULL, NULL,
2162 &namesize) == ERROR_SUCCESS)) {
2163 TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
2164 /* NT: ERROR_PRINT_MONITOR_ALREADY_INSTALLED (3006)
2165 9x: ERROR_ALREADY_EXISTS (183) */
2166 SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
2168 else
2170 INT len;
2171 len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
2172 res = (RegSetValueExW(hentry, DriverW, 0,
2173 REG_SZ, (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
2175 RegCloseKey(hentry);
2178 RegCloseKey(hroot);
2179 return (res);
2182 /******************************************************************
2183 * DeletePrinterDriverA [WINSPOOL.@]
2186 BOOL WINAPI DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
2188 return DeletePrinterDriverExA(pName, pEnvironment, pDriverName, 0, 0);
2191 /******************************************************************
2192 * DeletePrinterDriverW [WINSPOOL.@]
2195 BOOL WINAPI DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
2197 return DeletePrinterDriverExW(pName, pEnvironment, pDriverName, 0, 0);
2200 /******************************************************************
2201 * DeleteMonitorA [WINSPOOL.@]
2203 * See DeleteMonitorW.
2206 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
2208 LPWSTR nameW = NULL;
2209 LPWSTR EnvironmentW = NULL;
2210 LPWSTR MonitorNameW = NULL;
2211 BOOL res;
2212 INT len;
2214 if (pName) {
2215 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2216 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2217 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2220 if (pEnvironment) {
2221 len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
2222 EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2223 MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
2225 if (pMonitorName) {
2226 len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
2227 MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2228 MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
2231 res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
2233 HeapFree(GetProcessHeap(), 0, MonitorNameW);
2234 HeapFree(GetProcessHeap(), 0, EnvironmentW);
2235 HeapFree(GetProcessHeap(), 0, nameW);
2236 return (res);
2239 /******************************************************************
2240 * DeleteMonitorW [WINSPOOL.@]
2242 * Delete a specific Printmonitor from a Printing-Environment
2244 * PARAMS
2245 * pName [I] Servername or NULL (local Computer)
2246 * pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
2247 * pMonitorName [I] Name of the Monitor, that should be deleted
2249 * RETURNS
2250 * Success: TRUE
2251 * Failure: FALSE
2253 * NOTES
2254 * pEnvironment is ignored in Windows for the local Computer.
2258 BOOL WINAPI DeleteMonitorW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
2260 HKEY hroot = NULL;
2262 TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
2263 debugstr_w(pMonitorName));
2265 if (pName && (pName[0])) {
2266 FIXME("for server %s not implemented\n", debugstr_w(pName));
2267 SetLastError(ERROR_ACCESS_DENIED);
2268 return FALSE;
2271 /* pEnvironment is ignored in Windows for the local Computer */
2273 if (!pMonitorName || !pMonitorName[0]) {
2274 WARN("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
2275 SetLastError(ERROR_INVALID_PARAMETER);
2276 return FALSE;
2279 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
2280 ERR("unable to create key %s\n", debugstr_w(MonitorsW));
2281 return FALSE;
2284 if(RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS) {
2285 TRACE("monitor %s deleted\n", debugstr_w(pMonitorName));
2286 RegCloseKey(hroot);
2287 return TRUE;
2290 WARN("monitor %s does not exist\n", debugstr_w(pMonitorName));
2291 RegCloseKey(hroot);
2293 /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
2294 SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
2295 return (FALSE);
2298 /******************************************************************
2299 * DeletePortA [WINSPOOL.@]
2301 * See DeletePortW.
2304 BOOL WINAPI DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
2306 LPWSTR nameW = NULL;
2307 LPWSTR portW = NULL;
2308 INT len;
2309 DWORD res;
2311 TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
2313 /* convert servername to unicode */
2314 if (pName) {
2315 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2316 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2317 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2320 /* convert portname to unicode */
2321 if (pPortName) {
2322 len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
2323 portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2324 MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
2327 res = DeletePortW(nameW, hWnd, portW);
2328 HeapFree(GetProcessHeap(), 0, nameW);
2329 HeapFree(GetProcessHeap(), 0, portW);
2330 return res;
2333 /******************************************************************
2334 * DeletePortW [WINSPOOL.@]
2336 * Delete a specific Port
2338 * PARAMS
2339 * pName [I] Servername or NULL (local Computer)
2340 * hWnd [I] Handle to parent Window for the Dialog-Box
2341 * pPortName [I] Name of the Port, that should be deleted
2343 * RETURNS
2344 * Success: TRUE
2345 * Failure: FALSE
2348 BOOL WINAPI DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
2350 monitor_t * pm;
2351 monitor_t * pui;
2352 DWORD res;
2354 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
2356 if (pName && pName[0]) {
2357 SetLastError(ERROR_INVALID_PARAMETER);
2358 return FALSE;
2361 if (!pPortName) {
2362 SetLastError(RPC_X_NULL_REF_POINTER);
2363 return FALSE;
2366 /* an empty Portname is Invalid */
2367 if (!pPortName[0]) {
2368 SetLastError(ERROR_NOT_SUPPORTED);
2369 return FALSE;
2372 pm = monitor_load_by_port(pPortName);
2373 if (pm && pm->monitor && pm->monitor->pfnDeletePort) {
2374 TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
2375 res = pm->monitor->pfnDeletePort(pName, hWnd, pPortName);
2376 TRACE("got %d with %u\n", res, GetLastError());
2378 else
2380 pui = monitor_loadui(pm);
2381 if (pui && pui->monitorUI && pui->monitorUI->pfnDeletePortUI) {
2382 TRACE("use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
2383 res = pui->monitorUI->pfnDeletePortUI(pName, hWnd, pPortName);
2384 TRACE("got %d with %u\n", res, GetLastError());
2386 else
2388 FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
2389 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
2391 /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
2392 SetLastError(ERROR_NOT_SUPPORTED);
2393 res = FALSE;
2395 monitor_unload(pui);
2397 monitor_unload(pm);
2399 TRACE("returning %d with %u\n", res, GetLastError());
2400 return res;
2403 /******************************************************************************
2404 * SetPrinterW [WINSPOOL.@]
2406 BOOL WINAPI SetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command)
2408 FIXME("(%p, %d, %p, %d): stub\n", hPrinter, Level, pPrinter, Command);
2409 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2410 return FALSE;
2413 /******************************************************************************
2414 * WritePrinter [WINSPOOL.@]
2416 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
2418 opened_printer_t *printer;
2419 BOOL ret = FALSE;
2421 TRACE("(%p, %p, %d, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
2423 EnterCriticalSection(&printer_handles_cs);
2424 printer = get_opened_printer(hPrinter);
2425 if(!printer)
2427 SetLastError(ERROR_INVALID_HANDLE);
2428 goto end;
2431 if(!printer->doc)
2433 SetLastError(ERROR_SPL_NO_STARTDOC);
2434 goto end;
2437 ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
2438 end:
2439 LeaveCriticalSection(&printer_handles_cs);
2440 return ret;
2443 /*****************************************************************************
2444 * AddFormA [WINSPOOL.@]
2446 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2448 FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2449 return 1;
2452 /*****************************************************************************
2453 * AddFormW [WINSPOOL.@]
2455 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2457 FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2458 return 1;
2461 /*****************************************************************************
2462 * AddJobA [WINSPOOL.@]
2464 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2466 BOOL ret;
2467 BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2468 DWORD needed;
2470 if(Level != 1) {
2471 SetLastError(ERROR_INVALID_LEVEL);
2472 return FALSE;
2475 ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
2477 if(ret) {
2478 ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
2479 DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
2480 *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
2481 if(*pcbNeeded > cbBuf) {
2482 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2483 ret = FALSE;
2484 } else {
2485 ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
2486 addjobA->JobId = addjobW->JobId;
2487 addjobA->Path = (char *)(addjobA + 1);
2488 WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
2491 return ret;
2494 /*****************************************************************************
2495 * AddJobW [WINSPOOL.@]
2497 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2499 opened_printer_t *printer;
2500 job_t *job;
2501 BOOL ret = FALSE;
2502 static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
2503 static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
2504 WCHAR path[MAX_PATH], filename[MAX_PATH];
2505 DWORD len;
2506 ADDJOB_INFO_1W *addjob;
2508 TRACE("(%p,%d,%p,%d,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
2510 EnterCriticalSection(&printer_handles_cs);
2512 printer = get_opened_printer(hPrinter);
2514 if(!printer) {
2515 SetLastError(ERROR_INVALID_HANDLE);
2516 goto end;
2519 if(Level != 1) {
2520 SetLastError(ERROR_INVALID_LEVEL);
2521 goto end;
2524 job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
2525 if(!job)
2526 goto end;
2528 job->job_id = InterlockedIncrement(&next_job_id);
2530 len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
2531 if(path[len - 1] != '\\')
2532 path[len++] = '\\';
2533 memcpy(path + len, spool_path, sizeof(spool_path));
2534 sprintfW(filename, fmtW, path, job->job_id);
2536 len = strlenW(filename);
2537 job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2538 memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
2539 job->document_title = strdupW(default_doc_title);
2540 list_add_tail(&printer->queue->jobs, &job->entry);
2542 *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
2543 if(*pcbNeeded <= cbBuf) {
2544 addjob = (ADDJOB_INFO_1W*)pData;
2545 addjob->JobId = job->job_id;
2546 addjob->Path = (WCHAR *)(addjob + 1);
2547 memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
2548 ret = TRUE;
2549 } else
2550 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2552 end:
2553 LeaveCriticalSection(&printer_handles_cs);
2554 return ret;
2557 /*****************************************************************************
2558 * GetPrintProcessorDirectoryA [WINSPOOL.@]
2560 * Return the PATH for the Print-Processors
2562 * See GetPrintProcessorDirectoryW.
2566 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
2567 DWORD level, LPBYTE Info,
2568 DWORD cbBuf, LPDWORD pcbNeeded)
2570 LPWSTR serverW = NULL;
2571 LPWSTR envW = NULL;
2572 BOOL ret;
2573 INT len;
2575 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(server),
2576 debugstr_a(env), level, Info, cbBuf, pcbNeeded);
2579 if (server) {
2580 len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
2581 serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2582 MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
2585 if (env) {
2586 len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
2587 envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2588 MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
2591 /* NT requires the buffersize from GetPrintProcessorDirectoryW also
2592 for GetPrintProcessorDirectoryA and WC2MB is done in-place.
2594 ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info,
2595 cbBuf, pcbNeeded);
2597 if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
2598 cbBuf, NULL, NULL) > 0;
2601 TRACE(" required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
2602 HeapFree(GetProcessHeap(), 0, envW);
2603 HeapFree(GetProcessHeap(), 0, serverW);
2604 return ret;
2607 /*****************************************************************************
2608 * GetPrintProcessorDirectoryW [WINSPOOL.@]
2610 * Return the PATH for the Print-Processors
2612 * PARAMS
2613 * server [I] Servername (NT only) or NULL (local Computer)
2614 * env [I] Printing-Environment (see below) or NULL (Default)
2615 * level [I] Structure-Level (must be 1)
2616 * Info [O] PTR to Buffer that receives the Result
2617 * cbBuf [I] Size of Buffer at "Info"
2618 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
2619 * required for the Buffer at "Info"
2621 * RETURNS
2622 * Success: TRUE and in pcbNeeded the Bytes used in Info
2623 * Failure: FALSE and in pcbNeeded the Bytes required for Info,
2624 * if cbBuf is too small
2626 * Native Values returned in Info on Success:
2627 *| NT(Windows NT x86): "%winsysdir%\\spool\\PRTPROCS\\w32x86"
2628 *| NT(Windows 4.0): "%winsysdir%\\spool\\PRTPROCS\\win40"
2629 *| win9x(Windows 4.0): "%winsysdir%"
2631 * "%winsysdir%" is the Value from GetSystemDirectoryW()
2633 * BUGS
2634 * Only NULL or "" is supported for server
2637 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
2638 DWORD level, LPBYTE Info,
2639 DWORD cbBuf, LPDWORD pcbNeeded)
2641 DWORD needed;
2642 const printenv_t * env_t;
2644 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(server),
2645 debugstr_w(env), level, Info, cbBuf, pcbNeeded);
2647 if(server != NULL && server[0]) {
2648 FIXME("server not supported: %s\n", debugstr_w(server));
2649 SetLastError(ERROR_INVALID_PARAMETER);
2650 return FALSE;
2653 env_t = validate_envW(env);
2654 if(!env_t) return FALSE; /* environment invalid or unsupported */
2656 if(level != 1) {
2657 WARN("(Level: %d) is ignored in win9x\n", level);
2658 SetLastError(ERROR_INVALID_LEVEL);
2659 return FALSE;
2662 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2663 needed = GetSystemDirectoryW(NULL, 0);
2664 /* add the Size for the Subdirectories */
2665 needed += lstrlenW(spoolprtprocsW);
2666 needed += lstrlenW(env_t->subdir);
2667 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
2669 if(pcbNeeded) *pcbNeeded = needed;
2670 TRACE ("required: 0x%x/%d\n", needed, needed);
2671 if (needed > cbBuf) {
2672 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2673 return FALSE;
2675 if(pcbNeeded == NULL) {
2676 /* NT: RPC_X_NULL_REF_POINTER, 9x: ignored */
2677 WARN("(pcbNeeded == NULL) is ignored in win9x\n");
2678 SetLastError(RPC_X_NULL_REF_POINTER);
2679 return FALSE;
2681 if(Info == NULL) {
2682 /* NT: RPC_X_NULL_REF_POINTER, 9x: ERROR_INVALID_PARAMETER */
2683 SetLastError(RPC_X_NULL_REF_POINTER);
2684 return FALSE;
2687 GetSystemDirectoryW((LPWSTR) Info, cbBuf/sizeof(WCHAR));
2688 /* add the Subdirectories */
2689 lstrcatW((LPWSTR) Info, spoolprtprocsW);
2690 lstrcatW((LPWSTR) Info, env_t->subdir);
2691 TRACE(" => %s\n", debugstr_w((LPWSTR) Info));
2692 return TRUE;
2695 /*****************************************************************************
2696 * WINSPOOL_OpenDriverReg [internal]
2698 * opens the registry for the printer drivers depending on the given input
2699 * variable pEnvironment
2701 * RETURNS:
2702 * the opened hkey on success
2703 * NULL on error
2705 static HKEY WINSPOOL_OpenDriverReg( LPCVOID pEnvironment, BOOL unicode)
2707 HKEY retval = NULL;
2708 LPWSTR buffer;
2709 const printenv_t * env;
2711 TRACE("(%s, %d)\n",
2712 (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment), unicode);
2714 if (!pEnvironment || unicode) {
2715 /* pEnvironment was NULL or an Unicode-String: use it direct */
2716 env = validate_envW(pEnvironment);
2718 else
2720 /* pEnvironment was an ANSI-String: convert to unicode first */
2721 LPWSTR buffer;
2722 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
2723 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2724 if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
2725 env = validate_envW(buffer);
2726 HeapFree(GetProcessHeap(), 0, buffer);
2728 if (!env) return NULL;
2730 buffer = HeapAlloc( GetProcessHeap(), 0,
2731 (strlenW(DriversW) + strlenW(env->envname) +
2732 strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2733 if(buffer) {
2734 wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2735 RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2736 HeapFree(GetProcessHeap(), 0, buffer);
2738 return retval;
2741 /*****************************************************************************
2742 * AddPrinterW [WINSPOOL.@]
2744 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2746 PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2747 LPDEVMODEA dmA;
2748 LPDEVMODEW dmW;
2749 HANDLE retval;
2750 HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2751 LONG size;
2752 static const WCHAR attributesW[] = {'A','t','t','r','i','b','u','t','e','s',0},
2753 default_devmodeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0},
2754 priorityW[] = {'P','r','i','o','r','i','t','y',0},
2755 start_timeW[] = {'S','t','a','r','t','T','i','m','e',0},
2756 statusW[] = {'S','t','a','t','u','s',0},
2757 until_timeW[] = {'U','n','t','i','l','T','i','m','e',0};
2759 TRACE("(%s,%d,%p)\n", debugstr_w(pName), Level, pPrinter);
2761 if(pName != NULL) {
2762 ERR("pName = %s - unsupported\n", debugstr_w(pName));
2763 SetLastError(ERROR_INVALID_PARAMETER);
2764 return 0;
2766 if(Level != 2) {
2767 ERR("Level = %d, unsupported!\n", Level);
2768 SetLastError(ERROR_INVALID_LEVEL);
2769 return 0;
2771 if(!pPrinter) {
2772 SetLastError(ERROR_INVALID_PARAMETER);
2773 return 0;
2775 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2776 ERROR_SUCCESS) {
2777 ERR("Can't create Printers key\n");
2778 return 0;
2780 if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2781 if (!RegQueryValueW(hkeyPrinter, attributesW, NULL, NULL)) {
2782 SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2783 RegCloseKey(hkeyPrinter);
2784 RegCloseKey(hkeyPrinters);
2785 return 0;
2787 RegCloseKey(hkeyPrinter);
2789 hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
2790 if(!hkeyDrivers) {
2791 ERR("Can't create Drivers key\n");
2792 RegCloseKey(hkeyPrinters);
2793 return 0;
2795 if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2796 ERROR_SUCCESS) {
2797 WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2798 RegCloseKey(hkeyPrinters);
2799 RegCloseKey(hkeyDrivers);
2800 SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2801 return 0;
2803 RegCloseKey(hkeyDriver);
2804 RegCloseKey(hkeyDrivers);
2806 if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) { /* FIXME */
2807 FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2808 SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2809 RegCloseKey(hkeyPrinters);
2810 return 0;
2813 if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2814 ERROR_SUCCESS) {
2815 FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2816 SetLastError(ERROR_INVALID_PRINTER_NAME);
2817 RegCloseKey(hkeyPrinters);
2818 return 0;
2820 RegSetValueExW(hkeyPrinter, attributesW, 0, REG_DWORD,
2821 (LPBYTE)&pi->Attributes, sizeof(DWORD));
2822 set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2824 /* See if we can load the driver. We may need the devmode structure anyway
2826 * FIXME:
2827 * Note that DocumentPropertiesW will briefly try to open the printer we
2828 * just create to find a DEVMODEA struct (it will use the WINEPS default
2829 * one in case it is not there, so we are ok).
2831 size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2833 if(size < 0) {
2834 FIXME("DocumentPropertiesW on printer %s fails\n", debugstr_w(pi->pPrinterName));
2835 size = sizeof(DEVMODEW);
2837 if(pi->pDevMode)
2838 dmW = pi->pDevMode;
2839 else
2841 dmW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
2842 dmW->dmSize = size;
2843 if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER))
2845 WARN("DocumentPropertiesW on printer %s failed!\n", debugstr_w(pi->pPrinterName));
2846 HeapFree(GetProcessHeap(),0,dmW);
2847 dmW=NULL;
2849 else
2851 /* set devmode to printer name */
2852 lstrcpynW(dmW->dmDeviceName, pi->pPrinterName, CCHDEVICENAME);
2856 /* Write DEVMODEA not DEVMODEW into reg. This is what win9x does
2857 and we support these drivers. NT writes DEVMODEW so somehow
2858 we'll need to distinguish between these when we support NT
2859 drivers */
2860 if (dmW)
2862 dmA = DEVMODEdupWtoA(GetProcessHeap(), dmW);
2863 RegSetValueExW(hkeyPrinter, default_devmodeW, 0, REG_BINARY,
2864 (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
2865 HeapFree(GetProcessHeap(), 0, dmA);
2866 if(!pi->pDevMode)
2867 HeapFree(GetProcessHeap(), 0, dmW);
2869 set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
2870 set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
2871 set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
2872 set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
2874 set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
2875 set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
2876 set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
2877 RegSetValueExW(hkeyPrinter, priorityW, 0, REG_DWORD,
2878 (LPBYTE)&pi->Priority, sizeof(DWORD));
2879 set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
2880 set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
2881 RegSetValueExW(hkeyPrinter, start_timeW, 0, REG_DWORD,
2882 (LPBYTE)&pi->StartTime, sizeof(DWORD));
2883 RegSetValueExW(hkeyPrinter, statusW, 0, REG_DWORD,
2884 (LPBYTE)&pi->Status, sizeof(DWORD));
2885 RegSetValueExW(hkeyPrinter, until_timeW, 0, REG_DWORD,
2886 (LPBYTE)&pi->UntilTime, sizeof(DWORD));
2888 RegCloseKey(hkeyPrinter);
2889 RegCloseKey(hkeyPrinters);
2890 if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
2891 ERR("OpenPrinter failing\n");
2892 return 0;
2894 return retval;
2897 /*****************************************************************************
2898 * AddPrinterA [WINSPOOL.@]
2900 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
2902 UNICODE_STRING pNameW;
2903 PWSTR pwstrNameW;
2904 PRINTER_INFO_2W *piW;
2905 PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
2906 HANDLE ret;
2908 TRACE("(%s,%d,%p): stub\n", debugstr_a(pName), Level, pPrinter);
2909 if(Level != 2) {
2910 ERR("Level = %d, unsupported!\n", Level);
2911 SetLastError(ERROR_INVALID_LEVEL);
2912 return 0;
2914 pwstrNameW = asciitounicode(&pNameW,pName);
2915 piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
2917 ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
2919 FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
2920 RtlFreeUnicodeString(&pNameW);
2921 return ret;
2925 /*****************************************************************************
2926 * ClosePrinter [WINSPOOL.@]
2928 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
2930 UINT_PTR i = (UINT_PTR)hPrinter;
2931 opened_printer_t *printer = NULL;
2932 BOOL ret = FALSE;
2934 TRACE("(%p)\n", hPrinter);
2936 EnterCriticalSection(&printer_handles_cs);
2938 if ((i > 0) && (i <= nb_printer_handles))
2939 printer = printer_handles[i - 1];
2942 if(printer)
2944 struct list *cursor, *cursor2;
2946 TRACE("%p: %s (hXcv: %p) for %s (doc: %p)\n", printer->pm,
2947 debugstr_w(printer->pm ? printer->pm->dllname : NULL),
2948 printer->hXcv, debugstr_w(printer->name), printer->doc );
2950 if(printer->doc)
2951 EndDocPrinter(hPrinter);
2953 if(InterlockedDecrement(&printer->queue->ref) == 0)
2955 LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
2957 job_t *job = LIST_ENTRY(cursor, job_t, entry);
2958 ScheduleJob(hPrinter, job->job_id);
2960 HeapFree(GetProcessHeap(), 0, printer->queue);
2962 if (printer->hXcv) printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
2963 monitor_unload(printer->pm);
2964 HeapFree(GetProcessHeap(), 0, printer->printername);
2965 HeapFree(GetProcessHeap(), 0, printer->name);
2966 HeapFree(GetProcessHeap(), 0, printer);
2967 printer_handles[i - 1] = NULL;
2968 ret = TRUE;
2970 LeaveCriticalSection(&printer_handles_cs);
2971 return ret;
2974 /*****************************************************************************
2975 * DeleteFormA [WINSPOOL.@]
2977 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
2979 FIXME("(%p,%s): stub\n", hPrinter, pFormName);
2980 return 1;
2983 /*****************************************************************************
2984 * DeleteFormW [WINSPOOL.@]
2986 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
2988 FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
2989 return 1;
2992 /*****************************************************************************
2993 * DeletePrinter [WINSPOOL.@]
2995 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
2997 LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
2998 HKEY hkeyPrinters, hkey;
3000 if(!lpNameW) {
3001 SetLastError(ERROR_INVALID_HANDLE);
3002 return FALSE;
3004 if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
3005 RegDeleteTreeW(hkeyPrinters, lpNameW);
3006 RegCloseKey(hkeyPrinters);
3008 WriteProfileStringW(devicesW, lpNameW, NULL);
3009 if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
3010 RegDeleteValueW(hkey, lpNameW);
3011 RegCloseKey(hkey);
3013 return TRUE;
3016 /*****************************************************************************
3017 * SetPrinterA [WINSPOOL.@]
3019 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3020 DWORD Command)
3022 FIXME("(%p,%d,%p,%d): stub\n",hPrinter,Level,pPrinter,Command);
3023 return FALSE;
3026 /*****************************************************************************
3027 * SetJobA [WINSPOOL.@]
3029 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
3030 LPBYTE pJob, DWORD Command)
3032 BOOL ret;
3033 LPBYTE JobW;
3034 UNICODE_STRING usBuffer;
3036 TRACE("(%p, %d, %d, %p, %d)\n",hPrinter, JobId, Level, pJob, Command);
3038 /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
3039 are all ignored by SetJob, so we don't bother copying them */
3040 switch(Level)
3042 case 0:
3043 JobW = NULL;
3044 break;
3045 case 1:
3047 JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
3048 JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
3050 JobW = (LPBYTE)info1W;
3051 info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
3052 info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
3053 info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
3054 info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
3055 info1W->Status = info1A->Status;
3056 info1W->Priority = info1A->Priority;
3057 info1W->Position = info1A->Position;
3058 info1W->PagesPrinted = info1A->PagesPrinted;
3059 break;
3061 case 2:
3063 JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
3064 JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
3066 JobW = (LPBYTE)info2W;
3067 info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
3068 info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
3069 info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
3070 info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
3071 info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
3072 info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
3073 info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
3074 info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
3075 info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
3076 info2W->Status = info2A->Status;
3077 info2W->Priority = info2A->Priority;
3078 info2W->Position = info2A->Position;
3079 info2W->StartTime = info2A->StartTime;
3080 info2W->UntilTime = info2A->UntilTime;
3081 info2W->PagesPrinted = info2A->PagesPrinted;
3082 break;
3084 case 3:
3085 JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
3086 memcpy(JobW, pJob, sizeof(JOB_INFO_3));
3087 break;
3088 default:
3089 SetLastError(ERROR_INVALID_LEVEL);
3090 return FALSE;
3093 ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
3095 switch(Level)
3097 case 1:
3099 JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
3100 HeapFree(GetProcessHeap(), 0, info1W->pUserName);
3101 HeapFree(GetProcessHeap(), 0, info1W->pDocument);
3102 HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
3103 HeapFree(GetProcessHeap(), 0, info1W->pStatus);
3104 break;
3106 case 2:
3108 JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
3109 HeapFree(GetProcessHeap(), 0, info2W->pUserName);
3110 HeapFree(GetProcessHeap(), 0, info2W->pDocument);
3111 HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
3112 HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
3113 HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
3114 HeapFree(GetProcessHeap(), 0, info2W->pParameters);
3115 HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
3116 HeapFree(GetProcessHeap(), 0, info2W->pStatus);
3117 break;
3120 HeapFree(GetProcessHeap(), 0, JobW);
3122 return ret;
3125 /*****************************************************************************
3126 * SetJobW [WINSPOOL.@]
3128 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
3129 LPBYTE pJob, DWORD Command)
3131 BOOL ret = FALSE;
3132 job_t *job;
3134 TRACE("(%p, %d, %d, %p, %d)\n", hPrinter, JobId, Level, pJob, Command);
3135 FIXME("Ignoring everything other than document title\n");
3137 EnterCriticalSection(&printer_handles_cs);
3138 job = get_job(hPrinter, JobId);
3139 if(!job)
3140 goto end;
3142 switch(Level)
3144 case 0:
3145 break;
3146 case 1:
3148 JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
3149 HeapFree(GetProcessHeap(), 0, job->document_title);
3150 job->document_title = strdupW(info1->pDocument);
3151 break;
3153 case 2:
3155 JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
3156 HeapFree(GetProcessHeap(), 0, job->document_title);
3157 job->document_title = strdupW(info2->pDocument);
3158 break;
3160 case 3:
3161 break;
3162 default:
3163 SetLastError(ERROR_INVALID_LEVEL);
3164 goto end;
3166 ret = TRUE;
3167 end:
3168 LeaveCriticalSection(&printer_handles_cs);
3169 return ret;
3172 /*****************************************************************************
3173 * EndDocPrinter [WINSPOOL.@]
3175 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
3177 opened_printer_t *printer;
3178 BOOL ret = FALSE;
3179 TRACE("(%p)\n", hPrinter);
3181 EnterCriticalSection(&printer_handles_cs);
3183 printer = get_opened_printer(hPrinter);
3184 if(!printer)
3186 SetLastError(ERROR_INVALID_HANDLE);
3187 goto end;
3190 if(!printer->doc)
3192 SetLastError(ERROR_SPL_NO_STARTDOC);
3193 goto end;
3196 CloseHandle(printer->doc->hf);
3197 ScheduleJob(hPrinter, printer->doc->job_id);
3198 HeapFree(GetProcessHeap(), 0, printer->doc);
3199 printer->doc = NULL;
3200 ret = TRUE;
3201 end:
3202 LeaveCriticalSection(&printer_handles_cs);
3203 return ret;
3206 /*****************************************************************************
3207 * EndPagePrinter [WINSPOOL.@]
3209 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
3211 FIXME("(%p): stub\n", hPrinter);
3212 return TRUE;
3215 /*****************************************************************************
3216 * StartDocPrinterA [WINSPOOL.@]
3218 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3220 UNICODE_STRING usBuffer;
3221 DOC_INFO_2W doc2W;
3222 DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
3223 DWORD ret;
3225 /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
3226 or one (DOC_INFO_3) extra DWORDs */
3228 switch(Level) {
3229 case 2:
3230 doc2W.JobId = doc2->JobId;
3231 /* fall through */
3232 case 3:
3233 doc2W.dwMode = doc2->dwMode;
3234 /* fall through */
3235 case 1:
3236 doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
3237 doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
3238 doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
3239 break;
3241 default:
3242 SetLastError(ERROR_INVALID_LEVEL);
3243 return FALSE;
3246 ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
3248 HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
3249 HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
3250 HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
3252 return ret;
3255 /*****************************************************************************
3256 * StartDocPrinterW [WINSPOOL.@]
3258 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3260 DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
3261 opened_printer_t *printer;
3262 BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
3263 ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
3264 JOB_INFO_1W job_info;
3265 DWORD needed, ret = 0;
3266 HANDLE hf;
3267 WCHAR *filename;
3269 TRACE("(hPrinter = %p, Level = %d, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
3270 hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
3271 debugstr_w(doc->pDatatype));
3273 if(Level < 1 || Level > 3)
3275 SetLastError(ERROR_INVALID_LEVEL);
3276 return 0;
3279 EnterCriticalSection(&printer_handles_cs);
3280 printer = get_opened_printer(hPrinter);
3281 if(!printer)
3283 SetLastError(ERROR_INVALID_HANDLE);
3284 goto end;
3287 if(printer->doc)
3289 SetLastError(ERROR_INVALID_PRINTER_STATE);
3290 goto end;
3293 /* Even if we're printing to a file we still add a print job, we'll
3294 just ignore the spool file name */
3296 if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
3298 ERR("AddJob failed gle %u\n", GetLastError());
3299 goto end;
3302 if(doc->pOutputFile)
3303 filename = doc->pOutputFile;
3304 else
3305 filename = addjob->Path;
3307 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3308 if(hf == INVALID_HANDLE_VALUE)
3309 goto end;
3311 memset(&job_info, 0, sizeof(job_info));
3312 job_info.pDocument = doc->pDocName;
3313 SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
3315 printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
3316 printer->doc->hf = hf;
3317 ret = printer->doc->job_id = addjob->JobId;
3318 end:
3319 LeaveCriticalSection(&printer_handles_cs);
3321 return ret;
3324 /*****************************************************************************
3325 * StartPagePrinter [WINSPOOL.@]
3327 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
3329 FIXME("(%p): stub\n", hPrinter);
3330 return TRUE;
3333 /*****************************************************************************
3334 * GetFormA [WINSPOOL.@]
3336 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3337 LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3339 FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,pFormName,
3340 Level,pForm,cbBuf,pcbNeeded);
3341 return FALSE;
3344 /*****************************************************************************
3345 * GetFormW [WINSPOOL.@]
3347 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3348 LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3350 FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,
3351 debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
3352 return FALSE;
3355 /*****************************************************************************
3356 * SetFormA [WINSPOOL.@]
3358 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3359 LPBYTE pForm)
3361 FIXME("(%p,%s,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3362 return FALSE;
3365 /*****************************************************************************
3366 * SetFormW [WINSPOOL.@]
3368 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3369 LPBYTE pForm)
3371 FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3372 return FALSE;
3375 /*****************************************************************************
3376 * ReadPrinter [WINSPOOL.@]
3378 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
3379 LPDWORD pNoBytesRead)
3381 FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
3382 return FALSE;
3385 /*****************************************************************************
3386 * ResetPrinterA [WINSPOOL.@]
3388 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
3390 FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3391 return FALSE;
3394 /*****************************************************************************
3395 * ResetPrinterW [WINSPOOL.@]
3397 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
3399 FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3400 return FALSE;
3403 /*****************************************************************************
3404 * WINSPOOL_GetDWORDFromReg
3406 * Return DWORD associated with ValueName from hkey.
3408 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
3410 DWORD sz = sizeof(DWORD), type, value = 0;
3411 LONG ret;
3413 ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
3415 if(ret != ERROR_SUCCESS) {
3416 WARN("Got ret = %d on name %s\n", ret, ValueName);
3417 return 0;
3419 if(type != REG_DWORD) {
3420 ERR("Got type %d\n", type);
3421 return 0;
3423 return value;
3427 /*****************************************************************************
3428 * get_filename_from_reg [internal]
3430 * Get ValueName from hkey storing result in out
3431 * when the Value in the registry has only a filename, use driverdir as prefix
3432 * outlen is space left in out
3433 * String is stored either as unicode or ascii
3437 static BOOL get_filename_from_reg(HKEY hkey, LPCWSTR driverdir, DWORD dirlen, LPCWSTR ValueName,
3438 LPBYTE out, DWORD outlen, LPDWORD needed, BOOL unicode)
3440 WCHAR filename[MAX_PATH];
3441 DWORD size;
3442 DWORD type;
3443 LONG ret;
3444 LPWSTR buffer = filename;
3445 LPWSTR ptr;
3447 *needed = 0;
3448 size = sizeof(filename);
3449 buffer[0] = '\0';
3450 ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3451 if (ret == ERROR_MORE_DATA) {
3452 TRACE("need dynamic buffer: %u\n", size);
3453 buffer = HeapAlloc(GetProcessHeap(), 0, size);
3454 if (!buffer) {
3455 /* No Memory is bad */
3456 return FALSE;
3458 buffer[0] = '\0';
3459 ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3462 if ((ret != ERROR_SUCCESS) || (!buffer[0])) {
3463 if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3464 return FALSE;
3467 ptr = buffer;
3468 while (ptr) {
3469 /* do we have a full path ? */
3470 ret = (((buffer[0] == '\\') && (buffer[1] == '\\')) ||
3471 (buffer[0] && (buffer[1] == ':') && (buffer[2] == '\\')) );
3473 if (!ret) {
3474 /* we must build the full Path */
3475 *needed += dirlen;
3476 if ((out) && (outlen > dirlen)) {
3477 if (unicode) {
3478 lstrcpyW((LPWSTR)out, driverdir);
3480 else
3482 WideCharToMultiByte(CP_ACP, 0, driverdir, -1, (LPSTR)out, outlen, NULL, NULL);
3484 out += dirlen;
3485 outlen -= dirlen;
3487 else
3488 out = NULL;
3491 /* write the filename */
3492 if (unicode) {
3493 size = (lstrlenW(ptr) + 1) * sizeof(WCHAR);
3494 if ((out) && (outlen >= size)) {
3495 lstrcpyW((LPWSTR)out, ptr);
3496 out += size;
3497 outlen -= size;
3499 else
3500 out = NULL;
3502 else
3504 size = WideCharToMultiByte(CP_ACP, 0, ptr, -1, NULL, 0, NULL, NULL);
3505 if ((out) && (outlen >= size)) {
3506 WideCharToMultiByte(CP_ACP, 0, ptr, -1, (LPSTR)out, outlen, NULL, NULL);
3507 out += size;
3508 outlen -= size;
3510 else
3511 out = NULL;
3513 *needed += size;
3514 ptr += lstrlenW(ptr)+1;
3515 if ((type != REG_MULTI_SZ) || (!ptr[0])) ptr = NULL;
3518 if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3520 /* write the multisz-termination */
3521 if (type == REG_MULTI_SZ) {
3522 size = (unicode) ? sizeof(WCHAR) : 1;
3524 *needed += size;
3525 if (out && (outlen >= size)) {
3526 memset (out, 0, size);
3529 return TRUE;
3532 /*****************************************************************************
3533 * WINSPOOL_GetStringFromReg
3535 * Get ValueName from hkey storing result in ptr. buflen is space left in ptr
3536 * String is stored either as unicode or ascii.
3537 * Bit of a hack here to get the ValueName if we want ascii.
3539 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
3540 DWORD buflen, DWORD *needed,
3541 BOOL unicode)
3543 DWORD sz = buflen, type;
3544 LONG ret;
3546 if(unicode)
3547 ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3548 else {
3549 LPSTR ValueNameA = strdupWtoA(ValueName);
3550 ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
3551 HeapFree(GetProcessHeap(),0,ValueNameA);
3553 if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
3554 WARN("Got ret = %d\n", ret);
3555 *needed = 0;
3556 return FALSE;
3558 /* add space for terminating '\0' */
3559 sz += unicode ? sizeof(WCHAR) : 1;
3560 *needed = sz;
3562 if (ptr)
3563 TRACE("%s: %s\n", debugstr_w(ValueName), unicode ? debugstr_w((LPCWSTR)ptr) : debugstr_a((LPCSTR)ptr));
3565 return TRUE;
3568 /*****************************************************************************
3569 * WINSPOOL_GetDefaultDevMode
3571 * Get a default DevMode values for wineps.
3572 * FIXME - use ppd.
3575 static void WINSPOOL_GetDefaultDevMode(
3576 LPBYTE ptr,
3577 DWORD buflen, DWORD *needed,
3578 BOOL unicode)
3580 DEVMODEA dm;
3581 static const char szwps[] = "wineps.drv";
3583 /* fill default DEVMODE - should be read from ppd... */
3584 ZeroMemory( &dm, sizeof(dm) );
3585 memcpy(dm.dmDeviceName,szwps,sizeof szwps);
3586 dm.dmSpecVersion = DM_SPECVERSION;
3587 dm.dmDriverVersion = 1;
3588 dm.dmSize = sizeof(DEVMODEA);
3589 dm.dmDriverExtra = 0;
3590 dm.dmFields =
3591 DM_ORIENTATION | DM_PAPERSIZE |
3592 DM_PAPERLENGTH | DM_PAPERWIDTH |
3593 DM_SCALE |
3594 DM_COPIES |
3595 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
3596 DM_YRESOLUTION | DM_TTOPTION;
3598 dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
3599 dm.u1.s1.dmPaperSize = DMPAPER_A4;
3600 dm.u1.s1.dmPaperLength = 2970;
3601 dm.u1.s1.dmPaperWidth = 2100;
3603 dm.dmScale = 100;
3604 dm.dmCopies = 1;
3605 dm.dmDefaultSource = DMBIN_AUTO;
3606 dm.dmPrintQuality = DMRES_MEDIUM;
3607 /* dm.dmColor */
3608 /* dm.dmDuplex */
3609 dm.dmYResolution = 300; /* 300dpi */
3610 dm.dmTTOption = DMTT_BITMAP;
3611 /* dm.dmCollate */
3612 /* dm.dmFormName */
3613 /* dm.dmLogPixels */
3614 /* dm.dmBitsPerPel */
3615 /* dm.dmPelsWidth */
3616 /* dm.dmPelsHeight */
3617 /* dm.dmDisplayFlags */
3618 /* dm.dmDisplayFrequency */
3619 /* dm.dmICMMethod */
3620 /* dm.dmICMIntent */
3621 /* dm.dmMediaType */
3622 /* dm.dmDitherType */
3623 /* dm.dmReserved1 */
3624 /* dm.dmReserved2 */
3625 /* dm.dmPanningWidth */
3626 /* dm.dmPanningHeight */
3628 if(unicode) {
3629 if(buflen >= sizeof(DEVMODEW)) {
3630 DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
3631 memcpy(ptr, pdmW, sizeof(DEVMODEW));
3632 HeapFree(GetProcessHeap(),0,pdmW);
3634 *needed = sizeof(DEVMODEW);
3636 else
3638 if(buflen >= sizeof(DEVMODEA)) {
3639 memcpy(ptr, &dm, sizeof(DEVMODEA));
3641 *needed = sizeof(DEVMODEA);
3645 /*****************************************************************************
3646 * WINSPOOL_GetDevModeFromReg
3648 * Get ValueName from hkey storing result in ptr. buflen is space left in ptr
3649 * DevMode is stored either as unicode or ascii.
3651 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
3652 LPBYTE ptr,
3653 DWORD buflen, DWORD *needed,
3654 BOOL unicode)
3656 DWORD sz = buflen, type;
3657 LONG ret;
3659 if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
3660 ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3661 if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
3662 if (sz < sizeof(DEVMODEA))
3664 TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
3665 return FALSE;
3667 /* ensures that dmSize is not erratically bogus if registry is invalid */
3668 if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
3669 ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
3670 if(unicode) {
3671 sz += (CCHDEVICENAME + CCHFORMNAME);
3672 if(buflen >= sz) {
3673 DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
3674 memcpy(ptr, dmW, sz);
3675 HeapFree(GetProcessHeap(),0,dmW);
3678 *needed = sz;
3679 return TRUE;
3682 /*********************************************************************
3683 * WINSPOOL_GetPrinter_1
3685 * Fills out a PRINTER_INFO_1A|W struct storing the strings in buf.
3686 * The strings are either stored as unicode or ascii.
3688 static BOOL WINSPOOL_GetPrinter_1(HKEY hkeyPrinter, PRINTER_INFO_1W *pi1,
3689 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3690 BOOL unicode)
3692 DWORD size, left = cbBuf;
3693 BOOL space = (cbBuf > 0);
3694 LPBYTE ptr = buf;
3696 *pcbNeeded = 0;
3698 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3699 unicode)) {
3700 if(space && size <= left) {
3701 pi1->pName = (LPWSTR)ptr;
3702 ptr += size;
3703 left -= size;
3704 } else
3705 space = FALSE;
3706 *pcbNeeded += size;
3709 /* FIXME: pDescription should be something like "Name,Driver_Name,". */
3710 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3711 unicode)) {
3712 if(space && size <= left) {
3713 pi1->pDescription = (LPWSTR)ptr;
3714 ptr += size;
3715 left -= size;
3716 } else
3717 space = FALSE;
3718 *pcbNeeded += size;
3721 if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3722 unicode)) {
3723 if(space && size <= left) {
3724 pi1->pComment = (LPWSTR)ptr;
3725 ptr += size;
3726 left -= size;
3727 } else
3728 space = FALSE;
3729 *pcbNeeded += size;
3732 if(pi1) pi1->Flags = PRINTER_ENUM_ICON8; /* We're a printer */
3734 if(!space && pi1) /* zero out pi1 if we can't completely fill buf */
3735 memset(pi1, 0, sizeof(*pi1));
3737 return space;
3739 /*********************************************************************
3740 * WINSPOOL_GetPrinter_2
3742 * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
3743 * The strings are either stored as unicode or ascii.
3745 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
3746 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3747 BOOL unicode)
3749 DWORD size, left = cbBuf;
3750 BOOL space = (cbBuf > 0);
3751 LPBYTE ptr = buf;
3753 *pcbNeeded = 0;
3755 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3756 unicode)) {
3757 if(space && size <= left) {
3758 pi2->pPrinterName = (LPWSTR)ptr;
3759 ptr += size;
3760 left -= size;
3761 } else
3762 space = FALSE;
3763 *pcbNeeded += size;
3765 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
3766 unicode)) {
3767 if(space && size <= left) {
3768 pi2->pShareName = (LPWSTR)ptr;
3769 ptr += size;
3770 left -= size;
3771 } else
3772 space = FALSE;
3773 *pcbNeeded += size;
3775 if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3776 unicode)) {
3777 if(space && size <= left) {
3778 pi2->pPortName = (LPWSTR)ptr;
3779 ptr += size;
3780 left -= size;
3781 } else
3782 space = FALSE;
3783 *pcbNeeded += size;
3785 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
3786 &size, unicode)) {
3787 if(space && size <= left) {
3788 pi2->pDriverName = (LPWSTR)ptr;
3789 ptr += size;
3790 left -= size;
3791 } else
3792 space = FALSE;
3793 *pcbNeeded += size;
3795 if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3796 unicode)) {
3797 if(space && size <= left) {
3798 pi2->pComment = (LPWSTR)ptr;
3799 ptr += size;
3800 left -= size;
3801 } else
3802 space = FALSE;
3803 *pcbNeeded += size;
3805 if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
3806 unicode)) {
3807 if(space && size <= left) {
3808 pi2->pLocation = (LPWSTR)ptr;
3809 ptr += size;
3810 left -= size;
3811 } else
3812 space = FALSE;
3813 *pcbNeeded += size;
3815 if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
3816 &size, unicode)) {
3817 if(space && size <= left) {
3818 pi2->pDevMode = (LPDEVMODEW)ptr;
3819 ptr += size;
3820 left -= size;
3821 } else
3822 space = FALSE;
3823 *pcbNeeded += size;
3825 else
3827 WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
3828 if(space && size <= left) {
3829 pi2->pDevMode = (LPDEVMODEW)ptr;
3830 ptr += size;
3831 left -= size;
3832 } else
3833 space = FALSE;
3834 *pcbNeeded += size;
3836 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
3837 &size, unicode)) {
3838 if(space && size <= left) {
3839 pi2->pSepFile = (LPWSTR)ptr;
3840 ptr += size;
3841 left -= size;
3842 } else
3843 space = FALSE;
3844 *pcbNeeded += size;
3846 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
3847 &size, unicode)) {
3848 if(space && size <= left) {
3849 pi2->pPrintProcessor = (LPWSTR)ptr;
3850 ptr += size;
3851 left -= size;
3852 } else
3853 space = FALSE;
3854 *pcbNeeded += size;
3856 if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
3857 &size, unicode)) {
3858 if(space && size <= left) {
3859 pi2->pDatatype = (LPWSTR)ptr;
3860 ptr += size;
3861 left -= size;
3862 } else
3863 space = FALSE;
3864 *pcbNeeded += size;
3866 if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
3867 &size, unicode)) {
3868 if(space && size <= left) {
3869 pi2->pParameters = (LPWSTR)ptr;
3870 ptr += size;
3871 left -= size;
3872 } else
3873 space = FALSE;
3874 *pcbNeeded += size;
3876 if(pi2) {
3877 pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3878 pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
3879 pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3880 "Default Priority");
3881 pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
3882 pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
3885 if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
3886 memset(pi2, 0, sizeof(*pi2));
3888 return space;
3891 /*********************************************************************
3892 * WINSPOOL_GetPrinter_4
3894 * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
3896 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
3897 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3898 BOOL unicode)
3900 DWORD size, left = cbBuf;
3901 BOOL space = (cbBuf > 0);
3902 LPBYTE ptr = buf;
3904 *pcbNeeded = 0;
3906 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3907 unicode)) {
3908 if(space && size <= left) {
3909 pi4->pPrinterName = (LPWSTR)ptr;
3910 ptr += size;
3911 left -= size;
3912 } else
3913 space = FALSE;
3914 *pcbNeeded += size;
3916 if(pi4) {
3917 pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3920 if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
3921 memset(pi4, 0, sizeof(*pi4));
3923 return space;
3926 /*********************************************************************
3927 * WINSPOOL_GetPrinter_5
3929 * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
3931 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
3932 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3933 BOOL unicode)
3935 DWORD size, left = cbBuf;
3936 BOOL space = (cbBuf > 0);
3937 LPBYTE ptr = buf;
3939 *pcbNeeded = 0;
3941 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3942 unicode)) {
3943 if(space && size <= left) {
3944 pi5->pPrinterName = (LPWSTR)ptr;
3945 ptr += size;
3946 left -= size;
3947 } else
3948 space = FALSE;
3949 *pcbNeeded += size;
3951 if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3952 unicode)) {
3953 if(space && size <= left) {
3954 pi5->pPortName = (LPWSTR)ptr;
3955 ptr += size;
3956 left -= size;
3957 } else
3958 space = FALSE;
3959 *pcbNeeded += size;
3961 if(pi5) {
3962 pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3963 pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3964 "dnsTimeout");
3965 pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3966 "txTimeout");
3969 if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
3970 memset(pi5, 0, sizeof(*pi5));
3972 return space;
3975 /*****************************************************************************
3976 * WINSPOOL_GetPrinter
3978 * Implementation of GetPrinterA|W. Relies on PRINTER_INFO_*W being
3979 * essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
3980 * just a collection of pointers to strings.
3982 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3983 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
3985 LPCWSTR name;
3986 DWORD size, needed = 0;
3987 LPBYTE ptr = NULL;
3988 HKEY hkeyPrinter, hkeyPrinters;
3989 BOOL ret;
3991 TRACE("(%p,%d,%p,%d,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
3993 if (!(name = get_opened_printer_name(hPrinter))) {
3994 SetLastError(ERROR_INVALID_HANDLE);
3995 return FALSE;
3998 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
3999 ERROR_SUCCESS) {
4000 ERR("Can't create Printers key\n");
4001 return FALSE;
4003 if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
4005 ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4006 RegCloseKey(hkeyPrinters);
4007 SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4008 return FALSE;
4011 switch(Level) {
4012 case 2:
4014 PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
4016 size = sizeof(PRINTER_INFO_2W);
4017 if(size <= cbBuf) {
4018 ptr = pPrinter + size;
4019 cbBuf -= size;
4020 memset(pPrinter, 0, size);
4021 } else {
4022 pi2 = NULL;
4023 cbBuf = 0;
4025 ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
4026 unicode);
4027 needed += size;
4028 break;
4031 case 4:
4033 PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
4035 size = sizeof(PRINTER_INFO_4W);
4036 if(size <= cbBuf) {
4037 ptr = pPrinter + size;
4038 cbBuf -= size;
4039 memset(pPrinter, 0, size);
4040 } else {
4041 pi4 = NULL;
4042 cbBuf = 0;
4044 ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
4045 unicode);
4046 needed += size;
4047 break;
4051 case 5:
4053 PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
4055 size = sizeof(PRINTER_INFO_5W);
4056 if(size <= cbBuf) {
4057 ptr = pPrinter + size;
4058 cbBuf -= size;
4059 memset(pPrinter, 0, size);
4060 } else {
4061 pi5 = NULL;
4062 cbBuf = 0;
4065 ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
4066 unicode);
4067 needed += size;
4068 break;
4071 default:
4072 FIXME("Unimplemented level %d\n", Level);
4073 SetLastError(ERROR_INVALID_LEVEL);
4074 RegCloseKey(hkeyPrinters);
4075 RegCloseKey(hkeyPrinter);
4076 return FALSE;
4079 RegCloseKey(hkeyPrinter);
4080 RegCloseKey(hkeyPrinters);
4082 TRACE("returning %d needed = %d\n", ret, needed);
4083 if(pcbNeeded) *pcbNeeded = needed;
4084 if(!ret)
4085 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4086 return ret;
4089 /*****************************************************************************
4090 * GetPrinterW [WINSPOOL.@]
4092 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4093 DWORD cbBuf, LPDWORD pcbNeeded)
4095 return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4096 TRUE);
4099 /*****************************************************************************
4100 * GetPrinterA [WINSPOOL.@]
4102 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4103 DWORD cbBuf, LPDWORD pcbNeeded)
4105 return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4106 FALSE);
4109 /*****************************************************************************
4110 * WINSPOOL_EnumPrinters
4112 * Implementation of EnumPrintersA|W
4114 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
4115 DWORD dwLevel, LPBYTE lpbPrinters,
4116 DWORD cbBuf, LPDWORD lpdwNeeded,
4117 LPDWORD lpdwReturned, BOOL unicode)
4120 HKEY hkeyPrinters, hkeyPrinter;
4121 WCHAR PrinterName[255];
4122 DWORD needed = 0, number = 0;
4123 DWORD used, i, left;
4124 PBYTE pi, buf;
4126 if(lpbPrinters)
4127 memset(lpbPrinters, 0, cbBuf);
4128 if(lpdwReturned)
4129 *lpdwReturned = 0;
4130 if(lpdwNeeded)
4131 *lpdwNeeded = 0;
4133 /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
4134 if(dwType == PRINTER_ENUM_DEFAULT)
4135 return TRUE;
4137 if (dwType & PRINTER_ENUM_CONNECTIONS) {
4138 TRACE("ignoring PRINTER_ENUM_CONNECTIONS\n");
4139 dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
4140 if (!dwType) {
4141 FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
4142 *lpdwNeeded = 0;
4143 *lpdwReturned = 0;
4144 return TRUE;
4149 if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
4150 FIXME("dwType = %08x\n", dwType);
4151 SetLastError(ERROR_INVALID_FLAGS);
4152 return FALSE;
4155 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4156 ERROR_SUCCESS) {
4157 ERR("Can't create Printers key\n");
4158 return FALSE;
4161 if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
4162 NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4163 RegCloseKey(hkeyPrinters);
4164 ERR("Can't query Printers key\n");
4165 return FALSE;
4167 TRACE("Found %d printers\n", number);
4169 switch(dwLevel) {
4170 case 1:
4171 used = number * sizeof(PRINTER_INFO_1W);
4172 break;
4173 case 2:
4174 used = number * sizeof(PRINTER_INFO_2W);
4175 break;
4176 case 4:
4177 used = number * sizeof(PRINTER_INFO_4W);
4178 break;
4179 case 5:
4180 used = number * sizeof(PRINTER_INFO_5W);
4181 break;
4183 default:
4184 SetLastError(ERROR_INVALID_LEVEL);
4185 RegCloseKey(hkeyPrinters);
4186 return FALSE;
4188 pi = (used <= cbBuf) ? lpbPrinters : NULL;
4190 for(i = 0; i < number; i++) {
4191 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) !=
4192 ERROR_SUCCESS) {
4193 ERR("Can't enum key number %d\n", i);
4194 RegCloseKey(hkeyPrinters);
4195 return FALSE;
4197 TRACE("Printer %d is %s\n", i, debugstr_w(PrinterName));
4198 if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
4199 ERROR_SUCCESS) {
4200 ERR("Can't open key %s\n", debugstr_w(PrinterName));
4201 RegCloseKey(hkeyPrinters);
4202 return FALSE;
4205 if(cbBuf > used) {
4206 buf = lpbPrinters + used;
4207 left = cbBuf - used;
4208 } else {
4209 buf = NULL;
4210 left = 0;
4213 switch(dwLevel) {
4214 case 1:
4215 WINSPOOL_GetPrinter_1(hkeyPrinter, (PRINTER_INFO_1W *)pi, buf,
4216 left, &needed, unicode);
4217 used += needed;
4218 if(pi) pi += sizeof(PRINTER_INFO_1W);
4219 break;
4220 case 2:
4221 WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
4222 left, &needed, unicode);
4223 used += needed;
4224 if(pi) pi += sizeof(PRINTER_INFO_2W);
4225 break;
4226 case 4:
4227 WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
4228 left, &needed, unicode);
4229 used += needed;
4230 if(pi) pi += sizeof(PRINTER_INFO_4W);
4231 break;
4232 case 5:
4233 WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
4234 left, &needed, unicode);
4235 used += needed;
4236 if(pi) pi += sizeof(PRINTER_INFO_5W);
4237 break;
4238 default:
4239 ERR("Shouldn't be here!\n");
4240 RegCloseKey(hkeyPrinter);
4241 RegCloseKey(hkeyPrinters);
4242 return FALSE;
4244 RegCloseKey(hkeyPrinter);
4246 RegCloseKey(hkeyPrinters);
4248 if(lpdwNeeded)
4249 *lpdwNeeded = used;
4251 if(used > cbBuf) {
4252 if(lpbPrinters)
4253 memset(lpbPrinters, 0, cbBuf);
4254 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4255 return FALSE;
4257 if(lpdwReturned)
4258 *lpdwReturned = number;
4259 SetLastError(ERROR_SUCCESS);
4260 return TRUE;
4264 /******************************************************************
4265 * EnumPrintersW [WINSPOOL.@]
4267 * Enumerates the available printers, print servers and print
4268 * providers, depending on the specified flags, name and level.
4270 * RETURNS:
4272 * If level is set to 1:
4273 * Returns an array of PRINTER_INFO_1 data structures in the
4274 * lpbPrinters buffer.
4276 * If level is set to 2:
4277 * Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4278 * Returns an array of PRINTER_INFO_2 data structures in the
4279 * lpbPrinters buffer. Note that according to MSDN also an
4280 * OpenPrinter should be performed on every remote printer.
4282 * If level is set to 4 (officially WinNT only):
4283 * Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4284 * Fast: Only the registry is queried to retrieve printer names,
4285 * no connection to the driver is made.
4286 * Returns an array of PRINTER_INFO_4 data structures in the
4287 * lpbPrinters buffer.
4289 * If level is set to 5 (officially WinNT4/Win9x only):
4290 * Fast: Only the registry is queried to retrieve printer names,
4291 * no connection to the driver is made.
4292 * Returns an array of PRINTER_INFO_5 data structures in the
4293 * lpbPrinters buffer.
4295 * If level set to 3 or 6+:
4296 * returns zero (failure!)
4298 * Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
4299 * for information.
4301 * BUGS:
4302 * - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
4303 * - Only levels 2, 4 and 5 are implemented at the moment.
4304 * - 16-bit printer drivers are not enumerated.
4305 * - Returned amount of bytes used/needed does not match the real Windoze
4306 * implementation (as in this implementation, all strings are part
4307 * of the buffer, whereas Win32 keeps them somewhere else)
4308 * - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
4310 * NOTE:
4311 * - In a regular Wine installation, no registry settings for printers
4312 * exist, which makes this function return an empty list.
4314 BOOL WINAPI EnumPrintersW(
4315 DWORD dwType, /* [in] Types of print objects to enumerate */
4316 LPWSTR lpszName, /* [in] name of objects to enumerate */
4317 DWORD dwLevel, /* [in] type of printer info structure */
4318 LPBYTE lpbPrinters, /* [out] buffer which receives info */
4319 DWORD cbBuf, /* [in] max size of buffer in bytes */
4320 LPDWORD lpdwNeeded, /* [out] pointer to var: # bytes used/needed */
4321 LPDWORD lpdwReturned /* [out] number of entries returned */
4324 return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
4325 lpdwNeeded, lpdwReturned, TRUE);
4328 /******************************************************************
4329 * EnumPrintersA [WINSPOOL.@]
4332 BOOL WINAPI EnumPrintersA(DWORD dwType, LPSTR lpszName,
4333 DWORD dwLevel, LPBYTE lpbPrinters,
4334 DWORD cbBuf, LPDWORD lpdwNeeded,
4335 LPDWORD lpdwReturned)
4337 BOOL ret, unicode = FALSE;
4338 UNICODE_STRING lpszNameW;
4339 PWSTR pwstrNameW;
4341 pwstrNameW = asciitounicode(&lpszNameW,lpszName);
4342 if(!cbBuf) unicode = TRUE; /* return a buffer that's big enough for the unicode version */
4343 ret = WINSPOOL_EnumPrinters(dwType, pwstrNameW, dwLevel, lpbPrinters, cbBuf,
4344 lpdwNeeded, lpdwReturned, unicode);
4345 RtlFreeUnicodeString(&lpszNameW);
4346 return ret;
4349 /*****************************************************************************
4350 * WINSPOOL_GetDriverInfoFromReg [internal]
4352 * Enters the information from the registry into the DRIVER_INFO struct
4354 * RETURNS
4355 * zero if the printer driver does not exist in the registry
4356 * (only if Level > 1) otherwise nonzero
4358 static BOOL WINSPOOL_GetDriverInfoFromReg(
4359 HKEY hkeyDrivers,
4360 LPWSTR DriverName,
4361 const printenv_t * env,
4362 DWORD Level,
4363 LPBYTE ptr, /* DRIVER_INFO */
4364 LPBYTE pDriverStrings, /* strings buffer */
4365 DWORD cbBuf, /* size of string buffer */
4366 LPDWORD pcbNeeded, /* space needed for str. */
4367 BOOL unicode) /* type of strings */
4369 DWORD size, tmp;
4370 HKEY hkeyDriver;
4371 WCHAR driverdir[MAX_PATH];
4372 DWORD dirlen;
4373 LPBYTE strPtr = pDriverStrings;
4374 LPDRIVER_INFO_8W di = (LPDRIVER_INFO_8W) ptr;
4376 TRACE("(%p, %s, %p, %d, %p, %p, %d, %d)\n", hkeyDrivers,
4377 debugstr_w(DriverName), env,
4378 Level, di, pDriverStrings, cbBuf, unicode);
4380 if (di) ZeroMemory(di, di_sizeof[Level]);
4382 if (unicode) {
4383 *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
4384 if (*pcbNeeded <= cbBuf)
4385 strcpyW((LPWSTR)strPtr, DriverName);
4387 else
4389 *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0, NULL, NULL);
4390 if (*pcbNeeded <= cbBuf)
4391 WideCharToMultiByte(CP_ACP, 0, DriverName, -1, (LPSTR)strPtr, *pcbNeeded, NULL, NULL);
4394 /* pName for level 1 has a different offset! */
4395 if (Level == 1) {
4396 if (di) ((LPDRIVER_INFO_1W) di)->pName = (LPWSTR) strPtr;
4397 return TRUE;
4400 /* .cVersion and .pName for level > 1 */
4401 if (di) {
4402 di->cVersion = env->driverversion;
4403 di->pName = (LPWSTR) strPtr;
4404 strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4407 /* Reserve Space for the largest subdir and a Backslash*/
4408 size = sizeof(driverdir) - sizeof(Version3_SubdirW) - sizeof(WCHAR);
4409 if (!GetPrinterDriverDirectoryW(NULL, (LPWSTR) env->envname, 1, (LPBYTE) driverdir, size, &size)) {
4410 /* Should never Fail */
4411 return FALSE;
4413 lstrcatW(driverdir, env->versionsubdir);
4414 lstrcatW(driverdir, backslashW);
4416 /* dirlen must not include the terminating zero */
4417 dirlen = (unicode) ? lstrlenW(driverdir) * sizeof(WCHAR) :
4418 WideCharToMultiByte(CP_ACP, 0, driverdir, -1, NULL, 0, NULL, NULL) -1;
4420 if (!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
4421 ERR("Can't find driver %s in registry\n", debugstr_w(DriverName));
4422 SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
4423 return FALSE;
4426 /* pEnvironment */
4427 if (unicode)
4428 size = (lstrlenW(env->envname) + 1) * sizeof(WCHAR);
4429 else
4430 size = WideCharToMultiByte(CP_ACP, 0, env->envname, -1, NULL, 0, NULL, NULL);
4432 *pcbNeeded += size;
4433 if (*pcbNeeded <= cbBuf) {
4434 if (unicode) {
4435 lstrcpyW((LPWSTR)strPtr, env->envname);
4437 else
4439 WideCharToMultiByte(CP_ACP, 0, env->envname, -1, (LPSTR)strPtr, size, NULL, NULL);
4441 if (di) di->pEnvironment = (LPWSTR)strPtr;
4442 strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4445 /* .pDriverPath is the Graphics rendering engine.
4446 The full Path is required to avoid a crash in some apps */
4447 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, 0, &size, unicode)) {
4448 *pcbNeeded += size;
4449 if (*pcbNeeded <= cbBuf)
4450 get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, size, &tmp, unicode);
4452 if (di) di->pDriverPath = (LPWSTR)strPtr;
4453 strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4456 /* .pDataFile: For postscript-drivers, this is the ppd-file */
4457 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, 0, &size, unicode)) {
4458 *pcbNeeded += size;
4459 if (*pcbNeeded <= cbBuf)
4460 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, size, &size, unicode);
4462 if (di) di->pDataFile = (LPWSTR)strPtr;
4463 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4466 /* .pConfigFile is the Driver user Interface */
4467 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, 0, &size, unicode)) {
4468 *pcbNeeded += size;
4469 if (*pcbNeeded <= cbBuf)
4470 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, size, &size, unicode);
4472 if (di) di->pConfigFile = (LPWSTR)strPtr;
4473 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4476 if (Level == 2 ) {
4477 RegCloseKey(hkeyDriver);
4478 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4479 return TRUE;
4482 if (Level == 5 ) {
4483 RegCloseKey(hkeyDriver);
4484 FIXME("level 5: incomplete\n");
4485 return TRUE;
4488 /* .pHelpFile */
4489 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, 0, &size, unicode)) {
4490 *pcbNeeded += size;
4491 if (*pcbNeeded <= cbBuf)
4492 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, size, &size, unicode);
4494 if (di) di->pHelpFile = (LPWSTR)strPtr;
4495 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4498 /* .pDependentFiles */
4499 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, 0, &size, unicode)) {
4500 *pcbNeeded += size;
4501 if (*pcbNeeded <= cbBuf)
4502 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, size, &size, unicode);
4504 if (di) di->pDependentFiles = (LPWSTR)strPtr;
4505 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4508 /* .pMonitorName is the optional Language Monitor */
4509 if (WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size, unicode)) {
4510 *pcbNeeded += size;
4511 if (*pcbNeeded <= cbBuf)
4512 WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, size, &size, unicode);
4514 if (di) di->pMonitorName = (LPWSTR)strPtr;
4515 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4518 /* .pDefaultDataType */
4519 if (WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size, unicode)) {
4520 *pcbNeeded += size;
4521 if(*pcbNeeded <= cbBuf)
4522 WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, size, &size, unicode);
4524 if (di) di->pDefaultDataType = (LPWSTR)strPtr;
4525 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4528 if (Level == 3 ) {
4529 RegCloseKey(hkeyDriver);
4530 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4531 return TRUE;
4534 /* .pszzPreviousNames */
4535 if (WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, 0, &size, unicode)) {
4536 *pcbNeeded += size;
4537 if(*pcbNeeded <= cbBuf)
4538 WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, size, &size, unicode);
4540 if (di) di->pszzPreviousNames = (LPWSTR)strPtr;
4541 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4544 if (Level == 4 ) {
4545 RegCloseKey(hkeyDriver);
4546 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4547 return TRUE;
4550 /* support is missing, but not important enough for a FIXME */
4551 TRACE("%s: DriverDate + DriverVersion not supported\n", debugstr_w(DriverName));
4553 /* .pszMfgName */
4554 if (WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, 0, &size, unicode)) {
4555 *pcbNeeded += size;
4556 if(*pcbNeeded <= cbBuf)
4557 WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, size, &size, unicode);
4559 if (di) di->pszMfgName = (LPWSTR)strPtr;
4560 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4563 /* .pszOEMUrl */
4564 if (WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, 0, &size, unicode)) {
4565 *pcbNeeded += size;
4566 if(*pcbNeeded <= cbBuf)
4567 WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, size, &size, unicode);
4569 if (di) di->pszOEMUrl = (LPWSTR)strPtr;
4570 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4573 /* .pszHardwareID */
4574 if (WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, 0, &size, unicode)) {
4575 *pcbNeeded += size;
4576 if(*pcbNeeded <= cbBuf)
4577 WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, size, &size, unicode);
4579 if (di) di->pszHardwareID = (LPWSTR)strPtr;
4580 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4583 /* .pszProvider */
4584 if (WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, 0, &size, unicode)) {
4585 *pcbNeeded += size;
4586 if(*pcbNeeded <= cbBuf)
4587 WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, size, &size, unicode);
4589 if (di) di->pszProvider = (LPWSTR)strPtr;
4590 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4593 if (Level == 6 ) {
4594 RegCloseKey(hkeyDriver);
4595 return TRUE;
4598 /* support is missing, but not important enough for a FIXME */
4599 TRACE("level 8: incomplete\n");
4601 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4602 RegCloseKey(hkeyDriver);
4603 return TRUE;
4606 /*****************************************************************************
4607 * WINSPOOL_GetPrinterDriver
4609 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPCWSTR pEnvironment,
4610 DWORD Level, LPBYTE pDriverInfo,
4611 DWORD cbBuf, LPDWORD pcbNeeded,
4612 BOOL unicode)
4614 LPCWSTR name;
4615 WCHAR DriverName[100];
4616 DWORD ret, type, size, needed = 0;
4617 LPBYTE ptr = NULL;
4618 HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
4619 const printenv_t * env;
4621 TRACE("(%p,%s,%d,%p,%d,%p)\n",hPrinter,debugstr_w(pEnvironment),
4622 Level,pDriverInfo,cbBuf, pcbNeeded);
4625 if (!(name = get_opened_printer_name(hPrinter))) {
4626 SetLastError(ERROR_INVALID_HANDLE);
4627 return FALSE;
4630 if (Level < 1 || Level == 7 || Level > 8) {
4631 SetLastError(ERROR_INVALID_LEVEL);
4632 return FALSE;
4635 env = validate_envW(pEnvironment);
4636 if (!env) return FALSE; /* SetLastError() is in validate_envW */
4638 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4639 ERROR_SUCCESS) {
4640 ERR("Can't create Printers key\n");
4641 return FALSE;
4643 if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
4644 != ERROR_SUCCESS) {
4645 ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4646 RegCloseKey(hkeyPrinters);
4647 SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4648 return FALSE;
4650 size = sizeof(DriverName);
4651 DriverName[0] = 0;
4652 ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
4653 (LPBYTE)DriverName, &size);
4654 RegCloseKey(hkeyPrinter);
4655 RegCloseKey(hkeyPrinters);
4656 if(ret != ERROR_SUCCESS) {
4657 ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
4658 return FALSE;
4661 hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
4662 if(!hkeyDrivers) {
4663 ERR("Can't create Drivers key\n");
4664 return FALSE;
4667 size = di_sizeof[Level];
4668 if ((size <= cbBuf) && pDriverInfo)
4669 ptr = pDriverInfo + size;
4671 if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
4672 env, Level, pDriverInfo, ptr,
4673 (cbBuf < size) ? 0 : cbBuf - size,
4674 &needed, unicode)) {
4675 RegCloseKey(hkeyDrivers);
4676 return FALSE;
4679 RegCloseKey(hkeyDrivers);
4681 if(pcbNeeded) *pcbNeeded = size + needed;
4682 TRACE("buffer space %d required %d\n", cbBuf, size + needed);
4683 if(cbBuf >= needed) return TRUE;
4684 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4685 return FALSE;
4688 /*****************************************************************************
4689 * GetPrinterDriverA [WINSPOOL.@]
4691 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
4692 DWORD Level, LPBYTE pDriverInfo,
4693 DWORD cbBuf, LPDWORD pcbNeeded)
4695 BOOL ret;
4696 UNICODE_STRING pEnvW;
4697 PWSTR pwstrEnvW;
4699 pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
4700 ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
4701 cbBuf, pcbNeeded, FALSE);
4702 RtlFreeUnicodeString(&pEnvW);
4703 return ret;
4705 /*****************************************************************************
4706 * GetPrinterDriverW [WINSPOOL.@]
4708 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
4709 DWORD Level, LPBYTE pDriverInfo,
4710 DWORD cbBuf, LPDWORD pcbNeeded)
4712 return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
4713 pDriverInfo, cbBuf, pcbNeeded, TRUE);
4716 /*****************************************************************************
4717 * GetPrinterDriverDirectoryW [WINSPOOL.@]
4719 * Return the PATH for the Printer-Drivers (UNICODE)
4721 * PARAMS
4722 * pName [I] Servername (NT only) or NULL (local Computer)
4723 * pEnvironment [I] Printing-Environment (see below) or NULL (Default)
4724 * Level [I] Structure-Level (must be 1)
4725 * pDriverDirectory [O] PTR to Buffer that receives the Result
4726 * cbBuf [I] Size of Buffer at pDriverDirectory
4727 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
4728 * required for pDriverDirectory
4730 * RETURNS
4731 * Success: TRUE and in pcbNeeded the Bytes used in pDriverDirectory
4732 * Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
4733 * if cbBuf is too small
4735 * Native Values returned in pDriverDirectory on Success:
4736 *| NT(Windows NT x86): "%winsysdir%\\spool\\DRIVERS\\w32x86"
4737 *| NT(Windows 4.0): "%winsysdir%\\spool\\DRIVERS\\win40"
4738 *| win9x(Windows 4.0): "%winsysdir%"
4740 * "%winsysdir%" is the Value from GetSystemDirectoryW()
4742 * FIXME
4743 *- Only NULL or "" is supported for pName
4746 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
4747 DWORD Level, LPBYTE pDriverDirectory,
4748 DWORD cbBuf, LPDWORD pcbNeeded)
4750 DWORD needed;
4751 const printenv_t * env;
4753 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName),
4754 debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4755 if(pName != NULL && pName[0]) {
4756 FIXME("pName unsupported: %s\n", debugstr_w(pName));
4757 SetLastError(ERROR_INVALID_PARAMETER);
4758 return FALSE;
4761 env = validate_envW(pEnvironment);
4762 if(!env) return FALSE; /* pEnvironment invalid or unsupported */
4764 if(Level != 1) {
4765 WARN("(Level: %d) is ignored in win9x\n", Level);
4766 SetLastError(ERROR_INVALID_LEVEL);
4767 return FALSE;
4770 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
4771 needed = GetSystemDirectoryW(NULL, 0);
4772 /* add the Size for the Subdirectories */
4773 needed += lstrlenW(spooldriversW);
4774 needed += lstrlenW(env->subdir);
4775 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
4777 if(pcbNeeded)
4778 *pcbNeeded = needed;
4779 TRACE("required: 0x%x/%d\n", needed, needed);
4780 if(needed > cbBuf) {
4781 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4782 return FALSE;
4784 if(pcbNeeded == NULL) {
4785 WARN("(pcbNeeded == NULL) is ignored in win9x\n");
4786 SetLastError(RPC_X_NULL_REF_POINTER);
4787 return FALSE;
4789 if(pDriverDirectory == NULL) {
4790 /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
4791 SetLastError(ERROR_INVALID_USER_BUFFER);
4792 return FALSE;
4795 GetSystemDirectoryW((LPWSTR) pDriverDirectory, cbBuf/sizeof(WCHAR));
4796 /* add the Subdirectories */
4797 lstrcatW((LPWSTR) pDriverDirectory, spooldriversW);
4798 lstrcatW((LPWSTR) pDriverDirectory, env->subdir);
4799 TRACE(" => %s\n", debugstr_w((LPWSTR) pDriverDirectory));
4800 return TRUE;
4804 /*****************************************************************************
4805 * GetPrinterDriverDirectoryA [WINSPOOL.@]
4807 * Return the PATH for the Printer-Drivers (ANSI)
4809 * See GetPrinterDriverDirectoryW.
4811 * NOTES
4812 * On NT, pDriverDirectory need the same Size as the Unicode-Version
4815 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
4816 DWORD Level, LPBYTE pDriverDirectory,
4817 DWORD cbBuf, LPDWORD pcbNeeded)
4819 UNICODE_STRING nameW, environmentW;
4820 BOOL ret;
4821 DWORD pcbNeededW;
4822 INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
4823 WCHAR *driverDirectoryW = NULL;
4825 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(pName),
4826 debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4828 if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
4830 if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
4831 else nameW.Buffer = NULL;
4832 if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
4833 else environmentW.Buffer = NULL;
4835 ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
4836 (LPBYTE)driverDirectoryW, len, &pcbNeededW );
4837 if (ret) {
4838 DWORD needed;
4839 needed = WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1,
4840 (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
4841 if(pcbNeeded)
4842 *pcbNeeded = needed;
4843 ret = (needed <= cbBuf) ? TRUE : FALSE;
4844 } else
4845 if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
4847 TRACE("required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
4849 HeapFree( GetProcessHeap(), 0, driverDirectoryW );
4850 RtlFreeUnicodeString(&environmentW);
4851 RtlFreeUnicodeString(&nameW);
4853 return ret;
4856 /*****************************************************************************
4857 * AddPrinterDriverA [WINSPOOL.@]
4859 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
4861 DRIVER_INFO_3A di3;
4862 HKEY hkeyDrivers, hkeyName;
4863 static CHAR empty[] = "",
4864 nullnull[] = "\0";
4866 TRACE("(%s,%d,%p)\n",debugstr_a(pName),level,pDriverInfo);
4868 if(level != 2 && level != 3) {
4869 SetLastError(ERROR_INVALID_LEVEL);
4870 return FALSE;
4872 if ((pName) && (pName[0])) {
4873 FIXME("pName= %s - unsupported\n", debugstr_a(pName));
4874 SetLastError(ERROR_INVALID_PARAMETER);
4875 return FALSE;
4877 if(!pDriverInfo) {
4878 WARN("pDriverInfo == NULL\n");
4879 SetLastError(ERROR_INVALID_PARAMETER);
4880 return FALSE;
4883 if(level == 3)
4884 di3 = *(DRIVER_INFO_3A *)pDriverInfo;
4885 else {
4886 memset(&di3, 0, sizeof(di3));
4887 memcpy(&di3, pDriverInfo, sizeof(DRIVER_INFO_2A));
4890 if(!di3.pName || !di3.pDriverPath || !di3.pConfigFile ||
4891 !di3.pDataFile) {
4892 SetLastError(ERROR_INVALID_PARAMETER);
4893 return FALSE;
4896 if(!di3.pDefaultDataType) di3.pDefaultDataType = empty;
4897 if(!di3.pDependentFiles) di3.pDependentFiles = nullnull;
4898 if(!di3.pHelpFile) di3.pHelpFile = empty;
4899 if(!di3.pMonitorName) di3.pMonitorName = empty;
4901 hkeyDrivers = WINSPOOL_OpenDriverReg(di3.pEnvironment, FALSE);
4903 if(!hkeyDrivers) {
4904 ERR("Can't create Drivers key\n");
4905 return FALSE;
4908 if(level == 2) { /* apparently can't overwrite with level2 */
4909 if(RegOpenKeyA(hkeyDrivers, di3.pName, &hkeyName) == ERROR_SUCCESS) {
4910 RegCloseKey(hkeyName);
4911 RegCloseKey(hkeyDrivers);
4912 WARN("Trying to create existing printer driver %s\n", debugstr_a(di3.pName));
4913 SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
4914 return FALSE;
4917 if(RegCreateKeyA(hkeyDrivers, di3.pName, &hkeyName) != ERROR_SUCCESS) {
4918 RegCloseKey(hkeyDrivers);
4919 ERR("Can't create Name key\n");
4920 return FALSE;
4922 RegSetValueExA(hkeyName, "Configuration File", 0, REG_SZ, (LPBYTE) di3.pConfigFile,
4923 lstrlenA(di3.pConfigFile) + 1);
4924 RegSetValueExA(hkeyName, "Data File", 0, REG_SZ, (LPBYTE) di3.pDataFile, lstrlenA(di3.pDataFile) + 1);
4925 RegSetValueExA(hkeyName, "Driver", 0, REG_SZ, (LPBYTE) di3.pDriverPath, lstrlenA(di3.pDriverPath) + 1);
4926 RegSetValueExA(hkeyName, "Version", 0, REG_DWORD, (LPBYTE) &di3.cVersion,
4927 sizeof(DWORD));
4928 RegSetValueExA(hkeyName, "Datatype", 0, REG_SZ, (LPBYTE) di3.pDefaultDataType, lstrlenA(di3.pDefaultDataType));
4929 RegSetValueExA(hkeyName, "Dependent Files", 0, REG_MULTI_SZ,
4930 (LPBYTE) di3.pDependentFiles, multi_sz_lenA(di3.pDependentFiles));
4931 RegSetValueExA(hkeyName, "Help File", 0, REG_SZ, (LPBYTE) di3.pHelpFile, lstrlenA(di3.pHelpFile) + 1);
4932 RegSetValueExA(hkeyName, "Monitor", 0, REG_SZ, (LPBYTE) di3.pMonitorName, lstrlenA(di3.pMonitorName) + 1);
4933 RegCloseKey(hkeyName);
4934 RegCloseKey(hkeyDrivers);
4936 return TRUE;
4939 /*****************************************************************************
4940 * AddPrinterDriverW [WINSPOOL.@]
4942 BOOL WINAPI AddPrinterDriverW(LPWSTR printerName,DWORD level,
4943 LPBYTE pDriverInfo)
4945 FIXME("(%s,%d,%p): stub\n",debugstr_w(printerName),
4946 level,pDriverInfo);
4947 return FALSE;
4950 /*****************************************************************************
4951 * AddPrintProcessorA [WINSPOOL.@]
4953 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
4954 LPSTR pPrintProcessorName)
4956 FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
4957 debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
4958 return FALSE;
4961 /*****************************************************************************
4962 * AddPrintProcessorW [WINSPOOL.@]
4964 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
4965 LPWSTR pPrintProcessorName)
4967 FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
4968 debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
4969 return FALSE;
4972 /*****************************************************************************
4973 * AddPrintProvidorA [WINSPOOL.@]
4975 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
4977 FIXME("(%s,0x%08x,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
4978 return FALSE;
4981 /*****************************************************************************
4982 * AddPrintProvidorW [WINSPOOL.@]
4984 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
4986 FIXME("(%s,0x%08x,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
4987 return FALSE;
4990 /*****************************************************************************
4991 * AdvancedDocumentPropertiesA [WINSPOOL.@]
4993 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
4994 PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
4996 FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
4997 pDevModeOutput, pDevModeInput);
4998 return 0;
5001 /*****************************************************************************
5002 * AdvancedDocumentPropertiesW [WINSPOOL.@]
5004 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
5005 PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
5007 FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
5008 pDevModeOutput, pDevModeInput);
5009 return 0;
5012 /*****************************************************************************
5013 * PrinterProperties [WINSPOOL.@]
5015 * Displays a dialog to set the properties of the printer.
5017 * RETURNS
5018 * nonzero on success or zero on failure
5020 * BUGS
5021 * implemented as stub only
5023 BOOL WINAPI PrinterProperties(HWND hWnd, /* [in] handle to parent window */
5024 HANDLE hPrinter /* [in] handle to printer object */
5026 FIXME("(%p,%p): stub\n", hWnd, hPrinter);
5027 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5028 return FALSE;
5031 /*****************************************************************************
5032 * EnumJobsA [WINSPOOL.@]
5035 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
5036 DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
5037 LPDWORD pcReturned)
5039 FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
5040 hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
5042 if(pcbNeeded) *pcbNeeded = 0;
5043 if(pcReturned) *pcReturned = 0;
5044 return FALSE;
5048 /*****************************************************************************
5049 * EnumJobsW [WINSPOOL.@]
5052 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
5053 DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
5054 LPDWORD pcReturned)
5056 FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
5057 hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
5059 if(pcbNeeded) *pcbNeeded = 0;
5060 if(pcReturned) *pcReturned = 0;
5061 return FALSE;
5064 /*****************************************************************************
5065 * WINSPOOL_EnumPrinterDrivers [internal]
5067 * Delivers information about all printer drivers installed on the
5068 * localhost or a given server
5070 * RETURNS
5071 * nonzero on success or zero on failure. If the buffer for the returned
5072 * information is too small the function will return an error
5074 * BUGS
5075 * - only implemented for localhost, foreign hosts will return an error
5077 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPCWSTR pEnvironment,
5078 DWORD Level, LPBYTE pDriverInfo,
5079 DWORD cbBuf, LPDWORD pcbNeeded,
5080 LPDWORD pcReturned, BOOL unicode)
5082 { HKEY hkeyDrivers;
5083 DWORD i, needed, number = 0, size = 0;
5084 WCHAR DriverNameW[255];
5085 PBYTE ptr;
5086 const printenv_t * env;
5088 TRACE("%s,%s,%d,%p,%d,%d\n",
5089 debugstr_w(pName), debugstr_w(pEnvironment),
5090 Level, pDriverInfo, cbBuf, unicode);
5092 /* check for local drivers */
5093 if((pName) && (pName[0])) {
5094 FIXME("remote drivers (%s) not supported!\n", debugstr_w(pName));
5095 SetLastError(ERROR_ACCESS_DENIED);
5096 return FALSE;
5099 env = validate_envW(pEnvironment);
5100 if (!env) return FALSE; /* SetLastError() is in validate_envW */
5102 /* check input parameter */
5103 if ((Level < 1) || (Level == 7) || (Level > 8)) {
5104 SetLastError(ERROR_INVALID_LEVEL);
5105 return FALSE;
5108 /* initialize return values */
5109 if(pDriverInfo)
5110 memset( pDriverInfo, 0, cbBuf);
5111 *pcbNeeded = 0;
5112 *pcReturned = 0;
5114 hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
5115 if(!hkeyDrivers) {
5116 ERR("Can't open Drivers key\n");
5117 return FALSE;
5120 if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
5121 NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
5122 RegCloseKey(hkeyDrivers);
5123 ERR("Can't query Drivers key\n");
5124 return FALSE;
5126 TRACE("Found %d Drivers\n", number);
5128 /* get size of single struct
5129 * unicode and ascii structure have the same size
5131 size = di_sizeof[Level];
5133 /* calculate required buffer size */
5134 *pcbNeeded = size * number;
5136 for( i = 0, ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
5137 i < number;
5138 i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
5139 if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW))
5140 != ERROR_SUCCESS) {
5141 ERR("Can't enum key number %d\n", i);
5142 RegCloseKey(hkeyDrivers);
5143 return FALSE;
5145 if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
5146 env, Level, ptr,
5147 (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
5148 (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
5149 &needed, unicode)) {
5150 RegCloseKey(hkeyDrivers);
5151 return FALSE;
5153 (*pcbNeeded) += needed;
5156 RegCloseKey(hkeyDrivers);
5158 if(cbBuf < *pcbNeeded){
5159 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5160 return FALSE;
5163 *pcReturned = number;
5164 return TRUE;
5167 /*****************************************************************************
5168 * EnumPrinterDriversW [WINSPOOL.@]
5170 * see function EnumPrinterDrivers for RETURNS, BUGS
5172 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5173 LPBYTE pDriverInfo, DWORD cbBuf,
5174 LPDWORD pcbNeeded, LPDWORD pcReturned)
5176 return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
5177 cbBuf, pcbNeeded, pcReturned, TRUE);
5180 /*****************************************************************************
5181 * EnumPrinterDriversA [WINSPOOL.@]
5183 * see function EnumPrinterDrivers for RETURNS, BUGS
5185 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
5186 LPBYTE pDriverInfo, DWORD cbBuf,
5187 LPDWORD pcbNeeded, LPDWORD pcReturned)
5188 { BOOL ret;
5189 UNICODE_STRING pNameW, pEnvironmentW;
5190 PWSTR pwstrNameW, pwstrEnvironmentW;
5192 pwstrNameW = asciitounicode(&pNameW, pName);
5193 pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
5195 ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
5196 Level, pDriverInfo, cbBuf, pcbNeeded,
5197 pcReturned, FALSE);
5198 RtlFreeUnicodeString(&pNameW);
5199 RtlFreeUnicodeString(&pEnvironmentW);
5201 return ret;
5204 /******************************************************************************
5205 * EnumPortsA (WINSPOOL.@)
5207 * See EnumPortsW.
5210 BOOL WINAPI EnumPortsA( LPSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
5211 LPDWORD pcbNeeded, LPDWORD pcReturned)
5213 BOOL res;
5214 LPBYTE bufferW = NULL;
5215 LPWSTR nameW = NULL;
5216 DWORD needed = 0;
5217 DWORD numentries = 0;
5218 INT len;
5220 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pPorts,
5221 cbBuf, pcbNeeded, pcReturned);
5223 /* convert servername to unicode */
5224 if (pName) {
5225 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5226 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5227 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5229 /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the Ports */
5230 needed = cbBuf * sizeof(WCHAR);
5231 if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
5232 res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5234 if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
5235 if (pcbNeeded) needed = *pcbNeeded;
5236 /* HeapReAlloc return NULL, when bufferW was NULL */
5237 bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
5238 HeapAlloc(GetProcessHeap(), 0, needed);
5240 /* Try again with the large Buffer */
5241 res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5243 needed = pcbNeeded ? *pcbNeeded : 0;
5244 numentries = pcReturned ? *pcReturned : 0;
5247 W2k require the buffersize from EnumPortsW also for EnumPortsA.
5248 We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
5250 if (res) {
5251 /* EnumPortsW collected all Data. Parse them to calculate ANSI-Size */
5252 DWORD entrysize = 0;
5253 DWORD index;
5254 LPSTR ptr;
5255 LPPORT_INFO_2W pi2w;
5256 LPPORT_INFO_2A pi2a;
5258 needed = 0;
5259 entrysize = (Level == 1) ? sizeof(PORT_INFO_1A) : sizeof(PORT_INFO_2A);
5261 /* First pass: calculate the size for all Entries */
5262 pi2w = (LPPORT_INFO_2W) bufferW;
5263 pi2a = (LPPORT_INFO_2A) pPorts;
5264 index = 0;
5265 while (index < numentries) {
5266 index++;
5267 needed += entrysize; /* PORT_INFO_?A */
5268 TRACE("%p: parsing #%d (%s)\n", pi2w, index, debugstr_w(pi2w->pPortName));
5270 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5271 NULL, 0, NULL, NULL);
5272 if (Level > 1) {
5273 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5274 NULL, 0, NULL, NULL);
5275 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5276 NULL, 0, NULL, NULL);
5278 /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5279 pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5280 pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5283 /* check for errors and quit on failure */
5284 if (cbBuf < needed) {
5285 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5286 res = FALSE;
5287 goto cleanup;
5289 len = entrysize * numentries; /* room for all PORT_INFO_?A */
5290 ptr = (LPSTR) &pPorts[len]; /* room for strings */
5291 cbBuf -= len ; /* free Bytes in the user-Buffer */
5292 pi2w = (LPPORT_INFO_2W) bufferW;
5293 pi2a = (LPPORT_INFO_2A) pPorts;
5294 index = 0;
5295 /* Second Pass: Fill the User Buffer (if we have one) */
5296 while ((index < numentries) && pPorts) {
5297 index++;
5298 TRACE("%p: writing PORT_INFO_%dA #%d\n", pi2a, Level, index);
5299 pi2a->pPortName = ptr;
5300 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5301 ptr, cbBuf , NULL, NULL);
5302 ptr += len;
5303 cbBuf -= len;
5304 if (Level > 1) {
5305 pi2a->pMonitorName = ptr;
5306 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5307 ptr, cbBuf, NULL, NULL);
5308 ptr += len;
5309 cbBuf -= len;
5311 pi2a->pDescription = ptr;
5312 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5313 ptr, cbBuf, NULL, NULL);
5314 ptr += len;
5315 cbBuf -= len;
5317 pi2a->fPortType = pi2w->fPortType;
5318 pi2a->Reserved = 0; /* documented: "must be zero" */
5321 /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5322 pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5323 pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5327 cleanup:
5328 if (pcbNeeded) *pcbNeeded = needed;
5329 if (pcReturned) *pcReturned = (res) ? numentries : 0;
5331 HeapFree(GetProcessHeap(), 0, nameW);
5332 HeapFree(GetProcessHeap(), 0, bufferW);
5334 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
5335 (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5337 return (res);
5341 /******************************************************************************
5342 * EnumPortsW (WINSPOOL.@)
5344 * Enumerate available Ports
5346 * PARAMS
5347 * name [I] Servername or NULL (local Computer)
5348 * level [I] Structure-Level (1 or 2)
5349 * buffer [O] PTR to Buffer that receives the Result
5350 * bufsize [I] Size of Buffer at buffer
5351 * bufneeded [O] PTR to DWORD that receives the size in Bytes used / required for buffer
5352 * bufreturned [O] PTR to DWORD that receives the number of Ports in buffer
5354 * RETURNS
5355 * Success: TRUE
5356 * Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
5360 BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5362 DWORD needed = 0;
5363 DWORD numentries = 0;
5364 BOOL res = FALSE;
5366 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
5367 cbBuf, pcbNeeded, pcReturned);
5369 if (pName && (pName[0])) {
5370 FIXME("not implemented for Server %s\n", debugstr_w(pName));
5371 SetLastError(ERROR_ACCESS_DENIED);
5372 goto emP_cleanup;
5375 /* Level is not checked in win9x */
5376 if (!Level || (Level > 2)) {
5377 WARN("level (%d) is ignored in win9x\n", Level);
5378 SetLastError(ERROR_INVALID_LEVEL);
5379 goto emP_cleanup;
5381 if (!pcbNeeded) {
5382 SetLastError(RPC_X_NULL_REF_POINTER);
5383 goto emP_cleanup;
5386 EnterCriticalSection(&monitor_handles_cs);
5387 monitor_loadall();
5389 /* Scan all local Ports */
5390 numentries = 0;
5391 needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
5393 /* we calculated the needed buffersize. now do the error-checks */
5394 if (cbBuf < needed) {
5395 monitor_unloadall();
5396 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5397 goto emP_cleanup_cs;
5399 else if (!pPorts || !pcReturned) {
5400 monitor_unloadall();
5401 SetLastError(RPC_X_NULL_REF_POINTER);
5402 goto emP_cleanup_cs;
5405 /* Fill the Buffer */
5406 needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
5407 res = TRUE;
5408 monitor_unloadall();
5410 emP_cleanup_cs:
5411 LeaveCriticalSection(&monitor_handles_cs);
5413 emP_cleanup:
5414 if (pcbNeeded) *pcbNeeded = needed;
5415 if (pcReturned) *pcReturned = (res) ? numentries : 0;
5417 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
5418 (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5420 return (res);
5423 /******************************************************************************
5424 * GetDefaultPrinterW (WINSPOOL.@)
5426 * FIXME
5427 * This function must read the value from data 'device' of key
5428 * HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
5430 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
5432 BOOL retval = TRUE;
5433 DWORD insize, len;
5434 WCHAR *buffer, *ptr;
5436 if (!namesize)
5438 SetLastError(ERROR_INVALID_PARAMETER);
5439 return FALSE;
5442 /* make the buffer big enough for the stuff from the profile/registry,
5443 * the content must fit into the local buffer to compute the correct
5444 * size even if the extern buffer is too small or not given.
5445 * (20 for ,driver,port) */
5446 insize = *namesize;
5447 len = max(100, (insize + 20));
5448 buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
5450 if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
5452 SetLastError (ERROR_FILE_NOT_FOUND);
5453 retval = FALSE;
5454 goto end;
5456 TRACE("%s\n", debugstr_w(buffer));
5458 if ((ptr = strchrW(buffer, ',')) == NULL)
5460 SetLastError(ERROR_INVALID_NAME);
5461 retval = FALSE;
5462 goto end;
5465 *ptr = 0;
5466 *namesize = strlenW(buffer) + 1;
5467 if(!name || (*namesize > insize))
5469 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5470 retval = FALSE;
5471 goto end;
5473 strcpyW(name, buffer);
5475 end:
5476 HeapFree( GetProcessHeap(), 0, buffer);
5477 return retval;
5481 /******************************************************************************
5482 * GetDefaultPrinterA (WINSPOOL.@)
5484 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
5486 BOOL retval = TRUE;
5487 DWORD insize = 0;
5488 WCHAR *bufferW = NULL;
5490 if (!namesize)
5492 SetLastError(ERROR_INVALID_PARAMETER);
5493 return FALSE;
5496 if(name && *namesize) {
5497 insize = *namesize;
5498 bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
5501 if(!GetDefaultPrinterW( bufferW, namesize)) {
5502 retval = FALSE;
5503 goto end;
5506 *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
5507 NULL, NULL);
5508 if (!*namesize)
5510 *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
5511 retval = FALSE;
5513 TRACE("0x%08x/0x%08x:%s\n", *namesize, insize, debugstr_w(bufferW));
5515 end:
5516 HeapFree( GetProcessHeap(), 0, bufferW);
5517 return retval;
5521 /******************************************************************************
5522 * SetDefaultPrinterW (WINSPOOL.204)
5524 * Set the Name of the Default Printer
5526 * PARAMS
5527 * pszPrinter [I] Name of the Printer or NULL
5529 * RETURNS
5530 * Success: True
5531 * Failure: FALSE
5533 * NOTES
5534 * When the Parameter is NULL or points to an Empty String and
5535 * a Default Printer was already present, then this Function changes nothing.
5536 * Without a Default Printer and NULL (or an Empty String) as Parameter,
5537 * the First enumerated local Printer is used.
5540 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
5543 TRACE("(%s)\n", debugstr_w(pszPrinter));
5545 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5546 return FALSE;
5549 /******************************************************************************
5550 * SetDefaultPrinterA (WINSPOOL.202)
5552 * See SetDefaultPrinterW.
5555 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
5558 TRACE("(%s)\n", debugstr_a(pszPrinter));
5560 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5561 return FALSE;
5565 /******************************************************************************
5566 * SetPrinterDataExA (WINSPOOL.@)
5568 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5569 LPCSTR pValueName, DWORD Type,
5570 LPBYTE pData, DWORD cbData)
5572 HKEY hkeyPrinter, hkeySubkey;
5573 DWORD ret;
5575 TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_a(pKeyName),
5576 debugstr_a(pValueName), Type, pData, cbData);
5578 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5579 != ERROR_SUCCESS)
5580 return ret;
5582 if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5583 != ERROR_SUCCESS) {
5584 ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
5585 RegCloseKey(hkeyPrinter);
5586 return ret;
5588 ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
5589 RegCloseKey(hkeySubkey);
5590 RegCloseKey(hkeyPrinter);
5591 return ret;
5594 /******************************************************************************
5595 * SetPrinterDataExW (WINSPOOL.@)
5597 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5598 LPCWSTR pValueName, DWORD Type,
5599 LPBYTE pData, DWORD cbData)
5601 HKEY hkeyPrinter, hkeySubkey;
5602 DWORD ret;
5604 TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_w(pKeyName),
5605 debugstr_w(pValueName), Type, pData, cbData);
5607 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5608 != ERROR_SUCCESS)
5609 return ret;
5611 if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5612 != ERROR_SUCCESS) {
5613 ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
5614 RegCloseKey(hkeyPrinter);
5615 return ret;
5617 ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
5618 RegCloseKey(hkeySubkey);
5619 RegCloseKey(hkeyPrinter);
5620 return ret;
5623 /******************************************************************************
5624 * SetPrinterDataA (WINSPOOL.@)
5626 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
5627 LPBYTE pData, DWORD cbData)
5629 return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
5630 pData, cbData);
5633 /******************************************************************************
5634 * SetPrinterDataW (WINSPOOL.@)
5636 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
5637 LPBYTE pData, DWORD cbData)
5639 return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
5640 pData, cbData);
5643 /******************************************************************************
5644 * GetPrinterDataExA (WINSPOOL.@)
5646 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5647 LPCSTR pValueName, LPDWORD pType,
5648 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5650 HKEY hkeyPrinter, hkeySubkey;
5651 DWORD ret;
5653 TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5654 debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
5655 pcbNeeded);
5657 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5658 != ERROR_SUCCESS)
5659 return ret;
5661 if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5662 != ERROR_SUCCESS) {
5663 WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
5664 RegCloseKey(hkeyPrinter);
5665 return ret;
5667 *pcbNeeded = nSize;
5668 ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5669 RegCloseKey(hkeySubkey);
5670 RegCloseKey(hkeyPrinter);
5671 return ret;
5674 /******************************************************************************
5675 * GetPrinterDataExW (WINSPOOL.@)
5677 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5678 LPCWSTR pValueName, LPDWORD pType,
5679 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5681 HKEY hkeyPrinter, hkeySubkey;
5682 DWORD ret;
5684 TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5685 debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
5686 pcbNeeded);
5688 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5689 != ERROR_SUCCESS)
5690 return ret;
5692 if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5693 != ERROR_SUCCESS) {
5694 WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
5695 RegCloseKey(hkeyPrinter);
5696 return ret;
5698 *pcbNeeded = nSize;
5699 ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5700 RegCloseKey(hkeySubkey);
5701 RegCloseKey(hkeyPrinter);
5702 return ret;
5705 /******************************************************************************
5706 * GetPrinterDataA (WINSPOOL.@)
5708 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
5709 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5711 return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
5712 pData, nSize, pcbNeeded);
5715 /******************************************************************************
5716 * GetPrinterDataW (WINSPOOL.@)
5718 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
5719 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5721 return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
5722 pData, nSize, pcbNeeded);
5725 /*******************************************************************************
5726 * EnumPrinterDataExW [WINSPOOL.@]
5728 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5729 LPBYTE pEnumValues, DWORD cbEnumValues,
5730 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5732 HKEY hkPrinter, hkSubKey;
5733 DWORD r, ret, dwIndex, cValues, cbMaxValueNameLen,
5734 cbValueNameLen, cbMaxValueLen, cbValueLen,
5735 cbBufSize, dwType;
5736 LPWSTR lpValueName;
5737 HANDLE hHeap;
5738 PBYTE lpValue;
5739 PPRINTER_ENUM_VALUESW ppev;
5741 TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
5743 if (pKeyName == NULL || *pKeyName == 0)
5744 return ERROR_INVALID_PARAMETER;
5746 ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
5747 if (ret != ERROR_SUCCESS)
5749 TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %i\n",
5750 hPrinter, ret);
5751 return ret;
5754 ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
5755 if (ret != ERROR_SUCCESS)
5757 r = RegCloseKey (hkPrinter);
5758 if (r != ERROR_SUCCESS)
5759 WARN ("RegCloseKey returned %i\n", r);
5760 TRACE ("RegOpenKeyExW (%p, %s) returned %i\n", hPrinter,
5761 debugstr_w (pKeyName), ret);
5762 return ret;
5765 ret = RegCloseKey (hkPrinter);
5766 if (ret != ERROR_SUCCESS)
5768 ERR ("RegCloseKey returned %i\n", ret);
5769 r = RegCloseKey (hkSubKey);
5770 if (r != ERROR_SUCCESS)
5771 WARN ("RegCloseKey returned %i\n", r);
5772 return ret;
5775 ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
5776 &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
5777 if (ret != ERROR_SUCCESS)
5779 r = RegCloseKey (hkSubKey);
5780 if (r != ERROR_SUCCESS)
5781 WARN ("RegCloseKey returned %i\n", r);
5782 TRACE ("RegQueryInfoKeyW (%p) returned %i\n", hkSubKey, ret);
5783 return ret;
5786 TRACE ("RegQueryInfoKeyW returned cValues = %i, cbMaxValueNameLen = %i, "
5787 "cbMaxValueLen = %i\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
5789 if (cValues == 0) /* empty key */
5791 r = RegCloseKey (hkSubKey);
5792 if (r != ERROR_SUCCESS)
5793 WARN ("RegCloseKey returned %i\n", r);
5794 *pcbEnumValues = *pnEnumValues = 0;
5795 return ERROR_SUCCESS;
5798 ++cbMaxValueNameLen; /* allow for trailing '\0' */
5800 hHeap = GetProcessHeap ();
5801 if (hHeap == NULL)
5803 ERR ("GetProcessHeap failed\n");
5804 r = RegCloseKey (hkSubKey);
5805 if (r != ERROR_SUCCESS)
5806 WARN ("RegCloseKey returned %i\n", r);
5807 return ERROR_OUTOFMEMORY;
5810 lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
5811 if (lpValueName == NULL)
5813 ERR ("Failed to allocate %i WCHARs from process heap\n", cbMaxValueNameLen);
5814 r = RegCloseKey (hkSubKey);
5815 if (r != ERROR_SUCCESS)
5816 WARN ("RegCloseKey returned %i\n", r);
5817 return ERROR_OUTOFMEMORY;
5820 lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
5821 if (lpValue == NULL)
5823 ERR ("Failed to allocate %i bytes from process heap\n", cbMaxValueLen);
5824 if (HeapFree (hHeap, 0, lpValueName) == 0)
5825 WARN ("HeapFree failed with code %i\n", GetLastError ());
5826 r = RegCloseKey (hkSubKey);
5827 if (r != ERROR_SUCCESS)
5828 WARN ("RegCloseKey returned %i\n", r);
5829 return ERROR_OUTOFMEMORY;
5832 TRACE ("pass 1: calculating buffer required for all names and values\n");
5834 cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
5836 TRACE ("%i bytes required for %i headers\n", cbBufSize, cValues);
5838 for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5840 cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5841 ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5842 NULL, NULL, lpValue, &cbValueLen);
5843 if (ret != ERROR_SUCCESS)
5845 if (HeapFree (hHeap, 0, lpValue) == 0)
5846 WARN ("HeapFree failed with code %i\n", GetLastError ());
5847 if (HeapFree (hHeap, 0, lpValueName) == 0)
5848 WARN ("HeapFree failed with code %i\n", GetLastError ());
5849 r = RegCloseKey (hkSubKey);
5850 if (r != ERROR_SUCCESS)
5851 WARN ("RegCloseKey returned %i\n", r);
5852 TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5853 return ret;
5856 TRACE ("%s [%i]: name needs %i WCHARs, data needs %i bytes\n",
5857 debugstr_w (lpValueName), dwIndex,
5858 cbValueNameLen + 1, cbValueLen);
5860 cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
5861 cbBufSize += cbValueLen;
5864 TRACE ("%i bytes required for all %i values\n", cbBufSize, cValues);
5866 *pcbEnumValues = cbBufSize;
5867 *pnEnumValues = cValues;
5869 if (cbEnumValues < cbBufSize) /* buffer too small */
5871 if (HeapFree (hHeap, 0, lpValue) == 0)
5872 WARN ("HeapFree failed with code %i\n", GetLastError ());
5873 if (HeapFree (hHeap, 0, lpValueName) == 0)
5874 WARN ("HeapFree failed with code %i\n", GetLastError ());
5875 r = RegCloseKey (hkSubKey);
5876 if (r != ERROR_SUCCESS)
5877 WARN ("RegCloseKey returned %i\n", r);
5878 TRACE ("%i byte buffer is not large enough\n", cbEnumValues);
5879 return ERROR_MORE_DATA;
5882 TRACE ("pass 2: copying all names and values to buffer\n");
5884 ppev = (PPRINTER_ENUM_VALUESW) pEnumValues; /* array of structs */
5885 pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
5887 for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5889 cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5890 ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5891 NULL, &dwType, lpValue, &cbValueLen);
5892 if (ret != ERROR_SUCCESS)
5894 if (HeapFree (hHeap, 0, lpValue) == 0)
5895 WARN ("HeapFree failed with code %i\n", GetLastError ());
5896 if (HeapFree (hHeap, 0, lpValueName) == 0)
5897 WARN ("HeapFree failed with code %i\n", GetLastError ());
5898 r = RegCloseKey (hkSubKey);
5899 if (r != ERROR_SUCCESS)
5900 WARN ("RegCloseKey returned %i\n", r);
5901 TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5902 return ret;
5905 cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
5906 memcpy (pEnumValues, lpValueName, cbValueNameLen);
5907 ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
5908 pEnumValues += cbValueNameLen;
5910 /* return # of *bytes* (including trailing \0), not # of chars */
5911 ppev[dwIndex].cbValueName = cbValueNameLen;
5913 ppev[dwIndex].dwType = dwType;
5915 memcpy (pEnumValues, lpValue, cbValueLen);
5916 ppev[dwIndex].pData = pEnumValues;
5917 pEnumValues += cbValueLen;
5919 ppev[dwIndex].cbData = cbValueLen;
5921 TRACE ("%s [%i]: copied name (%i bytes) and data (%i bytes)\n",
5922 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
5925 if (HeapFree (hHeap, 0, lpValue) == 0)
5927 ret = GetLastError ();
5928 ERR ("HeapFree failed with code %i\n", ret);
5929 if (HeapFree (hHeap, 0, lpValueName) == 0)
5930 WARN ("HeapFree failed with code %i\n", GetLastError ());
5931 r = RegCloseKey (hkSubKey);
5932 if (r != ERROR_SUCCESS)
5933 WARN ("RegCloseKey returned %i\n", r);
5934 return ret;
5937 if (HeapFree (hHeap, 0, lpValueName) == 0)
5939 ret = GetLastError ();
5940 ERR ("HeapFree failed with code %i\n", ret);
5941 r = RegCloseKey (hkSubKey);
5942 if (r != ERROR_SUCCESS)
5943 WARN ("RegCloseKey returned %i\n", r);
5944 return ret;
5947 ret = RegCloseKey (hkSubKey);
5948 if (ret != ERROR_SUCCESS)
5950 ERR ("RegCloseKey returned %i\n", ret);
5951 return ret;
5954 return ERROR_SUCCESS;
5957 /*******************************************************************************
5958 * EnumPrinterDataExA [WINSPOOL.@]
5960 * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
5961 * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers. This is
5962 * what Windows 2000 SP1 does.
5965 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5966 LPBYTE pEnumValues, DWORD cbEnumValues,
5967 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5969 INT len;
5970 LPWSTR pKeyNameW;
5971 DWORD ret, dwIndex, dwBufSize;
5972 HANDLE hHeap;
5973 LPSTR pBuffer;
5975 TRACE ("%p %s\n", hPrinter, pKeyName);
5977 if (pKeyName == NULL || *pKeyName == 0)
5978 return ERROR_INVALID_PARAMETER;
5980 len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
5981 if (len == 0)
5983 ret = GetLastError ();
5984 ERR ("MultiByteToWideChar failed with code %i\n", ret);
5985 return ret;
5988 hHeap = GetProcessHeap ();
5989 if (hHeap == NULL)
5991 ERR ("GetProcessHeap failed\n");
5992 return ERROR_OUTOFMEMORY;
5995 pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
5996 if (pKeyNameW == NULL)
5998 ERR ("Failed to allocate %i bytes from process heap\n",
5999 (LONG)(len * sizeof (WCHAR)));
6000 return ERROR_OUTOFMEMORY;
6003 if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
6005 ret = GetLastError ();
6006 ERR ("MultiByteToWideChar failed with code %i\n", ret);
6007 if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6008 WARN ("HeapFree failed with code %i\n", GetLastError ());
6009 return ret;
6012 ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
6013 pcbEnumValues, pnEnumValues);
6014 if (ret != ERROR_SUCCESS)
6016 if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6017 WARN ("HeapFree failed with code %i\n", GetLastError ());
6018 TRACE ("EnumPrinterDataExW returned %i\n", ret);
6019 return ret;
6022 if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6024 ret = GetLastError ();
6025 ERR ("HeapFree failed with code %i\n", ret);
6026 return ret;
6029 if (*pnEnumValues == 0) /* empty key */
6030 return ERROR_SUCCESS;
6032 dwBufSize = 0;
6033 for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6035 PPRINTER_ENUM_VALUESW ppev =
6036 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6038 if (dwBufSize < ppev->cbValueName)
6039 dwBufSize = ppev->cbValueName;
6041 if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
6042 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
6043 dwBufSize = ppev->cbData;
6046 TRACE ("Largest Unicode name or value is %i bytes\n", dwBufSize);
6048 pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
6049 if (pBuffer == NULL)
6051 ERR ("Failed to allocate %i bytes from process heap\n", dwBufSize);
6052 return ERROR_OUTOFMEMORY;
6055 for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6057 PPRINTER_ENUM_VALUESW ppev =
6058 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6060 len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
6061 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
6062 NULL);
6063 if (len == 0)
6065 ret = GetLastError ();
6066 ERR ("WideCharToMultiByte failed with code %i\n", ret);
6067 if (HeapFree (hHeap, 0, pBuffer) == 0)
6068 WARN ("HeapFree failed with code %i\n", GetLastError ());
6069 return ret;
6072 memcpy (ppev->pValueName, pBuffer, len);
6074 TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6076 if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
6077 ppev->dwType != REG_MULTI_SZ)
6078 continue;
6080 len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
6081 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
6082 if (len == 0)
6084 ret = GetLastError ();
6085 ERR ("WideCharToMultiByte failed with code %i\n", ret);
6086 if (HeapFree (hHeap, 0, pBuffer) == 0)
6087 WARN ("HeapFree failed with code %i\n", GetLastError ());
6088 return ret;
6091 memcpy (ppev->pData, pBuffer, len);
6093 TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6094 TRACE (" (only first string of REG_MULTI_SZ printed)\n");
6097 if (HeapFree (hHeap, 0, pBuffer) == 0)
6099 ret = GetLastError ();
6100 ERR ("HeapFree failed with code %i\n", ret);
6101 return ret;
6104 return ERROR_SUCCESS;
6107 /******************************************************************************
6108 * AbortPrinter (WINSPOOL.@)
6110 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
6112 FIXME("(%p), stub!\n", hPrinter);
6113 return TRUE;
6116 /******************************************************************************
6117 * AddPortA (WINSPOOL.@)
6119 * See AddPortW.
6122 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
6124 LPWSTR nameW = NULL;
6125 LPWSTR monitorW = NULL;
6126 DWORD len;
6127 BOOL res;
6129 TRACE("(%s, %p, %s)\n",debugstr_a(pName), hWnd, debugstr_a(pMonitorName));
6131 if (pName) {
6132 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6133 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6134 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6137 if (pMonitorName) {
6138 len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6139 monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6140 MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6142 res = AddPortW(nameW, hWnd, monitorW);
6143 HeapFree(GetProcessHeap(), 0, nameW);
6144 HeapFree(GetProcessHeap(), 0, monitorW);
6145 return res;
6148 /******************************************************************************
6149 * AddPortW (WINSPOOL.@)
6151 * Add a Port for a specific Monitor
6153 * PARAMS
6154 * pName [I] Servername or NULL (local Computer)
6155 * hWnd [I] Handle to parent Window for the Dialog-Box
6156 * pMonitorName [I] Name of the Monitor that manage the Port
6158 * RETURNS
6159 * Success: TRUE
6160 * Failure: FALSE
6163 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
6165 monitor_t * pm;
6166 monitor_t * pui;
6167 DWORD res;
6169 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
6171 if (pName && pName[0]) {
6172 SetLastError(ERROR_INVALID_PARAMETER);
6173 return FALSE;
6176 if (!pMonitorName) {
6177 SetLastError(RPC_X_NULL_REF_POINTER);
6178 return FALSE;
6181 /* an empty Monitorname is Invalid */
6182 if (!pMonitorName[0]) {
6183 SetLastError(ERROR_NOT_SUPPORTED);
6184 return FALSE;
6187 pm = monitor_load(pMonitorName, NULL);
6188 if (pm && pm->monitor && pm->monitor->pfnAddPort) {
6189 res = pm->monitor->pfnAddPort(pName, hWnd, pMonitorName);
6190 TRACE("got %d with %u\n", res, GetLastError());
6191 res = TRUE;
6193 else
6195 pui = monitor_loadui(pm);
6196 if (pui && pui->monitorUI && pui->monitorUI->pfnAddPortUI) {
6197 TRACE("use %p: %s\n", pui, debugstr_w(pui->dllname));
6198 res = pui->monitorUI->pfnAddPortUI(pName, hWnd, pMonitorName, NULL);
6199 TRACE("got %d with %u\n", res, GetLastError());
6200 res = TRUE;
6202 else
6204 FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pMonitorName),
6205 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
6207 /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6208 SetLastError(ERROR_NOT_SUPPORTED);
6209 res = FALSE;
6211 monitor_unload(pui);
6213 monitor_unload(pm);
6214 TRACE("returning %d with %u\n", res, GetLastError());
6215 return res;
6218 /******************************************************************************
6219 * AddPortExA (WINSPOOL.@)
6221 * See AddPortExW.
6224 BOOL WINAPI AddPortExA(LPSTR pName, DWORD level, LPBYTE pBuffer, LPSTR pMonitorName)
6226 PORT_INFO_2W pi2W;
6227 PORT_INFO_2A * pi2A;
6228 LPWSTR nameW = NULL;
6229 LPWSTR monitorW = NULL;
6230 DWORD len;
6231 BOOL res;
6233 pi2A = (PORT_INFO_2A *) pBuffer;
6235 TRACE("(%s, %d, %p, %s): %s\n", debugstr_a(pName), level, pBuffer,
6236 debugstr_a(pMonitorName), debugstr_a(pi2A ? pi2A->pPortName : NULL));
6238 if ((level < 1) || (level > 2)) {
6239 SetLastError(ERROR_INVALID_LEVEL);
6240 return FALSE;
6243 if (!pi2A) {
6244 SetLastError(ERROR_INVALID_PARAMETER);
6245 return FALSE;
6248 if (pName) {
6249 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6250 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6251 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6254 if (pMonitorName) {
6255 len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6256 monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6257 MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6260 ZeroMemory(&pi2W, sizeof(PORT_INFO_2W));
6262 if (pi2A->pPortName) {
6263 len = MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, NULL, 0);
6264 pi2W.pPortName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6265 MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, pi2W.pPortName, len);
6268 if (level > 1) {
6269 if (pi2A->pMonitorName) {
6270 len = MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, NULL, 0);
6271 pi2W.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6272 MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, pi2W.pMonitorName, len);
6275 if (pi2A->pDescription) {
6276 len = MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, NULL, 0);
6277 pi2W.pDescription = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6278 MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, pi2W.pDescription, len);
6280 pi2W.fPortType = pi2A->fPortType;
6281 pi2W.Reserved = pi2A->Reserved;
6284 res = AddPortExW(nameW, level, (LPBYTE) &pi2W, monitorW);
6286 HeapFree(GetProcessHeap(), 0, nameW);
6287 HeapFree(GetProcessHeap(), 0, monitorW);
6288 HeapFree(GetProcessHeap(), 0, pi2W.pPortName);
6289 HeapFree(GetProcessHeap(), 0, pi2W.pMonitorName);
6290 HeapFree(GetProcessHeap(), 0, pi2W.pDescription);
6291 return res;
6295 /******************************************************************************
6296 * AddPortExW (WINSPOOL.@)
6298 * Add a Port for a specific Monitor, without presenting a user interface
6300 * PARAMS
6301 * pName [I] Servername or NULL (local Computer)
6302 * level [I] Structure-Level (1 or 2) for pBuffer
6303 * pBuffer [I] PTR to: PORT_INFO_1 or PORT_INFO_2
6304 * pMonitorName [I] Name of the Monitor that manage the Port
6306 * RETURNS
6307 * Success: TRUE
6308 * Failure: FALSE
6311 BOOL WINAPI AddPortExW(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
6313 PORT_INFO_2W * pi2;
6314 monitor_t * pm;
6315 DWORD res = FALSE;
6317 pi2 = (PORT_INFO_2W *) pBuffer;
6319 TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
6320 debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
6321 debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
6322 debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
6325 if ((level < 1) || (level > 2)) {
6326 SetLastError(ERROR_INVALID_LEVEL);
6327 return FALSE;
6330 if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
6331 SetLastError(ERROR_INVALID_PARAMETER);
6332 return FALSE;
6335 /* load the Monitor */
6336 pm = monitor_load(pMonitorName, NULL);
6337 if (!pm) {
6338 SetLastError(ERROR_INVALID_PARAMETER);
6339 return FALSE;
6342 if (pm->monitor && pm->monitor->pfnAddPortEx) {
6343 res = pm->monitor->pfnAddPortEx(pName, level, pBuffer, pMonitorName);
6344 TRACE("got %u with %u\n", res, GetLastError());
6346 else
6348 FIXME("not implemented for %s (%p)\n", debugstr_w(pMonitorName), pm->monitor);
6350 monitor_unload(pm);
6351 return res;
6354 /******************************************************************************
6355 * AddPrinterConnectionA (WINSPOOL.@)
6357 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
6359 FIXME("%s\n", debugstr_a(pName));
6360 return FALSE;
6363 /******************************************************************************
6364 * AddPrinterConnectionW (WINSPOOL.@)
6366 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
6368 FIXME("%s\n", debugstr_w(pName));
6369 return FALSE;
6372 /******************************************************************************
6373 * AddPrinterDriverExW (WINSPOOL.@)
6375 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD Level,
6376 LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6378 FIXME("%s %d %p %d\n", debugstr_w(pName),
6379 Level, pDriverInfo, dwFileCopyFlags);
6380 SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
6381 return FALSE;
6384 /******************************************************************************
6385 * AddPrinterDriverExA (WINSPOOL.@)
6387 BOOL WINAPI AddPrinterDriverExA( LPSTR pName, DWORD Level,
6388 LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6390 FIXME("%s %d %p %d\n", debugstr_a(pName),
6391 Level, pDriverInfo, dwFileCopyFlags);
6392 SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
6393 return FALSE;
6396 /******************************************************************************
6397 * ConfigurePortA (WINSPOOL.@)
6399 * See ConfigurePortW.
6402 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
6404 LPWSTR nameW = NULL;
6405 LPWSTR portW = NULL;
6406 INT len;
6407 DWORD res;
6409 TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
6411 /* convert servername to unicode */
6412 if (pName) {
6413 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6414 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6415 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6418 /* convert portname to unicode */
6419 if (pPortName) {
6420 len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
6421 portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6422 MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
6425 res = ConfigurePortW(nameW, hWnd, portW);
6426 HeapFree(GetProcessHeap(), 0, nameW);
6427 HeapFree(GetProcessHeap(), 0, portW);
6428 return res;
6431 /******************************************************************************
6432 * ConfigurePortW (WINSPOOL.@)
6434 * Display the Configuration-Dialog for a specific Port
6436 * PARAMS
6437 * pName [I] Servername or NULL (local Computer)
6438 * hWnd [I] Handle to parent Window for the Dialog-Box
6439 * pPortName [I] Name of the Port, that should be configured
6441 * RETURNS
6442 * Success: TRUE
6443 * Failure: FALSE
6446 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
6448 monitor_t * pm;
6449 monitor_t * pui;
6450 DWORD res;
6452 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
6454 if (pName && pName[0]) {
6455 SetLastError(ERROR_INVALID_PARAMETER);
6456 return FALSE;
6459 if (!pPortName) {
6460 SetLastError(RPC_X_NULL_REF_POINTER);
6461 return FALSE;
6464 /* an empty Portname is Invalid, but can popup a Dialog */
6465 if (!pPortName[0]) {
6466 SetLastError(ERROR_NOT_SUPPORTED);
6467 return FALSE;
6470 pm = monitor_load_by_port(pPortName);
6471 if (pm && pm->monitor && pm->monitor->pfnConfigurePort) {
6472 TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
6473 res = pm->monitor->pfnConfigurePort(pName, hWnd, pPortName);
6474 TRACE("got %d with %u\n", res, GetLastError());
6476 else
6478 pui = monitor_loadui(pm);
6479 if (pui && pui->monitorUI && pui->monitorUI->pfnConfigurePortUI) {
6480 TRACE("Use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
6481 res = pui->monitorUI->pfnConfigurePortUI(pName, hWnd, pPortName);
6482 TRACE("got %d with %u\n", res, GetLastError());
6484 else
6486 FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
6487 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
6489 /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6490 SetLastError(ERROR_NOT_SUPPORTED);
6491 res = FALSE;
6493 monitor_unload(pui);
6495 monitor_unload(pm);
6497 TRACE("returning %d with %u\n", res, GetLastError());
6498 return res;
6501 /******************************************************************************
6502 * ConnectToPrinterDlg (WINSPOOL.@)
6504 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
6506 FIXME("%p %x\n", hWnd, Flags);
6507 return NULL;
6510 /******************************************************************************
6511 * DeletePrinterConnectionA (WINSPOOL.@)
6513 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
6515 FIXME("%s\n", debugstr_a(pName));
6516 return TRUE;
6519 /******************************************************************************
6520 * DeletePrinterConnectionW (WINSPOOL.@)
6522 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
6524 FIXME("%s\n", debugstr_w(pName));
6525 return TRUE;
6528 /******************************************************************************
6529 * DeletePrinterDriverExW (WINSPOOL.@)
6531 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
6532 LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6534 HKEY hkey_drivers;
6535 BOOL ret = FALSE;
6537 TRACE("%s %s %s %x %x\n", debugstr_w(pName), debugstr_w(pEnvironment),
6538 debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
6540 if(pName && pName[0])
6542 FIXME("pName = %s - unsupported\n", debugstr_w(pName));
6543 SetLastError(ERROR_INVALID_PARAMETER);
6544 return FALSE;
6547 if(dwDeleteFlag)
6549 FIXME("dwDeleteFlag = %x - unsupported\n", dwDeleteFlag);
6550 SetLastError(ERROR_INVALID_PARAMETER);
6551 return FALSE;
6554 hkey_drivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
6556 if(!hkey_drivers)
6558 ERR("Can't open drivers key\n");
6559 return FALSE;
6562 if(RegDeleteTreeW(hkey_drivers, pDriverName) == ERROR_SUCCESS)
6563 ret = TRUE;
6565 RegCloseKey(hkey_drivers);
6567 return ret;
6570 /******************************************************************************
6571 * DeletePrinterDriverExA (WINSPOOL.@)
6573 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
6574 LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6576 UNICODE_STRING NameW, EnvW, DriverW;
6577 BOOL ret;
6579 asciitounicode(&NameW, pName);
6580 asciitounicode(&EnvW, pEnvironment);
6581 asciitounicode(&DriverW, pDriverName);
6583 ret = DeletePrinterDriverExW(NameW.Buffer, EnvW.Buffer, DriverW.Buffer, dwDeleteFlag, dwVersionFlag);
6585 RtlFreeUnicodeString(&DriverW);
6586 RtlFreeUnicodeString(&EnvW);
6587 RtlFreeUnicodeString(&NameW);
6589 return ret;
6592 /******************************************************************************
6593 * DeletePrinterDataExW (WINSPOOL.@)
6595 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
6596 LPCWSTR pValueName)
6598 FIXME("%p %s %s\n", hPrinter,
6599 debugstr_w(pKeyName), debugstr_w(pValueName));
6600 return ERROR_INVALID_PARAMETER;
6603 /******************************************************************************
6604 * DeletePrinterDataExA (WINSPOOL.@)
6606 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
6607 LPCSTR pValueName)
6609 FIXME("%p %s %s\n", hPrinter,
6610 debugstr_a(pKeyName), debugstr_a(pValueName));
6611 return ERROR_INVALID_PARAMETER;
6614 /******************************************************************************
6615 * DeletePrintProcessorA (WINSPOOL.@)
6617 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
6619 FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6620 debugstr_a(pPrintProcessorName));
6621 return TRUE;
6624 /******************************************************************************
6625 * DeletePrintProcessorW (WINSPOOL.@)
6627 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
6629 FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6630 debugstr_w(pPrintProcessorName));
6631 return TRUE;
6634 /******************************************************************************
6635 * DeletePrintProvidorA (WINSPOOL.@)
6637 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
6639 FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6640 debugstr_a(pPrintProviderName));
6641 return TRUE;
6644 /******************************************************************************
6645 * DeletePrintProvidorW (WINSPOOL.@)
6647 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
6649 FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6650 debugstr_w(pPrintProviderName));
6651 return TRUE;
6654 /******************************************************************************
6655 * EnumFormsA (WINSPOOL.@)
6657 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6658 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6660 FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6661 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6662 return FALSE;
6665 /******************************************************************************
6666 * EnumFormsW (WINSPOOL.@)
6668 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6669 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6671 FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6672 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6673 return FALSE;
6676 /*****************************************************************************
6677 * EnumMonitorsA [WINSPOOL.@]
6679 * See EnumMonitorsW.
6682 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
6683 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6685 BOOL res;
6686 LPBYTE bufferW = NULL;
6687 LPWSTR nameW = NULL;
6688 DWORD needed = 0;
6689 DWORD numentries = 0;
6690 INT len;
6692 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pMonitors,
6693 cbBuf, pcbNeeded, pcReturned);
6695 /* convert servername to unicode */
6696 if (pName) {
6697 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6698 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6699 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6701 /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
6702 needed = cbBuf * sizeof(WCHAR);
6703 if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
6704 res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6706 if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
6707 if (pcbNeeded) needed = *pcbNeeded;
6708 /* HeapReAlloc return NULL, when bufferW was NULL */
6709 bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
6710 HeapAlloc(GetProcessHeap(), 0, needed);
6712 /* Try again with the large Buffer */
6713 res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6715 numentries = pcReturned ? *pcReturned : 0;
6716 needed = 0;
6718 W2k require the buffersize from EnumMonitorsW also for EnumMonitorsA.
6719 We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
6721 if (res) {
6722 /* EnumMonitorsW collected all Data. Parse them to calculate ANSI-Size */
6723 DWORD entrysize = 0;
6724 DWORD index;
6725 LPSTR ptr;
6726 LPMONITOR_INFO_2W mi2w;
6727 LPMONITOR_INFO_2A mi2a;
6729 /* MONITOR_INFO_*W and MONITOR_INFO_*A have the same size */
6730 entrysize = (Level == 1) ? sizeof(MONITOR_INFO_1A) : sizeof(MONITOR_INFO_2A);
6732 /* First pass: calculate the size for all Entries */
6733 mi2w = (LPMONITOR_INFO_2W) bufferW;
6734 mi2a = (LPMONITOR_INFO_2A) pMonitors;
6735 index = 0;
6736 while (index < numentries) {
6737 index++;
6738 needed += entrysize; /* MONITOR_INFO_?A */
6739 TRACE("%p: parsing #%d (%s)\n", mi2w, index, debugstr_w(mi2w->pName));
6741 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6742 NULL, 0, NULL, NULL);
6743 if (Level > 1) {
6744 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6745 NULL, 0, NULL, NULL);
6746 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
6747 NULL, 0, NULL, NULL);
6749 /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
6750 mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
6751 mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6754 /* check for errors and quit on failure */
6755 if (cbBuf < needed) {
6756 SetLastError(ERROR_INSUFFICIENT_BUFFER);
6757 res = FALSE;
6758 goto emA_cleanup;
6760 len = entrysize * numentries; /* room for all MONITOR_INFO_?A */
6761 ptr = (LPSTR) &pMonitors[len]; /* room for strings */
6762 cbBuf -= len ; /* free Bytes in the user-Buffer */
6763 mi2w = (LPMONITOR_INFO_2W) bufferW;
6764 mi2a = (LPMONITOR_INFO_2A) pMonitors;
6765 index = 0;
6766 /* Second Pass: Fill the User Buffer (if we have one) */
6767 while ((index < numentries) && pMonitors) {
6768 index++;
6769 TRACE("%p: writing MONITOR_INFO_%dA #%d\n", mi2a, Level, index);
6770 mi2a->pName = ptr;
6771 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6772 ptr, cbBuf , NULL, NULL);
6773 ptr += len;
6774 cbBuf -= len;
6775 if (Level > 1) {
6776 mi2a->pEnvironment = ptr;
6777 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6778 ptr, cbBuf, NULL, NULL);
6779 ptr += len;
6780 cbBuf -= len;
6782 mi2a->pDLLName = ptr;
6783 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
6784 ptr, cbBuf, NULL, NULL);
6785 ptr += len;
6786 cbBuf -= len;
6788 /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
6789 mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
6790 mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6793 emA_cleanup:
6794 if (pcbNeeded) *pcbNeeded = needed;
6795 if (pcReturned) *pcReturned = (res) ? numentries : 0;
6797 HeapFree(GetProcessHeap(), 0, nameW);
6798 HeapFree(GetProcessHeap(), 0, bufferW);
6800 TRACE("returning %d with %d (%d byte for %d entries)\n",
6801 (res), GetLastError(), needed, numentries);
6803 return (res);
6807 /*****************************************************************************
6808 * EnumMonitorsW [WINSPOOL.@]
6810 * Enumerate available Port-Monitors
6812 * PARAMS
6813 * pName [I] Servername or NULL (local Computer)
6814 * Level [I] Structure-Level (1:Win9x+NT or 2:NT only)
6815 * pMonitors [O] PTR to Buffer that receives the Result
6816 * cbBuf [I] Size of Buffer at pMonitors
6817 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
6818 * pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
6820 * RETURNS
6821 * Success: TRUE
6822 * Failure: FALSE and in pcbNeeded the Bytes required for buffer, if cbBuf is too small
6824 * NOTES
6825 * Windows reads the Registry once and cache the Results.
6827 *| Language-Monitors are also installed in the same Registry-Location but
6828 *| they are filtered in Windows (not returned by EnumMonitors).
6829 *| We do no filtering to simplify our Code.
6832 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
6833 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6835 DWORD needed = 0;
6836 DWORD numentries = 0;
6837 BOOL res = FALSE;
6839 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
6840 cbBuf, pcbNeeded, pcReturned);
6842 if (pName && (lstrlenW(pName))) {
6843 FIXME("for Server %s not implemented\n", debugstr_w(pName));
6844 SetLastError(ERROR_ACCESS_DENIED);
6845 goto emW_cleanup;
6848 /* Level is not checked in win9x */
6849 if (!Level || (Level > 2)) {
6850 WARN("level (%d) is ignored in win9x\n", Level);
6851 SetLastError(ERROR_INVALID_LEVEL);
6852 goto emW_cleanup;
6854 if (!pcbNeeded) {
6855 SetLastError(RPC_X_NULL_REF_POINTER);
6856 goto emW_cleanup;
6859 /* Scan all Monitor-Keys */
6860 numentries = 0;
6861 needed = get_local_monitors(Level, NULL, 0, &numentries);
6863 /* we calculated the needed buffersize. now do the error-checks */
6864 if (cbBuf < needed) {
6865 SetLastError(ERROR_INSUFFICIENT_BUFFER);
6866 goto emW_cleanup;
6868 else if (!pMonitors || !pcReturned) {
6869 SetLastError(RPC_X_NULL_REF_POINTER);
6870 goto emW_cleanup;
6873 /* fill the Buffer with the Monitor-Keys */
6874 needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
6875 res = TRUE;
6877 emW_cleanup:
6878 if (pcbNeeded) *pcbNeeded = needed;
6879 if (pcReturned) *pcReturned = numentries;
6881 TRACE("returning %d with %d (%d byte for %d entries)\n",
6882 res, GetLastError(), needed, numentries);
6884 return (res);
6887 /******************************************************************************
6888 * XcvDataW (WINSPOOL.@)
6890 * Execute commands in the Printmonitor DLL
6892 * PARAMS
6893 * hXcv [i] Handle from OpenPrinter (with XcvMonitor or XcvPort)
6894 * pszDataName [i] Name of the command to execute
6895 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
6896 * cbInputData [i] Size in Bytes of Buffer at pInputData
6897 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
6898 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
6899 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
6900 * pdwStatus [o] PTR to receive the win32 error code from the Printmonitor DLL
6902 * RETURNS
6903 * Success: TRUE
6904 * Failure: FALSE
6906 * NOTES
6907 * Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
6908 * The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
6910 * Minimal List of commands, that a Printmonitor DLL should support:
6912 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
6913 *| "AddPort" : Add a Port
6914 *| "DeletePort": Delete a Port
6916 * Many Printmonitors support additional commands. Examples for localspl.dll:
6917 * "GetDefaultCommConfig", "SetDefaultCommConfig",
6918 * "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
6921 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
6922 DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
6923 PDWORD pcbOutputNeeded, PDWORD pdwStatus)
6925 opened_printer_t *printer;
6927 TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
6928 pInputData, cbInputData, pOutputData,
6929 cbOutputData, pcbOutputNeeded, pdwStatus);
6931 printer = get_opened_printer(hXcv);
6932 if (!printer || (!printer->hXcv)) {
6933 SetLastError(ERROR_INVALID_HANDLE);
6934 return FALSE;
6937 if (!pcbOutputNeeded) {
6938 SetLastError(ERROR_INVALID_PARAMETER);
6939 return FALSE;
6942 if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
6943 SetLastError(RPC_X_NULL_REF_POINTER);
6944 return FALSE;
6947 *pcbOutputNeeded = 0;
6949 *pdwStatus = printer->pm->monitor->pfnXcvDataPort(printer->hXcv, pszDataName,
6950 pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
6952 return TRUE;
6955 /*****************************************************************************
6956 * EnumPrinterDataA [WINSPOOL.@]
6959 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
6960 DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
6961 DWORD cbData, LPDWORD pcbData )
6963 FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
6964 cbValueName, pcbValueName, pType, pData, cbData, pcbData);
6965 return ERROR_NO_MORE_ITEMS;
6968 /*****************************************************************************
6969 * EnumPrinterDataW [WINSPOOL.@]
6972 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
6973 DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
6974 DWORD cbData, LPDWORD pcbData )
6976 FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
6977 cbValueName, pcbValueName, pType, pData, cbData, pcbData);
6978 return ERROR_NO_MORE_ITEMS;
6981 /*****************************************************************************
6982 * EnumPrintProcessorDatatypesA [WINSPOOL.@]
6985 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
6986 DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
6987 LPDWORD pcbNeeded, LPDWORD pcReturned)
6989 FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_a(pName),
6990 debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
6991 pcbNeeded, pcReturned);
6992 return FALSE;
6995 /*****************************************************************************
6996 * EnumPrintProcessorDatatypesW [WINSPOOL.@]
6999 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
7000 DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7001 LPDWORD pcbNeeded, LPDWORD pcReturned)
7003 FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7004 debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
7005 pcbNeeded, pcReturned);
7006 return FALSE;
7009 /*****************************************************************************
7010 * EnumPrintProcessorsA [WINSPOOL.@]
7013 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
7014 LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
7016 FIXME("Stub: %s %s %d %p %d %p %p\n", pName, pEnvironment, Level,
7017 pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
7018 return FALSE;
7021 /*****************************************************************************
7022 * EnumPrintProcessorsW [WINSPOOL.@]
7025 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
7026 LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
7028 FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7029 debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
7030 cbBuf, pcbNeeded, pcbReturned);
7031 return FALSE;
7034 /*****************************************************************************
7035 * ExtDeviceMode [WINSPOOL.@]
7038 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
7039 LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
7040 DWORD fMode)
7042 FIXME("Stub: %p %p %p %s %s %p %s %x\n", hWnd, hInst, pDevModeOutput,
7043 debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
7044 debugstr_a(pProfile), fMode);
7045 return -1;
7048 /*****************************************************************************
7049 * FindClosePrinterChangeNotification [WINSPOOL.@]
7052 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
7054 FIXME("Stub: %p\n", hChange);
7055 return TRUE;
7058 /*****************************************************************************
7059 * FindFirstPrinterChangeNotification [WINSPOOL.@]
7062 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
7063 DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
7065 FIXME("Stub: %p %x %x %p\n",
7066 hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
7067 return INVALID_HANDLE_VALUE;
7070 /*****************************************************************************
7071 * FindNextPrinterChangeNotification [WINSPOOL.@]
7074 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
7075 LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
7077 FIXME("Stub: %p %p %p %p\n",
7078 hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
7079 return FALSE;
7082 /*****************************************************************************
7083 * FreePrinterNotifyInfo [WINSPOOL.@]
7086 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
7088 FIXME("Stub: %p\n", pPrinterNotifyInfo);
7089 return TRUE;
7092 /*****************************************************************************
7093 * string_to_buf
7095 * Copies a unicode string into a buffer. The buffer will either contain unicode or
7096 * ansi depending on the unicode parameter.
7098 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
7100 if(!str)
7102 *size = 0;
7103 return TRUE;
7106 if(unicode)
7108 *size = (strlenW(str) + 1) * sizeof(WCHAR);
7109 if(*size <= cb)
7111 memcpy(ptr, str, *size);
7112 return TRUE;
7114 return FALSE;
7116 else
7118 *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
7119 if(*size <= cb)
7121 WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
7122 return TRUE;
7124 return FALSE;
7128 /*****************************************************************************
7129 * get_job_info_1
7131 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
7132 LPDWORD pcbNeeded, BOOL unicode)
7134 DWORD size, left = cbBuf;
7135 BOOL space = (cbBuf > 0);
7136 LPBYTE ptr = buf;
7138 *pcbNeeded = 0;
7140 if(space)
7142 ji1->JobId = job->job_id;
7145 string_to_buf(job->document_title, ptr, left, &size, unicode);
7146 if(space && size <= left)
7148 ji1->pDocument = (LPWSTR)ptr;
7149 ptr += size;
7150 left -= size;
7152 else
7153 space = FALSE;
7154 *pcbNeeded += size;
7156 return space;
7159 /*****************************************************************************
7160 * get_job_info_2
7162 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
7163 LPDWORD pcbNeeded, BOOL unicode)
7165 DWORD size, left = cbBuf;
7166 BOOL space = (cbBuf > 0);
7167 LPBYTE ptr = buf;
7169 *pcbNeeded = 0;
7171 if(space)
7173 ji2->JobId = job->job_id;
7176 string_to_buf(job->document_title, ptr, left, &size, unicode);
7177 if(space && size <= left)
7179 ji2->pDocument = (LPWSTR)ptr;
7180 ptr += size;
7181 left -= size;
7183 else
7184 space = FALSE;
7185 *pcbNeeded += size;
7187 return space;
7190 /*****************************************************************************
7191 * get_job_info
7193 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7194 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
7196 BOOL ret = FALSE;
7197 DWORD needed = 0, size;
7198 job_t *job;
7199 LPBYTE ptr = pJob;
7201 TRACE("%p %d %d %p %d %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
7203 EnterCriticalSection(&printer_handles_cs);
7204 job = get_job(hPrinter, JobId);
7205 if(!job)
7206 goto end;
7208 switch(Level)
7210 case 1:
7211 size = sizeof(JOB_INFO_1W);
7212 if(cbBuf >= size)
7214 cbBuf -= size;
7215 ptr += size;
7216 memset(pJob, 0, size);
7218 else
7219 cbBuf = 0;
7220 ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
7221 needed += size;
7222 break;
7224 case 2:
7225 size = sizeof(JOB_INFO_2W);
7226 if(cbBuf >= size)
7228 cbBuf -= size;
7229 ptr += size;
7230 memset(pJob, 0, size);
7232 else
7233 cbBuf = 0;
7234 ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
7235 needed += size;
7236 break;
7238 case 3:
7239 size = sizeof(JOB_INFO_3);
7240 if(cbBuf >= size)
7242 cbBuf -= size;
7243 memset(pJob, 0, size);
7244 ret = TRUE;
7246 else
7247 cbBuf = 0;
7248 needed = size;
7249 break;
7251 default:
7252 SetLastError(ERROR_INVALID_LEVEL);
7253 goto end;
7255 if(pcbNeeded)
7256 *pcbNeeded = needed;
7257 end:
7258 LeaveCriticalSection(&printer_handles_cs);
7259 return ret;
7262 /*****************************************************************************
7263 * GetJobA [WINSPOOL.@]
7266 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7267 DWORD cbBuf, LPDWORD pcbNeeded)
7269 return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
7272 /*****************************************************************************
7273 * GetJobW [WINSPOOL.@]
7276 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7277 DWORD cbBuf, LPDWORD pcbNeeded)
7279 return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
7282 /*****************************************************************************
7283 * schedule_lpr
7285 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
7287 char *unixname, *queue, *cmd;
7288 char fmt[] = "lpr -P%s %s";
7289 DWORD len;
7291 if(!(unixname = wine_get_unix_file_name(filename)))
7292 return FALSE;
7294 len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7295 queue = HeapAlloc(GetProcessHeap(), 0, len);
7296 WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7298 cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
7299 sprintf(cmd, fmt, queue, unixname);
7301 TRACE("printing with: %s\n", cmd);
7302 system(cmd);
7304 HeapFree(GetProcessHeap(), 0, cmd);
7305 HeapFree(GetProcessHeap(), 0, queue);
7306 HeapFree(GetProcessHeap(), 0, unixname);
7307 return TRUE;
7310 /*****************************************************************************
7311 * schedule_cups
7313 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
7315 #ifdef SONAME_LIBCUPS
7316 if(pcupsPrintFile)
7318 char *unixname, *queue, *doc_titleA;
7319 DWORD len;
7320 BOOL ret;
7322 if(!(unixname = wine_get_unix_file_name(filename)))
7323 return FALSE;
7325 len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7326 queue = HeapAlloc(GetProcessHeap(), 0, len);
7327 WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7329 len = WideCharToMultiByte(CP_ACP, 0, document_title, -1, NULL, 0, NULL, NULL);
7330 doc_titleA = HeapAlloc(GetProcessHeap(), 0, len);
7331 WideCharToMultiByte(CP_ACP, 0, document_title, -1, doc_titleA, len, NULL, NULL);
7333 TRACE("printing via cups\n");
7334 ret = pcupsPrintFile(queue, unixname, doc_titleA, 0, NULL);
7335 HeapFree(GetProcessHeap(), 0, doc_titleA);
7336 HeapFree(GetProcessHeap(), 0, queue);
7337 HeapFree(GetProcessHeap(), 0, unixname);
7338 return ret;
7340 else
7341 #endif
7343 return schedule_lpr(printer_name, filename);
7347 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
7349 LPWSTR filename;
7351 switch(msg)
7353 case WM_INITDIALOG:
7354 SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
7355 return TRUE;
7357 case WM_COMMAND:
7358 if(HIWORD(wparam) == BN_CLICKED)
7360 if(LOWORD(wparam) == IDOK)
7362 HANDLE hf;
7363 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
7364 LPWSTR *output;
7366 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
7367 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
7369 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
7371 WCHAR caption[200], message[200];
7372 int mb_ret;
7374 LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7375 LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
7376 mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
7377 if(mb_ret == IDCANCEL)
7379 HeapFree(GetProcessHeap(), 0, filename);
7380 return TRUE;
7383 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
7384 if(hf == INVALID_HANDLE_VALUE)
7386 WCHAR caption[200], message[200];
7388 LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7389 LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
7390 MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
7391 HeapFree(GetProcessHeap(), 0, filename);
7392 return TRUE;
7394 CloseHandle(hf);
7395 DeleteFileW(filename);
7396 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
7397 *output = filename;
7398 EndDialog(hwnd, IDOK);
7399 return TRUE;
7401 if(LOWORD(wparam) == IDCANCEL)
7403 EndDialog(hwnd, IDCANCEL);
7404 return TRUE;
7407 return FALSE;
7409 return FALSE;
7412 /*****************************************************************************
7413 * get_filename
7415 static BOOL get_filename(LPWSTR *filename)
7417 return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
7418 file_dlg_proc, (LPARAM)filename) == IDOK;
7421 /*****************************************************************************
7422 * schedule_file
7424 static BOOL schedule_file(LPCWSTR filename)
7426 LPWSTR output = NULL;
7428 if(get_filename(&output))
7430 TRACE("copy to %s\n", debugstr_w(output));
7431 CopyFileW(filename, output, FALSE);
7432 HeapFree(GetProcessHeap(), 0, output);
7433 return TRUE;
7435 return FALSE;
7438 /*****************************************************************************
7439 * schedule_pipe
7441 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
7443 #ifdef HAVE_FORK
7444 char *unixname, *cmdA;
7445 DWORD len;
7446 int fds[2] = {-1, -1}, file_fd = -1, no_read;
7447 BOOL ret = FALSE;
7448 char buf[1024];
7450 if(!(unixname = wine_get_unix_file_name(filename)))
7451 return FALSE;
7453 len = WideCharToMultiByte(CP_ACP, 0, cmd, -1, NULL, 0, NULL, NULL);
7454 cmdA = HeapAlloc(GetProcessHeap(), 0, len);
7455 WideCharToMultiByte(CP_ACP, 0, cmd, -1, cmdA, len, NULL, NULL);
7457 TRACE("printing with: %s\n", cmdA);
7459 if((file_fd = open(unixname, O_RDONLY)) == -1)
7460 goto end;
7462 if (pipe(fds))
7464 ERR("pipe() failed!\n");
7465 goto end;
7468 if (fork() == 0)
7470 close(0);
7471 dup2(fds[0], 0);
7472 close(fds[1]);
7474 /* reset signals that we previously set to SIG_IGN */
7475 signal(SIGPIPE, SIG_DFL);
7476 signal(SIGCHLD, SIG_DFL);
7478 execl("/bin/sh", "/bin/sh", "-c", cmdA, (char*)0);
7479 _exit(1);
7482 while((no_read = read(file_fd, buf, sizeof(buf))) > 0)
7483 write(fds[1], buf, no_read);
7485 ret = TRUE;
7487 end:
7488 if(file_fd != -1) close(file_fd);
7489 if(fds[0] != -1) close(fds[0]);
7490 if(fds[1] != -1) close(fds[1]);
7492 HeapFree(GetProcessHeap(), 0, cmdA);
7493 HeapFree(GetProcessHeap(), 0, unixname);
7494 return ret;
7495 #else
7496 return FALSE;
7497 #endif
7500 /*****************************************************************************
7501 * schedule_unixfile
7503 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
7505 int in_fd, out_fd, no_read;
7506 char buf[1024];
7507 BOOL ret = FALSE;
7508 char *unixname, *outputA;
7509 DWORD len;
7511 if(!(unixname = wine_get_unix_file_name(filename)))
7512 return FALSE;
7514 len = WideCharToMultiByte(CP_ACP, 0, output, -1, NULL, 0, NULL, NULL);
7515 outputA = HeapAlloc(GetProcessHeap(), 0, len);
7516 WideCharToMultiByte(CP_ACP, 0, output, -1, outputA, len, NULL, NULL);
7518 out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
7519 in_fd = open(unixname, O_RDONLY);
7520 if(out_fd == -1 || in_fd == -1)
7521 goto end;
7523 while((no_read = read(in_fd, buf, sizeof(buf))) > 0)
7524 write(out_fd, buf, no_read);
7526 ret = TRUE;
7527 end:
7528 if(in_fd != -1) close(in_fd);
7529 if(out_fd != -1) close(out_fd);
7530 HeapFree(GetProcessHeap(), 0, outputA);
7531 HeapFree(GetProcessHeap(), 0, unixname);
7532 return ret;
7535 /*****************************************************************************
7536 * ScheduleJob [WINSPOOL.@]
7539 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
7541 opened_printer_t *printer;
7542 BOOL ret = FALSE;
7543 struct list *cursor, *cursor2;
7545 TRACE("(%p, %x)\n", hPrinter, dwJobID);
7546 EnterCriticalSection(&printer_handles_cs);
7547 printer = get_opened_printer(hPrinter);
7548 if(!printer)
7549 goto end;
7551 LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
7553 job_t *job = LIST_ENTRY(cursor, job_t, entry);
7554 HANDLE hf;
7556 if(job->job_id != dwJobID) continue;
7558 hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7559 if(hf != INVALID_HANDLE_VALUE)
7561 PRINTER_INFO_5W *pi5;
7562 DWORD needed;
7563 HKEY hkey;
7564 WCHAR output[1024];
7565 static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
7566 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
7568 GetPrinterW(hPrinter, 5, NULL, 0, &needed);
7569 pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
7570 GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
7571 TRACE("need to schedule job %d filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
7572 debugstr_w(pi5->pPortName));
7574 output[0] = 0;
7576 /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
7577 if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
7579 DWORD type, count = sizeof(output);
7580 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
7581 RegCloseKey(hkey);
7583 if(output[0] == '|')
7585 schedule_pipe(output + 1, job->filename);
7587 else if(output[0])
7589 schedule_unixfile(output, job->filename);
7591 else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
7593 schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
7595 else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
7597 schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
7599 else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
7601 schedule_file(job->filename);
7603 else
7605 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
7607 HeapFree(GetProcessHeap(), 0, pi5);
7608 CloseHandle(hf);
7609 DeleteFileW(job->filename);
7611 list_remove(cursor);
7612 HeapFree(GetProcessHeap(), 0, job->document_title);
7613 HeapFree(GetProcessHeap(), 0, job->filename);
7614 HeapFree(GetProcessHeap(), 0, job);
7615 ret = TRUE;
7616 break;
7618 end:
7619 LeaveCriticalSection(&printer_handles_cs);
7620 return ret;
7623 /*****************************************************************************
7624 * StartDocDlgA [WINSPOOL.@]
7626 LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
7628 UNICODE_STRING usBuffer;
7629 DOCINFOW docW;
7630 LPWSTR retW;
7631 LPWSTR docnameW = NULL, outputW = NULL, datatypeW = NULL;
7632 LPSTR ret = NULL;
7634 docW.cbSize = sizeof(docW);
7635 if (doc->lpszDocName)
7637 docnameW = asciitounicode(&usBuffer, doc->lpszDocName);
7638 if (!(docW.lpszDocName = docnameW)) return NULL;
7640 if (doc->lpszOutput)
7642 outputW = asciitounicode(&usBuffer, doc->lpszOutput);
7643 if (!(docW.lpszOutput = outputW)) return NULL;
7645 if (doc->lpszDatatype)
7647 datatypeW = asciitounicode(&usBuffer, doc->lpszDatatype);
7648 if (!(docW.lpszDatatype = datatypeW)) return NULL;
7650 docW.fwType = doc->fwType;
7652 retW = StartDocDlgW(hPrinter, &docW);
7654 if(retW)
7656 DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
7657 ret = HeapAlloc(GetProcessHeap(), 0, len);
7658 WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
7659 HeapFree(GetProcessHeap(), 0, retW);
7662 HeapFree(GetProcessHeap(), 0, datatypeW);
7663 HeapFree(GetProcessHeap(), 0, outputW);
7664 HeapFree(GetProcessHeap(), 0, docnameW);
7666 return ret;
7669 /*****************************************************************************
7670 * StartDocDlgW [WINSPOOL.@]
7672 * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
7673 * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
7674 * port is "FILE:". Also returns the full path if passed a relative path.
7676 * The caller should free the returned string from the process heap.
7678 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
7680 LPWSTR ret = NULL;
7681 DWORD len, attr;
7683 if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
7685 PRINTER_INFO_5W *pi5;
7686 GetPrinterW(hPrinter, 5, NULL, 0, &len);
7687 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
7688 return NULL;
7689 pi5 = HeapAlloc(GetProcessHeap(), 0, len);
7690 GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
7691 if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
7693 HeapFree(GetProcessHeap(), 0, pi5);
7694 return NULL;
7696 HeapFree(GetProcessHeap(), 0, pi5);
7699 if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
7701 LPWSTR name;
7703 if (get_filename(&name))
7705 if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
7707 HeapFree(GetProcessHeap(), 0, name);
7708 return NULL;
7710 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7711 GetFullPathNameW(name, len, ret, NULL);
7712 HeapFree(GetProcessHeap(), 0, name);
7714 return ret;
7717 if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
7718 return NULL;
7720 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7721 GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
7723 attr = GetFileAttributesW(ret);
7724 if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
7726 HeapFree(GetProcessHeap(), 0, ret);
7727 ret = NULL;
7729 return ret;