winspool: Implement AddPrinterDriverExW.
[wine/wine64.git] / dlls / winspool.drv / info.c
blob6f09e7f012c9f93386841494a911b06c2e76b342
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 WCHAR src[MAX_PATH+MAX_PATH];
90 WCHAR dst[MAX_PATH+MAX_PATH];
91 DWORD srclen;
92 DWORD dstlen;
93 DWORD copyflags;
94 } apd_data_t;
96 typedef struct {
97 struct list entry;
98 LPWSTR name;
99 LPWSTR dllname;
100 PMONITORUI monitorUI;
101 LPMONITOR monitor;
102 HMODULE hdll;
103 DWORD refcount;
104 DWORD dwMonitorSize;
105 } monitor_t;
107 typedef struct {
108 DWORD job_id;
109 HANDLE hf;
110 } started_doc_t;
112 typedef struct {
113 struct list jobs;
114 LONG ref;
115 } jobqueue_t;
117 typedef struct {
118 LPWSTR name;
119 LPWSTR printername;
120 monitor_t *pm;
121 HANDLE hXcv;
122 jobqueue_t *queue;
123 started_doc_t *doc;
124 } opened_printer_t;
126 typedef struct {
127 struct list entry;
128 DWORD job_id;
129 WCHAR *filename;
130 WCHAR *document_title;
131 } job_t;
134 typedef struct {
135 LPCWSTR envname;
136 LPCWSTR subdir;
137 DWORD driverversion;
138 LPCWSTR versionregpath;
139 LPCWSTR versionsubdir;
140 } printenv_t;
142 /* ############################### */
144 static struct list monitor_handles = LIST_INIT( monitor_handles );
145 static monitor_t * pm_localport;
147 static opened_printer_t **printer_handles;
148 static int nb_printer_handles;
149 static LONG next_job_id = 1;
151 static DWORD (WINAPI *GDI_CallDeviceCapabilities16)( LPCSTR lpszDevice, LPCSTR lpszPort,
152 WORD fwCapability, LPSTR lpszOutput,
153 LPDEVMODEA lpdm );
154 static INT (WINAPI *GDI_CallExtDeviceMode16)( HWND hwnd, LPDEVMODEA lpdmOutput,
155 LPSTR lpszDevice, LPSTR lpszPort,
156 LPDEVMODEA lpdmInput, LPSTR lpszProfile,
157 DWORD fwMode );
159 static const WCHAR DriversW[] = { 'S','y','s','t','e','m','\\',
160 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
161 'c','o','n','t','r','o','l','\\',
162 'P','r','i','n','t','\\',
163 'E','n','v','i','r','o','n','m','e','n','t','s','\\',
164 '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
166 static const WCHAR MonitorsW[] = { 'S','y','s','t','e','m','\\',
167 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
168 'C','o','n','t','r','o','l','\\',
169 'P','r','i','n','t','\\',
170 'M','o','n','i','t','o','r','s','\\',0};
172 static const WCHAR PrintersW[] = {'S','y','s','t','e','m','\\',
173 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
174 'C','o','n','t','r','o','l','\\',
175 'P','r','i','n','t','\\',
176 'P','r','i','n','t','e','r','s',0};
178 static const WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
180 static const WCHAR user_default_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
181 'M','i','c','r','o','s','o','f','t','\\',
182 'W','i','n','d','o','w','s',' ','N','T','\\',
183 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
184 'W','i','n','d','o','w','s',0};
186 static const WCHAR user_printers_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
187 'M','i','c','r','o','s','o','f','t','\\',
188 'W','i','n','d','o','w','s',' ','N','T','\\',
189 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
190 'D','e','v','i','c','e','s',0};
192 static const WCHAR WinNT_CV_PortsW[] = {'S','o','f','t','w','a','r','e','\\',
193 'M','i','c','r','o','s','o','f','t','\\',
194 'W','i','n','d','o','w','s',' ','N','T','\\',
195 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
196 'P','o','r','t','s',0};
198 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
199 static const WCHAR envname_win40W[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
200 static const WCHAR envname_x86W[] = {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
201 static const WCHAR subdir_win40W[] = {'w','i','n','4','0',0};
202 static const WCHAR subdir_x86W[] = {'w','3','2','x','8','6',0};
203 static const WCHAR Version0_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','0',0};
204 static const WCHAR Version0_SubdirW[] = {'\\','0',0};
205 static const WCHAR Version3_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
206 static const WCHAR Version3_SubdirW[] = {'\\','3',0};
208 static const WCHAR spooldriversW[] = {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\',0};
209 static const WCHAR spoolprtprocsW[] = {'\\','s','p','o','o','l','\\','p','r','t','p','r','o','c','s','\\',0};
211 static const WCHAR backslashW[] = {'\\',0};
212 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
213 'i','o','n',' ','F','i','l','e',0};
214 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
215 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
216 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0};
217 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
218 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
219 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
220 static const WCHAR HardwareIDW[] = {'H','a','r','d','w','a','r','e','I','D',0};
221 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
222 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
223 static const WCHAR ManufacturerW[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0};
224 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
225 static const WCHAR MonitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
226 static const WCHAR NameW[] = {'N','a','m','e',0};
227 static const WCHAR OEM_UrlW[] = {'O','E','M',' ','U','r','l',0};
228 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
229 static const WCHAR PortW[] = {'P','o','r','t',0};
230 static const WCHAR bs_Ports_bsW[] = {'\\','P','o','r','t','s','\\',0};
231 static const WCHAR Previous_NamesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
232 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e','s','s','o','r',0};
233 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i','v','e','r',0};
234 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i','v','e','r','D','a','t','a',0};
235 static const WCHAR ProviderW[] = {'P','r','o','v','i','d','e','r',0};
236 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F','i','l','e',0};
237 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
238 static const WCHAR VersionW[] = {'V','e','r','s','i','o','n',0};
239 static const WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
240 static const WCHAR deviceW[] = {'d','e','v','i','c','e',0};
241 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
242 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
243 static const WCHAR emptyStringW[] = {0};
244 static const WCHAR XcvMonitorW[] = {',','X','c','v','M','o','n','i','t','o','r',' ',0};
245 static const WCHAR XcvPortW[] = {',','X','c','v','P','o','r','t',' ',0};
247 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
249 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
250 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
251 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
253 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
254 'D','o','c','u','m','e','n','t',0};
256 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
257 sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
258 sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
259 0, sizeof(DRIVER_INFO_8W)};
261 /******************************************************************
262 * validate the user-supplied printing-environment [internal]
264 * PARAMS
265 * env [I] PTR to Environment-String or NULL
267 * RETURNS
268 * Failure: NULL
269 * Success: PTR to printenv_t
271 * NOTES
272 * An empty string is handled the same way as NULL.
273 * SetLastEror(ERROR_INVALID_ENVIRONMENT) is called on Failure
277 static const printenv_t * validate_envW(LPCWSTR env)
279 static const printenv_t env_x86 = {envname_x86W, subdir_x86W,
280 3, Version3_RegPathW, Version3_SubdirW};
281 static const printenv_t env_win40 = {envname_win40W, subdir_win40W,
282 0, Version0_RegPathW, Version0_SubdirW};
284 static const printenv_t * const all_printenv[]={&env_x86, &env_win40};
286 const printenv_t *result = NULL;
287 unsigned int i;
289 TRACE("testing %s\n", debugstr_w(env));
290 if (env && env[0])
292 for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
294 if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
296 result = all_printenv[i];
297 break;
301 if (result == NULL) {
302 FIXME("unsupported Environment: %s\n", debugstr_w(env));
303 SetLastError(ERROR_INVALID_ENVIRONMENT);
305 /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
307 else
309 result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
311 TRACE("using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
313 return result;
317 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
318 if passed a NULL string. This returns NULLs to the result.
320 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
322 if ( (src) )
324 RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
325 return usBufferPtr->Buffer;
327 usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
328 return NULL;
331 static LPWSTR strdupW(LPCWSTR p)
333 LPWSTR ret;
334 DWORD len;
336 if(!p) return NULL;
337 len = (strlenW(p) + 1) * sizeof(WCHAR);
338 ret = HeapAlloc(GetProcessHeap(), 0, len);
339 memcpy(ret, p, len);
340 return ret;
343 static LPSTR strdupWtoA( LPCWSTR str )
345 LPSTR ret;
346 INT len;
348 if (!str) return NULL;
349 len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
350 ret = HeapAlloc( GetProcessHeap(), 0, len );
351 if(ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
352 return ret;
355 /******************************************************************
356 * apd_copyfile [internal]
358 * Copy a file from the driverdirectory to the versioned directory
360 * RETURNS
361 * Success: TRUE
362 * Failure: FALSE
365 static BOOL apd_copyfile(LPWSTR filename, apd_data_t *apd)
367 LPWSTR ptr;
368 LPWSTR srcname;
369 DWORD res;
371 apd->src[apd->srclen] = '\0';
372 apd->dst[apd->dstlen] = '\0';
374 if (!filename || !filename[0]) {
375 /* nothing to copy */
376 return TRUE;
379 ptr = strrchrW(filename, '\\');
380 if (ptr) {
381 ptr++;
383 else
385 ptr = filename;
388 if (apd->copyflags & APD_COPY_FROM_DIRECTORY) {
389 /* we have an absolute Path */
390 srcname = filename;
392 else
394 srcname = apd->src;
395 lstrcatW(srcname, ptr);
397 lstrcatW(apd->dst, ptr);
399 TRACE("%s => %s\n", debugstr_w(filename), debugstr_w(apd->dst));
401 /* FIXME: handle APD_COPY_NEW_FILES */
402 res = CopyFileW(srcname, apd->dst, FALSE);
403 TRACE("got %u with %u\n", res, GetLastError());
405 /* FIXME: install of wineps.drv must be fixed, before we return the real result
406 return res;
408 return TRUE;
412 /******************************************************************
413 * Return the number of bytes for an multi_sz string.
414 * The result includes all \0s
415 * (specifically the extra \0, that is needed as multi_sz terminator).
417 static int multi_sz_lenW(const WCHAR *str)
419 const WCHAR *ptr = str;
420 if(!str) return 0;
423 ptr += lstrlenW(ptr) + 1;
424 } while(*ptr);
426 return (ptr - str + 1) * sizeof(WCHAR);
429 /* ################################ */
431 static int multi_sz_lenA(const char *str)
433 const char *ptr = str;
434 if(!str) return 0;
437 ptr += lstrlenA(ptr) + 1;
438 } while(*ptr);
440 return ptr - str + 1;
443 static void
444 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name, BOOL force) {
445 char qbuf[200];
447 /* If forcing, or no profile string entry for device yet, set the entry
449 * The always change entry if not WINEPS yet is discussable.
451 if (force ||
452 !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf)) ||
453 !strcmp(qbuf,"*") ||
454 !strstr(qbuf,"WINEPS.DRV")
456 char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
457 HKEY hkey;
459 sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
460 WriteProfileStringA("windows","device",buf);
461 if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
462 RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
463 RegCloseKey(hkey);
465 HeapFree(GetProcessHeap(),0,buf);
469 static BOOL add_printer_driver(const char *name)
471 DRIVER_INFO_3A di3a;
473 static char driver_path[] = "wineps16",
474 data_file[] = "<datafile?>",
475 config_file[] = "wineps16",
476 help_file[] = "<helpfile?>",
477 dep_file[] = "<dependent files?>\0",
478 monitor_name[] = "<monitor name?>",
479 default_data_type[] = "RAW";
481 di3a.cVersion = (GetVersion() & 0x80000000) ? 0 : 3; /* FIXME: add 1, 2 */
482 di3a.pName = (char *)name;
483 di3a.pEnvironment = NULL; /* NULL means auto */
484 di3a.pDriverPath = driver_path;
485 di3a.pDataFile = data_file;
486 di3a.pConfigFile = config_file;
487 di3a.pHelpFile = help_file;
488 di3a.pDependentFiles = dep_file;
489 di3a.pMonitorName = monitor_name;
490 di3a.pDefaultDataType = default_data_type;
492 if (!AddPrinterDriverA(NULL, 3, (LPBYTE)&di3a))
494 ERR("Failed adding driver (%d)\n", GetLastError());
495 return FALSE;
497 return TRUE;
500 #ifdef SONAME_LIBCUPS
501 static typeof(cupsGetDests) *pcupsGetDests;
502 static typeof(cupsGetPPD) *pcupsGetPPD;
503 static typeof(cupsPrintFile) *pcupsPrintFile;
504 static void *cupshandle;
506 static BOOL CUPS_LoadPrinters(void)
508 int i, nrofdests;
509 BOOL hadprinter = FALSE, haddefault = FALSE;
510 cups_dest_t *dests;
511 PRINTER_INFO_2A pinfo2a;
512 char *port,*devline;
513 HKEY hkeyPrinter, hkeyPrinters, hkey;
514 char loaderror[256];
516 cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, loaderror, sizeof(loaderror));
517 if (!cupshandle) {
518 TRACE("%s\n", loaderror);
519 return FALSE;
521 TRACE("%p: %s loaded\n", cupshandle, SONAME_LIBCUPS);
523 #define DYNCUPS(x) \
524 p##x = wine_dlsym(cupshandle, #x, NULL,0); \
525 if (!p##x) return FALSE;
527 DYNCUPS(cupsGetPPD);
528 DYNCUPS(cupsGetDests);
529 DYNCUPS(cupsPrintFile);
530 #undef DYNCUPS
532 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
533 ERROR_SUCCESS) {
534 ERR("Can't create Printers key\n");
535 return FALSE;
538 nrofdests = pcupsGetDests(&dests);
539 TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
540 for (i=0;i<nrofdests;i++) {
541 port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
542 sprintf(port,"LPR:%s",dests[i].name);
543 devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
544 sprintf(devline,"WINEPS.DRV,%s",port);
545 WriteProfileStringA("devices",dests[i].name,devline);
546 if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
547 RegSetValueExA(hkey, dests[i].name, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
548 RegCloseKey(hkey);
550 HeapFree(GetProcessHeap(),0,devline);
552 TRACE("Printer %d: %s\n", i, dests[i].name);
553 if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
554 /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
555 and continue */
556 TRACE("Printer already exists\n");
557 RegDeleteValueW(hkeyPrinter, May_Delete_Value);
558 RegCloseKey(hkeyPrinter);
559 } else {
560 static CHAR data_type[] = "RAW",
561 print_proc[] = "WinPrint",
562 comment[] = "WINEPS Printer using CUPS",
563 location[] = "<physical location of printer>",
564 params[] = "<parameters?>",
565 share_name[] = "<share name?>",
566 sep_file[] = "<sep file?>";
568 add_printer_driver(dests[i].name);
570 memset(&pinfo2a,0,sizeof(pinfo2a));
571 pinfo2a.pPrinterName = dests[i].name;
572 pinfo2a.pDatatype = data_type;
573 pinfo2a.pPrintProcessor = print_proc;
574 pinfo2a.pDriverName = dests[i].name;
575 pinfo2a.pComment = comment;
576 pinfo2a.pLocation = location;
577 pinfo2a.pPortName = port;
578 pinfo2a.pParameters = params;
579 pinfo2a.pShareName = share_name;
580 pinfo2a.pSepFile = sep_file;
582 if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
583 if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
584 ERR("printer '%s' not added by AddPrinterA (error %d)\n",dests[i].name,GetLastError());
587 HeapFree(GetProcessHeap(),0,port);
589 hadprinter = TRUE;
590 if (dests[i].is_default) {
591 WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
592 haddefault = TRUE;
595 if (hadprinter & !haddefault)
596 WINSPOOL_SetDefaultPrinter(dests[0].name, dests[0].name, TRUE);
597 RegCloseKey(hkeyPrinters);
598 return hadprinter;
600 #endif
602 static BOOL
603 PRINTCAP_ParseEntry(const char *pent, BOOL isfirst) {
604 PRINTER_INFO_2A pinfo2a;
605 char *e,*s,*name,*prettyname,*devname;
606 BOOL ret = FALSE, set_default = FALSE;
607 char *port,*devline,*env_default;
608 HKEY hkeyPrinter, hkeyPrinters, hkey;
610 while (isspace(*pent)) pent++;
611 s = strchr(pent,':');
612 if(s) *s='\0';
613 name = HeapAlloc(GetProcessHeap(), 0, strlen(pent) + 1);
614 strcpy(name,pent);
615 if(s) {
616 *s=':';
617 pent = s;
618 } else
619 pent = "";
621 TRACE("name=%s entry=%s\n",name, pent);
623 if(ispunct(*name)) { /* a tc entry, not a real printer */
624 TRACE("skipping tc entry\n");
625 goto end;
628 if(strstr(pent,":server")) { /* server only version so skip */
629 TRACE("skipping server entry\n");
630 goto end;
633 /* Determine whether this is a postscript printer. */
635 ret = TRUE;
636 env_default = getenv("PRINTER");
637 prettyname = name;
638 /* Get longest name, usually the one at the right for later display. */
639 while((s=strchr(prettyname,'|'))) {
640 *s = '\0';
641 e = s;
642 while(isspace(*--e)) *e = '\0';
643 TRACE("\t%s\n", debugstr_a(prettyname));
644 if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
645 for(prettyname = s+1; isspace(*prettyname); prettyname++)
648 e = prettyname + strlen(prettyname);
649 while(isspace(*--e)) *e = '\0';
650 TRACE("\t%s\n", debugstr_a(prettyname));
651 if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
653 /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
654 * if it is too long, we use it as comment below. */
655 devname = prettyname;
656 if (strlen(devname)>=CCHDEVICENAME-1)
657 devname = name;
658 if (strlen(devname)>=CCHDEVICENAME-1) {
659 ret = FALSE;
660 goto end;
663 port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
664 sprintf(port,"LPR:%s",name);
666 devline=HeapAlloc(GetProcessHeap(),0,sizeof("WINEPS.DRV,")+strlen(port));
667 sprintf(devline,"WINEPS.DRV,%s",port);
668 WriteProfileStringA("devices",devname,devline);
669 if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
670 RegSetValueExA(hkey, devname, 0, REG_SZ, (LPBYTE)devline, strlen(devline) + 1);
671 RegCloseKey(hkey);
673 HeapFree(GetProcessHeap(),0,devline);
675 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
676 ERROR_SUCCESS) {
677 ERR("Can't create Printers key\n");
678 ret = FALSE;
679 goto end;
681 if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
682 /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
683 and continue */
684 TRACE("Printer already exists\n");
685 RegDeleteValueW(hkeyPrinter, May_Delete_Value);
686 RegCloseKey(hkeyPrinter);
687 } else {
688 static CHAR data_type[] = "RAW",
689 print_proc[] = "WinPrint",
690 comment[] = "WINEPS Printer using LPR",
691 params[] = "<parameters?>",
692 share_name[] = "<share name?>",
693 sep_file[] = "<sep file?>";
695 add_printer_driver(devname);
697 memset(&pinfo2a,0,sizeof(pinfo2a));
698 pinfo2a.pPrinterName = devname;
699 pinfo2a.pDatatype = data_type;
700 pinfo2a.pPrintProcessor = print_proc;
701 pinfo2a.pDriverName = devname;
702 pinfo2a.pComment = comment;
703 pinfo2a.pLocation = prettyname;
704 pinfo2a.pPortName = port;
705 pinfo2a.pParameters = params;
706 pinfo2a.pShareName = share_name;
707 pinfo2a.pSepFile = sep_file;
709 if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
710 if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
711 ERR("%s not added by AddPrinterA (%d)\n",name,GetLastError());
714 RegCloseKey(hkeyPrinters);
716 if (isfirst || set_default)
717 WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
719 HeapFree(GetProcessHeap(), 0, port);
720 end:
721 HeapFree(GetProcessHeap(), 0, name);
722 return ret;
725 static BOOL
726 PRINTCAP_LoadPrinters(void) {
727 BOOL hadprinter = FALSE;
728 char buf[200];
729 FILE *f;
730 char *pent = NULL;
731 BOOL had_bash = FALSE;
733 f = fopen("/etc/printcap","r");
734 if (!f)
735 return FALSE;
737 while(fgets(buf,sizeof(buf),f)) {
738 char *start, *end;
740 end=strchr(buf,'\n');
741 if (end) *end='\0';
743 start = buf;
744 while(isspace(*start)) start++;
745 if(*start == '#' || *start == '\0')
746 continue;
748 if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
749 hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
750 HeapFree(GetProcessHeap(),0,pent);
751 pent = NULL;
754 if (end && *--end == '\\') {
755 *end = '\0';
756 had_bash = TRUE;
757 } else
758 had_bash = FALSE;
760 if (pent) {
761 pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
762 strcat(pent,start);
763 } else {
764 pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
765 strcpy(pent,start);
769 if(pent) {
770 hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
771 HeapFree(GetProcessHeap(),0,pent);
773 fclose(f);
774 return hadprinter;
777 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
779 if (value)
780 return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
781 (lstrlenW(value) + 1) * sizeof(WCHAR));
782 else
783 return ERROR_FILE_NOT_FOUND;
786 /*****************************************************************************
787 * enumerate the local monitors (INTERNAL)
789 * returns the needed size (in bytes) for pMonitors
790 * and *lpreturned is set to number of entries returned in pMonitors
793 static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
795 HKEY hroot = NULL;
796 HKEY hentry = NULL;
797 LPWSTR ptr;
798 LPMONITOR_INFO_2W mi;
799 WCHAR buffer[MAX_PATH];
800 WCHAR dllname[MAX_PATH];
801 DWORD dllsize;
802 DWORD len;
803 DWORD index = 0;
804 DWORD needed = 0;
805 DWORD numentries;
806 DWORD entrysize;
808 entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
810 numentries = *lpreturned; /* this is 0, when we scan the registry */
811 len = entrysize * numentries;
812 ptr = (LPWSTR) &pMonitors[len];
814 numentries = 0;
815 len = sizeof(buffer);
816 buffer[0] = '\0';
818 /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
819 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) == ERROR_SUCCESS) {
820 /* Scan all Monitor-Registry-Keys */
821 while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
822 TRACE("Monitor_%d: %s\n", numentries, debugstr_w(buffer));
823 dllsize = sizeof(dllname);
824 dllname[0] = '\0';
826 /* The Monitor must have a Driver-DLL */
827 if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
828 if (RegQueryValueExW(hentry, DriverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
829 /* We found a valid DLL for this Monitor. */
830 TRACE("using Driver: %s\n", debugstr_w(dllname));
832 RegCloseKey(hentry);
835 /* Windows returns only Port-Monitors here, but to simplify our code,
836 we do no filtering for Language-Monitors */
837 if (dllname[0]) {
838 numentries++;
839 needed += entrysize;
840 needed += (len+1) * sizeof(WCHAR); /* len is lstrlenW(monitorname) */
841 if (level > 1) {
842 /* we install and return only monitors for "Windows NT x86" */
843 needed += (lstrlenW(envname_x86W) +1) * sizeof(WCHAR);
844 needed += dllsize;
847 /* required size is calculated. Now fill the user-buffer */
848 if (pMonitors && (cbBuf >= needed)){
849 mi = (LPMONITOR_INFO_2W) pMonitors;
850 pMonitors += entrysize;
852 TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi, level, numentries);
853 mi->pName = ptr;
854 lstrcpyW(ptr, buffer); /* Name of the Monitor */
855 ptr += (len+1); /* len is lstrlenW(monitorname) */
856 if (level > 1) {
857 mi->pEnvironment = ptr;
858 lstrcpyW(ptr, envname_x86W); /* fixed to "Windows NT x86" */
859 ptr += (lstrlenW(envname_x86W)+1);
861 mi->pDLLName = ptr;
862 lstrcpyW(ptr, dllname); /* Name of the Driver-DLL */
863 ptr += (dllsize / sizeof(WCHAR));
867 index++;
868 len = sizeof(buffer);
869 buffer[0] = '\0';
871 RegCloseKey(hroot);
873 *lpreturned = numentries;
874 TRACE("need %d byte for %d entries\n", needed, numentries);
875 return needed;
878 /******************************************************************
879 * monitor_unload [internal]
881 * release a printmonitor and unload it from memory, when needed
884 static void monitor_unload(monitor_t * pm)
886 if (pm == NULL) return;
887 TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
889 EnterCriticalSection(&monitor_handles_cs);
891 if (pm->refcount) pm->refcount--;
893 if (pm->refcount == 0) {
894 list_remove(&pm->entry);
895 FreeLibrary(pm->hdll);
896 HeapFree(GetProcessHeap(), 0, pm->name);
897 HeapFree(GetProcessHeap(), 0, pm->dllname);
898 HeapFree(GetProcessHeap(), 0, pm);
900 LeaveCriticalSection(&monitor_handles_cs);
903 /******************************************************************
904 * monitor_unloadall [internal]
906 * release all printmonitors and unload them from memory, when needed
909 static void monitor_unloadall(void)
911 monitor_t * pm;
912 monitor_t * next;
914 EnterCriticalSection(&monitor_handles_cs);
915 /* iterate through the list, with safety against removal */
916 LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
918 monitor_unload(pm);
920 LeaveCriticalSection(&monitor_handles_cs);
923 /******************************************************************
924 * monitor_load [internal]
926 * load a printmonitor, get the dllname from the registry, when needed
927 * initialize the monitor and dump found function-pointers
929 * On failure, SetLastError() is called and NULL is returned
932 static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname)
934 LPMONITOR2 (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
935 PMONITORUI (WINAPI *pInitializePrintMonitorUI)(VOID);
936 LPMONITOREX (WINAPI *pInitializePrintMonitor) (LPWSTR);
937 DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
938 DWORD (WINAPI *pInitializeMonitor) (LPWSTR);
940 monitor_t * pm = NULL;
941 monitor_t * cursor;
942 LPWSTR regroot = NULL;
943 LPWSTR driver = dllname;
945 TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
946 /* Is the Monitor already loaded? */
947 EnterCriticalSection(&monitor_handles_cs);
949 if (name) {
950 LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
952 if (cursor->name && (lstrcmpW(name, cursor->name) == 0)) {
953 pm = cursor;
954 break;
959 if (pm == NULL) {
960 pm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(monitor_t));
961 if (pm == NULL) goto cleanup;
962 list_add_tail(&monitor_handles, &pm->entry);
964 pm->refcount++;
966 if (pm->name == NULL) {
967 /* Load the monitor */
968 LPMONITOREX pmonitorEx;
969 DWORD len;
971 if (name) {
972 len = lstrlenW(MonitorsW) + lstrlenW(name) + 2;
973 regroot = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
976 if (regroot) {
977 lstrcpyW(regroot, MonitorsW);
978 lstrcatW(regroot, name);
979 /* Get the Driver from the Registry */
980 if (driver == NULL) {
981 HKEY hroot;
982 DWORD namesize;
983 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
984 if (RegQueryValueExW(hroot, DriverW, NULL, NULL, NULL,
985 &namesize) == ERROR_SUCCESS) {
986 driver = HeapAlloc(GetProcessHeap(), 0, namesize);
987 RegQueryValueExW(hroot, DriverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
989 RegCloseKey(hroot);
994 pm->name = strdupW(name);
995 pm->dllname = strdupW(driver);
997 if ((name && (!regroot || !pm->name)) || !pm->dllname) {
998 monitor_unload(pm);
999 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1000 pm = NULL;
1001 goto cleanup;
1004 pm->hdll = LoadLibraryW(driver);
1005 TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
1007 if (pm->hdll == NULL) {
1008 monitor_unload(pm);
1009 SetLastError(ERROR_MOD_NOT_FOUND);
1010 pm = NULL;
1011 goto cleanup;
1014 pInitializePrintMonitor2 = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
1015 pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
1016 pInitializePrintMonitor = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
1017 pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
1018 pInitializeMonitor = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
1021 TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
1022 TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
1023 TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
1024 TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
1025 TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
1027 if (pInitializePrintMonitorUI != NULL) {
1028 pm->monitorUI = pInitializePrintMonitorUI();
1029 TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver));
1030 if (pm->monitorUI) {
1031 TRACE( "0x%08x: dwMonitorSize (%d)\n",
1032 pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize );
1037 if (pInitializePrintMonitor && regroot) {
1038 pmonitorEx = pInitializePrintMonitor(regroot);
1039 TRACE( "%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
1040 pmonitorEx, debugstr_w(driver), debugstr_w(regroot));
1042 if (pmonitorEx) {
1043 pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
1044 pm->monitor = &(pmonitorEx->Monitor);
1048 if (pm->monitor) {
1049 TRACE( "0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize );
1053 if (!pm->monitor && regroot) {
1054 if (pInitializePrintMonitor2 != NULL) {
1055 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
1057 if (pInitializeMonitorEx != NULL) {
1058 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
1060 if (pInitializeMonitor != NULL) {
1061 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
1064 if (!pm->monitor && !pm->monitorUI) {
1065 monitor_unload(pm);
1066 SetLastError(ERROR_PROC_NOT_FOUND);
1067 pm = NULL;
1070 cleanup:
1071 if ((pm_localport == NULL) && (pm != NULL) && (lstrcmpW(pm->name, LocalPortW) == 0)) {
1072 pm->refcount++;
1073 pm_localport = pm;
1075 LeaveCriticalSection(&monitor_handles_cs);
1076 if (driver != dllname) HeapFree(GetProcessHeap(), 0, driver);
1077 HeapFree(GetProcessHeap(), 0, regroot);
1078 TRACE("=> %p\n", pm);
1079 return pm;
1082 /******************************************************************
1083 * monitor_loadall [internal]
1085 * Load all registered monitors
1088 static DWORD monitor_loadall(void)
1090 monitor_t * pm;
1091 DWORD registered = 0;
1092 DWORD loaded = 0;
1093 HKEY hmonitors;
1094 WCHAR buffer[MAX_PATH];
1095 DWORD id = 0;
1097 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hmonitors) == ERROR_SUCCESS) {
1098 RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, &registered, NULL, NULL,
1099 NULL, NULL, NULL, NULL, NULL);
1101 TRACE("%d monitors registered\n", registered);
1103 EnterCriticalSection(&monitor_handles_cs);
1104 while (id < registered) {
1105 buffer[0] = '\0';
1106 RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
1107 pm = monitor_load(buffer, NULL);
1108 if (pm) loaded++;
1109 id++;
1111 LeaveCriticalSection(&monitor_handles_cs);
1112 RegCloseKey(hmonitors);
1114 TRACE("%d monitors loaded\n", loaded);
1115 return loaded;
1118 /******************************************************************
1119 * monitor_loadui [internal]
1121 * load the userinterface-dll for a given portmonitor
1123 * On failure, NULL is returned
1126 static monitor_t * monitor_loadui(monitor_t * pm)
1128 monitor_t * pui = NULL;
1129 LPWSTR buffer[MAX_PATH];
1130 HANDLE hXcv;
1131 DWORD len;
1132 DWORD res;
1134 if (pm == NULL) return NULL;
1135 TRACE("(%p) => dllname: %s\n", pm, debugstr_w(pm->dllname));
1137 /* Try the Portmonitor first; works for many monitors */
1138 if (pm->monitorUI) {
1139 EnterCriticalSection(&monitor_handles_cs);
1140 pm->refcount++;
1141 LeaveCriticalSection(&monitor_handles_cs);
1142 return pm;
1145 /* query the userinterface-dllname from the Portmonitor */
1146 if ((pm->monitor) && (pm->monitor->pfnXcvDataPort)) {
1147 /* building (",XcvMonitor %s",pm->name) not needed yet */
1148 res = pm->monitor->pfnXcvOpenPort(emptyStringW, SERVER_ACCESS_ADMINISTER, &hXcv);
1149 TRACE("got %u with %p\n", res, hXcv);
1150 if (res) {
1151 res = pm->monitor->pfnXcvDataPort(hXcv, MonitorUIW, NULL, 0, (BYTE *) buffer, sizeof(buffer), &len);
1152 TRACE("got %u with %s\n", res, debugstr_w((LPWSTR) buffer));
1153 if (res == ERROR_SUCCESS) pui = monitor_load(NULL, (LPWSTR) buffer);
1154 pm->monitor->pfnXcvClosePort(hXcv);
1157 return pui;
1161 /******************************************************************
1162 * monitor_load_by_port [internal]
1164 * load a printmonitor for a given port
1166 * On failure, NULL is returned
1169 static monitor_t * monitor_load_by_port(LPCWSTR portname)
1171 HKEY hroot;
1172 HKEY hport;
1173 LPWSTR buffer;
1174 monitor_t * pm = NULL;
1175 DWORD registered = 0;
1176 DWORD id = 0;
1177 DWORD len;
1179 TRACE("(%s)\n", debugstr_w(portname));
1181 /* Try the Local Monitor first */
1182 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot) == ERROR_SUCCESS) {
1183 if (RegQueryValueExW(hroot, portname, NULL, NULL, NULL, &len) == ERROR_SUCCESS) {
1184 /* found the portname */
1185 RegCloseKey(hroot);
1186 return monitor_load(LocalPortW, NULL);
1188 RegCloseKey(hroot);
1191 len = MAX_PATH + lstrlenW(bs_Ports_bsW) + lstrlenW(portname) + 1;
1192 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1193 if (buffer == NULL) return NULL;
1195 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) == ERROR_SUCCESS) {
1196 EnterCriticalSection(&monitor_handles_cs);
1197 RegQueryInfoKeyW(hroot, NULL, NULL, NULL, &registered, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1199 while ((pm == NULL) && (id < registered)) {
1200 buffer[0] = '\0';
1201 RegEnumKeyW(hroot, id, buffer, MAX_PATH);
1202 TRACE("testing %s\n", debugstr_w(buffer));
1203 len = lstrlenW(buffer);
1204 lstrcatW(buffer, bs_Ports_bsW);
1205 lstrcatW(buffer, portname);
1206 if (RegOpenKeyW(hroot, buffer, &hport) == ERROR_SUCCESS) {
1207 RegCloseKey(hport);
1208 buffer[len] = '\0'; /* use only the Monitor-Name */
1209 pm = monitor_load(buffer, NULL);
1211 id++;
1213 LeaveCriticalSection(&monitor_handles_cs);
1214 RegCloseKey(hroot);
1216 HeapFree(GetProcessHeap(), 0, buffer);
1217 return pm;
1220 /******************************************************************
1221 * enumerate the local Ports from all loaded monitors (internal)
1223 * returns the needed size (in bytes) for pPorts
1224 * and *lpreturned is set to number of entries returned in pPorts
1227 static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
1229 monitor_t * pm;
1230 LPWSTR ptr;
1231 LPPORT_INFO_2W cache;
1232 LPPORT_INFO_2W out;
1233 LPBYTE pi_buffer = NULL;
1234 DWORD pi_allocated = 0;
1235 DWORD pi_needed;
1236 DWORD pi_index;
1237 DWORD pi_returned;
1238 DWORD res;
1239 DWORD outindex = 0;
1240 DWORD needed;
1241 DWORD numentries;
1242 DWORD entrysize;
1245 TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
1246 entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
1248 numentries = *lpreturned; /* this is 0, when we scan the registry */
1249 needed = entrysize * numentries;
1250 ptr = (LPWSTR) &pPorts[needed];
1252 numentries = 0;
1253 needed = 0;
1255 LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
1257 if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
1258 pi_needed = 0;
1259 pi_returned = 0;
1260 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
1261 if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
1262 /* Do not use HeapReAlloc (we do not need the old data in the buffer) */
1263 HeapFree(GetProcessHeap(), 0, pi_buffer);
1264 pi_buffer = HeapAlloc(GetProcessHeap(), 0, pi_needed);
1265 pi_allocated = (pi_buffer) ? pi_needed : 0;
1266 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
1268 TRACE( "(%s) got %d with %d (need %d byte for %d entries)\n",
1269 debugstr_w(pm->name), res, GetLastError(), pi_needed, pi_returned);
1271 numentries += pi_returned;
1272 needed += pi_needed;
1274 /* fill the output-buffer (pPorts), if we have one */
1275 if (pPorts && (cbBuf >= needed ) && pi_buffer) {
1276 pi_index = 0;
1277 while (pi_returned > pi_index) {
1278 cache = (LPPORT_INFO_2W) &pi_buffer[pi_index * entrysize];
1279 out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
1280 out->pPortName = ptr;
1281 lstrcpyW(ptr, cache->pPortName);
1282 ptr += (lstrlenW(ptr)+1);
1283 if (level > 1) {
1284 out->pMonitorName = ptr;
1285 lstrcpyW(ptr, cache->pMonitorName);
1286 ptr += (lstrlenW(ptr)+1);
1288 out->pDescription = ptr;
1289 lstrcpyW(ptr, cache->pDescription);
1290 ptr += (lstrlenW(ptr)+1);
1291 out->fPortType = cache->fPortType;
1292 out->Reserved = cache->Reserved;
1294 pi_index++;
1295 outindex++;
1300 /* the temporary portinfo-buffer is no longer needed */
1301 HeapFree(GetProcessHeap(), 0, pi_buffer);
1303 *lpreturned = numentries;
1304 TRACE("need %d byte for %d entries\n", needed, numentries);
1305 return needed;
1308 /******************************************************************
1309 * get_servername_from_name (internal)
1311 * for an external server, a copy of the serverpart from the full name is returned
1314 static LPWSTR get_servername_from_name(LPCWSTR name)
1316 LPWSTR server;
1317 LPWSTR ptr;
1318 WCHAR buffer[MAX_PATH];
1319 DWORD len;
1321 if (name == NULL) return NULL;
1322 if ((name[0] != '\\') || (name[1] != '\\')) return NULL;
1324 server = strdupW(&name[2]); /* skip over both backslash */
1325 if (server == NULL) return NULL;
1327 /* strip '\' and the printername */
1328 ptr = strchrW(server, '\\');
1329 if (ptr) ptr[0] = '\0';
1331 TRACE("found %s\n", debugstr_w(server));
1333 len = sizeof(buffer)/sizeof(buffer[0]);
1334 if (GetComputerNameW(buffer, &len)) {
1335 if (lstrcmpW(buffer, server) == 0) {
1336 /* The requested Servername is our computername */
1337 HeapFree(GetProcessHeap(), 0, server);
1338 return NULL;
1341 return server;
1344 /******************************************************************
1345 * get_basename_from_name (internal)
1347 * skip over the serverpart from the full name
1350 static LPCWSTR get_basename_from_name(LPCWSTR name)
1352 if (name == NULL) return NULL;
1353 if ((name[0] == '\\') && (name[1] == '\\')) {
1354 /* skip over the servername and search for the following '\' */
1355 name = strchrW(&name[2], '\\');
1356 if ((name) && (name[1])) {
1357 /* found a separator ('\') followed by a name:
1358 skip over the separator and return the rest */
1359 name++;
1361 else
1363 /* no basename present (we found only a servername) */
1364 return NULL;
1367 return name;
1370 /******************************************************************
1371 * get_opened_printer_entry
1372 * Get the first place empty in the opened printer table
1374 * ToDo:
1375 * - pDefault is ignored
1377 static HANDLE get_opened_printer_entry(LPCWSTR name, LPPRINTER_DEFAULTSW pDefault)
1379 UINT_PTR handle = nb_printer_handles, i;
1380 jobqueue_t *queue = NULL;
1381 opened_printer_t *printer = NULL;
1382 LPWSTR servername;
1383 LPCWSTR printername;
1384 HKEY hkeyPrinters;
1385 HKEY hkeyPrinter;
1386 DWORD len;
1388 servername = get_servername_from_name(name);
1389 if (servername) {
1390 FIXME("server %s not supported\n", debugstr_w(servername));
1391 HeapFree(GetProcessHeap(), 0, servername);
1392 SetLastError(ERROR_INVALID_PRINTER_NAME);
1393 return NULL;
1396 printername = get_basename_from_name(name);
1397 if (name != printername) TRACE("converted %s to %s\n", debugstr_w(name), debugstr_w(printername));
1399 /* an empty printername is invalid */
1400 if (printername && (!printername[0])) {
1401 SetLastError(ERROR_INVALID_PARAMETER);
1402 return NULL;
1405 EnterCriticalSection(&printer_handles_cs);
1407 for (i = 0; i < nb_printer_handles; i++)
1409 if (!printer_handles[i])
1411 if(handle == nb_printer_handles)
1412 handle = i;
1414 else
1416 if(!queue && (name) && !lstrcmpW(name, printer_handles[i]->name))
1417 queue = printer_handles[i]->queue;
1421 if (handle >= nb_printer_handles)
1423 opened_printer_t **new_array;
1424 if (printer_handles)
1425 new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
1426 (nb_printer_handles + 16) * sizeof(*new_array) );
1427 else
1428 new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1429 (nb_printer_handles + 16) * sizeof(*new_array) );
1431 if (!new_array)
1433 handle = 0;
1434 goto end;
1436 printer_handles = new_array;
1437 nb_printer_handles += 16;
1440 if (!(printer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*printer))))
1442 handle = 0;
1443 goto end;
1447 /* clone the base name. This is NULL for the printserver */
1448 printer->printername = strdupW(printername);
1450 /* clone the full name */
1451 printer->name = strdupW(name);
1452 if (name && (!printer->name)) {
1453 handle = 0;
1454 goto end;
1457 if (printername) {
1458 len = sizeof(XcvMonitorW)/sizeof(WCHAR) - 1;
1459 if (strncmpW(printername, XcvMonitorW, len) == 0) {
1460 /* OpenPrinter(",XcvMonitor " detected */
1461 TRACE(",XcvMonitor: %s\n", debugstr_w(&printername[len]));
1462 printer->pm = monitor_load(&printername[len], NULL);
1463 if (printer->pm == NULL) {
1464 SetLastError(ERROR_UNKNOWN_PORT);
1465 handle = 0;
1466 goto end;
1469 else
1471 len = sizeof(XcvPortW)/sizeof(WCHAR) - 1;
1472 if (strncmpW( printername, XcvPortW, len) == 0) {
1473 /* OpenPrinter(",XcvPort " detected */
1474 TRACE(",XcvPort: %s\n", debugstr_w(&printername[len]));
1475 printer->pm = monitor_load_by_port(&printername[len]);
1476 if (printer->pm == NULL) {
1477 SetLastError(ERROR_UNKNOWN_PORT);
1478 handle = 0;
1479 goto end;
1484 if (printer->pm) {
1485 if ((printer->pm->monitor) && (printer->pm->monitor->pfnXcvOpenPort)) {
1486 printer->pm->monitor->pfnXcvOpenPort(&printername[len],
1487 pDefault ? pDefault->DesiredAccess : 0,
1488 &printer->hXcv);
1490 if (printer->hXcv == NULL) {
1491 SetLastError(ERROR_INVALID_PARAMETER);
1492 handle = 0;
1493 goto end;
1496 else
1498 /* Does the Printer exist? */
1499 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) != ERROR_SUCCESS) {
1500 ERR("Can't create Printers key\n");
1501 handle = 0;
1502 goto end;
1504 if (RegOpenKeyW(hkeyPrinters, printername, &hkeyPrinter) != ERROR_SUCCESS) {
1505 WARN("Printer not found in Registry: %s\n", debugstr_w(printername));
1506 RegCloseKey(hkeyPrinters);
1507 SetLastError(ERROR_INVALID_PRINTER_NAME);
1508 handle = 0;
1509 goto end;
1511 RegCloseKey(hkeyPrinter);
1512 RegCloseKey(hkeyPrinters);
1515 else
1517 TRACE("using the local printserver\n");
1520 if(queue)
1521 printer->queue = queue;
1522 else
1524 printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
1525 if (!printer->queue) {
1526 handle = 0;
1527 goto end;
1529 list_init(&printer->queue->jobs);
1530 printer->queue->ref = 0;
1532 InterlockedIncrement(&printer->queue->ref);
1534 printer_handles[handle] = printer;
1535 handle++;
1536 end:
1537 LeaveCriticalSection(&printer_handles_cs);
1538 if (!handle && printer) {
1539 /* Something failed: Free all resources */
1540 if (printer->hXcv) printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
1541 monitor_unload(printer->pm);
1542 HeapFree(GetProcessHeap(), 0, printer->printername);
1543 HeapFree(GetProcessHeap(), 0, printer->name);
1544 if (!queue) HeapFree(GetProcessHeap(), 0, printer->queue);
1545 HeapFree(GetProcessHeap(), 0, printer);
1548 return (HANDLE)handle;
1551 /******************************************************************
1552 * get_opened_printer
1553 * Get the pointer to the opened printer referred by the handle
1555 static opened_printer_t *get_opened_printer(HANDLE hprn)
1557 UINT_PTR idx = (UINT_PTR)hprn;
1558 opened_printer_t *ret = NULL;
1560 EnterCriticalSection(&printer_handles_cs);
1562 if ((idx <= 0) || (idx > nb_printer_handles))
1563 goto end;
1565 ret = printer_handles[idx - 1];
1566 end:
1567 LeaveCriticalSection(&printer_handles_cs);
1568 return ret;
1571 /******************************************************************
1572 * get_opened_printer_name
1573 * Get the pointer to the opened printer name referred by the handle
1575 static LPCWSTR get_opened_printer_name(HANDLE hprn)
1577 opened_printer_t *printer = get_opened_printer(hprn);
1578 if(!printer) return NULL;
1579 return printer->name;
1582 /******************************************************************
1583 * WINSPOOL_GetOpenedPrinterRegKey
1586 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
1588 LPCWSTR name = get_opened_printer_name(hPrinter);
1589 DWORD ret;
1590 HKEY hkeyPrinters;
1592 if(!name) return ERROR_INVALID_HANDLE;
1594 if((ret = RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters)) !=
1595 ERROR_SUCCESS)
1596 return ret;
1598 if(RegOpenKeyW(hkeyPrinters, name, phkey) != ERROR_SUCCESS)
1600 ERR("Can't find opened printer %s in registry\n",
1601 debugstr_w(name));
1602 RegCloseKey(hkeyPrinters);
1603 return ERROR_INVALID_PRINTER_NAME; /* ? */
1605 RegCloseKey(hkeyPrinters);
1606 return ERROR_SUCCESS;
1609 void WINSPOOL_LoadSystemPrinters(void)
1611 HKEY hkey, hkeyPrinters;
1612 HANDLE hprn;
1613 DWORD needed, num, i;
1614 WCHAR PrinterName[256];
1615 BOOL done = FALSE;
1617 /* This ensures that all printer entries have a valid Name value. If causes
1618 problems later if they don't. If one is found to be missed we create one
1619 and set it equal to the name of the key */
1620 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
1621 if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
1622 NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
1623 for(i = 0; i < num; i++) {
1624 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) == ERROR_SUCCESS) {
1625 if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
1626 if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
1627 set_reg_szW(hkey, NameW, PrinterName);
1629 RegCloseKey(hkey);
1634 RegCloseKey(hkeyPrinters);
1637 /* We want to avoid calling AddPrinter on printers as much as
1638 possible, because on cups printers this will (eventually) lead
1639 to a call to cupsGetPPD which takes forever, even with non-cups
1640 printers AddPrinter takes a while. So we'll tag all printers that
1641 were automatically added last time around, if they still exist
1642 we'll leave them be otherwise we'll delete them. */
1643 EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1644 if(needed) {
1645 PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1646 if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1647 for(i = 0; i < num; i++) {
1648 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1649 if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1650 if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1651 DWORD dw = 1;
1652 RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
1653 RegCloseKey(hkey);
1655 ClosePrinter(hprn);
1660 HeapFree(GetProcessHeap(), 0, pi);
1664 #ifdef SONAME_LIBCUPS
1665 done = CUPS_LoadPrinters();
1666 #endif
1668 if(!done) /* If we have any CUPS based printers, skip looking for printcap printers */
1669 PRINTCAP_LoadPrinters();
1671 /* Now enumerate the list again and delete any printers that a still tagged */
1672 EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
1673 if(needed) {
1674 PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
1675 if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
1676 for(i = 0; i < num; i++) {
1677 if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
1678 if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
1679 BOOL delete_driver = FALSE;
1680 if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
1681 DWORD dw, type, size = sizeof(dw);
1682 if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
1683 TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
1684 DeletePrinter(hprn);
1685 delete_driver = TRUE;
1687 RegCloseKey(hkey);
1689 ClosePrinter(hprn);
1690 if(delete_driver)
1691 DeletePrinterDriverExA(NULL, NULL, pi[i].pPrinterName, 0, 0);
1696 HeapFree(GetProcessHeap(), 0, pi);
1699 return;
1703 /******************************************************************
1704 * get_job
1706 * Get the pointer to the specified job.
1707 * Should hold the printer_handles_cs before calling.
1709 static job_t *get_job(HANDLE hprn, DWORD JobId)
1711 opened_printer_t *printer = get_opened_printer(hprn);
1712 job_t *job;
1714 if(!printer) return NULL;
1715 LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
1717 if(job->job_id == JobId)
1718 return job;
1720 return NULL;
1723 /***********************************************************
1724 * DEVMODEcpyAtoW
1726 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
1728 BOOL Formname;
1729 ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
1730 DWORD size;
1732 Formname = (dmA->dmSize > off_formname);
1733 size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
1734 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
1735 dmW->dmDeviceName, CCHDEVICENAME);
1736 if(!Formname) {
1737 memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1738 dmA->dmSize - CCHDEVICENAME);
1739 } else {
1740 memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1741 off_formname - CCHDEVICENAME);
1742 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
1743 dmW->dmFormName, CCHFORMNAME);
1744 memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
1745 (off_formname + CCHFORMNAME));
1747 dmW->dmSize = size;
1748 memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
1749 dmA->dmDriverExtra);
1750 return dmW;
1753 /***********************************************************
1754 * DEVMODEdupWtoA
1755 * Creates an ascii copy of supplied devmode on heap
1757 static LPDEVMODEA DEVMODEdupWtoA(HANDLE heap, const DEVMODEW *dmW)
1759 LPDEVMODEA dmA;
1760 DWORD size;
1761 BOOL Formname;
1762 ptrdiff_t off_formname = (const char *)dmW->dmFormName - (const char *)dmW;
1764 if(!dmW) return NULL;
1765 Formname = (dmW->dmSize > off_formname);
1766 size = dmW->dmSize - CCHDEVICENAME - (Formname ? CCHFORMNAME : 0);
1767 dmA = HeapAlloc(heap, HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra);
1768 WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1,
1769 (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
1770 if(!Formname) {
1771 memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1772 dmW->dmSize - CCHDEVICENAME * sizeof(WCHAR));
1773 } else {
1774 memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion,
1775 off_formname - CCHDEVICENAME * sizeof(WCHAR));
1776 WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1,
1777 (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
1778 memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize -
1779 (off_formname + CCHFORMNAME * sizeof(WCHAR)));
1781 dmA->dmSize = size;
1782 memcpy((char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize,
1783 dmW->dmDriverExtra);
1784 return dmA;
1787 /***********************************************************
1788 * PRINTER_INFO_2AtoW
1789 * Creates a unicode copy of PRINTER_INFO_2A on heap
1791 static LPPRINTER_INFO_2W PRINTER_INFO_2AtoW(HANDLE heap, LPPRINTER_INFO_2A piA)
1793 LPPRINTER_INFO_2W piW;
1794 UNICODE_STRING usBuffer;
1796 if(!piA) return NULL;
1797 piW = HeapAlloc(heap, 0, sizeof(*piW));
1798 memcpy(piW, piA, sizeof(*piW)); /* copy everything first */
1800 piW->pServerName = asciitounicode(&usBuffer,piA->pServerName);
1801 piW->pPrinterName = asciitounicode(&usBuffer,piA->pPrinterName);
1802 piW->pShareName = asciitounicode(&usBuffer,piA->pShareName);
1803 piW->pPortName = asciitounicode(&usBuffer,piA->pPortName);
1804 piW->pDriverName = asciitounicode(&usBuffer,piA->pDriverName);
1805 piW->pComment = asciitounicode(&usBuffer,piA->pComment);
1806 piW->pLocation = asciitounicode(&usBuffer,piA->pLocation);
1807 piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW(piA->pDevMode) : NULL;
1808 piW->pSepFile = asciitounicode(&usBuffer,piA->pSepFile);
1809 piW->pPrintProcessor = asciitounicode(&usBuffer,piA->pPrintProcessor);
1810 piW->pDatatype = asciitounicode(&usBuffer,piA->pDatatype);
1811 piW->pParameters = asciitounicode(&usBuffer,piA->pParameters);
1812 return piW;
1815 /***********************************************************
1816 * FREE_PRINTER_INFO_2W
1817 * Free PRINTER_INFO_2W and all strings
1819 static void FREE_PRINTER_INFO_2W(HANDLE heap, LPPRINTER_INFO_2W piW)
1821 if(!piW) return;
1823 HeapFree(heap,0,piW->pServerName);
1824 HeapFree(heap,0,piW->pPrinterName);
1825 HeapFree(heap,0,piW->pShareName);
1826 HeapFree(heap,0,piW->pPortName);
1827 HeapFree(heap,0,piW->pDriverName);
1828 HeapFree(heap,0,piW->pComment);
1829 HeapFree(heap,0,piW->pLocation);
1830 HeapFree(heap,0,piW->pDevMode);
1831 HeapFree(heap,0,piW->pSepFile);
1832 HeapFree(heap,0,piW->pPrintProcessor);
1833 HeapFree(heap,0,piW->pDatatype);
1834 HeapFree(heap,0,piW->pParameters);
1835 HeapFree(heap,0,piW);
1836 return;
1839 /******************************************************************
1840 * DeviceCapabilities [WINSPOOL.@]
1841 * DeviceCapabilitiesA [WINSPOOL.@]
1844 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
1845 LPSTR pOutput, LPDEVMODEA lpdm)
1847 INT ret;
1849 if (!GDI_CallDeviceCapabilities16)
1851 GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1852 (LPCSTR)104 );
1853 if (!GDI_CallDeviceCapabilities16) return -1;
1855 ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
1857 /* If DC_PAPERSIZE map POINT16s to POINTs */
1858 if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
1859 POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
1860 POINT *pt = (POINT *)pOutput;
1861 INT i;
1862 memcpy(tmp, pOutput, ret * sizeof(POINT16));
1863 for(i = 0; i < ret; i++, pt++)
1865 pt->x = tmp[i].x;
1866 pt->y = tmp[i].y;
1868 HeapFree( GetProcessHeap(), 0, tmp );
1870 return ret;
1874 /*****************************************************************************
1875 * DeviceCapabilitiesW [WINSPOOL.@]
1877 * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
1880 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
1881 WORD fwCapability, LPWSTR pOutput,
1882 const DEVMODEW *pDevMode)
1884 LPDEVMODEA dmA = DEVMODEdupWtoA(GetProcessHeap(), pDevMode);
1885 LPSTR pDeviceA = strdupWtoA(pDevice);
1886 LPSTR pPortA = strdupWtoA(pPort);
1887 INT ret;
1889 if(pOutput && (fwCapability == DC_BINNAMES ||
1890 fwCapability == DC_FILEDEPENDENCIES ||
1891 fwCapability == DC_PAPERNAMES)) {
1892 /* These need A -> W translation */
1893 INT size = 0, i;
1894 LPSTR pOutputA;
1895 ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
1896 dmA);
1897 if(ret == -1)
1898 return ret;
1899 switch(fwCapability) {
1900 case DC_BINNAMES:
1901 size = 24;
1902 break;
1903 case DC_PAPERNAMES:
1904 case DC_FILEDEPENDENCIES:
1905 size = 64;
1906 break;
1908 pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
1909 ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
1910 dmA);
1911 for(i = 0; i < ret; i++)
1912 MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
1913 pOutput + (i * size), size);
1914 HeapFree(GetProcessHeap(), 0, pOutputA);
1915 } else {
1916 ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
1917 (LPSTR)pOutput, dmA);
1919 HeapFree(GetProcessHeap(),0,pPortA);
1920 HeapFree(GetProcessHeap(),0,pDeviceA);
1921 HeapFree(GetProcessHeap(),0,dmA);
1922 return ret;
1925 /******************************************************************
1926 * DocumentPropertiesA [WINSPOOL.@]
1928 * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1930 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
1931 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
1932 LPDEVMODEA pDevModeInput,DWORD fMode )
1934 LPSTR lpName = pDeviceName;
1935 static CHAR port[] = "LPT1:";
1936 LONG ret;
1938 TRACE("(%p,%p,%s,%p,%p,%d)\n",
1939 hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
1942 if(!pDeviceName) {
1943 LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1944 if(!lpNameW) {
1945 ERR("no name from hPrinter?\n");
1946 SetLastError(ERROR_INVALID_HANDLE);
1947 return -1;
1949 lpName = strdupWtoA(lpNameW);
1952 if (!GDI_CallExtDeviceMode16)
1954 GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1955 (LPCSTR)102 );
1956 if (!GDI_CallExtDeviceMode16) {
1957 ERR("No CallExtDeviceMode16?\n");
1958 return -1;
1961 ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, port,
1962 pDevModeInput, NULL, fMode);
1964 if(!pDeviceName)
1965 HeapFree(GetProcessHeap(),0,lpName);
1966 return ret;
1970 /*****************************************************************************
1971 * DocumentPropertiesW (WINSPOOL.@)
1973 * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1975 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1976 LPWSTR pDeviceName,
1977 LPDEVMODEW pDevModeOutput,
1978 LPDEVMODEW pDevModeInput, DWORD fMode)
1981 LPSTR pDeviceNameA = strdupWtoA(pDeviceName);
1982 LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(GetProcessHeap(),pDevModeInput);
1983 LPDEVMODEA pDevModeOutputA = NULL;
1984 LONG ret;
1986 TRACE("(%p,%p,%s,%p,%p,%d)\n",
1987 hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1988 fMode);
1989 if(pDevModeOutput) {
1990 ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1991 if(ret < 0) return ret;
1992 pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1994 ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1995 pDevModeInputA, fMode);
1996 if(pDevModeOutput) {
1997 DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1998 HeapFree(GetProcessHeap(),0,pDevModeOutputA);
2000 if(fMode == 0 && ret > 0)
2001 ret += (CCHDEVICENAME + CCHFORMNAME);
2002 HeapFree(GetProcessHeap(),0,pDevModeInputA);
2003 HeapFree(GetProcessHeap(),0,pDeviceNameA);
2004 return ret;
2007 /******************************************************************
2008 * OpenPrinterA [WINSPOOL.@]
2010 * See OpenPrinterW.
2013 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
2014 LPPRINTER_DEFAULTSA pDefault)
2016 UNICODE_STRING lpPrinterNameW;
2017 UNICODE_STRING usBuffer;
2018 PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
2019 PWSTR pwstrPrinterNameW;
2020 BOOL ret;
2022 pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
2024 if(pDefault) {
2025 DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
2026 DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
2027 DefaultW.DesiredAccess = pDefault->DesiredAccess;
2028 pDefaultW = &DefaultW;
2030 ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
2031 if(pDefault) {
2032 RtlFreeUnicodeString(&usBuffer);
2033 HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
2035 RtlFreeUnicodeString(&lpPrinterNameW);
2036 return ret;
2039 /******************************************************************
2040 * OpenPrinterW [WINSPOOL.@]
2042 * Open a Printer / Printserver or a Printer-Object
2044 * PARAMS
2045 * lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
2046 * phPrinter [O] The resulting Handle is stored here
2047 * pDefault [I] PTR to Default Printer Settings or NULL
2049 * RETURNS
2050 * Success: TRUE
2051 * Failure: FALSE
2053 * NOTES
2054 * lpPrinterName is one of:
2055 *| Printserver (NT only): "Servername" or NULL for the local Printserver
2056 *| Printer: "PrinterName"
2057 *| Printer-Object: "PrinterName,Job xxx"
2058 *| XcvMonitor: "Servername,XcvMonitor MonitorName"
2059 *| XcvPort: "Servername,XcvPort PortName"
2061 * BUGS
2062 *| Printer-Object not supported
2063 *| pDefaults is ignored
2066 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter, LPPRINTER_DEFAULTSW pDefault)
2069 TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), phPrinter, pDefault);
2070 if (pDefault) {
2071 FIXME("PRINTER_DEFAULTS ignored => %s,%p,0x%08x\n",
2072 debugstr_w(pDefault->pDatatype), pDefault->pDevMode, pDefault->DesiredAccess);
2075 if(!phPrinter) {
2076 /* NT: FALSE with ERROR_INVALID_PARAMETER, 9x: TRUE */
2077 SetLastError(ERROR_INVALID_PARAMETER);
2078 return FALSE;
2081 /* Get the unique handle of the printer or Printserver */
2082 *phPrinter = get_opened_printer_entry(lpPrinterName, pDefault);
2083 TRACE("returning %d with %u and %p\n", *phPrinter != NULL, GetLastError(), *phPrinter);
2084 return (*phPrinter != 0);
2087 /******************************************************************
2088 * AddMonitorA [WINSPOOL.@]
2090 * See AddMonitorW.
2093 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
2095 LPWSTR nameW = NULL;
2096 INT len;
2097 BOOL res;
2098 LPMONITOR_INFO_2A mi2a;
2099 MONITOR_INFO_2W mi2w;
2101 mi2a = (LPMONITOR_INFO_2A) pMonitors;
2102 TRACE("(%s, %d, %p) : %s %s %s\n", debugstr_a(pName), Level, pMonitors,
2103 mi2a ? debugstr_a(mi2a->pName) : NULL,
2104 mi2a ? debugstr_a(mi2a->pEnvironment) : NULL,
2105 mi2a ? debugstr_a(mi2a->pDLLName) : NULL);
2107 if (Level != 2) {
2108 SetLastError(ERROR_INVALID_LEVEL);
2109 return FALSE;
2112 /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2113 if (mi2a == NULL) {
2114 return FALSE;
2117 if (pName) {
2118 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2119 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2120 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2123 memset(&mi2w, 0, sizeof(MONITOR_INFO_2W));
2124 if (mi2a->pName) {
2125 len = MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, NULL, 0);
2126 mi2w.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2127 MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, mi2w.pName, len);
2129 if (mi2a->pEnvironment) {
2130 len = MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, NULL, 0);
2131 mi2w.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2132 MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, mi2w.pEnvironment, len);
2134 if (mi2a->pDLLName) {
2135 len = MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, NULL, 0);
2136 mi2w.pDLLName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2137 MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, mi2w.pDLLName, len);
2140 res = AddMonitorW(nameW, Level, (LPBYTE) &mi2w);
2142 HeapFree(GetProcessHeap(), 0, mi2w.pName);
2143 HeapFree(GetProcessHeap(), 0, mi2w.pEnvironment);
2144 HeapFree(GetProcessHeap(), 0, mi2w.pDLLName);
2146 HeapFree(GetProcessHeap(), 0, nameW);
2147 return (res);
2150 /******************************************************************************
2151 * AddMonitorW [WINSPOOL.@]
2153 * Install a Printmonitor
2155 * PARAMS
2156 * pName [I] Servername or NULL (local Computer)
2157 * Level [I] Structure-Level (Must be 2)
2158 * pMonitors [I] PTR to MONITOR_INFO_2
2160 * RETURNS
2161 * Success: TRUE
2162 * Failure: FALSE
2164 * NOTES
2165 * All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
2168 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
2170 monitor_t * pm = NULL;
2171 LPMONITOR_INFO_2W mi2w;
2172 HKEY hroot = NULL;
2173 HKEY hentry = NULL;
2174 DWORD disposition;
2175 BOOL res = FALSE;
2177 mi2w = (LPMONITOR_INFO_2W) pMonitors;
2178 TRACE("(%s, %d, %p) : %s %s %s\n", debugstr_w(pName), Level, pMonitors,
2179 mi2w ? debugstr_w(mi2w->pName) : NULL,
2180 mi2w ? debugstr_w(mi2w->pEnvironment) : NULL,
2181 mi2w ? debugstr_w(mi2w->pDLLName) : NULL);
2183 if (Level != 2) {
2184 SetLastError(ERROR_INVALID_LEVEL);
2185 return FALSE;
2188 /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2189 if (mi2w == NULL) {
2190 return FALSE;
2193 if (pName && (pName[0])) {
2194 FIXME("for server %s not implemented\n", debugstr_w(pName));
2195 SetLastError(ERROR_ACCESS_DENIED);
2196 return FALSE;
2200 if (!mi2w->pName || (! mi2w->pName[0])) {
2201 WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
2202 SetLastError(ERROR_INVALID_PARAMETER);
2203 return FALSE;
2205 if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, envname_x86W)) {
2206 WARN("Environment %s requested (we support only %s)\n",
2207 debugstr_w(mi2w->pEnvironment), debugstr_w(envname_x86W));
2208 SetLastError(ERROR_INVALID_ENVIRONMENT);
2209 return FALSE;
2212 if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
2213 WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
2214 SetLastError(ERROR_INVALID_PARAMETER);
2215 return FALSE;
2218 /* Load and initialize the monitor. SetLastError() is called on failure */
2219 if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
2220 return FALSE;
2222 monitor_unload(pm);
2224 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
2225 ERR("unable to create key %s\n", debugstr_w(MonitorsW));
2226 return FALSE;
2229 if(RegCreateKeyExW( hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
2230 KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
2231 &disposition) == ERROR_SUCCESS) {
2233 /* Some installers set options for the port before calling AddMonitor.
2234 We query the "Driver" entry to verify that the monitor is installed,
2235 before we return an error.
2236 When a user installs two print monitors at the same time with the
2237 same name but with a different driver DLL and a task switch comes
2238 between RegQueryValueExW and RegSetValueExW, a race condition
2239 is possible but silently ignored. */
2241 DWORD namesize = 0;
2243 if ((disposition == REG_OPENED_EXISTING_KEY) &&
2244 (RegQueryValueExW(hentry, DriverW, NULL, NULL, NULL,
2245 &namesize) == ERROR_SUCCESS)) {
2246 TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
2247 /* NT: ERROR_PRINT_MONITOR_ALREADY_INSTALLED (3006)
2248 9x: ERROR_ALREADY_EXISTS (183) */
2249 SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
2251 else
2253 INT len;
2254 len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
2255 res = (RegSetValueExW(hentry, DriverW, 0,
2256 REG_SZ, (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
2258 RegCloseKey(hentry);
2261 RegCloseKey(hroot);
2262 return (res);
2265 /******************************************************************
2266 * DeletePrinterDriverA [WINSPOOL.@]
2269 BOOL WINAPI DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
2271 return DeletePrinterDriverExA(pName, pEnvironment, pDriverName, 0, 0);
2274 /******************************************************************
2275 * DeletePrinterDriverW [WINSPOOL.@]
2278 BOOL WINAPI DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
2280 return DeletePrinterDriverExW(pName, pEnvironment, pDriverName, 0, 0);
2283 /******************************************************************
2284 * DeleteMonitorA [WINSPOOL.@]
2286 * See DeleteMonitorW.
2289 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
2291 LPWSTR nameW = NULL;
2292 LPWSTR EnvironmentW = NULL;
2293 LPWSTR MonitorNameW = NULL;
2294 BOOL res;
2295 INT len;
2297 if (pName) {
2298 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2299 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2300 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2303 if (pEnvironment) {
2304 len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
2305 EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2306 MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
2308 if (pMonitorName) {
2309 len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
2310 MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2311 MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
2314 res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
2316 HeapFree(GetProcessHeap(), 0, MonitorNameW);
2317 HeapFree(GetProcessHeap(), 0, EnvironmentW);
2318 HeapFree(GetProcessHeap(), 0, nameW);
2319 return (res);
2322 /******************************************************************
2323 * DeleteMonitorW [WINSPOOL.@]
2325 * Delete a specific Printmonitor from a Printing-Environment
2327 * PARAMS
2328 * pName [I] Servername or NULL (local Computer)
2329 * pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
2330 * pMonitorName [I] Name of the Monitor, that should be deleted
2332 * RETURNS
2333 * Success: TRUE
2334 * Failure: FALSE
2336 * NOTES
2337 * pEnvironment is ignored in Windows for the local Computer.
2341 BOOL WINAPI DeleteMonitorW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
2343 HKEY hroot = NULL;
2345 TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
2346 debugstr_w(pMonitorName));
2348 if (pName && (pName[0])) {
2349 FIXME("for server %s not implemented\n", debugstr_w(pName));
2350 SetLastError(ERROR_ACCESS_DENIED);
2351 return FALSE;
2354 /* pEnvironment is ignored in Windows for the local Computer */
2356 if (!pMonitorName || !pMonitorName[0]) {
2357 WARN("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
2358 SetLastError(ERROR_INVALID_PARAMETER);
2359 return FALSE;
2362 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hroot) != ERROR_SUCCESS) {
2363 ERR("unable to create key %s\n", debugstr_w(MonitorsW));
2364 return FALSE;
2367 if(RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS) {
2368 TRACE("monitor %s deleted\n", debugstr_w(pMonitorName));
2369 RegCloseKey(hroot);
2370 return TRUE;
2373 WARN("monitor %s does not exist\n", debugstr_w(pMonitorName));
2374 RegCloseKey(hroot);
2376 /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
2377 SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
2378 return (FALSE);
2381 /******************************************************************
2382 * DeletePortA [WINSPOOL.@]
2384 * See DeletePortW.
2387 BOOL WINAPI DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
2389 LPWSTR nameW = NULL;
2390 LPWSTR portW = NULL;
2391 INT len;
2392 DWORD res;
2394 TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
2396 /* convert servername to unicode */
2397 if (pName) {
2398 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2399 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2400 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2403 /* convert portname to unicode */
2404 if (pPortName) {
2405 len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
2406 portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2407 MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
2410 res = DeletePortW(nameW, hWnd, portW);
2411 HeapFree(GetProcessHeap(), 0, nameW);
2412 HeapFree(GetProcessHeap(), 0, portW);
2413 return res;
2416 /******************************************************************
2417 * DeletePortW [WINSPOOL.@]
2419 * Delete a specific Port
2421 * PARAMS
2422 * pName [I] Servername or NULL (local Computer)
2423 * hWnd [I] Handle to parent Window for the Dialog-Box
2424 * pPortName [I] Name of the Port, that should be deleted
2426 * RETURNS
2427 * Success: TRUE
2428 * Failure: FALSE
2431 BOOL WINAPI DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
2433 monitor_t * pm;
2434 monitor_t * pui;
2435 DWORD res;
2437 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
2439 if (pName && pName[0]) {
2440 SetLastError(ERROR_INVALID_PARAMETER);
2441 return FALSE;
2444 if (!pPortName) {
2445 SetLastError(RPC_X_NULL_REF_POINTER);
2446 return FALSE;
2449 /* an empty Portname is Invalid */
2450 if (!pPortName[0]) {
2451 SetLastError(ERROR_NOT_SUPPORTED);
2452 return FALSE;
2455 pm = monitor_load_by_port(pPortName);
2456 if (pm && pm->monitor && pm->monitor->pfnDeletePort) {
2457 TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
2458 res = pm->monitor->pfnDeletePort(pName, hWnd, pPortName);
2459 TRACE("got %d with %u\n", res, GetLastError());
2461 else
2463 pui = monitor_loadui(pm);
2464 if (pui && pui->monitorUI && pui->monitorUI->pfnDeletePortUI) {
2465 TRACE("use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
2466 res = pui->monitorUI->pfnDeletePortUI(pName, hWnd, pPortName);
2467 TRACE("got %d with %u\n", res, GetLastError());
2469 else
2471 FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
2472 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
2474 /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
2475 SetLastError(ERROR_NOT_SUPPORTED);
2476 res = FALSE;
2478 monitor_unload(pui);
2480 monitor_unload(pm);
2482 TRACE("returning %d with %u\n", res, GetLastError());
2483 return res;
2486 /******************************************************************************
2487 * SetPrinterW [WINSPOOL.@]
2489 BOOL WINAPI SetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command)
2491 FIXME("(%p, %d, %p, %d): stub\n", hPrinter, Level, pPrinter, Command);
2492 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2493 return FALSE;
2496 /******************************************************************************
2497 * WritePrinter [WINSPOOL.@]
2499 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
2501 opened_printer_t *printer;
2502 BOOL ret = FALSE;
2504 TRACE("(%p, %p, %d, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
2506 EnterCriticalSection(&printer_handles_cs);
2507 printer = get_opened_printer(hPrinter);
2508 if(!printer)
2510 SetLastError(ERROR_INVALID_HANDLE);
2511 goto end;
2514 if(!printer->doc)
2516 SetLastError(ERROR_SPL_NO_STARTDOC);
2517 goto end;
2520 ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
2521 end:
2522 LeaveCriticalSection(&printer_handles_cs);
2523 return ret;
2526 /*****************************************************************************
2527 * AddFormA [WINSPOOL.@]
2529 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2531 FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2532 return 1;
2535 /*****************************************************************************
2536 * AddFormW [WINSPOOL.@]
2538 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2540 FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2541 return 1;
2544 /*****************************************************************************
2545 * AddJobA [WINSPOOL.@]
2547 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2549 BOOL ret;
2550 BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2551 DWORD needed;
2553 if(Level != 1) {
2554 SetLastError(ERROR_INVALID_LEVEL);
2555 return FALSE;
2558 ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
2560 if(ret) {
2561 ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
2562 DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
2563 *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
2564 if(*pcbNeeded > cbBuf) {
2565 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2566 ret = FALSE;
2567 } else {
2568 ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
2569 addjobA->JobId = addjobW->JobId;
2570 addjobA->Path = (char *)(addjobA + 1);
2571 WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
2574 return ret;
2577 /*****************************************************************************
2578 * AddJobW [WINSPOOL.@]
2580 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2582 opened_printer_t *printer;
2583 job_t *job;
2584 BOOL ret = FALSE;
2585 static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
2586 static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
2587 WCHAR path[MAX_PATH], filename[MAX_PATH];
2588 DWORD len;
2589 ADDJOB_INFO_1W *addjob;
2591 TRACE("(%p,%d,%p,%d,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
2593 EnterCriticalSection(&printer_handles_cs);
2595 printer = get_opened_printer(hPrinter);
2597 if(!printer) {
2598 SetLastError(ERROR_INVALID_HANDLE);
2599 goto end;
2602 if(Level != 1) {
2603 SetLastError(ERROR_INVALID_LEVEL);
2604 goto end;
2607 job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
2608 if(!job)
2609 goto end;
2611 job->job_id = InterlockedIncrement(&next_job_id);
2613 len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
2614 if(path[len - 1] != '\\')
2615 path[len++] = '\\';
2616 memcpy(path + len, spool_path, sizeof(spool_path));
2617 sprintfW(filename, fmtW, path, job->job_id);
2619 len = strlenW(filename);
2620 job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2621 memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
2622 job->document_title = strdupW(default_doc_title);
2623 list_add_tail(&printer->queue->jobs, &job->entry);
2625 *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
2626 if(*pcbNeeded <= cbBuf) {
2627 addjob = (ADDJOB_INFO_1W*)pData;
2628 addjob->JobId = job->job_id;
2629 addjob->Path = (WCHAR *)(addjob + 1);
2630 memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
2631 ret = TRUE;
2632 } else
2633 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2635 end:
2636 LeaveCriticalSection(&printer_handles_cs);
2637 return ret;
2640 /*****************************************************************************
2641 * GetPrintProcessorDirectoryA [WINSPOOL.@]
2643 * Return the PATH for the Print-Processors
2645 * See GetPrintProcessorDirectoryW.
2649 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
2650 DWORD level, LPBYTE Info,
2651 DWORD cbBuf, LPDWORD pcbNeeded)
2653 LPWSTR serverW = NULL;
2654 LPWSTR envW = NULL;
2655 BOOL ret;
2656 INT len;
2658 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(server),
2659 debugstr_a(env), level, Info, cbBuf, pcbNeeded);
2662 if (server) {
2663 len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
2664 serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2665 MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
2668 if (env) {
2669 len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
2670 envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2671 MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
2674 /* NT requires the buffersize from GetPrintProcessorDirectoryW also
2675 for GetPrintProcessorDirectoryA and WC2MB is done in-place.
2677 ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info,
2678 cbBuf, pcbNeeded);
2680 if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
2681 cbBuf, NULL, NULL) > 0;
2684 TRACE(" required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
2685 HeapFree(GetProcessHeap(), 0, envW);
2686 HeapFree(GetProcessHeap(), 0, serverW);
2687 return ret;
2690 /*****************************************************************************
2691 * GetPrintProcessorDirectoryW [WINSPOOL.@]
2693 * Return the PATH for the Print-Processors
2695 * PARAMS
2696 * server [I] Servername (NT only) or NULL (local Computer)
2697 * env [I] Printing-Environment (see below) or NULL (Default)
2698 * level [I] Structure-Level (must be 1)
2699 * Info [O] PTR to Buffer that receives the Result
2700 * cbBuf [I] Size of Buffer at "Info"
2701 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
2702 * required for the Buffer at "Info"
2704 * RETURNS
2705 * Success: TRUE and in pcbNeeded the Bytes used in Info
2706 * Failure: FALSE and in pcbNeeded the Bytes required for Info,
2707 * if cbBuf is too small
2709 * Native Values returned in Info on Success:
2710 *| NT(Windows NT x86): "%winsysdir%\\spool\\PRTPROCS\\w32x86"
2711 *| NT(Windows 4.0): "%winsysdir%\\spool\\PRTPROCS\\win40"
2712 *| win9x(Windows 4.0): "%winsysdir%"
2714 * "%winsysdir%" is the Value from GetSystemDirectoryW()
2716 * BUGS
2717 * Only NULL or "" is supported for server
2720 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
2721 DWORD level, LPBYTE Info,
2722 DWORD cbBuf, LPDWORD pcbNeeded)
2724 DWORD needed;
2725 const printenv_t * env_t;
2727 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(server),
2728 debugstr_w(env), level, Info, cbBuf, pcbNeeded);
2730 if(server != NULL && server[0]) {
2731 FIXME("server not supported: %s\n", debugstr_w(server));
2732 SetLastError(ERROR_INVALID_PARAMETER);
2733 return FALSE;
2736 env_t = validate_envW(env);
2737 if(!env_t) return FALSE; /* environment invalid or unsupported */
2739 if(level != 1) {
2740 WARN("(Level: %d) is ignored in win9x\n", level);
2741 SetLastError(ERROR_INVALID_LEVEL);
2742 return FALSE;
2745 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2746 needed = GetSystemDirectoryW(NULL, 0);
2747 /* add the Size for the Subdirectories */
2748 needed += lstrlenW(spoolprtprocsW);
2749 needed += lstrlenW(env_t->subdir);
2750 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
2752 if(pcbNeeded) *pcbNeeded = needed;
2753 TRACE ("required: 0x%x/%d\n", needed, needed);
2754 if (needed > cbBuf) {
2755 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2756 return FALSE;
2758 if(pcbNeeded == NULL) {
2759 /* NT: RPC_X_NULL_REF_POINTER, 9x: ignored */
2760 WARN("(pcbNeeded == NULL) is ignored in win9x\n");
2761 SetLastError(RPC_X_NULL_REF_POINTER);
2762 return FALSE;
2764 if(Info == NULL) {
2765 /* NT: RPC_X_NULL_REF_POINTER, 9x: ERROR_INVALID_PARAMETER */
2766 SetLastError(RPC_X_NULL_REF_POINTER);
2767 return FALSE;
2770 GetSystemDirectoryW((LPWSTR) Info, cbBuf/sizeof(WCHAR));
2771 /* add the Subdirectories */
2772 lstrcatW((LPWSTR) Info, spoolprtprocsW);
2773 lstrcatW((LPWSTR) Info, env_t->subdir);
2774 TRACE(" => %s\n", debugstr_w((LPWSTR) Info));
2775 return TRUE;
2778 /*****************************************************************************
2779 * WINSPOOL_OpenDriverReg [internal]
2781 * opens the registry for the printer drivers depending on the given input
2782 * variable pEnvironment
2784 * RETURNS:
2785 * the opened hkey on success
2786 * NULL on error
2788 static HKEY WINSPOOL_OpenDriverReg( LPCVOID pEnvironment, BOOL unicode)
2790 HKEY retval = NULL;
2791 LPWSTR buffer;
2792 const printenv_t * env;
2794 TRACE("(%s, %d)\n",
2795 (unicode) ? debugstr_w(pEnvironment) : debugstr_a(pEnvironment), unicode);
2797 if (!pEnvironment || unicode) {
2798 /* pEnvironment was NULL or an Unicode-String: use it direct */
2799 env = validate_envW(pEnvironment);
2801 else
2803 /* pEnvironment was an ANSI-String: convert to unicode first */
2804 LPWSTR buffer;
2805 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, NULL, 0);
2806 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2807 if (buffer) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pEnvironment, -1, buffer, len);
2808 env = validate_envW(buffer);
2809 HeapFree(GetProcessHeap(), 0, buffer);
2811 if (!env) return NULL;
2813 buffer = HeapAlloc( GetProcessHeap(), 0,
2814 (strlenW(DriversW) + strlenW(env->envname) +
2815 strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2816 if(buffer) {
2817 wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2818 RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2819 HeapFree(GetProcessHeap(), 0, buffer);
2821 return retval;
2824 /*****************************************************************************
2825 * AddPrinterW [WINSPOOL.@]
2827 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2829 PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2830 LPDEVMODEA dmA;
2831 LPDEVMODEW dmW;
2832 HANDLE retval;
2833 HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2834 LONG size;
2835 static const WCHAR attributesW[] = {'A','t','t','r','i','b','u','t','e','s',0},
2836 default_devmodeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0},
2837 priorityW[] = {'P','r','i','o','r','i','t','y',0},
2838 start_timeW[] = {'S','t','a','r','t','T','i','m','e',0},
2839 statusW[] = {'S','t','a','t','u','s',0},
2840 until_timeW[] = {'U','n','t','i','l','T','i','m','e',0};
2842 TRACE("(%s,%d,%p)\n", debugstr_w(pName), Level, pPrinter);
2844 if(pName != NULL) {
2845 ERR("pName = %s - unsupported\n", debugstr_w(pName));
2846 SetLastError(ERROR_INVALID_PARAMETER);
2847 return 0;
2849 if(Level != 2) {
2850 ERR("Level = %d, unsupported!\n", Level);
2851 SetLastError(ERROR_INVALID_LEVEL);
2852 return 0;
2854 if(!pPrinter) {
2855 SetLastError(ERROR_INVALID_PARAMETER);
2856 return 0;
2858 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2859 ERROR_SUCCESS) {
2860 ERR("Can't create Printers key\n");
2861 return 0;
2863 if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2864 if (!RegQueryValueW(hkeyPrinter, attributesW, NULL, NULL)) {
2865 SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2866 RegCloseKey(hkeyPrinter);
2867 RegCloseKey(hkeyPrinters);
2868 return 0;
2870 RegCloseKey(hkeyPrinter);
2872 hkeyDrivers = WINSPOOL_OpenDriverReg( NULL, TRUE);
2873 if(!hkeyDrivers) {
2874 ERR("Can't create Drivers key\n");
2875 RegCloseKey(hkeyPrinters);
2876 return 0;
2878 if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2879 ERROR_SUCCESS) {
2880 WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2881 RegCloseKey(hkeyPrinters);
2882 RegCloseKey(hkeyDrivers);
2883 SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2884 return 0;
2886 RegCloseKey(hkeyDriver);
2887 RegCloseKey(hkeyDrivers);
2889 if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) { /* FIXME */
2890 FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2891 SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2892 RegCloseKey(hkeyPrinters);
2893 return 0;
2896 if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2897 ERROR_SUCCESS) {
2898 FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2899 SetLastError(ERROR_INVALID_PRINTER_NAME);
2900 RegCloseKey(hkeyPrinters);
2901 return 0;
2903 RegSetValueExW(hkeyPrinter, attributesW, 0, REG_DWORD,
2904 (LPBYTE)&pi->Attributes, sizeof(DWORD));
2905 set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2907 /* See if we can load the driver. We may need the devmode structure anyway
2909 * FIXME:
2910 * Note that DocumentPropertiesW will briefly try to open the printer we
2911 * just create to find a DEVMODEA struct (it will use the WINEPS default
2912 * one in case it is not there, so we are ok).
2914 size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2916 if(size < 0) {
2917 FIXME("DocumentPropertiesW on printer %s fails\n", debugstr_w(pi->pPrinterName));
2918 size = sizeof(DEVMODEW);
2920 if(pi->pDevMode)
2921 dmW = pi->pDevMode;
2922 else
2924 dmW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
2925 dmW->dmSize = size;
2926 if (0>DocumentPropertiesW(0,0,pi->pPrinterName,dmW,NULL,DM_OUT_BUFFER))
2928 WARN("DocumentPropertiesW on printer %s failed!\n", debugstr_w(pi->pPrinterName));
2929 HeapFree(GetProcessHeap(),0,dmW);
2930 dmW=NULL;
2932 else
2934 /* set devmode to printer name */
2935 lstrcpynW(dmW->dmDeviceName, pi->pPrinterName, CCHDEVICENAME);
2939 /* Write DEVMODEA not DEVMODEW into reg. This is what win9x does
2940 and we support these drivers. NT writes DEVMODEW so somehow
2941 we'll need to distinguish between these when we support NT
2942 drivers */
2943 if (dmW)
2945 dmA = DEVMODEdupWtoA(GetProcessHeap(), dmW);
2946 RegSetValueExW(hkeyPrinter, default_devmodeW, 0, REG_BINARY,
2947 (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra);
2948 HeapFree(GetProcessHeap(), 0, dmA);
2949 if(!pi->pDevMode)
2950 HeapFree(GetProcessHeap(), 0, dmW);
2952 set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
2953 set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
2954 set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
2955 set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
2957 set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
2958 set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
2959 set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
2960 RegSetValueExW(hkeyPrinter, priorityW, 0, REG_DWORD,
2961 (LPBYTE)&pi->Priority, sizeof(DWORD));
2962 set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
2963 set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
2964 RegSetValueExW(hkeyPrinter, start_timeW, 0, REG_DWORD,
2965 (LPBYTE)&pi->StartTime, sizeof(DWORD));
2966 RegSetValueExW(hkeyPrinter, statusW, 0, REG_DWORD,
2967 (LPBYTE)&pi->Status, sizeof(DWORD));
2968 RegSetValueExW(hkeyPrinter, until_timeW, 0, REG_DWORD,
2969 (LPBYTE)&pi->UntilTime, sizeof(DWORD));
2971 RegCloseKey(hkeyPrinter);
2972 RegCloseKey(hkeyPrinters);
2973 if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
2974 ERR("OpenPrinter failing\n");
2975 return 0;
2977 return retval;
2980 /*****************************************************************************
2981 * AddPrinterA [WINSPOOL.@]
2983 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
2985 UNICODE_STRING pNameW;
2986 PWSTR pwstrNameW;
2987 PRINTER_INFO_2W *piW;
2988 PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
2989 HANDLE ret;
2991 TRACE("(%s,%d,%p): stub\n", debugstr_a(pName), Level, pPrinter);
2992 if(Level != 2) {
2993 ERR("Level = %d, unsupported!\n", Level);
2994 SetLastError(ERROR_INVALID_LEVEL);
2995 return 0;
2997 pwstrNameW = asciitounicode(&pNameW,pName);
2998 piW = PRINTER_INFO_2AtoW(GetProcessHeap(), piA);
3000 ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
3002 FREE_PRINTER_INFO_2W(GetProcessHeap(), piW);
3003 RtlFreeUnicodeString(&pNameW);
3004 return ret;
3008 /*****************************************************************************
3009 * ClosePrinter [WINSPOOL.@]
3011 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
3013 UINT_PTR i = (UINT_PTR)hPrinter;
3014 opened_printer_t *printer = NULL;
3015 BOOL ret = FALSE;
3017 TRACE("(%p)\n", hPrinter);
3019 EnterCriticalSection(&printer_handles_cs);
3021 if ((i > 0) && (i <= nb_printer_handles))
3022 printer = printer_handles[i - 1];
3025 if(printer)
3027 struct list *cursor, *cursor2;
3029 TRACE("%p: %s (hXcv: %p) for %s (doc: %p)\n", printer->pm,
3030 debugstr_w(printer->pm ? printer->pm->dllname : NULL),
3031 printer->hXcv, debugstr_w(printer->name), printer->doc );
3033 if(printer->doc)
3034 EndDocPrinter(hPrinter);
3036 if(InterlockedDecrement(&printer->queue->ref) == 0)
3038 LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
3040 job_t *job = LIST_ENTRY(cursor, job_t, entry);
3041 ScheduleJob(hPrinter, job->job_id);
3043 HeapFree(GetProcessHeap(), 0, printer->queue);
3045 if (printer->hXcv) printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
3046 monitor_unload(printer->pm);
3047 HeapFree(GetProcessHeap(), 0, printer->printername);
3048 HeapFree(GetProcessHeap(), 0, printer->name);
3049 HeapFree(GetProcessHeap(), 0, printer);
3050 printer_handles[i - 1] = NULL;
3051 ret = TRUE;
3053 LeaveCriticalSection(&printer_handles_cs);
3054 return ret;
3057 /*****************************************************************************
3058 * DeleteFormA [WINSPOOL.@]
3060 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
3062 FIXME("(%p,%s): stub\n", hPrinter, pFormName);
3063 return 1;
3066 /*****************************************************************************
3067 * DeleteFormW [WINSPOOL.@]
3069 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
3071 FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
3072 return 1;
3075 /*****************************************************************************
3076 * DeletePrinter [WINSPOOL.@]
3078 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
3080 LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
3081 HKEY hkeyPrinters, hkey;
3083 if(!lpNameW) {
3084 SetLastError(ERROR_INVALID_HANDLE);
3085 return FALSE;
3087 if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
3088 RegDeleteTreeW(hkeyPrinters, lpNameW);
3089 RegCloseKey(hkeyPrinters);
3091 WriteProfileStringW(devicesW, lpNameW, NULL);
3092 if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
3093 RegDeleteValueW(hkey, lpNameW);
3094 RegCloseKey(hkey);
3096 return TRUE;
3099 /*****************************************************************************
3100 * SetPrinterA [WINSPOOL.@]
3102 BOOL WINAPI SetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3103 DWORD Command)
3105 FIXME("(%p,%d,%p,%d): stub\n",hPrinter,Level,pPrinter,Command);
3106 return FALSE;
3109 /*****************************************************************************
3110 * SetJobA [WINSPOOL.@]
3112 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
3113 LPBYTE pJob, DWORD Command)
3115 BOOL ret;
3116 LPBYTE JobW;
3117 UNICODE_STRING usBuffer;
3119 TRACE("(%p, %d, %d, %p, %d)\n",hPrinter, JobId, Level, pJob, Command);
3121 /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
3122 are all ignored by SetJob, so we don't bother copying them */
3123 switch(Level)
3125 case 0:
3126 JobW = NULL;
3127 break;
3128 case 1:
3130 JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
3131 JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
3133 JobW = (LPBYTE)info1W;
3134 info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
3135 info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
3136 info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
3137 info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
3138 info1W->Status = info1A->Status;
3139 info1W->Priority = info1A->Priority;
3140 info1W->Position = info1A->Position;
3141 info1W->PagesPrinted = info1A->PagesPrinted;
3142 break;
3144 case 2:
3146 JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
3147 JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
3149 JobW = (LPBYTE)info2W;
3150 info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
3151 info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
3152 info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
3153 info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
3154 info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
3155 info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
3156 info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
3157 info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
3158 info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
3159 info2W->Status = info2A->Status;
3160 info2W->Priority = info2A->Priority;
3161 info2W->Position = info2A->Position;
3162 info2W->StartTime = info2A->StartTime;
3163 info2W->UntilTime = info2A->UntilTime;
3164 info2W->PagesPrinted = info2A->PagesPrinted;
3165 break;
3167 case 3:
3168 JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
3169 memcpy(JobW, pJob, sizeof(JOB_INFO_3));
3170 break;
3171 default:
3172 SetLastError(ERROR_INVALID_LEVEL);
3173 return FALSE;
3176 ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
3178 switch(Level)
3180 case 1:
3182 JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
3183 HeapFree(GetProcessHeap(), 0, info1W->pUserName);
3184 HeapFree(GetProcessHeap(), 0, info1W->pDocument);
3185 HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
3186 HeapFree(GetProcessHeap(), 0, info1W->pStatus);
3187 break;
3189 case 2:
3191 JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
3192 HeapFree(GetProcessHeap(), 0, info2W->pUserName);
3193 HeapFree(GetProcessHeap(), 0, info2W->pDocument);
3194 HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
3195 HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
3196 HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
3197 HeapFree(GetProcessHeap(), 0, info2W->pParameters);
3198 HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
3199 HeapFree(GetProcessHeap(), 0, info2W->pStatus);
3200 break;
3203 HeapFree(GetProcessHeap(), 0, JobW);
3205 return ret;
3208 /*****************************************************************************
3209 * SetJobW [WINSPOOL.@]
3211 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
3212 LPBYTE pJob, DWORD Command)
3214 BOOL ret = FALSE;
3215 job_t *job;
3217 TRACE("(%p, %d, %d, %p, %d)\n", hPrinter, JobId, Level, pJob, Command);
3218 FIXME("Ignoring everything other than document title\n");
3220 EnterCriticalSection(&printer_handles_cs);
3221 job = get_job(hPrinter, JobId);
3222 if(!job)
3223 goto end;
3225 switch(Level)
3227 case 0:
3228 break;
3229 case 1:
3231 JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
3232 HeapFree(GetProcessHeap(), 0, job->document_title);
3233 job->document_title = strdupW(info1->pDocument);
3234 break;
3236 case 2:
3238 JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
3239 HeapFree(GetProcessHeap(), 0, job->document_title);
3240 job->document_title = strdupW(info2->pDocument);
3241 break;
3243 case 3:
3244 break;
3245 default:
3246 SetLastError(ERROR_INVALID_LEVEL);
3247 goto end;
3249 ret = TRUE;
3250 end:
3251 LeaveCriticalSection(&printer_handles_cs);
3252 return ret;
3255 /*****************************************************************************
3256 * EndDocPrinter [WINSPOOL.@]
3258 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
3260 opened_printer_t *printer;
3261 BOOL ret = FALSE;
3262 TRACE("(%p)\n", hPrinter);
3264 EnterCriticalSection(&printer_handles_cs);
3266 printer = get_opened_printer(hPrinter);
3267 if(!printer)
3269 SetLastError(ERROR_INVALID_HANDLE);
3270 goto end;
3273 if(!printer->doc)
3275 SetLastError(ERROR_SPL_NO_STARTDOC);
3276 goto end;
3279 CloseHandle(printer->doc->hf);
3280 ScheduleJob(hPrinter, printer->doc->job_id);
3281 HeapFree(GetProcessHeap(), 0, printer->doc);
3282 printer->doc = NULL;
3283 ret = TRUE;
3284 end:
3285 LeaveCriticalSection(&printer_handles_cs);
3286 return ret;
3289 /*****************************************************************************
3290 * EndPagePrinter [WINSPOOL.@]
3292 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
3294 FIXME("(%p): stub\n", hPrinter);
3295 return TRUE;
3298 /*****************************************************************************
3299 * StartDocPrinterA [WINSPOOL.@]
3301 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3303 UNICODE_STRING usBuffer;
3304 DOC_INFO_2W doc2W;
3305 DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
3306 DWORD ret;
3308 /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
3309 or one (DOC_INFO_3) extra DWORDs */
3311 switch(Level) {
3312 case 2:
3313 doc2W.JobId = doc2->JobId;
3314 /* fall through */
3315 case 3:
3316 doc2W.dwMode = doc2->dwMode;
3317 /* fall through */
3318 case 1:
3319 doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
3320 doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
3321 doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
3322 break;
3324 default:
3325 SetLastError(ERROR_INVALID_LEVEL);
3326 return FALSE;
3329 ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
3331 HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
3332 HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
3333 HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
3335 return ret;
3338 /*****************************************************************************
3339 * StartDocPrinterW [WINSPOOL.@]
3341 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3343 DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
3344 opened_printer_t *printer;
3345 BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
3346 ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
3347 JOB_INFO_1W job_info;
3348 DWORD needed, ret = 0;
3349 HANDLE hf;
3350 WCHAR *filename;
3352 TRACE("(hPrinter = %p, Level = %d, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
3353 hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
3354 debugstr_w(doc->pDatatype));
3356 if(Level < 1 || Level > 3)
3358 SetLastError(ERROR_INVALID_LEVEL);
3359 return 0;
3362 EnterCriticalSection(&printer_handles_cs);
3363 printer = get_opened_printer(hPrinter);
3364 if(!printer)
3366 SetLastError(ERROR_INVALID_HANDLE);
3367 goto end;
3370 if(printer->doc)
3372 SetLastError(ERROR_INVALID_PRINTER_STATE);
3373 goto end;
3376 /* Even if we're printing to a file we still add a print job, we'll
3377 just ignore the spool file name */
3379 if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
3381 ERR("AddJob failed gle %u\n", GetLastError());
3382 goto end;
3385 if(doc->pOutputFile)
3386 filename = doc->pOutputFile;
3387 else
3388 filename = addjob->Path;
3390 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3391 if(hf == INVALID_HANDLE_VALUE)
3392 goto end;
3394 memset(&job_info, 0, sizeof(job_info));
3395 job_info.pDocument = doc->pDocName;
3396 SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
3398 printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
3399 printer->doc->hf = hf;
3400 ret = printer->doc->job_id = addjob->JobId;
3401 end:
3402 LeaveCriticalSection(&printer_handles_cs);
3404 return ret;
3407 /*****************************************************************************
3408 * StartPagePrinter [WINSPOOL.@]
3410 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
3412 FIXME("(%p): stub\n", hPrinter);
3413 return TRUE;
3416 /*****************************************************************************
3417 * GetFormA [WINSPOOL.@]
3419 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3420 LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3422 FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,pFormName,
3423 Level,pForm,cbBuf,pcbNeeded);
3424 return FALSE;
3427 /*****************************************************************************
3428 * GetFormW [WINSPOOL.@]
3430 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3431 LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3433 FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,
3434 debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
3435 return FALSE;
3438 /*****************************************************************************
3439 * SetFormA [WINSPOOL.@]
3441 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3442 LPBYTE pForm)
3444 FIXME("(%p,%s,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3445 return FALSE;
3448 /*****************************************************************************
3449 * SetFormW [WINSPOOL.@]
3451 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3452 LPBYTE pForm)
3454 FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3455 return FALSE;
3458 /*****************************************************************************
3459 * ReadPrinter [WINSPOOL.@]
3461 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
3462 LPDWORD pNoBytesRead)
3464 FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
3465 return FALSE;
3468 /*****************************************************************************
3469 * ResetPrinterA [WINSPOOL.@]
3471 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
3473 FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3474 return FALSE;
3477 /*****************************************************************************
3478 * ResetPrinterW [WINSPOOL.@]
3480 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
3482 FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3483 return FALSE;
3486 /*****************************************************************************
3487 * WINSPOOL_GetDWORDFromReg
3489 * Return DWORD associated with ValueName from hkey.
3491 static DWORD WINSPOOL_GetDWORDFromReg(HKEY hkey, LPCSTR ValueName)
3493 DWORD sz = sizeof(DWORD), type, value = 0;
3494 LONG ret;
3496 ret = RegQueryValueExA(hkey, ValueName, 0, &type, (LPBYTE)&value, &sz);
3498 if(ret != ERROR_SUCCESS) {
3499 WARN("Got ret = %d on name %s\n", ret, ValueName);
3500 return 0;
3502 if(type != REG_DWORD) {
3503 ERR("Got type %d\n", type);
3504 return 0;
3506 return value;
3510 /*****************************************************************************
3511 * get_filename_from_reg [internal]
3513 * Get ValueName from hkey storing result in out
3514 * when the Value in the registry has only a filename, use driverdir as prefix
3515 * outlen is space left in out
3516 * String is stored either as unicode or ascii
3520 static BOOL get_filename_from_reg(HKEY hkey, LPCWSTR driverdir, DWORD dirlen, LPCWSTR ValueName,
3521 LPBYTE out, DWORD outlen, LPDWORD needed, BOOL unicode)
3523 WCHAR filename[MAX_PATH];
3524 DWORD size;
3525 DWORD type;
3526 LONG ret;
3527 LPWSTR buffer = filename;
3528 LPWSTR ptr;
3530 *needed = 0;
3531 size = sizeof(filename);
3532 buffer[0] = '\0';
3533 ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3534 if (ret == ERROR_MORE_DATA) {
3535 TRACE("need dynamic buffer: %u\n", size);
3536 buffer = HeapAlloc(GetProcessHeap(), 0, size);
3537 if (!buffer) {
3538 /* No Memory is bad */
3539 return FALSE;
3541 buffer[0] = '\0';
3542 ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3545 if ((ret != ERROR_SUCCESS) || (!buffer[0])) {
3546 if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3547 return FALSE;
3550 ptr = buffer;
3551 while (ptr) {
3552 /* do we have a full path ? */
3553 ret = (((buffer[0] == '\\') && (buffer[1] == '\\')) ||
3554 (buffer[0] && (buffer[1] == ':') && (buffer[2] == '\\')) );
3556 if (!ret) {
3557 /* we must build the full Path */
3558 *needed += dirlen;
3559 if ((out) && (outlen > dirlen)) {
3560 if (unicode) {
3561 lstrcpyW((LPWSTR)out, driverdir);
3563 else
3565 WideCharToMultiByte(CP_ACP, 0, driverdir, -1, (LPSTR)out, outlen, NULL, NULL);
3567 out += dirlen;
3568 outlen -= dirlen;
3570 else
3571 out = NULL;
3574 /* write the filename */
3575 if (unicode) {
3576 size = (lstrlenW(ptr) + 1) * sizeof(WCHAR);
3577 if ((out) && (outlen >= size)) {
3578 lstrcpyW((LPWSTR)out, ptr);
3579 out += size;
3580 outlen -= size;
3582 else
3583 out = NULL;
3585 else
3587 size = WideCharToMultiByte(CP_ACP, 0, ptr, -1, NULL, 0, NULL, NULL);
3588 if ((out) && (outlen >= size)) {
3589 WideCharToMultiByte(CP_ACP, 0, ptr, -1, (LPSTR)out, outlen, NULL, NULL);
3590 out += size;
3591 outlen -= size;
3593 else
3594 out = NULL;
3596 *needed += size;
3597 ptr += lstrlenW(ptr)+1;
3598 if ((type != REG_MULTI_SZ) || (!ptr[0])) ptr = NULL;
3601 if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3603 /* write the multisz-termination */
3604 if (type == REG_MULTI_SZ) {
3605 size = (unicode) ? sizeof(WCHAR) : 1;
3607 *needed += size;
3608 if (out && (outlen >= size)) {
3609 memset (out, 0, size);
3612 return TRUE;
3615 /*****************************************************************************
3616 * WINSPOOL_GetStringFromReg
3618 * Get ValueName from hkey storing result in ptr. buflen is space left in ptr
3619 * String is stored either as unicode or ascii.
3620 * Bit of a hack here to get the ValueName if we want ascii.
3622 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
3623 DWORD buflen, DWORD *needed,
3624 BOOL unicode)
3626 DWORD sz = buflen, type;
3627 LONG ret;
3629 if(unicode)
3630 ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3631 else {
3632 LPSTR ValueNameA = strdupWtoA(ValueName);
3633 ret = RegQueryValueExA(hkey, ValueNameA, 0, &type, ptr, &sz);
3634 HeapFree(GetProcessHeap(),0,ValueNameA);
3636 if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
3637 WARN("Got ret = %d\n", ret);
3638 *needed = 0;
3639 return FALSE;
3641 /* add space for terminating '\0' */
3642 sz += unicode ? sizeof(WCHAR) : 1;
3643 *needed = sz;
3645 if (ptr)
3646 TRACE("%s: %s\n", debugstr_w(ValueName), unicode ? debugstr_w((LPCWSTR)ptr) : debugstr_a((LPCSTR)ptr));
3648 return TRUE;
3651 /*****************************************************************************
3652 * WINSPOOL_GetDefaultDevMode
3654 * Get a default DevMode values for wineps.
3655 * FIXME - use ppd.
3658 static void WINSPOOL_GetDefaultDevMode(
3659 LPBYTE ptr,
3660 DWORD buflen, DWORD *needed,
3661 BOOL unicode)
3663 DEVMODEA dm;
3664 static const char szwps[] = "wineps.drv";
3666 /* fill default DEVMODE - should be read from ppd... */
3667 ZeroMemory( &dm, sizeof(dm) );
3668 memcpy(dm.dmDeviceName,szwps,sizeof szwps);
3669 dm.dmSpecVersion = DM_SPECVERSION;
3670 dm.dmDriverVersion = 1;
3671 dm.dmSize = sizeof(DEVMODEA);
3672 dm.dmDriverExtra = 0;
3673 dm.dmFields =
3674 DM_ORIENTATION | DM_PAPERSIZE |
3675 DM_PAPERLENGTH | DM_PAPERWIDTH |
3676 DM_SCALE |
3677 DM_COPIES |
3678 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
3679 DM_YRESOLUTION | DM_TTOPTION;
3681 dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
3682 dm.u1.s1.dmPaperSize = DMPAPER_A4;
3683 dm.u1.s1.dmPaperLength = 2970;
3684 dm.u1.s1.dmPaperWidth = 2100;
3686 dm.dmScale = 100;
3687 dm.dmCopies = 1;
3688 dm.dmDefaultSource = DMBIN_AUTO;
3689 dm.dmPrintQuality = DMRES_MEDIUM;
3690 /* dm.dmColor */
3691 /* dm.dmDuplex */
3692 dm.dmYResolution = 300; /* 300dpi */
3693 dm.dmTTOption = DMTT_BITMAP;
3694 /* dm.dmCollate */
3695 /* dm.dmFormName */
3696 /* dm.dmLogPixels */
3697 /* dm.dmBitsPerPel */
3698 /* dm.dmPelsWidth */
3699 /* dm.dmPelsHeight */
3700 /* dm.dmDisplayFlags */
3701 /* dm.dmDisplayFrequency */
3702 /* dm.dmICMMethod */
3703 /* dm.dmICMIntent */
3704 /* dm.dmMediaType */
3705 /* dm.dmDitherType */
3706 /* dm.dmReserved1 */
3707 /* dm.dmReserved2 */
3708 /* dm.dmPanningWidth */
3709 /* dm.dmPanningHeight */
3711 if(unicode) {
3712 if(buflen >= sizeof(DEVMODEW)) {
3713 DEVMODEW *pdmW = GdiConvertToDevmodeW(&dm);
3714 memcpy(ptr, pdmW, sizeof(DEVMODEW));
3715 HeapFree(GetProcessHeap(),0,pdmW);
3717 *needed = sizeof(DEVMODEW);
3719 else
3721 if(buflen >= sizeof(DEVMODEA)) {
3722 memcpy(ptr, &dm, sizeof(DEVMODEA));
3724 *needed = sizeof(DEVMODEA);
3728 /*****************************************************************************
3729 * WINSPOOL_GetDevModeFromReg
3731 * Get ValueName from hkey storing result in ptr. buflen is space left in ptr
3732 * DevMode is stored either as unicode or ascii.
3734 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
3735 LPBYTE ptr,
3736 DWORD buflen, DWORD *needed,
3737 BOOL unicode)
3739 DWORD sz = buflen, type;
3740 LONG ret;
3742 if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
3743 ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3744 if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
3745 if (sz < sizeof(DEVMODEA))
3747 TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
3748 return FALSE;
3750 /* ensures that dmSize is not erratically bogus if registry is invalid */
3751 if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
3752 ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
3753 if(unicode) {
3754 sz += (CCHDEVICENAME + CCHFORMNAME);
3755 if(buflen >= sz) {
3756 DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
3757 memcpy(ptr, dmW, sz);
3758 HeapFree(GetProcessHeap(),0,dmW);
3761 *needed = sz;
3762 return TRUE;
3765 /*********************************************************************
3766 * WINSPOOL_GetPrinter_1
3768 * Fills out a PRINTER_INFO_1A|W struct storing the strings in buf.
3769 * The strings are either stored as unicode or ascii.
3771 static BOOL WINSPOOL_GetPrinter_1(HKEY hkeyPrinter, PRINTER_INFO_1W *pi1,
3772 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3773 BOOL unicode)
3775 DWORD size, left = cbBuf;
3776 BOOL space = (cbBuf > 0);
3777 LPBYTE ptr = buf;
3779 *pcbNeeded = 0;
3781 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3782 unicode)) {
3783 if(space && size <= left) {
3784 pi1->pName = (LPWSTR)ptr;
3785 ptr += size;
3786 left -= size;
3787 } else
3788 space = FALSE;
3789 *pcbNeeded += size;
3792 /* FIXME: pDescription should be something like "Name,Driver_Name,". */
3793 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3794 unicode)) {
3795 if(space && size <= left) {
3796 pi1->pDescription = (LPWSTR)ptr;
3797 ptr += size;
3798 left -= size;
3799 } else
3800 space = FALSE;
3801 *pcbNeeded += size;
3804 if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3805 unicode)) {
3806 if(space && size <= left) {
3807 pi1->pComment = (LPWSTR)ptr;
3808 ptr += size;
3809 left -= size;
3810 } else
3811 space = FALSE;
3812 *pcbNeeded += size;
3815 if(pi1) pi1->Flags = PRINTER_ENUM_ICON8; /* We're a printer */
3817 if(!space && pi1) /* zero out pi1 if we can't completely fill buf */
3818 memset(pi1, 0, sizeof(*pi1));
3820 return space;
3822 /*********************************************************************
3823 * WINSPOOL_GetPrinter_2
3825 * Fills out a PRINTER_INFO_2A|W struct storing the strings in buf.
3826 * The strings are either stored as unicode or ascii.
3828 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
3829 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3830 BOOL unicode)
3832 DWORD size, left = cbBuf;
3833 BOOL space = (cbBuf > 0);
3834 LPBYTE ptr = buf;
3836 *pcbNeeded = 0;
3838 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3839 unicode)) {
3840 if(space && size <= left) {
3841 pi2->pPrinterName = (LPWSTR)ptr;
3842 ptr += size;
3843 left -= size;
3844 } else
3845 space = FALSE;
3846 *pcbNeeded += size;
3848 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size,
3849 unicode)) {
3850 if(space && size <= left) {
3851 pi2->pShareName = (LPWSTR)ptr;
3852 ptr += size;
3853 left -= size;
3854 } else
3855 space = FALSE;
3856 *pcbNeeded += size;
3858 if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
3859 unicode)) {
3860 if(space && size <= left) {
3861 pi2->pPortName = (LPWSTR)ptr;
3862 ptr += size;
3863 left -= size;
3864 } else
3865 space = FALSE;
3866 *pcbNeeded += size;
3868 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left,
3869 &size, unicode)) {
3870 if(space && size <= left) {
3871 pi2->pDriverName = (LPWSTR)ptr;
3872 ptr += size;
3873 left -= size;
3874 } else
3875 space = FALSE;
3876 *pcbNeeded += size;
3878 if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size,
3879 unicode)) {
3880 if(space && size <= left) {
3881 pi2->pComment = (LPWSTR)ptr;
3882 ptr += size;
3883 left -= size;
3884 } else
3885 space = FALSE;
3886 *pcbNeeded += size;
3888 if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size,
3889 unicode)) {
3890 if(space && size <= left) {
3891 pi2->pLocation = (LPWSTR)ptr;
3892 ptr += size;
3893 left -= size;
3894 } else
3895 space = FALSE;
3896 *pcbNeeded += size;
3898 if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left,
3899 &size, unicode)) {
3900 if(space && size <= left) {
3901 pi2->pDevMode = (LPDEVMODEW)ptr;
3902 ptr += size;
3903 left -= size;
3904 } else
3905 space = FALSE;
3906 *pcbNeeded += size;
3908 else
3910 WINSPOOL_GetDefaultDevMode(ptr, left, &size, unicode);
3911 if(space && size <= left) {
3912 pi2->pDevMode = (LPDEVMODEW)ptr;
3913 ptr += size;
3914 left -= size;
3915 } else
3916 space = FALSE;
3917 *pcbNeeded += size;
3919 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left,
3920 &size, unicode)) {
3921 if(space && size <= left) {
3922 pi2->pSepFile = (LPWSTR)ptr;
3923 ptr += size;
3924 left -= size;
3925 } else
3926 space = FALSE;
3927 *pcbNeeded += size;
3929 if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left,
3930 &size, unicode)) {
3931 if(space && size <= left) {
3932 pi2->pPrintProcessor = (LPWSTR)ptr;
3933 ptr += size;
3934 left -= size;
3935 } else
3936 space = FALSE;
3937 *pcbNeeded += size;
3939 if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left,
3940 &size, unicode)) {
3941 if(space && size <= left) {
3942 pi2->pDatatype = (LPWSTR)ptr;
3943 ptr += size;
3944 left -= size;
3945 } else
3946 space = FALSE;
3947 *pcbNeeded += size;
3949 if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left,
3950 &size, unicode)) {
3951 if(space && size <= left) {
3952 pi2->pParameters = (LPWSTR)ptr;
3953 ptr += size;
3954 left -= size;
3955 } else
3956 space = FALSE;
3957 *pcbNeeded += size;
3959 if(pi2) {
3960 pi2->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
3961 pi2->Priority = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Priority");
3962 pi2->DefaultPriority = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
3963 "Default Priority");
3964 pi2->StartTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "StartTime");
3965 pi2->UntilTime = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "UntilTime");
3968 if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
3969 memset(pi2, 0, sizeof(*pi2));
3971 return space;
3974 /*********************************************************************
3975 * WINSPOOL_GetPrinter_4
3977 * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
3979 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
3980 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
3981 BOOL unicode)
3983 DWORD size, left = cbBuf;
3984 BOOL space = (cbBuf > 0);
3985 LPBYTE ptr = buf;
3987 *pcbNeeded = 0;
3989 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
3990 unicode)) {
3991 if(space && size <= left) {
3992 pi4->pPrinterName = (LPWSTR)ptr;
3993 ptr += size;
3994 left -= size;
3995 } else
3996 space = FALSE;
3997 *pcbNeeded += size;
3999 if(pi4) {
4000 pi4->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
4003 if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
4004 memset(pi4, 0, sizeof(*pi4));
4006 return space;
4009 /*********************************************************************
4010 * WINSPOOL_GetPrinter_5
4012 * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
4014 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
4015 LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded,
4016 BOOL unicode)
4018 DWORD size, left = cbBuf;
4019 BOOL space = (cbBuf > 0);
4020 LPBYTE ptr = buf;
4022 *pcbNeeded = 0;
4024 if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size,
4025 unicode)) {
4026 if(space && size <= left) {
4027 pi5->pPrinterName = (LPWSTR)ptr;
4028 ptr += size;
4029 left -= size;
4030 } else
4031 space = FALSE;
4032 *pcbNeeded += size;
4034 if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size,
4035 unicode)) {
4036 if(space && size <= left) {
4037 pi5->pPortName = (LPWSTR)ptr;
4038 ptr += size;
4039 left -= size;
4040 } else
4041 space = FALSE;
4042 *pcbNeeded += size;
4044 if(pi5) {
4045 pi5->Attributes = WINSPOOL_GetDWORDFromReg(hkeyPrinter, "Attributes");
4046 pi5->DeviceNotSelectedTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
4047 "dnsTimeout");
4048 pi5->TransmissionRetryTimeout = WINSPOOL_GetDWORDFromReg(hkeyPrinter,
4049 "txTimeout");
4052 if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
4053 memset(pi5, 0, sizeof(*pi5));
4055 return space;
4058 /*****************************************************************************
4059 * WINSPOOL_GetPrinter
4061 * Implementation of GetPrinterA|W. Relies on PRINTER_INFO_*W being
4062 * essentially the same as PRINTER_INFO_*A. i.e. the structure itself is
4063 * just a collection of pointers to strings.
4065 static BOOL WINSPOOL_GetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4066 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
4068 LPCWSTR name;
4069 DWORD size, needed = 0;
4070 LPBYTE ptr = NULL;
4071 HKEY hkeyPrinter, hkeyPrinters;
4072 BOOL ret;
4074 TRACE("(%p,%d,%p,%d,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
4076 if (!(name = get_opened_printer_name(hPrinter))) {
4077 SetLastError(ERROR_INVALID_HANDLE);
4078 return FALSE;
4081 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4082 ERROR_SUCCESS) {
4083 ERR("Can't create Printers key\n");
4084 return FALSE;
4086 if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter) != ERROR_SUCCESS)
4088 ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4089 RegCloseKey(hkeyPrinters);
4090 SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4091 return FALSE;
4094 switch(Level) {
4095 case 2:
4097 PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
4099 size = sizeof(PRINTER_INFO_2W);
4100 if(size <= cbBuf) {
4101 ptr = pPrinter + size;
4102 cbBuf -= size;
4103 memset(pPrinter, 0, size);
4104 } else {
4105 pi2 = NULL;
4106 cbBuf = 0;
4108 ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed,
4109 unicode);
4110 needed += size;
4111 break;
4114 case 4:
4116 PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
4118 size = sizeof(PRINTER_INFO_4W);
4119 if(size <= cbBuf) {
4120 ptr = pPrinter + size;
4121 cbBuf -= size;
4122 memset(pPrinter, 0, size);
4123 } else {
4124 pi4 = NULL;
4125 cbBuf = 0;
4127 ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed,
4128 unicode);
4129 needed += size;
4130 break;
4134 case 5:
4136 PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
4138 size = sizeof(PRINTER_INFO_5W);
4139 if(size <= cbBuf) {
4140 ptr = pPrinter + size;
4141 cbBuf -= size;
4142 memset(pPrinter, 0, size);
4143 } else {
4144 pi5 = NULL;
4145 cbBuf = 0;
4148 ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed,
4149 unicode);
4150 needed += size;
4151 break;
4154 default:
4155 FIXME("Unimplemented level %d\n", Level);
4156 SetLastError(ERROR_INVALID_LEVEL);
4157 RegCloseKey(hkeyPrinters);
4158 RegCloseKey(hkeyPrinter);
4159 return FALSE;
4162 RegCloseKey(hkeyPrinter);
4163 RegCloseKey(hkeyPrinters);
4165 TRACE("returning %d needed = %d\n", ret, needed);
4166 if(pcbNeeded) *pcbNeeded = needed;
4167 if(!ret)
4168 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4169 return ret;
4172 /*****************************************************************************
4173 * GetPrinterW [WINSPOOL.@]
4175 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4176 DWORD cbBuf, LPDWORD pcbNeeded)
4178 return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4179 TRUE);
4182 /*****************************************************************************
4183 * GetPrinterA [WINSPOOL.@]
4185 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4186 DWORD cbBuf, LPDWORD pcbNeeded)
4188 return WINSPOOL_GetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded,
4189 FALSE);
4192 /*****************************************************************************
4193 * WINSPOOL_EnumPrinters
4195 * Implementation of EnumPrintersA|W
4197 static BOOL WINSPOOL_EnumPrinters(DWORD dwType, LPWSTR lpszName,
4198 DWORD dwLevel, LPBYTE lpbPrinters,
4199 DWORD cbBuf, LPDWORD lpdwNeeded,
4200 LPDWORD lpdwReturned, BOOL unicode)
4203 HKEY hkeyPrinters, hkeyPrinter;
4204 WCHAR PrinterName[255];
4205 DWORD needed = 0, number = 0;
4206 DWORD used, i, left;
4207 PBYTE pi, buf;
4209 if(lpbPrinters)
4210 memset(lpbPrinters, 0, cbBuf);
4211 if(lpdwReturned)
4212 *lpdwReturned = 0;
4213 if(lpdwNeeded)
4214 *lpdwNeeded = 0;
4216 /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
4217 if(dwType == PRINTER_ENUM_DEFAULT)
4218 return TRUE;
4220 if (dwType & PRINTER_ENUM_CONNECTIONS) {
4221 TRACE("ignoring PRINTER_ENUM_CONNECTIONS\n");
4222 dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
4223 if (!dwType) {
4224 FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
4225 *lpdwNeeded = 0;
4226 *lpdwReturned = 0;
4227 return TRUE;
4232 if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
4233 FIXME("dwType = %08x\n", dwType);
4234 SetLastError(ERROR_INVALID_FLAGS);
4235 return FALSE;
4238 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4239 ERROR_SUCCESS) {
4240 ERR("Can't create Printers key\n");
4241 return FALSE;
4244 if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
4245 NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4246 RegCloseKey(hkeyPrinters);
4247 ERR("Can't query Printers key\n");
4248 return FALSE;
4250 TRACE("Found %d printers\n", number);
4252 switch(dwLevel) {
4253 case 1:
4254 used = number * sizeof(PRINTER_INFO_1W);
4255 break;
4256 case 2:
4257 used = number * sizeof(PRINTER_INFO_2W);
4258 break;
4259 case 4:
4260 used = number * sizeof(PRINTER_INFO_4W);
4261 break;
4262 case 5:
4263 used = number * sizeof(PRINTER_INFO_5W);
4264 break;
4266 default:
4267 SetLastError(ERROR_INVALID_LEVEL);
4268 RegCloseKey(hkeyPrinters);
4269 return FALSE;
4271 pi = (used <= cbBuf) ? lpbPrinters : NULL;
4273 for(i = 0; i < number; i++) {
4274 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) !=
4275 ERROR_SUCCESS) {
4276 ERR("Can't enum key number %d\n", i);
4277 RegCloseKey(hkeyPrinters);
4278 return FALSE;
4280 TRACE("Printer %d is %s\n", i, debugstr_w(PrinterName));
4281 if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
4282 ERROR_SUCCESS) {
4283 ERR("Can't open key %s\n", debugstr_w(PrinterName));
4284 RegCloseKey(hkeyPrinters);
4285 return FALSE;
4288 if(cbBuf > used) {
4289 buf = lpbPrinters + used;
4290 left = cbBuf - used;
4291 } else {
4292 buf = NULL;
4293 left = 0;
4296 switch(dwLevel) {
4297 case 1:
4298 WINSPOOL_GetPrinter_1(hkeyPrinter, (PRINTER_INFO_1W *)pi, buf,
4299 left, &needed, unicode);
4300 used += needed;
4301 if(pi) pi += sizeof(PRINTER_INFO_1W);
4302 break;
4303 case 2:
4304 WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
4305 left, &needed, unicode);
4306 used += needed;
4307 if(pi) pi += sizeof(PRINTER_INFO_2W);
4308 break;
4309 case 4:
4310 WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
4311 left, &needed, unicode);
4312 used += needed;
4313 if(pi) pi += sizeof(PRINTER_INFO_4W);
4314 break;
4315 case 5:
4316 WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
4317 left, &needed, unicode);
4318 used += needed;
4319 if(pi) pi += sizeof(PRINTER_INFO_5W);
4320 break;
4321 default:
4322 ERR("Shouldn't be here!\n");
4323 RegCloseKey(hkeyPrinter);
4324 RegCloseKey(hkeyPrinters);
4325 return FALSE;
4327 RegCloseKey(hkeyPrinter);
4329 RegCloseKey(hkeyPrinters);
4331 if(lpdwNeeded)
4332 *lpdwNeeded = used;
4334 if(used > cbBuf) {
4335 if(lpbPrinters)
4336 memset(lpbPrinters, 0, cbBuf);
4337 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4338 return FALSE;
4340 if(lpdwReturned)
4341 *lpdwReturned = number;
4342 SetLastError(ERROR_SUCCESS);
4343 return TRUE;
4347 /******************************************************************
4348 * EnumPrintersW [WINSPOOL.@]
4350 * Enumerates the available printers, print servers and print
4351 * providers, depending on the specified flags, name and level.
4353 * RETURNS:
4355 * If level is set to 1:
4356 * Returns an array of PRINTER_INFO_1 data structures in the
4357 * lpbPrinters buffer.
4359 * If level is set to 2:
4360 * Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4361 * Returns an array of PRINTER_INFO_2 data structures in the
4362 * lpbPrinters buffer. Note that according to MSDN also an
4363 * OpenPrinter should be performed on every remote printer.
4365 * If level is set to 4 (officially WinNT only):
4366 * Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4367 * Fast: Only the registry is queried to retrieve printer names,
4368 * no connection to the driver is made.
4369 * Returns an array of PRINTER_INFO_4 data structures in the
4370 * lpbPrinters buffer.
4372 * If level is set to 5 (officially WinNT4/Win9x only):
4373 * Fast: Only the registry is queried to retrieve printer names,
4374 * no connection to the driver is made.
4375 * Returns an array of PRINTER_INFO_5 data structures in the
4376 * lpbPrinters buffer.
4378 * If level set to 3 or 6+:
4379 * returns zero (failure!)
4381 * Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
4382 * for information.
4384 * BUGS:
4385 * - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
4386 * - Only levels 2, 4 and 5 are implemented at the moment.
4387 * - 16-bit printer drivers are not enumerated.
4388 * - Returned amount of bytes used/needed does not match the real Windoze
4389 * implementation (as in this implementation, all strings are part
4390 * of the buffer, whereas Win32 keeps them somewhere else)
4391 * - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
4393 * NOTE:
4394 * - In a regular Wine installation, no registry settings for printers
4395 * exist, which makes this function return an empty list.
4397 BOOL WINAPI EnumPrintersW(
4398 DWORD dwType, /* [in] Types of print objects to enumerate */
4399 LPWSTR lpszName, /* [in] name of objects to enumerate */
4400 DWORD dwLevel, /* [in] type of printer info structure */
4401 LPBYTE lpbPrinters, /* [out] buffer which receives info */
4402 DWORD cbBuf, /* [in] max size of buffer in bytes */
4403 LPDWORD lpdwNeeded, /* [out] pointer to var: # bytes used/needed */
4404 LPDWORD lpdwReturned /* [out] number of entries returned */
4407 return WINSPOOL_EnumPrinters(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
4408 lpdwNeeded, lpdwReturned, TRUE);
4411 /******************************************************************
4412 * EnumPrintersA [WINSPOOL.@]
4415 BOOL WINAPI EnumPrintersA(DWORD dwType, LPSTR lpszName,
4416 DWORD dwLevel, LPBYTE lpbPrinters,
4417 DWORD cbBuf, LPDWORD lpdwNeeded,
4418 LPDWORD lpdwReturned)
4420 BOOL ret, unicode = FALSE;
4421 UNICODE_STRING lpszNameW;
4422 PWSTR pwstrNameW;
4424 pwstrNameW = asciitounicode(&lpszNameW,lpszName);
4425 if(!cbBuf) unicode = TRUE; /* return a buffer that's big enough for the unicode version */
4426 ret = WINSPOOL_EnumPrinters(dwType, pwstrNameW, dwLevel, lpbPrinters, cbBuf,
4427 lpdwNeeded, lpdwReturned, unicode);
4428 RtlFreeUnicodeString(&lpszNameW);
4429 return ret;
4432 /*****************************************************************************
4433 * WINSPOOL_GetDriverInfoFromReg [internal]
4435 * Enters the information from the registry into the DRIVER_INFO struct
4437 * RETURNS
4438 * zero if the printer driver does not exist in the registry
4439 * (only if Level > 1) otherwise nonzero
4441 static BOOL WINSPOOL_GetDriverInfoFromReg(
4442 HKEY hkeyDrivers,
4443 LPWSTR DriverName,
4444 const printenv_t * env,
4445 DWORD Level,
4446 LPBYTE ptr, /* DRIVER_INFO */
4447 LPBYTE pDriverStrings, /* strings buffer */
4448 DWORD cbBuf, /* size of string buffer */
4449 LPDWORD pcbNeeded, /* space needed for str. */
4450 BOOL unicode) /* type of strings */
4452 DWORD size, tmp;
4453 HKEY hkeyDriver;
4454 WCHAR driverdir[MAX_PATH];
4455 DWORD dirlen;
4456 LPBYTE strPtr = pDriverStrings;
4457 LPDRIVER_INFO_8W di = (LPDRIVER_INFO_8W) ptr;
4459 TRACE("(%p, %s, %p, %d, %p, %p, %d, %d)\n", hkeyDrivers,
4460 debugstr_w(DriverName), env,
4461 Level, di, pDriverStrings, cbBuf, unicode);
4463 if (di) ZeroMemory(di, di_sizeof[Level]);
4465 if (unicode) {
4466 *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
4467 if (*pcbNeeded <= cbBuf)
4468 strcpyW((LPWSTR)strPtr, DriverName);
4470 else
4472 *pcbNeeded = WideCharToMultiByte(CP_ACP, 0, DriverName, -1, NULL, 0, NULL, NULL);
4473 if (*pcbNeeded <= cbBuf)
4474 WideCharToMultiByte(CP_ACP, 0, DriverName, -1, (LPSTR)strPtr, *pcbNeeded, NULL, NULL);
4477 /* pName for level 1 has a different offset! */
4478 if (Level == 1) {
4479 if (di) ((LPDRIVER_INFO_1W) di)->pName = (LPWSTR) strPtr;
4480 return TRUE;
4483 /* .cVersion and .pName for level > 1 */
4484 if (di) {
4485 di->cVersion = env->driverversion;
4486 di->pName = (LPWSTR) strPtr;
4487 strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4490 /* Reserve Space for the largest subdir and a Backslash*/
4491 size = sizeof(driverdir) - sizeof(Version3_SubdirW) - sizeof(WCHAR);
4492 if (!GetPrinterDriverDirectoryW(NULL, (LPWSTR) env->envname, 1, (LPBYTE) driverdir, size, &size)) {
4493 /* Should never Fail */
4494 return FALSE;
4496 lstrcatW(driverdir, env->versionsubdir);
4497 lstrcatW(driverdir, backslashW);
4499 /* dirlen must not include the terminating zero */
4500 dirlen = (unicode) ? lstrlenW(driverdir) * sizeof(WCHAR) :
4501 WideCharToMultiByte(CP_ACP, 0, driverdir, -1, NULL, 0, NULL, NULL) -1;
4503 if (!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
4504 ERR("Can't find driver %s in registry\n", debugstr_w(DriverName));
4505 SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
4506 return FALSE;
4509 /* pEnvironment */
4510 if (unicode)
4511 size = (lstrlenW(env->envname) + 1) * sizeof(WCHAR);
4512 else
4513 size = WideCharToMultiByte(CP_ACP, 0, env->envname, -1, NULL, 0, NULL, NULL);
4515 *pcbNeeded += size;
4516 if (*pcbNeeded <= cbBuf) {
4517 if (unicode) {
4518 lstrcpyW((LPWSTR)strPtr, env->envname);
4520 else
4522 WideCharToMultiByte(CP_ACP, 0, env->envname, -1, (LPSTR)strPtr, size, NULL, NULL);
4524 if (di) di->pEnvironment = (LPWSTR)strPtr;
4525 strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4528 /* .pDriverPath is the Graphics rendering engine.
4529 The full Path is required to avoid a crash in some apps */
4530 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, 0, &size, unicode)) {
4531 *pcbNeeded += size;
4532 if (*pcbNeeded <= cbBuf)
4533 get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, size, &tmp, unicode);
4535 if (di) di->pDriverPath = (LPWSTR)strPtr;
4536 strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4539 /* .pDataFile: For postscript-drivers, this is the ppd-file */
4540 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, 0, &size, unicode)) {
4541 *pcbNeeded += size;
4542 if (*pcbNeeded <= cbBuf)
4543 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, size, &size, unicode);
4545 if (di) di->pDataFile = (LPWSTR)strPtr;
4546 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4549 /* .pConfigFile is the Driver user Interface */
4550 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, 0, &size, unicode)) {
4551 *pcbNeeded += size;
4552 if (*pcbNeeded <= cbBuf)
4553 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, size, &size, unicode);
4555 if (di) di->pConfigFile = (LPWSTR)strPtr;
4556 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4559 if (Level == 2 ) {
4560 RegCloseKey(hkeyDriver);
4561 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4562 return TRUE;
4565 if (Level == 5 ) {
4566 RegCloseKey(hkeyDriver);
4567 FIXME("level 5: incomplete\n");
4568 return TRUE;
4571 /* .pHelpFile */
4572 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, 0, &size, unicode)) {
4573 *pcbNeeded += size;
4574 if (*pcbNeeded <= cbBuf)
4575 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, size, &size, unicode);
4577 if (di) di->pHelpFile = (LPWSTR)strPtr;
4578 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4581 /* .pDependentFiles */
4582 if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, 0, &size, unicode)) {
4583 *pcbNeeded += size;
4584 if (*pcbNeeded <= cbBuf)
4585 get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, size, &size, unicode);
4587 if (di) di->pDependentFiles = (LPWSTR)strPtr;
4588 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4591 /* .pMonitorName is the optional Language Monitor */
4592 if (WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size, unicode)) {
4593 *pcbNeeded += size;
4594 if (*pcbNeeded <= cbBuf)
4595 WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, size, &size, unicode);
4597 if (di) di->pMonitorName = (LPWSTR)strPtr;
4598 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4601 /* .pDefaultDataType */
4602 if (WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size, unicode)) {
4603 *pcbNeeded += size;
4604 if(*pcbNeeded <= cbBuf)
4605 WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, size, &size, unicode);
4607 if (di) di->pDefaultDataType = (LPWSTR)strPtr;
4608 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4611 if (Level == 3 ) {
4612 RegCloseKey(hkeyDriver);
4613 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4614 return TRUE;
4617 /* .pszzPreviousNames */
4618 if (WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, 0, &size, unicode)) {
4619 *pcbNeeded += size;
4620 if(*pcbNeeded <= cbBuf)
4621 WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, size, &size, unicode);
4623 if (di) di->pszzPreviousNames = (LPWSTR)strPtr;
4624 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4627 if (Level == 4 ) {
4628 RegCloseKey(hkeyDriver);
4629 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4630 return TRUE;
4633 /* support is missing, but not important enough for a FIXME */
4634 TRACE("%s: DriverDate + DriverVersion not supported\n", debugstr_w(DriverName));
4636 /* .pszMfgName */
4637 if (WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, 0, &size, unicode)) {
4638 *pcbNeeded += size;
4639 if(*pcbNeeded <= cbBuf)
4640 WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, size, &size, unicode);
4642 if (di) di->pszMfgName = (LPWSTR)strPtr;
4643 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4646 /* .pszOEMUrl */
4647 if (WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, 0, &size, unicode)) {
4648 *pcbNeeded += size;
4649 if(*pcbNeeded <= cbBuf)
4650 WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, size, &size, unicode);
4652 if (di) di->pszOEMUrl = (LPWSTR)strPtr;
4653 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4656 /* .pszHardwareID */
4657 if (WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, 0, &size, unicode)) {
4658 *pcbNeeded += size;
4659 if(*pcbNeeded <= cbBuf)
4660 WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, size, &size, unicode);
4662 if (di) di->pszHardwareID = (LPWSTR)strPtr;
4663 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4666 /* .pszProvider */
4667 if (WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, 0, &size, unicode)) {
4668 *pcbNeeded += size;
4669 if(*pcbNeeded <= cbBuf)
4670 WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, size, &size, unicode);
4672 if (di) di->pszProvider = (LPWSTR)strPtr;
4673 strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4676 if (Level == 6 ) {
4677 RegCloseKey(hkeyDriver);
4678 return TRUE;
4681 /* support is missing, but not important enough for a FIXME */
4682 TRACE("level 8: incomplete\n");
4684 TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4685 RegCloseKey(hkeyDriver);
4686 return TRUE;
4689 /*****************************************************************************
4690 * WINSPOOL_GetPrinterDriver
4692 static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPCWSTR pEnvironment,
4693 DWORD Level, LPBYTE pDriverInfo,
4694 DWORD cbBuf, LPDWORD pcbNeeded,
4695 BOOL unicode)
4697 LPCWSTR name;
4698 WCHAR DriverName[100];
4699 DWORD ret, type, size, needed = 0;
4700 LPBYTE ptr = NULL;
4701 HKEY hkeyPrinter, hkeyPrinters, hkeyDrivers;
4702 const printenv_t * env;
4704 TRACE("(%p,%s,%d,%p,%d,%p)\n",hPrinter,debugstr_w(pEnvironment),
4705 Level,pDriverInfo,cbBuf, pcbNeeded);
4708 if (!(name = get_opened_printer_name(hPrinter))) {
4709 SetLastError(ERROR_INVALID_HANDLE);
4710 return FALSE;
4713 if (Level < 1 || Level == 7 || Level > 8) {
4714 SetLastError(ERROR_INVALID_LEVEL);
4715 return FALSE;
4718 env = validate_envW(pEnvironment);
4719 if (!env) return FALSE; /* SetLastError() is in validate_envW */
4721 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4722 ERROR_SUCCESS) {
4723 ERR("Can't create Printers key\n");
4724 return FALSE;
4726 if(RegOpenKeyW(hkeyPrinters, name, &hkeyPrinter)
4727 != ERROR_SUCCESS) {
4728 ERR("Can't find opened printer %s in registry\n", debugstr_w(name));
4729 RegCloseKey(hkeyPrinters);
4730 SetLastError(ERROR_INVALID_PRINTER_NAME); /* ? */
4731 return FALSE;
4733 size = sizeof(DriverName);
4734 DriverName[0] = 0;
4735 ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
4736 (LPBYTE)DriverName, &size);
4737 RegCloseKey(hkeyPrinter);
4738 RegCloseKey(hkeyPrinters);
4739 if(ret != ERROR_SUCCESS) {
4740 ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
4741 return FALSE;
4744 hkeyDrivers = WINSPOOL_OpenDriverReg( pEnvironment, TRUE);
4745 if(!hkeyDrivers) {
4746 ERR("Can't create Drivers key\n");
4747 return FALSE;
4750 size = di_sizeof[Level];
4751 if ((size <= cbBuf) && pDriverInfo)
4752 ptr = pDriverInfo + size;
4754 if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
4755 env, Level, pDriverInfo, ptr,
4756 (cbBuf < size) ? 0 : cbBuf - size,
4757 &needed, unicode)) {
4758 RegCloseKey(hkeyDrivers);
4759 return FALSE;
4762 RegCloseKey(hkeyDrivers);
4764 if(pcbNeeded) *pcbNeeded = size + needed;
4765 TRACE("buffer space %d required %d\n", cbBuf, size + needed);
4766 if(cbBuf >= needed) return TRUE;
4767 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4768 return FALSE;
4771 /*****************************************************************************
4772 * GetPrinterDriverA [WINSPOOL.@]
4774 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
4775 DWORD Level, LPBYTE pDriverInfo,
4776 DWORD cbBuf, LPDWORD pcbNeeded)
4778 BOOL ret;
4779 UNICODE_STRING pEnvW;
4780 PWSTR pwstrEnvW;
4782 pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
4783 ret = WINSPOOL_GetPrinterDriver(hPrinter, pwstrEnvW, Level, pDriverInfo,
4784 cbBuf, pcbNeeded, FALSE);
4785 RtlFreeUnicodeString(&pEnvW);
4786 return ret;
4788 /*****************************************************************************
4789 * GetPrinterDriverW [WINSPOOL.@]
4791 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
4792 DWORD Level, LPBYTE pDriverInfo,
4793 DWORD cbBuf, LPDWORD pcbNeeded)
4795 return WINSPOOL_GetPrinterDriver(hPrinter, pEnvironment, Level,
4796 pDriverInfo, cbBuf, pcbNeeded, TRUE);
4799 /*****************************************************************************
4800 * GetPrinterDriverDirectoryW [WINSPOOL.@]
4802 * Return the PATH for the Printer-Drivers (UNICODE)
4804 * PARAMS
4805 * pName [I] Servername (NT only) or NULL (local Computer)
4806 * pEnvironment [I] Printing-Environment (see below) or NULL (Default)
4807 * Level [I] Structure-Level (must be 1)
4808 * pDriverDirectory [O] PTR to Buffer that receives the Result
4809 * cbBuf [I] Size of Buffer at pDriverDirectory
4810 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
4811 * required for pDriverDirectory
4813 * RETURNS
4814 * Success: TRUE and in pcbNeeded the Bytes used in pDriverDirectory
4815 * Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
4816 * if cbBuf is too small
4818 * Native Values returned in pDriverDirectory on Success:
4819 *| NT(Windows NT x86): "%winsysdir%\\spool\\DRIVERS\\w32x86"
4820 *| NT(Windows 4.0): "%winsysdir%\\spool\\DRIVERS\\win40"
4821 *| win9x(Windows 4.0): "%winsysdir%"
4823 * "%winsysdir%" is the Value from GetSystemDirectoryW()
4825 * FIXME
4826 *- Only NULL or "" is supported for pName
4829 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
4830 DWORD Level, LPBYTE pDriverDirectory,
4831 DWORD cbBuf, LPDWORD pcbNeeded)
4833 DWORD needed;
4834 const printenv_t * env;
4836 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName),
4837 debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4838 if(pName != NULL && pName[0]) {
4839 FIXME("pName unsupported: %s\n", debugstr_w(pName));
4840 SetLastError(ERROR_INVALID_PARAMETER);
4841 return FALSE;
4844 env = validate_envW(pEnvironment);
4845 if(!env) return FALSE; /* pEnvironment invalid or unsupported */
4847 if(Level != 1) {
4848 WARN("(Level: %d) is ignored in win9x\n", Level);
4849 SetLastError(ERROR_INVALID_LEVEL);
4850 return FALSE;
4853 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
4854 needed = GetSystemDirectoryW(NULL, 0);
4855 /* add the Size for the Subdirectories */
4856 needed += lstrlenW(spooldriversW);
4857 needed += lstrlenW(env->subdir);
4858 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
4860 if(pcbNeeded)
4861 *pcbNeeded = needed;
4862 TRACE("required: 0x%x/%d\n", needed, needed);
4863 if(needed > cbBuf) {
4864 SetLastError(ERROR_INSUFFICIENT_BUFFER);
4865 return FALSE;
4867 if(pcbNeeded == NULL) {
4868 WARN("(pcbNeeded == NULL) is ignored in win9x\n");
4869 SetLastError(RPC_X_NULL_REF_POINTER);
4870 return FALSE;
4872 if(pDriverDirectory == NULL) {
4873 /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
4874 SetLastError(ERROR_INVALID_USER_BUFFER);
4875 return FALSE;
4878 GetSystemDirectoryW((LPWSTR) pDriverDirectory, cbBuf/sizeof(WCHAR));
4879 /* add the Subdirectories */
4880 lstrcatW((LPWSTR) pDriverDirectory, spooldriversW);
4881 lstrcatW((LPWSTR) pDriverDirectory, env->subdir);
4882 TRACE(" => %s\n", debugstr_w((LPWSTR) pDriverDirectory));
4883 return TRUE;
4887 /*****************************************************************************
4888 * GetPrinterDriverDirectoryA [WINSPOOL.@]
4890 * Return the PATH for the Printer-Drivers (ANSI)
4892 * See GetPrinterDriverDirectoryW.
4894 * NOTES
4895 * On NT, pDriverDirectory need the same Size as the Unicode-Version
4898 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
4899 DWORD Level, LPBYTE pDriverDirectory,
4900 DWORD cbBuf, LPDWORD pcbNeeded)
4902 UNICODE_STRING nameW, environmentW;
4903 BOOL ret;
4904 DWORD pcbNeededW;
4905 INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
4906 WCHAR *driverDirectoryW = NULL;
4908 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(pName),
4909 debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4911 if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
4913 if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
4914 else nameW.Buffer = NULL;
4915 if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
4916 else environmentW.Buffer = NULL;
4918 ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
4919 (LPBYTE)driverDirectoryW, len, &pcbNeededW );
4920 if (ret) {
4921 DWORD needed;
4922 needed = WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1,
4923 (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
4924 if(pcbNeeded)
4925 *pcbNeeded = needed;
4926 ret = (needed <= cbBuf) ? TRUE : FALSE;
4927 } else
4928 if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
4930 TRACE("required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
4932 HeapFree( GetProcessHeap(), 0, driverDirectoryW );
4933 RtlFreeUnicodeString(&environmentW);
4934 RtlFreeUnicodeString(&nameW);
4936 return ret;
4939 /*****************************************************************************
4940 * AddPrinterDriverA [WINSPOOL.@]
4942 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
4944 DRIVER_INFO_3A di3;
4945 HKEY hkeyDrivers, hkeyName;
4946 static CHAR empty[] = "",
4947 nullnull[] = "\0";
4949 TRACE("(%s,%d,%p)\n",debugstr_a(pName),level,pDriverInfo);
4951 if(level != 2 && level != 3) {
4952 SetLastError(ERROR_INVALID_LEVEL);
4953 return FALSE;
4955 if ((pName) && (pName[0])) {
4956 FIXME("pName= %s - unsupported\n", debugstr_a(pName));
4957 SetLastError(ERROR_INVALID_PARAMETER);
4958 return FALSE;
4960 if(!pDriverInfo) {
4961 WARN("pDriverInfo == NULL\n");
4962 SetLastError(ERROR_INVALID_PARAMETER);
4963 return FALSE;
4966 if(level == 3)
4967 di3 = *(DRIVER_INFO_3A *)pDriverInfo;
4968 else {
4969 memset(&di3, 0, sizeof(di3));
4970 memcpy(&di3, pDriverInfo, sizeof(DRIVER_INFO_2A));
4973 if(!di3.pName || !di3.pDriverPath || !di3.pConfigFile ||
4974 !di3.pDataFile) {
4975 SetLastError(ERROR_INVALID_PARAMETER);
4976 return FALSE;
4979 if(!di3.pDefaultDataType) di3.pDefaultDataType = empty;
4980 if(!di3.pDependentFiles) di3.pDependentFiles = nullnull;
4981 if(!di3.pHelpFile) di3.pHelpFile = empty;
4982 if(!di3.pMonitorName) di3.pMonitorName = empty;
4984 hkeyDrivers = WINSPOOL_OpenDriverReg(di3.pEnvironment, FALSE);
4986 if(!hkeyDrivers) {
4987 ERR("Can't create Drivers key\n");
4988 return FALSE;
4991 if(level == 2) { /* apparently can't overwrite with level2 */
4992 if(RegOpenKeyA(hkeyDrivers, di3.pName, &hkeyName) == ERROR_SUCCESS) {
4993 RegCloseKey(hkeyName);
4994 RegCloseKey(hkeyDrivers);
4995 WARN("Trying to create existing printer driver %s\n", debugstr_a(di3.pName));
4996 SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
4997 return FALSE;
5000 if(RegCreateKeyA(hkeyDrivers, di3.pName, &hkeyName) != ERROR_SUCCESS) {
5001 RegCloseKey(hkeyDrivers);
5002 ERR("Can't create Name key\n");
5003 return FALSE;
5005 RegSetValueExA(hkeyName, "Configuration File", 0, REG_SZ, (LPBYTE) di3.pConfigFile,
5006 lstrlenA(di3.pConfigFile) + 1);
5007 RegSetValueExA(hkeyName, "Data File", 0, REG_SZ, (LPBYTE) di3.pDataFile, lstrlenA(di3.pDataFile) + 1);
5008 RegSetValueExA(hkeyName, "Driver", 0, REG_SZ, (LPBYTE) di3.pDriverPath, lstrlenA(di3.pDriverPath) + 1);
5009 RegSetValueExA(hkeyName, "Version", 0, REG_DWORD, (LPBYTE) &di3.cVersion,
5010 sizeof(DWORD));
5011 RegSetValueExA(hkeyName, "Datatype", 0, REG_SZ, (LPBYTE) di3.pDefaultDataType, lstrlenA(di3.pDefaultDataType));
5012 RegSetValueExA(hkeyName, "Dependent Files", 0, REG_MULTI_SZ,
5013 (LPBYTE) di3.pDependentFiles, multi_sz_lenA(di3.pDependentFiles));
5014 RegSetValueExA(hkeyName, "Help File", 0, REG_SZ, (LPBYTE) di3.pHelpFile, lstrlenA(di3.pHelpFile) + 1);
5015 RegSetValueExA(hkeyName, "Monitor", 0, REG_SZ, (LPBYTE) di3.pMonitorName, lstrlenA(di3.pMonitorName) + 1);
5016 RegCloseKey(hkeyName);
5017 RegCloseKey(hkeyDrivers);
5019 return TRUE;
5022 /*****************************************************************************
5023 * AddPrinterDriverW [WINSPOOL.@]
5025 BOOL WINAPI AddPrinterDriverW(LPWSTR printerName,DWORD level,
5026 LPBYTE pDriverInfo)
5028 FIXME("(%s,%d,%p): stub\n",debugstr_w(printerName),
5029 level,pDriverInfo);
5030 return FALSE;
5033 /*****************************************************************************
5034 * AddPrintProcessorA [WINSPOOL.@]
5036 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
5037 LPSTR pPrintProcessorName)
5039 FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
5040 debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
5041 return FALSE;
5044 /*****************************************************************************
5045 * AddPrintProcessorW [WINSPOOL.@]
5047 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
5048 LPWSTR pPrintProcessorName)
5050 FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
5051 debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
5052 return FALSE;
5055 /*****************************************************************************
5056 * AddPrintProvidorA [WINSPOOL.@]
5058 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
5060 FIXME("(%s,0x%08x,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
5061 return FALSE;
5064 /*****************************************************************************
5065 * AddPrintProvidorW [WINSPOOL.@]
5067 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
5069 FIXME("(%s,0x%08x,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
5070 return FALSE;
5073 /*****************************************************************************
5074 * AdvancedDocumentPropertiesA [WINSPOOL.@]
5076 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
5077 PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
5079 FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
5080 pDevModeOutput, pDevModeInput);
5081 return 0;
5084 /*****************************************************************************
5085 * AdvancedDocumentPropertiesW [WINSPOOL.@]
5087 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
5088 PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
5090 FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
5091 pDevModeOutput, pDevModeInput);
5092 return 0;
5095 /*****************************************************************************
5096 * PrinterProperties [WINSPOOL.@]
5098 * Displays a dialog to set the properties of the printer.
5100 * RETURNS
5101 * nonzero on success or zero on failure
5103 * BUGS
5104 * implemented as stub only
5106 BOOL WINAPI PrinterProperties(HWND hWnd, /* [in] handle to parent window */
5107 HANDLE hPrinter /* [in] handle to printer object */
5109 FIXME("(%p,%p): stub\n", hWnd, hPrinter);
5110 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5111 return FALSE;
5114 /*****************************************************************************
5115 * EnumJobsA [WINSPOOL.@]
5118 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
5119 DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
5120 LPDWORD pcReturned)
5122 FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
5123 hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
5125 if(pcbNeeded) *pcbNeeded = 0;
5126 if(pcReturned) *pcReturned = 0;
5127 return FALSE;
5131 /*****************************************************************************
5132 * EnumJobsW [WINSPOOL.@]
5135 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
5136 DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
5137 LPDWORD pcReturned)
5139 FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
5140 hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
5142 if(pcbNeeded) *pcbNeeded = 0;
5143 if(pcReturned) *pcReturned = 0;
5144 return FALSE;
5147 /*****************************************************************************
5148 * WINSPOOL_EnumPrinterDrivers [internal]
5150 * Delivers information about all printer drivers installed on the
5151 * localhost or a given server
5153 * RETURNS
5154 * nonzero on success or zero on failure. If the buffer for the returned
5155 * information is too small the function will return an error
5157 * BUGS
5158 * - only implemented for localhost, foreign hosts will return an error
5160 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPCWSTR pEnvironment,
5161 DWORD Level, LPBYTE pDriverInfo,
5162 DWORD cbBuf, LPDWORD pcbNeeded,
5163 LPDWORD pcReturned, BOOL unicode)
5165 { HKEY hkeyDrivers;
5166 DWORD i, needed, number = 0, size = 0;
5167 WCHAR DriverNameW[255];
5168 PBYTE ptr;
5169 const printenv_t * env;
5171 TRACE("%s,%s,%d,%p,%d,%d\n",
5172 debugstr_w(pName), debugstr_w(pEnvironment),
5173 Level, pDriverInfo, cbBuf, unicode);
5175 /* check for local drivers */
5176 if((pName) && (pName[0])) {
5177 FIXME("remote drivers (%s) not supported!\n", debugstr_w(pName));
5178 SetLastError(ERROR_ACCESS_DENIED);
5179 return FALSE;
5182 env = validate_envW(pEnvironment);
5183 if (!env) return FALSE; /* SetLastError() is in validate_envW */
5185 /* check input parameter */
5186 if ((Level < 1) || (Level == 7) || (Level > 8)) {
5187 SetLastError(ERROR_INVALID_LEVEL);
5188 return FALSE;
5191 if ((pcbNeeded == NULL) || (pcReturned == NULL)) {
5192 SetLastError(RPC_X_NULL_REF_POINTER);
5193 return FALSE;
5196 /* initialize return values */
5197 if(pDriverInfo)
5198 memset( pDriverInfo, 0, cbBuf);
5199 *pcbNeeded = 0;
5200 *pcReturned = 0;
5202 hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
5203 if(!hkeyDrivers) {
5204 ERR("Can't open Drivers key\n");
5205 return FALSE;
5208 if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, &number, NULL, NULL,
5209 NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
5210 RegCloseKey(hkeyDrivers);
5211 ERR("Can't query Drivers key\n");
5212 return FALSE;
5214 TRACE("Found %d Drivers\n", number);
5216 /* get size of single struct
5217 * unicode and ascii structure have the same size
5219 size = di_sizeof[Level];
5221 /* calculate required buffer size */
5222 *pcbNeeded = size * number;
5224 for( i = 0, ptr = (pDriverInfo && (cbBuf >= size)) ? pDriverInfo : NULL ;
5225 i < number;
5226 i++, ptr = (ptr && (cbBuf >= size * i)) ? ptr + size : NULL) {
5227 if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW))
5228 != ERROR_SUCCESS) {
5229 ERR("Can't enum key number %d\n", i);
5230 RegCloseKey(hkeyDrivers);
5231 return FALSE;
5233 if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
5234 env, Level, ptr,
5235 (cbBuf < *pcbNeeded) ? NULL : pDriverInfo + *pcbNeeded,
5236 (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
5237 &needed, unicode)) {
5238 RegCloseKey(hkeyDrivers);
5239 return FALSE;
5241 (*pcbNeeded) += needed;
5244 RegCloseKey(hkeyDrivers);
5246 if(cbBuf < *pcbNeeded){
5247 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5248 return FALSE;
5251 *pcReturned = number;
5252 return TRUE;
5255 /*****************************************************************************
5256 * EnumPrinterDriversW [WINSPOOL.@]
5258 * see function EnumPrinterDrivers for RETURNS, BUGS
5260 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5261 LPBYTE pDriverInfo, DWORD cbBuf,
5262 LPDWORD pcbNeeded, LPDWORD pcReturned)
5264 return WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
5265 cbBuf, pcbNeeded, pcReturned, TRUE);
5268 /*****************************************************************************
5269 * EnumPrinterDriversA [WINSPOOL.@]
5271 * see function EnumPrinterDrivers for RETURNS, BUGS
5273 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
5274 LPBYTE pDriverInfo, DWORD cbBuf,
5275 LPDWORD pcbNeeded, LPDWORD pcReturned)
5276 { BOOL ret;
5277 UNICODE_STRING pNameW, pEnvironmentW;
5278 PWSTR pwstrNameW, pwstrEnvironmentW;
5280 pwstrNameW = asciitounicode(&pNameW, pName);
5281 pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
5283 ret = WINSPOOL_EnumPrinterDrivers(pwstrNameW, pwstrEnvironmentW,
5284 Level, pDriverInfo, cbBuf, pcbNeeded,
5285 pcReturned, FALSE);
5286 RtlFreeUnicodeString(&pNameW);
5287 RtlFreeUnicodeString(&pEnvironmentW);
5289 return ret;
5292 /******************************************************************************
5293 * EnumPortsA (WINSPOOL.@)
5295 * See EnumPortsW.
5298 BOOL WINAPI EnumPortsA( LPSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
5299 LPDWORD pcbNeeded, LPDWORD pcReturned)
5301 BOOL res;
5302 LPBYTE bufferW = NULL;
5303 LPWSTR nameW = NULL;
5304 DWORD needed = 0;
5305 DWORD numentries = 0;
5306 INT len;
5308 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pPorts,
5309 cbBuf, pcbNeeded, pcReturned);
5311 /* convert servername to unicode */
5312 if (pName) {
5313 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5314 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5315 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5317 /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the Ports */
5318 needed = cbBuf * sizeof(WCHAR);
5319 if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
5320 res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5322 if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
5323 if (pcbNeeded) needed = *pcbNeeded;
5324 /* HeapReAlloc return NULL, when bufferW was NULL */
5325 bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
5326 HeapAlloc(GetProcessHeap(), 0, needed);
5328 /* Try again with the large Buffer */
5329 res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5331 needed = pcbNeeded ? *pcbNeeded : 0;
5332 numentries = pcReturned ? *pcReturned : 0;
5335 W2k require the buffersize from EnumPortsW also for EnumPortsA.
5336 We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
5338 if (res) {
5339 /* EnumPortsW collected all Data. Parse them to calculate ANSI-Size */
5340 DWORD entrysize = 0;
5341 DWORD index;
5342 LPSTR ptr;
5343 LPPORT_INFO_2W pi2w;
5344 LPPORT_INFO_2A pi2a;
5346 needed = 0;
5347 entrysize = (Level == 1) ? sizeof(PORT_INFO_1A) : sizeof(PORT_INFO_2A);
5349 /* First pass: calculate the size for all Entries */
5350 pi2w = (LPPORT_INFO_2W) bufferW;
5351 pi2a = (LPPORT_INFO_2A) pPorts;
5352 index = 0;
5353 while (index < numentries) {
5354 index++;
5355 needed += entrysize; /* PORT_INFO_?A */
5356 TRACE("%p: parsing #%d (%s)\n", pi2w, index, debugstr_w(pi2w->pPortName));
5358 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5359 NULL, 0, NULL, NULL);
5360 if (Level > 1) {
5361 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5362 NULL, 0, NULL, NULL);
5363 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5364 NULL, 0, NULL, NULL);
5366 /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5367 pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5368 pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5371 /* check for errors and quit on failure */
5372 if (cbBuf < needed) {
5373 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5374 res = FALSE;
5375 goto cleanup;
5377 len = entrysize * numentries; /* room for all PORT_INFO_?A */
5378 ptr = (LPSTR) &pPorts[len]; /* room for strings */
5379 cbBuf -= len ; /* free Bytes in the user-Buffer */
5380 pi2w = (LPPORT_INFO_2W) bufferW;
5381 pi2a = (LPPORT_INFO_2A) pPorts;
5382 index = 0;
5383 /* Second Pass: Fill the User Buffer (if we have one) */
5384 while ((index < numentries) && pPorts) {
5385 index++;
5386 TRACE("%p: writing PORT_INFO_%dA #%d\n", pi2a, Level, index);
5387 pi2a->pPortName = ptr;
5388 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5389 ptr, cbBuf , NULL, NULL);
5390 ptr += len;
5391 cbBuf -= len;
5392 if (Level > 1) {
5393 pi2a->pMonitorName = ptr;
5394 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5395 ptr, cbBuf, NULL, NULL);
5396 ptr += len;
5397 cbBuf -= len;
5399 pi2a->pDescription = ptr;
5400 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5401 ptr, cbBuf, NULL, NULL);
5402 ptr += len;
5403 cbBuf -= len;
5405 pi2a->fPortType = pi2w->fPortType;
5406 pi2a->Reserved = 0; /* documented: "must be zero" */
5409 /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5410 pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5411 pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5415 cleanup:
5416 if (pcbNeeded) *pcbNeeded = needed;
5417 if (pcReturned) *pcReturned = (res) ? numentries : 0;
5419 HeapFree(GetProcessHeap(), 0, nameW);
5420 HeapFree(GetProcessHeap(), 0, bufferW);
5422 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
5423 (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5425 return (res);
5429 /******************************************************************************
5430 * EnumPortsW (WINSPOOL.@)
5432 * Enumerate available Ports
5434 * PARAMS
5435 * name [I] Servername or NULL (local Computer)
5436 * level [I] Structure-Level (1 or 2)
5437 * buffer [O] PTR to Buffer that receives the Result
5438 * bufsize [I] Size of Buffer at buffer
5439 * bufneeded [O] PTR to DWORD that receives the size in Bytes used / required for buffer
5440 * bufreturned [O] PTR to DWORD that receives the number of Ports in buffer
5442 * RETURNS
5443 * Success: TRUE
5444 * Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
5448 BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5450 DWORD needed = 0;
5451 DWORD numentries = 0;
5452 BOOL res = FALSE;
5454 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
5455 cbBuf, pcbNeeded, pcReturned);
5457 if (pName && (pName[0])) {
5458 FIXME("not implemented for Server %s\n", debugstr_w(pName));
5459 SetLastError(ERROR_ACCESS_DENIED);
5460 goto emP_cleanup;
5463 /* Level is not checked in win9x */
5464 if (!Level || (Level > 2)) {
5465 WARN("level (%d) is ignored in win9x\n", Level);
5466 SetLastError(ERROR_INVALID_LEVEL);
5467 goto emP_cleanup;
5469 if (!pcbNeeded) {
5470 SetLastError(RPC_X_NULL_REF_POINTER);
5471 goto emP_cleanup;
5474 EnterCriticalSection(&monitor_handles_cs);
5475 monitor_loadall();
5477 /* Scan all local Ports */
5478 numentries = 0;
5479 needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
5481 /* we calculated the needed buffersize. now do the error-checks */
5482 if (cbBuf < needed) {
5483 monitor_unloadall();
5484 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5485 goto emP_cleanup_cs;
5487 else if (!pPorts || !pcReturned) {
5488 monitor_unloadall();
5489 SetLastError(RPC_X_NULL_REF_POINTER);
5490 goto emP_cleanup_cs;
5493 /* Fill the Buffer */
5494 needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
5495 res = TRUE;
5496 monitor_unloadall();
5498 emP_cleanup_cs:
5499 LeaveCriticalSection(&monitor_handles_cs);
5501 emP_cleanup:
5502 if (pcbNeeded) *pcbNeeded = needed;
5503 if (pcReturned) *pcReturned = (res) ? numentries : 0;
5505 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
5506 (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5508 return (res);
5511 /******************************************************************************
5512 * GetDefaultPrinterW (WINSPOOL.@)
5514 * FIXME
5515 * This function must read the value from data 'device' of key
5516 * HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
5518 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
5520 BOOL retval = TRUE;
5521 DWORD insize, len;
5522 WCHAR *buffer, *ptr;
5524 if (!namesize)
5526 SetLastError(ERROR_INVALID_PARAMETER);
5527 return FALSE;
5530 /* make the buffer big enough for the stuff from the profile/registry,
5531 * the content must fit into the local buffer to compute the correct
5532 * size even if the extern buffer is too small or not given.
5533 * (20 for ,driver,port) */
5534 insize = *namesize;
5535 len = max(100, (insize + 20));
5536 buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
5538 if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
5540 SetLastError (ERROR_FILE_NOT_FOUND);
5541 retval = FALSE;
5542 goto end;
5544 TRACE("%s\n", debugstr_w(buffer));
5546 if ((ptr = strchrW(buffer, ',')) == NULL)
5548 SetLastError(ERROR_INVALID_NAME);
5549 retval = FALSE;
5550 goto end;
5553 *ptr = 0;
5554 *namesize = strlenW(buffer) + 1;
5555 if(!name || (*namesize > insize))
5557 SetLastError(ERROR_INSUFFICIENT_BUFFER);
5558 retval = FALSE;
5559 goto end;
5561 strcpyW(name, buffer);
5563 end:
5564 HeapFree( GetProcessHeap(), 0, buffer);
5565 return retval;
5569 /******************************************************************************
5570 * GetDefaultPrinterA (WINSPOOL.@)
5572 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
5574 BOOL retval = TRUE;
5575 DWORD insize = 0;
5576 WCHAR *bufferW = NULL;
5578 if (!namesize)
5580 SetLastError(ERROR_INVALID_PARAMETER);
5581 return FALSE;
5584 if(name && *namesize) {
5585 insize = *namesize;
5586 bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
5589 if(!GetDefaultPrinterW( bufferW, namesize)) {
5590 retval = FALSE;
5591 goto end;
5594 *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
5595 NULL, NULL);
5596 if (!*namesize)
5598 *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
5599 retval = FALSE;
5601 TRACE("0x%08x/0x%08x:%s\n", *namesize, insize, debugstr_w(bufferW));
5603 end:
5604 HeapFree( GetProcessHeap(), 0, bufferW);
5605 return retval;
5609 /******************************************************************************
5610 * SetDefaultPrinterW (WINSPOOL.204)
5612 * Set the Name of the Default Printer
5614 * PARAMS
5615 * pszPrinter [I] Name of the Printer or NULL
5617 * RETURNS
5618 * Success: True
5619 * Failure: FALSE
5621 * NOTES
5622 * When the Parameter is NULL or points to an Empty String and
5623 * a Default Printer was already present, then this Function changes nothing.
5624 * Without a Default Printer and NULL (or an Empty String) as Parameter,
5625 * the First enumerated local Printer is used.
5628 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
5631 TRACE("(%s)\n", debugstr_w(pszPrinter));
5633 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5634 return FALSE;
5637 /******************************************************************************
5638 * SetDefaultPrinterA (WINSPOOL.202)
5640 * See SetDefaultPrinterW.
5643 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
5646 TRACE("(%s)\n", debugstr_a(pszPrinter));
5648 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
5649 return FALSE;
5653 /******************************************************************************
5654 * SetPrinterDataExA (WINSPOOL.@)
5656 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5657 LPCSTR pValueName, DWORD Type,
5658 LPBYTE pData, DWORD cbData)
5660 HKEY hkeyPrinter, hkeySubkey;
5661 DWORD ret;
5663 TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_a(pKeyName),
5664 debugstr_a(pValueName), Type, pData, cbData);
5666 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5667 != ERROR_SUCCESS)
5668 return ret;
5670 if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5671 != ERROR_SUCCESS) {
5672 ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
5673 RegCloseKey(hkeyPrinter);
5674 return ret;
5676 ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
5677 RegCloseKey(hkeySubkey);
5678 RegCloseKey(hkeyPrinter);
5679 return ret;
5682 /******************************************************************************
5683 * SetPrinterDataExW (WINSPOOL.@)
5685 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5686 LPCWSTR pValueName, DWORD Type,
5687 LPBYTE pData, DWORD cbData)
5689 HKEY hkeyPrinter, hkeySubkey;
5690 DWORD ret;
5692 TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_w(pKeyName),
5693 debugstr_w(pValueName), Type, pData, cbData);
5695 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5696 != ERROR_SUCCESS)
5697 return ret;
5699 if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5700 != ERROR_SUCCESS) {
5701 ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
5702 RegCloseKey(hkeyPrinter);
5703 return ret;
5705 ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
5706 RegCloseKey(hkeySubkey);
5707 RegCloseKey(hkeyPrinter);
5708 return ret;
5711 /******************************************************************************
5712 * SetPrinterDataA (WINSPOOL.@)
5714 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
5715 LPBYTE pData, DWORD cbData)
5717 return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
5718 pData, cbData);
5721 /******************************************************************************
5722 * SetPrinterDataW (WINSPOOL.@)
5724 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
5725 LPBYTE pData, DWORD cbData)
5727 return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
5728 pData, cbData);
5731 /******************************************************************************
5732 * GetPrinterDataExA (WINSPOOL.@)
5734 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5735 LPCSTR pValueName, LPDWORD pType,
5736 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5738 HKEY hkeyPrinter, hkeySubkey;
5739 DWORD ret;
5741 TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5742 debugstr_a(pKeyName), debugstr_a(pValueName), pType, pData, nSize,
5743 pcbNeeded);
5745 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5746 != ERROR_SUCCESS)
5747 return ret;
5749 if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5750 != ERROR_SUCCESS) {
5751 WARN("Can't open subkey %s\n", debugstr_a(pKeyName));
5752 RegCloseKey(hkeyPrinter);
5753 return ret;
5755 *pcbNeeded = nSize;
5756 ret = RegQueryValueExA(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5757 RegCloseKey(hkeySubkey);
5758 RegCloseKey(hkeyPrinter);
5759 return ret;
5762 /******************************************************************************
5763 * GetPrinterDataExW (WINSPOOL.@)
5765 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5766 LPCWSTR pValueName, LPDWORD pType,
5767 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5769 HKEY hkeyPrinter, hkeySubkey;
5770 DWORD ret;
5772 TRACE("(%p, %s, %s %p, %p, %08x, %p)\n", hPrinter,
5773 debugstr_w(pKeyName), debugstr_w(pValueName), pType, pData, nSize,
5774 pcbNeeded);
5776 if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5777 != ERROR_SUCCESS)
5778 return ret;
5780 if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5781 != ERROR_SUCCESS) {
5782 WARN("Can't open subkey %s\n", debugstr_w(pKeyName));
5783 RegCloseKey(hkeyPrinter);
5784 return ret;
5786 *pcbNeeded = nSize;
5787 ret = RegQueryValueExW(hkeySubkey, pValueName, 0, pType, pData, pcbNeeded);
5788 RegCloseKey(hkeySubkey);
5789 RegCloseKey(hkeyPrinter);
5790 return ret;
5793 /******************************************************************************
5794 * GetPrinterDataA (WINSPOOL.@)
5796 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
5797 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5799 return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
5800 pData, nSize, pcbNeeded);
5803 /******************************************************************************
5804 * GetPrinterDataW (WINSPOOL.@)
5806 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
5807 LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5809 return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
5810 pData, nSize, pcbNeeded);
5813 /*******************************************************************************
5814 * EnumPrinterDataExW [WINSPOOL.@]
5816 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5817 LPBYTE pEnumValues, DWORD cbEnumValues,
5818 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5820 HKEY hkPrinter, hkSubKey;
5821 DWORD r, ret, dwIndex, cValues, cbMaxValueNameLen,
5822 cbValueNameLen, cbMaxValueLen, cbValueLen,
5823 cbBufSize, dwType;
5824 LPWSTR lpValueName;
5825 HANDLE hHeap;
5826 PBYTE lpValue;
5827 PPRINTER_ENUM_VALUESW ppev;
5829 TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
5831 if (pKeyName == NULL || *pKeyName == 0)
5832 return ERROR_INVALID_PARAMETER;
5834 ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
5835 if (ret != ERROR_SUCCESS)
5837 TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %i\n",
5838 hPrinter, ret);
5839 return ret;
5842 ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
5843 if (ret != ERROR_SUCCESS)
5845 r = RegCloseKey (hkPrinter);
5846 if (r != ERROR_SUCCESS)
5847 WARN ("RegCloseKey returned %i\n", r);
5848 TRACE ("RegOpenKeyExW (%p, %s) returned %i\n", hPrinter,
5849 debugstr_w (pKeyName), ret);
5850 return ret;
5853 ret = RegCloseKey (hkPrinter);
5854 if (ret != ERROR_SUCCESS)
5856 ERR ("RegCloseKey returned %i\n", ret);
5857 r = RegCloseKey (hkSubKey);
5858 if (r != ERROR_SUCCESS)
5859 WARN ("RegCloseKey returned %i\n", r);
5860 return ret;
5863 ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
5864 &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
5865 if (ret != ERROR_SUCCESS)
5867 r = RegCloseKey (hkSubKey);
5868 if (r != ERROR_SUCCESS)
5869 WARN ("RegCloseKey returned %i\n", r);
5870 TRACE ("RegQueryInfoKeyW (%p) returned %i\n", hkSubKey, ret);
5871 return ret;
5874 TRACE ("RegQueryInfoKeyW returned cValues = %i, cbMaxValueNameLen = %i, "
5875 "cbMaxValueLen = %i\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
5877 if (cValues == 0) /* empty key */
5879 r = RegCloseKey (hkSubKey);
5880 if (r != ERROR_SUCCESS)
5881 WARN ("RegCloseKey returned %i\n", r);
5882 *pcbEnumValues = *pnEnumValues = 0;
5883 return ERROR_SUCCESS;
5886 ++cbMaxValueNameLen; /* allow for trailing '\0' */
5888 hHeap = GetProcessHeap ();
5889 if (hHeap == NULL)
5891 ERR ("GetProcessHeap failed\n");
5892 r = RegCloseKey (hkSubKey);
5893 if (r != ERROR_SUCCESS)
5894 WARN ("RegCloseKey returned %i\n", r);
5895 return ERROR_OUTOFMEMORY;
5898 lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
5899 if (lpValueName == NULL)
5901 ERR ("Failed to allocate %i WCHARs from process heap\n", cbMaxValueNameLen);
5902 r = RegCloseKey (hkSubKey);
5903 if (r != ERROR_SUCCESS)
5904 WARN ("RegCloseKey returned %i\n", r);
5905 return ERROR_OUTOFMEMORY;
5908 lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
5909 if (lpValue == NULL)
5911 ERR ("Failed to allocate %i bytes from process heap\n", cbMaxValueLen);
5912 if (HeapFree (hHeap, 0, lpValueName) == 0)
5913 WARN ("HeapFree failed with code %i\n", GetLastError ());
5914 r = RegCloseKey (hkSubKey);
5915 if (r != ERROR_SUCCESS)
5916 WARN ("RegCloseKey returned %i\n", r);
5917 return ERROR_OUTOFMEMORY;
5920 TRACE ("pass 1: calculating buffer required for all names and values\n");
5922 cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
5924 TRACE ("%i bytes required for %i headers\n", cbBufSize, cValues);
5926 for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5928 cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5929 ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5930 NULL, NULL, lpValue, &cbValueLen);
5931 if (ret != ERROR_SUCCESS)
5933 if (HeapFree (hHeap, 0, lpValue) == 0)
5934 WARN ("HeapFree failed with code %i\n", GetLastError ());
5935 if (HeapFree (hHeap, 0, lpValueName) == 0)
5936 WARN ("HeapFree failed with code %i\n", GetLastError ());
5937 r = RegCloseKey (hkSubKey);
5938 if (r != ERROR_SUCCESS)
5939 WARN ("RegCloseKey returned %i\n", r);
5940 TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5941 return ret;
5944 TRACE ("%s [%i]: name needs %i WCHARs, data needs %i bytes\n",
5945 debugstr_w (lpValueName), dwIndex,
5946 cbValueNameLen + 1, cbValueLen);
5948 cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
5949 cbBufSize += cbValueLen;
5952 TRACE ("%i bytes required for all %i values\n", cbBufSize, cValues);
5954 *pcbEnumValues = cbBufSize;
5955 *pnEnumValues = cValues;
5957 if (cbEnumValues < cbBufSize) /* buffer too small */
5959 if (HeapFree (hHeap, 0, lpValue) == 0)
5960 WARN ("HeapFree failed with code %i\n", GetLastError ());
5961 if (HeapFree (hHeap, 0, lpValueName) == 0)
5962 WARN ("HeapFree failed with code %i\n", GetLastError ());
5963 r = RegCloseKey (hkSubKey);
5964 if (r != ERROR_SUCCESS)
5965 WARN ("RegCloseKey returned %i\n", r);
5966 TRACE ("%i byte buffer is not large enough\n", cbEnumValues);
5967 return ERROR_MORE_DATA;
5970 TRACE ("pass 2: copying all names and values to buffer\n");
5972 ppev = (PPRINTER_ENUM_VALUESW) pEnumValues; /* array of structs */
5973 pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
5975 for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5977 cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5978 ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5979 NULL, &dwType, lpValue, &cbValueLen);
5980 if (ret != ERROR_SUCCESS)
5982 if (HeapFree (hHeap, 0, lpValue) == 0)
5983 WARN ("HeapFree failed with code %i\n", GetLastError ());
5984 if (HeapFree (hHeap, 0, lpValueName) == 0)
5985 WARN ("HeapFree failed with code %i\n", GetLastError ());
5986 r = RegCloseKey (hkSubKey);
5987 if (r != ERROR_SUCCESS)
5988 WARN ("RegCloseKey returned %i\n", r);
5989 TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5990 return ret;
5993 cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
5994 memcpy (pEnumValues, lpValueName, cbValueNameLen);
5995 ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
5996 pEnumValues += cbValueNameLen;
5998 /* return # of *bytes* (including trailing \0), not # of chars */
5999 ppev[dwIndex].cbValueName = cbValueNameLen;
6001 ppev[dwIndex].dwType = dwType;
6003 memcpy (pEnumValues, lpValue, cbValueLen);
6004 ppev[dwIndex].pData = pEnumValues;
6005 pEnumValues += cbValueLen;
6007 ppev[dwIndex].cbData = cbValueLen;
6009 TRACE ("%s [%i]: copied name (%i bytes) and data (%i bytes)\n",
6010 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
6013 if (HeapFree (hHeap, 0, lpValue) == 0)
6015 ret = GetLastError ();
6016 ERR ("HeapFree failed with code %i\n", ret);
6017 if (HeapFree (hHeap, 0, lpValueName) == 0)
6018 WARN ("HeapFree failed with code %i\n", GetLastError ());
6019 r = RegCloseKey (hkSubKey);
6020 if (r != ERROR_SUCCESS)
6021 WARN ("RegCloseKey returned %i\n", r);
6022 return ret;
6025 if (HeapFree (hHeap, 0, lpValueName) == 0)
6027 ret = GetLastError ();
6028 ERR ("HeapFree failed with code %i\n", ret);
6029 r = RegCloseKey (hkSubKey);
6030 if (r != ERROR_SUCCESS)
6031 WARN ("RegCloseKey returned %i\n", r);
6032 return ret;
6035 ret = RegCloseKey (hkSubKey);
6036 if (ret != ERROR_SUCCESS)
6038 ERR ("RegCloseKey returned %i\n", ret);
6039 return ret;
6042 return ERROR_SUCCESS;
6045 /*******************************************************************************
6046 * EnumPrinterDataExA [WINSPOOL.@]
6048 * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
6049 * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers. This is
6050 * what Windows 2000 SP1 does.
6053 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
6054 LPBYTE pEnumValues, DWORD cbEnumValues,
6055 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
6057 INT len;
6058 LPWSTR pKeyNameW;
6059 DWORD ret, dwIndex, dwBufSize;
6060 HANDLE hHeap;
6061 LPSTR pBuffer;
6063 TRACE ("%p %s\n", hPrinter, pKeyName);
6065 if (pKeyName == NULL || *pKeyName == 0)
6066 return ERROR_INVALID_PARAMETER;
6068 len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
6069 if (len == 0)
6071 ret = GetLastError ();
6072 ERR ("MultiByteToWideChar failed with code %i\n", ret);
6073 return ret;
6076 hHeap = GetProcessHeap ();
6077 if (hHeap == NULL)
6079 ERR ("GetProcessHeap failed\n");
6080 return ERROR_OUTOFMEMORY;
6083 pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
6084 if (pKeyNameW == NULL)
6086 ERR ("Failed to allocate %i bytes from process heap\n",
6087 (LONG)(len * sizeof (WCHAR)));
6088 return ERROR_OUTOFMEMORY;
6091 if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
6093 ret = GetLastError ();
6094 ERR ("MultiByteToWideChar failed with code %i\n", ret);
6095 if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6096 WARN ("HeapFree failed with code %i\n", GetLastError ());
6097 return ret;
6100 ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
6101 pcbEnumValues, pnEnumValues);
6102 if (ret != ERROR_SUCCESS)
6104 if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6105 WARN ("HeapFree failed with code %i\n", GetLastError ());
6106 TRACE ("EnumPrinterDataExW returned %i\n", ret);
6107 return ret;
6110 if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6112 ret = GetLastError ();
6113 ERR ("HeapFree failed with code %i\n", ret);
6114 return ret;
6117 if (*pnEnumValues == 0) /* empty key */
6118 return ERROR_SUCCESS;
6120 dwBufSize = 0;
6121 for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6123 PPRINTER_ENUM_VALUESW ppev =
6124 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6126 if (dwBufSize < ppev->cbValueName)
6127 dwBufSize = ppev->cbValueName;
6129 if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
6130 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
6131 dwBufSize = ppev->cbData;
6134 TRACE ("Largest Unicode name or value is %i bytes\n", dwBufSize);
6136 pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
6137 if (pBuffer == NULL)
6139 ERR ("Failed to allocate %i bytes from process heap\n", dwBufSize);
6140 return ERROR_OUTOFMEMORY;
6143 for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6145 PPRINTER_ENUM_VALUESW ppev =
6146 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6148 len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
6149 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
6150 NULL);
6151 if (len == 0)
6153 ret = GetLastError ();
6154 ERR ("WideCharToMultiByte failed with code %i\n", ret);
6155 if (HeapFree (hHeap, 0, pBuffer) == 0)
6156 WARN ("HeapFree failed with code %i\n", GetLastError ());
6157 return ret;
6160 memcpy (ppev->pValueName, pBuffer, len);
6162 TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6164 if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
6165 ppev->dwType != REG_MULTI_SZ)
6166 continue;
6168 len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
6169 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
6170 if (len == 0)
6172 ret = GetLastError ();
6173 ERR ("WideCharToMultiByte failed with code %i\n", ret);
6174 if (HeapFree (hHeap, 0, pBuffer) == 0)
6175 WARN ("HeapFree failed with code %i\n", GetLastError ());
6176 return ret;
6179 memcpy (ppev->pData, pBuffer, len);
6181 TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6182 TRACE (" (only first string of REG_MULTI_SZ printed)\n");
6185 if (HeapFree (hHeap, 0, pBuffer) == 0)
6187 ret = GetLastError ();
6188 ERR ("HeapFree failed with code %i\n", ret);
6189 return ret;
6192 return ERROR_SUCCESS;
6195 /******************************************************************************
6196 * AbortPrinter (WINSPOOL.@)
6198 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
6200 FIXME("(%p), stub!\n", hPrinter);
6201 return TRUE;
6204 /******************************************************************************
6205 * AddPortA (WINSPOOL.@)
6207 * See AddPortW.
6210 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
6212 LPWSTR nameW = NULL;
6213 LPWSTR monitorW = NULL;
6214 DWORD len;
6215 BOOL res;
6217 TRACE("(%s, %p, %s)\n",debugstr_a(pName), hWnd, debugstr_a(pMonitorName));
6219 if (pName) {
6220 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6221 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6222 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6225 if (pMonitorName) {
6226 len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6227 monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6228 MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6230 res = AddPortW(nameW, hWnd, monitorW);
6231 HeapFree(GetProcessHeap(), 0, nameW);
6232 HeapFree(GetProcessHeap(), 0, monitorW);
6233 return res;
6236 /******************************************************************************
6237 * AddPortW (WINSPOOL.@)
6239 * Add a Port for a specific Monitor
6241 * PARAMS
6242 * pName [I] Servername or NULL (local Computer)
6243 * hWnd [I] Handle to parent Window for the Dialog-Box
6244 * pMonitorName [I] Name of the Monitor that manage the Port
6246 * RETURNS
6247 * Success: TRUE
6248 * Failure: FALSE
6251 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
6253 monitor_t * pm;
6254 monitor_t * pui;
6255 DWORD res;
6257 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
6259 if (pName && pName[0]) {
6260 SetLastError(ERROR_INVALID_PARAMETER);
6261 return FALSE;
6264 if (!pMonitorName) {
6265 SetLastError(RPC_X_NULL_REF_POINTER);
6266 return FALSE;
6269 /* an empty Monitorname is Invalid */
6270 if (!pMonitorName[0]) {
6271 SetLastError(ERROR_NOT_SUPPORTED);
6272 return FALSE;
6275 pm = monitor_load(pMonitorName, NULL);
6276 if (pm && pm->monitor && pm->monitor->pfnAddPort) {
6277 res = pm->monitor->pfnAddPort(pName, hWnd, pMonitorName);
6278 TRACE("got %d with %u\n", res, GetLastError());
6279 res = TRUE;
6281 else
6283 pui = monitor_loadui(pm);
6284 if (pui && pui->monitorUI && pui->monitorUI->pfnAddPortUI) {
6285 TRACE("use %p: %s\n", pui, debugstr_w(pui->dllname));
6286 res = pui->monitorUI->pfnAddPortUI(pName, hWnd, pMonitorName, NULL);
6287 TRACE("got %d with %u\n", res, GetLastError());
6288 res = TRUE;
6290 else
6292 FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pMonitorName),
6293 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
6295 /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6296 SetLastError(ERROR_NOT_SUPPORTED);
6297 res = FALSE;
6299 monitor_unload(pui);
6301 monitor_unload(pm);
6302 TRACE("returning %d with %u\n", res, GetLastError());
6303 return res;
6306 /******************************************************************************
6307 * AddPortExA (WINSPOOL.@)
6309 * See AddPortExW.
6312 BOOL WINAPI AddPortExA(LPSTR pName, DWORD level, LPBYTE pBuffer, LPSTR pMonitorName)
6314 PORT_INFO_2W pi2W;
6315 PORT_INFO_2A * pi2A;
6316 LPWSTR nameW = NULL;
6317 LPWSTR monitorW = NULL;
6318 DWORD len;
6319 BOOL res;
6321 pi2A = (PORT_INFO_2A *) pBuffer;
6323 TRACE("(%s, %d, %p, %s): %s\n", debugstr_a(pName), level, pBuffer,
6324 debugstr_a(pMonitorName), debugstr_a(pi2A ? pi2A->pPortName : NULL));
6326 if ((level < 1) || (level > 2)) {
6327 SetLastError(ERROR_INVALID_LEVEL);
6328 return FALSE;
6331 if (!pi2A) {
6332 SetLastError(ERROR_INVALID_PARAMETER);
6333 return FALSE;
6336 if (pName) {
6337 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6338 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6339 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6342 if (pMonitorName) {
6343 len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6344 monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6345 MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6348 ZeroMemory(&pi2W, sizeof(PORT_INFO_2W));
6350 if (pi2A->pPortName) {
6351 len = MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, NULL, 0);
6352 pi2W.pPortName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6353 MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, pi2W.pPortName, len);
6356 if (level > 1) {
6357 if (pi2A->pMonitorName) {
6358 len = MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, NULL, 0);
6359 pi2W.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6360 MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, pi2W.pMonitorName, len);
6363 if (pi2A->pDescription) {
6364 len = MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, NULL, 0);
6365 pi2W.pDescription = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6366 MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, pi2W.pDescription, len);
6368 pi2W.fPortType = pi2A->fPortType;
6369 pi2W.Reserved = pi2A->Reserved;
6372 res = AddPortExW(nameW, level, (LPBYTE) &pi2W, monitorW);
6374 HeapFree(GetProcessHeap(), 0, nameW);
6375 HeapFree(GetProcessHeap(), 0, monitorW);
6376 HeapFree(GetProcessHeap(), 0, pi2W.pPortName);
6377 HeapFree(GetProcessHeap(), 0, pi2W.pMonitorName);
6378 HeapFree(GetProcessHeap(), 0, pi2W.pDescription);
6379 return res;
6383 /******************************************************************************
6384 * AddPortExW (WINSPOOL.@)
6386 * Add a Port for a specific Monitor, without presenting a user interface
6388 * PARAMS
6389 * pName [I] Servername or NULL (local Computer)
6390 * level [I] Structure-Level (1 or 2) for pBuffer
6391 * pBuffer [I] PTR to: PORT_INFO_1 or PORT_INFO_2
6392 * pMonitorName [I] Name of the Monitor that manage the Port
6394 * RETURNS
6395 * Success: TRUE
6396 * Failure: FALSE
6399 BOOL WINAPI AddPortExW(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
6401 PORT_INFO_2W * pi2;
6402 monitor_t * pm;
6403 DWORD res = FALSE;
6405 pi2 = (PORT_INFO_2W *) pBuffer;
6407 TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
6408 debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
6409 debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
6410 debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
6413 if ((level < 1) || (level > 2)) {
6414 SetLastError(ERROR_INVALID_LEVEL);
6415 return FALSE;
6418 if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
6419 SetLastError(ERROR_INVALID_PARAMETER);
6420 return FALSE;
6423 /* load the Monitor */
6424 pm = monitor_load(pMonitorName, NULL);
6425 if (!pm) {
6426 SetLastError(ERROR_INVALID_PARAMETER);
6427 return FALSE;
6430 if (pm->monitor && pm->monitor->pfnAddPortEx) {
6431 res = pm->monitor->pfnAddPortEx(pName, level, pBuffer, pMonitorName);
6432 TRACE("got %u with %u\n", res, GetLastError());
6434 else
6436 FIXME("not implemented for %s (%p)\n", debugstr_w(pMonitorName), pm->monitor);
6438 monitor_unload(pm);
6439 return res;
6442 /******************************************************************************
6443 * AddPrinterConnectionA (WINSPOOL.@)
6445 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
6447 FIXME("%s\n", debugstr_a(pName));
6448 return FALSE;
6451 /******************************************************************************
6452 * AddPrinterConnectionW (WINSPOOL.@)
6454 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
6456 FIXME("%s\n", debugstr_w(pName));
6457 return FALSE;
6460 /******************************************************************************
6461 * AddPrinterDriverExW (WINSPOOL.@)
6463 * Install a Printer Driver with the Option to upgrade / downgrade the Files
6465 * PARAMS
6466 * pName [I] Servername or NULL (local Computer)
6467 * level [I] Level for the supplied DRIVER_INFO_*W struct
6468 * pDriverInfo [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
6469 * dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
6471 * RESULTS
6472 * Success: TRUE
6473 * Failure: FALSE
6476 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6478 const printenv_t *env;
6479 apd_data_t apd;
6480 DRIVER_INFO_8W di;
6481 LPWSTR ptr;
6482 HKEY hroot;
6483 HKEY hdrv;
6484 DWORD disposition;
6485 DWORD len;
6486 LONG lres;
6488 TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
6490 if (level < 2 || level == 5 || level == 7 || level > 8) {
6491 SetLastError(ERROR_INVALID_LEVEL);
6492 return FALSE;
6495 if (!pDriverInfo) {
6496 SetLastError(ERROR_INVALID_PARAMETER);
6497 return FALSE;
6500 if ((dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY) != APD_COPY_ALL_FILES) {
6501 FIXME("Flags 0x%x ignored (Fallback to APD_COPY_ALL_FILES)\n", dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY);
6504 ptr = get_servername_from_name(pName);
6505 HeapFree(GetProcessHeap(), 0, ptr);
6506 if (ptr) {
6507 FIXME("not suported for server: %s\n", debugstr_w(pName));
6508 SetLastError(ERROR_ACCESS_DENIED);
6509 return FALSE;
6512 /* we need to set all entries in the Registry, independent from the Level of
6513 DRIVER_INFO, that the caller supplied */
6515 ZeroMemory(&di, sizeof(di));
6516 if (pDriverInfo && (level < (sizeof(di_sizeof) / sizeof(di_sizeof[0])))) {
6517 memcpy(&di, pDriverInfo, di_sizeof[level]);
6520 /* dump the most used infos */
6521 TRACE("%p: .cVersion : 0x%x/%d\n", pDriverInfo, di.cVersion, di.cVersion);
6522 TRACE("%p: .pName : %s\n", di.pName, debugstr_w(di.pName));
6523 TRACE("%p: .pEnvironment: %s\n", di.pEnvironment, debugstr_w(di.pEnvironment));
6524 TRACE("%p: .pDriverPath : %s\n", di.pDriverPath, debugstr_w(di.pDriverPath));
6525 TRACE("%p: .pDataFile : %s\n", di.pDataFile, debugstr_w(di.pDataFile));
6526 TRACE("%p: .pConfigFile : %s\n", di.pConfigFile, debugstr_w(di.pConfigFile));
6527 TRACE("%p: .pHelpFile : %s\n", di.pHelpFile, debugstr_w(di.pHelpFile));
6528 /* dump only the first of the additional Files */
6529 TRACE("%p: .pDependentFiles: %s\n", di.pDependentFiles, debugstr_w(di.pDependentFiles));
6532 /* check environment */
6533 env = validate_envW(di.pEnvironment);
6534 if (env == NULL) return FALSE; /* ERROR_INVALID_ENVIRONMENT */
6536 /* fill the copy-data / get the driverdir */
6537 len = sizeof(apd.src) - sizeof(Version3_SubdirW) - sizeof(WCHAR);
6538 if (!GetPrinterDriverDirectoryW(NULL, (LPWSTR) env->envname, 1,
6539 (LPBYTE) apd.src, len, &len)) {
6540 /* Should never Fail */
6541 return FALSE;
6543 memcpy(apd.dst, apd.src, len);
6544 lstrcatW(apd.src, backslashW);
6545 apd.srclen = lstrlenW(apd.src);
6546 lstrcatW(apd.dst, env->versionsubdir);
6547 lstrcatW(apd.dst, backslashW);
6548 apd.dstlen = lstrlenW(apd.dst);
6549 apd.copyflags = dwFileCopyFlags;
6550 CreateDirectoryW(apd.src, NULL);
6551 CreateDirectoryW(apd.dst, NULL);
6553 /* Fill the Registry for the Driver */
6554 hroot = WINSPOOL_OpenDriverReg(env->envname, TRUE);
6555 if(!hroot) {
6556 ERR("Can't create Drivers key\n");
6557 return FALSE;
6560 if ((lres = RegCreateKeyExW(hroot, di.pName, 0, NULL, REG_OPTION_NON_VOLATILE,
6561 KEY_WRITE | KEY_QUERY_VALUE, NULL,
6562 &hdrv, &disposition)) != ERROR_SUCCESS) {
6564 ERR("can't create driver %s: %u\n", debugstr_w(di.pName), lres);
6565 RegCloseKey(hroot);
6566 SetLastError(lres);
6567 return FALSE;
6569 RegCloseKey(hroot);
6571 if (disposition == REG_OPENED_EXISTING_KEY) {
6572 TRACE("driver %s already installed\n", debugstr_w(di.pName));
6573 RegCloseKey(hdrv);
6574 SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
6575 return FALSE;
6578 /* Verified with the Adobe PS Driver, that w2k does not use di.Version */
6579 RegSetValueExW(hdrv, VersionW, 0, REG_DWORD, (LPBYTE) &env->driverversion,
6580 sizeof(DWORD));
6582 RegSetValueExW(hdrv, DriverW, 0, REG_SZ, (LPBYTE) di.pDriverPath,
6583 (lstrlenW(di.pDriverPath)+1)* sizeof(WCHAR));
6584 apd_copyfile(di.pDriverPath, &apd);
6586 RegSetValueExW(hdrv, Data_FileW, 0, REG_SZ, (LPBYTE) di.pDataFile,
6587 (lstrlenW(di.pDataFile)+1)* sizeof(WCHAR));
6588 apd_copyfile(di.pDataFile, &apd);
6590 RegSetValueExW(hdrv, Configuration_FileW, 0, REG_SZ, (LPBYTE) di.pConfigFile,
6591 (lstrlenW(di.pConfigFile)+1)* sizeof(WCHAR));
6592 apd_copyfile(di.pConfigFile, &apd);
6594 /* settings for level 3 */
6595 RegSetValueExW(hdrv, Help_FileW, 0, REG_SZ, (LPBYTE) di.pHelpFile,
6596 di.pHelpFile ? (lstrlenW(di.pHelpFile)+1)* sizeof(WCHAR) : 0);
6597 apd_copyfile(di.pHelpFile, &apd);
6600 ptr = di.pDependentFiles;
6601 RegSetValueExW(hdrv, Dependent_FilesW, 0, REG_MULTI_SZ, (LPBYTE) di.pDependentFiles,
6602 di.pDependentFiles ? multi_sz_lenW(di.pDependentFiles) : 0);
6603 while ((ptr != NULL) && (ptr[0])) {
6604 if (apd_copyfile(ptr, &apd)) {
6605 ptr += lstrlenW(ptr) + 1;
6607 else
6609 WARN("Failed to copy %s\n", debugstr_w(ptr));
6610 ptr = NULL;
6614 /* The language-Monitor was already copied to "%SystemRoot%\system32" */
6615 RegSetValueExW(hdrv, MonitorW, 0, REG_SZ, (LPBYTE) di.pMonitorName,
6616 di.pMonitorName ? (lstrlenW(di.pMonitorName)+1)* sizeof(WCHAR) : 0);
6618 RegSetValueExW(hdrv, DatatypeW, 0, REG_SZ, (LPBYTE) di.pDefaultDataType,
6619 di.pDefaultDataType ? (lstrlenW(di.pDefaultDataType)+1)* sizeof(WCHAR) : 0);
6621 /* settings for level 4 */
6622 RegSetValueExW(hdrv, Previous_NamesW, 0, REG_MULTI_SZ, (LPBYTE) di.pszzPreviousNames,
6623 di.pszzPreviousNames ? multi_sz_lenW(di.pszzPreviousNames) : 0);
6625 if (level > 5) FIXME("level %u for Driver %s is incomplete\n", level, debugstr_w(di.pName));
6628 RegCloseKey(hdrv);
6629 FIXME("### DrvDriverEvent(...,DRIVEREVENT_INITIALIZE) not implemented yet\n");
6632 TRACE("=> TRUE with %u\n", GetLastError());
6633 return TRUE;
6637 /******************************************************************************
6638 * AddPrinterDriverExA (WINSPOOL.@)
6640 BOOL WINAPI AddPrinterDriverExA( LPSTR pName, DWORD Level,
6641 LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6643 FIXME("%s %d %p %d\n", debugstr_a(pName),
6644 Level, pDriverInfo, dwFileCopyFlags);
6645 SetLastError(ERROR_PRINTER_DRIVER_BLOCKED);
6646 return FALSE;
6649 /******************************************************************************
6650 * ConfigurePortA (WINSPOOL.@)
6652 * See ConfigurePortW.
6655 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
6657 LPWSTR nameW = NULL;
6658 LPWSTR portW = NULL;
6659 INT len;
6660 DWORD res;
6662 TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
6664 /* convert servername to unicode */
6665 if (pName) {
6666 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6667 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6668 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6671 /* convert portname to unicode */
6672 if (pPortName) {
6673 len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
6674 portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6675 MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
6678 res = ConfigurePortW(nameW, hWnd, portW);
6679 HeapFree(GetProcessHeap(), 0, nameW);
6680 HeapFree(GetProcessHeap(), 0, portW);
6681 return res;
6684 /******************************************************************************
6685 * ConfigurePortW (WINSPOOL.@)
6687 * Display the Configuration-Dialog for a specific Port
6689 * PARAMS
6690 * pName [I] Servername or NULL (local Computer)
6691 * hWnd [I] Handle to parent Window for the Dialog-Box
6692 * pPortName [I] Name of the Port, that should be configured
6694 * RETURNS
6695 * Success: TRUE
6696 * Failure: FALSE
6699 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
6701 monitor_t * pm;
6702 monitor_t * pui;
6703 DWORD res;
6705 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
6707 if (pName && pName[0]) {
6708 SetLastError(ERROR_INVALID_PARAMETER);
6709 return FALSE;
6712 if (!pPortName) {
6713 SetLastError(RPC_X_NULL_REF_POINTER);
6714 return FALSE;
6717 /* an empty Portname is Invalid, but can popup a Dialog */
6718 if (!pPortName[0]) {
6719 SetLastError(ERROR_NOT_SUPPORTED);
6720 return FALSE;
6723 pm = monitor_load_by_port(pPortName);
6724 if (pm && pm->monitor && pm->monitor->pfnConfigurePort) {
6725 TRACE("Using %s for %s (%p: %s)\n", debugstr_w(pm->name), debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
6726 res = pm->monitor->pfnConfigurePort(pName, hWnd, pPortName);
6727 TRACE("got %d with %u\n", res, GetLastError());
6729 else
6731 pui = monitor_loadui(pm);
6732 if (pui && pui->monitorUI && pui->monitorUI->pfnConfigurePortUI) {
6733 TRACE("Use %s for %s (%p: %s)\n", debugstr_w(pui->name), debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
6734 res = pui->monitorUI->pfnConfigurePortUI(pName, hWnd, pPortName);
6735 TRACE("got %d with %u\n", res, GetLastError());
6737 else
6739 FIXME("not implemented for %s (%p: %s => %p: %s)\n", debugstr_w(pPortName),
6740 pm, pm ? debugstr_w(pm->dllname) : NULL, pui, pui ? debugstr_w(pui->dllname) : NULL);
6742 /* XP: ERROR_NOT_SUPPORTED, NT351,9x: ERROR_INVALID_PARAMETER */
6743 SetLastError(ERROR_NOT_SUPPORTED);
6744 res = FALSE;
6746 monitor_unload(pui);
6748 monitor_unload(pm);
6750 TRACE("returning %d with %u\n", res, GetLastError());
6751 return res;
6754 /******************************************************************************
6755 * ConnectToPrinterDlg (WINSPOOL.@)
6757 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
6759 FIXME("%p %x\n", hWnd, Flags);
6760 return NULL;
6763 /******************************************************************************
6764 * DeletePrinterConnectionA (WINSPOOL.@)
6766 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
6768 FIXME("%s\n", debugstr_a(pName));
6769 return TRUE;
6772 /******************************************************************************
6773 * DeletePrinterConnectionW (WINSPOOL.@)
6775 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
6777 FIXME("%s\n", debugstr_w(pName));
6778 return TRUE;
6781 /******************************************************************************
6782 * DeletePrinterDriverExW (WINSPOOL.@)
6784 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
6785 LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6787 HKEY hkey_drivers;
6788 BOOL ret = FALSE;
6790 TRACE("%s %s %s %x %x\n", debugstr_w(pName), debugstr_w(pEnvironment),
6791 debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
6793 if(pName && pName[0])
6795 FIXME("pName = %s - unsupported\n", debugstr_w(pName));
6796 SetLastError(ERROR_INVALID_PARAMETER);
6797 return FALSE;
6800 if(dwDeleteFlag)
6802 FIXME("dwDeleteFlag = %x - unsupported\n", dwDeleteFlag);
6803 SetLastError(ERROR_INVALID_PARAMETER);
6804 return FALSE;
6807 hkey_drivers = WINSPOOL_OpenDriverReg(pEnvironment, TRUE);
6809 if(!hkey_drivers)
6811 ERR("Can't open drivers key\n");
6812 return FALSE;
6815 if(RegDeleteTreeW(hkey_drivers, pDriverName) == ERROR_SUCCESS)
6816 ret = TRUE;
6818 RegCloseKey(hkey_drivers);
6820 return ret;
6823 /******************************************************************************
6824 * DeletePrinterDriverExA (WINSPOOL.@)
6826 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
6827 LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6829 UNICODE_STRING NameW, EnvW, DriverW;
6830 BOOL ret;
6832 asciitounicode(&NameW, pName);
6833 asciitounicode(&EnvW, pEnvironment);
6834 asciitounicode(&DriverW, pDriverName);
6836 ret = DeletePrinterDriverExW(NameW.Buffer, EnvW.Buffer, DriverW.Buffer, dwDeleteFlag, dwVersionFlag);
6838 RtlFreeUnicodeString(&DriverW);
6839 RtlFreeUnicodeString(&EnvW);
6840 RtlFreeUnicodeString(&NameW);
6842 return ret;
6845 /******************************************************************************
6846 * DeletePrinterDataExW (WINSPOOL.@)
6848 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
6849 LPCWSTR pValueName)
6851 FIXME("%p %s %s\n", hPrinter,
6852 debugstr_w(pKeyName), debugstr_w(pValueName));
6853 return ERROR_INVALID_PARAMETER;
6856 /******************************************************************************
6857 * DeletePrinterDataExA (WINSPOOL.@)
6859 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
6860 LPCSTR pValueName)
6862 FIXME("%p %s %s\n", hPrinter,
6863 debugstr_a(pKeyName), debugstr_a(pValueName));
6864 return ERROR_INVALID_PARAMETER;
6867 /******************************************************************************
6868 * DeletePrintProcessorA (WINSPOOL.@)
6870 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
6872 FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6873 debugstr_a(pPrintProcessorName));
6874 return TRUE;
6877 /******************************************************************************
6878 * DeletePrintProcessorW (WINSPOOL.@)
6880 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
6882 FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6883 debugstr_w(pPrintProcessorName));
6884 return TRUE;
6887 /******************************************************************************
6888 * DeletePrintProvidorA (WINSPOOL.@)
6890 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
6892 FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6893 debugstr_a(pPrintProviderName));
6894 return TRUE;
6897 /******************************************************************************
6898 * DeletePrintProvidorW (WINSPOOL.@)
6900 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
6902 FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6903 debugstr_w(pPrintProviderName));
6904 return TRUE;
6907 /******************************************************************************
6908 * EnumFormsA (WINSPOOL.@)
6910 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6911 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6913 FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6914 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6915 return FALSE;
6918 /******************************************************************************
6919 * EnumFormsW (WINSPOOL.@)
6921 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6922 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6924 FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6925 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6926 return FALSE;
6929 /*****************************************************************************
6930 * EnumMonitorsA [WINSPOOL.@]
6932 * See EnumMonitorsW.
6935 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
6936 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6938 BOOL res;
6939 LPBYTE bufferW = NULL;
6940 LPWSTR nameW = NULL;
6941 DWORD needed = 0;
6942 DWORD numentries = 0;
6943 INT len;
6945 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pMonitors,
6946 cbBuf, pcbNeeded, pcReturned);
6948 /* convert servername to unicode */
6949 if (pName) {
6950 len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6951 nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6952 MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6954 /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
6955 needed = cbBuf * sizeof(WCHAR);
6956 if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
6957 res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6959 if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
6960 if (pcbNeeded) needed = *pcbNeeded;
6961 /* HeapReAlloc return NULL, when bufferW was NULL */
6962 bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
6963 HeapAlloc(GetProcessHeap(), 0, needed);
6965 /* Try again with the large Buffer */
6966 res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6968 numentries = pcReturned ? *pcReturned : 0;
6969 needed = 0;
6971 W2k require the buffersize from EnumMonitorsW also for EnumMonitorsA.
6972 We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
6974 if (res) {
6975 /* EnumMonitorsW collected all Data. Parse them to calculate ANSI-Size */
6976 DWORD entrysize = 0;
6977 DWORD index;
6978 LPSTR ptr;
6979 LPMONITOR_INFO_2W mi2w;
6980 LPMONITOR_INFO_2A mi2a;
6982 /* MONITOR_INFO_*W and MONITOR_INFO_*A have the same size */
6983 entrysize = (Level == 1) ? sizeof(MONITOR_INFO_1A) : sizeof(MONITOR_INFO_2A);
6985 /* First pass: calculate the size for all Entries */
6986 mi2w = (LPMONITOR_INFO_2W) bufferW;
6987 mi2a = (LPMONITOR_INFO_2A) pMonitors;
6988 index = 0;
6989 while (index < numentries) {
6990 index++;
6991 needed += entrysize; /* MONITOR_INFO_?A */
6992 TRACE("%p: parsing #%d (%s)\n", mi2w, index, debugstr_w(mi2w->pName));
6994 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6995 NULL, 0, NULL, NULL);
6996 if (Level > 1) {
6997 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6998 NULL, 0, NULL, NULL);
6999 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
7000 NULL, 0, NULL, NULL);
7002 /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
7003 mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
7004 mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
7007 /* check for errors and quit on failure */
7008 if (cbBuf < needed) {
7009 SetLastError(ERROR_INSUFFICIENT_BUFFER);
7010 res = FALSE;
7011 goto emA_cleanup;
7013 len = entrysize * numentries; /* room for all MONITOR_INFO_?A */
7014 ptr = (LPSTR) &pMonitors[len]; /* room for strings */
7015 cbBuf -= len ; /* free Bytes in the user-Buffer */
7016 mi2w = (LPMONITOR_INFO_2W) bufferW;
7017 mi2a = (LPMONITOR_INFO_2A) pMonitors;
7018 index = 0;
7019 /* Second Pass: Fill the User Buffer (if we have one) */
7020 while ((index < numentries) && pMonitors) {
7021 index++;
7022 TRACE("%p: writing MONITOR_INFO_%dA #%d\n", mi2a, Level, index);
7023 mi2a->pName = ptr;
7024 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
7025 ptr, cbBuf , NULL, NULL);
7026 ptr += len;
7027 cbBuf -= len;
7028 if (Level > 1) {
7029 mi2a->pEnvironment = ptr;
7030 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
7031 ptr, cbBuf, NULL, NULL);
7032 ptr += len;
7033 cbBuf -= len;
7035 mi2a->pDLLName = ptr;
7036 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
7037 ptr, cbBuf, NULL, NULL);
7038 ptr += len;
7039 cbBuf -= len;
7041 /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
7042 mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
7043 mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
7046 emA_cleanup:
7047 if (pcbNeeded) *pcbNeeded = needed;
7048 if (pcReturned) *pcReturned = (res) ? numentries : 0;
7050 HeapFree(GetProcessHeap(), 0, nameW);
7051 HeapFree(GetProcessHeap(), 0, bufferW);
7053 TRACE("returning %d with %d (%d byte for %d entries)\n",
7054 (res), GetLastError(), needed, numentries);
7056 return (res);
7060 /*****************************************************************************
7061 * EnumMonitorsW [WINSPOOL.@]
7063 * Enumerate available Port-Monitors
7065 * PARAMS
7066 * pName [I] Servername or NULL (local Computer)
7067 * Level [I] Structure-Level (1:Win9x+NT or 2:NT only)
7068 * pMonitors [O] PTR to Buffer that receives the Result
7069 * cbBuf [I] Size of Buffer at pMonitors
7070 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
7071 * pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
7073 * RETURNS
7074 * Success: TRUE
7075 * Failure: FALSE and in pcbNeeded the Bytes required for buffer, if cbBuf is too small
7077 * NOTES
7078 * Windows reads the Registry once and cache the Results.
7080 *| Language-Monitors are also installed in the same Registry-Location but
7081 *| they are filtered in Windows (not returned by EnumMonitors).
7082 *| We do no filtering to simplify our Code.
7085 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
7086 DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
7088 DWORD needed = 0;
7089 DWORD numentries = 0;
7090 BOOL res = FALSE;
7092 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
7093 cbBuf, pcbNeeded, pcReturned);
7095 if (pName && (lstrlenW(pName))) {
7096 FIXME("for Server %s not implemented\n", debugstr_w(pName));
7097 SetLastError(ERROR_ACCESS_DENIED);
7098 goto emW_cleanup;
7101 /* Level is not checked in win9x */
7102 if (!Level || (Level > 2)) {
7103 WARN("level (%d) is ignored in win9x\n", Level);
7104 SetLastError(ERROR_INVALID_LEVEL);
7105 goto emW_cleanup;
7107 if (!pcbNeeded) {
7108 SetLastError(RPC_X_NULL_REF_POINTER);
7109 goto emW_cleanup;
7112 /* Scan all Monitor-Keys */
7113 numentries = 0;
7114 needed = get_local_monitors(Level, NULL, 0, &numentries);
7116 /* we calculated the needed buffersize. now do the error-checks */
7117 if (cbBuf < needed) {
7118 SetLastError(ERROR_INSUFFICIENT_BUFFER);
7119 goto emW_cleanup;
7121 else if (!pMonitors || !pcReturned) {
7122 SetLastError(RPC_X_NULL_REF_POINTER);
7123 goto emW_cleanup;
7126 /* fill the Buffer with the Monitor-Keys */
7127 needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
7128 res = TRUE;
7130 emW_cleanup:
7131 if (pcbNeeded) *pcbNeeded = needed;
7132 if (pcReturned) *pcReturned = numentries;
7134 TRACE("returning %d with %d (%d byte for %d entries)\n",
7135 res, GetLastError(), needed, numentries);
7137 return (res);
7140 /******************************************************************************
7141 * XcvDataW (WINSPOOL.@)
7143 * Execute commands in the Printmonitor DLL
7145 * PARAMS
7146 * hXcv [i] Handle from OpenPrinter (with XcvMonitor or XcvPort)
7147 * pszDataName [i] Name of the command to execute
7148 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
7149 * cbInputData [i] Size in Bytes of Buffer at pInputData
7150 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
7151 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
7152 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
7153 * pdwStatus [o] PTR to receive the win32 error code from the Printmonitor DLL
7155 * RETURNS
7156 * Success: TRUE
7157 * Failure: FALSE
7159 * NOTES
7160 * Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
7161 * The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
7163 * Minimal List of commands, that a Printmonitor DLL should support:
7165 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
7166 *| "AddPort" : Add a Port
7167 *| "DeletePort": Delete a Port
7169 * Many Printmonitors support additional commands. Examples for localspl.dll:
7170 * "GetDefaultCommConfig", "SetDefaultCommConfig",
7171 * "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
7174 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
7175 DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
7176 PDWORD pcbOutputNeeded, PDWORD pdwStatus)
7178 opened_printer_t *printer;
7180 TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
7181 pInputData, cbInputData, pOutputData,
7182 cbOutputData, pcbOutputNeeded, pdwStatus);
7184 printer = get_opened_printer(hXcv);
7185 if (!printer || (!printer->hXcv)) {
7186 SetLastError(ERROR_INVALID_HANDLE);
7187 return FALSE;
7190 if (!pcbOutputNeeded) {
7191 SetLastError(ERROR_INVALID_PARAMETER);
7192 return FALSE;
7195 if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
7196 SetLastError(RPC_X_NULL_REF_POINTER);
7197 return FALSE;
7200 *pcbOutputNeeded = 0;
7202 *pdwStatus = printer->pm->monitor->pfnXcvDataPort(printer->hXcv, pszDataName,
7203 pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
7205 return TRUE;
7208 /*****************************************************************************
7209 * EnumPrinterDataA [WINSPOOL.@]
7212 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
7213 DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7214 DWORD cbData, LPDWORD pcbData )
7216 FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7217 cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7218 return ERROR_NO_MORE_ITEMS;
7221 /*****************************************************************************
7222 * EnumPrinterDataW [WINSPOOL.@]
7225 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
7226 DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7227 DWORD cbData, LPDWORD pcbData )
7229 FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7230 cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7231 return ERROR_NO_MORE_ITEMS;
7234 /*****************************************************************************
7235 * EnumPrintProcessorDatatypesA [WINSPOOL.@]
7238 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
7239 DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7240 LPDWORD pcbNeeded, LPDWORD pcReturned)
7242 FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_a(pName),
7243 debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
7244 pcbNeeded, pcReturned);
7245 return FALSE;
7248 /*****************************************************************************
7249 * EnumPrintProcessorDatatypesW [WINSPOOL.@]
7252 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
7253 DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7254 LPDWORD pcbNeeded, LPDWORD pcReturned)
7256 FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7257 debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
7258 pcbNeeded, pcReturned);
7259 return FALSE;
7262 /*****************************************************************************
7263 * EnumPrintProcessorsA [WINSPOOL.@]
7266 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
7267 LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
7269 FIXME("Stub: %s %s %d %p %d %p %p\n", pName, pEnvironment, Level,
7270 pPrintProcessorInfo, cbBuf, pcbNeeded, pcbReturned);
7271 return FALSE;
7274 /*****************************************************************************
7275 * EnumPrintProcessorsW [WINSPOOL.@]
7278 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
7279 LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned)
7281 FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7282 debugstr_w(pEnvironment), Level, pPrintProcessorInfo,
7283 cbBuf, pcbNeeded, pcbReturned);
7284 return FALSE;
7287 /*****************************************************************************
7288 * ExtDeviceMode [WINSPOOL.@]
7291 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
7292 LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
7293 DWORD fMode)
7295 FIXME("Stub: %p %p %p %s %s %p %s %x\n", hWnd, hInst, pDevModeOutput,
7296 debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
7297 debugstr_a(pProfile), fMode);
7298 return -1;
7301 /*****************************************************************************
7302 * FindClosePrinterChangeNotification [WINSPOOL.@]
7305 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
7307 FIXME("Stub: %p\n", hChange);
7308 return TRUE;
7311 /*****************************************************************************
7312 * FindFirstPrinterChangeNotification [WINSPOOL.@]
7315 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
7316 DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
7318 FIXME("Stub: %p %x %x %p\n",
7319 hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
7320 return INVALID_HANDLE_VALUE;
7323 /*****************************************************************************
7324 * FindNextPrinterChangeNotification [WINSPOOL.@]
7327 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
7328 LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
7330 FIXME("Stub: %p %p %p %p\n",
7331 hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
7332 return FALSE;
7335 /*****************************************************************************
7336 * FreePrinterNotifyInfo [WINSPOOL.@]
7339 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
7341 FIXME("Stub: %p\n", pPrinterNotifyInfo);
7342 return TRUE;
7345 /*****************************************************************************
7346 * string_to_buf
7348 * Copies a unicode string into a buffer. The buffer will either contain unicode or
7349 * ansi depending on the unicode parameter.
7351 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
7353 if(!str)
7355 *size = 0;
7356 return TRUE;
7359 if(unicode)
7361 *size = (strlenW(str) + 1) * sizeof(WCHAR);
7362 if(*size <= cb)
7364 memcpy(ptr, str, *size);
7365 return TRUE;
7367 return FALSE;
7369 else
7371 *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
7372 if(*size <= cb)
7374 WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
7375 return TRUE;
7377 return FALSE;
7381 /*****************************************************************************
7382 * get_job_info_1
7384 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
7385 LPDWORD pcbNeeded, BOOL unicode)
7387 DWORD size, left = cbBuf;
7388 BOOL space = (cbBuf > 0);
7389 LPBYTE ptr = buf;
7391 *pcbNeeded = 0;
7393 if(space)
7395 ji1->JobId = job->job_id;
7398 string_to_buf(job->document_title, ptr, left, &size, unicode);
7399 if(space && size <= left)
7401 ji1->pDocument = (LPWSTR)ptr;
7402 ptr += size;
7403 left -= size;
7405 else
7406 space = FALSE;
7407 *pcbNeeded += size;
7409 return space;
7412 /*****************************************************************************
7413 * get_job_info_2
7415 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
7416 LPDWORD pcbNeeded, BOOL unicode)
7418 DWORD size, left = cbBuf;
7419 BOOL space = (cbBuf > 0);
7420 LPBYTE ptr = buf;
7422 *pcbNeeded = 0;
7424 if(space)
7426 ji2->JobId = job->job_id;
7429 string_to_buf(job->document_title, ptr, left, &size, unicode);
7430 if(space && size <= left)
7432 ji2->pDocument = (LPWSTR)ptr;
7433 ptr += size;
7434 left -= size;
7436 else
7437 space = FALSE;
7438 *pcbNeeded += size;
7440 return space;
7443 /*****************************************************************************
7444 * get_job_info
7446 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7447 DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
7449 BOOL ret = FALSE;
7450 DWORD needed = 0, size;
7451 job_t *job;
7452 LPBYTE ptr = pJob;
7454 TRACE("%p %d %d %p %d %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
7456 EnterCriticalSection(&printer_handles_cs);
7457 job = get_job(hPrinter, JobId);
7458 if(!job)
7459 goto end;
7461 switch(Level)
7463 case 1:
7464 size = sizeof(JOB_INFO_1W);
7465 if(cbBuf >= size)
7467 cbBuf -= size;
7468 ptr += size;
7469 memset(pJob, 0, size);
7471 else
7472 cbBuf = 0;
7473 ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
7474 needed += size;
7475 break;
7477 case 2:
7478 size = sizeof(JOB_INFO_2W);
7479 if(cbBuf >= size)
7481 cbBuf -= size;
7482 ptr += size;
7483 memset(pJob, 0, size);
7485 else
7486 cbBuf = 0;
7487 ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
7488 needed += size;
7489 break;
7491 case 3:
7492 size = sizeof(JOB_INFO_3);
7493 if(cbBuf >= size)
7495 cbBuf -= size;
7496 memset(pJob, 0, size);
7497 ret = TRUE;
7499 else
7500 cbBuf = 0;
7501 needed = size;
7502 break;
7504 default:
7505 SetLastError(ERROR_INVALID_LEVEL);
7506 goto end;
7508 if(pcbNeeded)
7509 *pcbNeeded = needed;
7510 end:
7511 LeaveCriticalSection(&printer_handles_cs);
7512 return ret;
7515 /*****************************************************************************
7516 * GetJobA [WINSPOOL.@]
7519 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7520 DWORD cbBuf, LPDWORD pcbNeeded)
7522 return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
7525 /*****************************************************************************
7526 * GetJobW [WINSPOOL.@]
7529 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7530 DWORD cbBuf, LPDWORD pcbNeeded)
7532 return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
7535 /*****************************************************************************
7536 * schedule_lpr
7538 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
7540 char *unixname, *queue, *cmd;
7541 char fmt[] = "lpr -P%s %s";
7542 DWORD len;
7544 if(!(unixname = wine_get_unix_file_name(filename)))
7545 return FALSE;
7547 len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7548 queue = HeapAlloc(GetProcessHeap(), 0, len);
7549 WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7551 cmd = HeapAlloc(GetProcessHeap(), 0, strlen(unixname) + len + sizeof(fmt) - 5);
7552 sprintf(cmd, fmt, queue, unixname);
7554 TRACE("printing with: %s\n", cmd);
7555 system(cmd);
7557 HeapFree(GetProcessHeap(), 0, cmd);
7558 HeapFree(GetProcessHeap(), 0, queue);
7559 HeapFree(GetProcessHeap(), 0, unixname);
7560 return TRUE;
7563 /*****************************************************************************
7564 * schedule_cups
7566 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
7568 #ifdef SONAME_LIBCUPS
7569 if(pcupsPrintFile)
7571 char *unixname, *queue, *doc_titleA;
7572 DWORD len;
7573 BOOL ret;
7575 if(!(unixname = wine_get_unix_file_name(filename)))
7576 return FALSE;
7578 len = WideCharToMultiByte(CP_ACP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7579 queue = HeapAlloc(GetProcessHeap(), 0, len);
7580 WideCharToMultiByte(CP_ACP, 0, printer_name, -1, queue, len, NULL, NULL);
7582 len = WideCharToMultiByte(CP_ACP, 0, document_title, -1, NULL, 0, NULL, NULL);
7583 doc_titleA = HeapAlloc(GetProcessHeap(), 0, len);
7584 WideCharToMultiByte(CP_ACP, 0, document_title, -1, doc_titleA, len, NULL, NULL);
7586 TRACE("printing via cups\n");
7587 ret = pcupsPrintFile(queue, unixname, doc_titleA, 0, NULL);
7588 HeapFree(GetProcessHeap(), 0, doc_titleA);
7589 HeapFree(GetProcessHeap(), 0, queue);
7590 HeapFree(GetProcessHeap(), 0, unixname);
7591 return ret;
7593 else
7594 #endif
7596 return schedule_lpr(printer_name, filename);
7600 INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
7602 LPWSTR filename;
7604 switch(msg)
7606 case WM_INITDIALOG:
7607 SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
7608 return TRUE;
7610 case WM_COMMAND:
7611 if(HIWORD(wparam) == BN_CLICKED)
7613 if(LOWORD(wparam) == IDOK)
7615 HANDLE hf;
7616 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
7617 LPWSTR *output;
7619 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
7620 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
7622 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
7624 WCHAR caption[200], message[200];
7625 int mb_ret;
7627 LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7628 LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
7629 mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
7630 if(mb_ret == IDCANCEL)
7632 HeapFree(GetProcessHeap(), 0, filename);
7633 return TRUE;
7636 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
7637 if(hf == INVALID_HANDLE_VALUE)
7639 WCHAR caption[200], message[200];
7641 LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7642 LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
7643 MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
7644 HeapFree(GetProcessHeap(), 0, filename);
7645 return TRUE;
7647 CloseHandle(hf);
7648 DeleteFileW(filename);
7649 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
7650 *output = filename;
7651 EndDialog(hwnd, IDOK);
7652 return TRUE;
7654 if(LOWORD(wparam) == IDCANCEL)
7656 EndDialog(hwnd, IDCANCEL);
7657 return TRUE;
7660 return FALSE;
7662 return FALSE;
7665 /*****************************************************************************
7666 * get_filename
7668 static BOOL get_filename(LPWSTR *filename)
7670 return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
7671 file_dlg_proc, (LPARAM)filename) == IDOK;
7674 /*****************************************************************************
7675 * schedule_file
7677 static BOOL schedule_file(LPCWSTR filename)
7679 LPWSTR output = NULL;
7681 if(get_filename(&output))
7683 TRACE("copy to %s\n", debugstr_w(output));
7684 CopyFileW(filename, output, FALSE);
7685 HeapFree(GetProcessHeap(), 0, output);
7686 return TRUE;
7688 return FALSE;
7691 /*****************************************************************************
7692 * schedule_pipe
7694 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
7696 #ifdef HAVE_FORK
7697 char *unixname, *cmdA;
7698 DWORD len;
7699 int fds[2] = {-1, -1}, file_fd = -1, no_read;
7700 BOOL ret = FALSE;
7701 char buf[1024];
7703 if(!(unixname = wine_get_unix_file_name(filename)))
7704 return FALSE;
7706 len = WideCharToMultiByte(CP_ACP, 0, cmd, -1, NULL, 0, NULL, NULL);
7707 cmdA = HeapAlloc(GetProcessHeap(), 0, len);
7708 WideCharToMultiByte(CP_ACP, 0, cmd, -1, cmdA, len, NULL, NULL);
7710 TRACE("printing with: %s\n", cmdA);
7712 if((file_fd = open(unixname, O_RDONLY)) == -1)
7713 goto end;
7715 if (pipe(fds))
7717 ERR("pipe() failed!\n");
7718 goto end;
7721 if (fork() == 0)
7723 close(0);
7724 dup2(fds[0], 0);
7725 close(fds[1]);
7727 /* reset signals that we previously set to SIG_IGN */
7728 signal(SIGPIPE, SIG_DFL);
7729 signal(SIGCHLD, SIG_DFL);
7731 execl("/bin/sh", "/bin/sh", "-c", cmdA, (char*)0);
7732 _exit(1);
7735 while((no_read = read(file_fd, buf, sizeof(buf))) > 0)
7736 write(fds[1], buf, no_read);
7738 ret = TRUE;
7740 end:
7741 if(file_fd != -1) close(file_fd);
7742 if(fds[0] != -1) close(fds[0]);
7743 if(fds[1] != -1) close(fds[1]);
7745 HeapFree(GetProcessHeap(), 0, cmdA);
7746 HeapFree(GetProcessHeap(), 0, unixname);
7747 return ret;
7748 #else
7749 return FALSE;
7750 #endif
7753 /*****************************************************************************
7754 * schedule_unixfile
7756 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
7758 int in_fd, out_fd, no_read;
7759 char buf[1024];
7760 BOOL ret = FALSE;
7761 char *unixname, *outputA;
7762 DWORD len;
7764 if(!(unixname = wine_get_unix_file_name(filename)))
7765 return FALSE;
7767 len = WideCharToMultiByte(CP_ACP, 0, output, -1, NULL, 0, NULL, NULL);
7768 outputA = HeapAlloc(GetProcessHeap(), 0, len);
7769 WideCharToMultiByte(CP_ACP, 0, output, -1, outputA, len, NULL, NULL);
7771 out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
7772 in_fd = open(unixname, O_RDONLY);
7773 if(out_fd == -1 || in_fd == -1)
7774 goto end;
7776 while((no_read = read(in_fd, buf, sizeof(buf))) > 0)
7777 write(out_fd, buf, no_read);
7779 ret = TRUE;
7780 end:
7781 if(in_fd != -1) close(in_fd);
7782 if(out_fd != -1) close(out_fd);
7783 HeapFree(GetProcessHeap(), 0, outputA);
7784 HeapFree(GetProcessHeap(), 0, unixname);
7785 return ret;
7788 /*****************************************************************************
7789 * ScheduleJob [WINSPOOL.@]
7792 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
7794 opened_printer_t *printer;
7795 BOOL ret = FALSE;
7796 struct list *cursor, *cursor2;
7798 TRACE("(%p, %x)\n", hPrinter, dwJobID);
7799 EnterCriticalSection(&printer_handles_cs);
7800 printer = get_opened_printer(hPrinter);
7801 if(!printer)
7802 goto end;
7804 LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
7806 job_t *job = LIST_ENTRY(cursor, job_t, entry);
7807 HANDLE hf;
7809 if(job->job_id != dwJobID) continue;
7811 hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7812 if(hf != INVALID_HANDLE_VALUE)
7814 PRINTER_INFO_5W *pi5;
7815 DWORD needed;
7816 HKEY hkey;
7817 WCHAR output[1024];
7818 static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
7819 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
7821 GetPrinterW(hPrinter, 5, NULL, 0, &needed);
7822 pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
7823 GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
7824 TRACE("need to schedule job %d filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
7825 debugstr_w(pi5->pPortName));
7827 output[0] = 0;
7829 /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
7830 if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
7832 DWORD type, count = sizeof(output);
7833 RegQueryValueExW(hkey, pi5->pPortName, NULL, &type, (LPBYTE)output, &count);
7834 RegCloseKey(hkey);
7836 if(output[0] == '|')
7838 schedule_pipe(output + 1, job->filename);
7840 else if(output[0])
7842 schedule_unixfile(output, job->filename);
7844 else if(!strncmpW(pi5->pPortName, LPR_Port, strlenW(LPR_Port)))
7846 schedule_lpr(pi5->pPortName + strlenW(LPR_Port), job->filename);
7848 else if(!strncmpW(pi5->pPortName, CUPS_Port, strlenW(CUPS_Port)))
7850 schedule_cups(pi5->pPortName + strlenW(CUPS_Port), job->filename, job->document_title);
7852 else if(!strncmpW(pi5->pPortName, FILE_Port, strlenW(FILE_Port)))
7854 schedule_file(job->filename);
7856 else
7858 FIXME("can't schedule to port %s\n", debugstr_w(pi5->pPortName));
7860 HeapFree(GetProcessHeap(), 0, pi5);
7861 CloseHandle(hf);
7862 DeleteFileW(job->filename);
7864 list_remove(cursor);
7865 HeapFree(GetProcessHeap(), 0, job->document_title);
7866 HeapFree(GetProcessHeap(), 0, job->filename);
7867 HeapFree(GetProcessHeap(), 0, job);
7868 ret = TRUE;
7869 break;
7871 end:
7872 LeaveCriticalSection(&printer_handles_cs);
7873 return ret;
7876 /*****************************************************************************
7877 * StartDocDlgA [WINSPOOL.@]
7879 LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
7881 UNICODE_STRING usBuffer;
7882 DOCINFOW docW;
7883 LPWSTR retW;
7884 LPWSTR docnameW = NULL, outputW = NULL, datatypeW = NULL;
7885 LPSTR ret = NULL;
7887 docW.cbSize = sizeof(docW);
7888 if (doc->lpszDocName)
7890 docnameW = asciitounicode(&usBuffer, doc->lpszDocName);
7891 if (!(docW.lpszDocName = docnameW)) return NULL;
7893 if (doc->lpszOutput)
7895 outputW = asciitounicode(&usBuffer, doc->lpszOutput);
7896 if (!(docW.lpszOutput = outputW)) return NULL;
7898 if (doc->lpszDatatype)
7900 datatypeW = asciitounicode(&usBuffer, doc->lpszDatatype);
7901 if (!(docW.lpszDatatype = datatypeW)) return NULL;
7903 docW.fwType = doc->fwType;
7905 retW = StartDocDlgW(hPrinter, &docW);
7907 if(retW)
7909 DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
7910 ret = HeapAlloc(GetProcessHeap(), 0, len);
7911 WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
7912 HeapFree(GetProcessHeap(), 0, retW);
7915 HeapFree(GetProcessHeap(), 0, datatypeW);
7916 HeapFree(GetProcessHeap(), 0, outputW);
7917 HeapFree(GetProcessHeap(), 0, docnameW);
7919 return ret;
7922 /*****************************************************************************
7923 * StartDocDlgW [WINSPOOL.@]
7925 * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
7926 * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
7927 * port is "FILE:". Also returns the full path if passed a relative path.
7929 * The caller should free the returned string from the process heap.
7931 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
7933 LPWSTR ret = NULL;
7934 DWORD len, attr;
7936 if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
7938 PRINTER_INFO_5W *pi5;
7939 GetPrinterW(hPrinter, 5, NULL, 0, &len);
7940 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
7941 return NULL;
7942 pi5 = HeapAlloc(GetProcessHeap(), 0, len);
7943 GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
7944 if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
7946 HeapFree(GetProcessHeap(), 0, pi5);
7947 return NULL;
7949 HeapFree(GetProcessHeap(), 0, pi5);
7952 if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
7954 LPWSTR name;
7956 if (get_filename(&name))
7958 if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
7960 HeapFree(GetProcessHeap(), 0, name);
7961 return NULL;
7963 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7964 GetFullPathNameW(name, len, ret, NULL);
7965 HeapFree(GetProcessHeap(), 0, name);
7967 return ret;
7970 if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
7971 return NULL;
7973 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7974 GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
7976 attr = GetFileAttributesW(ret);
7977 if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
7979 HeapFree(GetProcessHeap(), 0, ret);
7980 ret = NULL;
7982 return ret;