msi/tests: Test for error control flag handling.
[wine.git] / dlls / localspl / provider.c
blobb5fcdb1e6a1717a16c14f2dd47d4aed171b50df0
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/list.h"
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
37 #include "localspl_private.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(localspl);
41 /* ############################### */
43 static CRITICAL_SECTION monitor_handles_cs;
44 static CRITICAL_SECTION_DEBUG monitor_handles_cs_debug =
46 0, 0, &monitor_handles_cs,
47 { &monitor_handles_cs_debug.ProcessLocksList, &monitor_handles_cs_debug.ProcessLocksList },
48 0, 0, { (DWORD_PTR)(__FILE__ ": monitor_handles_cs") }
50 static CRITICAL_SECTION monitor_handles_cs = { &monitor_handles_cs_debug, -1, 0, 0, 0, 0 };
52 /* ############################### */
54 typedef struct {
55 WCHAR src[MAX_PATH+MAX_PATH];
56 WCHAR dst[MAX_PATH+MAX_PATH];
57 DWORD srclen;
58 DWORD dstlen;
59 DWORD copyflags;
60 BOOL lazy;
61 } apd_data_t;
63 typedef struct {
64 struct list entry;
65 LPWSTR name;
66 LPWSTR dllname;
67 PMONITORUI monitorUI;
68 LPMONITOR monitor;
69 HMODULE hdll;
70 DWORD refcount;
71 DWORD dwMonitorSize;
72 } monitor_t;
74 typedef struct {
75 LPCWSTR envname;
76 LPCWSTR subdir;
77 DWORD driverversion;
78 LPCWSTR versionregpath;
79 LPCWSTR versionsubdir;
80 } printenv_t;
82 typedef struct {
83 LPWSTR name;
84 LPWSTR printername;
85 monitor_t * pm;
86 HANDLE hXcv;
87 } printer_t;
89 /* ############################### */
91 static struct list monitor_handles = LIST_INIT( monitor_handles );
92 static monitor_t * pm_localport;
94 static const PRINTPROVIDOR * pprovider = NULL;
96 static const WCHAR backslashW[] = {'\\',0};
97 static const WCHAR bs_ports_bsW[] = {'\\','P','o','r','t','s','\\',0};
98 static const WCHAR configuration_fileW[] = {'C','o','n','f','i','g','u','r','a','t','i','o','n',' ','F','i','l','e',0};
99 static const WCHAR datatypeW[] = {'D','a','t','a','t','y','p','e',0};
100 static const WCHAR data_fileW[] = {'D','a','t','a',' ','F','i','l','e',0};
101 static const WCHAR dependent_filesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
102 static const WCHAR driverW[] = {'D','r','i','v','e','r',0};
103 static const WCHAR emptyW[] = {0};
104 static const WCHAR fmt_driversW[] = { 'S','y','s','t','e','m','\\',
105 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
106 'c','o','n','t','r','o','l','\\',
107 'P','r','i','n','t','\\',
108 'E','n','v','i','r','o','n','m','e','n','t','s','\\',
109 '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
110 static const WCHAR fmt_printprocessorsW[] = { 'S','y','s','t','e','m','\\',
111 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
112 'C','o','n','t','r','o','l','\\',
113 'P','r','i','n','t','\\',
114 'E','n','v','i','r','o','n','m','e','n','t','s','\\','%','s','\\',
115 'P','r','i','n','t',' ','P','r','o','c','e','s','s','o','r','s',0 };
116 static const WCHAR help_fileW[] = {'H','e','l','p',' ','F','i','l','e',0};
117 static const WCHAR ia64_envnameW[] = {'W','i','n','d','o','w','s',' ','I','A','6','4',0};
118 static const WCHAR ia64_subdirW[] = {'i','a','6','4',0};
119 static const WCHAR localportW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
120 static const WCHAR monitorW[] = {'M','o','n','i','t','o','r',0};
121 static const WCHAR monitorsW[] = {'S','y','s','t','e','m','\\',
122 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
123 'C','o','n','t','r','o','l','\\',
124 'P','r','i','n','t','\\',
125 'M','o','n','i','t','o','r','s','\\',0};
126 static const WCHAR monitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
127 static const WCHAR previous_namesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
128 static const WCHAR printersW[] = {'S','y','s','t','e','m','\\',
129 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
130 'C','o','n','t','r','o','l','\\',
131 'P','r','i','n','t','\\',
132 'P','r','i','n','t','e','r','s',0};
133 static const WCHAR spoolW[] = {'\\','s','p','o','o','l',0};
134 static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0};
135 static const WCHAR spoolprtprocsW[] = {'\\','s','p','o','o','l','\\','p','r','t','p','r','o','c','s','\\',0};
136 static const WCHAR version0_regpathW[] = {'\\','V','e','r','s','i','o','n','-','0',0};
137 static const WCHAR version0_subdirW[] = {'\\','0',0};
138 static const WCHAR version3_regpathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
139 static const WCHAR version3_subdirW[] = {'\\','3',0};
140 static const WCHAR versionW[] = {'V','e','r','s','i','o','n',0};
141 static const WCHAR win40_envnameW[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
142 static const WCHAR win40_subdirW[] = {'w','i','n','4','0',0};
143 static const WCHAR winnt_cv_portsW[] = {'S','o','f','t','w','a','r','e','\\',
144 'M','i','c','r','o','s','o','f','t','\\',
145 'W','i','n','d','o','w','s',' ','N','T','\\',
146 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
147 'P','o','r','t','s',0};
148 static const WCHAR winprintW[] = {'w','i','n','p','r','i','n','t',0};
149 static const WCHAR x64_envnameW[] = {'W','i','n','d','o','w','s',' ','x','6','4',0};
150 static const WCHAR x64_subdirW[] = {'x','6','4',0};
151 static const WCHAR x86_envnameW[] = {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
152 static const WCHAR x86_subdirW[] = {'w','3','2','x','8','6',0};
153 static const WCHAR XcvMonitorW[] = {',','X','c','v','M','o','n','i','t','o','r',' ',0};
154 static const WCHAR XcvPortW[] = {',','X','c','v','P','o','r','t',' ',0};
157 static const printenv_t env_ia64 = {ia64_envnameW, ia64_subdirW, 3,
158 version3_regpathW, version3_subdirW};
160 static const printenv_t env_x86 = {x86_envnameW, x86_subdirW, 3,
161 version3_regpathW, version3_subdirW};
163 static const printenv_t env_x64 = {x64_envnameW, x64_subdirW, 3,
164 version3_regpathW, version3_subdirW};
166 static const printenv_t env_win40 = {win40_envnameW, win40_subdirW, 0,
167 version0_regpathW, version0_subdirW};
169 static const printenv_t * const all_printenv[] = {&env_x86, &env_x64, &env_ia64, &env_win40};
172 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
173 sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
174 sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
175 0, sizeof(DRIVER_INFO_8W)};
178 /******************************************************************
179 * strdupW [internal]
181 * create a copy of a unicode-string
184 static LPWSTR strdupW(LPCWSTR p)
186 LPWSTR ret;
187 DWORD len;
189 if(!p) return NULL;
190 len = (lstrlenW(p) + 1) * sizeof(WCHAR);
191 ret = heap_alloc(len);
192 if (ret) memcpy(ret, p, len);
193 return ret;
196 /******************************************************************
197 * apd_copyfile [internal]
199 * Copy a file from the driverdirectory to the versioned directory
201 * RETURNS
202 * Success: TRUE
203 * Failure: FALSE
206 static BOOL apd_copyfile( WCHAR *pathname, WCHAR *file_part, apd_data_t *apd )
208 WCHAR *srcname;
209 BOOL res;
211 apd->src[apd->srclen] = '\0';
212 apd->dst[apd->dstlen] = '\0';
214 if (!pathname || !pathname[0]) {
215 /* nothing to copy */
216 return TRUE;
219 if (apd->copyflags & APD_COPY_FROM_DIRECTORY)
220 srcname = pathname;
221 else
223 srcname = apd->src;
224 strcatW( srcname, file_part );
226 strcatW( apd->dst, file_part );
228 TRACE("%s => %s\n", debugstr_w(srcname), debugstr_w(apd->dst));
230 /* FIXME: handle APD_COPY_NEW_FILES */
231 res = CopyFileW(srcname, apd->dst, FALSE);
232 TRACE("got %d with %u\n", res, GetLastError());
234 return apd->lazy || res;
237 /******************************************************************
238 * copy_servername_from_name (internal)
240 * for an external server, the serverpart from the name is copied.
242 * RETURNS
243 * the length (in WCHAR) of the serverpart (0 for the local computer)
244 * (-length), when the name is too long
247 static LONG copy_servername_from_name(LPCWSTR name, LPWSTR target)
249 LPCWSTR server;
250 LPWSTR ptr;
251 WCHAR buffer[MAX_COMPUTERNAME_LENGTH +1];
252 DWORD len;
253 DWORD serverlen;
255 if (target) *target = '\0';
257 if (name == NULL) return 0;
258 if ((name[0] != '\\') || (name[1] != '\\')) return 0;
260 server = &name[2];
261 /* skip over both backslash, find separator '\' */
262 ptr = strchrW(server, '\\');
263 serverlen = (ptr) ? ptr - server : lstrlenW(server);
265 /* servername is empty */
266 if (serverlen == 0) return 0;
268 TRACE("found %s\n", debugstr_wn(server, serverlen));
270 if (serverlen > MAX_COMPUTERNAME_LENGTH) return -serverlen;
272 if (target) {
273 memcpy(target, server, serverlen * sizeof(WCHAR));
274 target[serverlen] = '\0';
277 len = sizeof(buffer) / sizeof(buffer[0]);
278 if (GetComputerNameW(buffer, &len)) {
279 if ((serverlen == len) && (strncmpiW(server, buffer, len) == 0)) {
280 /* The requested Servername is our computername */
281 return 0;
284 return serverlen;
287 /******************************************************************
288 * get_basename_from_name (internal)
290 * skip over the serverpart from the full name
293 static LPCWSTR get_basename_from_name(LPCWSTR name)
295 if (name == NULL) return NULL;
296 if ((name[0] == '\\') && (name[1] == '\\')) {
297 /* skip over the servername and search for the following '\' */
298 name = strchrW(&name[2], '\\');
299 if ((name) && (name[1])) {
300 /* found a separator ('\') followed by a name:
301 skip over the separator and return the rest */
302 name++;
304 else
306 /* no basename present (we found only a servername) */
307 return NULL;
310 return name;
313 /******************************************************************
314 * monitor_unload [internal]
316 * release a printmonitor and unload it from memory, when needed
319 static void monitor_unload(monitor_t * pm)
321 if (pm == NULL) return;
322 TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
324 EnterCriticalSection(&monitor_handles_cs);
326 if (pm->refcount) pm->refcount--;
328 if (pm->refcount == 0) {
329 list_remove(&pm->entry);
330 FreeLibrary(pm->hdll);
331 heap_free(pm->name);
332 heap_free(pm->dllname);
333 heap_free(pm);
335 LeaveCriticalSection(&monitor_handles_cs);
338 /******************************************************************
339 * monitor_unloadall [internal]
341 * release all registered printmonitors and unload them from memory, when needed
345 static void monitor_unloadall(void)
347 monitor_t * pm;
348 monitor_t * next;
350 EnterCriticalSection(&monitor_handles_cs);
351 /* iterate through the list, with safety against removal */
352 LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
354 /* skip monitorui dlls */
355 if (pm->monitor) monitor_unload(pm);
357 LeaveCriticalSection(&monitor_handles_cs);
360 /******************************************************************
361 * monitor_load [internal]
363 * load a printmonitor, get the dllname from the registry, when needed
364 * initialize the monitor and dump found function-pointers
366 * On failure, SetLastError() is called and NULL is returned
369 static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname)
371 LPMONITOR2 (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
372 PMONITORUI (WINAPI *pInitializePrintMonitorUI)(VOID);
373 LPMONITOREX (WINAPI *pInitializePrintMonitor) (LPWSTR);
374 DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
375 DWORD (WINAPI *pInitializeMonitor) (LPWSTR);
377 monitor_t * pm = NULL;
378 monitor_t * cursor;
379 LPWSTR regroot = NULL;
380 LPWSTR driver = dllname;
382 TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
383 /* Is the Monitor already loaded? */
384 EnterCriticalSection(&monitor_handles_cs);
386 if (name) {
387 LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
389 if (cursor->name && (lstrcmpW(name, cursor->name) == 0)) {
390 pm = cursor;
391 break;
396 if (pm == NULL) {
397 pm = heap_alloc_zero(sizeof(monitor_t));
398 if (pm == NULL) goto cleanup;
399 list_add_tail(&monitor_handles, &pm->entry);
401 pm->refcount++;
403 if (pm->name == NULL) {
404 /* Load the monitor */
405 LPMONITOREX pmonitorEx;
406 DWORD len;
408 if (name) {
409 len = lstrlenW(monitorsW) + lstrlenW(name) + 2;
410 regroot = heap_alloc(len * sizeof(WCHAR));
413 if (regroot) {
414 lstrcpyW(regroot, monitorsW);
415 lstrcatW(regroot, name);
416 /* Get the Driver from the Registry */
417 if (driver == NULL) {
418 HKEY hroot;
419 DWORD namesize;
420 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
421 if (RegQueryValueExW(hroot, driverW, NULL, NULL, NULL,
422 &namesize) == ERROR_SUCCESS) {
423 driver = heap_alloc(namesize);
424 RegQueryValueExW(hroot, driverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
426 RegCloseKey(hroot);
431 pm->name = strdupW(name);
432 pm->dllname = strdupW(driver);
434 if ((name && (!regroot || !pm->name)) || !pm->dllname) {
435 monitor_unload(pm);
436 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
437 pm = NULL;
438 goto cleanup;
441 pm->hdll = LoadLibraryW(driver);
442 TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
444 if (pm->hdll == NULL) {
445 monitor_unload(pm);
446 SetLastError(ERROR_MOD_NOT_FOUND);
447 pm = NULL;
448 goto cleanup;
451 pInitializePrintMonitor2 = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
452 pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
453 pInitializePrintMonitor = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
454 pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
455 pInitializeMonitor = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
458 TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
459 TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
460 TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
461 TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
462 TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
464 if (pInitializePrintMonitorUI != NULL) {
465 pm->monitorUI = pInitializePrintMonitorUI();
466 TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver));
467 if (pm->monitorUI) {
468 TRACE("0x%08x: dwMonitorSize (%d)\n",
469 pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize);
474 if (pInitializePrintMonitor && regroot) {
475 pmonitorEx = pInitializePrintMonitor(regroot);
476 TRACE("%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
477 pmonitorEx, debugstr_w(driver), debugstr_w(regroot));
479 if (pmonitorEx) {
480 pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
481 pm->monitor = &(pmonitorEx->Monitor);
485 if (pm->monitor) {
486 TRACE("0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize);
490 if (!pm->monitor && regroot) {
491 if (pInitializePrintMonitor2 != NULL) {
492 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
494 if (pInitializeMonitorEx != NULL) {
495 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
497 if (pInitializeMonitor != NULL) {
498 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
501 if (!pm->monitor && !pm->monitorUI) {
502 monitor_unload(pm);
503 SetLastError(ERROR_PROC_NOT_FOUND);
504 pm = NULL;
507 cleanup:
508 if ((pm_localport == NULL) && (pm != NULL) && (lstrcmpW(pm->name, localportW) == 0)) {
509 pm->refcount++;
510 pm_localport = pm;
512 LeaveCriticalSection(&monitor_handles_cs);
513 if (driver != dllname) heap_free(driver);
514 heap_free(regroot);
515 TRACE("=> %p\n", pm);
516 return pm;
519 /******************************************************************
520 * monitor_loadall [internal]
522 * Load all registered monitors
525 static DWORD monitor_loadall(void)
527 monitor_t * pm;
528 DWORD registered = 0;
529 DWORD loaded = 0;
530 HKEY hmonitors;
531 WCHAR buffer[MAX_PATH];
532 DWORD id = 0;
534 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hmonitors) == ERROR_SUCCESS) {
535 RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, &registered, NULL, NULL,
536 NULL, NULL, NULL, NULL, NULL);
538 TRACE("%d monitors registered\n", registered);
540 while (id < registered) {
541 buffer[0] = '\0';
542 RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
543 pm = monitor_load(buffer, NULL);
544 if (pm) loaded++;
545 id++;
547 RegCloseKey(hmonitors);
549 TRACE("%d monitors loaded\n", loaded);
550 return loaded;
553 /******************************************************************
554 * monitor_loadui [internal]
556 * load the userinterface-dll for a given portmonitor
558 * On failure, NULL is returned
560 static monitor_t * monitor_loadui(monitor_t * pm)
562 monitor_t * pui = NULL;
563 WCHAR buffer[MAX_PATH];
564 HANDLE hXcv;
565 DWORD len;
566 DWORD res;
568 if (pm == NULL) return NULL;
569 TRACE("(%p) => dllname: %s\n", pm, debugstr_w(pm->dllname));
571 /* Try the Portmonitor first; works for many monitors */
572 if (pm->monitorUI) {
573 EnterCriticalSection(&monitor_handles_cs);
574 pm->refcount++;
575 LeaveCriticalSection(&monitor_handles_cs);
576 return pm;
579 /* query the userinterface-dllname from the Portmonitor */
580 if ((pm->monitor) && (pm->monitor->pfnXcvDataPort)) {
581 /* building (",XcvMonitor %s",pm->name) not needed yet */
582 res = pm->monitor->pfnXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, &hXcv);
583 TRACE("got %u with %p\n", res, hXcv);
584 if (res) {
585 res = pm->monitor->pfnXcvDataPort(hXcv, monitorUIW, NULL, 0, (BYTE *) buffer, sizeof(buffer), &len);
586 TRACE("got %u with %s\n", res, debugstr_w(buffer));
587 if (res == ERROR_SUCCESS) pui = monitor_load(NULL, buffer);
588 pm->monitor->pfnXcvClosePort(hXcv);
591 return pui;
594 /******************************************************************
595 * monitor_load_by_port [internal]
597 * load a printmonitor for a given port
599 * On failure, NULL is returned
602 static monitor_t * monitor_load_by_port(LPCWSTR portname)
604 HKEY hroot;
605 HKEY hport;
606 LPWSTR buffer;
607 monitor_t * pm = NULL;
608 DWORD registered = 0;
609 DWORD id = 0;
610 DWORD len;
612 TRACE("(%s)\n", debugstr_w(portname));
614 /* Try the Local Monitor first */
615 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, winnt_cv_portsW, &hroot) == ERROR_SUCCESS) {
616 if (RegQueryValueExW(hroot, portname, NULL, NULL, NULL, &len) == ERROR_SUCCESS) {
617 /* found the portname */
618 RegCloseKey(hroot);
619 return monitor_load(localportW, NULL);
621 RegCloseKey(hroot);
624 len = MAX_PATH + lstrlenW(bs_ports_bsW) + lstrlenW(portname) + 1;
625 buffer = heap_alloc(len * sizeof(WCHAR));
626 if (buffer == NULL) return NULL;
628 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) == ERROR_SUCCESS) {
629 EnterCriticalSection(&monitor_handles_cs);
630 RegQueryInfoKeyW(hroot, NULL, NULL, NULL, &registered, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
632 while ((pm == NULL) && (id < registered)) {
633 buffer[0] = '\0';
634 RegEnumKeyW(hroot, id, buffer, MAX_PATH);
635 TRACE("testing %s\n", debugstr_w(buffer));
636 len = lstrlenW(buffer);
637 lstrcatW(buffer, bs_ports_bsW);
638 lstrcatW(buffer, portname);
639 if (RegOpenKeyW(hroot, buffer, &hport) == ERROR_SUCCESS) {
640 RegCloseKey(hport);
641 buffer[len] = '\0'; /* use only the Monitor-Name */
642 pm = monitor_load(buffer, NULL);
644 id++;
646 LeaveCriticalSection(&monitor_handles_cs);
647 RegCloseKey(hroot);
649 heap_free(buffer);
650 return pm;
653 /******************************************************************
654 * Return the number of bytes for an multi_sz string.
655 * The result includes all \0s
656 * (specifically the extra \0, that is needed as multi_sz terminator).
658 static int multi_sz_lenW(const WCHAR *str)
660 const WCHAR *ptr = str;
661 if (!str) return 0;
664 ptr += lstrlenW(ptr) + 1;
665 } while (*ptr);
667 return (ptr - str + 1) * sizeof(WCHAR);
670 /******************************************************************
671 * validate_envW [internal]
673 * validate the user-supplied printing-environment
675 * PARAMS
676 * env [I] PTR to Environment-String or NULL
678 * RETURNS
679 * Success: PTR to printenv_t
680 * Failure: NULL and ERROR_INVALID_ENVIRONMENT
682 * NOTES
683 * An empty string is handled the same way as NULL.
687 static const printenv_t * validate_envW(LPCWSTR env)
689 const printenv_t *result = NULL;
690 unsigned int i;
692 TRACE("(%s)\n", debugstr_w(env));
693 if (env && env[0])
695 for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
697 if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
699 result = all_printenv[i];
700 break;
703 if (result == NULL) {
704 FIXME("unsupported Environment: %s\n", debugstr_w(env));
705 SetLastError(ERROR_INVALID_ENVIRONMENT);
707 /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
709 else
711 result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
714 TRACE("=> using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
715 return result;
718 /*****************************************************************************
719 * enumerate the local monitors (INTERNAL)
721 * returns the needed size (in bytes) for pMonitors
722 * and *lpreturned is set to number of entries returned in pMonitors
724 * Language-Monitors are also installed in the same Registry-Location but
725 * they are filtered in Windows (not returned by EnumMonitors).
726 * We do no filtering to simplify our Code.
729 static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
731 HKEY hroot = NULL;
732 HKEY hentry = NULL;
733 LPWSTR ptr;
734 LPMONITOR_INFO_2W mi;
735 WCHAR buffer[MAX_PATH];
736 WCHAR dllname[MAX_PATH];
737 DWORD dllsize;
738 DWORD len;
739 DWORD index = 0;
740 DWORD needed = 0;
741 DWORD numentries;
742 DWORD entrysize;
744 entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
746 numentries = *lpreturned; /* this is 0, when we scan the registry */
747 len = entrysize * numentries;
748 ptr = (LPWSTR) &pMonitors[len];
750 numentries = 0;
751 len = sizeof(buffer)/sizeof(buffer[0]);
752 buffer[0] = '\0';
754 /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
755 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) == ERROR_SUCCESS) {
756 /* Scan all Monitor-Registry-Keys */
757 while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
758 TRACE("Monitor_%d: %s\n", numentries, debugstr_w(buffer));
759 dllsize = sizeof(dllname);
760 dllname[0] = '\0';
762 /* The Monitor must have a Driver-DLL */
763 if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
764 if (RegQueryValueExW(hentry, driverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
765 /* We found a valid DLL for this Monitor. */
766 TRACE("using Driver: %s\n", debugstr_w(dllname));
768 RegCloseKey(hentry);
771 /* Windows returns only Port-Monitors here, but to simplify our code,
772 we do no filtering for Language-Monitors */
773 if (dllname[0]) {
774 numentries++;
775 needed += entrysize;
776 needed += (len+1) * sizeof(WCHAR); /* len is lstrlenW(monitorname) */
777 if (level > 1) {
778 /* we install and return only monitors for "Windows NT x86" */
779 needed += (lstrlenW(x86_envnameW) +1) * sizeof(WCHAR);
780 needed += dllsize;
783 /* required size is calculated. Now fill the user-buffer */
784 if (pMonitors && (cbBuf >= needed)){
785 mi = (LPMONITOR_INFO_2W) pMonitors;
786 pMonitors += entrysize;
788 TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi, level, numentries);
789 mi->pName = ptr;
790 lstrcpyW(ptr, buffer); /* Name of the Monitor */
791 ptr += (len+1); /* len is lstrlenW(monitorname) */
792 if (level > 1) {
793 mi->pEnvironment = ptr;
794 lstrcpyW(ptr, x86_envnameW); /* fixed to "Windows NT x86" */
795 ptr += (lstrlenW(x86_envnameW)+1);
797 mi->pDLLName = ptr;
798 lstrcpyW(ptr, dllname); /* Name of the Driver-DLL */
799 ptr += (dllsize / sizeof(WCHAR));
803 index++;
804 len = sizeof(buffer)/sizeof(buffer[0]);
805 buffer[0] = '\0';
807 RegCloseKey(hroot);
809 *lpreturned = numentries;
810 TRACE("need %d byte for %d entries\n", needed, numentries);
811 return needed;
814 /*****************************************************************************
815 * enumerate the local print processors (INTERNAL)
817 * returns the needed size (in bytes) for pPPInfo
818 * and *lpreturned is set to number of entries returned in pPPInfo
821 static DWORD get_local_printprocessors(LPWSTR regpathW, LPBYTE pPPInfo, DWORD cbBuf, LPDWORD lpreturned)
823 HKEY hroot = NULL;
824 HKEY hentry = NULL;
825 LPWSTR ptr;
826 PPRINTPROCESSOR_INFO_1W ppi;
827 WCHAR buffer[MAX_PATH];
828 WCHAR dllname[MAX_PATH];
829 DWORD dllsize;
830 DWORD len;
831 DWORD index = 0;
832 DWORD needed = 0;
833 DWORD numentries;
835 numentries = *lpreturned; /* this is 0, when we scan the registry */
836 len = numentries * sizeof(PRINTPROCESSOR_INFO_1W);
837 ptr = (LPWSTR) &pPPInfo[len];
839 numentries = 0;
840 len = sizeof(buffer)/sizeof(buffer[0]);
841 buffer[0] = '\0';
843 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, regpathW, &hroot) == ERROR_SUCCESS) {
844 /* add "winprint" first */
845 numentries++;
846 needed = sizeof(PRINTPROCESSOR_INFO_1W) + sizeof(winprintW);
847 if (pPPInfo && (cbBuf >= needed)){
848 ppi = (PPRINTPROCESSOR_INFO_1W) pPPInfo;
849 pPPInfo += sizeof(PRINTPROCESSOR_INFO_1W);
851 TRACE("%p: writing PRINTPROCESSOR_INFO_1W #%d\n", ppi, numentries);
852 ppi->pName = ptr;
853 lstrcpyW(ptr, winprintW); /* Name of the Print Processor */
854 ptr += sizeof(winprintW) / sizeof(WCHAR);
857 /* Scan all Printprocessor Keys */
858 while ((RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) &&
859 (lstrcmpiW(buffer, winprintW) != 0)) {
860 TRACE("PrintProcessor_%d: %s\n", numentries, debugstr_w(buffer));
861 dllsize = sizeof(dllname);
862 dllname[0] = '\0';
864 /* The Print Processor must have a Driver-DLL */
865 if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
866 if (RegQueryValueExW(hentry, driverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
867 /* We found a valid DLL for this Print Processor */
868 TRACE("using Driver: %s\n", debugstr_w(dllname));
870 RegCloseKey(hentry);
873 if (dllname[0]) {
874 numentries++;
875 needed += sizeof(PRINTPROCESSOR_INFO_1W);
876 needed += (len+1) * sizeof(WCHAR); /* len is lstrlenW(printprocessor name) */
878 /* required size is calculated. Now fill the user-buffer */
879 if (pPPInfo && (cbBuf >= needed)){
880 ppi = (PPRINTPROCESSOR_INFO_1W) pPPInfo;
881 pPPInfo += sizeof(PRINTPROCESSOR_INFO_1W);
883 TRACE("%p: writing PRINTPROCESSOR_INFO_1W #%d\n", ppi, numentries);
884 ppi->pName = ptr;
885 lstrcpyW(ptr, buffer); /* Name of the Print Processor */
886 ptr += (len+1); /* len is lstrlenW(printprosessor name) */
889 index++;
890 len = sizeof(buffer)/sizeof(buffer[0]);
891 buffer[0] = '\0';
893 RegCloseKey(hroot);
895 *lpreturned = numentries;
896 TRACE("need %d byte for %d entries\n", needed, numentries);
897 return needed;
900 /******************************************************************
901 * enumerate the local Ports from all loaded monitors (internal)
903 * returns the needed size (in bytes) for pPorts
904 * and *lpreturned is set to number of entries returned in pPorts
907 static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
909 monitor_t * pm;
910 LPWSTR ptr;
911 LPPORT_INFO_2W cache;
912 LPPORT_INFO_2W out;
913 LPBYTE pi_buffer = NULL;
914 DWORD pi_allocated = 0;
915 DWORD pi_needed;
916 DWORD pi_index;
917 DWORD pi_returned;
918 DWORD res;
919 DWORD outindex = 0;
920 DWORD needed;
921 DWORD numentries;
922 DWORD entrysize;
925 TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
926 entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
928 numentries = *lpreturned; /* this is 0, when we scan the registry */
929 needed = entrysize * numentries;
930 ptr = (LPWSTR) &pPorts[needed];
932 numentries = 0;
933 needed = 0;
935 LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
937 if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
938 pi_needed = 0;
939 pi_returned = 0;
940 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
941 if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
942 /* Do not use heap_realloc (we do not need the old data in the buffer) */
943 heap_free(pi_buffer);
944 pi_buffer = heap_alloc(pi_needed);
945 pi_allocated = (pi_buffer) ? pi_needed : 0;
946 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
948 TRACE("(%s) got %d with %d (need %d byte for %d entries)\n",
949 debugstr_w(pm->name), res, GetLastError(), pi_needed, pi_returned);
951 numentries += pi_returned;
952 needed += pi_needed;
954 /* fill the output-buffer (pPorts), if we have one */
955 if (pPorts && (cbBuf >= needed ) && pi_buffer) {
956 pi_index = 0;
957 while (pi_returned > pi_index) {
958 cache = (LPPORT_INFO_2W) &pi_buffer[pi_index * entrysize];
959 out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
960 out->pPortName = ptr;
961 lstrcpyW(ptr, cache->pPortName);
962 ptr += (lstrlenW(ptr)+1);
963 if (level > 1) {
964 out->pMonitorName = ptr;
965 lstrcpyW(ptr, cache->pMonitorName);
966 ptr += (lstrlenW(ptr)+1);
968 out->pDescription = ptr;
969 lstrcpyW(ptr, cache->pDescription);
970 ptr += (lstrlenW(ptr)+1);
971 out->fPortType = cache->fPortType;
972 out->Reserved = cache->Reserved;
974 pi_index++;
975 outindex++;
980 /* the temporary portinfo-buffer is no longer needed */
981 heap_free(pi_buffer);
983 *lpreturned = numentries;
984 TRACE("need %d byte for %d entries\n", needed, numentries);
985 return needed;
989 /*****************************************************************************
990 * open_driver_reg [internal]
992 * opens the registry for the printer drivers depending on the given input
993 * variable pEnvironment
995 * RETURNS:
996 * Success: the opened hkey
997 * Failure: NULL
999 static HKEY open_driver_reg(LPCWSTR pEnvironment)
1001 HKEY retval = NULL;
1002 LPWSTR buffer;
1003 const printenv_t * env;
1005 TRACE("(%s)\n", debugstr_w(pEnvironment));
1007 env = validate_envW(pEnvironment);
1008 if (!env) return NULL;
1010 buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(fmt_driversW) +
1011 (lstrlenW(env->envname) + lstrlenW(env->versionregpath)) * sizeof(WCHAR));
1013 if (buffer) {
1014 wsprintfW(buffer, fmt_driversW, env->envname, env->versionregpath);
1015 RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
1016 HeapFree(GetProcessHeap(), 0, buffer);
1018 return retval;
1021 /*****************************************************************************
1022 * fpGetPrinterDriverDirectory [exported through PRINTPROVIDOR]
1024 * Return the PATH for the Printer-Drivers
1026 * PARAMS
1027 * pName [I] Servername (NT only) or NULL (local Computer)
1028 * pEnvironment [I] Printing-Environment (see below) or NULL (Default)
1029 * Level [I] Structure-Level (must be 1)
1030 * pDriverDirectory [O] PTR to Buffer that receives the Result
1031 * cbBuf [I] Size of Buffer at pDriverDirectory
1032 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
1033 * required for pDriverDirectory
1035 * RETURNS
1036 * Success: TRUE and in pcbNeeded the Bytes used in pDriverDirectory
1037 * Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
1038 * if cbBuf is too small
1040 * Native Values returned in pDriverDirectory on Success:
1041 *| NT(Windows NT x86): "%winsysdir%\\spool\\DRIVERS\\w32x86"
1042 *| NT(Windows 4.0): "%winsysdir%\\spool\\DRIVERS\\win40"
1043 *| win9x(Windows 4.0): "%winsysdir%"
1045 * "%winsysdir%" is the Value from GetSystemDirectoryW()
1048 static BOOL WINAPI fpGetPrinterDriverDirectory(LPWSTR pName, LPWSTR pEnvironment,
1049 DWORD Level, LPBYTE pDriverDirectory, DWORD cbBuf, LPDWORD pcbNeeded)
1051 DWORD needed;
1052 const printenv_t * env;
1053 WCHAR * const dir = (WCHAR *)pDriverDirectory;
1055 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName),
1056 debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
1058 if (pName != NULL && pName[0]) {
1059 FIXME("server %s not supported\n", debugstr_w(pName));
1060 SetLastError(ERROR_INVALID_PARAMETER);
1061 return FALSE;
1064 env = validate_envW(pEnvironment);
1065 if (!env) return FALSE; /* pEnvironment invalid or unsupported */
1068 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
1069 needed = GetSystemDirectoryW(NULL, 0);
1070 /* add the Size for the Subdirectories */
1071 needed += lstrlenW(spoolW);
1072 needed += lstrlenW(driversW);
1073 needed += lstrlenW(env->subdir);
1074 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
1076 *pcbNeeded = needed;
1078 if (needed > cbBuf) {
1079 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1080 return FALSE;
1083 if (dir == NULL) {
1084 /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
1085 SetLastError(ERROR_INVALID_USER_BUFFER);
1086 return FALSE;
1089 GetSystemDirectoryW( dir, cbBuf / sizeof(WCHAR) );
1090 /* add the Subdirectories */
1091 lstrcatW( dir, spoolW );
1092 CreateDirectoryW( dir, NULL );
1093 lstrcatW( dir, driversW );
1094 CreateDirectoryW( dir, NULL );
1095 lstrcatW( dir, env->subdir );
1096 CreateDirectoryW( dir, NULL );
1098 TRACE( "=> %s\n", debugstr_w( dir ) );
1099 return TRUE;
1102 /******************************************************************
1103 * driver_load [internal]
1105 * load a driver user interface dll
1107 * On failure, NULL is returned
1111 static HMODULE driver_load(const printenv_t * env, LPWSTR dllname)
1113 WCHAR fullname[MAX_PATH];
1114 HMODULE hui;
1115 DWORD len;
1117 TRACE("(%p, %s)\n", env, debugstr_w(dllname));
1119 /* build the driverdir */
1120 len = sizeof(fullname) -
1121 (lstrlenW(env->versionsubdir) + 1 + lstrlenW(dllname) + 1) * sizeof(WCHAR);
1123 if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
1124 (LPBYTE) fullname, len, &len)) {
1125 /* Should never fail */
1126 SetLastError(ERROR_BUFFER_OVERFLOW);
1127 return NULL;
1130 lstrcatW(fullname, env->versionsubdir);
1131 lstrcatW(fullname, backslashW);
1132 lstrcatW(fullname, dllname);
1134 hui = LoadLibraryW(fullname);
1135 TRACE("%p: LoadLibrary(%s) %d\n", hui, debugstr_w(fullname), GetLastError());
1137 return hui;
1140 /******************************************************************
1141 * printer_free
1142 * free the data pointer of an opened printer
1144 static VOID printer_free(printer_t * printer)
1146 if (printer->hXcv)
1147 printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
1149 monitor_unload(printer->pm);
1151 heap_free(printer->printername);
1152 heap_free(printer->name);
1153 heap_free(printer);
1156 /******************************************************************
1157 * printer_alloc_handle
1158 * alloc a printer handle and remember the data pointer in the printer handle table
1161 static HANDLE printer_alloc_handle(LPCWSTR name, LPPRINTER_DEFAULTSW pDefault)
1163 WCHAR servername[MAX_COMPUTERNAME_LENGTH + 1];
1164 printer_t *printer = NULL;
1165 LPCWSTR printername;
1166 HKEY hkeyPrinters;
1167 HKEY hkeyPrinter;
1168 DWORD len;
1170 if (copy_servername_from_name(name, servername)) {
1171 FIXME("server %s not supported\n", debugstr_w(servername));
1172 SetLastError(ERROR_INVALID_PRINTER_NAME);
1173 return NULL;
1176 printername = get_basename_from_name(name);
1177 if (name != printername) TRACE("converted %s to %s\n", debugstr_w(name), debugstr_w(printername));
1179 /* an empty printername is invalid */
1180 if (printername && (!printername[0])) {
1181 SetLastError(ERROR_INVALID_PARAMETER);
1182 return NULL;
1185 printer = heap_alloc_zero(sizeof(printer_t));
1186 if (!printer) goto end;
1188 /* clone the base name. This is NULL for the printserver */
1189 printer->printername = strdupW(printername);
1191 /* clone the full name */
1192 printer->name = strdupW(name);
1193 if (name && (!printer->name)) {
1194 printer_free(printer);
1195 printer = NULL;
1197 if (printername) {
1198 len = sizeof(XcvMonitorW)/sizeof(WCHAR) - 1;
1199 if (strncmpW(printername, XcvMonitorW, len) == 0) {
1200 /* OpenPrinter(",XcvMonitor ", ...) detected */
1201 TRACE(",XcvMonitor: %s\n", debugstr_w(&printername[len]));
1202 printer->pm = monitor_load(&printername[len], NULL);
1203 if (printer->pm == NULL) {
1204 printer_free(printer);
1205 SetLastError(ERROR_UNKNOWN_PORT);
1206 printer = NULL;
1207 goto end;
1210 else
1212 len = sizeof(XcvPortW)/sizeof(WCHAR) - 1;
1213 if (strncmpW( printername, XcvPortW, len) == 0) {
1214 /* OpenPrinter(",XcvPort ", ...) detected */
1215 TRACE(",XcvPort: %s\n", debugstr_w(&printername[len]));
1216 printer->pm = monitor_load_by_port(&printername[len]);
1217 if (printer->pm == NULL) {
1218 printer_free(printer);
1219 SetLastError(ERROR_UNKNOWN_PORT);
1220 printer = NULL;
1221 goto end;
1226 if (printer->pm) {
1227 if ((printer->pm->monitor) && (printer->pm->monitor->pfnXcvOpenPort)) {
1228 printer->pm->monitor->pfnXcvOpenPort(&printername[len],
1229 pDefault ? pDefault->DesiredAccess : 0,
1230 &printer->hXcv);
1232 if (printer->hXcv == NULL) {
1233 printer_free(printer);
1234 SetLastError(ERROR_INVALID_PARAMETER);
1235 printer = NULL;
1236 goto end;
1239 else
1241 /* Does the Printer exist? */
1242 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, printersW, &hkeyPrinters) != ERROR_SUCCESS) {
1243 ERR("Can't create Printers key\n");
1244 printer_free(printer);
1245 SetLastError(ERROR_INVALID_PRINTER_NAME);
1246 printer = NULL;
1247 goto end;
1249 if (RegOpenKeyW(hkeyPrinters, printername, &hkeyPrinter) != ERROR_SUCCESS) {
1250 WARN("Printer not found in Registry: %s\n", debugstr_w(printername));
1251 RegCloseKey(hkeyPrinters);
1252 printer_free(printer);
1253 SetLastError(ERROR_INVALID_PRINTER_NAME);
1254 printer = NULL;
1255 goto end;
1257 RegCloseKey(hkeyPrinter);
1258 RegCloseKey(hkeyPrinters);
1261 else
1263 TRACE("using the local printserver\n");
1266 end:
1268 TRACE("==> %p\n", printer);
1269 return (HANDLE)printer;
1272 static inline WCHAR *get_file_part( WCHAR *name )
1274 WCHAR *ptr = strrchrW( name, '\\' );
1275 if (ptr) return ptr + 1;
1276 return name;
1279 /******************************************************************************
1280 * myAddPrinterDriverEx [internal]
1282 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1283 * and a special mode with lazy error checking.
1286 static BOOL myAddPrinterDriverEx(DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags, BOOL lazy)
1288 const printenv_t *env;
1289 apd_data_t apd;
1290 DRIVER_INFO_8W di;
1291 BOOL (WINAPI *pDrvDriverEvent)(DWORD, DWORD, LPBYTE, LPARAM);
1292 HMODULE hui;
1293 WCHAR *file;
1294 HKEY hroot;
1295 HKEY hdrv;
1296 DWORD disposition;
1297 DWORD len;
1298 LONG lres;
1299 BOOL res;
1301 /* we need to set all entries in the Registry, independent from the Level of
1302 DRIVER_INFO, that the caller supplied */
1304 ZeroMemory(&di, sizeof(di));
1305 if (pDriverInfo && (level < (sizeof(di_sizeof) / sizeof(di_sizeof[0])))) {
1306 memcpy(&di, pDriverInfo, di_sizeof[level]);
1309 /* dump the most used infos */
1310 TRACE("%p: .cVersion : 0x%x/%d\n", pDriverInfo, di.cVersion, di.cVersion);
1311 TRACE("%p: .pName : %s\n", di.pName, debugstr_w(di.pName));
1312 TRACE("%p: .pEnvironment: %s\n", di.pEnvironment, debugstr_w(di.pEnvironment));
1313 TRACE("%p: .pDriverPath : %s\n", di.pDriverPath, debugstr_w(di.pDriverPath));
1314 TRACE("%p: .pDataFile : %s\n", di.pDataFile, debugstr_w(di.pDataFile));
1315 TRACE("%p: .pConfigFile : %s\n", di.pConfigFile, debugstr_w(di.pConfigFile));
1316 TRACE("%p: .pHelpFile : %s\n", di.pHelpFile, debugstr_w(di.pHelpFile));
1317 /* dump only the first of the additional Files */
1318 TRACE("%p: .pDependentFiles: %s\n", di.pDependentFiles, debugstr_w(di.pDependentFiles));
1321 /* check environment */
1322 env = validate_envW(di.pEnvironment);
1323 if (env == NULL) return FALSE; /* ERROR_INVALID_ENVIRONMENT */
1325 /* fill the copy-data / get the driverdir */
1326 len = sizeof(apd.src) - sizeof(version3_subdirW) - sizeof(WCHAR);
1327 if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
1328 (LPBYTE) apd.src, len, &len)) {
1329 /* Should never fail */
1330 return FALSE;
1332 memcpy(apd.dst, apd.src, len);
1333 lstrcatW(apd.src, backslashW);
1334 apd.srclen = lstrlenW(apd.src);
1335 lstrcatW(apd.dst, env->versionsubdir);
1336 lstrcatW(apd.dst, backslashW);
1337 apd.dstlen = lstrlenW(apd.dst);
1338 apd.copyflags = dwFileCopyFlags;
1339 apd.lazy = lazy;
1340 CreateDirectoryW(apd.src, NULL);
1341 CreateDirectoryW(apd.dst, NULL);
1343 hroot = open_driver_reg(env->envname);
1344 if (!hroot) {
1345 ERR("Can't create Drivers key\n");
1346 return FALSE;
1349 /* Fill the Registry for the Driver */
1350 if ((lres = RegCreateKeyExW(hroot, di.pName, 0, NULL, REG_OPTION_NON_VOLATILE,
1351 KEY_WRITE | KEY_QUERY_VALUE, NULL,
1352 &hdrv, &disposition)) != ERROR_SUCCESS) {
1354 ERR("can't create driver %s: %u\n", debugstr_w(di.pName), lres);
1355 RegCloseKey(hroot);
1356 SetLastError(lres);
1357 return FALSE;
1359 RegCloseKey(hroot);
1361 /* Verified with the Adobe PS Driver, that w2k does not use di.Version */
1362 RegSetValueExW(hdrv, versionW, 0, REG_DWORD, (const BYTE*) &env->driverversion,
1363 sizeof(DWORD));
1365 file = get_file_part( di.pDriverPath );
1366 RegSetValueExW( hdrv, driverW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
1367 apd_copyfile( di.pDriverPath, file, &apd );
1369 file = get_file_part( di.pDataFile );
1370 RegSetValueExW( hdrv, data_fileW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
1371 apd_copyfile( di.pDataFile, file, &apd );
1373 file = get_file_part( di.pConfigFile );
1374 RegSetValueExW( hdrv, configuration_fileW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
1375 apd_copyfile( di.pConfigFile, file, &apd );
1377 /* settings for level 3 */
1378 if (di.pHelpFile)
1380 file = get_file_part( di.pHelpFile );
1381 RegSetValueExW( hdrv, help_fileW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
1382 apd_copyfile( di.pHelpFile, file, &apd );
1384 else
1385 RegSetValueExW( hdrv, help_fileW, 0, REG_SZ, (const BYTE*)emptyW, sizeof(emptyW) );
1387 if (di.pDependentFiles && *di.pDependentFiles)
1389 WCHAR *reg, *reg_ptr, *in_ptr;
1390 reg = reg_ptr = HeapAlloc( GetProcessHeap(), 0, multi_sz_lenW( di.pDependentFiles ) );
1392 for (in_ptr = di.pDependentFiles; *in_ptr; in_ptr += strlenW( in_ptr ) + 1)
1394 file = get_file_part( in_ptr );
1395 len = strlenW( file ) + 1;
1396 memcpy( reg_ptr, file, len * sizeof(WCHAR) );
1397 reg_ptr += len;
1398 apd_copyfile( in_ptr, file, &apd );
1400 *reg_ptr = 0;
1402 RegSetValueExW( hdrv, dependent_filesW, 0, REG_MULTI_SZ, (LPBYTE)reg, (reg_ptr - reg + 1) * sizeof(WCHAR) );
1403 HeapFree( GetProcessHeap(), 0, reg );
1405 else
1406 RegSetValueExW(hdrv, dependent_filesW, 0, REG_MULTI_SZ, (const BYTE*)emptyW, sizeof(emptyW));
1408 /* The language-Monitor was already copied by the caller to "%SystemRoot%\system32" */
1409 if (di.pMonitorName)
1410 RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (LPBYTE) di.pMonitorName,
1411 (lstrlenW(di.pMonitorName)+1)* sizeof(WCHAR));
1412 else
1413 RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (const BYTE*)emptyW, sizeof(emptyW));
1415 if (di.pDefaultDataType)
1416 RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (LPBYTE) di.pDefaultDataType,
1417 (lstrlenW(di.pDefaultDataType)+1)* sizeof(WCHAR));
1418 else
1419 RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (const BYTE*)emptyW, sizeof(emptyW));
1421 /* settings for level 4 */
1422 if (di.pszzPreviousNames)
1423 RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (LPBYTE) di.pszzPreviousNames,
1424 multi_sz_lenW(di.pszzPreviousNames));
1425 else
1426 RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (const BYTE*)emptyW, sizeof(emptyW));
1428 if (level > 5) TRACE("level %u for Driver %s is incomplete\n", level, debugstr_w(di.pName));
1430 RegCloseKey(hdrv);
1431 hui = driver_load(env, di.pConfigFile);
1432 pDrvDriverEvent = (void *)GetProcAddress(hui, "DrvDriverEvent");
1433 if (hui && pDrvDriverEvent) {
1435 /* Support for DrvDriverEvent is optional */
1436 TRACE("DRIVER_EVENT_INITIALIZE for %s (%s)\n", debugstr_w(di.pName), debugstr_w(di.pConfigFile));
1437 /* MSDN: level for DRIVER_INFO is 1 to 3 */
1438 res = pDrvDriverEvent(DRIVER_EVENT_INITIALIZE, 3, (LPBYTE) &di, 0);
1439 TRACE("got %d from DRIVER_EVENT_INITIALIZE\n", res);
1441 FreeLibrary(hui);
1443 TRACE("=> TRUE with %u\n", GetLastError());
1444 return TRUE;
1448 /******************************************************************************
1449 * fpAddMonitor [exported through PRINTPROVIDOR]
1451 * Install a Printmonitor
1453 * PARAMS
1454 * pName [I] Servername or NULL (local Computer)
1455 * Level [I] Structure-Level (Must be 2)
1456 * pMonitors [I] PTR to MONITOR_INFO_2
1458 * RETURNS
1459 * Success: TRUE
1460 * Failure: FALSE
1462 * NOTES
1463 * All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1466 static BOOL WINAPI fpAddMonitor(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1468 monitor_t * pm = NULL;
1469 LPMONITOR_INFO_2W mi2w;
1470 HKEY hroot = NULL;
1471 HKEY hentry = NULL;
1472 DWORD disposition;
1473 BOOL res = FALSE;
1475 mi2w = (LPMONITOR_INFO_2W) pMonitors;
1476 TRACE("(%s, %d, %p): %s %s %s\n", debugstr_w(pName), Level, pMonitors,
1477 debugstr_w(mi2w ? mi2w->pName : NULL),
1478 debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
1479 debugstr_w(mi2w ? mi2w->pDLLName : NULL));
1481 if (copy_servername_from_name(pName, NULL)) {
1482 FIXME("server %s not supported\n", debugstr_w(pName));
1483 SetLastError(ERROR_ACCESS_DENIED);
1484 return FALSE;
1487 if (!mi2w->pName || (! mi2w->pName[0])) {
1488 WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
1489 SetLastError(ERROR_INVALID_PARAMETER);
1490 return FALSE;
1492 if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, x86_envnameW)) {
1493 WARN("Environment %s requested (we support only %s)\n",
1494 debugstr_w(mi2w->pEnvironment), debugstr_w(x86_envnameW));
1495 SetLastError(ERROR_INVALID_ENVIRONMENT);
1496 return FALSE;
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 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1512 ERR("unable to create key %s\n", debugstr_w(monitorsW));
1513 return FALSE;
1516 if (RegCreateKeyExW(hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
1517 KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
1518 &disposition) == ERROR_SUCCESS) {
1520 /* Some installers set options for the port before calling AddMonitor.
1521 We query the "Driver" entry to verify that the monitor is installed,
1522 before we return an error.
1523 When a user installs two print monitors at the same time with the
1524 same name, a race condition is possible but silently ignored. */
1526 DWORD namesize = 0;
1528 if ((disposition == REG_OPENED_EXISTING_KEY) &&
1529 (RegQueryValueExW(hentry, driverW, NULL, NULL, NULL,
1530 &namesize) == ERROR_SUCCESS)) {
1531 TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
1532 /* 9x use ERROR_ALREADY_EXISTS */
1533 SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
1535 else
1537 INT len;
1538 len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
1539 res = (RegSetValueExW(hentry, driverW, 0, REG_SZ,
1540 (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
1542 RegCloseKey(hentry);
1545 RegCloseKey(hroot);
1546 return (res);
1549 /******************************************************************************
1550 * fpAddPort [exported through PRINTPROVIDOR]
1552 * Add a Port for a specific Monitor
1554 * PARAMS
1555 * pName [I] Servername or NULL (local Computer)
1556 * hWnd [I] Handle to parent Window for the Dialog-Box
1557 * pMonitorName [I] Name of the Monitor that manage the Port
1559 * RETURNS
1560 * Success: TRUE
1561 * Failure: FALSE
1564 static BOOL WINAPI fpAddPort(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
1566 monitor_t * pm;
1567 monitor_t * pui;
1568 LONG lres;
1569 DWORD res;
1571 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
1573 lres = copy_servername_from_name(pName, NULL);
1574 if (lres) {
1575 FIXME("server %s not supported\n", debugstr_w(pName));
1576 SetLastError(ERROR_INVALID_PARAMETER);
1577 return FALSE;
1580 /* an empty Monitorname is Invalid */
1581 if (!pMonitorName[0]) {
1582 SetLastError(ERROR_NOT_SUPPORTED);
1583 return FALSE;
1586 pm = monitor_load(pMonitorName, NULL);
1587 if (pm && pm->monitor && pm->monitor->pfnAddPort) {
1588 res = pm->monitor->pfnAddPort(pName, hWnd, pMonitorName);
1589 TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pm->dllname));
1591 else
1593 pui = monitor_loadui(pm);
1594 if (pui && pui->monitorUI && pui->monitorUI->pfnAddPortUI) {
1595 res = pui->monitorUI->pfnAddPortUI(pName, hWnd, pMonitorName, NULL);
1596 TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pui->dllname));
1598 else
1600 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1601 debugstr_w(pMonitorName), pm, debugstr_w(pm ? pm->dllname : NULL),
1602 pui, debugstr_w(pui ? pui->dllname : NULL));
1604 SetLastError(ERROR_NOT_SUPPORTED);
1605 res = FALSE;
1607 monitor_unload(pui);
1609 monitor_unload(pm);
1611 TRACE("returning %d with %u\n", res, GetLastError());
1612 return res;
1615 /******************************************************************************
1616 * fpAddPortEx [exported through PRINTPROVIDOR]
1618 * Add a Port for a specific Monitor, without presenting a user interface
1620 * PARAMS
1621 * pName [I] Servername or NULL (local Computer)
1622 * level [I] Structure-Level (1 or 2) for pBuffer
1623 * pBuffer [I] PTR to: PORT_INFO_1 or PORT_INFO_2
1624 * pMonitorName [I] Name of the Monitor that manage the Port
1626 * RETURNS
1627 * Success: TRUE
1628 * Failure: FALSE
1631 static BOOL WINAPI fpAddPortEx(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
1633 PORT_INFO_2W * pi2;
1634 monitor_t * pm;
1635 DWORD lres;
1636 DWORD res;
1638 pi2 = (PORT_INFO_2W *) pBuffer;
1640 TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
1641 debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
1642 debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
1643 debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
1645 lres = copy_servername_from_name(pName, NULL);
1646 if (lres) {
1647 FIXME("server %s not supported\n", debugstr_w(pName));
1648 SetLastError(ERROR_INVALID_PARAMETER);
1649 return FALSE;
1652 if ((level < 1) || (level > 2)) {
1653 SetLastError(ERROR_INVALID_LEVEL);
1654 return FALSE;
1657 if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
1658 SetLastError(ERROR_INVALID_PARAMETER);
1659 return FALSE;
1662 /* load the Monitor */
1663 pm = monitor_load(pMonitorName, NULL);
1664 if (pm && pm->monitor && pm->monitor->pfnAddPortEx) {
1665 res = pm->monitor->pfnAddPortEx(pName, level, pBuffer, pMonitorName);
1666 TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pm->dllname));
1668 else
1670 FIXME("not implemented for %s (monitor %p: %s)\n",
1671 debugstr_w(pMonitorName), pm, pm ? debugstr_w(pm->dllname) : "(null)");
1672 SetLastError(ERROR_INVALID_PARAMETER);
1673 res = FALSE;
1675 monitor_unload(pm);
1676 return res;
1679 /******************************************************************************
1680 * fpAddPrinterDriverEx [exported through PRINTPROVIDOR]
1682 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1684 * PARAMS
1685 * pName [I] Servername or NULL (local Computer)
1686 * level [I] Level for the supplied DRIVER_INFO_*W struct
1687 * pDriverInfo [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
1688 * dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
1690 * RESULTS
1691 * Success: TRUE
1692 * Failure: FALSE
1695 static BOOL WINAPI fpAddPrinterDriverEx(LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
1697 LONG lres;
1699 TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
1700 lres = copy_servername_from_name(pName, NULL);
1701 if (lres) {
1702 FIXME("server %s not supported\n", debugstr_w(pName));
1703 SetLastError(ERROR_ACCESS_DENIED);
1704 return FALSE;
1707 if ((dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY) != APD_COPY_ALL_FILES) {
1708 TRACE("Flags 0x%x ignored (using APD_COPY_ALL_FILES)\n", dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY);
1711 return myAddPrinterDriverEx(level, pDriverInfo, dwFileCopyFlags, TRUE);
1714 /******************************************************************************
1715 * fpClosePrinter [exported through PRINTPROVIDOR]
1717 * Close a printer handle and free associated resources
1719 * PARAMS
1720 * hPrinter [I] Printerhandle to close
1722 * RESULTS
1723 * Success: TRUE
1724 * Failure: FALSE
1727 static BOOL WINAPI fpClosePrinter(HANDLE hPrinter)
1729 printer_t *printer = (printer_t *) hPrinter;
1731 TRACE("(%p)\n", hPrinter);
1733 if (printer) {
1734 printer_free(printer);
1735 return TRUE;
1737 return FALSE;
1740 /******************************************************************************
1741 * fpConfigurePort [exported through PRINTPROVIDOR]
1743 * Display the Configuration-Dialog for a specific Port
1745 * PARAMS
1746 * pName [I] Servername or NULL (local Computer)
1747 * hWnd [I] Handle to parent Window for the Dialog-Box
1748 * pPortName [I] Name of the Port, that should be configured
1750 * RETURNS
1751 * Success: TRUE
1752 * Failure: FALSE
1755 static BOOL WINAPI fpConfigurePort(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1757 monitor_t * pm;
1758 monitor_t * pui;
1759 LONG lres;
1760 DWORD res;
1762 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
1764 lres = copy_servername_from_name(pName, NULL);
1765 if (lres) {
1766 FIXME("server %s not supported\n", debugstr_w(pName));
1767 SetLastError(ERROR_INVALID_NAME);
1768 return FALSE;
1771 /* an empty Portname is Invalid, but can popup a Dialog */
1772 if (!pPortName[0]) {
1773 SetLastError(ERROR_NOT_SUPPORTED);
1774 return FALSE;
1777 pm = monitor_load_by_port(pPortName);
1778 if (pm && pm->monitor && pm->monitor->pfnConfigurePort) {
1779 TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm->name),
1780 debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
1781 res = pm->monitor->pfnConfigurePort(pName, hWnd, pPortName);
1782 TRACE("got %d with %u\n", res, GetLastError());
1784 else
1786 pui = monitor_loadui(pm);
1787 if (pui && pui->monitorUI && pui->monitorUI->pfnConfigurePortUI) {
1788 TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui->name),
1789 debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
1790 res = pui->monitorUI->pfnConfigurePortUI(pName, hWnd, pPortName);
1791 TRACE("got %d with %u\n", res, GetLastError());
1793 else
1795 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1796 debugstr_w(pPortName), pm, debugstr_w(pm ? pm->dllname : NULL),
1797 pui, debugstr_w(pui ? pui->dllname : NULL));
1799 SetLastError(ERROR_NOT_SUPPORTED);
1800 res = FALSE;
1802 monitor_unload(pui);
1804 monitor_unload(pm);
1806 TRACE("returning %d with %u\n", res, GetLastError());
1807 return res;
1810 /******************************************************************
1811 * fpDeleteMonitor [exported through PRINTPROVIDOR]
1813 * Delete a specific Printmonitor from a Printing-Environment
1815 * PARAMS
1816 * pName [I] Servername or NULL (local Computer)
1817 * pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1818 * pMonitorName [I] Name of the Monitor, that should be deleted
1820 * RETURNS
1821 * Success: TRUE
1822 * Failure: FALSE
1824 * NOTES
1825 * pEnvironment is ignored in Windows for the local Computer.
1829 static BOOL WINAPI fpDeleteMonitor(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1831 HKEY hroot = NULL;
1832 LONG lres;
1834 TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
1835 debugstr_w(pMonitorName));
1837 lres = copy_servername_from_name(pName, NULL);
1838 if (lres) {
1839 FIXME("server %s not supported\n", debugstr_w(pName));
1840 SetLastError(ERROR_INVALID_NAME);
1841 return FALSE;
1844 /* pEnvironment is ignored in Windows for the local Computer */
1845 if (!pMonitorName || !pMonitorName[0]) {
1846 TRACE("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
1847 SetLastError(ERROR_INVALID_PARAMETER);
1848 return FALSE;
1851 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1852 ERR("unable to create key %s\n", debugstr_w(monitorsW));
1853 return FALSE;
1856 if(RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS) {
1857 TRACE("%s deleted\n", debugstr_w(pMonitorName));
1858 RegCloseKey(hroot);
1859 return TRUE;
1862 TRACE("%s does not exist\n", debugstr_w(pMonitorName));
1863 RegCloseKey(hroot);
1865 /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
1866 SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
1867 return FALSE;
1870 /*****************************************************************************
1871 * fpDeletePort [exported through PRINTPROVIDOR]
1873 * Delete a specific Port
1875 * PARAMS
1876 * pName [I] Servername or NULL (local Computer)
1877 * hWnd [I] Handle to parent Window for the Dialog-Box
1878 * pPortName [I] Name of the Port, that should be deleted
1880 * RETURNS
1881 * Success: TRUE
1882 * Failure: FALSE
1885 static BOOL WINAPI fpDeletePort(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1887 monitor_t * pm;
1888 monitor_t * pui;
1889 LONG lres;
1890 DWORD res;
1892 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
1894 lres = copy_servername_from_name(pName, NULL);
1895 if (lres) {
1896 FIXME("server %s not supported\n", debugstr_w(pName));
1897 SetLastError(ERROR_INVALID_NAME);
1898 return FALSE;
1901 /* an empty Portname is Invalid */
1902 if (!pPortName[0]) {
1903 SetLastError(ERROR_NOT_SUPPORTED);
1904 return FALSE;
1907 pm = monitor_load_by_port(pPortName);
1908 if (pm && pm->monitor && pm->monitor->pfnDeletePort) {
1909 TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm->name),
1910 debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
1911 res = pm->monitor->pfnDeletePort(pName, hWnd, pPortName);
1912 TRACE("got %d with %u\n", res, GetLastError());
1914 else
1916 pui = monitor_loadui(pm);
1917 if (pui && pui->monitorUI && pui->monitorUI->pfnDeletePortUI) {
1918 TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui->name),
1919 debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
1920 res = pui->monitorUI->pfnDeletePortUI(pName, hWnd, pPortName);
1921 TRACE("got %d with %u\n", res, GetLastError());
1923 else
1925 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1926 debugstr_w(pPortName), pm, debugstr_w(pm ? pm->dllname : NULL),
1927 pui, debugstr_w(pui ? pui->dllname : NULL));
1929 SetLastError(ERROR_NOT_SUPPORTED);
1930 res = FALSE;
1932 monitor_unload(pui);
1934 monitor_unload(pm);
1936 TRACE("returning %d with %u\n", res, GetLastError());
1937 return res;
1940 /*****************************************************************************
1941 * fpEnumMonitors [exported through PRINTPROVIDOR]
1943 * Enumerate available Port-Monitors
1945 * PARAMS
1946 * pName [I] Servername or NULL (local Computer)
1947 * Level [I] Structure-Level (1:Win9x+NT or 2:NT only)
1948 * pMonitors [O] PTR to Buffer that receives the Result
1949 * cbBuf [I] Size of Buffer at pMonitors
1950 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
1951 * pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
1953 * RETURNS
1954 * Success: TRUE
1955 * Failure: FALSE and in pcbNeeded the Bytes required for pMonitors, if cbBuf is too small
1957 * NOTES
1958 * Windows reads the Registry once and cache the Results.
1961 static BOOL WINAPI fpEnumMonitors(LPWSTR pName, DWORD Level, LPBYTE pMonitors, DWORD cbBuf,
1962 LPDWORD pcbNeeded, LPDWORD pcReturned)
1964 DWORD numentries = 0;
1965 DWORD needed = 0;
1966 LONG lres;
1967 BOOL res = FALSE;
1969 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
1970 cbBuf, pcbNeeded, pcReturned);
1972 lres = copy_servername_from_name(pName, NULL);
1973 if (lres) {
1974 FIXME("server %s not supported\n", debugstr_w(pName));
1975 SetLastError(ERROR_INVALID_NAME);
1976 goto em_cleanup;
1979 if (!Level || (Level > 2)) {
1980 WARN("level (%d) is ignored in win9x\n", Level);
1981 SetLastError(ERROR_INVALID_LEVEL);
1982 return FALSE;
1985 /* Scan all Monitor-Keys */
1986 numentries = 0;
1987 needed = get_local_monitors(Level, NULL, 0, &numentries);
1989 /* we calculated the needed buffersize. now do more error-checks */
1990 if (cbBuf < needed) {
1991 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1992 goto em_cleanup;
1995 /* fill the Buffer with the Monitor-Keys */
1996 needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
1997 res = TRUE;
1999 em_cleanup:
2000 if (pcbNeeded) *pcbNeeded = needed;
2001 if (pcReturned) *pcReturned = numentries;
2003 TRACE("returning %d with %d (%d byte for %d entries)\n",
2004 res, GetLastError(), needed, numentries);
2006 return (res);
2009 /******************************************************************************
2010 * fpEnumPorts [exported through PRINTPROVIDOR]
2012 * Enumerate available Ports
2014 * PARAMS
2015 * pName [I] Servername or NULL (local Computer)
2016 * Level [I] Structure-Level (1 or 2)
2017 * pPorts [O] PTR to Buffer that receives the Result
2018 * cbBuf [I] Size of Buffer at pPorts
2019 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
2020 * pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
2022 * RETURNS
2023 * Success: TRUE
2024 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
2027 static BOOL WINAPI fpEnumPorts(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
2028 LPDWORD pcbNeeded, LPDWORD pcReturned)
2030 DWORD needed = 0;
2031 DWORD numentries = 0;
2032 LONG lres;
2033 BOOL res = FALSE;
2035 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
2036 cbBuf, pcbNeeded, pcReturned);
2038 lres = copy_servername_from_name(pName, NULL);
2039 if (lres) {
2040 FIXME("server %s not supported\n", debugstr_w(pName));
2041 SetLastError(ERROR_INVALID_NAME);
2042 goto emP_cleanup;
2045 if (!Level || (Level > 2)) {
2046 SetLastError(ERROR_INVALID_LEVEL);
2047 goto emP_cleanup;
2050 if (!pcbNeeded || (!pPorts && (cbBuf > 0))) {
2051 SetLastError(RPC_X_NULL_REF_POINTER);
2052 goto emP_cleanup;
2055 EnterCriticalSection(&monitor_handles_cs);
2056 monitor_loadall();
2058 /* Scan all local Ports */
2059 numentries = 0;
2060 needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
2062 /* we calculated the needed buffersize. now do the error-checks */
2063 if (cbBuf < needed) {
2064 monitor_unloadall();
2065 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2066 goto emP_cleanup_cs;
2068 else if (!pPorts || !pcReturned) {
2069 monitor_unloadall();
2070 SetLastError(RPC_X_NULL_REF_POINTER);
2071 goto emP_cleanup_cs;
2074 /* Fill the Buffer */
2075 needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
2076 res = TRUE;
2077 monitor_unloadall();
2079 emP_cleanup_cs:
2080 LeaveCriticalSection(&monitor_handles_cs);
2082 emP_cleanup:
2083 if (pcbNeeded) *pcbNeeded = needed;
2084 if (pcReturned) *pcReturned = (res) ? numentries : 0;
2086 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
2087 (res), GetLastError(), needed, (res) ? numentries : 0, numentries);
2089 return (res);
2092 /*****************************************************************************
2093 * fpEnumPrintProcessors [exported through PRINTPROVIDOR]
2095 * Enumerate available Print Processors
2097 * PARAMS
2098 * pName [I] Servername or NULL (local Computer)
2099 * pEnvironment [I] Printing-Environment or NULL (Default)
2100 * Level [I] Structure-Level (Only 1 is allowed)
2101 * pPPInfo [O] PTR to Buffer that receives the Result
2102 * cbBuf [I] Size of Buffer at pMonitors
2103 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPPInfo
2104 * pcReturned [O] PTR to DWORD that receives the number of Print Processors in pPPInfo
2106 * RETURNS
2107 * Success: TRUE
2108 * Failure: FALSE and in pcbNeeded the Bytes required for pPPInfo, if cbBuf is too small
2111 static BOOL WINAPI fpEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
2112 LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
2114 const printenv_t * env;
2115 LPWSTR regpathW = NULL;
2116 DWORD numentries = 0;
2117 DWORD needed = 0;
2118 LONG lres;
2119 BOOL res = FALSE;
2121 TRACE("(%s, %s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), debugstr_w(pEnvironment),
2122 Level, pPPInfo, cbBuf, pcbNeeded, pcReturned);
2124 lres = copy_servername_from_name(pName, NULL);
2125 if (lres) {
2126 FIXME("server %s not supported\n", debugstr_w(pName));
2127 SetLastError(ERROR_INVALID_NAME);
2128 goto epp_cleanup;
2131 if (Level != 1) {
2132 SetLastError(ERROR_INVALID_LEVEL);
2133 goto epp_cleanup;
2136 env = validate_envW(pEnvironment);
2137 if (!env)
2138 goto epp_cleanup; /* ERROR_INVALID_ENVIRONMENT */
2140 regpathW = heap_alloc(sizeof(fmt_printprocessorsW) +
2141 (lstrlenW(env->envname) * sizeof(WCHAR)));
2143 if (!regpathW)
2144 goto epp_cleanup;
2146 wsprintfW(regpathW, fmt_printprocessorsW, env->envname);
2148 /* Scan all Printprocessor-Keys */
2149 numentries = 0;
2150 needed = get_local_printprocessors(regpathW, NULL, 0, &numentries);
2152 /* we calculated the needed buffersize. now do more error-checks */
2153 if (cbBuf < needed) {
2154 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2155 goto epp_cleanup;
2158 /* fill the Buffer with the Printprocessor Infos */
2159 needed = get_local_printprocessors(regpathW, pPPInfo, cbBuf, &numentries);
2160 res = TRUE;
2162 epp_cleanup:
2163 heap_free(regpathW);
2164 if (pcbNeeded) *pcbNeeded = needed;
2165 if (pcReturned) *pcReturned = numentries;
2167 TRACE("returning %d with %d (%d byte for %d entries)\n",
2168 res, GetLastError(), needed, numentries);
2170 return (res);
2173 /******************************************************************************
2174 * fpGetPrintProcessorDirectory [exported through PRINTPROVIDOR]
2176 * Return the PATH for the Print-Processors
2178 * PARAMS
2179 * pName [I] Servername or NULL (this computer)
2180 * pEnvironment [I] Printing-Environment or NULL (Default)
2181 * level [I] Structure-Level (must be 1)
2182 * pPPInfo [O] PTR to Buffer that receives the Result
2183 * cbBuf [I] Size of Buffer at pPPInfo
2184 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPPInfo
2186 * RETURNS
2187 * Success: TRUE
2188 * Failure: FALSE and in pcbNeeded the Bytes required for pPPInfo, if cbBuf is too small
2190 * Native Values returned in pPPInfo on Success for this computer:
2191 *| NT(Windows x64): "%winsysdir%\\spool\\PRTPROCS\\x64"
2192 *| NT(Windows NT x86): "%winsysdir%\\spool\\PRTPROCS\\w32x86"
2193 *| NT(Windows 4.0): "%winsysdir%\\spool\\PRTPROCS\\win40"
2195 * "%winsysdir%" is the Value from GetSystemDirectoryW()
2198 static BOOL WINAPI fpGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD level,
2199 LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded)
2201 const printenv_t * env;
2202 DWORD needed;
2203 LONG lres;
2205 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), debugstr_w(pEnvironment),
2206 level, pPPInfo, cbBuf, pcbNeeded);
2208 *pcbNeeded = 0;
2209 lres = copy_servername_from_name(pName, NULL);
2210 if (lres) {
2211 FIXME("server %s not supported\n", debugstr_w(pName));
2212 SetLastError(RPC_S_SERVER_UNAVAILABLE);
2213 return FALSE;
2216 env = validate_envW(pEnvironment);
2217 if (!env)
2218 return FALSE; /* ERROR_INVALID_ENVIRONMENT */
2220 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2221 needed = GetSystemDirectoryW(NULL, 0);
2222 /* add the Size for the Subdirectories */
2223 needed += lstrlenW(spoolprtprocsW);
2224 needed += lstrlenW(env->subdir);
2225 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
2227 *pcbNeeded = needed;
2229 if (needed > cbBuf) {
2230 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2231 return FALSE;
2234 GetSystemDirectoryW((LPWSTR) pPPInfo, cbBuf/sizeof(WCHAR));
2235 /* add the Subdirectories */
2236 lstrcatW((LPWSTR) pPPInfo, spoolprtprocsW);
2237 lstrcatW((LPWSTR) pPPInfo, env->subdir);
2238 TRACE("==> %s\n", debugstr_w((LPWSTR) pPPInfo));
2239 return TRUE;
2242 /******************************************************************************
2243 * fpOpenPrinter [exported through PRINTPROVIDOR]
2245 * Open a Printer / Printserver or a Printer-Object
2247 * PARAMS
2248 * lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
2249 * pPrinter [O] The resulting Handle is stored here
2250 * pDefaults [I] PTR to Default Printer Settings or NULL
2252 * RETURNS
2253 * Success: TRUE
2254 * Failure: FALSE
2256 * NOTES
2257 * lpPrinterName is one of:
2258 *| Printserver (NT only): "Servername" or NULL for the local Printserver
2259 *| Printer: "PrinterName"
2260 *| Printer-Object: "PrinterName,Job xxx"
2261 *| XcvMonitor: "Servername,XcvMonitor MonitorName"
2262 *| XcvPort: "Servername,XcvPort PortName"
2266 static BOOL WINAPI fpOpenPrinter(LPWSTR lpPrinterName, HANDLE *pPrinter,
2267 LPPRINTER_DEFAULTSW pDefaults)
2270 TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), pPrinter, pDefaults);
2272 *pPrinter = printer_alloc_handle(lpPrinterName, pDefaults);
2274 return (*pPrinter != 0);
2277 /******************************************************************************
2278 * fpXcvData [exported through PRINTPROVIDOR]
2280 * Execute commands in the Printmonitor DLL
2282 * PARAMS
2283 * hXcv [i] Handle from fpOpenPrinter (with XcvMonitor or XcvPort)
2284 * pszDataName [i] Name of the command to execute
2285 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
2286 * cbInputData [i] Size in Bytes of Buffer at pInputData
2287 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
2288 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
2289 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
2290 * pdwStatus [o] PTR to receive the win32 error code from the Printmonitor DLL
2292 * RETURNS
2293 * Success: TRUE
2294 * Failure: FALSE
2296 * NOTES
2297 * Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
2298 * The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
2300 * Minimal List of commands, that a Printmonitor DLL should support:
2302 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
2303 *| "AddPort" : Add a Port
2304 *| "DeletePort": Delete a Port
2306 * Many Printmonitors support additional commands. Examples for localspl.dll:
2307 * "GetDefaultCommConfig", "SetDefaultCommConfig",
2308 * "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
2311 static BOOL WINAPI fpXcvData(HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
2312 DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
2313 PDWORD pcbOutputNeeded, PDWORD pdwStatus)
2315 printer_t *printer = (printer_t * ) hXcv;
2317 TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
2318 pInputData, cbInputData, pOutputData,
2319 cbOutputData, pcbOutputNeeded, pdwStatus);
2321 if (!printer || (!printer->hXcv)) {
2322 SetLastError(ERROR_INVALID_HANDLE);
2323 return FALSE;
2326 if (!pcbOutputNeeded) {
2327 SetLastError(ERROR_INVALID_PARAMETER);
2328 return FALSE;
2331 if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
2332 SetLastError(RPC_X_NULL_REF_POINTER);
2333 return FALSE;
2336 *pcbOutputNeeded = 0;
2338 *pdwStatus = printer->pm->monitor->pfnXcvDataPort(printer->hXcv, pszDataName,
2339 pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
2341 return TRUE;
2344 /*****************************************************
2345 * setup_provider [internal]
2347 void setup_provider(void)
2349 static const PRINTPROVIDOR backend = {
2350 fpOpenPrinter,
2351 NULL, /* fpSetJob */
2352 NULL, /* fpGetJob */
2353 NULL, /* fpEnumJobs */
2354 NULL, /* fpAddPrinter */
2355 NULL, /* fpDeletePrinter */
2356 NULL, /* fpSetPrinter */
2357 NULL, /* fpGetPrinter */
2358 NULL, /* fpEnumPrinters */
2359 NULL, /* fpAddPrinterDriver */
2360 NULL, /* fpEnumPrinterDrivers */
2361 NULL, /* fpGetPrinterDriver */
2362 fpGetPrinterDriverDirectory,
2363 NULL, /* fpDeletePrinterDriver */
2364 NULL, /* fpAddPrintProcessor */
2365 fpEnumPrintProcessors,
2366 fpGetPrintProcessorDirectory,
2367 NULL, /* fpDeletePrintProcessor */
2368 NULL, /* fpEnumPrintProcessorDatatypes */
2369 NULL, /* fpStartDocPrinter */
2370 NULL, /* fpStartPagePrinter */
2371 NULL, /* fpWritePrinter */
2372 NULL, /* fpEndPagePrinter */
2373 NULL, /* fpAbortPrinter */
2374 NULL, /* fpReadPrinter */
2375 NULL, /* fpEndDocPrinter */
2376 NULL, /* fpAddJob */
2377 NULL, /* fpScheduleJob */
2378 NULL, /* fpGetPrinterData */
2379 NULL, /* fpSetPrinterData */
2380 NULL, /* fpWaitForPrinterChange */
2381 fpClosePrinter,
2382 NULL, /* fpAddForm */
2383 NULL, /* fpDeleteForm */
2384 NULL, /* fpGetForm */
2385 NULL, /* fpSetForm */
2386 NULL, /* fpEnumForms */
2387 fpEnumMonitors,
2388 fpEnumPorts,
2389 fpAddPort,
2390 fpConfigurePort,
2391 fpDeletePort,
2392 NULL, /* fpCreatePrinterIC */
2393 NULL, /* fpPlayGdiScriptOnPrinterIC */
2394 NULL, /* fpDeletePrinterIC */
2395 NULL, /* fpAddPrinterConnection */
2396 NULL, /* fpDeletePrinterConnection */
2397 NULL, /* fpPrinterMessageBox */
2398 fpAddMonitor,
2399 fpDeleteMonitor,
2400 NULL, /* fpResetPrinter */
2401 NULL, /* fpGetPrinterDriverEx */
2402 NULL, /* fpFindFirstPrinterChangeNotification */
2403 NULL, /* fpFindClosePrinterChangeNotification */
2404 fpAddPortEx,
2405 NULL, /* fpShutDown */
2406 NULL, /* fpRefreshPrinterChangeNotification */
2407 NULL, /* fpOpenPrinterEx */
2408 NULL, /* fpAddPrinterEx */
2409 NULL, /* fpSetPort */
2410 NULL, /* fpEnumPrinterData */
2411 NULL, /* fpDeletePrinterData */
2412 NULL, /* fpClusterSplOpen */
2413 NULL, /* fpClusterSplClose */
2414 NULL, /* fpClusterSplIsAlive */
2415 NULL, /* fpSetPrinterDataEx */
2416 NULL, /* fpGetPrinterDataEx */
2417 NULL, /* fpEnumPrinterDataEx */
2418 NULL, /* fpEnumPrinterKey */
2419 NULL, /* fpDeletePrinterDataEx */
2420 NULL, /* fpDeletePrinterKey */
2421 NULL, /* fpSeekPrinter */
2422 NULL, /* fpDeletePrinterDriverEx */
2423 NULL, /* fpAddPerMachineConnection */
2424 NULL, /* fpDeletePerMachineConnection */
2425 NULL, /* fpEnumPerMachineConnections */
2426 fpXcvData,
2427 fpAddPrinterDriverEx,
2428 NULL, /* fpSplReadPrinter */
2429 NULL, /* fpDriverUnloadComplete */
2430 NULL, /* fpGetSpoolFileInfo */
2431 NULL, /* fpCommitSpoolData */
2432 NULL, /* fpCloseSpoolFileHandle */
2433 NULL, /* fpFlushPrinter */
2434 NULL, /* fpSendRecvBidiData */
2435 NULL /* fpAddDriverCatalog */
2437 pprovider = &backend;
2441 /*****************************************************
2442 * InitializePrintProvidor (localspl.@)
2444 * Initialize the Printprovider
2446 * PARAMS
2447 * pPrintProvidor [I] Buffer to fill with a struct PRINTPROVIDOR
2448 * cbPrintProvidor [I] Size of Buffer in Bytes
2449 * pFullRegistryPath [I] Registry-Path for the Printprovidor
2451 * RETURNS
2452 * Success: TRUE and pPrintProvidor filled
2453 * Failure: FALSE
2455 * NOTES
2456 * The RegistryPath should be:
2457 * "System\CurrentControlSet\Control\Print\Providers\<providername>",
2458 * but this Parameter is ignored in "localspl.dll".
2462 BOOL WINAPI InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor,
2463 DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
2466 TRACE("(%p, %u, %s)\n", pPrintProvidor, cbPrintProvidor, debugstr_w(pFullRegistryPath));
2467 memcpy(pPrintProvidor, pprovider,
2468 (cbPrintProvidor < sizeof(PRINTPROVIDOR)) ? cbPrintProvidor : sizeof(PRINTPROVIDOR));
2470 return TRUE;