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 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
)
364 LeaveCriticalSection(&monitor_handles_cs
);
367 /******************************************************************
368 * monitor_load [internal]
370 * load a printmonitor, get the dllname from the registry, when needed
371 * initialize the monitor and dump found function-pointers
373 * On failure, SetLastError() is called and NULL is returned
376 static monitor_t
* monitor_load(LPCWSTR name
, LPWSTR dllname
)
378 LPMONITOR2 (WINAPI
*pInitializePrintMonitor2
) (PMONITORINIT
, LPHANDLE
);
379 PMONITORUI (WINAPI
*pInitializePrintMonitorUI
)(VOID
);
380 LPMONITOREX (WINAPI
*pInitializePrintMonitor
) (LPWSTR
);
381 DWORD (WINAPI
*pInitializeMonitorEx
)(LPWSTR
, LPMONITOR
);
382 DWORD (WINAPI
*pInitializeMonitor
) (LPWSTR
);
384 monitor_t
* pm
= NULL
;
386 LPWSTR regroot
= NULL
;
387 LPWSTR driver
= dllname
;
389 TRACE("(%s, %s)\n", debugstr_w(name
), debugstr_w(dllname
));
390 /* Is the Monitor already loaded? */
391 EnterCriticalSection(&monitor_handles_cs
);
394 LIST_FOR_EACH_ENTRY(cursor
, &monitor_handles
, monitor_t
, entry
)
396 if (cursor
->name
&& (lstrcmpW(name
, cursor
->name
) == 0)) {
404 pm
= heap_alloc_zero(sizeof(monitor_t
));
405 if (pm
== NULL
) goto cleanup
;
406 list_add_tail(&monitor_handles
, &pm
->entry
);
410 if (pm
->name
== NULL
) {
411 /* Load the monitor */
412 LPMONITOREX pmonitorEx
;
416 len
= lstrlenW(monitorsW
) + lstrlenW(name
) + 2;
417 regroot
= heap_alloc(len
* sizeof(WCHAR
));
421 lstrcpyW(regroot
, monitorsW
);
422 lstrcatW(regroot
, name
);
423 /* Get the Driver from the Registry */
424 if (driver
== NULL
) {
427 if (RegOpenKeyW(HKEY_LOCAL_MACHINE
, regroot
, &hroot
) == ERROR_SUCCESS
) {
428 if (RegQueryValueExW(hroot
, driverW
, NULL
, NULL
, NULL
,
429 &namesize
) == ERROR_SUCCESS
) {
430 driver
= heap_alloc(namesize
);
431 RegQueryValueExW(hroot
, driverW
, NULL
, NULL
, (LPBYTE
) driver
, &namesize
) ;
438 pm
->name
= strdupW(name
);
439 pm
->dllname
= strdupW(driver
);
441 if ((name
&& (!regroot
|| !pm
->name
)) || !pm
->dllname
) {
443 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
448 pm
->hdll
= LoadLibraryW(driver
);
449 TRACE("%p: LoadLibrary(%s) => %d\n", pm
->hdll
, debugstr_w(driver
), GetLastError());
451 if (pm
->hdll
== NULL
) {
453 SetLastError(ERROR_MOD_NOT_FOUND
);
458 pInitializePrintMonitor2
= (void *)GetProcAddress(pm
->hdll
, "InitializePrintMonitor2");
459 pInitializePrintMonitorUI
= (void *)GetProcAddress(pm
->hdll
, "InitializePrintMonitorUI");
460 pInitializePrintMonitor
= (void *)GetProcAddress(pm
->hdll
, "InitializePrintMonitor");
461 pInitializeMonitorEx
= (void *)GetProcAddress(pm
->hdll
, "InitializeMonitorEx");
462 pInitializeMonitor
= (void *)GetProcAddress(pm
->hdll
, "InitializeMonitor");
465 TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2
, debugstr_w(driver
));
466 TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI
, debugstr_w(driver
));
467 TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor
, debugstr_w(driver
));
468 TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx
, debugstr_w(driver
));
469 TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor
, debugstr_w(driver
));
471 if (pInitializePrintMonitorUI
!= NULL
) {
472 pm
->monitorUI
= pInitializePrintMonitorUI();
473 TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm
->monitorUI
, debugstr_w(driver
));
475 TRACE("0x%08x: dwMonitorSize (%d)\n",
476 pm
->monitorUI
->dwMonitorUISize
, pm
->monitorUI
->dwMonitorUISize
);
481 if (pInitializePrintMonitor
&& regroot
) {
482 pmonitorEx
= pInitializePrintMonitor(regroot
);
483 TRACE("%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
484 pmonitorEx
, debugstr_w(driver
), debugstr_w(regroot
));
487 pm
->dwMonitorSize
= pmonitorEx
->dwMonitorSize
;
488 pm
->monitor
= &(pmonitorEx
->Monitor
);
493 TRACE("0x%08x: dwMonitorSize (%d)\n", pm
->dwMonitorSize
, pm
->dwMonitorSize
);
497 if (!pm
->monitor
&& regroot
) {
498 if (pInitializePrintMonitor2
!= NULL
) {
499 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver
));
501 if (pInitializeMonitorEx
!= NULL
) {
502 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver
));
504 if (pInitializeMonitor
!= NULL
) {
505 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver
));
508 if (!pm
->monitor
&& !pm
->monitorUI
) {
510 SetLastError(ERROR_PROC_NOT_FOUND
);
515 if ((pm_localport
== NULL
) && (pm
!= NULL
) && (lstrcmpW(pm
->name
, localportW
) == 0)) {
519 LeaveCriticalSection(&monitor_handles_cs
);
520 if (driver
!= dllname
) heap_free(driver
);
522 TRACE("=> %p\n", pm
);
526 /******************************************************************
527 * monitor_loadall [internal]
529 * Load all registered monitors
532 static DWORD
monitor_loadall(void)
535 DWORD registered
= 0;
538 WCHAR buffer
[MAX_PATH
];
541 if (RegOpenKeyW(HKEY_LOCAL_MACHINE
, monitorsW
, &hmonitors
) == ERROR_SUCCESS
) {
542 RegQueryInfoKeyW(hmonitors
, NULL
, NULL
, NULL
, ®istered
, NULL
, NULL
,
543 NULL
, NULL
, NULL
, NULL
, NULL
);
545 TRACE("%d monitors registered\n", registered
);
547 while (id
< registered
) {
549 RegEnumKeyW(hmonitors
, id
, buffer
, MAX_PATH
);
550 pm
= monitor_load(buffer
, NULL
);
554 RegCloseKey(hmonitors
);
556 TRACE("%d monitors loaded\n", loaded
);
560 /******************************************************************
561 * monitor_loadui [internal]
563 * load the userinterface-dll for a given portmonitor
565 * On failure, NULL is returned
567 static monitor_t
* monitor_loadui(monitor_t
* pm
)
569 monitor_t
* pui
= NULL
;
570 LPWSTR buffer
[MAX_PATH
];
575 if (pm
== NULL
) return NULL
;
576 TRACE("(%p) => dllname: %s\n", pm
, debugstr_w(pm
->dllname
));
578 /* Try the Portmonitor first; works for many monitors */
580 EnterCriticalSection(&monitor_handles_cs
);
582 LeaveCriticalSection(&monitor_handles_cs
);
586 /* query the userinterface-dllname from the Portmonitor */
587 if ((pm
->monitor
) && (pm
->monitor
->pfnXcvDataPort
)) {
588 /* building (",XcvMonitor %s",pm->name) not needed yet */
589 res
= pm
->monitor
->pfnXcvOpenPort(emptyW
, SERVER_ACCESS_ADMINISTER
, &hXcv
);
590 TRACE("got %u with %p\n", res
, hXcv
);
592 res
= pm
->monitor
->pfnXcvDataPort(hXcv
, monitorUIW
, NULL
, 0, (BYTE
*) buffer
, sizeof(buffer
), &len
);
593 TRACE("got %u with %s\n", res
, debugstr_w((LPWSTR
) buffer
));
594 if (res
== ERROR_SUCCESS
) pui
= monitor_load(NULL
, (LPWSTR
) buffer
);
595 pm
->monitor
->pfnXcvClosePort(hXcv
);
601 /******************************************************************
602 * monitor_load_by_port [internal]
604 * load a printmonitor for a given port
606 * On failure, NULL is returned
609 static monitor_t
* monitor_load_by_port(LPCWSTR portname
)
614 monitor_t
* pm
= NULL
;
615 DWORD registered
= 0;
619 TRACE("(%s)\n", debugstr_w(portname
));
621 /* Try the Local Monitor first */
622 if (RegOpenKeyW(HKEY_LOCAL_MACHINE
, winnt_cv_portsW
, &hroot
) == ERROR_SUCCESS
) {
623 if (RegQueryValueExW(hroot
, portname
, NULL
, NULL
, NULL
, &len
) == ERROR_SUCCESS
) {
624 /* found the portname */
626 return monitor_load(localportW
, NULL
);
631 len
= MAX_PATH
+ lstrlenW(bs_ports_bsW
) + lstrlenW(portname
) + 1;
632 buffer
= heap_alloc(len
* sizeof(WCHAR
));
633 if (buffer
== NULL
) return NULL
;
635 if (RegOpenKeyW(HKEY_LOCAL_MACHINE
, monitorsW
, &hroot
) == ERROR_SUCCESS
) {
636 EnterCriticalSection(&monitor_handles_cs
);
637 RegQueryInfoKeyW(hroot
, NULL
, NULL
, NULL
, ®istered
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
639 while ((pm
== NULL
) && (id
< registered
)) {
641 RegEnumKeyW(hroot
, id
, buffer
, MAX_PATH
);
642 TRACE("testing %s\n", debugstr_w(buffer
));
643 len
= lstrlenW(buffer
);
644 lstrcatW(buffer
, bs_ports_bsW
);
645 lstrcatW(buffer
, portname
);
646 if (RegOpenKeyW(hroot
, buffer
, &hport
) == ERROR_SUCCESS
) {
648 buffer
[len
] = '\0'; /* use only the Monitor-Name */
649 pm
= monitor_load(buffer
, NULL
);
653 LeaveCriticalSection(&monitor_handles_cs
);
660 /******************************************************************
661 * Return the number of bytes for an multi_sz string.
662 * The result includes all \0s
663 * (specifically the extra \0, that is needed as multi_sz terminator).
665 static int multi_sz_lenW(const WCHAR
*str
)
667 const WCHAR
*ptr
= str
;
671 ptr
+= lstrlenW(ptr
) + 1;
674 return (ptr
- str
+ 1) * sizeof(WCHAR
);
677 /******************************************************************
678 * validate_envW [internal]
680 * validate the user-supplied printing-environment
683 * env [I] PTR to Environment-String or NULL
686 * Success: PTR to printenv_t
687 * Failure: NULL and ERROR_INVALID_ENVIRONMENT
690 * An empty string is handled the same way as NULL.
694 static const printenv_t
* validate_envW(LPCWSTR env
)
696 const printenv_t
*result
= NULL
;
699 TRACE("(%s)\n", debugstr_w(env
));
702 for (i
= 0; i
< sizeof(all_printenv
)/sizeof(all_printenv
[0]); i
++)
704 if (lstrcmpiW(env
, all_printenv
[i
]->envname
) == 0)
706 result
= all_printenv
[i
];
710 if (result
== NULL
) {
711 FIXME("unsupported Environment: %s\n", debugstr_w(env
));
712 SetLastError(ERROR_INVALID_ENVIRONMENT
);
714 /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
718 result
= (GetVersion() & 0x80000000) ? &env_win40
: &env_x86
;
721 TRACE("=> using %p: %s\n", result
, debugstr_w(result
? result
->envname
: NULL
));
725 /*****************************************************************************
726 * enumerate the local monitors (INTERNAL)
728 * returns the needed size (in bytes) for pMonitors
729 * and *lpreturned is set to number of entries returned in pMonitors
731 * Language-Monitors are also installed in the same Registry-Location but
732 * they are filtered in Windows (not returned by EnumMonitors).
733 * We do no filtering to simplify our Code.
736 static DWORD
get_local_monitors(DWORD level
, LPBYTE pMonitors
, DWORD cbBuf
, LPDWORD lpreturned
)
741 LPMONITOR_INFO_2W mi
;
742 WCHAR buffer
[MAX_PATH
];
743 WCHAR dllname
[MAX_PATH
];
751 entrysize
= (level
== 1) ? sizeof(MONITOR_INFO_1W
) : sizeof(MONITOR_INFO_2W
);
753 numentries
= *lpreturned
; /* this is 0, when we scan the registry */
754 len
= entrysize
* numentries
;
755 ptr
= (LPWSTR
) &pMonitors
[len
];
758 len
= sizeof(buffer
)/sizeof(buffer
[0]);
761 /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
762 if (RegCreateKeyW(HKEY_LOCAL_MACHINE
, monitorsW
, &hroot
) == ERROR_SUCCESS
) {
763 /* Scan all Monitor-Registry-Keys */
764 while (RegEnumKeyExW(hroot
, index
, buffer
, &len
, NULL
, NULL
, NULL
, NULL
) == ERROR_SUCCESS
) {
765 TRACE("Monitor_%d: %s\n", numentries
, debugstr_w(buffer
));
766 dllsize
= sizeof(dllname
);
769 /* The Monitor must have a Driver-DLL */
770 if (RegOpenKeyExW(hroot
, buffer
, 0, KEY_READ
, &hentry
) == ERROR_SUCCESS
) {
771 if (RegQueryValueExW(hentry
, driverW
, NULL
, NULL
, (LPBYTE
) dllname
, &dllsize
) == ERROR_SUCCESS
) {
772 /* We found a valid DLL for this Monitor. */
773 TRACE("using Driver: %s\n", debugstr_w(dllname
));
778 /* Windows returns only Port-Monitors here, but to simplify our code,
779 we do no filtering for Language-Monitors */
783 needed
+= (len
+1) * sizeof(WCHAR
); /* len is lstrlenW(monitorname) */
785 /* we install and return only monitors for "Windows NT x86" */
786 needed
+= (lstrlenW(x86_envnameW
) +1) * sizeof(WCHAR
);
790 /* required size is calculated. Now fill the user-buffer */
791 if (pMonitors
&& (cbBuf
>= needed
)){
792 mi
= (LPMONITOR_INFO_2W
) pMonitors
;
793 pMonitors
+= entrysize
;
795 TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi
, level
, numentries
);
797 lstrcpyW(ptr
, buffer
); /* Name of the Monitor */
798 ptr
+= (len
+1); /* len is lstrlenW(monitorname) */
800 mi
->pEnvironment
= ptr
;
801 lstrcpyW(ptr
, x86_envnameW
); /* fixed to "Windows NT x86" */
802 ptr
+= (lstrlenW(x86_envnameW
)+1);
805 lstrcpyW(ptr
, dllname
); /* Name of the Driver-DLL */
806 ptr
+= (dllsize
/ sizeof(WCHAR
));
811 len
= sizeof(buffer
)/sizeof(buffer
[0]);
816 *lpreturned
= numentries
;
817 TRACE("need %d byte for %d entries\n", needed
, numentries
);
821 /******************************************************************
822 * enumerate the local Ports from all loaded monitors (internal)
824 * returns the needed size (in bytes) for pPorts
825 * and *lpreturned is set to number of entries returned in pPorts
828 static DWORD
get_ports_from_all_monitors(DWORD level
, LPBYTE pPorts
, DWORD cbBuf
, LPDWORD lpreturned
)
832 LPPORT_INFO_2W cache
;
834 LPBYTE pi_buffer
= NULL
;
835 DWORD pi_allocated
= 0;
846 TRACE("(%d, %p, %d, %p)\n", level
, pPorts
, cbBuf
, lpreturned
);
847 entrysize
= (level
== 1) ? sizeof(PORT_INFO_1W
) : sizeof(PORT_INFO_2W
);
849 numentries
= *lpreturned
; /* this is 0, when we scan the registry */
850 needed
= entrysize
* numentries
;
851 ptr
= (LPWSTR
) &pPorts
[needed
];
856 LIST_FOR_EACH_ENTRY(pm
, &monitor_handles
, monitor_t
, entry
)
858 if ((pm
->monitor
) && (pm
->monitor
->pfnEnumPorts
)) {
861 res
= pm
->monitor
->pfnEnumPorts(NULL
, level
, pi_buffer
, pi_allocated
, &pi_needed
, &pi_returned
);
862 if (!res
&& (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)) {
863 /* Do not use heap_realloc (we do not need the old data in the buffer) */
864 heap_free(pi_buffer
);
865 pi_buffer
= heap_alloc(pi_needed
);
866 pi_allocated
= (pi_buffer
) ? pi_needed
: 0;
867 res
= pm
->monitor
->pfnEnumPorts(NULL
, level
, pi_buffer
, pi_allocated
, &pi_needed
, &pi_returned
);
869 TRACE("(%s) got %d with %d (need %d byte for %d entries)\n",
870 debugstr_w(pm
->name
), res
, GetLastError(), pi_needed
, pi_returned
);
872 numentries
+= pi_returned
;
875 /* fill the output-buffer (pPorts), if we have one */
876 if (pPorts
&& (cbBuf
>= needed
) && pi_buffer
) {
878 while (pi_returned
> pi_index
) {
879 cache
= (LPPORT_INFO_2W
) &pi_buffer
[pi_index
* entrysize
];
880 out
= (LPPORT_INFO_2W
) &pPorts
[outindex
* entrysize
];
881 out
->pPortName
= ptr
;
882 lstrcpyW(ptr
, cache
->pPortName
);
883 ptr
+= (lstrlenW(ptr
)+1);
885 out
->pMonitorName
= ptr
;
886 lstrcpyW(ptr
, cache
->pMonitorName
);
887 ptr
+= (lstrlenW(ptr
)+1);
889 out
->pDescription
= ptr
;
890 lstrcpyW(ptr
, cache
->pDescription
);
891 ptr
+= (lstrlenW(ptr
)+1);
892 out
->fPortType
= cache
->fPortType
;
893 out
->Reserved
= cache
->Reserved
;
901 /* the temporary portinfo-buffer is no longer needed */
902 heap_free(pi_buffer
);
904 *lpreturned
= numentries
;
905 TRACE("need %d byte for %d entries\n", needed
, numentries
);
910 /*****************************************************************************
911 * open_driver_reg [internal]
913 * opens the registry for the printer drivers depending on the given input
914 * variable pEnvironment
917 * Success: the opened hkey
920 static HKEY
open_driver_reg(LPCWSTR pEnvironment
)
924 const printenv_t
* env
;
926 TRACE("(%s)\n", debugstr_w(pEnvironment
));
928 env
= validate_envW(pEnvironment
);
929 if (!env
) return NULL
;
931 buffer
= HeapAlloc(GetProcessHeap(), 0, sizeof(fmt_driversW
) +
932 (lstrlenW(env
->envname
) + lstrlenW(env
->versionregpath
)) * sizeof(WCHAR
));
935 wsprintfW(buffer
, fmt_driversW
, env
->envname
, env
->versionregpath
);
936 RegCreateKeyW(HKEY_LOCAL_MACHINE
, buffer
, &retval
);
937 HeapFree(GetProcessHeap(), 0, buffer
);
942 /*****************************************************************************
943 * fpGetPrinterDriverDirectory [exported through PRINTPROVIDOR]
945 * Return the PATH for the Printer-Drivers
948 * pName [I] Servername (NT only) or NULL (local Computer)
949 * pEnvironment [I] Printing-Environment (see below) or NULL (Default)
950 * Level [I] Structure-Level (must be 1)
951 * pDriverDirectory [O] PTR to Buffer that receives the Result
952 * cbBuf [I] Size of Buffer at pDriverDirectory
953 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
954 * required for pDriverDirectory
957 * Success: TRUE and in pcbNeeded the Bytes used in pDriverDirectory
958 * Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
959 * if cbBuf is too small
961 * Native Values returned in pDriverDirectory on Success:
962 *| NT(Windows NT x86): "%winsysdir%\\spool\\DRIVERS\\w32x86"
963 *| NT(Windows 4.0): "%winsysdir%\\spool\\DRIVERS\\win40"
964 *| win9x(Windows 4.0): "%winsysdir%"
966 * "%winsysdir%" is the Value from GetSystemDirectoryW()
969 static BOOL WINAPI
fpGetPrinterDriverDirectory(LPWSTR pName
, LPWSTR pEnvironment
,
970 DWORD Level
, LPBYTE pDriverDirectory
, DWORD cbBuf
, LPDWORD pcbNeeded
)
973 const printenv_t
* env
;
975 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName
),
976 debugstr_w(pEnvironment
), Level
, pDriverDirectory
, cbBuf
, pcbNeeded
);
978 if (pName
!= NULL
&& pName
[0]) {
979 FIXME("server %s not supported\n", debugstr_w(pName
));
980 SetLastError(ERROR_INVALID_PARAMETER
);
984 env
= validate_envW(pEnvironment
);
985 if (!env
) return FALSE
; /* pEnvironment invalid or unsupported */
988 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
989 needed
= GetSystemDirectoryW(NULL
, 0);
990 /* add the Size for the Subdirectories */
991 needed
+= lstrlenW(spooldriversW
);
992 needed
+= lstrlenW(env
->subdir
);
993 needed
*= sizeof(WCHAR
); /* return-value is size in Bytes */
997 if (needed
> cbBuf
) {
998 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1002 if (pDriverDirectory
== NULL
) {
1003 /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
1004 SetLastError(ERROR_INVALID_USER_BUFFER
);
1008 GetSystemDirectoryW((LPWSTR
) pDriverDirectory
, cbBuf
/sizeof(WCHAR
));
1009 /* add the Subdirectories */
1010 lstrcatW((LPWSTR
) pDriverDirectory
, spooldriversW
);
1011 lstrcatW((LPWSTR
) pDriverDirectory
, env
->subdir
);
1013 TRACE("=> %s\n", debugstr_w((LPWSTR
) pDriverDirectory
));
1017 /******************************************************************
1018 * driver_load [internal]
1020 * load a driver user interface dll
1022 * On failure, NULL is returned
1026 static HMODULE
driver_load(const printenv_t
* env
, LPWSTR dllname
)
1028 WCHAR fullname
[MAX_PATH
];
1032 TRACE("(%p, %s)\n", env
, debugstr_w(dllname
));
1034 /* build the driverdir */
1035 len
= sizeof(fullname
) -
1036 (lstrlenW(env
->versionsubdir
) + 1 + lstrlenW(dllname
) + 1) * sizeof(WCHAR
);
1038 if (!fpGetPrinterDriverDirectory(NULL
, (LPWSTR
) env
->envname
, 1,
1039 (LPBYTE
) fullname
, len
, &len
)) {
1040 /* Should never Fail */
1041 SetLastError(ERROR_BUFFER_OVERFLOW
);
1045 lstrcatW(fullname
, env
->versionsubdir
);
1046 lstrcatW(fullname
, backslashW
);
1047 lstrcatW(fullname
, dllname
);
1049 hui
= LoadLibraryW(fullname
);
1050 TRACE("%p: LoadLibrary(%s) %d\n", hui
, debugstr_w(fullname
), GetLastError());
1055 /******************************************************************
1057 * free the data pointer of an opened printer
1059 static VOID
printer_free(printer_t
* printer
)
1062 printer
->pm
->monitor
->pfnXcvClosePort(printer
->hXcv
);
1064 monitor_unload(printer
->pm
);
1066 heap_free(printer
->printername
);
1067 heap_free(printer
->name
);
1071 /******************************************************************
1072 * printer_alloc_handle
1073 * alloc a printer handle and remember the data pointer in the printer handle table
1076 static HANDLE
printer_alloc_handle(LPCWSTR name
, LPPRINTER_DEFAULTSW pDefault
)
1078 WCHAR servername
[MAX_COMPUTERNAME_LENGTH
+ 1];
1079 printer_t
*printer
= NULL
;
1080 LPCWSTR printername
;
1085 if (copy_servername_from_name(name
, servername
)) {
1086 FIXME("server %s not supported\n", debugstr_w(servername
));
1087 SetLastError(ERROR_INVALID_PRINTER_NAME
);
1091 printername
= get_basename_from_name(name
);
1092 if (name
!= printername
) TRACE("converted %s to %s\n", debugstr_w(name
), debugstr_w(printername
));
1094 /* an empty printername is invalid */
1095 if (printername
&& (!printername
[0])) {
1096 SetLastError(ERROR_INVALID_PARAMETER
);
1100 printer
= heap_alloc_zero(sizeof(printer_t
));
1101 if (!printer
) goto end
;
1103 /* clone the base name. This is NULL for the printserver */
1104 printer
->printername
= strdupW(printername
);
1106 /* clone the full name */
1107 printer
->name
= strdupW(name
);
1108 if (name
&& (!printer
->name
)) {
1109 printer_free(printer
);
1113 len
= sizeof(XcvMonitorW
)/sizeof(WCHAR
) - 1;
1114 if (strncmpW(printername
, XcvMonitorW
, len
) == 0) {
1115 /* OpenPrinter(",XcvMonitor ", ...) detected */
1116 TRACE(",XcvMonitor: %s\n", debugstr_w(&printername
[len
]));
1117 printer
->pm
= monitor_load(&printername
[len
], NULL
);
1118 if (printer
->pm
== NULL
) {
1119 printer_free(printer
);
1120 SetLastError(ERROR_UNKNOWN_PORT
);
1127 len
= sizeof(XcvPortW
)/sizeof(WCHAR
) - 1;
1128 if (strncmpW( printername
, XcvPortW
, len
) == 0) {
1129 /* OpenPrinter(",XcvPort ", ...) detected */
1130 TRACE(",XcvPort: %s\n", debugstr_w(&printername
[len
]));
1131 printer
->pm
= monitor_load_by_port(&printername
[len
]);
1132 if (printer
->pm
== NULL
) {
1133 printer_free(printer
);
1134 SetLastError(ERROR_UNKNOWN_PORT
);
1142 if ((printer
->pm
->monitor
) && (printer
->pm
->monitor
->pfnXcvOpenPort
)) {
1143 printer
->pm
->monitor
->pfnXcvOpenPort(&printername
[len
],
1144 pDefault
? pDefault
->DesiredAccess
: 0,
1147 if (printer
->hXcv
== NULL
) {
1148 printer_free(printer
);
1149 SetLastError(ERROR_INVALID_PARAMETER
);
1156 /* Does the Printer exist? */
1157 if (RegCreateKeyW(HKEY_LOCAL_MACHINE
, printersW
, &hkeyPrinters
) != ERROR_SUCCESS
) {
1158 ERR("Can't create Printers key\n");
1159 printer_free(printer
);
1160 SetLastError(ERROR_INVALID_PRINTER_NAME
);
1164 if (RegOpenKeyW(hkeyPrinters
, printername
, &hkeyPrinter
) != ERROR_SUCCESS
) {
1165 WARN("Printer not found in Registry: %s\n", debugstr_w(printername
));
1166 RegCloseKey(hkeyPrinters
);
1167 printer_free(printer
);
1168 SetLastError(ERROR_INVALID_PRINTER_NAME
);
1172 RegCloseKey(hkeyPrinter
);
1173 RegCloseKey(hkeyPrinters
);
1178 TRACE("using the local printserver\n");
1183 TRACE("==> %p\n", printer
);
1184 return (HANDLE
)printer
;
1188 /******************************************************************************
1189 * myAddPrinterDriverEx [internal]
1191 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1192 * and a special mode with lazy error checking.
1195 static BOOL
myAddPrinterDriverEx(DWORD level
, LPBYTE pDriverInfo
, DWORD dwFileCopyFlags
, BOOL lazy
)
1197 const printenv_t
*env
;
1200 BOOL (WINAPI
*pDrvDriverEvent
)(DWORD
, DWORD
, LPBYTE
, LPARAM
);
1210 /* we need to set all entries in the Registry, independent from the Level of
1211 DRIVER_INFO, that the caller supplied */
1213 ZeroMemory(&di
, sizeof(di
));
1214 if (pDriverInfo
&& (level
< (sizeof(di_sizeof
) / sizeof(di_sizeof
[0])))) {
1215 memcpy(&di
, pDriverInfo
, di_sizeof
[level
]);
1218 /* dump the most used infos */
1219 TRACE("%p: .cVersion : 0x%x/%d\n", pDriverInfo
, di
.cVersion
, di
.cVersion
);
1220 TRACE("%p: .pName : %s\n", di
.pName
, debugstr_w(di
.pName
));
1221 TRACE("%p: .pEnvironment: %s\n", di
.pEnvironment
, debugstr_w(di
.pEnvironment
));
1222 TRACE("%p: .pDriverPath : %s\n", di
.pDriverPath
, debugstr_w(di
.pDriverPath
));
1223 TRACE("%p: .pDataFile : %s\n", di
.pDataFile
, debugstr_w(di
.pDataFile
));
1224 TRACE("%p: .pConfigFile : %s\n", di
.pConfigFile
, debugstr_w(di
.pConfigFile
));
1225 TRACE("%p: .pHelpFile : %s\n", di
.pHelpFile
, debugstr_w(di
.pHelpFile
));
1226 /* dump only the first of the additional Files */
1227 TRACE("%p: .pDependentFiles: %s\n", di
.pDependentFiles
, debugstr_w(di
.pDependentFiles
));
1230 /* check environment */
1231 env
= validate_envW(di
.pEnvironment
);
1232 if (env
== NULL
) return FALSE
; /* ERROR_INVALID_ENVIRONMENT */
1234 /* fill the copy-data / get the driverdir */
1235 len
= sizeof(apd
.src
) - sizeof(version3_subdirW
) - sizeof(WCHAR
);
1236 if (!fpGetPrinterDriverDirectory(NULL
, (LPWSTR
) env
->envname
, 1,
1237 (LPBYTE
) apd
.src
, len
, &len
)) {
1238 /* Should never Fail */
1241 memcpy(apd
.dst
, apd
.src
, len
);
1242 lstrcatW(apd
.src
, backslashW
);
1243 apd
.srclen
= lstrlenW(apd
.src
);
1244 lstrcatW(apd
.dst
, env
->versionsubdir
);
1245 lstrcatW(apd
.dst
, backslashW
);
1246 apd
.dstlen
= lstrlenW(apd
.dst
);
1247 apd
.copyflags
= dwFileCopyFlags
;
1249 CreateDirectoryW(apd
.src
, NULL
);
1250 CreateDirectoryW(apd
.dst
, NULL
);
1252 hroot
= open_driver_reg(env
->envname
);
1254 ERR("Can't create Drivers key\n");
1258 /* Fill the Registry for the Driver */
1259 if ((lres
= RegCreateKeyExW(hroot
, di
.pName
, 0, NULL
, REG_OPTION_NON_VOLATILE
,
1260 KEY_WRITE
| KEY_QUERY_VALUE
, NULL
,
1261 &hdrv
, &disposition
)) != ERROR_SUCCESS
) {
1263 ERR("can't create driver %s: %u\n", debugstr_w(di
.pName
), lres
);
1270 if (disposition
== REG_OPENED_EXISTING_KEY
) {
1271 TRACE("driver %s already installed\n", debugstr_w(di
.pName
));
1273 SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED
);
1277 /* Verified with the Adobe PS Driver, that w2k does not use di.Version */
1278 RegSetValueExW(hdrv
, versionW
, 0, REG_DWORD
, (LPBYTE
) &env
->driverversion
,
1281 RegSetValueExW(hdrv
, driverW
, 0, REG_SZ
, (LPBYTE
) di
.pDriverPath
,
1282 (lstrlenW(di
.pDriverPath
)+1)* sizeof(WCHAR
));
1283 apd_copyfile(di
.pDriverPath
, &apd
);
1285 RegSetValueExW(hdrv
, data_fileW
, 0, REG_SZ
, (LPBYTE
) di
.pDataFile
,
1286 (lstrlenW(di
.pDataFile
)+1)* sizeof(WCHAR
));
1287 apd_copyfile(di
.pDataFile
, &apd
);
1289 RegSetValueExW(hdrv
, configuration_fileW
, 0, REG_SZ
, (LPBYTE
) di
.pConfigFile
,
1290 (lstrlenW(di
.pConfigFile
)+1)* sizeof(WCHAR
));
1291 apd_copyfile(di
.pConfigFile
, &apd
);
1293 /* settings for level 3 */
1295 RegSetValueExW(hdrv
, help_fileW
, 0, REG_SZ
, (LPBYTE
) di
.pHelpFile
,
1296 (lstrlenW(di
.pHelpFile
)+1)* sizeof(WCHAR
));
1298 RegSetValueExW(hdrv
, help_fileW
, 0, REG_SZ
, (LPBYTE
)emptyW
, sizeof(emptyW
));
1299 apd_copyfile(di
.pHelpFile
, &apd
);
1302 ptr
= di
.pDependentFiles
;
1304 RegSetValueExW(hdrv
, dependent_filesW
, 0, REG_MULTI_SZ
, (LPBYTE
) di
.pDependentFiles
,
1305 multi_sz_lenW(di
.pDependentFiles
));
1307 RegSetValueExW(hdrv
, dependent_filesW
, 0, REG_MULTI_SZ
, (LPBYTE
)emptyW
, sizeof(emptyW
));
1308 while ((ptr
!= NULL
) && (ptr
[0])) {
1309 if (apd_copyfile(ptr
, &apd
)) {
1310 ptr
+= lstrlenW(ptr
) + 1;
1314 WARN("Failed to copy %s\n", debugstr_w(ptr
));
1318 /* The language-Monitor was already copied by the caller to "%SystemRoot%\system32" */
1319 if (di
.pMonitorName
)
1320 RegSetValueExW(hdrv
, monitorW
, 0, REG_SZ
, (LPBYTE
) di
.pMonitorName
,
1321 (lstrlenW(di
.pMonitorName
)+1)* sizeof(WCHAR
));
1323 RegSetValueExW(hdrv
, monitorW
, 0, REG_SZ
, (LPBYTE
)emptyW
, sizeof(emptyW
));
1325 if (di
.pDefaultDataType
)
1326 RegSetValueExW(hdrv
, datatypeW
, 0, REG_SZ
, (LPBYTE
) di
.pDefaultDataType
,
1327 (lstrlenW(di
.pDefaultDataType
)+1)* sizeof(WCHAR
));
1329 RegSetValueExW(hdrv
, datatypeW
, 0, REG_SZ
, (LPBYTE
)emptyW
, sizeof(emptyW
));
1331 /* settings for level 4 */
1332 if (di
.pszzPreviousNames
)
1333 RegSetValueExW(hdrv
, previous_namesW
, 0, REG_MULTI_SZ
, (LPBYTE
) di
.pszzPreviousNames
,
1334 multi_sz_lenW(di
.pszzPreviousNames
));
1336 RegSetValueExW(hdrv
, previous_namesW
, 0, REG_MULTI_SZ
, (LPBYTE
)emptyW
, sizeof(emptyW
));
1338 if (level
> 5) TRACE("level %u for Driver %s is incomplete\n", level
, debugstr_w(di
.pName
));
1341 hui
= driver_load(env
, di
.pConfigFile
);
1342 pDrvDriverEvent
= (void *)GetProcAddress(hui
, "DrvDriverEvent");
1343 if (hui
&& pDrvDriverEvent
) {
1345 /* Support for DrvDriverEvent is optional */
1346 TRACE("DRIVER_EVENT_INITIALIZE for %s (%s)\n", debugstr_w(di
.pName
), debugstr_w(di
.pConfigFile
));
1347 /* MSDN: level for DRIVER_INFO is 1 to 3 */
1348 res
= pDrvDriverEvent(DRIVER_EVENT_INITIALIZE
, 3, (LPBYTE
) &di
, 0);
1349 TRACE("got %d from DRIVER_EVENT_INITIALIZE\n", res
);
1353 TRACE("=> TRUE with %u\n", GetLastError());
1358 /******************************************************************************
1359 * fpAddMonitor [exported through PRINTPROVIDOR]
1361 * Install a Printmonitor
1364 * pName [I] Servername or NULL (local Computer)
1365 * Level [I] Structure-Level (Must be 2)
1366 * pMonitors [I] PTR to MONITOR_INFO_2
1373 * All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1376 static BOOL WINAPI
fpAddMonitor(LPWSTR pName
, DWORD Level
, LPBYTE pMonitors
)
1378 monitor_t
* pm
= NULL
;
1379 LPMONITOR_INFO_2W mi2w
;
1385 mi2w
= (LPMONITOR_INFO_2W
) pMonitors
;
1386 TRACE("(%s, %d, %p): %s %s %s\n", debugstr_w(pName
), Level
, pMonitors
,
1387 debugstr_w(mi2w
? mi2w
->pName
: NULL
),
1388 debugstr_w(mi2w
? mi2w
->pEnvironment
: NULL
),
1389 debugstr_w(mi2w
? mi2w
->pDLLName
: NULL
));
1391 if (copy_servername_from_name(pName
, NULL
)) {
1392 FIXME("server %s not supported\n", debugstr_w(pName
));
1393 SetLastError(ERROR_ACCESS_DENIED
);
1397 if (!mi2w
->pName
|| (! mi2w
->pName
[0])) {
1398 WARN("pName not valid : %s\n", debugstr_w(mi2w
->pName
));
1399 SetLastError(ERROR_INVALID_PARAMETER
);
1402 if (!mi2w
->pEnvironment
|| lstrcmpW(mi2w
->pEnvironment
, x86_envnameW
)) {
1403 WARN("Environment %s requested (we support only %s)\n",
1404 debugstr_w(mi2w
->pEnvironment
), debugstr_w(x86_envnameW
));
1405 SetLastError(ERROR_INVALID_ENVIRONMENT
);
1409 if (!mi2w
->pDLLName
|| (! mi2w
->pDLLName
[0])) {
1410 WARN("pDLLName not valid : %s\n", debugstr_w(mi2w
->pDLLName
));
1411 SetLastError(ERROR_INVALID_PARAMETER
);
1415 /* Load and initialize the monitor. SetLastError() is called on failure */
1416 if ((pm
= monitor_load(mi2w
->pName
, mi2w
->pDLLName
)) == NULL
) {
1421 if (RegCreateKeyW(HKEY_LOCAL_MACHINE
, monitorsW
, &hroot
) != ERROR_SUCCESS
) {
1422 ERR("unable to create key %s\n", debugstr_w(monitorsW
));
1426 if (RegCreateKeyExW(hroot
, mi2w
->pName
, 0, NULL
, REG_OPTION_NON_VOLATILE
,
1427 KEY_WRITE
| KEY_QUERY_VALUE
, NULL
, &hentry
,
1428 &disposition
) == ERROR_SUCCESS
) {
1430 /* Some installers set options for the port before calling AddMonitor.
1431 We query the "Driver" entry to verify that the monitor is installed,
1432 before we return an error.
1433 When a user installs two print monitors at the same time with the
1434 same name, a race condition is possible but silently ignored. */
1438 if ((disposition
== REG_OPENED_EXISTING_KEY
) &&
1439 (RegQueryValueExW(hentry
, driverW
, NULL
, NULL
, NULL
,
1440 &namesize
) == ERROR_SUCCESS
)) {
1441 TRACE("monitor %s already exists\n", debugstr_w(mi2w
->pName
));
1442 /* 9x use ERROR_ALREADY_EXISTS */
1443 SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED
);
1448 len
= (lstrlenW(mi2w
->pDLLName
) +1) * sizeof(WCHAR
);
1449 res
= (RegSetValueExW(hentry
, driverW
, 0, REG_SZ
,
1450 (LPBYTE
) mi2w
->pDLLName
, len
) == ERROR_SUCCESS
);
1452 RegCloseKey(hentry
);
1459 /******************************************************************************
1460 * fpAddPrinterDriverEx [exported through PRINTPROVIDOR]
1462 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1465 * pName [I] Servername or NULL (local Computer)
1466 * level [I] Level for the supplied DRIVER_INFO_*W struct
1467 * pDriverInfo [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
1468 * dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
1475 static BOOL WINAPI
fpAddPrinterDriverEx(LPWSTR pName
, DWORD level
, LPBYTE pDriverInfo
, DWORD dwFileCopyFlags
)
1479 TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName
), level
, pDriverInfo
, dwFileCopyFlags
);
1480 lres
= copy_servername_from_name(pName
, NULL
);
1482 FIXME("server %s not supported\n", debugstr_w(pName
));
1483 SetLastError(ERROR_ACCESS_DENIED
);
1487 if ((dwFileCopyFlags
& ~APD_COPY_FROM_DIRECTORY
) != APD_COPY_ALL_FILES
) {
1488 TRACE("Flags 0x%x ignored (using APD_COPY_ALL_FILES)\n", dwFileCopyFlags
& ~APD_COPY_FROM_DIRECTORY
);
1491 return myAddPrinterDriverEx(level
, pDriverInfo
, dwFileCopyFlags
, TRUE
);
1494 /******************************************************************************
1495 * fpClosePrinter [exported through PRINTPROVIDOR]
1497 * Close a printer handle and free associated resources
1500 * hPrinter [I] Printerhandle to close
1507 static BOOL WINAPI
fpClosePrinter(HANDLE hPrinter
)
1509 printer_t
*printer
= (printer_t
*) hPrinter
;
1511 TRACE("(%p)\n", hPrinter
);
1514 printer_free(printer
);
1520 /******************************************************************************
1521 * fpConfigurePort [exported through PRINTPROVIDOR]
1523 * Display the Configuration-Dialog for a specific Port
1526 * pName [I] Servername or NULL (local Computer)
1527 * hWnd [I] Handle to parent Window for the Dialog-Box
1528 * pPortName [I] Name of the Port, that should be configured
1535 static BOOL WINAPI
fpConfigurePort(LPWSTR pName
, HWND hWnd
, LPWSTR pPortName
)
1542 TRACE("(%s, %p, %s)\n", debugstr_w(pName
), hWnd
, debugstr_w(pPortName
));
1544 lres
= copy_servername_from_name(pName
, NULL
);
1546 FIXME("server %s not supported\n", debugstr_w(pName
));
1547 SetLastError(ERROR_INVALID_NAME
);
1551 /* an empty Portname is Invalid, but can popup a Dialog */
1552 if (!pPortName
[0]) {
1553 SetLastError(ERROR_NOT_SUPPORTED
);
1557 pm
= monitor_load_by_port(pPortName
);
1558 if (pm
&& pm
->monitor
&& pm
->monitor
->pfnConfigurePort
) {
1559 TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm
->name
),
1560 debugstr_w(pPortName
), pm
, debugstr_w(pm
->dllname
));
1561 res
= pm
->monitor
->pfnConfigurePort(pName
, hWnd
, pPortName
);
1562 TRACE("got %d with %u\n", res
, GetLastError());
1566 pui
= monitor_loadui(pm
);
1567 if (pui
&& pui
->monitorUI
&& pui
->monitorUI
->pfnConfigurePortUI
) {
1568 TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui
->name
),
1569 debugstr_w(pPortName
), pui
, debugstr_w(pui
->dllname
));
1570 res
= pui
->monitorUI
->pfnConfigurePortUI(pName
, hWnd
, pPortName
);
1571 TRACE("got %d with %u\n", res
, GetLastError());
1575 FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
1576 debugstr_w(pPortName
), pm
, debugstr_w(pm
? pm
->dllname
: NULL
),
1577 pui
, debugstr_w(pui
? pui
->dllname
: NULL
));
1579 SetLastError(ERROR_NOT_SUPPORTED
);
1582 monitor_unload(pui
);
1586 TRACE("returning %d with %u\n", res
, GetLastError());
1590 /******************************************************************
1591 * fpDeleteMonitor [exported through PRINTPROVIDOR]
1593 * Delete a specific Printmonitor from a Printing-Environment
1596 * pName [I] Servername or NULL (local Computer)
1597 * pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1598 * pMonitorName [I] Name of the Monitor, that should be deleted
1605 * pEnvironment is ignored in Windows for the local Computer.
1609 static BOOL WINAPI
fpDeleteMonitor(LPWSTR pName
, LPWSTR pEnvironment
, LPWSTR pMonitorName
)
1614 TRACE("(%s, %s, %s)\n",debugstr_w(pName
),debugstr_w(pEnvironment
),
1615 debugstr_w(pMonitorName
));
1617 lres
= copy_servername_from_name(pName
, NULL
);
1619 FIXME("server %s not supported\n", debugstr_w(pName
));
1620 SetLastError(ERROR_INVALID_NAME
);
1624 /* pEnvironment is ignored in Windows for the local Computer */
1625 if (!pMonitorName
|| !pMonitorName
[0]) {
1626 TRACE("pMonitorName %s is invalid\n", debugstr_w(pMonitorName
));
1627 SetLastError(ERROR_INVALID_PARAMETER
);
1631 if(RegCreateKeyW(HKEY_LOCAL_MACHINE
, monitorsW
, &hroot
) != ERROR_SUCCESS
) {
1632 ERR("unable to create key %s\n", debugstr_w(monitorsW
));
1636 if(RegDeleteTreeW(hroot
, pMonitorName
) == ERROR_SUCCESS
) {
1637 TRACE("%s deleted\n", debugstr_w(pMonitorName
));
1642 TRACE("%s does not exist\n", debugstr_w(pMonitorName
));
1645 /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
1646 SetLastError(ERROR_UNKNOWN_PRINT_MONITOR
);
1650 /*****************************************************************************
1651 * fpEnumMonitors [exported through PRINTPROVIDOR]
1653 * Enumerate available Port-Monitors
1656 * pName [I] Servername or NULL (local Computer)
1657 * Level [I] Structure-Level (1:Win9x+NT or 2:NT only)
1658 * pMonitors [O] PTR to Buffer that receives the Result
1659 * cbBuf [I] Size of Buffer at pMonitors
1660 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
1661 * pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
1665 * Failure: FALSE and in pcbNeeded the Bytes required for pMonitors, if cbBuf is too small
1668 * Windows reads the Registry once and cache the Results.
1671 static BOOL WINAPI
fpEnumMonitors(LPWSTR pName
, DWORD Level
, LPBYTE pMonitors
, DWORD cbBuf
,
1672 LPDWORD pcbNeeded
, LPDWORD pcReturned
)
1674 DWORD numentries
= 0;
1679 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName
), Level
, pMonitors
,
1680 cbBuf
, pcbNeeded
, pcReturned
);
1682 lres
= copy_servername_from_name(pName
, NULL
);
1684 FIXME("server %s not supported\n", debugstr_w(pName
));
1685 SetLastError(ERROR_INVALID_NAME
);
1689 if (!Level
|| (Level
> 2)) {
1690 WARN("level (%d) is ignored in win9x\n", Level
);
1691 SetLastError(ERROR_INVALID_LEVEL
);
1695 /* Scan all Monitor-Keys */
1697 needed
= get_local_monitors(Level
, NULL
, 0, &numentries
);
1699 /* we calculated the needed buffersize. now do more error-checks */
1700 if (cbBuf
< needed
) {
1701 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1705 /* fill the Buffer with the Monitor-Keys */
1706 needed
= get_local_monitors(Level
, pMonitors
, cbBuf
, &numentries
);
1710 if (pcbNeeded
) *pcbNeeded
= needed
;
1711 if (pcReturned
) *pcReturned
= numentries
;
1713 TRACE("returning %d with %d (%d byte for %d entries)\n",
1714 res
, GetLastError(), needed
, numentries
);
1719 /******************************************************************************
1720 * fpEnumPorts [exported through PRINTPROVIDOR]
1722 * Enumerate available Ports
1725 * pName [I] Servername or NULL (local Computer)
1726 * Level [I] Structure-Level (1 or 2)
1727 * pPorts [O] PTR to Buffer that receives the Result
1728 * cbBuf [I] Size of Buffer at pPorts
1729 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
1730 * pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
1734 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
1737 static BOOL WINAPI
fpEnumPorts(LPWSTR pName
, DWORD Level
, LPBYTE pPorts
, DWORD cbBuf
,
1738 LPDWORD pcbNeeded
, LPDWORD pcReturned
)
1741 DWORD numentries
= 0;
1745 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName
), Level
, pPorts
,
1746 cbBuf
, pcbNeeded
, pcReturned
);
1748 lres
= copy_servername_from_name(pName
, NULL
);
1750 FIXME("server %s not supported\n", debugstr_w(pName
));
1751 SetLastError(ERROR_INVALID_NAME
);
1755 if (!Level
|| (Level
> 2)) {
1756 SetLastError(ERROR_INVALID_LEVEL
);
1760 if (!pcbNeeded
|| (!pPorts
&& (cbBuf
> 0))) {
1761 SetLastError(RPC_X_NULL_REF_POINTER
);
1765 EnterCriticalSection(&monitor_handles_cs
);
1768 /* Scan all local Ports */
1770 needed
= get_ports_from_all_monitors(Level
, NULL
, 0, &numentries
);
1772 /* we calculated the needed buffersize. now do the error-checks */
1773 if (cbBuf
< needed
) {
1774 monitor_unloadall();
1775 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1776 goto emP_cleanup_cs
;
1778 else if (!pPorts
|| !pcReturned
) {
1779 monitor_unloadall();
1780 SetLastError(RPC_X_NULL_REF_POINTER
);
1781 goto emP_cleanup_cs
;
1784 /* Fill the Buffer */
1785 needed
= get_ports_from_all_monitors(Level
, pPorts
, cbBuf
, &numentries
);
1787 monitor_unloadall();
1790 LeaveCriticalSection(&monitor_handles_cs
);
1793 if (pcbNeeded
) *pcbNeeded
= needed
;
1794 if (pcReturned
) *pcReturned
= (res
) ? numentries
: 0;
1796 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
1797 (res
), GetLastError(), needed
, (res
) ? numentries
: 0, numentries
);
1802 /******************************************************************************
1803 * fpOpenPrinter [exported through PRINTPROVIDOR]
1805 * Open a Printer / Printserver or a Printer-Object
1808 * lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
1809 * pPrinter [O] The resulting Handle is stored here
1810 * pDefaults [I] PTR to Default Printer Settings or NULL
1817 * lpPrinterName is one of:
1818 *| Printserver (NT only): "Servername" or NULL for the local Printserver
1819 *| Printer: "PrinterName"
1820 *| Printer-Object: "PrinterName,Job xxx"
1821 *| XcvMonitor: "Servername,XcvMonitor MonitorName"
1822 *| XcvPort: "Servername,XcvPort PortName"
1826 static BOOL WINAPI
fpOpenPrinter(LPWSTR lpPrinterName
, HANDLE
*pPrinter
,
1827 LPPRINTER_DEFAULTSW pDefaults
)
1830 TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName
), pPrinter
, pDefaults
);
1832 *pPrinter
= printer_alloc_handle(lpPrinterName
, pDefaults
);
1834 return (*pPrinter
!= 0);
1837 /******************************************************************************
1838 * fpXcvData [exported through PRINTPROVIDOR]
1840 * Execute commands in the Printmonitor DLL
1843 * hXcv [i] Handle from fpOpenPrinter (with XcvMonitor or XcvPort)
1844 * pszDataName [i] Name of the command to execute
1845 * pInputData [i] Buffer for extra Input Data (needed only for some commands)
1846 * cbInputData [i] Size in Bytes of Buffer at pInputData
1847 * pOutputData [o] Buffer to receive additional Data (needed only for some commands)
1848 * cbOutputData [i] Size in Bytes of Buffer at pOutputData
1849 * pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
1850 * pdwStatus [o] PTR to receive the win32 error code from the Printmonitor DLL
1857 * Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
1858 * The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
1860 * Minimal List of commands, that a Printmonitor DLL should support:
1862 *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
1863 *| "AddPort" : Add a Port
1864 *| "DeletePort": Delete a Port
1866 * Many Printmonitors support additional commands. Examples for localspl.dll:
1867 * "GetDefaultCommConfig", "SetDefaultCommConfig",
1868 * "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
1871 static BOOL WINAPI
fpXcvData(HANDLE hXcv
, LPCWSTR pszDataName
, PBYTE pInputData
,
1872 DWORD cbInputData
, PBYTE pOutputData
, DWORD cbOutputData
,
1873 PDWORD pcbOutputNeeded
, PDWORD pdwStatus
)
1875 printer_t
*printer
= (printer_t
* ) hXcv
;
1877 TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv
, debugstr_w(pszDataName
),
1878 pInputData
, cbInputData
, pOutputData
,
1879 cbOutputData
, pcbOutputNeeded
, pdwStatus
);
1881 if (!printer
|| (!printer
->hXcv
)) {
1882 SetLastError(ERROR_INVALID_HANDLE
);
1886 if (!pcbOutputNeeded
) {
1887 SetLastError(ERROR_INVALID_PARAMETER
);
1891 if (!pszDataName
|| !pdwStatus
|| (!pOutputData
&& (cbOutputData
> 0))) {
1892 SetLastError(RPC_X_NULL_REF_POINTER
);
1896 *pcbOutputNeeded
= 0;
1898 *pdwStatus
= printer
->pm
->monitor
->pfnXcvDataPort(printer
->hXcv
, pszDataName
,
1899 pInputData
, cbInputData
, pOutputData
, cbOutputData
, pcbOutputNeeded
);
1904 /*****************************************************
1905 * setup_provider [internal]
1907 void setup_provider(void)
1909 static const PRINTPROVIDOR backend
= {
1911 NULL
, /* fpSetJob */
1912 NULL
, /* fpGetJob */
1913 NULL
, /* fpEnumJobs */
1914 NULL
, /* fpAddPrinter */
1915 NULL
, /* fpDeletePrinter */
1916 NULL
, /* fpSetPrinter */
1917 NULL
, /* fpGetPrinter */
1918 NULL
, /* fpEnumPrinters */
1919 NULL
, /* fpAddPrinterDriver */
1920 NULL
, /* fpEnumPrinterDrivers */
1921 NULL
, /* fpGetPrinterDriver */
1922 fpGetPrinterDriverDirectory
,
1923 NULL
, /* fpDeletePrinterDriver */
1924 NULL
, /* fpAddPrintProcessor */
1925 NULL
, /* fpEnumPrintProcessors */
1926 NULL
, /* fpGetPrintProcessorDirectory */
1927 NULL
, /* fpDeletePrintProcessor */
1928 NULL
, /* fpEnumPrintProcessorDatatypes */
1929 NULL
, /* fpStartDocPrinter */
1930 NULL
, /* fpStartPagePrinter */
1931 NULL
, /* fpWritePrinter */
1932 NULL
, /* fpEndPagePrinter */
1933 NULL
, /* fpAbortPrinter */
1934 NULL
, /* fpReadPrinter */
1935 NULL
, /* fpEndDocPrinter */
1936 NULL
, /* fpAddJob */
1937 NULL
, /* fpScheduleJob */
1938 NULL
, /* fpGetPrinterData */
1939 NULL
, /* fpSetPrinterData */
1940 NULL
, /* fpWaitForPrinterChange */
1942 NULL
, /* fpAddForm */
1943 NULL
, /* fpDeleteForm */
1944 NULL
, /* fpGetForm */
1945 NULL
, /* fpSetForm */
1946 NULL
, /* fpEnumForms */
1949 NULL
, /* fpAddPort */
1951 NULL
, /* fpDeletePort */
1952 NULL
, /* fpCreatePrinterIC */
1953 NULL
, /* fpPlayGdiScriptOnPrinterIC */
1954 NULL
, /* fpDeletePrinterIC */
1955 NULL
, /* fpAddPrinterConnection */
1956 NULL
, /* fpDeletePrinterConnection */
1957 NULL
, /* fpPrinterMessageBox */
1960 NULL
, /* fpResetPrinter */
1961 NULL
, /* fpGetPrinterDriverEx */
1962 NULL
, /* fpFindFirstPrinterChangeNotification */
1963 NULL
, /* fpFindClosePrinterChangeNotification */
1964 NULL
, /* fpAddPortEx */
1965 NULL
, /* fpShutDown */
1966 NULL
, /* fpRefreshPrinterChangeNotification */
1967 NULL
, /* fpOpenPrinterEx */
1968 NULL
, /* fpAddPrinterEx */
1969 NULL
, /* fpSetPort */
1970 NULL
, /* fpEnumPrinterData */
1971 NULL
, /* fpDeletePrinterData */
1972 NULL
, /* fpClusterSplOpen */
1973 NULL
, /* fpClusterSplClose */
1974 NULL
, /* fpClusterSplIsAlive */
1975 NULL
, /* fpSetPrinterDataEx */
1976 NULL
, /* fpGetPrinterDataEx */
1977 NULL
, /* fpEnumPrinterDataEx */
1978 NULL
, /* fpEnumPrinterKey */
1979 NULL
, /* fpDeletePrinterDataEx */
1980 NULL
, /* fpDeletePrinterKey */
1981 NULL
, /* fpSeekPrinter */
1982 NULL
, /* fpDeletePrinterDriverEx */
1983 NULL
, /* fpAddPerMachineConnection */
1984 NULL
, /* fpDeletePerMachineConnection */
1985 NULL
, /* fpEnumPerMachineConnections */
1987 fpAddPrinterDriverEx
,
1988 NULL
, /* fpSplReadPrinter */
1989 NULL
, /* fpDriverUnloadComplete */
1990 NULL
, /* fpGetSpoolFileInfo */
1991 NULL
, /* fpCommitSpoolData */
1992 NULL
, /* fpCloseSpoolFileHandle */
1993 NULL
, /* fpFlushPrinter */
1994 NULL
, /* fpSendRecvBidiData */
1995 NULL
/* fpAddDriverCatalog */
1997 pprovider
= &backend
;
2001 /*****************************************************
2002 * InitializePrintProvidor (localspl.@)
2004 * Initialize the Printprovider
2007 * pPrintProvidor [I] Buffer to fill with a struct PRINTPROVIDOR
2008 * cbPrintProvidor [I] Size of Buffer in Bytes
2009 * pFullRegistryPath [I] Registry-Path for the Printprovidor
2012 * Success: TRUE and pPrintProvidor filled
2016 * The RegistryPath should be:
2017 * "System\CurrentControlSet\Control\Print\Providers\<providername>",
2018 * but this Parameter is ignored in "localspl.dll".
2022 BOOL WINAPI
InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor
,
2023 DWORD cbPrintProvidor
, LPWSTR pFullRegistryPath
)
2026 TRACE("(%p, %u, %s)\n", pPrintProvidor
, cbPrintProvidor
, debugstr_w(pFullRegistryPath
));
2027 memcpy(pPrintProvidor
, pprovider
,
2028 (cbPrintProvidor
< sizeof(PRINTPROVIDOR
)) ? cbPrintProvidor
: sizeof(PRINTPROVIDOR
));