include: Use the hard-float calling convention for Windows APIs on ARM
[wine.git] / dlls / localspl / provider.c
blob4f6bac376ebe1440339e7c83cc84978b38a87513
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 const printenv_t * env;
1469 monitor_t * pm = NULL;
1470 LPMONITOR_INFO_2W mi2w;
1471 HKEY hroot = NULL;
1472 HKEY hentry = NULL;
1473 DWORD disposition;
1474 BOOL res = FALSE;
1476 mi2w = (LPMONITOR_INFO_2W) pMonitors;
1477 TRACE("(%s, %d, %p): %s %s %s\n", debugstr_w(pName), Level, pMonitors,
1478 debugstr_w(mi2w ? mi2w->pName : NULL),
1479 debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
1480 debugstr_w(mi2w ? mi2w->pDLLName : NULL));
1482 if (copy_servername_from_name(pName, NULL)) {
1483 FIXME("server %s not supported\n", debugstr_w(pName));
1484 SetLastError(ERROR_ACCESS_DENIED);
1485 return FALSE;
1488 if (!mi2w->pName || (! mi2w->pName[0])) {
1489 WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
1490 SetLastError(ERROR_INVALID_PARAMETER);
1491 return FALSE;
1494 env = validate_envW(mi2w->pEnvironment);
1495 if (!env)
1496 return FALSE; /* ERROR_INVALID_ENVIRONMENT */
1498 if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
1499 WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
1500 SetLastError(ERROR_INVALID_PARAMETER);
1501 return FALSE;
1504 /* Load and initialize the monitor. SetLastError() is called on failure */
1505 if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
1506 return FALSE;
1508 monitor_unload(pm);
1510 SetLastError(ERROR_SUCCESS); /* Monitor installer depends on this */
1512 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1513 ERR("unable to create key %s\n", debugstr_w(monitorsW));
1514 return FALSE;
1517 if (RegCreateKeyExW(hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
1518 KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
1519 &disposition) == ERROR_SUCCESS) {
1521 /* Some installers set options for the port before calling AddMonitor.
1522 We query the "Driver" entry to verify that the monitor is installed,
1523 before we return an error.
1524 When a user installs two print monitors at the same time with the
1525 same name, a race condition is possible but silently ignored. */
1527 DWORD namesize = 0;
1529 if ((disposition == REG_OPENED_EXISTING_KEY) &&
1530 (RegQueryValueExW(hentry, driverW, NULL, NULL, NULL,
1531 &namesize) == ERROR_SUCCESS)) {
1532 TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
1533 /* 9x use ERROR_ALREADY_EXISTS */
1534 SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
1536 else
1538 INT len;
1539 len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
1540 res = (RegSetValueExW(hentry, driverW, 0, REG_SZ,
1541 (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
1543 RegCloseKey(hentry);
1546 RegCloseKey(hroot);
1547 return (res);
1550 /******************************************************************************
1551 * fpAddPort [exported through PRINTPROVIDOR]
1553 * Add a Port for a specific Monitor
1555 * PARAMS
1556 * pName [I] Servername or NULL (local Computer)
1557 * hWnd [I] Handle to parent Window for the Dialog-Box
1558 * pMonitorName [I] Name of the Monitor that manage the Port
1560 * RETURNS
1561 * Success: TRUE
1562 * Failure: FALSE
1565 static BOOL WINAPI fpAddPort(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
1567 monitor_t * pm;
1568 monitor_t * pui;
1569 LONG lres;
1570 DWORD res;
1572 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
1574 lres = copy_servername_from_name(pName, NULL);
1575 if (lres) {
1576 FIXME("server %s not supported\n", debugstr_w(pName));
1577 SetLastError(ERROR_INVALID_PARAMETER);
1578 return FALSE;
1581 /* an empty Monitorname is Invalid */
1582 if (!pMonitorName[0]) {
1583 SetLastError(ERROR_NOT_SUPPORTED);
1584 return FALSE;
1587 pm = monitor_load(pMonitorName, NULL);
1588 if (pm && pm->monitor && pm->monitor->pfnAddPort) {
1589 res = pm->monitor->pfnAddPort(pName, hWnd, pMonitorName);
1590 TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pm->dllname));
1592 else
1594 pui = monitor_loadui(pm);
1595 if (pui && pui->monitorUI && pui->monitorUI->pfnAddPortUI) {
1596 res = pui->monitorUI->pfnAddPortUI(pName, hWnd, pMonitorName, NULL);
1597 TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pui->dllname));
1599 else
1601 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1602 debugstr_w(pMonitorName), pm, debugstr_w(pm ? pm->dllname : NULL),
1603 pui, debugstr_w(pui ? pui->dllname : NULL));
1605 SetLastError(ERROR_NOT_SUPPORTED);
1606 res = FALSE;
1608 monitor_unload(pui);
1610 monitor_unload(pm);
1612 TRACE("returning %d with %u\n", res, GetLastError());
1613 return res;
1616 /******************************************************************************
1617 * fpAddPortEx [exported through PRINTPROVIDOR]
1619 * Add a Port for a specific Monitor, without presenting a user interface
1621 * PARAMS
1622 * pName [I] Servername or NULL (local Computer)
1623 * level [I] Structure-Level (1 or 2) for pBuffer
1624 * pBuffer [I] PTR to: PORT_INFO_1 or PORT_INFO_2
1625 * pMonitorName [I] Name of the Monitor that manage the Port
1627 * RETURNS
1628 * Success: TRUE
1629 * Failure: FALSE
1632 static BOOL WINAPI fpAddPortEx(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
1634 PORT_INFO_2W * pi2;
1635 monitor_t * pm;
1636 DWORD lres;
1637 DWORD res;
1639 pi2 = (PORT_INFO_2W *) pBuffer;
1641 TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
1642 debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
1643 debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
1644 debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
1646 lres = copy_servername_from_name(pName, NULL);
1647 if (lres) {
1648 FIXME("server %s not supported\n", debugstr_w(pName));
1649 SetLastError(ERROR_INVALID_PARAMETER);
1650 return FALSE;
1653 if ((level < 1) || (level > 2)) {
1654 SetLastError(ERROR_INVALID_LEVEL);
1655 return FALSE;
1658 if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
1659 SetLastError(ERROR_INVALID_PARAMETER);
1660 return FALSE;
1663 /* load the Monitor */
1664 pm = monitor_load(pMonitorName, NULL);
1665 if (pm && pm->monitor && pm->monitor->pfnAddPortEx) {
1666 res = pm->monitor->pfnAddPortEx(pName, level, pBuffer, pMonitorName);
1667 TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pm->dllname));
1669 else
1671 FIXME("not implemented for %s (monitor %p: %s)\n",
1672 debugstr_w(pMonitorName), pm, pm ? debugstr_w(pm->dllname) : "(null)");
1673 SetLastError(ERROR_INVALID_PARAMETER);
1674 res = FALSE;
1676 monitor_unload(pm);
1677 return res;
1680 /******************************************************************************
1681 * fpAddPrinterDriverEx [exported through PRINTPROVIDOR]
1683 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1685 * PARAMS
1686 * pName [I] Servername or NULL (local Computer)
1687 * level [I] Level for the supplied DRIVER_INFO_*W struct
1688 * pDriverInfo [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
1689 * dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
1691 * RESULTS
1692 * Success: TRUE
1693 * Failure: FALSE
1696 static BOOL WINAPI fpAddPrinterDriverEx(LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
1698 LONG lres;
1700 TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
1701 lres = copy_servername_from_name(pName, NULL);
1702 if (lres) {
1703 FIXME("server %s not supported\n", debugstr_w(pName));
1704 SetLastError(ERROR_ACCESS_DENIED);
1705 return FALSE;
1708 if ((dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY) != APD_COPY_ALL_FILES) {
1709 TRACE("Flags 0x%x ignored (using APD_COPY_ALL_FILES)\n", dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY);
1712 return myAddPrinterDriverEx(level, pDriverInfo, dwFileCopyFlags, TRUE);
1715 /******************************************************************************
1716 * fpClosePrinter [exported through PRINTPROVIDOR]
1718 * Close a printer handle and free associated resources
1720 * PARAMS
1721 * hPrinter [I] Printerhandle to close
1723 * RESULTS
1724 * Success: TRUE
1725 * Failure: FALSE
1728 static BOOL WINAPI fpClosePrinter(HANDLE hPrinter)
1730 printer_t *printer = (printer_t *) hPrinter;
1732 TRACE("(%p)\n", hPrinter);
1734 if (printer) {
1735 printer_free(printer);
1736 return TRUE;
1738 return FALSE;
1741 /******************************************************************************
1742 * fpConfigurePort [exported through PRINTPROVIDOR]
1744 * Display the Configuration-Dialog for a specific Port
1746 * PARAMS
1747 * pName [I] Servername or NULL (local Computer)
1748 * hWnd [I] Handle to parent Window for the Dialog-Box
1749 * pPortName [I] Name of the Port, that should be configured
1751 * RETURNS
1752 * Success: TRUE
1753 * Failure: FALSE
1756 static BOOL WINAPI fpConfigurePort(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1758 monitor_t * pm;
1759 monitor_t * pui;
1760 LONG lres;
1761 DWORD res;
1763 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
1765 lres = copy_servername_from_name(pName, NULL);
1766 if (lres) {
1767 FIXME("server %s not supported\n", debugstr_w(pName));
1768 SetLastError(ERROR_INVALID_NAME);
1769 return FALSE;
1772 /* an empty Portname is Invalid, but can popup a Dialog */
1773 if (!pPortName[0]) {
1774 SetLastError(ERROR_NOT_SUPPORTED);
1775 return FALSE;
1778 pm = monitor_load_by_port(pPortName);
1779 if (pm && pm->monitor && pm->monitor->pfnConfigurePort) {
1780 TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm->name),
1781 debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
1782 res = pm->monitor->pfnConfigurePort(pName, hWnd, pPortName);
1783 TRACE("got %d with %u\n", res, GetLastError());
1785 else
1787 pui = monitor_loadui(pm);
1788 if (pui && pui->monitorUI && pui->monitorUI->pfnConfigurePortUI) {
1789 TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui->name),
1790 debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
1791 res = pui->monitorUI->pfnConfigurePortUI(pName, hWnd, pPortName);
1792 TRACE("got %d with %u\n", res, GetLastError());
1794 else
1796 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1797 debugstr_w(pPortName), pm, debugstr_w(pm ? pm->dllname : NULL),
1798 pui, debugstr_w(pui ? pui->dllname : NULL));
1800 SetLastError(ERROR_NOT_SUPPORTED);
1801 res = FALSE;
1803 monitor_unload(pui);
1805 monitor_unload(pm);
1807 TRACE("returning %d with %u\n", res, GetLastError());
1808 return res;
1811 /******************************************************************
1812 * fpDeleteMonitor [exported through PRINTPROVIDOR]
1814 * Delete a specific Printmonitor from a Printing-Environment
1816 * PARAMS
1817 * pName [I] Servername or NULL (local Computer)
1818 * pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1819 * pMonitorName [I] Name of the Monitor, that should be deleted
1821 * RETURNS
1822 * Success: TRUE
1823 * Failure: FALSE
1825 * NOTES
1826 * pEnvironment is ignored in Windows for the local Computer.
1830 static BOOL WINAPI fpDeleteMonitor(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1832 HKEY hroot = NULL;
1833 LONG lres;
1835 TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
1836 debugstr_w(pMonitorName));
1838 lres = copy_servername_from_name(pName, NULL);
1839 if (lres) {
1840 FIXME("server %s not supported\n", debugstr_w(pName));
1841 SetLastError(ERROR_INVALID_NAME);
1842 return FALSE;
1845 /* pEnvironment is ignored in Windows for the local Computer */
1846 if (!pMonitorName || !pMonitorName[0]) {
1847 TRACE("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
1848 SetLastError(ERROR_INVALID_PARAMETER);
1849 return FALSE;
1852 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1853 ERR("unable to create key %s\n", debugstr_w(monitorsW));
1854 return FALSE;
1857 if(RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS) {
1858 TRACE("%s deleted\n", debugstr_w(pMonitorName));
1859 RegCloseKey(hroot);
1860 return TRUE;
1863 TRACE("%s does not exist\n", debugstr_w(pMonitorName));
1864 RegCloseKey(hroot);
1866 /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
1867 SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
1868 return FALSE;
1871 /*****************************************************************************
1872 * fpDeletePort [exported through PRINTPROVIDOR]
1874 * Delete a specific Port
1876 * PARAMS
1877 * pName [I] Servername or NULL (local Computer)
1878 * hWnd [I] Handle to parent Window for the Dialog-Box
1879 * pPortName [I] Name of the Port, that should be deleted
1881 * RETURNS
1882 * Success: TRUE
1883 * Failure: FALSE
1886 static BOOL WINAPI fpDeletePort(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
1888 monitor_t * pm;
1889 monitor_t * pui;
1890 LONG lres;
1891 DWORD res;
1893 TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
1895 lres = copy_servername_from_name(pName, NULL);
1896 if (lres) {
1897 FIXME("server %s not supported\n", debugstr_w(pName));
1898 SetLastError(ERROR_INVALID_NAME);
1899 return FALSE;
1902 /* an empty Portname is Invalid */
1903 if (!pPortName[0]) {
1904 SetLastError(ERROR_NOT_SUPPORTED);
1905 return FALSE;
1908 pm = monitor_load_by_port(pPortName);
1909 if (pm && pm->monitor && pm->monitor->pfnDeletePort) {
1910 TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm->name),
1911 debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
1912 res = pm->monitor->pfnDeletePort(pName, hWnd, pPortName);
1913 TRACE("got %d with %u\n", res, GetLastError());
1915 else
1917 pui = monitor_loadui(pm);
1918 if (pui && pui->monitorUI && pui->monitorUI->pfnDeletePortUI) {
1919 TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui->name),
1920 debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
1921 res = pui->monitorUI->pfnDeletePortUI(pName, hWnd, pPortName);
1922 TRACE("got %d with %u\n", res, GetLastError());
1924 else
1926 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1927 debugstr_w(pPortName), pm, debugstr_w(pm ? pm->dllname : NULL),
1928 pui, debugstr_w(pui ? pui->dllname : NULL));
1930 SetLastError(ERROR_NOT_SUPPORTED);
1931 res = FALSE;
1933 monitor_unload(pui);
1935 monitor_unload(pm);
1937 TRACE("returning %d with %u\n", res, GetLastError());
1938 return res;
1941 /*****************************************************************************
1942 * fpEnumMonitors [exported through PRINTPROVIDOR]
1944 * Enumerate available Port-Monitors
1946 * PARAMS
1947 * pName [I] Servername or NULL (local Computer)
1948 * Level [I] Structure-Level (1:Win9x+NT or 2:NT only)
1949 * pMonitors [O] PTR to Buffer that receives the Result
1950 * cbBuf [I] Size of Buffer at pMonitors
1951 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
1952 * pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
1954 * RETURNS
1955 * Success: TRUE
1956 * Failure: FALSE and in pcbNeeded the Bytes required for pMonitors, if cbBuf is too small
1958 * NOTES
1959 * Windows reads the Registry once and cache the Results.
1962 static BOOL WINAPI fpEnumMonitors(LPWSTR pName, DWORD Level, LPBYTE pMonitors, DWORD cbBuf,
1963 LPDWORD pcbNeeded, LPDWORD pcReturned)
1965 DWORD numentries = 0;
1966 DWORD needed = 0;
1967 LONG lres;
1968 BOOL res = FALSE;
1970 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
1971 cbBuf, pcbNeeded, pcReturned);
1973 lres = copy_servername_from_name(pName, NULL);
1974 if (lres) {
1975 FIXME("server %s not supported\n", debugstr_w(pName));
1976 SetLastError(ERROR_INVALID_NAME);
1977 goto em_cleanup;
1980 if (!Level || (Level > 2)) {
1981 WARN("level (%d) is ignored in win9x\n", Level);
1982 SetLastError(ERROR_INVALID_LEVEL);
1983 return FALSE;
1986 /* Scan all Monitor-Keys */
1987 numentries = 0;
1988 needed = get_local_monitors(Level, NULL, 0, &numentries);
1990 /* we calculated the needed buffersize. now do more error-checks */
1991 if (cbBuf < needed) {
1992 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1993 goto em_cleanup;
1996 /* fill the Buffer with the Monitor-Keys */
1997 needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
1998 res = TRUE;
2000 em_cleanup:
2001 if (pcbNeeded) *pcbNeeded = needed;
2002 if (pcReturned) *pcReturned = numentries;
2004 TRACE("returning %d with %d (%d byte for %d entries)\n",
2005 res, GetLastError(), needed, numentries);
2007 return (res);
2010 /******************************************************************************
2011 * fpEnumPorts [exported through PRINTPROVIDOR]
2013 * Enumerate available Ports
2015 * PARAMS
2016 * pName [I] Servername or NULL (local Computer)
2017 * Level [I] Structure-Level (1 or 2)
2018 * pPorts [O] PTR to Buffer that receives the Result
2019 * cbBuf [I] Size of Buffer at pPorts
2020 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
2021 * pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
2023 * RETURNS
2024 * Success: TRUE
2025 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
2028 static BOOL WINAPI fpEnumPorts(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
2029 LPDWORD pcbNeeded, LPDWORD pcReturned)
2031 DWORD needed = 0;
2032 DWORD numentries = 0;
2033 LONG lres;
2034 BOOL res = FALSE;
2036 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
2037 cbBuf, pcbNeeded, pcReturned);
2039 lres = copy_servername_from_name(pName, NULL);
2040 if (lres) {
2041 FIXME("server %s not supported\n", debugstr_w(pName));
2042 SetLastError(ERROR_INVALID_NAME);
2043 goto emP_cleanup;
2046 if (!Level || (Level > 2)) {
2047 SetLastError(ERROR_INVALID_LEVEL);
2048 goto emP_cleanup;
2051 if (!pcbNeeded || (!pPorts && (cbBuf > 0))) {
2052 SetLastError(RPC_X_NULL_REF_POINTER);
2053 goto emP_cleanup;
2056 EnterCriticalSection(&monitor_handles_cs);
2057 monitor_loadall();
2059 /* Scan all local Ports */
2060 numentries = 0;
2061 needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
2063 /* we calculated the needed buffersize. now do the error-checks */
2064 if (cbBuf < needed) {
2065 monitor_unloadall();
2066 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2067 goto emP_cleanup_cs;
2069 else if (!pPorts || !pcReturned) {
2070 monitor_unloadall();
2071 SetLastError(RPC_X_NULL_REF_POINTER);
2072 goto emP_cleanup_cs;
2075 /* Fill the Buffer */
2076 needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
2077 res = TRUE;
2078 monitor_unloadall();
2080 emP_cleanup_cs:
2081 LeaveCriticalSection(&monitor_handles_cs);
2083 emP_cleanup:
2084 if (pcbNeeded) *pcbNeeded = needed;
2085 if (pcReturned) *pcReturned = (res) ? numentries : 0;
2087 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
2088 (res), GetLastError(), needed, (res) ? numentries : 0, numentries);
2090 return (res);
2093 /*****************************************************************************
2094 * fpEnumPrintProcessors [exported through PRINTPROVIDOR]
2096 * Enumerate available Print Processors
2098 * PARAMS
2099 * pName [I] Servername or NULL (local Computer)
2100 * pEnvironment [I] Printing-Environment or NULL (Default)
2101 * Level [I] Structure-Level (Only 1 is allowed)
2102 * pPPInfo [O] PTR to Buffer that receives the Result
2103 * cbBuf [I] Size of Buffer at pMonitors
2104 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPPInfo
2105 * pcReturned [O] PTR to DWORD that receives the number of Print Processors in pPPInfo
2107 * RETURNS
2108 * Success: TRUE
2109 * Failure: FALSE and in pcbNeeded the Bytes required for pPPInfo, if cbBuf is too small
2112 static BOOL WINAPI fpEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
2113 LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
2115 const printenv_t * env;
2116 LPWSTR regpathW = NULL;
2117 DWORD numentries = 0;
2118 DWORD needed = 0;
2119 LONG lres;
2120 BOOL res = FALSE;
2122 TRACE("(%s, %s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), debugstr_w(pEnvironment),
2123 Level, pPPInfo, cbBuf, pcbNeeded, pcReturned);
2125 lres = copy_servername_from_name(pName, NULL);
2126 if (lres) {
2127 FIXME("server %s not supported\n", debugstr_w(pName));
2128 SetLastError(ERROR_INVALID_NAME);
2129 goto epp_cleanup;
2132 if (Level != 1) {
2133 SetLastError(ERROR_INVALID_LEVEL);
2134 goto epp_cleanup;
2137 env = validate_envW(pEnvironment);
2138 if (!env)
2139 goto epp_cleanup; /* ERROR_INVALID_ENVIRONMENT */
2141 regpathW = heap_alloc(sizeof(fmt_printprocessorsW) +
2142 (lstrlenW(env->envname) * sizeof(WCHAR)));
2144 if (!regpathW)
2145 goto epp_cleanup;
2147 wsprintfW(regpathW, fmt_printprocessorsW, env->envname);
2149 /* Scan all Printprocessor-Keys */
2150 numentries = 0;
2151 needed = get_local_printprocessors(regpathW, NULL, 0, &numentries);
2153 /* we calculated the needed buffersize. now do more error-checks */
2154 if (cbBuf < needed) {
2155 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2156 goto epp_cleanup;
2159 /* fill the Buffer with the Printprocessor Infos */
2160 needed = get_local_printprocessors(regpathW, pPPInfo, cbBuf, &numentries);
2161 res = TRUE;
2163 epp_cleanup:
2164 heap_free(regpathW);
2165 if (pcbNeeded) *pcbNeeded = needed;
2166 if (pcReturned) *pcReturned = numentries;
2168 TRACE("returning %d with %d (%d byte for %d entries)\n",
2169 res, GetLastError(), needed, numentries);
2171 return (res);
2174 /******************************************************************************
2175 * fpGetPrintProcessorDirectory [exported through PRINTPROVIDOR]
2177 * Return the PATH for the Print-Processors
2179 * PARAMS
2180 * pName [I] Servername or NULL (this computer)
2181 * pEnvironment [I] Printing-Environment or NULL (Default)
2182 * level [I] Structure-Level (must be 1)
2183 * pPPInfo [O] PTR to Buffer that receives the Result
2184 * cbBuf [I] Size of Buffer at pPPInfo
2185 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPPInfo
2187 * RETURNS
2188 * Success: TRUE
2189 * Failure: FALSE and in pcbNeeded the Bytes required for pPPInfo, if cbBuf is too small
2191 * Native Values returned in pPPInfo on Success for this computer:
2192 *| NT(Windows x64): "%winsysdir%\\spool\\PRTPROCS\\x64"
2193 *| NT(Windows NT x86): "%winsysdir%\\spool\\PRTPROCS\\w32x86"
2194 *| NT(Windows 4.0): "%winsysdir%\\spool\\PRTPROCS\\win40"
2196 * "%winsysdir%" is the Value from GetSystemDirectoryW()
2199 static BOOL WINAPI fpGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD level,
2200 LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded)
2202 const printenv_t * env;
2203 DWORD needed;
2204 LONG lres;
2206 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), debugstr_w(pEnvironment),
2207 level, pPPInfo, cbBuf, pcbNeeded);
2209 *pcbNeeded = 0;
2210 lres = copy_servername_from_name(pName, NULL);
2211 if (lres) {
2212 FIXME("server %s not supported\n", debugstr_w(pName));
2213 SetLastError(RPC_S_SERVER_UNAVAILABLE);
2214 return FALSE;
2217 env = validate_envW(pEnvironment);
2218 if (!env)
2219 return FALSE; /* ERROR_INVALID_ENVIRONMENT */
2221 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
2222 needed = GetSystemDirectoryW(NULL, 0);
2223 /* add the Size for the Subdirectories */
2224 needed += lstrlenW(spoolprtprocsW);
2225 needed += lstrlenW(env->subdir);
2226 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
2228 *pcbNeeded = needed;
2230 if (needed > cbBuf) {
2231 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2232 return FALSE;
2235 GetSystemDirectoryW((LPWSTR) pPPInfo, cbBuf/sizeof(WCHAR));
2236 /* add the Subdirectories */
2237 lstrcatW((LPWSTR) pPPInfo, spoolprtprocsW);
2238 lstrcatW((LPWSTR) pPPInfo, env->subdir);
2239 TRACE("==> %s\n", debugstr_w((LPWSTR) pPPInfo));
2240 return TRUE;
2243 /******************************************************************************
2244 * fpOpenPrinter [exported through PRINTPROVIDOR]
2246 * Open a Printer / Printserver or a Printer-Object
2248 * PARAMS
2249 * lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
2250 * pPrinter [O] The resulting Handle is stored here
2251 * pDefaults [I] PTR to Default Printer Settings or NULL
2253 * RETURNS
2254 * Success: TRUE
2255 * Failure: FALSE
2257 * NOTES
2258 * lpPrinterName is one of:
2259 *| Printserver (NT only): "Servername" or NULL for the local Printserver
2260 *| Printer: "PrinterName"
2261 *| Printer-Object: "PrinterName,Job xxx"
2262 *| XcvMonitor: "Servername,XcvMonitor MonitorName"
2263 *| XcvPort: "Servername,XcvPort PortName"
2267 static BOOL WINAPI fpOpenPrinter(LPWSTR lpPrinterName, HANDLE *pPrinter,
2268 LPPRINTER_DEFAULTSW pDefaults)
2271 TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), pPrinter, pDefaults);
2273 *pPrinter = printer_alloc_handle(lpPrinterName, pDefaults);
2275 return (*pPrinter != 0);
2278 /******************************************************************************
2279 * fpXcvData [exported through PRINTPROVIDOR]
2281 * Execute commands in the Printmonitor DLL
2283 * PARAMS
2284 * hXcv [i] Handle from fpOpenPrinter (with XcvMonitor or XcvPort)
2285 * pszDataName [i] Name of the command to execute
2286 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
2287 * cbInputData [i] Size in Bytes of Buffer at pInputData
2288 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
2289 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
2290 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
2291 * pdwStatus [o] PTR to receive the win32 error code from the Printmonitor DLL
2293 * RETURNS
2294 * Success: TRUE
2295 * Failure: FALSE
2297 * NOTES
2298 * Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
2299 * The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
2301 * Minimal List of commands, that a Printmonitor DLL should support:
2303 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
2304 *| "AddPort" : Add a Port
2305 *| "DeletePort": Delete a Port
2307 * Many Printmonitors support additional commands. Examples for localspl.dll:
2308 * "GetDefaultCommConfig", "SetDefaultCommConfig",
2309 * "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
2312 static BOOL WINAPI fpXcvData(HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
2313 DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
2314 PDWORD pcbOutputNeeded, PDWORD pdwStatus)
2316 printer_t *printer = (printer_t * ) hXcv;
2318 TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
2319 pInputData, cbInputData, pOutputData,
2320 cbOutputData, pcbOutputNeeded, pdwStatus);
2322 if (!printer || (!printer->hXcv)) {
2323 SetLastError(ERROR_INVALID_HANDLE);
2324 return FALSE;
2327 if (!pcbOutputNeeded) {
2328 SetLastError(ERROR_INVALID_PARAMETER);
2329 return FALSE;
2332 if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
2333 SetLastError(RPC_X_NULL_REF_POINTER);
2334 return FALSE;
2337 *pcbOutputNeeded = 0;
2339 *pdwStatus = printer->pm->monitor->pfnXcvDataPort(printer->hXcv, pszDataName,
2340 pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
2342 return TRUE;
2345 /*****************************************************
2346 * setup_provider [internal]
2348 void setup_provider(void)
2350 static const PRINTPROVIDOR backend = {
2351 fpOpenPrinter,
2352 NULL, /* fpSetJob */
2353 NULL, /* fpGetJob */
2354 NULL, /* fpEnumJobs */
2355 NULL, /* fpAddPrinter */
2356 NULL, /* fpDeletePrinter */
2357 NULL, /* fpSetPrinter */
2358 NULL, /* fpGetPrinter */
2359 NULL, /* fpEnumPrinters */
2360 NULL, /* fpAddPrinterDriver */
2361 NULL, /* fpEnumPrinterDrivers */
2362 NULL, /* fpGetPrinterDriver */
2363 fpGetPrinterDriverDirectory,
2364 NULL, /* fpDeletePrinterDriver */
2365 NULL, /* fpAddPrintProcessor */
2366 fpEnumPrintProcessors,
2367 fpGetPrintProcessorDirectory,
2368 NULL, /* fpDeletePrintProcessor */
2369 NULL, /* fpEnumPrintProcessorDatatypes */
2370 NULL, /* fpStartDocPrinter */
2371 NULL, /* fpStartPagePrinter */
2372 NULL, /* fpWritePrinter */
2373 NULL, /* fpEndPagePrinter */
2374 NULL, /* fpAbortPrinter */
2375 NULL, /* fpReadPrinter */
2376 NULL, /* fpEndDocPrinter */
2377 NULL, /* fpAddJob */
2378 NULL, /* fpScheduleJob */
2379 NULL, /* fpGetPrinterData */
2380 NULL, /* fpSetPrinterData */
2381 NULL, /* fpWaitForPrinterChange */
2382 fpClosePrinter,
2383 NULL, /* fpAddForm */
2384 NULL, /* fpDeleteForm */
2385 NULL, /* fpGetForm */
2386 NULL, /* fpSetForm */
2387 NULL, /* fpEnumForms */
2388 fpEnumMonitors,
2389 fpEnumPorts,
2390 fpAddPort,
2391 fpConfigurePort,
2392 fpDeletePort,
2393 NULL, /* fpCreatePrinterIC */
2394 NULL, /* fpPlayGdiScriptOnPrinterIC */
2395 NULL, /* fpDeletePrinterIC */
2396 NULL, /* fpAddPrinterConnection */
2397 NULL, /* fpDeletePrinterConnection */
2398 NULL, /* fpPrinterMessageBox */
2399 fpAddMonitor,
2400 fpDeleteMonitor,
2401 NULL, /* fpResetPrinter */
2402 NULL, /* fpGetPrinterDriverEx */
2403 NULL, /* fpFindFirstPrinterChangeNotification */
2404 NULL, /* fpFindClosePrinterChangeNotification */
2405 fpAddPortEx,
2406 NULL, /* fpShutDown */
2407 NULL, /* fpRefreshPrinterChangeNotification */
2408 NULL, /* fpOpenPrinterEx */
2409 NULL, /* fpAddPrinterEx */
2410 NULL, /* fpSetPort */
2411 NULL, /* fpEnumPrinterData */
2412 NULL, /* fpDeletePrinterData */
2413 NULL, /* fpClusterSplOpen */
2414 NULL, /* fpClusterSplClose */
2415 NULL, /* fpClusterSplIsAlive */
2416 NULL, /* fpSetPrinterDataEx */
2417 NULL, /* fpGetPrinterDataEx */
2418 NULL, /* fpEnumPrinterDataEx */
2419 NULL, /* fpEnumPrinterKey */
2420 NULL, /* fpDeletePrinterDataEx */
2421 NULL, /* fpDeletePrinterKey */
2422 NULL, /* fpSeekPrinter */
2423 NULL, /* fpDeletePrinterDriverEx */
2424 NULL, /* fpAddPerMachineConnection */
2425 NULL, /* fpDeletePerMachineConnection */
2426 NULL, /* fpEnumPerMachineConnections */
2427 fpXcvData,
2428 fpAddPrinterDriverEx,
2429 NULL, /* fpSplReadPrinter */
2430 NULL, /* fpDriverUnloadComplete */
2431 NULL, /* fpGetSpoolFileInfo */
2432 NULL, /* fpCommitSpoolData */
2433 NULL, /* fpCloseSpoolFileHandle */
2434 NULL, /* fpFlushPrinter */
2435 NULL, /* fpSendRecvBidiData */
2436 NULL /* fpAddDriverCatalog */
2438 pprovider = &backend;
2442 /*****************************************************
2443 * InitializePrintProvidor (localspl.@)
2445 * Initialize the Printprovider
2447 * PARAMS
2448 * pPrintProvidor [I] Buffer to fill with a struct PRINTPROVIDOR
2449 * cbPrintProvidor [I] Size of Buffer in Bytes
2450 * pFullRegistryPath [I] Registry-Path for the Printprovidor
2452 * RETURNS
2453 * Success: TRUE and pPrintProvidor filled
2454 * Failure: FALSE
2456 * NOTES
2457 * The RegistryPath should be:
2458 * "System\CurrentControlSet\Control\Print\Providers\<providername>",
2459 * but this Parameter is ignored in "localspl.dll".
2463 BOOL WINAPI InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor,
2464 DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
2467 TRACE("(%p, %u, %s)\n", pPrintProvidor, cbPrintProvidor, debugstr_w(pFullRegistryPath));
2468 memcpy(pPrintProvidor, pprovider,
2469 (cbPrintProvidor < sizeof(PRINTPROVIDOR)) ? cbPrintProvidor : sizeof(PRINTPROVIDOR));
2471 return TRUE;