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
24 #define NONAMELESSUNION
32 #include "ddk/winddiui.h"
33 #include "ddk/winsplp.h"
35 #include "wine/list.h"
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
38 #include "localspl_private.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(localspl
);
42 /* ############################### */
44 static CRITICAL_SECTION monitor_handles_cs
;
45 static CRITICAL_SECTION_DEBUG monitor_handles_cs_debug
=
47 0, 0, &monitor_handles_cs
,
48 { &monitor_handles_cs_debug
.ProcessLocksList
, &monitor_handles_cs_debug
.ProcessLocksList
},
49 0, 0, { (DWORD_PTR
)(__FILE__
": monitor_handles_cs") }
51 static CRITICAL_SECTION monitor_handles_cs
= { &monitor_handles_cs_debug
, -1, 0, 0, 0, 0 };
53 /* ############################### */
56 WCHAR src
[MAX_PATH
+MAX_PATH
];
57 WCHAR dst
[MAX_PATH
+MAX_PATH
];
79 LPCWSTR versionregpath
;
80 LPCWSTR versionsubdir
;
90 /* ############################### */
92 static struct list monitor_handles
= LIST_INIT( monitor_handles
);
93 static monitor_t
* pm_localport
;
95 static const PRINTPROVIDOR
* pprovider
= NULL
;
97 static const WCHAR backslashW
[] = {'\\',0};
98 static const WCHAR bs_ports_bsW
[] = {'\\','P','o','r','t','s','\\',0};
99 static const WCHAR configuration_fileW
[] = {'C','o','n','f','i','g','u','r','a','t','i','o','n',' ','F','i','l','e',0};
100 static const WCHAR datatypeW
[] = {'D','a','t','a','t','y','p','e',0};
101 static const WCHAR data_fileW
[] = {'D','a','t','a',' ','F','i','l','e',0};
102 static const WCHAR default_devmodeW
[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0};
103 static const WCHAR dependent_filesW
[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
104 static const WCHAR descriptionW
[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
105 static const WCHAR driverW
[] = {'D','r','i','v','e','r',0};
106 static const WCHAR emptyW
[] = {0};
107 static const WCHAR fmt_driversW
[] = { 'S','y','s','t','e','m','\\',
108 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
109 'c','o','n','t','r','o','l','\\',
110 'P','r','i','n','t','\\',
111 'E','n','v','i','r','o','n','m','e','n','t','s','\\',
112 '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
113 static const WCHAR hardwareidW
[] = {'H','a','r','d','w','a','r','e','I','D',0};
114 static const WCHAR help_fileW
[] = {'H','e','l','p',' ','F','i','l','e',0};
115 static const WCHAR localportW
[] = {'L','o','c','a','l',' ','P','o','r','t',0};
116 static const WCHAR locationW
[] = {'L','o','c','a','t','i','o','n',0};
117 static const WCHAR manufacturerW
[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0};
118 static const WCHAR monitorW
[] = {'M','o','n','i','t','o','r',0};
119 static const WCHAR monitorsW
[] = {'S','y','s','t','e','m','\\',
120 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
121 'C','o','n','t','r','o','l','\\',
122 'P','r','i','n','t','\\',
123 'M','o','n','i','t','o','r','s','\\',0};
124 static const WCHAR monitorUIW
[] = {'M','o','n','i','t','o','r','U','I',0};
125 static const WCHAR nameW
[] = {'N','a','m','e',0};
126 static const WCHAR oem_urlW
[] = {'O','E','M',' ','U','r','l',0};
127 static const WCHAR parametersW
[] = {'P','a','r','a','m','e','t','e','r','s',0};
128 static const WCHAR portW
[] = {'P','o','r','t',0};
129 static const WCHAR previous_namesW
[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
130 static const WCHAR printersW
[] = {'S','y','s','t','e','m','\\',
131 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
132 'C','o','n','t','r','o','l','\\',
133 'P','r','i','n','t','\\',
134 'P','r','i','n','t','e','r','s',0};
135 static const WCHAR spooldriversW
[] = {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','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 x64_envnameW
[] = {'W','i','n','d','o','w','s',' ','x','6','4',0};
149 static const WCHAR x64_subdirW
[] = {'x','6','4',0};
150 static const WCHAR x86_envnameW
[] = {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
151 static const WCHAR x86_subdirW
[] = {'w','3','2','x','8','6',0};
152 static const WCHAR XcvMonitorW
[] = {',','X','c','v','M','o','n','i','t','o','r',' ',0};
153 static const WCHAR XcvPortW
[] = {',','X','c','v','P','o','r','t',' ',0};
156 static const printenv_t env_x86
= {x86_envnameW
, x86_subdirW
, 3,
157 version3_regpathW
, version3_subdirW
};
159 static const printenv_t env_x64
= {x64_envnameW
, x64_subdirW
, 3,
160 version3_regpathW
, version3_subdirW
};
162 static const printenv_t env_win40
= {win40_envnameW
, win40_subdirW
, 0,
163 version0_regpathW
, version0_subdirW
};
165 static const printenv_t
* const all_printenv
[] = {&env_x86
, &env_x64
, &env_win40
};
168 static const DWORD di_sizeof
[] = {0, sizeof(DRIVER_INFO_1W
), sizeof(DRIVER_INFO_2W
),
169 sizeof(DRIVER_INFO_3W
), sizeof(DRIVER_INFO_4W
),
170 sizeof(DRIVER_INFO_5W
), sizeof(DRIVER_INFO_6W
),
171 0, sizeof(DRIVER_INFO_8W
)};
174 /******************************************************************
177 * create a copy of a unicode-string
180 static LPWSTR
strdupW(LPCWSTR p
)
186 len
= (lstrlenW(p
) + 1) * sizeof(WCHAR
);
187 ret
= heap_alloc(len
);
192 /******************************************************************
193 * apd_copyfile [internal]
195 * Copy a file from the driverdirectory to the versioned directory
202 static BOOL
apd_copyfile(LPWSTR filename
, apd_data_t
*apd
)
208 apd
->src
[apd
->srclen
] = '\0';
209 apd
->dst
[apd
->dstlen
] = '\0';
211 if (!filename
|| !filename
[0]) {
212 /* nothing to copy */
216 ptr
= strrchrW(filename
, '\\');
225 if (apd
->copyflags
& APD_COPY_FROM_DIRECTORY
) {
226 /* we have an absolute Path */
232 lstrcatW(srcname
, ptr
);
234 lstrcatW(apd
->dst
, ptr
);
236 TRACE("%s => %s\n", debugstr_w(filename
), debugstr_w(apd
->dst
));
238 /* FIXME: handle APD_COPY_NEW_FILES */
239 res
= CopyFileW(srcname
, apd
->dst
, FALSE
);
240 TRACE("got %u with %u\n", res
, GetLastError());
242 return (apd
->lazy
) ? TRUE
: res
;
245 /******************************************************************
246 * copy_servername_from_name (internal)
248 * for an external server, the serverpart from the name is copied.
251 * the length (in WCHAR) of the serverpart (0 for the local computer)
252 * (-length), when the name is to long
255 static LONG
copy_servername_from_name(LPCWSTR name
, LPWSTR target
)
259 WCHAR buffer
[MAX_COMPUTERNAME_LENGTH
+1];
263 if (target
) *target
= '\0';
265 if (name
== NULL
) return 0;
266 if ((name
[0] != '\\') || (name
[1] != '\\')) return 0;
269 /* skip over both backslash, find separator '\' */
270 ptr
= strchrW(server
, '\\');
271 serverlen
= (ptr
) ? ptr
- server
: lstrlenW(server
);
273 /* servername is empty */
274 if (serverlen
== 0) return 0;
276 TRACE("found %s\n", debugstr_wn(server
, serverlen
));
278 if (serverlen
> MAX_COMPUTERNAME_LENGTH
) return -serverlen
;
281 memcpy(target
, server
, serverlen
* sizeof(WCHAR
));
282 target
[serverlen
] = '\0';
285 len
= sizeof(buffer
) / sizeof(buffer
[0]);
286 if (GetComputerNameW(buffer
, &len
)) {
287 if ((serverlen
== len
) && (strncmpiW(server
, buffer
, len
) == 0)) {
288 /* The requested Servername is our computername */
295 /******************************************************************
296 * get_basename_from_name (internal)
298 * skip over the serverpart from the full name
301 static LPCWSTR
get_basename_from_name(LPCWSTR name
)
303 if (name
== NULL
) return NULL
;
304 if ((name
[0] == '\\') && (name
[1] == '\\')) {
305 /* skip over the servername and search for the following '\' */
306 name
= strchrW(&name
[2], '\\');
307 if ((name
) && (name
[1])) {
308 /* found a separator ('\') followed by a name:
309 skip over the separator and return the rest */
314 /* no basename present (we found only a servername) */
321 /******************************************************************
322 * monitor_unload [internal]
324 * release a printmonitor and unload it from memory, when needed
327 static void monitor_unload(monitor_t
* pm
)
329 if (pm
== NULL
) return;
330 TRACE("%p (refcount: %d) %s\n", pm
, pm
->refcount
, debugstr_w(pm
->name
));
332 EnterCriticalSection(&monitor_handles_cs
);
334 if (pm
->refcount
) pm
->refcount
--;
336 if (pm
->refcount
== 0) {
337 list_remove(&pm
->entry
);
338 FreeLibrary(pm
->hdll
);
340 heap_free(pm
->dllname
);
343 LeaveCriticalSection(&monitor_handles_cs
);
346 /******************************************************************
347 * monitor_unloadall [internal]
349 * release all registered printmonitors and unload them from memory, when needed
353 static void monitor_unloadall(void)
358 EnterCriticalSection(&monitor_handles_cs
);
359 /* iterate through the list, with safety against removal */
360 LIST_FOR_EACH_ENTRY_SAFE(pm
, next
, &monitor_handles
, monitor_t
, entry
)
362 /* skip monitorui dlls */
363 if (pm
->monitor
) monitor_unload(pm
);
365 LeaveCriticalSection(&monitor_handles_cs
);
368 /******************************************************************
369 * monitor_load [internal]
371 * load a printmonitor, get the dllname from the registry, when needed
372 * initialize the monitor and dump found function-pointers
374 * On failure, SetLastError() is called and NULL is returned
377 static monitor_t
* monitor_load(LPCWSTR name
, LPWSTR dllname
)
379 LPMONITOR2 (WINAPI
*pInitializePrintMonitor2
) (PMONITORINIT
, LPHANDLE
);
380 PMONITORUI (WINAPI
*pInitializePrintMonitorUI
)(VOID
);
381 LPMONITOREX (WINAPI
*pInitializePrintMonitor
) (LPWSTR
);
382 DWORD (WINAPI
*pInitializeMonitorEx
)(LPWSTR
, LPMONITOR
);
383 DWORD (WINAPI
*pInitializeMonitor
) (LPWSTR
);
385 monitor_t
* pm
= NULL
;
387 LPWSTR regroot
= NULL
;
388 LPWSTR driver
= dllname
;
390 TRACE("(%s, %s)\n", debugstr_w(name
), debugstr_w(dllname
));
391 /* Is the Monitor already loaded? */
392 EnterCriticalSection(&monitor_handles_cs
);
395 LIST_FOR_EACH_ENTRY(cursor
, &monitor_handles
, monitor_t
, entry
)
397 if (cursor
->name
&& (lstrcmpW(name
, cursor
->name
) == 0)) {
405 pm
= heap_alloc_zero(sizeof(monitor_t
));
406 if (pm
== NULL
) goto cleanup
;
407 list_add_tail(&monitor_handles
, &pm
->entry
);
411 if (pm
->name
== NULL
) {
412 /* Load the monitor */
413 LPMONITOREX pmonitorEx
;
417 len
= lstrlenW(monitorsW
) + lstrlenW(name
) + 2;
418 regroot
= heap_alloc(len
* sizeof(WCHAR
));
422 lstrcpyW(regroot
, monitorsW
);
423 lstrcatW(regroot
, name
);
424 /* Get the Driver from the Registry */
425 if (driver
== NULL
) {
428 if (RegOpenKeyW(HKEY_LOCAL_MACHINE
, regroot
, &hroot
) == ERROR_SUCCESS
) {
429 if (RegQueryValueExW(hroot
, driverW
, NULL
, NULL
, NULL
,
430 &namesize
) == ERROR_SUCCESS
) {
431 driver
= heap_alloc(namesize
);
432 RegQueryValueExW(hroot
, driverW
, NULL
, NULL
, (LPBYTE
) driver
, &namesize
) ;
439 pm
->name
= strdupW(name
);
440 pm
->dllname
= strdupW(driver
);
442 if ((name
&& (!regroot
|| !pm
->name
)) || !pm
->dllname
) {
444 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
449 pm
->hdll
= LoadLibraryW(driver
);
450 TRACE("%p: LoadLibrary(%s) => %d\n", pm
->hdll
, debugstr_w(driver
), GetLastError());
452 if (pm
->hdll
== NULL
) {
454 SetLastError(ERROR_MOD_NOT_FOUND
);
459 pInitializePrintMonitor2
= (void *)GetProcAddress(pm
->hdll
, "InitializePrintMonitor2");
460 pInitializePrintMonitorUI
= (void *)GetProcAddress(pm
->hdll
, "InitializePrintMonitorUI");
461 pInitializePrintMonitor
= (void *)GetProcAddress(pm
->hdll
, "InitializePrintMonitor");
462 pInitializeMonitorEx
= (void *)GetProcAddress(pm
->hdll
, "InitializeMonitorEx");
463 pInitializeMonitor
= (void *)GetProcAddress(pm
->hdll
, "InitializeMonitor");
466 TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2
, debugstr_w(driver
));
467 TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI
, debugstr_w(driver
));
468 TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor
, debugstr_w(driver
));
469 TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx
, debugstr_w(driver
));
470 TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor
, debugstr_w(driver
));
472 if (pInitializePrintMonitorUI
!= NULL
) {
473 pm
->monitorUI
= pInitializePrintMonitorUI();
474 TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm
->monitorUI
, debugstr_w(driver
));
476 TRACE("0x%08x: dwMonitorSize (%d)\n",
477 pm
->monitorUI
->dwMonitorUISize
, pm
->monitorUI
->dwMonitorUISize
);
482 if (pInitializePrintMonitor
&& regroot
) {
483 pmonitorEx
= pInitializePrintMonitor(regroot
);
484 TRACE("%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
485 pmonitorEx
, debugstr_w(driver
), debugstr_w(regroot
));
488 pm
->dwMonitorSize
= pmonitorEx
->dwMonitorSize
;
489 pm
->monitor
= &(pmonitorEx
->Monitor
);
494 TRACE("0x%08x: dwMonitorSize (%d)\n", pm
->dwMonitorSize
, pm
->dwMonitorSize
);
498 if (!pm
->monitor
&& regroot
) {
499 if (pInitializePrintMonitor2
!= NULL
) {
500 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver
));
502 if (pInitializeMonitorEx
!= NULL
) {
503 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver
));
505 if (pInitializeMonitor
!= NULL
) {
506 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver
));
509 if (!pm
->monitor
&& !pm
->monitorUI
) {
511 SetLastError(ERROR_PROC_NOT_FOUND
);
516 if ((pm_localport
== NULL
) && (pm
!= NULL
) && (lstrcmpW(pm
->name
, localportW
) == 0)) {
520 LeaveCriticalSection(&monitor_handles_cs
);
521 if (driver
!= dllname
) heap_free(driver
);
523 TRACE("=> %p\n", pm
);
527 /******************************************************************
528 * monitor_loadall [internal]
530 * Load all registered monitors
533 static DWORD
monitor_loadall(void)
536 DWORD registered
= 0;
539 WCHAR buffer
[MAX_PATH
];
542 if (RegOpenKeyW(HKEY_LOCAL_MACHINE
, monitorsW
, &hmonitors
) == ERROR_SUCCESS
) {
543 RegQueryInfoKeyW(hmonitors
, NULL
, NULL
, NULL
, ®istered
, NULL
, NULL
,
544 NULL
, NULL
, NULL
, NULL
, NULL
);
546 TRACE("%d monitors registered\n", registered
);
548 while (id
< registered
) {
550 RegEnumKeyW(hmonitors
, id
, buffer
, MAX_PATH
);
551 pm
= monitor_load(buffer
, NULL
);
555 RegCloseKey(hmonitors
);
557 TRACE("%d monitors loaded\n", loaded
);
561 /******************************************************************
562 * monitor_loadui [internal]
564 * load the userinterface-dll for a given portmonitor
566 * On failure, NULL is returned
568 static monitor_t
* monitor_loadui(monitor_t
* pm
)
570 monitor_t
* pui
= NULL
;
571 WCHAR buffer
[MAX_PATH
];
576 if (pm
== NULL
) return NULL
;
577 TRACE("(%p) => dllname: %s\n", pm
, debugstr_w(pm
->dllname
));
579 /* Try the Portmonitor first; works for many monitors */
581 EnterCriticalSection(&monitor_handles_cs
);
583 LeaveCriticalSection(&monitor_handles_cs
);
587 /* query the userinterface-dllname from the Portmonitor */
588 if ((pm
->monitor
) && (pm
->monitor
->pfnXcvDataPort
)) {
589 /* building (",XcvMonitor %s",pm->name) not needed yet */
590 res
= pm
->monitor
->pfnXcvOpenPort(emptyW
, SERVER_ACCESS_ADMINISTER
, &hXcv
);
591 TRACE("got %u with %p\n", res
, hXcv
);
593 res
= pm
->monitor
->pfnXcvDataPort(hXcv
, monitorUIW
, NULL
, 0, (BYTE
*) buffer
, sizeof(buffer
), &len
);
594 TRACE("got %u with %s\n", res
, debugstr_w(buffer
));
595 if (res
== ERROR_SUCCESS
) pui
= monitor_load(NULL
, buffer
);
596 pm
->monitor
->pfnXcvClosePort(hXcv
);
602 /******************************************************************
603 * monitor_load_by_port [internal]
605 * load a printmonitor for a given port
607 * On failure, NULL is returned
610 static monitor_t
* monitor_load_by_port(LPCWSTR portname
)
615 monitor_t
* pm
= NULL
;
616 DWORD registered
= 0;
620 TRACE("(%s)\n", debugstr_w(portname
));
622 /* Try the Local Monitor first */
623 if (RegOpenKeyW(HKEY_LOCAL_MACHINE
, winnt_cv_portsW
, &hroot
) == ERROR_SUCCESS
) {
624 if (RegQueryValueExW(hroot
, portname
, NULL
, NULL
, NULL
, &len
) == ERROR_SUCCESS
) {
625 /* found the portname */
627 return monitor_load(localportW
, NULL
);
632 len
= MAX_PATH
+ lstrlenW(bs_ports_bsW
) + lstrlenW(portname
) + 1;
633 buffer
= heap_alloc(len
* sizeof(WCHAR
));
634 if (buffer
== NULL
) return NULL
;
636 if (RegOpenKeyW(HKEY_LOCAL_MACHINE
, monitorsW
, &hroot
) == ERROR_SUCCESS
) {
637 EnterCriticalSection(&monitor_handles_cs
);
638 RegQueryInfoKeyW(hroot
, NULL
, NULL
, NULL
, ®istered
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
640 while ((pm
== NULL
) && (id
< registered
)) {
642 RegEnumKeyW(hroot
, id
, buffer
, MAX_PATH
);
643 TRACE("testing %s\n", debugstr_w(buffer
));
644 len
= lstrlenW(buffer
);
645 lstrcatW(buffer
, bs_ports_bsW
);
646 lstrcatW(buffer
, portname
);
647 if (RegOpenKeyW(hroot
, buffer
, &hport
) == ERROR_SUCCESS
) {
649 buffer
[len
] = '\0'; /* use only the Monitor-Name */
650 pm
= monitor_load(buffer
, NULL
);
654 LeaveCriticalSection(&monitor_handles_cs
);
661 /******************************************************************
662 * Return the number of bytes for an multi_sz string.
663 * The result includes all \0s
664 * (specifically the extra \0, that is needed as multi_sz terminator).
666 static int multi_sz_lenW(const WCHAR
*str
)
668 const WCHAR
*ptr
= str
;
672 ptr
+= lstrlenW(ptr
) + 1;
675 return (ptr
- str
+ 1) * sizeof(WCHAR
);
678 /******************************************************************
679 * validate_envW [internal]
681 * validate the user-supplied printing-environment
684 * env [I] PTR to Environment-String or NULL
687 * Success: PTR to printenv_t
688 * Failure: NULL and ERROR_INVALID_ENVIRONMENT
691 * An empty string is handled the same way as NULL.
695 static const printenv_t
* validate_envW(LPCWSTR env
)
697 const printenv_t
*result
= NULL
;
700 TRACE("(%s)\n", debugstr_w(env
));
703 for (i
= 0; i
< sizeof(all_printenv
)/sizeof(all_printenv
[0]); i
++)
705 if (lstrcmpiW(env
, all_printenv
[i
]->envname
) == 0)
707 result
= all_printenv
[i
];
711 if (result
== NULL
) {
712 FIXME("unsupported Environment: %s\n", debugstr_w(env
));
713 SetLastError(ERROR_INVALID_ENVIRONMENT
);
715 /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
719 result
= (GetVersion() & 0x80000000) ? &env_win40
: &env_x86
;
722 TRACE("=> using %p: %s\n", result
, debugstr_w(result
? result
->envname
: NULL
));
726 /*****************************************************************************
727 * enumerate the local monitors (INTERNAL)
729 * returns the needed size (in bytes) for pMonitors
730 * and *lpreturned is set to number of entries returned in pMonitors
732 * Language-Monitors are also installed in the same Registry-Location but
733 * they are filtered in Windows (not returned by EnumMonitors).
734 * We do no filtering to simplify our Code.
737 static DWORD
get_local_monitors(DWORD level
, LPBYTE pMonitors
, DWORD cbBuf
, LPDWORD lpreturned
)
742 LPMONITOR_INFO_2W mi
;
743 WCHAR buffer
[MAX_PATH
];
744 WCHAR dllname
[MAX_PATH
];
752 entrysize
= (level
== 1) ? sizeof(MONITOR_INFO_1W
) : sizeof(MONITOR_INFO_2W
);
754 numentries
= *lpreturned
; /* this is 0, when we scan the registry */
755 len
= entrysize
* numentries
;
756 ptr
= (LPWSTR
) &pMonitors
[len
];
759 len
= sizeof(buffer
)/sizeof(buffer
[0]);
762 /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
763 if (RegCreateKeyW(HKEY_LOCAL_MACHINE
, monitorsW
, &hroot
) == ERROR_SUCCESS
) {
764 /* Scan all Monitor-Registry-Keys */
765 while (RegEnumKeyExW(hroot
, index
, buffer
, &len
, NULL
, NULL
, NULL
, NULL
) == ERROR_SUCCESS
) {
766 TRACE("Monitor_%d: %s\n", numentries
, debugstr_w(buffer
));
767 dllsize
= sizeof(dllname
);
770 /* The Monitor must have a Driver-DLL */
771 if (RegOpenKeyExW(hroot
, buffer
, 0, KEY_READ
, &hentry
) == ERROR_SUCCESS
) {
772 if (RegQueryValueExW(hentry
, driverW
, NULL
, NULL
, (LPBYTE
) dllname
, &dllsize
) == ERROR_SUCCESS
) {
773 /* We found a valid DLL for this Monitor. */
774 TRACE("using Driver: %s\n", debugstr_w(dllname
));
779 /* Windows returns only Port-Monitors here, but to simplify our code,
780 we do no filtering for Language-Monitors */
784 needed
+= (len
+1) * sizeof(WCHAR
); /* len is lstrlenW(monitorname) */
786 /* we install and return only monitors for "Windows NT x86" */
787 needed
+= (lstrlenW(x86_envnameW
) +1) * sizeof(WCHAR
);
791 /* required size is calculated. Now fill the user-buffer */
792 if (pMonitors
&& (cbBuf
>= needed
)){
793 mi
= (LPMONITOR_INFO_2W
) pMonitors
;
794 pMonitors
+= entrysize
;
796 TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi
, level
, numentries
);
798 lstrcpyW(ptr
, buffer
); /* Name of the Monitor */
799 ptr
+= (len
+1); /* len is lstrlenW(monitorname) */
801 mi
->pEnvironment
= ptr
;
802 lstrcpyW(ptr
, x86_envnameW
); /* fixed to "Windows NT x86" */
803 ptr
+= (lstrlenW(x86_envnameW
)+1);
806 lstrcpyW(ptr
, dllname
); /* Name of the Driver-DLL */
807 ptr
+= (dllsize
/ sizeof(WCHAR
));
812 len
= sizeof(buffer
)/sizeof(buffer
[0]);
817 *lpreturned
= numentries
;
818 TRACE("need %d byte for %d entries\n", needed
, numentries
);
822 /******************************************************************
823 * enumerate the local Ports from all loaded monitors (internal)
825 * returns the needed size (in bytes) for pPorts
826 * and *lpreturned is set to number of entries returned in pPorts
829 static DWORD
get_ports_from_all_monitors(DWORD level
, LPBYTE pPorts
, DWORD cbBuf
, LPDWORD lpreturned
)
833 LPPORT_INFO_2W cache
;
835 LPBYTE pi_buffer
= NULL
;
836 DWORD pi_allocated
= 0;
847 TRACE("(%d, %p, %d, %p)\n", level
, pPorts
, cbBuf
, lpreturned
);
848 entrysize
= (level
== 1) ? sizeof(PORT_INFO_1W
) : sizeof(PORT_INFO_2W
);
850 numentries
= *lpreturned
; /* this is 0, when we scan the registry */
851 needed
= entrysize
* numentries
;
852 ptr
= (LPWSTR
) &pPorts
[needed
];
857 LIST_FOR_EACH_ENTRY(pm
, &monitor_handles
, monitor_t
, entry
)
859 if ((pm
->monitor
) && (pm
->monitor
->pfnEnumPorts
)) {
862 res
= pm
->monitor
->pfnEnumPorts(NULL
, level
, pi_buffer
, pi_allocated
, &pi_needed
, &pi_returned
);
863 if (!res
&& (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)) {
864 /* Do not use heap_realloc (we do not need the old data in the buffer) */
865 heap_free(pi_buffer
);
866 pi_buffer
= heap_alloc(pi_needed
);
867 pi_allocated
= (pi_buffer
) ? pi_needed
: 0;
868 res
= pm
->monitor
->pfnEnumPorts(NULL
, level
, pi_buffer
, pi_allocated
, &pi_needed
, &pi_returned
);
870 TRACE("(%s) got %d with %d (need %d byte for %d entries)\n",
871 debugstr_w(pm
->name
), res
, GetLastError(), pi_needed
, pi_returned
);
873 numentries
+= pi_returned
;
876 /* fill the output-buffer (pPorts), if we have one */
877 if (pPorts
&& (cbBuf
>= needed
) && pi_buffer
) {
879 while (pi_returned
> pi_index
) {
880 cache
= (LPPORT_INFO_2W
) &pi_buffer
[pi_index
* entrysize
];
881 out
= (LPPORT_INFO_2W
) &pPorts
[outindex
* entrysize
];
882 out
->pPortName
= ptr
;
883 lstrcpyW(ptr
, cache
->pPortName
);
884 ptr
+= (lstrlenW(ptr
)+1);
886 out
->pMonitorName
= ptr
;
887 lstrcpyW(ptr
, cache
->pMonitorName
);
888 ptr
+= (lstrlenW(ptr
)+1);
890 out
->pDescription
= ptr
;
891 lstrcpyW(ptr
, cache
->pDescription
);
892 ptr
+= (lstrlenW(ptr
)+1);
893 out
->fPortType
= cache
->fPortType
;
894 out
->Reserved
= cache
->Reserved
;
902 /* the temporary portinfo-buffer is no longer needed */
903 heap_free(pi_buffer
);
905 *lpreturned
= numentries
;
906 TRACE("need %d byte for %d entries\n", needed
, numentries
);
911 /*****************************************************************************
912 * open_driver_reg [internal]
914 * opens the registry for the printer drivers depending on the given input
915 * variable pEnvironment
918 * Success: the opened hkey
921 static HKEY
open_driver_reg(LPCWSTR pEnvironment
)
925 const printenv_t
* env
;
927 TRACE("(%s)\n", debugstr_w(pEnvironment
));
929 env
= validate_envW(pEnvironment
);
930 if (!env
) return NULL
;
932 buffer
= HeapAlloc(GetProcessHeap(), 0, sizeof(fmt_driversW
) +
933 (lstrlenW(env
->envname
) + lstrlenW(env
->versionregpath
)) * sizeof(WCHAR
));
936 wsprintfW(buffer
, fmt_driversW
, env
->envname
, env
->versionregpath
);
937 RegCreateKeyW(HKEY_LOCAL_MACHINE
, buffer
, &retval
);
938 HeapFree(GetProcessHeap(), 0, buffer
);
943 /*****************************************************************************
944 * fpGetPrinterDriverDirectory [exported through PRINTPROVIDOR]
946 * Return the PATH for the Printer-Drivers
949 * pName [I] Servername (NT only) or NULL (local Computer)
950 * pEnvironment [I] Printing-Environment (see below) or NULL (Default)
951 * Level [I] Structure-Level (must be 1)
952 * pDriverDirectory [O] PTR to Buffer that receives the Result
953 * cbBuf [I] Size of Buffer at pDriverDirectory
954 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
955 * required for pDriverDirectory
958 * Success: TRUE and in pcbNeeded the Bytes used in pDriverDirectory
959 * Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
960 * if cbBuf is too small
962 * Native Values returned in pDriverDirectory on Success:
963 *| NT(Windows NT x86): "%winsysdir%\\spool\\DRIVERS\\w32x86"
964 *| NT(Windows 4.0): "%winsysdir%\\spool\\DRIVERS\\win40"
965 *| win9x(Windows 4.0): "%winsysdir%"
967 * "%winsysdir%" is the Value from GetSystemDirectoryW()
970 static BOOL WINAPI
fpGetPrinterDriverDirectory(LPWSTR pName
, LPWSTR pEnvironment
,
971 DWORD Level
, LPBYTE pDriverDirectory
, DWORD cbBuf
, LPDWORD pcbNeeded
)
974 const printenv_t
* env
;
976 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName
),
977 debugstr_w(pEnvironment
), Level
, pDriverDirectory
, cbBuf
, pcbNeeded
);
979 if (pName
!= NULL
&& pName
[0]) {
980 FIXME("server %s not supported\n", debugstr_w(pName
));
981 SetLastError(ERROR_INVALID_PARAMETER
);
985 env
= validate_envW(pEnvironment
);
986 if (!env
) return FALSE
; /* pEnvironment invalid or unsupported */
989 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
990 needed
= GetSystemDirectoryW(NULL
, 0);
991 /* add the Size for the Subdirectories */
992 needed
+= lstrlenW(spooldriversW
);
993 needed
+= lstrlenW(env
->subdir
);
994 needed
*= sizeof(WCHAR
); /* return-value is size in Bytes */
998 if (needed
> cbBuf
) {
999 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1003 if (pDriverDirectory
== NULL
) {
1004 /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
1005 SetLastError(ERROR_INVALID_USER_BUFFER
);
1009 GetSystemDirectoryW((LPWSTR
) pDriverDirectory
, cbBuf
/sizeof(WCHAR
));
1010 /* add the Subdirectories */
1011 lstrcatW((LPWSTR
) pDriverDirectory
, spooldriversW
);
1012 lstrcatW((LPWSTR
) pDriverDirectory
, env
->subdir
);
1014 TRACE("=> %s\n", debugstr_w((LPWSTR
) pDriverDirectory
));
1018 /******************************************************************
1019 * driver_load [internal]
1021 * load a driver user interface dll
1023 * On failure, NULL is returned
1027 static HMODULE
driver_load(const printenv_t
* env
, LPWSTR dllname
)
1029 WCHAR fullname
[MAX_PATH
];
1033 TRACE("(%p, %s)\n", env
, debugstr_w(dllname
));
1035 /* build the driverdir */
1036 len
= sizeof(fullname
) -
1037 (lstrlenW(env
->versionsubdir
) + 1 + lstrlenW(dllname
) + 1) * sizeof(WCHAR
);
1039 if (!fpGetPrinterDriverDirectory(NULL
, (LPWSTR
) env
->envname
, 1,
1040 (LPBYTE
) fullname
, len
, &len
)) {
1041 /* Should never Fail */
1042 SetLastError(ERROR_BUFFER_OVERFLOW
);
1046 lstrcatW(fullname
, env
->versionsubdir
);
1047 lstrcatW(fullname
, backslashW
);
1048 lstrcatW(fullname
, dllname
);
1050 hui
= LoadLibraryW(fullname
);
1051 TRACE("%p: LoadLibrary(%s) %d\n", hui
, debugstr_w(fullname
), GetLastError());
1056 /******************************************************************
1058 * free the data pointer of an opened printer
1060 static VOID
printer_free(printer_t
* printer
)
1063 printer
->pm
->monitor
->pfnXcvClosePort(printer
->hXcv
);
1065 monitor_unload(printer
->pm
);
1067 heap_free(printer
->printername
);
1068 heap_free(printer
->name
);
1072 /******************************************************************
1073 * printer_alloc_handle
1074 * alloc a printer handle and remember the data pointer in the printer handle table
1077 static HANDLE
printer_alloc_handle(LPCWSTR name
, LPPRINTER_DEFAULTSW pDefault
)
1079 WCHAR servername
[MAX_COMPUTERNAME_LENGTH
+ 1];
1080 printer_t
*printer
= NULL
;
1081 LPCWSTR printername
;
1086 if (copy_servername_from_name(name
, servername
)) {
1087 FIXME("server %s not supported\n", debugstr_w(servername
));
1088 SetLastError(ERROR_INVALID_PRINTER_NAME
);
1092 printername
= get_basename_from_name(name
);
1093 if (name
!= printername
) TRACE("converted %s to %s\n", debugstr_w(name
), debugstr_w(printername
));
1095 /* an empty printername is invalid */
1096 if (printername
&& (!printername
[0])) {
1097 SetLastError(ERROR_INVALID_PARAMETER
);
1101 printer
= heap_alloc_zero(sizeof(printer_t
));
1102 if (!printer
) goto end
;
1104 /* clone the base name. This is NULL for the printserver */
1105 printer
->printername
= strdupW(printername
);
1107 /* clone the full name */
1108 printer
->name
= strdupW(name
);
1109 if (name
&& (!printer
->name
)) {
1110 printer_free(printer
);
1114 len
= sizeof(XcvMonitorW
)/sizeof(WCHAR
) - 1;
1115 if (strncmpW(printername
, XcvMonitorW
, len
) == 0) {
1116 /* OpenPrinter(",XcvMonitor ", ...) detected */
1117 TRACE(",XcvMonitor: %s\n", debugstr_w(&printername
[len
]));
1118 printer
->pm
= monitor_load(&printername
[len
], NULL
);
1119 if (printer
->pm
== NULL
) {
1120 printer_free(printer
);
1121 SetLastError(ERROR_UNKNOWN_PORT
);
1128 len
= sizeof(XcvPortW
)/sizeof(WCHAR
) - 1;
1129 if (strncmpW( printername
, XcvPortW
, len
) == 0) {
1130 /* OpenPrinter(",XcvPort ", ...) detected */
1131 TRACE(",XcvPort: %s\n", debugstr_w(&printername
[len
]));
1132 printer
->pm
= monitor_load_by_port(&printername
[len
]);
1133 if (printer
->pm
== NULL
) {
1134 printer_free(printer
);
1135 SetLastError(ERROR_UNKNOWN_PORT
);
1143 if ((printer
->pm
->monitor
) && (printer
->pm
->monitor
->pfnXcvOpenPort
)) {
1144 printer
->pm
->monitor
->pfnXcvOpenPort(&printername
[len
],
1145 pDefault
? pDefault
->DesiredAccess
: 0,
1148 if (printer
->hXcv
== NULL
) {
1149 printer_free(printer
);
1150 SetLastError(ERROR_INVALID_PARAMETER
);
1157 /* Does the Printer exist? */
1158 if (RegCreateKeyW(HKEY_LOCAL_MACHINE
, printersW
, &hkeyPrinters
) != ERROR_SUCCESS
) {
1159 ERR("Can't create Printers key\n");
1160 printer_free(printer
);
1161 SetLastError(ERROR_INVALID_PRINTER_NAME
);
1165 if (RegOpenKeyW(hkeyPrinters
, printername
, &hkeyPrinter
) != ERROR_SUCCESS
) {
1166 WARN("Printer not found in Registry: %s\n", debugstr_w(printername
));
1167 RegCloseKey(hkeyPrinters
);
1168 printer_free(printer
);
1169 SetLastError(ERROR_INVALID_PRINTER_NAME
);
1173 RegCloseKey(hkeyPrinter
);
1174 RegCloseKey(hkeyPrinters
);
1179 TRACE("using the local printserver\n");
1184 TRACE("==> %p\n", printer
);
1185 return (HANDLE
)printer
;
1189 /******************************************************************************
1190 * myAddPrinterDriverEx [internal]
1192 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1193 * and a special mode with lazy error checking.
1196 static BOOL
myAddPrinterDriverEx(DWORD level
, LPBYTE pDriverInfo
, DWORD dwFileCopyFlags
, BOOL lazy
)
1198 const printenv_t
*env
;
1201 BOOL (WINAPI
*pDrvDriverEvent
)(DWORD
, DWORD
, LPBYTE
, LPARAM
);
1211 /* we need to set all entries in the Registry, independent from the Level of
1212 DRIVER_INFO, that the caller supplied */
1214 ZeroMemory(&di
, sizeof(di
));
1215 if (pDriverInfo
&& (level
< (sizeof(di_sizeof
) / sizeof(di_sizeof
[0])))) {
1216 memcpy(&di
, pDriverInfo
, di_sizeof
[level
]);
1219 /* dump the most used infos */
1220 TRACE("%p: .cVersion : 0x%x/%d\n", pDriverInfo
, di
.cVersion
, di
.cVersion
);
1221 TRACE("%p: .pName : %s\n", di
.pName
, debugstr_w(di
.pName
));
1222 TRACE("%p: .pEnvironment: %s\n", di
.pEnvironment
, debugstr_w(di
.pEnvironment
));
1223 TRACE("%p: .pDriverPath : %s\n", di
.pDriverPath
, debugstr_w(di
.pDriverPath
));
1224 TRACE("%p: .pDataFile : %s\n", di
.pDataFile
, debugstr_w(di
.pDataFile
));
1225 TRACE("%p: .pConfigFile : %s\n", di
.pConfigFile
, debugstr_w(di
.pConfigFile
));
1226 TRACE("%p: .pHelpFile : %s\n", di
.pHelpFile
, debugstr_w(di
.pHelpFile
));
1227 /* dump only the first of the additional Files */
1228 TRACE("%p: .pDependentFiles: %s\n", di
.pDependentFiles
, debugstr_w(di
.pDependentFiles
));
1231 /* check environment */
1232 env
= validate_envW(di
.pEnvironment
);
1233 if (env
== NULL
) return FALSE
; /* ERROR_INVALID_ENVIRONMENT */
1235 /* fill the copy-data / get the driverdir */
1236 len
= sizeof(apd
.src
) - sizeof(version3_subdirW
) - sizeof(WCHAR
);
1237 if (!fpGetPrinterDriverDirectory(NULL
, (LPWSTR
) env
->envname
, 1,
1238 (LPBYTE
) apd
.src
, len
, &len
)) {
1239 /* Should never Fail */
1242 memcpy(apd
.dst
, apd
.src
, len
);
1243 lstrcatW(apd
.src
, backslashW
);
1244 apd
.srclen
= lstrlenW(apd
.src
);
1245 lstrcatW(apd
.dst
, env
->versionsubdir
);
1246 lstrcatW(apd
.dst
, backslashW
);
1247 apd
.dstlen
= lstrlenW(apd
.dst
);
1248 apd
.copyflags
= dwFileCopyFlags
;
1250 CreateDirectoryW(apd
.src
, NULL
);
1251 CreateDirectoryW(apd
.dst
, NULL
);
1253 hroot
= open_driver_reg(env
->envname
);
1255 ERR("Can't create Drivers key\n");
1259 /* Fill the Registry for the Driver */
1260 if ((lres
= RegCreateKeyExW(hroot
, di
.pName
, 0, NULL
, REG_OPTION_NON_VOLATILE
,
1261 KEY_WRITE
| KEY_QUERY_VALUE
, NULL
,
1262 &hdrv
, &disposition
)) != ERROR_SUCCESS
) {
1264 ERR("can't create driver %s: %u\n", debugstr_w(di
.pName
), lres
);
1271 if (disposition
== REG_OPENED_EXISTING_KEY
) {
1272 TRACE("driver %s already installed\n", debugstr_w(di
.pName
));
1274 SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED
);
1278 /* Verified with the Adobe PS Driver, that w2k does not use di.Version */
1279 RegSetValueExW(hdrv
, versionW
, 0, REG_DWORD
, (LPBYTE
) &env
->driverversion
,
1282 RegSetValueExW(hdrv
, driverW
, 0, REG_SZ
, (LPBYTE
) di
.pDriverPath
,
1283 (lstrlenW(di
.pDriverPath
)+1)* sizeof(WCHAR
));
1284 apd_copyfile(di
.pDriverPath
, &apd
);
1286 RegSetValueExW(hdrv
, data_fileW
, 0, REG_SZ
, (LPBYTE
) di
.pDataFile
,
1287 (lstrlenW(di
.pDataFile
)+1)* sizeof(WCHAR
));
1288 apd_copyfile(di
.pDataFile
, &apd
);
1290 RegSetValueExW(hdrv
, configuration_fileW
, 0, REG_SZ
, (LPBYTE
) di
.pConfigFile
,
1291 (lstrlenW(di
.pConfigFile
)+1)* sizeof(WCHAR
));
1292 apd_copyfile(di
.pConfigFile
, &apd
);
1294 /* settings for level 3 */
1296 RegSetValueExW(hdrv
, help_fileW
, 0, REG_SZ
, (LPBYTE
) di
.pHelpFile
,
1297 (lstrlenW(di
.pHelpFile
)+1)* sizeof(WCHAR
));
1299 RegSetValueExW(hdrv
, help_fileW
, 0, REG_SZ
, (LPBYTE
)emptyW
, sizeof(emptyW
));
1300 apd_copyfile(di
.pHelpFile
, &apd
);
1303 ptr
= di
.pDependentFiles
;
1305 RegSetValueExW(hdrv
, dependent_filesW
, 0, REG_MULTI_SZ
, (LPBYTE
) di
.pDependentFiles
,
1306 multi_sz_lenW(di
.pDependentFiles
));
1308 RegSetValueExW(hdrv
, dependent_filesW
, 0, REG_MULTI_SZ
, (LPBYTE
)emptyW
, sizeof(emptyW
));
1309 while ((ptr
!= NULL
) && (ptr
[0])) {
1310 if (apd_copyfile(ptr
, &apd
)) {
1311 ptr
+= lstrlenW(ptr
) + 1;
1315 WARN("Failed to copy %s\n", debugstr_w(ptr
));
1319 /* The language-Monitor was already copied by the caller to "%SystemRoot%\system32" */
1320 if (di
.pMonitorName
)
1321 RegSetValueExW(hdrv
, monitorW
, 0, REG_SZ
, (LPBYTE
) di
.pMonitorName
,
1322 (lstrlenW(di
.pMonitorName
)+1)* sizeof(WCHAR
));
1324 RegSetValueExW(hdrv
, monitorW
, 0, REG_SZ
, (LPBYTE
)emptyW
, sizeof(emptyW
));
1326 if (di
.pDefaultDataType
)
1327 RegSetValueExW(hdrv
, datatypeW
, 0, REG_SZ
, (LPBYTE
) di
.pDefaultDataType
,
1328 (lstrlenW(di
.pDefaultDataType
)+1)* sizeof(WCHAR
));
1330 RegSetValueExW(hdrv
, datatypeW
, 0, REG_SZ
, (LPBYTE
)emptyW
, sizeof(emptyW
));
1332 /* settings for level 4 */
1333 if (di
.pszzPreviousNames
)
1334 RegSetValueExW(hdrv
, previous_namesW
, 0, REG_MULTI_SZ
, (LPBYTE
) di
.pszzPreviousNames
,
1335 multi_sz_lenW(di
.pszzPreviousNames
));
1337 RegSetValueExW(hdrv
, previous_namesW
, 0, REG_MULTI_SZ
, (LPBYTE
)emptyW
, sizeof(emptyW
));
1339 if (level
> 5) TRACE("level %u for Driver %s is incomplete\n", level
, debugstr_w(di
.pName
));
1342 hui
= driver_load(env
, di
.pConfigFile
);
1343 pDrvDriverEvent
= (void *)GetProcAddress(hui
, "DrvDriverEvent");
1344 if (hui
&& pDrvDriverEvent
) {
1346 /* Support for DrvDriverEvent is optional */
1347 TRACE("DRIVER_EVENT_INITIALIZE for %s (%s)\n", debugstr_w(di
.pName
), debugstr_w(di
.pConfigFile
));
1348 /* MSDN: level for DRIVER_INFO is 1 to 3 */
1349 res
= pDrvDriverEvent(DRIVER_EVENT_INITIALIZE
, 3, (LPBYTE
) &di
, 0);
1350 TRACE("got %d from DRIVER_EVENT_INITIALIZE\n", res
);
1354 TRACE("=> TRUE with %u\n", GetLastError());
1359 /******************************************************************************
1360 * fpAddMonitor [exported through PRINTPROVIDOR]
1362 * Install a Printmonitor
1365 * pName [I] Servername or NULL (local Computer)
1366 * Level [I] Structure-Level (Must be 2)
1367 * pMonitors [I] PTR to MONITOR_INFO_2
1374 * All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1377 static BOOL WINAPI
fpAddMonitor(LPWSTR pName
, DWORD Level
, LPBYTE pMonitors
)
1379 monitor_t
* pm
= NULL
;
1380 LPMONITOR_INFO_2W mi2w
;
1386 mi2w
= (LPMONITOR_INFO_2W
) pMonitors
;
1387 TRACE("(%s, %d, %p): %s %s %s\n", debugstr_w(pName
), Level
, pMonitors
,
1388 debugstr_w(mi2w
? mi2w
->pName
: NULL
),
1389 debugstr_w(mi2w
? mi2w
->pEnvironment
: NULL
),
1390 debugstr_w(mi2w
? mi2w
->pDLLName
: NULL
));
1392 if (copy_servername_from_name(pName
, NULL
)) {
1393 FIXME("server %s not supported\n", debugstr_w(pName
));
1394 SetLastError(ERROR_ACCESS_DENIED
);
1398 if (!mi2w
->pName
|| (! mi2w
->pName
[0])) {
1399 WARN("pName not valid : %s\n", debugstr_w(mi2w
->pName
));
1400 SetLastError(ERROR_INVALID_PARAMETER
);
1403 if (!mi2w
->pEnvironment
|| lstrcmpW(mi2w
->pEnvironment
, x86_envnameW
)) {
1404 WARN("Environment %s requested (we support only %s)\n",
1405 debugstr_w(mi2w
->pEnvironment
), debugstr_w(x86_envnameW
));
1406 SetLastError(ERROR_INVALID_ENVIRONMENT
);
1410 if (!mi2w
->pDLLName
|| (! mi2w
->pDLLName
[0])) {
1411 WARN("pDLLName not valid : %s\n", debugstr_w(mi2w
->pDLLName
));
1412 SetLastError(ERROR_INVALID_PARAMETER
);
1416 /* Load and initialize the monitor. SetLastError() is called on failure */
1417 if ((pm
= monitor_load(mi2w
->pName
, mi2w
->pDLLName
)) == NULL
) {
1422 if (RegCreateKeyW(HKEY_LOCAL_MACHINE
, monitorsW
, &hroot
) != ERROR_SUCCESS
) {
1423 ERR("unable to create key %s\n", debugstr_w(monitorsW
));
1427 if (RegCreateKeyExW(hroot
, mi2w
->pName
, 0, NULL
, REG_OPTION_NON_VOLATILE
,
1428 KEY_WRITE
| KEY_QUERY_VALUE
, NULL
, &hentry
,
1429 &disposition
) == ERROR_SUCCESS
) {
1431 /* Some installers set options for the port before calling AddMonitor.
1432 We query the "Driver" entry to verify that the monitor is installed,
1433 before we return an error.
1434 When a user installs two print monitors at the same time with the
1435 same name, a race condition is possible but silently ignored. */
1439 if ((disposition
== REG_OPENED_EXISTING_KEY
) &&
1440 (RegQueryValueExW(hentry
, driverW
, NULL
, NULL
, NULL
,
1441 &namesize
) == ERROR_SUCCESS
)) {
1442 TRACE("monitor %s already exists\n", debugstr_w(mi2w
->pName
));
1443 /* 9x use ERROR_ALREADY_EXISTS */
1444 SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED
);
1449 len
= (lstrlenW(mi2w
->pDLLName
) +1) * sizeof(WCHAR
);
1450 res
= (RegSetValueExW(hentry
, driverW
, 0, REG_SZ
,
1451 (LPBYTE
) mi2w
->pDLLName
, len
) == ERROR_SUCCESS
);
1453 RegCloseKey(hentry
);
1460 /******************************************************************************
1461 * fpAddPrinterDriverEx [exported through PRINTPROVIDOR]
1463 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1466 * pName [I] Servername or NULL (local Computer)
1467 * level [I] Level for the supplied DRIVER_INFO_*W struct
1468 * pDriverInfo [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
1469 * dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
1476 static BOOL WINAPI
fpAddPrinterDriverEx(LPWSTR pName
, DWORD level
, LPBYTE pDriverInfo
, DWORD dwFileCopyFlags
)
1480 TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName
), level
, pDriverInfo
, dwFileCopyFlags
);
1481 lres
= copy_servername_from_name(pName
, NULL
);
1483 FIXME("server %s not supported\n", debugstr_w(pName
));
1484 SetLastError(ERROR_ACCESS_DENIED
);
1488 if ((dwFileCopyFlags
& ~APD_COPY_FROM_DIRECTORY
) != APD_COPY_ALL_FILES
) {
1489 TRACE("Flags 0x%x ignored (using APD_COPY_ALL_FILES)\n", dwFileCopyFlags
& ~APD_COPY_FROM_DIRECTORY
);
1492 return myAddPrinterDriverEx(level
, pDriverInfo
, dwFileCopyFlags
, TRUE
);
1495 /******************************************************************************
1496 * fpClosePrinter [exported through PRINTPROVIDOR]
1498 * Close a printer handle and free associated resources
1501 * hPrinter [I] Printerhandle to close
1508 static BOOL WINAPI
fpClosePrinter(HANDLE hPrinter
)
1510 printer_t
*printer
= (printer_t
*) hPrinter
;
1512 TRACE("(%p)\n", hPrinter
);
1515 printer_free(printer
);
1521 /******************************************************************************
1522 * fpConfigurePort [exported through PRINTPROVIDOR]
1524 * Display the Configuration-Dialog for a specific Port
1527 * pName [I] Servername or NULL (local Computer)
1528 * hWnd [I] Handle to parent Window for the Dialog-Box
1529 * pPortName [I] Name of the Port, that should be configured
1536 static BOOL WINAPI
fpConfigurePort(LPWSTR pName
, HWND hWnd
, LPWSTR pPortName
)
1543 TRACE("(%s, %p, %s)\n", debugstr_w(pName
), hWnd
, debugstr_w(pPortName
));
1545 lres
= copy_servername_from_name(pName
, NULL
);
1547 FIXME("server %s not supported\n", debugstr_w(pName
));
1548 SetLastError(ERROR_INVALID_NAME
);
1552 /* an empty Portname is Invalid, but can popup a Dialog */
1553 if (!pPortName
[0]) {
1554 SetLastError(ERROR_NOT_SUPPORTED
);
1558 pm
= monitor_load_by_port(pPortName
);
1559 if (pm
&& pm
->monitor
&& pm
->monitor
->pfnConfigurePort
) {
1560 TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm
->name
),
1561 debugstr_w(pPortName
), pm
, debugstr_w(pm
->dllname
));
1562 res
= pm
->monitor
->pfnConfigurePort(pName
, hWnd
, pPortName
);
1563 TRACE("got %d with %u\n", res
, GetLastError());
1567 pui
= monitor_loadui(pm
);
1568 if (pui
&& pui
->monitorUI
&& pui
->monitorUI
->pfnConfigurePortUI
) {
1569 TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui
->name
),
1570 debugstr_w(pPortName
), pui
, debugstr_w(pui
->dllname
));
1571 res
= pui
->monitorUI
->pfnConfigurePortUI(pName
, hWnd
, pPortName
);
1572 TRACE("got %d with %u\n", res
, GetLastError());
1576 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1577 debugstr_w(pPortName
), pm
, debugstr_w(pm
? pm
->dllname
: NULL
),
1578 pui
, debugstr_w(pui
? pui
->dllname
: NULL
));
1580 SetLastError(ERROR_NOT_SUPPORTED
);
1583 monitor_unload(pui
);
1587 TRACE("returning %d with %u\n", res
, GetLastError());
1591 /******************************************************************
1592 * fpDeleteMonitor [exported through PRINTPROVIDOR]
1594 * Delete a specific Printmonitor from a Printing-Environment
1597 * pName [I] Servername or NULL (local Computer)
1598 * pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1599 * pMonitorName [I] Name of the Monitor, that should be deleted
1606 * pEnvironment is ignored in Windows for the local Computer.
1610 static BOOL WINAPI
fpDeleteMonitor(LPWSTR pName
, LPWSTR pEnvironment
, LPWSTR pMonitorName
)
1615 TRACE("(%s, %s, %s)\n",debugstr_w(pName
),debugstr_w(pEnvironment
),
1616 debugstr_w(pMonitorName
));
1618 lres
= copy_servername_from_name(pName
, NULL
);
1620 FIXME("server %s not supported\n", debugstr_w(pName
));
1621 SetLastError(ERROR_INVALID_NAME
);
1625 /* pEnvironment is ignored in Windows for the local Computer */
1626 if (!pMonitorName
|| !pMonitorName
[0]) {
1627 TRACE("pMonitorName %s is invalid\n", debugstr_w(pMonitorName
));
1628 SetLastError(ERROR_INVALID_PARAMETER
);
1632 if(RegCreateKeyW(HKEY_LOCAL_MACHINE
, monitorsW
, &hroot
) != ERROR_SUCCESS
) {
1633 ERR("unable to create key %s\n", debugstr_w(monitorsW
));
1637 if(RegDeleteTreeW(hroot
, pMonitorName
) == ERROR_SUCCESS
) {
1638 TRACE("%s deleted\n", debugstr_w(pMonitorName
));
1643 TRACE("%s does not exist\n", debugstr_w(pMonitorName
));
1646 /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
1647 SetLastError(ERROR_UNKNOWN_PRINT_MONITOR
);
1651 /*****************************************************************************
1652 * fpDeletePort [exported through PRINTPROVIDOR]
1654 * Delete a specific Port
1657 * pName [I] Servername or NULL (local Computer)
1658 * hWnd [I] Handle to parent Window for the Dialog-Box
1659 * pPortName [I] Name of the Port, that should be deleted
1666 static BOOL WINAPI
fpDeletePort(LPWSTR pName
, HWND hWnd
, LPWSTR pPortName
)
1673 TRACE("(%s, %p, %s)\n", debugstr_w(pName
), hWnd
, debugstr_w(pPortName
));
1675 lres
= copy_servername_from_name(pName
, NULL
);
1677 FIXME("server %s not supported\n", debugstr_w(pName
));
1678 SetLastError(ERROR_INVALID_NAME
);
1682 /* an empty Portname is Invalid */
1683 if (!pPortName
[0]) {
1684 SetLastError(ERROR_NOT_SUPPORTED
);
1688 pm
= monitor_load_by_port(pPortName
);
1689 if (pm
&& pm
->monitor
&& pm
->monitor
->pfnDeletePort
) {
1690 TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm
->name
),
1691 debugstr_w(pPortName
), pm
, debugstr_w(pm
->dllname
));
1692 res
= pm
->monitor
->pfnDeletePort(pName
, hWnd
, pPortName
);
1693 TRACE("got %d with %u\n", res
, GetLastError());
1697 pui
= monitor_loadui(pm
);
1698 if (pui
&& pui
->monitorUI
&& pui
->monitorUI
->pfnDeletePortUI
) {
1699 TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui
->name
),
1700 debugstr_w(pPortName
), pui
, debugstr_w(pui
->dllname
));
1701 res
= pui
->monitorUI
->pfnDeletePortUI(pName
, hWnd
, pPortName
);
1702 TRACE("got %d with %u\n", res
, GetLastError());
1706 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1707 debugstr_w(pPortName
), pm
, debugstr_w(pm
? pm
->dllname
: NULL
),
1708 pui
, debugstr_w(pui
? pui
->dllname
: NULL
));
1710 SetLastError(ERROR_NOT_SUPPORTED
);
1713 monitor_unload(pui
);
1717 TRACE("returning %d with %u\n", res
, GetLastError());
1721 /*****************************************************************************
1722 * fpEnumMonitors [exported through PRINTPROVIDOR]
1724 * Enumerate available Port-Monitors
1727 * pName [I] Servername or NULL (local Computer)
1728 * Level [I] Structure-Level (1:Win9x+NT or 2:NT only)
1729 * pMonitors [O] PTR to Buffer that receives the Result
1730 * cbBuf [I] Size of Buffer at pMonitors
1731 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
1732 * pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
1736 * Failure: FALSE and in pcbNeeded the Bytes required for pMonitors, if cbBuf is too small
1739 * Windows reads the Registry once and cache the Results.
1742 static BOOL WINAPI
fpEnumMonitors(LPWSTR pName
, DWORD Level
, LPBYTE pMonitors
, DWORD cbBuf
,
1743 LPDWORD pcbNeeded
, LPDWORD pcReturned
)
1745 DWORD numentries
= 0;
1750 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName
), Level
, pMonitors
,
1751 cbBuf
, pcbNeeded
, pcReturned
);
1753 lres
= copy_servername_from_name(pName
, NULL
);
1755 FIXME("server %s not supported\n", debugstr_w(pName
));
1756 SetLastError(ERROR_INVALID_NAME
);
1760 if (!Level
|| (Level
> 2)) {
1761 WARN("level (%d) is ignored in win9x\n", Level
);
1762 SetLastError(ERROR_INVALID_LEVEL
);
1766 /* Scan all Monitor-Keys */
1768 needed
= get_local_monitors(Level
, NULL
, 0, &numentries
);
1770 /* we calculated the needed buffersize. now do more error-checks */
1771 if (cbBuf
< needed
) {
1772 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1776 /* fill the Buffer with the Monitor-Keys */
1777 needed
= get_local_monitors(Level
, pMonitors
, cbBuf
, &numentries
);
1781 if (pcbNeeded
) *pcbNeeded
= needed
;
1782 if (pcReturned
) *pcReturned
= numentries
;
1784 TRACE("returning %d with %d (%d byte for %d entries)\n",
1785 res
, GetLastError(), needed
, numentries
);
1790 /******************************************************************************
1791 * fpEnumPorts [exported through PRINTPROVIDOR]
1793 * Enumerate available Ports
1796 * pName [I] Servername or NULL (local Computer)
1797 * Level [I] Structure-Level (1 or 2)
1798 * pPorts [O] PTR to Buffer that receives the Result
1799 * cbBuf [I] Size of Buffer at pPorts
1800 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
1801 * pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
1805 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
1808 static BOOL WINAPI
fpEnumPorts(LPWSTR pName
, DWORD Level
, LPBYTE pPorts
, DWORD cbBuf
,
1809 LPDWORD pcbNeeded
, LPDWORD pcReturned
)
1812 DWORD numentries
= 0;
1816 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName
), Level
, pPorts
,
1817 cbBuf
, pcbNeeded
, pcReturned
);
1819 lres
= copy_servername_from_name(pName
, NULL
);
1821 FIXME("server %s not supported\n", debugstr_w(pName
));
1822 SetLastError(ERROR_INVALID_NAME
);
1826 if (!Level
|| (Level
> 2)) {
1827 SetLastError(ERROR_INVALID_LEVEL
);
1831 if (!pcbNeeded
|| (!pPorts
&& (cbBuf
> 0))) {
1832 SetLastError(RPC_X_NULL_REF_POINTER
);
1836 EnterCriticalSection(&monitor_handles_cs
);
1839 /* Scan all local Ports */
1841 needed
= get_ports_from_all_monitors(Level
, NULL
, 0, &numentries
);
1843 /* we calculated the needed buffersize. now do the error-checks */
1844 if (cbBuf
< needed
) {
1845 monitor_unloadall();
1846 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1847 goto emP_cleanup_cs
;
1849 else if (!pPorts
|| !pcReturned
) {
1850 monitor_unloadall();
1851 SetLastError(RPC_X_NULL_REF_POINTER
);
1852 goto emP_cleanup_cs
;
1855 /* Fill the Buffer */
1856 needed
= get_ports_from_all_monitors(Level
, pPorts
, cbBuf
, &numentries
);
1858 monitor_unloadall();
1861 LeaveCriticalSection(&monitor_handles_cs
);
1864 if (pcbNeeded
) *pcbNeeded
= needed
;
1865 if (pcReturned
) *pcReturned
= (res
) ? numentries
: 0;
1867 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
1868 (res
), GetLastError(), needed
, (res
) ? numentries
: 0, numentries
);
1873 /******************************************************************************
1874 * fpOpenPrinter [exported through PRINTPROVIDOR]
1876 * Open a Printer / Printserver or a Printer-Object
1879 * lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
1880 * pPrinter [O] The resulting Handle is stored here
1881 * pDefaults [I] PTR to Default Printer Settings or NULL
1888 * lpPrinterName is one of:
1889 *| Printserver (NT only): "Servername" or NULL for the local Printserver
1890 *| Printer: "PrinterName"
1891 *| Printer-Object: "PrinterName,Job xxx"
1892 *| XcvMonitor: "Servername,XcvMonitor MonitorName"
1893 *| XcvPort: "Servername,XcvPort PortName"
1897 static BOOL WINAPI
fpOpenPrinter(LPWSTR lpPrinterName
, HANDLE
*pPrinter
,
1898 LPPRINTER_DEFAULTSW pDefaults
)
1901 TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName
), pPrinter
, pDefaults
);
1903 *pPrinter
= printer_alloc_handle(lpPrinterName
, pDefaults
);
1905 return (*pPrinter
!= 0);
1908 /******************************************************************************
1909 * fpXcvData [exported through PRINTPROVIDOR]
1911 * Execute commands in the Printmonitor DLL
1914 * hXcv [i] Handle from fpOpenPrinter (with XcvMonitor or XcvPort)
1915 * pszDataName [i] Name of the command to execute
1916 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
1917 * cbInputData [i] Size in Bytes of Buffer at pInputData
1918 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
1919 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
1920 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
1921 * pdwStatus [o] PTR to receive the win32 error code from the Printmonitor DLL
1928 * Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
1929 * The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
1931 * Minimal List of commands, that a Printmonitor DLL should support:
1933 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
1934 *| "AddPort" : Add a Port
1935 *| "DeletePort": Delete a Port
1937 * Many Printmonitors support additional commands. Examples for localspl.dll:
1938 * "GetDefaultCommConfig", "SetDefaultCommConfig",
1939 * "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
1942 static BOOL WINAPI
fpXcvData(HANDLE hXcv
, LPCWSTR pszDataName
, PBYTE pInputData
,
1943 DWORD cbInputData
, PBYTE pOutputData
, DWORD cbOutputData
,
1944 PDWORD pcbOutputNeeded
, PDWORD pdwStatus
)
1946 printer_t
*printer
= (printer_t
* ) hXcv
;
1948 TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv
, debugstr_w(pszDataName
),
1949 pInputData
, cbInputData
, pOutputData
,
1950 cbOutputData
, pcbOutputNeeded
, pdwStatus
);
1952 if (!printer
|| (!printer
->hXcv
)) {
1953 SetLastError(ERROR_INVALID_HANDLE
);
1957 if (!pcbOutputNeeded
) {
1958 SetLastError(ERROR_INVALID_PARAMETER
);
1962 if (!pszDataName
|| !pdwStatus
|| (!pOutputData
&& (cbOutputData
> 0))) {
1963 SetLastError(RPC_X_NULL_REF_POINTER
);
1967 *pcbOutputNeeded
= 0;
1969 *pdwStatus
= printer
->pm
->monitor
->pfnXcvDataPort(printer
->hXcv
, pszDataName
,
1970 pInputData
, cbInputData
, pOutputData
, cbOutputData
, pcbOutputNeeded
);
1975 /*****************************************************
1976 * setup_provider [internal]
1978 void setup_provider(void)
1980 static const PRINTPROVIDOR backend
= {
1982 NULL
, /* fpSetJob */
1983 NULL
, /* fpGetJob */
1984 NULL
, /* fpEnumJobs */
1985 NULL
, /* fpAddPrinter */
1986 NULL
, /* fpDeletePrinter */
1987 NULL
, /* fpSetPrinter */
1988 NULL
, /* fpGetPrinter */
1989 NULL
, /* fpEnumPrinters */
1990 NULL
, /* fpAddPrinterDriver */
1991 NULL
, /* fpEnumPrinterDrivers */
1992 NULL
, /* fpGetPrinterDriver */
1993 fpGetPrinterDriverDirectory
,
1994 NULL
, /* fpDeletePrinterDriver */
1995 NULL
, /* fpAddPrintProcessor */
1996 NULL
, /* fpEnumPrintProcessors */
1997 NULL
, /* fpGetPrintProcessorDirectory */
1998 NULL
, /* fpDeletePrintProcessor */
1999 NULL
, /* fpEnumPrintProcessorDatatypes */
2000 NULL
, /* fpStartDocPrinter */
2001 NULL
, /* fpStartPagePrinter */
2002 NULL
, /* fpWritePrinter */
2003 NULL
, /* fpEndPagePrinter */
2004 NULL
, /* fpAbortPrinter */
2005 NULL
, /* fpReadPrinter */
2006 NULL
, /* fpEndDocPrinter */
2007 NULL
, /* fpAddJob */
2008 NULL
, /* fpScheduleJob */
2009 NULL
, /* fpGetPrinterData */
2010 NULL
, /* fpSetPrinterData */
2011 NULL
, /* fpWaitForPrinterChange */
2013 NULL
, /* fpAddForm */
2014 NULL
, /* fpDeleteForm */
2015 NULL
, /* fpGetForm */
2016 NULL
, /* fpSetForm */
2017 NULL
, /* fpEnumForms */
2020 NULL
, /* fpAddPort */
2023 NULL
, /* fpCreatePrinterIC */
2024 NULL
, /* fpPlayGdiScriptOnPrinterIC */
2025 NULL
, /* fpDeletePrinterIC */
2026 NULL
, /* fpAddPrinterConnection */
2027 NULL
, /* fpDeletePrinterConnection */
2028 NULL
, /* fpPrinterMessageBox */
2031 NULL
, /* fpResetPrinter */
2032 NULL
, /* fpGetPrinterDriverEx */
2033 NULL
, /* fpFindFirstPrinterChangeNotification */
2034 NULL
, /* fpFindClosePrinterChangeNotification */
2035 NULL
, /* fpAddPortEx */
2036 NULL
, /* fpShutDown */
2037 NULL
, /* fpRefreshPrinterChangeNotification */
2038 NULL
, /* fpOpenPrinterEx */
2039 NULL
, /* fpAddPrinterEx */
2040 NULL
, /* fpSetPort */
2041 NULL
, /* fpEnumPrinterData */
2042 NULL
, /* fpDeletePrinterData */
2043 NULL
, /* fpClusterSplOpen */
2044 NULL
, /* fpClusterSplClose */
2045 NULL
, /* fpClusterSplIsAlive */
2046 NULL
, /* fpSetPrinterDataEx */
2047 NULL
, /* fpGetPrinterDataEx */
2048 NULL
, /* fpEnumPrinterDataEx */
2049 NULL
, /* fpEnumPrinterKey */
2050 NULL
, /* fpDeletePrinterDataEx */
2051 NULL
, /* fpDeletePrinterKey */
2052 NULL
, /* fpSeekPrinter */
2053 NULL
, /* fpDeletePrinterDriverEx */
2054 NULL
, /* fpAddPerMachineConnection */
2055 NULL
, /* fpDeletePerMachineConnection */
2056 NULL
, /* fpEnumPerMachineConnections */
2058 fpAddPrinterDriverEx
,
2059 NULL
, /* fpSplReadPrinter */
2060 NULL
, /* fpDriverUnloadComplete */
2061 NULL
, /* fpGetSpoolFileInfo */
2062 NULL
, /* fpCommitSpoolData */
2063 NULL
, /* fpCloseSpoolFileHandle */
2064 NULL
, /* fpFlushPrinter */
2065 NULL
, /* fpSendRecvBidiData */
2066 NULL
/* fpAddDriverCatalog */
2068 pprovider
= &backend
;
2072 /*****************************************************
2073 * InitializePrintProvidor (localspl.@)
2075 * Initialize the Printprovider
2078 * pPrintProvidor [I] Buffer to fill with a struct PRINTPROVIDOR
2079 * cbPrintProvidor [I] Size of Buffer in Bytes
2080 * pFullRegistryPath [I] Registry-Path for the Printprovidor
2083 * Success: TRUE and pPrintProvidor filled
2087 * The RegistryPath should be:
2088 * "System\CurrentControlSet\Control\Print\Providers\<providername>",
2089 * but this Parameter is ignored in "localspl.dll".
2093 BOOL WINAPI
InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor
,
2094 DWORD cbPrintProvidor
, LPWSTR pFullRegistryPath
)
2097 TRACE("(%p, %u, %s)\n", pPrintProvidor
, cbPrintProvidor
, debugstr_w(pFullRegistryPath
));
2098 memcpy(pPrintProvidor
, pprovider
,
2099 (cbPrintProvidor
< sizeof(PRINTPROVIDOR
)) ? cbPrintProvidor
: sizeof(PRINTPROVIDOR
));