mpr: Implement WNetClearConnections().
[wine.git] / dlls / localspl / provider.c
blobc17d982fa725aabca6294e0430005fb924684df2
1 /*
2 * Implementation of the Local Printprovider
4 * Copyright 2006-2009 Detlef Riekenberg
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winreg.h"
29 #include "winspool.h"
30 #include "winuser.h"
31 #include "ddk/winddiui.h"
32 #include "ddk/winsplp.h"
34 #include "wine/debug.h"
35 #include "wine/heap.h"
36 #include "wine/list.h"
37 #include "wine/unicode.h"
38 #include "localspl_private.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(localspl);
42 /* ############################### */
44 static CRITICAL_SECTION monitor_handles_cs;
45 static CRITICAL_SECTION_DEBUG monitor_handles_cs_debug =
47 0, 0, &monitor_handles_cs,
48 { &monitor_handles_cs_debug.ProcessLocksList, &monitor_handles_cs_debug.ProcessLocksList },
49 0, 0, { (DWORD_PTR)(__FILE__ ": monitor_handles_cs") }
51 static CRITICAL_SECTION monitor_handles_cs = { &monitor_handles_cs_debug, -1, 0, 0, 0, 0 };
53 /* ############################### */
55 typedef struct {
56 WCHAR src[MAX_PATH+MAX_PATH];
57 WCHAR dst[MAX_PATH+MAX_PATH];
58 DWORD srclen;
59 DWORD dstlen;
60 DWORD copyflags;
61 BOOL lazy;
62 } apd_data_t;
64 typedef struct {
65 struct list entry;
66 LPWSTR name;
67 LPWSTR dllname;
68 PMONITORUI monitorUI;
69 LPMONITOR monitor;
70 HMODULE hdll;
71 DWORD refcount;
72 DWORD dwMonitorSize;
73 } monitor_t;
75 typedef struct {
76 LPCWSTR envname;
77 LPCWSTR subdir;
78 DWORD driverversion;
79 LPCWSTR versionregpath;
80 LPCWSTR versionsubdir;
81 } printenv_t;
83 typedef struct {
84 LPWSTR name;
85 LPWSTR printername;
86 monitor_t * pm;
87 HANDLE hXcv;
88 } printer_t;
90 /* ############################### */
92 static struct list monitor_handles = LIST_INIT( monitor_handles );
93 static monitor_t * pm_localport;
95 static const PRINTPROVIDOR * pprovider = NULL;
97 static const WCHAR backslashW[] = {'\\',0};
98 static const WCHAR bs_ports_bsW[] = {'\\','P','o','r','t','s','\\',0};
99 static const WCHAR configuration_fileW[] = {'C','o','n','f','i','g','u','r','a','t','i','o','n',' ','F','i','l','e',0};
100 static const WCHAR datatypeW[] = {'D','a','t','a','t','y','p','e',0};
101 static const WCHAR data_fileW[] = {'D','a','t','a',' ','F','i','l','e',0};
102 static const WCHAR dependent_filesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
103 static const WCHAR driverW[] = {'D','r','i','v','e','r',0};
104 static const WCHAR emptyW[] = {0};
105 static const WCHAR fmt_driversW[] = { 'S','y','s','t','e','m','\\',
106 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
107 'c','o','n','t','r','o','l','\\',
108 'P','r','i','n','t','\\',
109 'E','n','v','i','r','o','n','m','e','n','t','s','\\',
110 '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
111 static const WCHAR fmt_printprocessorsW[] = { 'S','y','s','t','e','m','\\',
112 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
113 'C','o','n','t','r','o','l','\\',
114 'P','r','i','n','t','\\',
115 'E','n','v','i','r','o','n','m','e','n','t','s','\\','%','s','\\',
116 'P','r','i','n','t',' ','P','r','o','c','e','s','s','o','r','s',0 };
117 static const WCHAR help_fileW[] = {'H','e','l','p',' ','F','i','l','e',0};
118 static const WCHAR ia64_envnameW[] = {'W','i','n','d','o','w','s',' ','I','A','6','4',0};
119 static const WCHAR ia64_subdirW[] = {'i','a','6','4',0};
120 static const WCHAR localportW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
121 static const WCHAR monitorW[] = {'M','o','n','i','t','o','r',0};
122 static const WCHAR monitorsW[] = {'S','y','s','t','e','m','\\',
123 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
124 'C','o','n','t','r','o','l','\\',
125 'P','r','i','n','t','\\',
126 'M','o','n','i','t','o','r','s','\\',0};
127 static const WCHAR monitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
128 static const WCHAR previous_namesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
129 static const WCHAR printersW[] = {'S','y','s','t','e','m','\\',
130 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
131 'C','o','n','t','r','o','l','\\',
132 'P','r','i','n','t','\\',
133 'P','r','i','n','t','e','r','s',0};
134 static const WCHAR spoolW[] = {'\\','s','p','o','o','l',0};
135 static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0};
136 static const WCHAR spoolprtprocsW[] = {'\\','s','p','o','o','l','\\','p','r','t','p','r','o','c','s','\\',0};
137 static const WCHAR version0_regpathW[] = {'\\','V','e','r','s','i','o','n','-','0',0};
138 static const WCHAR version0_subdirW[] = {'\\','0',0};
139 static const WCHAR version3_regpathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
140 static const WCHAR version3_subdirW[] = {'\\','3',0};
141 static const WCHAR versionW[] = {'V','e','r','s','i','o','n',0};
142 static const WCHAR win40_envnameW[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
143 static const WCHAR win40_subdirW[] = {'w','i','n','4','0',0};
144 static const WCHAR winnt_cv_portsW[] = {'S','o','f','t','w','a','r','e','\\',
145 'M','i','c','r','o','s','o','f','t','\\',
146 'W','i','n','d','o','w','s',' ','N','T','\\',
147 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
148 'P','o','r','t','s',0};
149 static const WCHAR winprintW[] = {'w','i','n','p','r','i','n','t',0};
150 static const WCHAR x64_envnameW[] = {'W','i','n','d','o','w','s',' ','x','6','4',0};
151 static const WCHAR x64_subdirW[] = {'x','6','4',0};
152 static const WCHAR x86_envnameW[] = {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
153 static const WCHAR x86_subdirW[] = {'w','3','2','x','8','6',0};
154 static const WCHAR XcvMonitorW[] = {',','X','c','v','M','o','n','i','t','o','r',' ',0};
155 static const WCHAR XcvPortW[] = {',','X','c','v','P','o','r','t',' ',0};
158 static const printenv_t env_ia64 = {ia64_envnameW, ia64_subdirW, 3,
159 version3_regpathW, version3_subdirW};
161 static const printenv_t env_x86 = {x86_envnameW, x86_subdirW, 3,
162 version3_regpathW, version3_subdirW};
164 static const printenv_t env_x64 = {x64_envnameW, x64_subdirW, 3,
165 version3_regpathW, version3_subdirW};
167 static const printenv_t env_win40 = {win40_envnameW, win40_subdirW, 0,
168 version0_regpathW, version0_subdirW};
170 static const printenv_t * const all_printenv[] = {&env_x86, &env_x64, &env_ia64, &env_win40};
173 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
174 sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
175 sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
176 0, sizeof(DRIVER_INFO_8W)};
179 /******************************************************************
180 * strdupW [internal]
182 * create a copy of a unicode-string
185 static LPWSTR strdupW(LPCWSTR p)
187 LPWSTR ret;
188 DWORD len;
190 if(!p) return NULL;
191 len = (lstrlenW(p) + 1) * sizeof(WCHAR);
192 ret = heap_alloc(len);
193 if (ret) memcpy(ret, p, len);
194 return ret;
197 /******************************************************************
198 * apd_copyfile [internal]
200 * Copy a file from the driverdirectory to the versioned directory
202 * RETURNS
203 * Success: TRUE
204 * Failure: FALSE
207 static BOOL apd_copyfile( WCHAR *pathname, WCHAR *file_part, apd_data_t *apd )
209 WCHAR *srcname;
210 BOOL res;
212 apd->src[apd->srclen] = '\0';
213 apd->dst[apd->dstlen] = '\0';
215 if (!pathname || !pathname[0]) {
216 /* nothing to copy */
217 return TRUE;
220 if (apd->copyflags & APD_COPY_FROM_DIRECTORY)
221 srcname = pathname;
222 else
224 srcname = apd->src;
225 strcatW( srcname, file_part );
227 strcatW( apd->dst, file_part );
229 TRACE("%s => %s\n", debugstr_w(srcname), debugstr_w(apd->dst));
231 /* FIXME: handle APD_COPY_NEW_FILES */
232 res = CopyFileW(srcname, apd->dst, FALSE);
233 TRACE("got %d with %u\n", res, GetLastError());
235 return apd->lazy || res;
238 /******************************************************************
239 * copy_servername_from_name (internal)
241 * for an external server, the serverpart from the name is copied.
243 * RETURNS
244 * the length (in WCHAR) of the serverpart (0 for the local computer)
245 * (-length), when the name is too long
248 static LONG copy_servername_from_name(LPCWSTR name, LPWSTR target)
250 LPCWSTR server;
251 LPWSTR ptr;
252 WCHAR buffer[MAX_COMPUTERNAME_LENGTH +1];
253 DWORD len;
254 DWORD serverlen;
256 if (target) *target = '\0';
258 if (name == NULL) return 0;
259 if ((name[0] != '\\') || (name[1] != '\\')) return 0;
261 server = &name[2];
262 /* skip over both backslash, find separator '\' */
263 ptr = strchrW(server, '\\');
264 serverlen = (ptr) ? ptr - server : lstrlenW(server);
266 /* servername is empty */
267 if (serverlen == 0) return 0;
269 TRACE("found %s\n", debugstr_wn(server, serverlen));
271 if (serverlen > MAX_COMPUTERNAME_LENGTH) return -serverlen;
273 if (target) {
274 memcpy(target, server, serverlen * sizeof(WCHAR));
275 target[serverlen] = '\0';
278 len = ARRAY_SIZE(buffer);
279 if (GetComputerNameW(buffer, &len)) {
280 if ((serverlen == len) && (strncmpiW(server, buffer, len) == 0)) {
281 /* The requested Servername is our computername */
282 return 0;
285 return serverlen;
288 /******************************************************************
289 * get_basename_from_name (internal)
291 * skip over the serverpart from the full name
294 static LPCWSTR get_basename_from_name(LPCWSTR name)
296 if (name == NULL) return NULL;
297 if ((name[0] == '\\') && (name[1] == '\\')) {
298 /* skip over the servername and search for the following '\' */
299 name = strchrW(&name[2], '\\');
300 if ((name) && (name[1])) {
301 /* found a separator ('\') followed by a name:
302 skip over the separator and return the rest */
303 name++;
305 else
307 /* no basename present (we found only a servername) */
308 return NULL;
311 return name;
314 /******************************************************************
315 * monitor_unload [internal]
317 * release a printmonitor and unload it from memory, when needed
320 static void monitor_unload(monitor_t * pm)
322 if (pm == NULL) return;
323 TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
325 EnterCriticalSection(&monitor_handles_cs);
327 if (pm->refcount) pm->refcount--;
329 if (pm->refcount == 0) {
330 list_remove(&pm->entry);
331 FreeLibrary(pm->hdll);
332 heap_free(pm->name);
333 heap_free(pm->dllname);
334 heap_free(pm);
336 LeaveCriticalSection(&monitor_handles_cs);
339 /******************************************************************
340 * monitor_unloadall [internal]
342 * release all registered printmonitors and unload them from memory, when needed
346 static void monitor_unloadall(void)
348 monitor_t * pm;
349 monitor_t * next;
351 EnterCriticalSection(&monitor_handles_cs);
352 /* iterate through the list, with safety against removal */
353 LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
355 /* skip monitorui dlls */
356 if (pm->monitor) monitor_unload(pm);
358 LeaveCriticalSection(&monitor_handles_cs);
361 /******************************************************************
362 * monitor_load [internal]
364 * load a printmonitor, get the dllname from the registry, when needed
365 * initialize the monitor and dump found function-pointers
367 * On failure, SetLastError() is called and NULL is returned
370 static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname)
372 LPMONITOR2 (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
373 PMONITORUI (WINAPI *pInitializePrintMonitorUI)(VOID);
374 LPMONITOREX (WINAPI *pInitializePrintMonitor) (LPWSTR);
375 DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
376 DWORD (WINAPI *pInitializeMonitor) (LPWSTR);
378 monitor_t * pm = NULL;
379 monitor_t * cursor;
380 LPWSTR regroot = NULL;
381 LPWSTR driver = dllname;
383 TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
384 /* Is the Monitor already loaded? */
385 EnterCriticalSection(&monitor_handles_cs);
387 if (name) {
388 LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
390 if (cursor->name && (lstrcmpW(name, cursor->name) == 0)) {
391 pm = cursor;
392 break;
397 if (pm == NULL) {
398 pm = heap_alloc_zero(sizeof(monitor_t));
399 if (pm == NULL) goto cleanup;
400 list_add_tail(&monitor_handles, &pm->entry);
402 pm->refcount++;
404 if (pm->name == NULL) {
405 /* Load the monitor */
406 LPMONITOREX pmonitorEx;
407 DWORD len;
409 if (name) {
410 len = lstrlenW(monitorsW) + lstrlenW(name) + 2;
411 regroot = heap_alloc(len * sizeof(WCHAR));
414 if (regroot) {
415 lstrcpyW(regroot, monitorsW);
416 lstrcatW(regroot, name);
417 /* Get the Driver from the Registry */
418 if (driver == NULL) {
419 HKEY hroot;
420 DWORD namesize;
421 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
422 if (RegQueryValueExW(hroot, driverW, NULL, NULL, NULL,
423 &namesize) == ERROR_SUCCESS) {
424 driver = heap_alloc(namesize);
425 RegQueryValueExW(hroot, driverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
427 RegCloseKey(hroot);
432 pm->name = strdupW(name);
433 pm->dllname = strdupW(driver);
435 if ((name && (!regroot || !pm->name)) || !pm->dllname) {
436 monitor_unload(pm);
437 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
438 pm = NULL;
439 goto cleanup;
442 pm->hdll = LoadLibraryW(driver);
443 TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
445 if (pm->hdll == NULL) {
446 monitor_unload(pm);
447 SetLastError(ERROR_MOD_NOT_FOUND);
448 pm = NULL;
449 goto cleanup;
452 pInitializePrintMonitor2 = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
453 pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
454 pInitializePrintMonitor = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
455 pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
456 pInitializeMonitor = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
459 TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
460 TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
461 TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
462 TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
463 TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
465 if (pInitializePrintMonitorUI != NULL) {
466 pm->monitorUI = pInitializePrintMonitorUI();
467 TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver));
468 if (pm->monitorUI) {
469 TRACE("0x%08x: dwMonitorSize (%d)\n",
470 pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize);
475 if (pInitializePrintMonitor && regroot) {
476 pmonitorEx = pInitializePrintMonitor(regroot);
477 TRACE("%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
478 pmonitorEx, debugstr_w(driver), debugstr_w(regroot));
480 if (pmonitorEx) {
481 pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
482 pm->monitor = &(pmonitorEx->Monitor);
486 if (pm->monitor) {
487 TRACE("0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize);
491 if (!pm->monitor && regroot) {
492 if (pInitializePrintMonitor2 != NULL) {
493 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
495 if (pInitializeMonitorEx != NULL) {
496 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
498 if (pInitializeMonitor != NULL) {
499 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
502 if (!pm->monitor && !pm->monitorUI) {
503 monitor_unload(pm);
504 SetLastError(ERROR_PROC_NOT_FOUND);
505 pm = NULL;
508 cleanup:
509 if ((pm_localport == NULL) && (pm != NULL) && (lstrcmpW(pm->name, localportW) == 0)) {
510 pm->refcount++;
511 pm_localport = pm;
513 LeaveCriticalSection(&monitor_handles_cs);
514 if (driver != dllname) heap_free(driver);
515 heap_free(regroot);
516 TRACE("=> %p\n", pm);
517 return pm;
520 /******************************************************************
521 * monitor_loadall [internal]
523 * Load all registered monitors
526 static DWORD monitor_loadall(void)
528 monitor_t * pm;
529 DWORD registered = 0;
530 DWORD loaded = 0;
531 HKEY hmonitors;
532 WCHAR buffer[MAX_PATH];
533 DWORD id = 0;
535 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hmonitors) == ERROR_SUCCESS) {
536 RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, &registered, NULL, NULL,
537 NULL, NULL, NULL, NULL, NULL);
539 TRACE("%d monitors registered\n", registered);
541 while (id < registered) {
542 buffer[0] = '\0';
543 RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
544 pm = monitor_load(buffer, NULL);
545 if (pm) loaded++;
546 id++;
548 RegCloseKey(hmonitors);
550 TRACE("%d monitors loaded\n", loaded);
551 return loaded;
554 /******************************************************************
555 * monitor_loadui [internal]
557 * load the userinterface-dll for a given portmonitor
559 * On failure, NULL is returned
561 static monitor_t * monitor_loadui(monitor_t * pm)
563 monitor_t * pui = NULL;
564 WCHAR buffer[MAX_PATH];
565 HANDLE hXcv;
566 DWORD len;
567 DWORD res;
569 if (pm == NULL) return NULL;
570 TRACE("(%p) => dllname: %s\n", pm, debugstr_w(pm->dllname));
572 /* Try the Portmonitor first; works for many monitors */
573 if (pm->monitorUI) {
574 EnterCriticalSection(&monitor_handles_cs);
575 pm->refcount++;
576 LeaveCriticalSection(&monitor_handles_cs);
577 return pm;
580 /* query the userinterface-dllname from the Portmonitor */
581 if ((pm->monitor) && (pm->monitor->pfnXcvDataPort)) {
582 /* building (",XcvMonitor %s",pm->name) not needed yet */
583 res = pm->monitor->pfnXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, &hXcv);
584 TRACE("got %u with %p\n", res, hXcv);
585 if (res) {
586 res = pm->monitor->pfnXcvDataPort(hXcv, monitorUIW, NULL, 0, (BYTE *) buffer, sizeof(buffer), &len);
587 TRACE("got %u with %s\n", res, debugstr_w(buffer));
588 if (res == ERROR_SUCCESS) pui = monitor_load(NULL, buffer);
589 pm->monitor->pfnXcvClosePort(hXcv);
592 return pui;
595 /******************************************************************
596 * monitor_load_by_port [internal]
598 * load a printmonitor for a given port
600 * On failure, NULL is returned
603 static monitor_t * monitor_load_by_port(LPCWSTR portname)
605 HKEY hroot;
606 HKEY hport;
607 LPWSTR buffer;
608 monitor_t * pm = NULL;
609 DWORD registered = 0;
610 DWORD id = 0;
611 DWORD len;
613 TRACE("(%s)\n", debugstr_w(portname));
615 /* Try the Local Monitor first */
616 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, winnt_cv_portsW, &hroot) == ERROR_SUCCESS) {
617 if (RegQueryValueExW(hroot, portname, NULL, NULL, NULL, &len) == ERROR_SUCCESS) {
618 /* found the portname */
619 RegCloseKey(hroot);
620 return monitor_load(localportW, NULL);
622 RegCloseKey(hroot);
625 len = MAX_PATH + lstrlenW(bs_ports_bsW) + lstrlenW(portname) + 1;
626 buffer = heap_alloc(len * sizeof(WCHAR));
627 if (buffer == NULL) return NULL;
629 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) == ERROR_SUCCESS) {
630 EnterCriticalSection(&monitor_handles_cs);
631 RegQueryInfoKeyW(hroot, NULL, NULL, NULL, &registered, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
633 while ((pm == NULL) && (id < registered)) {
634 buffer[0] = '\0';
635 RegEnumKeyW(hroot, id, buffer, MAX_PATH);
636 TRACE("testing %s\n", debugstr_w(buffer));
637 len = lstrlenW(buffer);
638 lstrcatW(buffer, bs_ports_bsW);
639 lstrcatW(buffer, portname);
640 if (RegOpenKeyW(hroot, buffer, &hport) == ERROR_SUCCESS) {
641 RegCloseKey(hport);
642 buffer[len] = '\0'; /* use only the Monitor-Name */
643 pm = monitor_load(buffer, NULL);
645 id++;
647 LeaveCriticalSection(&monitor_handles_cs);
648 RegCloseKey(hroot);
650 heap_free(buffer);
651 return pm;
654 /******************************************************************
655 * Return the number of bytes for an multi_sz string.
656 * The result includes all \0s
657 * (specifically the extra \0, that is needed as multi_sz terminator).
659 static int multi_sz_lenW(const WCHAR *str)
661 const WCHAR *ptr = str;
662 if (!str) return 0;
665 ptr += lstrlenW(ptr) + 1;
666 } while (*ptr);
668 return (ptr - str + 1) * sizeof(WCHAR);
671 /******************************************************************
672 * validate_envW [internal]
674 * validate the user-supplied printing-environment
676 * PARAMS
677 * env [I] PTR to Environment-String or NULL
679 * RETURNS
680 * Success: PTR to printenv_t
681 * Failure: NULL and ERROR_INVALID_ENVIRONMENT
683 * NOTES
684 * An empty string is handled the same way as NULL.
688 static const printenv_t * validate_envW(LPCWSTR env)
690 const printenv_t *result = NULL;
691 unsigned int i;
693 TRACE("(%s)\n", debugstr_w(env));
694 if (env && env[0])
696 for (i = 0; i < ARRAY_SIZE(all_printenv); i++)
698 if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
700 result = all_printenv[i];
701 break;
704 if (result == NULL) {
705 FIXME("unsupported Environment: %s\n", debugstr_w(env));
706 SetLastError(ERROR_INVALID_ENVIRONMENT);
708 /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
710 else
712 result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
715 TRACE("=> using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
716 return result;
719 /*****************************************************************************
720 * enumerate the local monitors (INTERNAL)
722 * returns the needed size (in bytes) for pMonitors
723 * and *lpreturned is set to number of entries returned in pMonitors
725 * Language-Monitors are also installed in the same Registry-Location but
726 * they are filtered in Windows (not returned by EnumMonitors).
727 * We do no filtering to simplify our Code.
730 static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
732 HKEY hroot = NULL;
733 HKEY hentry = NULL;
734 LPWSTR ptr;
735 LPMONITOR_INFO_2W mi;
736 WCHAR buffer[MAX_PATH];
737 WCHAR dllname[MAX_PATH];
738 DWORD dllsize;
739 DWORD len;
740 DWORD index = 0;
741 DWORD needed = 0;
742 DWORD numentries;
743 DWORD entrysize;
745 entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
747 numentries = *lpreturned; /* this is 0, when we scan the registry */
748 len = entrysize * numentries;
749 ptr = (LPWSTR) &pMonitors[len];
751 numentries = 0;
752 len = ARRAY_SIZE(buffer);
753 buffer[0] = '\0';
755 /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
756 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) == ERROR_SUCCESS) {
757 /* Scan all Monitor-Registry-Keys */
758 while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
759 TRACE("Monitor_%d: %s\n", numentries, debugstr_w(buffer));
760 dllsize = sizeof(dllname);
761 dllname[0] = '\0';
763 /* The Monitor must have a Driver-DLL */
764 if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
765 if (RegQueryValueExW(hentry, driverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
766 /* We found a valid DLL for this Monitor. */
767 TRACE("using Driver: %s\n", debugstr_w(dllname));
769 RegCloseKey(hentry);
772 /* Windows returns only Port-Monitors here, but to simplify our code,
773 we do no filtering for Language-Monitors */
774 if (dllname[0]) {
775 numentries++;
776 needed += entrysize;
777 needed += (len+1) * sizeof(WCHAR); /* len is lstrlenW(monitorname) */
778 if (level > 1) {
779 /* we install and return only monitors for "Windows NT x86" */
780 needed += (lstrlenW(x86_envnameW) +1) * sizeof(WCHAR);
781 needed += dllsize;
784 /* required size is calculated. Now fill the user-buffer */
785 if (pMonitors && (cbBuf >= needed)){
786 mi = (LPMONITOR_INFO_2W) pMonitors;
787 pMonitors += entrysize;
789 TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi, level, numentries);
790 mi->pName = ptr;
791 lstrcpyW(ptr, buffer); /* Name of the Monitor */
792 ptr += (len+1); /* len is lstrlenW(monitorname) */
793 if (level > 1) {
794 mi->pEnvironment = ptr;
795 lstrcpyW(ptr, x86_envnameW); /* fixed to "Windows NT x86" */
796 ptr += (lstrlenW(x86_envnameW)+1);
798 mi->pDLLName = ptr;
799 lstrcpyW(ptr, dllname); /* Name of the Driver-DLL */
800 ptr += (dllsize / sizeof(WCHAR));
804 index++;
805 len = ARRAY_SIZE(buffer);
806 buffer[0] = '\0';
808 RegCloseKey(hroot);
810 *lpreturned = numentries;
811 TRACE("need %d byte for %d entries\n", needed, numentries);
812 return needed;
815 /*****************************************************************************
816 * enumerate the local print processors (INTERNAL)
818 * returns the needed size (in bytes) for pPPInfo
819 * and *lpreturned is set to number of entries returned in pPPInfo
822 static DWORD get_local_printprocessors(LPWSTR regpathW, LPBYTE pPPInfo, DWORD cbBuf, LPDWORD lpreturned)
824 HKEY hroot = NULL;
825 HKEY hentry = NULL;
826 LPWSTR ptr;
827 PPRINTPROCESSOR_INFO_1W ppi;
828 WCHAR buffer[MAX_PATH];
829 WCHAR dllname[MAX_PATH];
830 DWORD dllsize;
831 DWORD len;
832 DWORD index = 0;
833 DWORD needed = 0;
834 DWORD numentries;
836 numentries = *lpreturned; /* this is 0, when we scan the registry */
837 len = numentries * sizeof(PRINTPROCESSOR_INFO_1W);
838 ptr = (LPWSTR) &pPPInfo[len];
840 numentries = 0;
841 len = ARRAY_SIZE(buffer);
842 buffer[0] = '\0';
844 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, regpathW, &hroot) == ERROR_SUCCESS) {
845 /* add "winprint" first */
846 numentries++;
847 needed = sizeof(PRINTPROCESSOR_INFO_1W) + sizeof(winprintW);
848 if (pPPInfo && (cbBuf >= needed)){
849 ppi = (PPRINTPROCESSOR_INFO_1W) pPPInfo;
850 pPPInfo += sizeof(PRINTPROCESSOR_INFO_1W);
852 TRACE("%p: writing PRINTPROCESSOR_INFO_1W #%d\n", ppi, numentries);
853 ppi->pName = ptr;
854 lstrcpyW(ptr, winprintW); /* Name of the Print Processor */
855 ptr += ARRAY_SIZE(winprintW);
858 /* Scan all Printprocessor Keys */
859 while ((RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) &&
860 (lstrcmpiW(buffer, winprintW) != 0)) {
861 TRACE("PrintProcessor_%d: %s\n", numentries, debugstr_w(buffer));
862 dllsize = sizeof(dllname);
863 dllname[0] = '\0';
865 /* The Print Processor must have a Driver-DLL */
866 if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
867 if (RegQueryValueExW(hentry, driverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
868 /* We found a valid DLL for this Print Processor */
869 TRACE("using Driver: %s\n", debugstr_w(dllname));
871 RegCloseKey(hentry);
874 if (dllname[0]) {
875 numentries++;
876 needed += sizeof(PRINTPROCESSOR_INFO_1W);
877 needed += (len+1) * sizeof(WCHAR); /* len is lstrlenW(printprocessor name) */
879 /* required size is calculated. Now fill the user-buffer */
880 if (pPPInfo && (cbBuf >= needed)){
881 ppi = (PPRINTPROCESSOR_INFO_1W) pPPInfo;
882 pPPInfo += sizeof(PRINTPROCESSOR_INFO_1W);
884 TRACE("%p: writing PRINTPROCESSOR_INFO_1W #%d\n", ppi, numentries);
885 ppi->pName = ptr;
886 lstrcpyW(ptr, buffer); /* Name of the Print Processor */
887 ptr += (len+1); /* len is lstrlenW(printprosessor name) */
890 index++;
891 len = ARRAY_SIZE(buffer);
892 buffer[0] = '\0';
894 RegCloseKey(hroot);
896 *lpreturned = numentries;
897 TRACE("need %d byte for %d entries\n", needed, numentries);
898 return needed;
901 /******************************************************************
902 * enumerate the local Ports from all loaded monitors (internal)
904 * returns the needed size (in bytes) for pPorts
905 * and *lpreturned is set to number of entries returned in pPorts
908 static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
910 monitor_t * pm;
911 LPWSTR ptr;
912 LPPORT_INFO_2W cache;
913 LPPORT_INFO_2W out;
914 LPBYTE pi_buffer = NULL;
915 DWORD pi_allocated = 0;
916 DWORD pi_needed;
917 DWORD pi_index;
918 DWORD pi_returned;
919 DWORD res;
920 DWORD outindex = 0;
921 DWORD needed;
922 DWORD numentries;
923 DWORD entrysize;
926 TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
927 entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
929 numentries = *lpreturned; /* this is 0, when we scan the registry */
930 needed = entrysize * numentries;
931 ptr = (LPWSTR) &pPorts[needed];
933 numentries = 0;
934 needed = 0;
936 LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
938 if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
939 pi_needed = 0;
940 pi_returned = 0;
941 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
942 if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
943 /* Do not use heap_realloc (we do not need the old data in the buffer) */
944 heap_free(pi_buffer);
945 pi_buffer = heap_alloc(pi_needed);
946 pi_allocated = (pi_buffer) ? pi_needed : 0;
947 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
949 TRACE("(%s) got %d with %d (need %d byte for %d entries)\n",
950 debugstr_w(pm->name), res, GetLastError(), pi_needed, pi_returned);
952 numentries += pi_returned;
953 needed += pi_needed;
955 /* fill the output-buffer (pPorts), if we have one */
956 if (pPorts && (cbBuf >= needed ) && pi_buffer) {
957 pi_index = 0;
958 while (pi_returned > pi_index) {
959 cache = (LPPORT_INFO_2W) &pi_buffer[pi_index * entrysize];
960 out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
961 out->pPortName = ptr;
962 lstrcpyW(ptr, cache->pPortName);
963 ptr += (lstrlenW(ptr)+1);
964 if (level > 1) {
965 out->pMonitorName = ptr;
966 lstrcpyW(ptr, cache->pMonitorName);
967 ptr += (lstrlenW(ptr)+1);
969 out->pDescription = ptr;
970 lstrcpyW(ptr, cache->pDescription);
971 ptr += (lstrlenW(ptr)+1);
972 out->fPortType = cache->fPortType;
973 out->Reserved = cache->Reserved;
975 pi_index++;
976 outindex++;
981 /* the temporary portinfo-buffer is no longer needed */
982 heap_free(pi_buffer);
984 *lpreturned = numentries;
985 TRACE("need %d byte for %d entries\n", needed, numentries);
986 return needed;
990 /*****************************************************************************
991 * open_driver_reg [internal]
993 * opens the registry for the printer drivers depending on the given input
994 * variable pEnvironment
996 * RETURNS:
997 * Success: the opened hkey
998 * Failure: NULL
1000 static HKEY open_driver_reg(LPCWSTR pEnvironment)
1002 HKEY retval = NULL;
1003 LPWSTR buffer;
1004 const printenv_t * env;
1006 TRACE("(%s)\n", debugstr_w(pEnvironment));
1008 env = validate_envW(pEnvironment);
1009 if (!env) return NULL;
1011 buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(fmt_driversW) +
1012 (lstrlenW(env->envname) + lstrlenW(env->versionregpath)) * sizeof(WCHAR));
1014 if (buffer) {
1015 wsprintfW(buffer, fmt_driversW, env->envname, env->versionregpath);
1016 RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
1017 HeapFree(GetProcessHeap(), 0, buffer);
1019 return retval;
1022 /*****************************************************************************
1023 * fpGetPrinterDriverDirectory [exported through PRINTPROVIDOR]
1025 * Return the PATH for the Printer-Drivers
1027 * PARAMS
1028 * pName [I] Servername (NT only) or NULL (local Computer)
1029 * pEnvironment [I] Printing-Environment (see below) or NULL (Default)
1030 * Level [I] Structure-Level (must be 1)
1031 * pDriverDirectory [O] PTR to Buffer that receives the Result
1032 * cbBuf [I] Size of Buffer at pDriverDirectory
1033 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
1034 * required for pDriverDirectory
1036 * RETURNS
1037 * Success: TRUE and in pcbNeeded the Bytes used in pDriverDirectory
1038 * Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
1039 * if cbBuf is too small
1041 * Native Values returned in pDriverDirectory on Success:
1042 *| NT(Windows NT x86): "%winsysdir%\\spool\\DRIVERS\\w32x86"
1043 *| NT(Windows 4.0): "%winsysdir%\\spool\\DRIVERS\\win40"
1044 *| win9x(Windows 4.0): "%winsysdir%"
1046 * "%winsysdir%" is the Value from GetSystemDirectoryW()
1049 static BOOL WINAPI fpGetPrinterDriverDirectory(LPWSTR pName, LPWSTR pEnvironment,
1050 DWORD Level, LPBYTE pDriverDirectory, DWORD cbBuf, LPDWORD pcbNeeded)
1052 DWORD needed;
1053 const printenv_t * env;
1054 WCHAR * const dir = (WCHAR *)pDriverDirectory;
1056 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName),
1057 debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
1059 if (pName != NULL && pName[0]) {
1060 FIXME("server %s not supported\n", debugstr_w(pName));
1061 SetLastError(ERROR_INVALID_PARAMETER);
1062 return FALSE;
1065 env = validate_envW(pEnvironment);
1066 if (!env) return FALSE; /* pEnvironment invalid or unsupported */
1069 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
1070 needed = GetSystemDirectoryW(NULL, 0);
1071 /* add the Size for the Subdirectories */
1072 needed += lstrlenW(spoolW);
1073 needed += lstrlenW(driversW);
1074 needed += lstrlenW(env->subdir);
1075 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
1077 *pcbNeeded = needed;
1079 if (needed > cbBuf) {
1080 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1081 return FALSE;
1084 if (dir == NULL) {
1085 /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
1086 SetLastError(ERROR_INVALID_USER_BUFFER);
1087 return FALSE;
1090 GetSystemDirectoryW( dir, cbBuf / sizeof(WCHAR) );
1091 /* add the Subdirectories */
1092 lstrcatW( dir, spoolW );
1093 CreateDirectoryW( dir, NULL );
1094 lstrcatW( dir, driversW );
1095 CreateDirectoryW( dir, NULL );
1096 lstrcatW( dir, env->subdir );
1097 CreateDirectoryW( dir, NULL );
1099 TRACE( "=> %s\n", debugstr_w( dir ) );
1100 return TRUE;
1103 /******************************************************************
1104 * driver_load [internal]
1106 * load a driver user interface dll
1108 * On failure, NULL is returned
1112 static HMODULE driver_load(const printenv_t * env, LPWSTR dllname)
1114 WCHAR fullname[MAX_PATH];
1115 HMODULE hui;
1116 DWORD len;
1118 TRACE("(%p, %s)\n", env, debugstr_w(dllname));
1120 /* build the driverdir */
1121 len = sizeof(fullname) -
1122 (lstrlenW(env->versionsubdir) + 1 + lstrlenW(dllname) + 1) * sizeof(WCHAR);
1124 if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
1125 (LPBYTE) fullname, len, &len)) {
1126 /* Should never fail */
1127 SetLastError(ERROR_BUFFER_OVERFLOW);
1128 return NULL;
1131 lstrcatW(fullname, env->versionsubdir);
1132 lstrcatW(fullname, backslashW);
1133 lstrcatW(fullname, dllname);
1135 hui = LoadLibraryW(fullname);
1136 TRACE("%p: LoadLibrary(%s) %d\n", hui, debugstr_w(fullname), GetLastError());
1138 return hui;
1141 /******************************************************************
1142 * printer_free
1143 * free the data pointer of an opened printer
1145 static VOID printer_free(printer_t * printer)
1147 if (printer->hXcv)
1148 printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
1150 monitor_unload(printer->pm);
1152 heap_free(printer->printername);
1153 heap_free(printer->name);
1154 heap_free(printer);
1157 /******************************************************************
1158 * printer_alloc_handle
1159 * alloc a printer handle and remember the data pointer in the printer handle table
1162 static HANDLE printer_alloc_handle(LPCWSTR name, LPPRINTER_DEFAULTSW pDefault)
1164 WCHAR servername[MAX_COMPUTERNAME_LENGTH + 1];
1165 printer_t *printer = NULL;
1166 LPCWSTR printername;
1167 HKEY hkeyPrinters;
1168 HKEY hkeyPrinter;
1169 DWORD len;
1171 if (copy_servername_from_name(name, servername)) {
1172 FIXME("server %s not supported\n", debugstr_w(servername));
1173 SetLastError(ERROR_INVALID_PRINTER_NAME);
1174 return NULL;
1177 printername = get_basename_from_name(name);
1178 if (name != printername) TRACE("converted %s to %s\n", debugstr_w(name), debugstr_w(printername));
1180 /* an empty printername is invalid */
1181 if (printername && (!printername[0])) {
1182 SetLastError(ERROR_INVALID_PARAMETER);
1183 return NULL;
1186 printer = heap_alloc_zero(sizeof(printer_t));
1187 if (!printer) goto end;
1189 /* clone the base name. This is NULL for the printserver */
1190 printer->printername = strdupW(printername);
1192 /* clone the full name */
1193 printer->name = strdupW(name);
1194 if (name && (!printer->name)) {
1195 printer_free(printer);
1196 printer = NULL;
1198 if (printername) {
1199 len = ARRAY_SIZE(XcvMonitorW) - 1;
1200 if (strncmpW(printername, XcvMonitorW, len) == 0) {
1201 /* OpenPrinter(",XcvMonitor ", ...) detected */
1202 TRACE(",XcvMonitor: %s\n", debugstr_w(&printername[len]));
1203 printer->pm = monitor_load(&printername[len], NULL);
1204 if (printer->pm == NULL) {
1205 printer_free(printer);
1206 SetLastError(ERROR_UNKNOWN_PORT);
1207 printer = NULL;
1208 goto end;
1211 else
1213 len = ARRAY_SIZE(XcvPortW) - 1;
1214 if (strncmpW( printername, XcvPortW, len) == 0) {
1215 /* OpenPrinter(",XcvPort ", ...) detected */
1216 TRACE(",XcvPort: %s\n", debugstr_w(&printername[len]));
1217 printer->pm = monitor_load_by_port(&printername[len]);
1218 if (printer->pm == NULL) {
1219 printer_free(printer);
1220 SetLastError(ERROR_UNKNOWN_PORT);
1221 printer = NULL;
1222 goto end;
1227 if (printer->pm) {
1228 if ((printer->pm->monitor) && (printer->pm->monitor->pfnXcvOpenPort)) {
1229 printer->pm->monitor->pfnXcvOpenPort(&printername[len],
1230 pDefault ? pDefault->DesiredAccess : 0,
1231 &printer->hXcv);
1233 if (printer->hXcv == NULL) {
1234 printer_free(printer);
1235 SetLastError(ERROR_INVALID_PARAMETER);
1236 printer = NULL;
1237 goto end;
1240 else
1242 /* Does the Printer exist? */
1243 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, printersW, &hkeyPrinters) != ERROR_SUCCESS) {
1244 ERR("Can't create Printers key\n");
1245 printer_free(printer);
1246 SetLastError(ERROR_INVALID_PRINTER_NAME);
1247 printer = NULL;
1248 goto end;
1250 if (RegOpenKeyW(hkeyPrinters, printername, &hkeyPrinter) != ERROR_SUCCESS) {
1251 WARN("Printer not found in Registry: %s\n", debugstr_w(printername));
1252 RegCloseKey(hkeyPrinters);
1253 printer_free(printer);
1254 SetLastError(ERROR_INVALID_PRINTER_NAME);
1255 printer = NULL;
1256 goto end;
1258 RegCloseKey(hkeyPrinter);
1259 RegCloseKey(hkeyPrinters);
1262 else
1264 TRACE("using the local printserver\n");
1267 end:
1269 TRACE("==> %p\n", printer);
1270 return (HANDLE)printer;
1273 static inline WCHAR *get_file_part( WCHAR *name )
1275 WCHAR *ptr = strrchrW( name, '\\' );
1276 if (ptr) return ptr + 1;
1277 return name;
1280 /******************************************************************************
1281 * myAddPrinterDriverEx [internal]
1283 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1284 * and a special mode with lazy error checking.
1287 static BOOL myAddPrinterDriverEx(DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags, BOOL lazy)
1289 const printenv_t *env;
1290 apd_data_t apd;
1291 DRIVER_INFO_8W di;
1292 BOOL (WINAPI *pDrvDriverEvent)(DWORD, DWORD, LPBYTE, LPARAM);
1293 HMODULE hui;
1294 WCHAR *file;
1295 HKEY hroot;
1296 HKEY hdrv;
1297 DWORD disposition;
1298 DWORD len;
1299 LONG lres;
1300 BOOL res;
1302 /* we need to set all entries in the Registry, independent from the Level of
1303 DRIVER_INFO, that the caller supplied */
1305 ZeroMemory(&di, sizeof(di));
1306 if (pDriverInfo && (level < ARRAY_SIZE(di_sizeof))) {
1307 memcpy(&di, pDriverInfo, di_sizeof[level]);
1310 /* dump the most used infos */
1311 TRACE("%p: .cVersion : 0x%x/%d\n", pDriverInfo, di.cVersion, di.cVersion);
1312 TRACE("%p: .pName : %s\n", di.pName, debugstr_w(di.pName));
1313 TRACE("%p: .pEnvironment: %s\n", di.pEnvironment, debugstr_w(di.pEnvironment));
1314 TRACE("%p: .pDriverPath : %s\n", di.pDriverPath, debugstr_w(di.pDriverPath));
1315 TRACE("%p: .pDataFile : %s\n", di.pDataFile, debugstr_w(di.pDataFile));
1316 TRACE("%p: .pConfigFile : %s\n", di.pConfigFile, debugstr_w(di.pConfigFile));
1317 TRACE("%p: .pHelpFile : %s\n", di.pHelpFile, debugstr_w(di.pHelpFile));
1318 /* dump only the first of the additional Files */
1319 TRACE("%p: .pDependentFiles: %s\n", di.pDependentFiles, debugstr_w(di.pDependentFiles));
1322 /* check environment */
1323 env = validate_envW(di.pEnvironment);
1324 if (env == NULL) return FALSE; /* ERROR_INVALID_ENVIRONMENT */
1326 /* fill the copy-data / get the driverdir */
1327 len = sizeof(apd.src) - sizeof(version3_subdirW) - sizeof(WCHAR);
1328 if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
1329 (LPBYTE) apd.src, len, &len)) {
1330 /* Should never fail */
1331 return FALSE;
1333 memcpy(apd.dst, apd.src, len);
1334 lstrcatW(apd.src, backslashW);
1335 apd.srclen = lstrlenW(apd.src);
1336 lstrcatW(apd.dst, env->versionsubdir);
1337 lstrcatW(apd.dst, backslashW);
1338 apd.dstlen = lstrlenW(apd.dst);
1339 apd.copyflags = dwFileCopyFlags;
1340 apd.lazy = lazy;
1341 CreateDirectoryW(apd.src, NULL);
1342 CreateDirectoryW(apd.dst, NULL);
1344 hroot = open_driver_reg(env->envname);
1345 if (!hroot) {
1346 ERR("Can't create Drivers key\n");
1347 return FALSE;
1350 /* Fill the Registry for the Driver */
1351 if ((lres = RegCreateKeyExW(hroot, di.pName, 0, NULL, REG_OPTION_NON_VOLATILE,
1352 KEY_WRITE | KEY_QUERY_VALUE, NULL,
1353 &hdrv, &disposition)) != ERROR_SUCCESS) {
1355 ERR("can't create driver %s: %u\n", debugstr_w(di.pName), lres);
1356 RegCloseKey(hroot);
1357 SetLastError(lres);
1358 return FALSE;
1360 RegCloseKey(hroot);
1362 /* Verified with the Adobe PS Driver, that w2k does not use di.Version */
1363 RegSetValueExW(hdrv, versionW, 0, REG_DWORD, (const BYTE*) &env->driverversion,
1364 sizeof(DWORD));
1366 file = get_file_part( di.pDriverPath );
1367 RegSetValueExW( hdrv, driverW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
1368 apd_copyfile( di.pDriverPath, file, &apd );
1370 file = get_file_part( di.pDataFile );
1371 RegSetValueExW( hdrv, data_fileW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
1372 apd_copyfile( di.pDataFile, file, &apd );
1374 file = get_file_part( di.pConfigFile );
1375 RegSetValueExW( hdrv, configuration_fileW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
1376 apd_copyfile( di.pConfigFile, file, &apd );
1378 /* settings for level 3 */
1379 if (di.pHelpFile)
1381 file = get_file_part( di.pHelpFile );
1382 RegSetValueExW( hdrv, help_fileW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
1383 apd_copyfile( di.pHelpFile, file, &apd );
1385 else
1386 RegSetValueExW( hdrv, help_fileW, 0, REG_SZ, (const BYTE*)emptyW, sizeof(emptyW) );
1388 if (di.pDependentFiles && *di.pDependentFiles)
1390 WCHAR *reg, *reg_ptr, *in_ptr;
1391 reg = reg_ptr = HeapAlloc( GetProcessHeap(), 0, multi_sz_lenW( di.pDependentFiles ) );
1393 for (in_ptr = di.pDependentFiles; *in_ptr; in_ptr += strlenW( in_ptr ) + 1)
1395 file = get_file_part( in_ptr );
1396 len = strlenW( file ) + 1;
1397 memcpy( reg_ptr, file, len * sizeof(WCHAR) );
1398 reg_ptr += len;
1399 apd_copyfile( in_ptr, file, &apd );
1401 *reg_ptr = 0;
1403 RegSetValueExW( hdrv, dependent_filesW, 0, REG_MULTI_SZ, (LPBYTE)reg, (reg_ptr - reg + 1) * sizeof(WCHAR) );
1404 HeapFree( GetProcessHeap(), 0, reg );
1406 else
1407 RegSetValueExW(hdrv, dependent_filesW, 0, REG_MULTI_SZ, (const BYTE*)emptyW, sizeof(emptyW));
1409 /* The language-Monitor was already copied by the caller to "%SystemRoot%\system32" */
1410 if (di.pMonitorName)
1411 RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (LPBYTE) di.pMonitorName,
1412 (lstrlenW(di.pMonitorName)+1)* sizeof(WCHAR));
1413 else
1414 RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (const BYTE*)emptyW, sizeof(emptyW));
1416 if (di.pDefaultDataType)
1417 RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (LPBYTE) di.pDefaultDataType,
1418 (lstrlenW(di.pDefaultDataType)+1)* sizeof(WCHAR));
1419 else
1420 RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (const BYTE*)emptyW, sizeof(emptyW));
1422 /* settings for level 4 */
1423 if (di.pszzPreviousNames)
1424 RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (LPBYTE) di.pszzPreviousNames,
1425 multi_sz_lenW(di.pszzPreviousNames));
1426 else
1427 RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (const BYTE*)emptyW, sizeof(emptyW));
1429 if (level > 5) TRACE("level %u for Driver %s is incomplete\n", level, debugstr_w(di.pName));
1431 RegCloseKey(hdrv);
1432 hui = driver_load(env, di.pConfigFile);
1433 pDrvDriverEvent = (void *)GetProcAddress(hui, "DrvDriverEvent");
1434 if (hui && pDrvDriverEvent) {
1436 /* Support for DrvDriverEvent is optional */
1437 TRACE("DRIVER_EVENT_INITIALIZE for %s (%s)\n", debugstr_w(di.pName), debugstr_w(di.pConfigFile));
1438 /* MSDN: level for DRIVER_INFO is 1 to 3 */
1439 res = pDrvDriverEvent(DRIVER_EVENT_INITIALIZE, 3, (LPBYTE) &di, 0);
1440 TRACE("got %d from DRIVER_EVENT_INITIALIZE\n", res);
1442 FreeLibrary(hui);
1444 TRACE("=> TRUE with %u\n", GetLastError());
1445 return TRUE;
1449 /******************************************************************************
1450 * fpAddMonitor [exported through PRINTPROVIDOR]
1452 * Install a Printmonitor
1454 * PARAMS
1455 * pName [I] Servername or NULL (local Computer)
1456 * Level [I] Structure-Level (Must be 2)
1457 * pMonitors [I] PTR to MONITOR_INFO_2
1459 * RETURNS
1460 * Success: TRUE
1461 * Failure: FALSE
1463 * NOTES
1464 * All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1467 static BOOL WINAPI fpAddMonitor(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1469 const printenv_t * env;
1470 monitor_t * pm = NULL;
1471 LPMONITOR_INFO_2W mi2w;
1472 HKEY hroot = NULL;
1473 HKEY hentry = NULL;
1474 DWORD disposition;
1475 BOOL res = FALSE;
1477 mi2w = (LPMONITOR_INFO_2W) pMonitors;
1478 TRACE("(%s, %d, %p): %s %s %s\n", debugstr_w(pName), Level, pMonitors,
1479 debugstr_w(mi2w ? mi2w->pName : NULL),
1480 debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
1481 debugstr_w(mi2w ? mi2w->pDLLName : NULL));
1483 if (copy_servername_from_name(pName, NULL)) {
1484 FIXME("server %s not supported\n", debugstr_w(pName));
1485 SetLastError(ERROR_ACCESS_DENIED);
1486 return FALSE;
1489 if (!mi2w->pName || (! mi2w->pName[0])) {
1490 WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
1491 SetLastError(ERROR_INVALID_PARAMETER);
1492 return FALSE;
1495 env = validate_envW(mi2w->pEnvironment);
1496 if (!env)
1497 return FALSE; /* ERROR_INVALID_ENVIRONMENT */
1499 if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
1500 WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
1501 SetLastError(ERROR_INVALID_PARAMETER);
1502 return FALSE;
1505 /* Load and initialize the monitor. SetLastError() is called on failure */
1506 if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
1507 return FALSE;
1509 monitor_unload(pm);
1511 SetLastError(ERROR_SUCCESS); /* Monitor installer depends on this */
1513 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1514 ERR("unable to create key %s\n", debugstr_w(monitorsW));
1515 return FALSE;
1518 if (RegCreateKeyExW(hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
1519 KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
1520 &disposition) == ERROR_SUCCESS) {
1522 /* Some installers set options for the port before calling AddMonitor.
1523 We query the "Driver" entry to verify that the monitor is installed,
1524 before we return an error.
1525 When a user installs two print monitors at the same time with the
1526 same name, a race condition is possible but silently ignored. */
1528 DWORD namesize = 0;
1530 if ((disposition == REG_OPENED_EXISTING_KEY) &&
1531 (RegQueryValueExW(hentry, driverW, NULL, NULL, NULL,
1532 &namesize) == ERROR_SUCCESS)) {
1533 TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
1534 /* 9x use ERROR_ALREADY_EXISTS */
1535 SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
1537 else
1539 INT len;
1540 len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
1541 res = (RegSetValueExW(hentry, driverW, 0, REG_SZ,
1542 (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
1544 RegCloseKey(hentry);
1547 RegCloseKey(hroot);
1548 return (res);
1551 /******************************************************************************
1552 * fpAddPort [exported through PRINTPROVIDOR]
1554 * Add a Port for a specific Monitor
1556 * PARAMS
1557 * pName [I] Servername or NULL (local Computer)
1558 * hWnd [I] Handle to parent Window for the Dialog-Box
1559 * pMonitorName [I] Name of the Monitor that manage the Port
1561 * RETURNS
1562 * Success: TRUE
1563 * Failure: FALSE
1566 static BOOL WINAPI fpAddPort(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
1568 monitor_t * pm;
1569 monitor_t * pui;
1570 LONG lres;
1571 DWORD res;
1573 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
1575 lres = copy_servername_from_name(pName, NULL);
1576 if (lres) {
1577 FIXME("server %s not supported\n", debugstr_w(pName));
1578 SetLastError(ERROR_INVALID_PARAMETER);
1579 return FALSE;
1582 /* an empty Monitorname is Invalid */
1583 if (!pMonitorName[0]) {
1584 SetLastError(ERROR_NOT_SUPPORTED);
1585 return FALSE;
1588 pm = monitor_load(pMonitorName, NULL);
1589 if (pm && pm->monitor && pm->monitor->pfnAddPort) {
1590 res = pm->monitor->pfnAddPort(pName, hWnd, pMonitorName);
1591 TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pm->dllname));
1593 else
1595 pui = monitor_loadui(pm);
1596 if (pui && pui->monitorUI && pui->monitorUI->pfnAddPortUI) {
1597 res = pui->monitorUI->pfnAddPortUI(pName, hWnd, pMonitorName, NULL);
1598 TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pui->dllname));
1600 else
1602 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1603 debugstr_w(pMonitorName), pm, debugstr_w(pm ? pm->dllname : NULL),
1604 pui, debugstr_w(pui ? pui->dllname : NULL));
1606 SetLastError(ERROR_NOT_SUPPORTED);
1607 res = FALSE;
1609 monitor_unload(pui);
1611 monitor_unload(pm);
1613 TRACE("returning %d with %u\n", res, GetLastError());
1614 return res;
1617 /******************************************************************************
1618 * fpAddPortEx [exported through PRINTPROVIDOR]
1620 * Add a Port for a specific Monitor, without presenting a user interface
1622 * PARAMS
1623 * pName [I] Servername or NULL (local Computer)
1624 * level [I] Structure-Level (1 or 2) for pBuffer
1625 * pBuffer [I] PTR to: PORT_INFO_1 or PORT_INFO_2
1626 * pMonitorName [I] Name of the Monitor that manage the Port
1628 * RETURNS
1629 * Success: TRUE
1630 * Failure: FALSE
1633 static BOOL WINAPI fpAddPortEx(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
1635 PORT_INFO_2W * pi2;
1636 monitor_t * pm;
1637 DWORD lres;
1638 DWORD res;
1640 pi2 = (PORT_INFO_2W *) pBuffer;
1642 TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
1643 debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
1644 debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
1645 debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
1647 lres = copy_servername_from_name(pName, NULL);
1648 if (lres) {
1649 FIXME("server %s not supported\n", debugstr_w(pName));
1650 SetLastError(ERROR_INVALID_PARAMETER);
1651 return FALSE;
1654 if ((level < 1) || (level > 2)) {
1655 SetLastError(ERROR_INVALID_LEVEL);
1656 return FALSE;
1659 if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
1660 SetLastError(ERROR_INVALID_PARAMETER);
1661 return FALSE;
1664 /* load the Monitor */
1665 pm = monitor_load(pMonitorName, NULL);
1666 if (pm && pm->monitor && pm->monitor->pfnAddPortEx) {
1667 res = pm->monitor->pfnAddPortEx(pName, level, pBuffer, pMonitorName);
1668 TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pm->dllname));
1670 else
1672 FIXME("not implemented for %s (monitor %p: %s)\n",
1673 debugstr_w(pMonitorName), pm, pm ? debugstr_w(pm->dllname) : "(null)");
1674 SetLastError(ERROR_INVALID_PARAMETER);
1675 res = FALSE;
1677 monitor_unload(pm);
1678 return res;
1681 /******************************************************************************
1682 * fpAddPrinterDriverEx [exported through PRINTPROVIDOR]
1684 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1686 * PARAMS
1687 * pName [I] Servername or NULL (local Computer)
1688 * level [I] Level for the supplied DRIVER_INFO_*W struct
1689 * pDriverInfo [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
1690 * dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
1692 * RESULTS
1693 * Success: TRUE
1694 * Failure: FALSE
1697 static BOOL WINAPI fpAddPrinterDriverEx(LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
1699 LONG lres;
1701 TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
1702 lres = copy_servername_from_name(pName, NULL);
1703 if (lres) {
1704 FIXME("server %s not supported\n", debugstr_w(pName));
1705 SetLastError(ERROR_ACCESS_DENIED);
1706 return FALSE;
1709 if ((dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY) != APD_COPY_ALL_FILES) {
1710 TRACE("Flags 0x%x ignored (using APD_COPY_ALL_FILES)\n", dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY);
1713 return myAddPrinterDriverEx(level, pDriverInfo, dwFileCopyFlags, TRUE);
1716 /******************************************************************************
1717 * fpClosePrinter [exported through PRINTPROVIDOR]
1719 * Close a printer handle and free associated resources
1721 * PARAMS
1722 * hPrinter [I] Printerhandle to close
1724 * RESULTS
1725 * Success: TRUE
1726 * Failure: FALSE
1729 static BOOL WINAPI fpClosePrinter(HANDLE hPrinter)
1731 printer_t *printer = (printer_t *) hPrinter;
1733 TRACE("(%p)\n", hPrinter);
1735 if (printer) {
1736 printer_free(printer);
1737 return TRUE;
1739 return FALSE;
1742 /******************************************************************************
1743 * fpConfigurePort [exported through PRINTPROVIDOR]
1745 * Display the Configuration-Dialog for a specific Port
1747 * PARAMS
1748 * pName [I] Servername or NULL (local Computer)
1749 * hWnd [I] Handle to parent Window for the Dialog-Box
1750 * pPortName [I] Name of the Port, that should be configured
1752 * RETURNS
1753 * Success: TRUE
1754 * Failure: FALSE
1757 static BOOL WINAPI fpConfigurePort(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1759 monitor_t * pm;
1760 monitor_t * pui;
1761 LONG lres;
1762 DWORD res;
1764 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
1766 lres = copy_servername_from_name(pName, NULL);
1767 if (lres) {
1768 FIXME("server %s not supported\n", debugstr_w(pName));
1769 SetLastError(ERROR_INVALID_NAME);
1770 return FALSE;
1773 /* an empty Portname is Invalid, but can popup a Dialog */
1774 if (!pPortName[0]) {
1775 SetLastError(ERROR_NOT_SUPPORTED);
1776 return FALSE;
1779 pm = monitor_load_by_port(pPortName);
1780 if (pm && pm->monitor && pm->monitor->pfnConfigurePort) {
1781 TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm->name),
1782 debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
1783 res = pm->monitor->pfnConfigurePort(pName, hWnd, pPortName);
1784 TRACE("got %d with %u\n", res, GetLastError());
1786 else
1788 pui = monitor_loadui(pm);
1789 if (pui && pui->monitorUI && pui->monitorUI->pfnConfigurePortUI) {
1790 TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui->name),
1791 debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
1792 res = pui->monitorUI->pfnConfigurePortUI(pName, hWnd, pPortName);
1793 TRACE("got %d with %u\n", res, GetLastError());
1795 else
1797 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1798 debugstr_w(pPortName), pm, debugstr_w(pm ? pm->dllname : NULL),
1799 pui, debugstr_w(pui ? pui->dllname : NULL));
1801 SetLastError(ERROR_NOT_SUPPORTED);
1802 res = FALSE;
1804 monitor_unload(pui);
1806 monitor_unload(pm);
1808 TRACE("returning %d with %u\n", res, GetLastError());
1809 return res;
1812 /******************************************************************
1813 * fpDeleteMonitor [exported through PRINTPROVIDOR]
1815 * Delete a specific Printmonitor from a Printing-Environment
1817 * PARAMS
1818 * pName [I] Servername or NULL (local Computer)
1819 * pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1820 * pMonitorName [I] Name of the Monitor, that should be deleted
1822 * RETURNS
1823 * Success: TRUE
1824 * Failure: FALSE
1826 * NOTES
1827 * pEnvironment is ignored in Windows for the local Computer.
1831 static BOOL WINAPI fpDeleteMonitor(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1833 HKEY hroot = NULL;
1834 LONG lres;
1836 TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
1837 debugstr_w(pMonitorName));
1839 lres = copy_servername_from_name(pName, NULL);
1840 if (lres) {
1841 FIXME("server %s not supported\n", debugstr_w(pName));
1842 SetLastError(ERROR_INVALID_NAME);
1843 return FALSE;
1846 /* pEnvironment is ignored in Windows for the local Computer */
1847 if (!pMonitorName || !pMonitorName[0]) {
1848 TRACE("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
1849 SetLastError(ERROR_INVALID_PARAMETER);
1850 return FALSE;
1853 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1854 ERR("unable to create key %s\n", debugstr_w(monitorsW));
1855 return FALSE;
1858 if(RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS) {
1859 TRACE("%s deleted\n", debugstr_w(pMonitorName));
1860 RegCloseKey(hroot);
1861 return TRUE;
1864 TRACE("%s does not exist\n", debugstr_w(pMonitorName));
1865 RegCloseKey(hroot);
1867 /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
1868 SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
1869 return FALSE;
1872 /*****************************************************************************
1873 * fpDeletePort [exported through PRINTPROVIDOR]
1875 * Delete a specific Port
1877 * PARAMS
1878 * pName [I] Servername or NULL (local Computer)
1879 * hWnd [I] Handle to parent Window for the Dialog-Box
1880 * pPortName [I] Name of the Port, that should be deleted
1882 * RETURNS
1883 * Success: TRUE
1884 * Failure: FALSE
1887 static BOOL WINAPI fpDeletePort(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1889 monitor_t * pm;
1890 monitor_t * pui;
1891 LONG lres;
1892 DWORD res;
1894 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
1896 lres = copy_servername_from_name(pName, NULL);
1897 if (lres) {
1898 FIXME("server %s not supported\n", debugstr_w(pName));
1899 SetLastError(ERROR_INVALID_NAME);
1900 return FALSE;
1903 /* an empty Portname is Invalid */
1904 if (!pPortName[0]) {
1905 SetLastError(ERROR_NOT_SUPPORTED);
1906 return FALSE;
1909 pm = monitor_load_by_port(pPortName);
1910 if (pm && pm->monitor && pm->monitor->pfnDeletePort) {
1911 TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm->name),
1912 debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
1913 res = pm->monitor->pfnDeletePort(pName, hWnd, pPortName);
1914 TRACE("got %d with %u\n", res, GetLastError());
1916 else
1918 pui = monitor_loadui(pm);
1919 if (pui && pui->monitorUI && pui->monitorUI->pfnDeletePortUI) {
1920 TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui->name),
1921 debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
1922 res = pui->monitorUI->pfnDeletePortUI(pName, hWnd, pPortName);
1923 TRACE("got %d with %u\n", res, GetLastError());
1925 else
1927 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1928 debugstr_w(pPortName), pm, debugstr_w(pm ? pm->dllname : NULL),
1929 pui, debugstr_w(pui ? pui->dllname : NULL));
1931 SetLastError(ERROR_NOT_SUPPORTED);
1932 res = FALSE;
1934 monitor_unload(pui);
1936 monitor_unload(pm);
1938 TRACE("returning %d with %u\n", res, GetLastError());
1939 return res;
1942 /*****************************************************************************
1943 * fpEnumMonitors [exported through PRINTPROVIDOR]
1945 * Enumerate available Port-Monitors
1947 * PARAMS
1948 * pName [I] Servername or NULL (local Computer)
1949 * Level [I] Structure-Level (1:Win9x+NT or 2:NT only)
1950 * pMonitors [O] PTR to Buffer that receives the Result
1951 * cbBuf [I] Size of Buffer at pMonitors
1952 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
1953 * pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
1955 * RETURNS
1956 * Success: TRUE
1957 * Failure: FALSE and in pcbNeeded the Bytes required for pMonitors, if cbBuf is too small
1959 * NOTES
1960 * Windows reads the Registry once and cache the Results.
1963 static BOOL WINAPI fpEnumMonitors(LPWSTR pName, DWORD Level, LPBYTE pMonitors, DWORD cbBuf,
1964 LPDWORD pcbNeeded, LPDWORD pcReturned)
1966 DWORD numentries = 0;
1967 DWORD needed = 0;
1968 LONG lres;
1969 BOOL res = FALSE;
1971 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
1972 cbBuf, pcbNeeded, pcReturned);
1974 lres = copy_servername_from_name(pName, NULL);
1975 if (lres) {
1976 FIXME("server %s not supported\n", debugstr_w(pName));
1977 SetLastError(ERROR_INVALID_NAME);
1978 goto em_cleanup;
1981 if (!Level || (Level > 2)) {
1982 WARN("level (%d) is ignored in win9x\n", Level);
1983 SetLastError(ERROR_INVALID_LEVEL);
1984 return FALSE;
1987 /* Scan all Monitor-Keys */
1988 numentries = 0;
1989 needed = get_local_monitors(Level, NULL, 0, &numentries);
1991 /* we calculated the needed buffersize. now do more error-checks */
1992 if (cbBuf < needed) {
1993 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1994 goto em_cleanup;
1997 /* fill the Buffer with the Monitor-Keys */
1998 needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
1999 res = TRUE;
2001 em_cleanup:
2002 if (pcbNeeded) *pcbNeeded = needed;
2003 if (pcReturned) *pcReturned = numentries;
2005 TRACE("returning %d with %d (%d byte for %d entries)\n",
2006 res, GetLastError(), needed, numentries);
2008 return (res);
2011 /******************************************************************************
2012 * fpEnumPorts [exported through PRINTPROVIDOR]
2014 * Enumerate available Ports
2016 * PARAMS
2017 * pName [I] Servername or NULL (local Computer)
2018 * Level [I] Structure-Level (1 or 2)
2019 * pPorts [O] PTR to Buffer that receives the Result
2020 * cbBuf [I] Size of Buffer at pPorts
2021 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
2022 * pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
2024 * RETURNS
2025 * Success: TRUE
2026 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
2029 static BOOL WINAPI fpEnumPorts(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
2030 LPDWORD pcbNeeded, LPDWORD pcReturned)
2032 DWORD needed = 0;
2033 DWORD numentries = 0;
2034 LONG lres;
2035 BOOL res = FALSE;
2037 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
2038 cbBuf, pcbNeeded, pcReturned);
2040 lres = copy_servername_from_name(pName, NULL);
2041 if (lres) {
2042 FIXME("server %s not supported\n", debugstr_w(pName));
2043 SetLastError(ERROR_INVALID_NAME);
2044 goto emP_cleanup;
2047 if (!Level || (Level > 2)) {
2048 SetLastError(ERROR_INVALID_LEVEL);
2049 goto emP_cleanup;
2052 if (!pcbNeeded || (!pPorts && (cbBuf > 0))) {
2053 SetLastError(RPC_X_NULL_REF_POINTER);
2054 goto emP_cleanup;
2057 EnterCriticalSection(&monitor_handles_cs);
2058 monitor_loadall();
2060 /* Scan all local Ports */
2061 numentries = 0;
2062 needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
2064 /* we calculated the needed buffersize. now do the error-checks */
2065 if (cbBuf < needed) {
2066 monitor_unloadall();
2067 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2068 goto emP_cleanup_cs;
2070 else if (!pPorts || !pcReturned) {
2071 monitor_unloadall();
2072 SetLastError(RPC_X_NULL_REF_POINTER);
2073 goto emP_cleanup_cs;
2076 /* Fill the Buffer */
2077 needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
2078 res = TRUE;
2079 monitor_unloadall();
2081 emP_cleanup_cs:
2082 LeaveCriticalSection(&monitor_handles_cs);
2084 emP_cleanup:
2085 if (pcbNeeded) *pcbNeeded = needed;
2086 if (pcReturned) *pcReturned = (res) ? numentries : 0;
2088 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
2089 (res), GetLastError(), needed, (res) ? numentries : 0, numentries);
2091 return (res);
2094 /*****************************************************************************
2095 * fpEnumPrintProcessors [exported through PRINTPROVIDOR]
2097 * Enumerate available Print Processors
2099 * PARAMS
2100 * pName [I] Servername or NULL (local Computer)
2101 * pEnvironment [I] Printing-Environment or NULL (Default)
2102 * Level [I] Structure-Level (Only 1 is allowed)
2103 * pPPInfo [O] PTR to Buffer that receives the Result
2104 * cbBuf [I] Size of Buffer at pMonitors
2105 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPPInfo
2106 * pcReturned [O] PTR to DWORD that receives the number of Print Processors in pPPInfo
2108 * RETURNS
2109 * Success: TRUE
2110 * Failure: FALSE and in pcbNeeded the Bytes required for pPPInfo, if cbBuf is too small
2113 static BOOL WINAPI fpEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
2114 LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
2116 const printenv_t * env;
2117 LPWSTR regpathW = NULL;
2118 DWORD numentries = 0;
2119 DWORD needed = 0;
2120 LONG lres;
2121 BOOL res = FALSE;
2123 TRACE("(%s, %s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), debugstr_w(pEnvironment),
2124 Level, pPPInfo, cbBuf, pcbNeeded, pcReturned);
2126 lres = copy_servername_from_name(pName, NULL);
2127 if (lres) {
2128 FIXME("server %s not supported\n", debugstr_w(pName));
2129 SetLastError(ERROR_INVALID_NAME);
2130 goto epp_cleanup;
2133 if (Level != 1) {
2134 SetLastError(ERROR_INVALID_LEVEL);
2135 goto epp_cleanup;
2138 env = validate_envW(pEnvironment);
2139 if (!env)
2140 goto epp_cleanup; /* ERROR_INVALID_ENVIRONMENT */
2142 regpathW = heap_alloc(sizeof(fmt_printprocessorsW) +
2143 (lstrlenW(env->envname) * sizeof(WCHAR)));
2145 if (!regpathW)
2146 goto epp_cleanup;
2148 wsprintfW(regpathW, fmt_printprocessorsW, env->envname);
2150 /* Scan all Printprocessor-Keys */
2151 numentries = 0;
2152 needed = get_local_printprocessors(regpathW, NULL, 0, &numentries);
2154 /* we calculated the needed buffersize. now do more error-checks */
2155 if (cbBuf < needed) {
2156 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2157 goto epp_cleanup;
2160 /* fill the Buffer with the Printprocessor Infos */
2161 needed = get_local_printprocessors(regpathW, pPPInfo, cbBuf, &numentries);
2162 res = TRUE;
2164 epp_cleanup:
2165 heap_free(regpathW);
2166 if (pcbNeeded) *pcbNeeded = needed;
2167 if (pcReturned) *pcReturned = numentries;
2169 TRACE("returning %d with %d (%d byte for %d entries)\n",
2170 res, GetLastError(), needed, numentries);
2172 return (res);
2175 /******************************************************************************
2176 * fpGetPrintProcessorDirectory [exported through PRINTPROVIDOR]
2178 * Return the PATH for the Print-Processors
2180 * PARAMS
2181 * pName [I] Servername or NULL (this computer)
2182 * pEnvironment [I] Printing-Environment or NULL (Default)
2183 * level [I] Structure-Level (must be 1)
2184 * pPPInfo [O] PTR to Buffer that receives the Result
2185 * cbBuf [I] Size of Buffer at pPPInfo
2186 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPPInfo
2188 * RETURNS
2189 * Success: TRUE
2190 * Failure: FALSE and in pcbNeeded the Bytes required for pPPInfo, if cbBuf is too small
2192 * Native Values returned in pPPInfo on Success for this computer:
2193 *| NT(Windows x64): "%winsysdir%\\spool\\PRTPROCS\\x64"
2194 *| NT(Windows NT x86): "%winsysdir%\\spool\\PRTPROCS\\w32x86"
2195 *| NT(Windows 4.0): "%winsysdir%\\spool\\PRTPROCS\\win40"
2197 * "%winsysdir%" is the Value from GetSystemDirectoryW()
2200 static BOOL WINAPI fpGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD level,
2201 LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded)
2203 const printenv_t * env;
2204 DWORD needed;
2205 LONG lres;
2207 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), debugstr_w(pEnvironment),
2208 level, pPPInfo, cbBuf, pcbNeeded);
2210 *pcbNeeded = 0;
2211 lres = copy_servername_from_name(pName, NULL);
2212 if (lres) {
2213 FIXME("server %s not supported\n", debugstr_w(pName));
2214 SetLastError(RPC_S_SERVER_UNAVAILABLE);
2215 return FALSE;
2218 env = validate_envW(pEnvironment);
2219 if (!env)
2220 return FALSE; /* ERROR_INVALID_ENVIRONMENT */
2222 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2223 needed = GetSystemDirectoryW(NULL, 0);
2224 /* add the Size for the Subdirectories */
2225 needed += lstrlenW(spoolprtprocsW);
2226 needed += lstrlenW(env->subdir);
2227 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
2229 *pcbNeeded = needed;
2231 if (needed > cbBuf) {
2232 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2233 return FALSE;
2236 GetSystemDirectoryW((LPWSTR) pPPInfo, cbBuf/sizeof(WCHAR));
2237 /* add the Subdirectories */
2238 lstrcatW((LPWSTR) pPPInfo, spoolprtprocsW);
2239 lstrcatW((LPWSTR) pPPInfo, env->subdir);
2240 TRACE("==> %s\n", debugstr_w((LPWSTR) pPPInfo));
2241 return TRUE;
2244 /******************************************************************************
2245 * fpOpenPrinter [exported through PRINTPROVIDOR]
2247 * Open a Printer / Printserver or a Printer-Object
2249 * PARAMS
2250 * lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
2251 * pPrinter [O] The resulting Handle is stored here
2252 * pDefaults [I] PTR to Default Printer Settings or NULL
2254 * RETURNS
2255 * Success: TRUE
2256 * Failure: FALSE
2258 * NOTES
2259 * lpPrinterName is one of:
2260 *| Printserver (NT only): "Servername" or NULL for the local Printserver
2261 *| Printer: "PrinterName"
2262 *| Printer-Object: "PrinterName,Job xxx"
2263 *| XcvMonitor: "Servername,XcvMonitor MonitorName"
2264 *| XcvPort: "Servername,XcvPort PortName"
2268 static BOOL WINAPI fpOpenPrinter(LPWSTR lpPrinterName, HANDLE *pPrinter,
2269 LPPRINTER_DEFAULTSW pDefaults)
2272 TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), pPrinter, pDefaults);
2274 *pPrinter = printer_alloc_handle(lpPrinterName, pDefaults);
2276 return (*pPrinter != 0);
2279 /******************************************************************************
2280 * fpXcvData [exported through PRINTPROVIDOR]
2282 * Execute commands in the Printmonitor DLL
2284 * PARAMS
2285 * hXcv [i] Handle from fpOpenPrinter (with XcvMonitor or XcvPort)
2286 * pszDataName [i] Name of the command to execute
2287 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
2288 * cbInputData [i] Size in Bytes of Buffer at pInputData
2289 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
2290 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
2291 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
2292 * pdwStatus [o] PTR to receive the win32 error code from the Printmonitor DLL
2294 * RETURNS
2295 * Success: TRUE
2296 * Failure: FALSE
2298 * NOTES
2299 * Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
2300 * The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
2302 * Minimal List of commands, that a Printmonitor DLL should support:
2304 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
2305 *| "AddPort" : Add a Port
2306 *| "DeletePort": Delete a Port
2308 * Many Printmonitors support additional commands. Examples for localspl.dll:
2309 * "GetDefaultCommConfig", "SetDefaultCommConfig",
2310 * "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
2313 static BOOL WINAPI fpXcvData(HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
2314 DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
2315 PDWORD pcbOutputNeeded, PDWORD pdwStatus)
2317 printer_t *printer = (printer_t * ) hXcv;
2319 TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
2320 pInputData, cbInputData, pOutputData,
2321 cbOutputData, pcbOutputNeeded, pdwStatus);
2323 if (!printer || (!printer->hXcv)) {
2324 SetLastError(ERROR_INVALID_HANDLE);
2325 return FALSE;
2328 if (!pcbOutputNeeded) {
2329 SetLastError(ERROR_INVALID_PARAMETER);
2330 return FALSE;
2333 if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
2334 SetLastError(RPC_X_NULL_REF_POINTER);
2335 return FALSE;
2338 *pcbOutputNeeded = 0;
2340 *pdwStatus = printer->pm->monitor->pfnXcvDataPort(printer->hXcv, pszDataName,
2341 pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
2343 return TRUE;
2346 /*****************************************************
2347 * setup_provider [internal]
2349 void setup_provider(void)
2351 static const PRINTPROVIDOR backend = {
2352 fpOpenPrinter,
2353 NULL, /* fpSetJob */
2354 NULL, /* fpGetJob */
2355 NULL, /* fpEnumJobs */
2356 NULL, /* fpAddPrinter */
2357 NULL, /* fpDeletePrinter */
2358 NULL, /* fpSetPrinter */
2359 NULL, /* fpGetPrinter */
2360 NULL, /* fpEnumPrinters */
2361 NULL, /* fpAddPrinterDriver */
2362 NULL, /* fpEnumPrinterDrivers */
2363 NULL, /* fpGetPrinterDriver */
2364 fpGetPrinterDriverDirectory,
2365 NULL, /* fpDeletePrinterDriver */
2366 NULL, /* fpAddPrintProcessor */
2367 fpEnumPrintProcessors,
2368 fpGetPrintProcessorDirectory,
2369 NULL, /* fpDeletePrintProcessor */
2370 NULL, /* fpEnumPrintProcessorDatatypes */
2371 NULL, /* fpStartDocPrinter */
2372 NULL, /* fpStartPagePrinter */
2373 NULL, /* fpWritePrinter */
2374 NULL, /* fpEndPagePrinter */
2375 NULL, /* fpAbortPrinter */
2376 NULL, /* fpReadPrinter */
2377 NULL, /* fpEndDocPrinter */
2378 NULL, /* fpAddJob */
2379 NULL, /* fpScheduleJob */
2380 NULL, /* fpGetPrinterData */
2381 NULL, /* fpSetPrinterData */
2382 NULL, /* fpWaitForPrinterChange */
2383 fpClosePrinter,
2384 NULL, /* fpAddForm */
2385 NULL, /* fpDeleteForm */
2386 NULL, /* fpGetForm */
2387 NULL, /* fpSetForm */
2388 NULL, /* fpEnumForms */
2389 fpEnumMonitors,
2390 fpEnumPorts,
2391 fpAddPort,
2392 fpConfigurePort,
2393 fpDeletePort,
2394 NULL, /* fpCreatePrinterIC */
2395 NULL, /* fpPlayGdiScriptOnPrinterIC */
2396 NULL, /* fpDeletePrinterIC */
2397 NULL, /* fpAddPrinterConnection */
2398 NULL, /* fpDeletePrinterConnection */
2399 NULL, /* fpPrinterMessageBox */
2400 fpAddMonitor,
2401 fpDeleteMonitor,
2402 NULL, /* fpResetPrinter */
2403 NULL, /* fpGetPrinterDriverEx */
2404 NULL, /* fpFindFirstPrinterChangeNotification */
2405 NULL, /* fpFindClosePrinterChangeNotification */
2406 fpAddPortEx,
2407 NULL, /* fpShutDown */
2408 NULL, /* fpRefreshPrinterChangeNotification */
2409 NULL, /* fpOpenPrinterEx */
2410 NULL, /* fpAddPrinterEx */
2411 NULL, /* fpSetPort */
2412 NULL, /* fpEnumPrinterData */
2413 NULL, /* fpDeletePrinterData */
2414 NULL, /* fpClusterSplOpen */
2415 NULL, /* fpClusterSplClose */
2416 NULL, /* fpClusterSplIsAlive */
2417 NULL, /* fpSetPrinterDataEx */
2418 NULL, /* fpGetPrinterDataEx */
2419 NULL, /* fpEnumPrinterDataEx */
2420 NULL, /* fpEnumPrinterKey */
2421 NULL, /* fpDeletePrinterDataEx */
2422 NULL, /* fpDeletePrinterKey */
2423 NULL, /* fpSeekPrinter */
2424 NULL, /* fpDeletePrinterDriverEx */
2425 NULL, /* fpAddPerMachineConnection */
2426 NULL, /* fpDeletePerMachineConnection */
2427 NULL, /* fpEnumPerMachineConnections */
2428 fpXcvData,
2429 fpAddPrinterDriverEx,
2430 NULL, /* fpSplReadPrinter */
2431 NULL, /* fpDriverUnloadComplete */
2432 NULL, /* fpGetSpoolFileInfo */
2433 NULL, /* fpCommitSpoolData */
2434 NULL, /* fpCloseSpoolFileHandle */
2435 NULL, /* fpFlushPrinter */
2436 NULL, /* fpSendRecvBidiData */
2437 NULL /* fpAddDriverCatalog */
2439 pprovider = &backend;
2443 /*****************************************************
2444 * InitializePrintProvidor (localspl.@)
2446 * Initialize the Printprovider
2448 * PARAMS
2449 * pPrintProvidor [I] Buffer to fill with a struct PRINTPROVIDOR
2450 * cbPrintProvidor [I] Size of Buffer in Bytes
2451 * pFullRegistryPath [I] Registry-Path for the Printprovidor
2453 * RETURNS
2454 * Success: TRUE and pPrintProvidor filled
2455 * Failure: FALSE
2457 * NOTES
2458 * The RegistryPath should be:
2459 * "System\CurrentControlSet\Control\Print\Providers\<providername>",
2460 * but this Parameter is ignored in "localspl.dll".
2464 BOOL WINAPI InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor,
2465 DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
2468 TRACE("(%p, %u, %s)\n", pPrintProvidor, cbPrintProvidor, debugstr_w(pFullRegistryPath));
2469 memcpy(pPrintProvidor, pprovider,
2470 (cbPrintProvidor < sizeof(PRINTPROVIDOR)) ? cbPrintProvidor : sizeof(PRINTPROVIDOR));
2472 return TRUE;