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;
83 /* ############################### */
85 static struct list monitor_handles = LIST_INIT( monitor_handles );
86 static monitor_t * pm_localport;
88 static const PRINTPROVIDOR * pprovider = NULL;
90 static const WCHAR backslashW[] = {'\\',0};
91 static const WCHAR configuration_fileW[] = {'C','o','n','f','i','g','u','r','a','t','i','o','n',' ','F','i','l','e',0};
92 static const WCHAR datatypeW[] = {'D','a','t','a','t','y','p','e',0};
93 static const WCHAR data_fileW[] = {'D','a','t','a',' ','F','i','l','e',0};
94 static const WCHAR default_devmodeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0};
95 static const WCHAR dependent_filesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
96 static const WCHAR descriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
97 static const WCHAR driverW[] = {'D','r','i','v','e','r',0};
98 static const WCHAR fmt_driversW[] = { 'S','y','s','t','e','m','\\',
99 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
100 'c','o','n','t','r','o','l','\\',
101 'P','r','i','n','t','\\',
102 'E','n','v','i','r','o','n','m','e','n','t','s','\\',
103 '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
104 static const WCHAR hardwareidW[] = {'H','a','r','d','w','a','r','e','I','D',0};
105 static const WCHAR help_fileW[] = {'H','e','l','p',' ','F','i','l','e',0};
106 static const WCHAR localportW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
107 static const WCHAR locationW[] = {'L','o','c','a','t','i','o','n',0};
108 static const WCHAR manufacturerW[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0};
109 static const WCHAR monitorW[] = {'M','o','n','i','t','o','r',0};
110 static const WCHAR monitorsW[] = {'S','y','s','t','e','m','\\',
111 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
112 'C','o','n','t','r','o','l','\\',
113 'P','r','i','n','t','\\',
114 'M','o','n','i','t','o','r','s','\\',0};
115 static const WCHAR monitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
116 static const WCHAR nameW[] = {'N','a','m','e',0};
117 static const WCHAR oem_urlW[] = {'O','E','M',' ','U','r','l',0};
118 static const WCHAR parametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
119 static const WCHAR portW[] = {'P','o','r','t',0};
120 static const WCHAR previous_namesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
121 static const WCHAR spooldriversW[] = {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\',0};
122 static const WCHAR versionW[] = {'V','e','r','s','i','o','n',0};
124 static const WCHAR win40_envnameW[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
125 static const WCHAR win40_subdirW[] = {'w','i','n','4','0',0};
126 static const WCHAR version0_regpathW[] = {'\\','V','e','r','s','i','o','n','-','0',0};
127 static const WCHAR version0_subdirW[] = {'\\','0',0};
129 static const WCHAR x64_envnameW[] = {'W','i','n','d','o','w','s',' ','x','6','4',0};
130 static const WCHAR x64_subdirW[] = {'x','6','4',0};
131 static const WCHAR x86_envnameW[] = {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
132 static const WCHAR x86_subdirW[] = {'w','3','2','x','8','6',0};
133 static const WCHAR version3_regpathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
134 static const WCHAR version3_subdirW[] = {'\\','3',0};
137 static const printenv_t env_x86 = {x86_envnameW, x86_subdirW, 3,
138 version3_regpathW, version3_subdirW};
140 static const printenv_t env_x64 = {x64_envnameW, x64_subdirW, 3,
141 version3_regpathW, version3_subdirW};
143 static const printenv_t env_win40 = {win40_envnameW, win40_subdirW, 0,
144 version0_regpathW, version0_subdirW};
146 static const printenv_t * const all_printenv[] = {&env_x86, &env_x64, &env_win40};
149 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
150 sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
151 sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
152 0, sizeof(DRIVER_INFO_8W)};
155 /******************************************************************
158 * create a copy of a unicode-string
161 static LPWSTR strdupW(LPCWSTR p)
167 len = (lstrlenW(p) + 1) * sizeof(WCHAR);
168 ret = heap_alloc(len);
173 /******************************************************************
174 * apd_copyfile [internal]
176 * Copy a file from the driverdirectory to the versioned directory
183 static BOOL apd_copyfile(LPWSTR filename, apd_data_t *apd)
189 apd->src[apd->srclen] = '\0';
190 apd->dst[apd->dstlen] = '\0';
192 if (!filename || !filename[0]) {
193 /* nothing to copy */
197 ptr = strrchrW(filename, '\\');
206 if (apd->copyflags & APD_COPY_FROM_DIRECTORY) {
207 /* we have an absolute Path */
213 lstrcatW(srcname, ptr);
215 lstrcatW(apd->dst, ptr);
217 TRACE("%s => %s\n", debugstr_w(filename), debugstr_w(apd->dst));
219 /* FIXME: handle APD_COPY_NEW_FILES */
220 res = CopyFileW(srcname, apd->dst, FALSE);
221 TRACE("got %u with %u\n", res, GetLastError());
223 return (apd->lazy) ? TRUE : res;
226 /******************************************************************
227 * copy_servername_from_name (internal)
229 * for an external server, the serverpart from the name is copied.
232 * the length (in WCHAR) of the serverpart (0 for the local computer)
233 * (-length), when the name is to long
236 static LONG copy_servername_from_name(LPCWSTR name, LPWSTR target)
240 WCHAR buffer[MAX_COMPUTERNAME_LENGTH +1];
244 if (target) *target = '\0';
246 if (name == NULL) return 0;
247 if ((name[0] != '\\') || (name[1] != '\\')) return 0;
250 /* skip over both backslash, find separator '\' */
251 ptr = strchrW(server, '\\');
252 serverlen = (ptr) ? ptr - server : lstrlenW(server);
254 /* servername is empty or to long */
255 if (serverlen == 0) return 0;
257 TRACE("found %s\n", debugstr_wn(server, serverlen));
259 if (serverlen > MAX_COMPUTERNAME_LENGTH) return -serverlen;
261 len = sizeof(buffer) / sizeof(buffer[0]);
262 if (GetComputerNameW(buffer, &len)) {
263 if ((serverlen == len) && (strncmpiW(server, buffer, len) == 0)) {
264 /* The requested Servername is our computername */
266 memcpy(target, server, serverlen * sizeof(WCHAR));
267 target[serverlen] = '\0';
275 /******************************************************************
276 * monitor_unload [internal]
278 * release a printmonitor and unload it from memory, when needed
281 static void monitor_unload(monitor_t * pm)
283 if (pm == NULL) return;
284 TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
286 EnterCriticalSection(&monitor_handles_cs);
288 if (pm->refcount) pm->refcount--;
290 if (pm->refcount == 0) {
291 list_remove(&pm->entry);
292 FreeLibrary(pm->hdll);
294 heap_free(pm->dllname);
297 LeaveCriticalSection(&monitor_handles_cs);
300 /******************************************************************
301 * monitor_unloadall [internal]
303 * release all printmonitors and unload them from memory, when needed
307 static void monitor_unloadall(void)
312 EnterCriticalSection(&monitor_handles_cs);
313 /* iterate through the list, with safety against removal */
314 LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
318 LeaveCriticalSection(&monitor_handles_cs);
321 /******************************************************************
322 * monitor_load [internal]
324 * load a printmonitor, get the dllname from the registry, when needed
325 * initialize the monitor and dump found function-pointers
327 * On failure, SetLastError() is called and NULL is returned
330 static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname)
332 LPMONITOR2 (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
333 PMONITORUI (WINAPI *pInitializePrintMonitorUI)(VOID);
334 LPMONITOREX (WINAPI *pInitializePrintMonitor) (LPWSTR);
335 DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
336 DWORD (WINAPI *pInitializeMonitor) (LPWSTR);
338 monitor_t * pm = NULL;
340 LPWSTR regroot = NULL;
341 LPWSTR driver = dllname;
343 TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
344 /* Is the Monitor already loaded? */
345 EnterCriticalSection(&monitor_handles_cs);
348 LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
350 if (cursor->name && (lstrcmpW(name, cursor->name) == 0)) {
358 pm = heap_alloc_zero(sizeof(monitor_t));
359 if (pm == NULL) goto cleanup;
360 list_add_tail(&monitor_handles, &pm->entry);
364 if (pm->name == NULL) {
365 /* Load the monitor */
366 LPMONITOREX pmonitorEx;
370 len = lstrlenW(monitorsW) + lstrlenW(name) + 2;
371 regroot = heap_alloc(len * sizeof(WCHAR));
375 lstrcpyW(regroot, monitorsW);
376 lstrcatW(regroot, name);
377 /* Get the Driver from the Registry */
378 if (driver == NULL) {
381 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
382 if (RegQueryValueExW(hroot, driverW, NULL, NULL, NULL,
383 &namesize) == ERROR_SUCCESS) {
384 driver = heap_alloc(namesize);
385 RegQueryValueExW(hroot, driverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
392 pm->name = strdupW(name);
393 pm->dllname = strdupW(driver);
395 if ((name && (!regroot || !pm->name)) || !pm->dllname) {
397 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
402 pm->hdll = LoadLibraryW(driver);
403 TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
405 if (pm->hdll == NULL) {
407 SetLastError(ERROR_MOD_NOT_FOUND);
412 pInitializePrintMonitor2 = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
413 pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
414 pInitializePrintMonitor = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
415 pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
416 pInitializeMonitor = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
419 TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
420 TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
421 TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
422 TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
423 TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
425 if (pInitializePrintMonitorUI != NULL) {
426 pm->monitorUI = pInitializePrintMonitorUI();
427 TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver));
429 TRACE("0x%08x: dwMonitorSize (%d)\n",
430 pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize);
435 if (pInitializePrintMonitor && regroot) {
436 pmonitorEx = pInitializePrintMonitor(regroot);
437 TRACE("%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
438 pmonitorEx, debugstr_w(driver), debugstr_w(regroot));
441 pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
442 pm->monitor = &(pmonitorEx->Monitor);
447 TRACE("0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize);
451 if (!pm->monitor && regroot) {
452 if (pInitializePrintMonitor2 != NULL) {
453 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
455 if (pInitializeMonitorEx != NULL) {
456 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
458 if (pInitializeMonitor != NULL) {
459 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
462 if (!pm->monitor && !pm->monitorUI) {
464 SetLastError(ERROR_PROC_NOT_FOUND);
469 if ((pm_localport == NULL) && (pm != NULL) && (lstrcmpW(pm->name, localportW) == 0)) {
473 LeaveCriticalSection(&monitor_handles_cs);
474 if (driver != dllname) heap_free(driver);
476 TRACE("=> %p\n", pm);
480 /******************************************************************
481 * monitor_loadall [internal]
483 * Load all registered monitors
486 static DWORD monitor_loadall(void)
489 DWORD registered = 0;
492 WCHAR buffer[MAX_PATH];
495 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hmonitors) == ERROR_SUCCESS) {
496 RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, ®istered, NULL, NULL,
497 NULL, NULL, NULL, NULL, NULL);
499 TRACE("%d monitors registered\n", registered);
501 while (id < registered) {
503 RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
504 pm = monitor_load(buffer, NULL);
508 RegCloseKey(hmonitors);
510 TRACE("%d monitors loaded\n", loaded);
514 /******************************************************************
515 * Return the number of bytes for an multi_sz string.
516 * The result includes all \0s
517 * (specifically the extra \0, that is needed as multi_sz terminator).
519 static int multi_sz_lenW(const WCHAR *str)
521 const WCHAR *ptr = str;
525 ptr += lstrlenW(ptr) + 1;
528 return (ptr - str + 1) * sizeof(WCHAR);
531 /******************************************************************
532 * validate_envW [internal]
534 * validate the user-supplied printing-environment
537 * env [I] PTR to Environment-String or NULL
540 * Success: PTR to printenv_t
541 * Failure: NULL and ERROR_INVALID_ENVIRONMENT
544 * An empty string is handled the same way as NULL.
548 static const printenv_t * validate_envW(LPCWSTR env)
550 const printenv_t *result = NULL;
553 TRACE("(%s)\n", debugstr_w(env));
556 for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
558 if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
560 result = all_printenv[i];
564 if (result == NULL) {
565 FIXME("unsupported Environment: %s\n", debugstr_w(env));
566 SetLastError(ERROR_INVALID_ENVIRONMENT);
568 /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
572 result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
575 TRACE("=> using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
579 /*****************************************************************************
580 * enumerate the local monitors (INTERNAL)
582 * returns the needed size (in bytes) for pMonitors
583 * and *lpreturned is set to number of entries returned in pMonitors
585 * Language-Monitors are also installed in the same Registry-Location but
586 * they are filtered in Windows (not returned by EnumMonitors).
587 * We do no filtering to simplify our Code.
590 static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
595 LPMONITOR_INFO_2W mi;
596 WCHAR buffer[MAX_PATH];
597 WCHAR dllname[MAX_PATH];
605 entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
607 numentries = *lpreturned; /* this is 0, when we scan the registry */
608 len = entrysize * numentries;
609 ptr = (LPWSTR) &pMonitors[len];
612 len = sizeof(buffer)/sizeof(buffer[0]);
615 /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
616 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) == ERROR_SUCCESS) {
617 /* Scan all Monitor-Registry-Keys */
618 while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
619 TRACE("Monitor_%d: %s\n", numentries, debugstr_w(buffer));
620 dllsize = sizeof(dllname);
623 /* The Monitor must have a Driver-DLL */
624 if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
625 if (RegQueryValueExW(hentry, driverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
626 /* We found a valid DLL for this Monitor. */
627 TRACE("using Driver: %s\n", debugstr_w(dllname));
632 /* Windows returns only Port-Monitors here, but to simplify our code,
633 we do no filtering for Language-Monitors */
637 needed += (len+1) * sizeof(WCHAR); /* len is lstrlenW(monitorname) */
639 /* we install and return only monitors for "Windows NT x86" */
640 needed += (lstrlenW(x86_envnameW) +1) * sizeof(WCHAR);
644 /* required size is calculated. Now fill the user-buffer */
645 if (pMonitors && (cbBuf >= needed)){
646 mi = (LPMONITOR_INFO_2W) pMonitors;
647 pMonitors += entrysize;
649 TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi, level, numentries);
651 lstrcpyW(ptr, buffer); /* Name of the Monitor */
652 ptr += (len+1); /* len is lstrlenW(monitorname) */
654 mi->pEnvironment = ptr;
655 lstrcpyW(ptr, x86_envnameW); /* fixed to "Windows NT x86" */
656 ptr += (lstrlenW(x86_envnameW)+1);
659 lstrcpyW(ptr, dllname); /* Name of the Driver-DLL */
660 ptr += (dllsize / sizeof(WCHAR));
665 len = sizeof(buffer)/sizeof(buffer[0]);
670 *lpreturned = numentries;
671 TRACE("need %d byte for %d entries\n", needed, numentries);
675 /******************************************************************
676 * enumerate the local Ports from all loaded monitors (internal)
678 * returns the needed size (in bytes) for pPorts
679 * and *lpreturned is set to number of entries returned in pPorts
682 static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
686 LPPORT_INFO_2W cache;
688 LPBYTE pi_buffer = NULL;
689 DWORD pi_allocated = 0;
700 TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
701 entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
703 numentries = *lpreturned; /* this is 0, when we scan the registry */
704 needed = entrysize * numentries;
705 ptr = (LPWSTR) &pPorts[needed];
710 LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
712 if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
715 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
716 if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
717 /* Do not use heap_realloc (we do not need the old data in the buffer) */
718 heap_free(pi_buffer);
719 pi_buffer = heap_alloc(pi_needed);
720 pi_allocated = (pi_buffer) ? pi_needed : 0;
721 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
723 TRACE("(%s) got %d with %d (need %d byte for %d entries)\n",
724 debugstr_w(pm->name), res, GetLastError(), pi_needed, pi_returned);
726 numentries += pi_returned;
729 /* fill the output-buffer (pPorts), if we have one */
730 if (pPorts && (cbBuf >= needed ) && pi_buffer) {
732 while (pi_returned > pi_index) {
733 cache = (LPPORT_INFO_2W) &pi_buffer[pi_index * entrysize];
734 out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
735 out->pPortName = ptr;
736 lstrcpyW(ptr, cache->pPortName);
737 ptr += (lstrlenW(ptr)+1);
739 out->pMonitorName = ptr;
740 lstrcpyW(ptr, cache->pMonitorName);
741 ptr += (lstrlenW(ptr)+1);
743 out->pDescription = ptr;
744 lstrcpyW(ptr, cache->pDescription);
745 ptr += (lstrlenW(ptr)+1);
746 out->fPortType = cache->fPortType;
747 out->Reserved = cache->Reserved;
755 /* the temporary portinfo-buffer is no longer needed */
756 heap_free(pi_buffer);
758 *lpreturned = numentries;
759 TRACE("need %d byte for %d entries\n", needed, numentries);
764 /*****************************************************************************
765 * open_driver_reg [internal]
767 * opens the registry for the printer drivers depending on the given input
768 * variable pEnvironment
771 * Success: the opened hkey
774 static HKEY open_driver_reg(LPCWSTR pEnvironment)
778 const printenv_t * env;
780 TRACE("(%s)\n", debugstr_w(pEnvironment));
782 env = validate_envW(pEnvironment);
783 if (!env) return NULL;
785 buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(fmt_driversW) +
786 (lstrlenW(env->envname) + lstrlenW(env->versionregpath)) * sizeof(WCHAR));
789 wsprintfW(buffer, fmt_driversW, env->envname, env->versionregpath);
790 RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
791 HeapFree(GetProcessHeap(), 0, buffer);
796 /*****************************************************************************
797 * fpGetPrinterDriverDirectory [exported through PRINTPROVIDOR]
799 * Return the PATH for the Printer-Drivers
802 * pName [I] Servername (NT only) or NULL (local Computer)
803 * pEnvironment [I] Printing-Environment (see below) or NULL (Default)
804 * Level [I] Structure-Level (must be 1)
805 * pDriverDirectory [O] PTR to Buffer that receives the Result
806 * cbBuf [I] Size of Buffer at pDriverDirectory
807 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
808 * required for pDriverDirectory
811 * Success: TRUE and in pcbNeeded the Bytes used in pDriverDirectory
812 * Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
813 * if cbBuf is too small
815 * Native Values returned in pDriverDirectory on Success:
816 *| NT(Windows NT x86): "%winsysdir%\\spool\\DRIVERS\\w32x86"
817 *| NT(Windows 4.0): "%winsysdir%\\spool\\DRIVERS\\win40"
818 *| win9x(Windows 4.0): "%winsysdir%"
820 * "%winsysdir%" is the Value from GetSystemDirectoryW()
823 static BOOL WINAPI fpGetPrinterDriverDirectory(LPWSTR pName, LPWSTR pEnvironment,
824 DWORD Level, LPBYTE pDriverDirectory, DWORD cbBuf, LPDWORD pcbNeeded)
827 const printenv_t * env;
829 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName),
830 debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
832 if (pName != NULL && pName[0]) {
833 FIXME("server %s not supported\n", debugstr_w(pName));
834 SetLastError(ERROR_INVALID_PARAMETER);
838 env = validate_envW(pEnvironment);
839 if (!env) return FALSE; /* pEnvironment invalid or unsupported */
842 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
843 needed = GetSystemDirectoryW(NULL, 0);
844 /* add the Size for the Subdirectories */
845 needed += lstrlenW(spooldriversW);
846 needed += lstrlenW(env->subdir);
847 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
851 if (needed > cbBuf) {
852 SetLastError(ERROR_INSUFFICIENT_BUFFER);
856 if (pDriverDirectory == NULL) {
857 /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
858 SetLastError(ERROR_INVALID_USER_BUFFER);
862 GetSystemDirectoryW((LPWSTR) pDriverDirectory, cbBuf/sizeof(WCHAR));
863 /* add the Subdirectories */
864 lstrcatW((LPWSTR) pDriverDirectory, spooldriversW);
865 lstrcatW((LPWSTR) pDriverDirectory, env->subdir);
867 TRACE("=> %s\n", debugstr_w((LPWSTR) pDriverDirectory));
871 /******************************************************************
872 * driver_load [internal]
874 * load a driver user interface dll
876 * On failure, NULL is returned
880 static HMODULE driver_load(const printenv_t * env, LPWSTR dllname)
882 WCHAR fullname[MAX_PATH];
886 TRACE("(%p, %s)\n", env, debugstr_w(dllname));
888 /* build the driverdir */
889 len = sizeof(fullname) -
890 (lstrlenW(env->versionsubdir) + 1 + lstrlenW(dllname) + 1) * sizeof(WCHAR);
892 if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
893 (LPBYTE) fullname, len, &len)) {
894 /* Should never Fail */
895 SetLastError(ERROR_BUFFER_OVERFLOW);
899 lstrcatW(fullname, env->versionsubdir);
900 lstrcatW(fullname, backslashW);
901 lstrcatW(fullname, dllname);
903 hui = LoadLibraryW(fullname);
904 TRACE("%p: LoadLibrary(%s) %d\n", hui, debugstr_w(fullname), GetLastError());
909 /******************************************************************************
910 * myAddPrinterDriverEx [internal]
912 * Install a Printer Driver with the Option to upgrade / downgrade the Files
913 * and a special mode with lazy error checking.
916 static BOOL myAddPrinterDriverEx(DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags, BOOL lazy)
918 static const WCHAR emptyW[1];
919 const printenv_t *env;
922 BOOL (WINAPI *pDrvDriverEvent)(DWORD, DWORD, LPBYTE, LPARAM);
932 /* we need to set all entries in the Registry, independent from the Level of
933 DRIVER_INFO, that the caller supplied */
935 ZeroMemory(&di, sizeof(di));
936 if (pDriverInfo && (level < (sizeof(di_sizeof) / sizeof(di_sizeof[0])))) {
937 memcpy(&di, pDriverInfo, di_sizeof[level]);
940 /* dump the most used infos */
941 TRACE("%p: .cVersion : 0x%x/%d\n", pDriverInfo, di.cVersion, di.cVersion);
942 TRACE("%p: .pName : %s\n", di.pName, debugstr_w(di.pName));
943 TRACE("%p: .pEnvironment: %s\n", di.pEnvironment, debugstr_w(di.pEnvironment));
944 TRACE("%p: .pDriverPath : %s\n", di.pDriverPath, debugstr_w(di.pDriverPath));
945 TRACE("%p: .pDataFile : %s\n", di.pDataFile, debugstr_w(di.pDataFile));
946 TRACE("%p: .pConfigFile : %s\n", di.pConfigFile, debugstr_w(di.pConfigFile));
947 TRACE("%p: .pHelpFile : %s\n", di.pHelpFile, debugstr_w(di.pHelpFile));
948 /* dump only the first of the additional Files */
949 TRACE("%p: .pDependentFiles: %s\n", di.pDependentFiles, debugstr_w(di.pDependentFiles));
952 /* check environment */
953 env = validate_envW(di.pEnvironment);
954 if (env == NULL) return FALSE; /* ERROR_INVALID_ENVIRONMENT */
956 /* fill the copy-data / get the driverdir */
957 len = sizeof(apd.src) - sizeof(version3_subdirW) - sizeof(WCHAR);
958 if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
959 (LPBYTE) apd.src, len, &len)) {
960 /* Should never Fail */
963 memcpy(apd.dst, apd.src, len);
964 lstrcatW(apd.src, backslashW);
965 apd.srclen = lstrlenW(apd.src);
966 lstrcatW(apd.dst, env->versionsubdir);
967 lstrcatW(apd.dst, backslashW);
968 apd.dstlen = lstrlenW(apd.dst);
969 apd.copyflags = dwFileCopyFlags;
971 CreateDirectoryW(apd.src, NULL);
972 CreateDirectoryW(apd.dst, NULL);
974 hroot = open_driver_reg(env->envname);
976 ERR("Can't create Drivers key\n");
980 /* Fill the Registry for the Driver */
981 if ((lres = RegCreateKeyExW(hroot, di.pName, 0, NULL, REG_OPTION_NON_VOLATILE,
982 KEY_WRITE | KEY_QUERY_VALUE, NULL,
983 &hdrv, &disposition)) != ERROR_SUCCESS) {
985 ERR("can't create driver %s: %u\n", debugstr_w(di.pName), lres);
992 if (disposition == REG_OPENED_EXISTING_KEY) {
993 TRACE("driver %s already installed\n", debugstr_w(di.pName));
995 SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
999 /* Verified with the Adobe PS Driver, that w2k does not use di.Version */
1000 RegSetValueExW(hdrv, versionW, 0, REG_DWORD, (LPBYTE) &env->driverversion,
1003 RegSetValueExW(hdrv, driverW, 0, REG_SZ, (LPBYTE) di.pDriverPath,
1004 (lstrlenW(di.pDriverPath)+1)* sizeof(WCHAR));
1005 apd_copyfile(di.pDriverPath, &apd);
1007 RegSetValueExW(hdrv, data_fileW, 0, REG_SZ, (LPBYTE) di.pDataFile,
1008 (lstrlenW(di.pDataFile)+1)* sizeof(WCHAR));
1009 apd_copyfile(di.pDataFile, &apd);
1011 RegSetValueExW(hdrv, configuration_fileW, 0, REG_SZ, (LPBYTE) di.pConfigFile,
1012 (lstrlenW(di.pConfigFile)+1)* sizeof(WCHAR));
1013 apd_copyfile(di.pConfigFile, &apd);
1015 /* settings for level 3 */
1017 RegSetValueExW(hdrv, help_fileW, 0, REG_SZ, (LPBYTE) di.pHelpFile,
1018 (lstrlenW(di.pHelpFile)+1)* sizeof(WCHAR));
1020 RegSetValueExW(hdrv, help_fileW, 0, REG_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1021 apd_copyfile(di.pHelpFile, &apd);
1024 ptr = di.pDependentFiles;
1026 RegSetValueExW(hdrv, dependent_filesW, 0, REG_MULTI_SZ, (LPBYTE) di.pDependentFiles,
1027 multi_sz_lenW(di.pDependentFiles));
1029 RegSetValueExW(hdrv, dependent_filesW, 0, REG_MULTI_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1030 while ((ptr != NULL) && (ptr[0])) {
1031 if (apd_copyfile(ptr, &apd)) {
1032 ptr += lstrlenW(ptr) + 1;
1036 WARN("Failed to copy %s\n", debugstr_w(ptr));
1040 /* The language-Monitor was already copied by the caller to "%SystemRoot%\system32" */
1041 if (di.pMonitorName)
1042 RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (LPBYTE) di.pMonitorName,
1043 (lstrlenW(di.pMonitorName)+1)* sizeof(WCHAR));
1045 RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1047 if (di.pDefaultDataType)
1048 RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (LPBYTE) di.pDefaultDataType,
1049 (lstrlenW(di.pDefaultDataType)+1)* sizeof(WCHAR));
1051 RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1053 /* settings for level 4 */
1054 if (di.pszzPreviousNames)
1055 RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (LPBYTE) di.pszzPreviousNames,
1056 multi_sz_lenW(di.pszzPreviousNames));
1058 RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1060 if (level > 5) TRACE("level %u for Driver %s is incomplete\n", level, debugstr_w(di.pName));
1063 hui = driver_load(env, di.pConfigFile);
1064 pDrvDriverEvent = (void *)GetProcAddress(hui, "DrvDriverEvent");
1065 if (hui && pDrvDriverEvent) {
1067 /* Support for DrvDriverEvent is optional */
1068 TRACE("DRIVER_EVENT_INITIALIZE for %s (%s)\n", debugstr_w(di.pName), debugstr_w(di.pConfigFile));
1069 /* MSDN: level for DRIVER_INFO is 1 to 3 */
1070 res = pDrvDriverEvent(DRIVER_EVENT_INITIALIZE, 3, (LPBYTE) &di, 0);
1071 TRACE("got %d from DRIVER_EVENT_INITIALIZE\n", res);
1075 TRACE("=> TRUE with %u\n", GetLastError());
1080 /******************************************************************************
1081 * fpAddMonitor [exported through PRINTPROVIDOR]
1083 * Install a Printmonitor
1086 * pName [I] Servername or NULL (local Computer)
1087 * Level [I] Structure-Level (Must be 2)
1088 * pMonitors [I] PTR to MONITOR_INFO_2
1095 * All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1098 static BOOL WINAPI fpAddMonitor(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1100 monitor_t * pm = NULL;
1101 LPMONITOR_INFO_2W mi2w;
1107 mi2w = (LPMONITOR_INFO_2W) pMonitors;
1108 TRACE("(%s, %d, %p): %s %s %s\n", debugstr_w(pName), Level, pMonitors,
1109 debugstr_w(mi2w ? mi2w->pName : NULL),
1110 debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
1111 debugstr_w(mi2w ? mi2w->pDLLName : NULL));
1113 if (copy_servername_from_name(pName, NULL)) {
1114 FIXME("server %s not supported\n", debugstr_w(pName));
1115 SetLastError(ERROR_ACCESS_DENIED);
1119 if (!mi2w->pName || (! mi2w->pName[0])) {
1120 WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
1121 SetLastError(ERROR_INVALID_PARAMETER);
1124 if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, x86_envnameW)) {
1125 WARN("Environment %s requested (we support only %s)\n",
1126 debugstr_w(mi2w->pEnvironment), debugstr_w(x86_envnameW));
1127 SetLastError(ERROR_INVALID_ENVIRONMENT);
1131 if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
1132 WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
1133 SetLastError(ERROR_INVALID_PARAMETER);
1137 /* Load and initialize the monitor. SetLastError() is called on failure */
1138 if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
1143 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1144 ERR("unable to create key %s\n", debugstr_w(monitorsW));
1148 if (RegCreateKeyExW(hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
1149 KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
1150 &disposition) == ERROR_SUCCESS) {
1152 /* Some installers set options for the port before calling AddMonitor.
1153 We query the "Driver" entry to verify that the monitor is installed,
1154 before we return an error.
1155 When a user installs two print monitors at the same time with the
1156 same name, a race condition is possible but silently ignored. */
1160 if ((disposition == REG_OPENED_EXISTING_KEY) &&
1161 (RegQueryValueExW(hentry, driverW, NULL, NULL, NULL,
1162 &namesize) == ERROR_SUCCESS)) {
1163 TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
1164 /* 9x use ERROR_ALREADY_EXISTS */
1165 SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
1170 len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
1171 res = (RegSetValueExW(hentry, driverW, 0, REG_SZ,
1172 (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
1174 RegCloseKey(hentry);
1181 /******************************************************************************
1182 * fpAddPrinterDriverEx [exported through PRINTPROVIDOR]
1184 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1187 * pName [I] Servername or NULL (local Computer)
1188 * level [I] Level for the supplied DRIVER_INFO_*W struct
1189 * pDriverInfo [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
1190 * dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
1197 static BOOL WINAPI fpAddPrinterDriverEx(LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
1201 TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
1202 lres = copy_servername_from_name(pName, NULL);
1204 FIXME("server %s not supported\n", debugstr_w(pName));
1205 SetLastError(ERROR_ACCESS_DENIED);
1209 if ((dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY) != APD_COPY_ALL_FILES) {
1210 TRACE("Flags 0x%x ignored (using APD_COPY_ALL_FILES)\n", dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY);
1213 return myAddPrinterDriverEx(level, pDriverInfo, dwFileCopyFlags, TRUE);
1215 /******************************************************************
1216 * fpDeleteMonitor [exported through PRINTPROVIDOR]
1218 * Delete a specific Printmonitor from a Printing-Environment
1221 * pName [I] Servername or NULL (local Computer)
1222 * pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1223 * pMonitorName [I] Name of the Monitor, that should be deleted
1230 * pEnvironment is ignored in Windows for the local Computer.
1234 static BOOL WINAPI fpDeleteMonitor(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1239 TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
1240 debugstr_w(pMonitorName));
1242 lres = copy_servername_from_name(pName, NULL);
1244 FIXME("server %s not supported\n", debugstr_w(pName));
1245 SetLastError(ERROR_INVALID_NAME);
1249 /* pEnvironment is ignored in Windows for the local Computer */
1250 if (!pMonitorName || !pMonitorName[0]) {
1251 TRACE("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
1252 SetLastError(ERROR_INVALID_PARAMETER);
1256 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1257 ERR("unable to create key %s\n", debugstr_w(monitorsW));
1261 if(RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS) {
1262 TRACE("%s deleted\n", debugstr_w(pMonitorName));
1267 TRACE("%s does not exist\n", debugstr_w(pMonitorName));
1270 /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
1271 SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
1275 /*****************************************************************************
1276 * fpEnumMonitors [exported through PRINTPROVIDOR]
1278 * Enumerate available Port-Monitors
1281 * pName [I] Servername or NULL (local Computer)
1282 * Level [I] Structure-Level (1:Win9x+NT or 2:NT only)
1283 * pMonitors [O] PTR to Buffer that receives the Result
1284 * cbBuf [I] Size of Buffer at pMonitors
1285 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
1286 * pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
1290 * Failure: FALSE and in pcbNeeded the Bytes required for pMonitors, if cbBuf is too small
1293 * Windows reads the Registry once and cache the Results.
1296 static BOOL WINAPI fpEnumMonitors(LPWSTR pName, DWORD Level, LPBYTE pMonitors, DWORD cbBuf,
1297 LPDWORD pcbNeeded, LPDWORD pcReturned)
1299 DWORD numentries = 0;
1304 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
1305 cbBuf, pcbNeeded, pcReturned);
1307 lres = copy_servername_from_name(pName, NULL);
1309 FIXME("server %s not supported\n", debugstr_w(pName));
1310 SetLastError(ERROR_INVALID_NAME);
1314 if (!Level || (Level > 2)) {
1315 WARN("level (%d) is ignored in win9x\n", Level);
1316 SetLastError(ERROR_INVALID_LEVEL);
1320 /* Scan all Monitor-Keys */
1322 needed = get_local_monitors(Level, NULL, 0, &numentries);
1324 /* we calculated the needed buffersize. now do more error-checks */
1325 if (cbBuf < needed) {
1326 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1330 /* fill the Buffer with the Monitor-Keys */
1331 needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
1335 if (pcbNeeded) *pcbNeeded = needed;
1336 if (pcReturned) *pcReturned = numentries;
1338 TRACE("returning %d with %d (%d byte for %d entries)\n",
1339 res, GetLastError(), needed, numentries);
1344 /******************************************************************************
1345 * fpEnumPorts [exported through PRINTPROVIDOR]
1347 * Enumerate available Ports
1350 * pName [I] Servername or NULL (local Computer)
1351 * Level [I] Structure-Level (1 or 2)
1352 * pPorts [O] PTR to Buffer that receives the Result
1353 * cbBuf [I] Size of Buffer at pPorts
1354 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
1355 * pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
1359 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
1362 static BOOL WINAPI fpEnumPorts(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
1363 LPDWORD pcbNeeded, LPDWORD pcReturned)
1366 DWORD numentries = 0;
1370 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
1371 cbBuf, pcbNeeded, pcReturned);
1373 lres = copy_servername_from_name(pName, NULL);
1375 FIXME("server %s not supported\n", debugstr_w(pName));
1376 SetLastError(ERROR_INVALID_NAME);
1380 if (!Level || (Level > 2)) {
1381 SetLastError(ERROR_INVALID_LEVEL);
1385 if (!pcbNeeded || (!pPorts && (cbBuf > 0))) {
1386 SetLastError(RPC_X_NULL_REF_POINTER);
1390 EnterCriticalSection(&monitor_handles_cs);
1393 /* Scan all local Ports */
1395 needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
1397 /* we calculated the needed buffersize. now do the error-checks */
1398 if (cbBuf < needed) {
1399 monitor_unloadall();
1400 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1401 goto emP_cleanup_cs;
1403 else if (!pPorts || !pcReturned) {
1404 monitor_unloadall();
1405 SetLastError(RPC_X_NULL_REF_POINTER);
1406 goto emP_cleanup_cs;
1409 /* Fill the Buffer */
1410 needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
1412 monitor_unloadall();
1415 LeaveCriticalSection(&monitor_handles_cs);
1418 if (pcbNeeded) *pcbNeeded = needed;
1419 if (pcReturned) *pcReturned = (res) ? numentries : 0;
1421 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
1422 (res), GetLastError(), needed, (res) ? numentries : 0, numentries);
1427 /*****************************************************
1428 * setup_provider [internal]
1430 void setup_provider(void)
1432 static const PRINTPROVIDOR backend = {
1433 NULL, /* fpOpenPrinter */
1434 NULL, /* fpSetJob */
1435 NULL, /* fpGetJob */
1436 NULL, /* fpEnumJobs */
1437 NULL, /* fpAddPrinter */
1438 NULL, /* fpDeletePrinter */
1439 NULL, /* fpSetPrinter */
1440 NULL, /* fpGetPrinter */
1441 NULL, /* fpEnumPrinters */
1442 NULL, /* fpAddPrinterDriver */
1443 NULL, /* fpEnumPrinterDrivers */
1444 NULL, /* fpGetPrinterDriver */
1445 fpGetPrinterDriverDirectory,
1446 NULL, /* fpDeletePrinterDriver */
1447 NULL, /* fpAddPrintProcessor */
1448 NULL, /* fpEnumPrintProcessors */
1449 NULL, /* fpGetPrintProcessorDirectory */
1450 NULL, /* fpDeletePrintProcessor */
1451 NULL, /* fpEnumPrintProcessorDatatypes */
1452 NULL, /* fpStartDocPrinter */
1453 NULL, /* fpStartPagePrinter */
1454 NULL, /* fpWritePrinter */
1455 NULL, /* fpEndPagePrinter */
1456 NULL, /* fpAbortPrinter */
1457 NULL, /* fpReadPrinter */
1458 NULL, /* fpEndDocPrinter */
1459 NULL, /* fpAddJob */
1460 NULL, /* fpScheduleJob */
1461 NULL, /* fpGetPrinterData */
1462 NULL, /* fpSetPrinterData */
1463 NULL, /* fpWaitForPrinterChange */
1464 NULL, /* fpClosePrinter */
1465 NULL, /* fpAddForm */
1466 NULL, /* fpDeleteForm */
1467 NULL, /* fpGetForm */
1468 NULL, /* fpSetForm */
1469 NULL, /* fpEnumForms */
1472 NULL, /* fpAddPort */
1473 NULL, /* fpConfigurePort */
1474 NULL, /* fpDeletePort */
1475 NULL, /* fpCreatePrinterIC */
1476 NULL, /* fpPlayGdiScriptOnPrinterIC */
1477 NULL, /* fpDeletePrinterIC */
1478 NULL, /* fpAddPrinterConnection */
1479 NULL, /* fpDeletePrinterConnection */
1480 NULL, /* fpPrinterMessageBox */
1483 NULL, /* fpResetPrinter */
1484 NULL, /* fpGetPrinterDriverEx */
1485 NULL, /* fpFindFirstPrinterChangeNotification */
1486 NULL, /* fpFindClosePrinterChangeNotification */
1487 NULL, /* fpAddPortEx */
1488 NULL, /* fpShutDown */
1489 NULL, /* fpRefreshPrinterChangeNotification */
1490 NULL, /* fpOpenPrinterEx */
1491 NULL, /* fpAddPrinterEx */
1492 NULL, /* fpSetPort */
1493 NULL, /* fpEnumPrinterData */
1494 NULL, /* fpDeletePrinterData */
1495 NULL, /* fpClusterSplOpen */
1496 NULL, /* fpClusterSplClose */
1497 NULL, /* fpClusterSplIsAlive */
1498 NULL, /* fpSetPrinterDataEx */
1499 NULL, /* fpGetPrinterDataEx */
1500 NULL, /* fpEnumPrinterDataEx */
1501 NULL, /* fpEnumPrinterKey */
1502 NULL, /* fpDeletePrinterDataEx */
1503 NULL, /* fpDeletePrinterKey */
1504 NULL, /* fpSeekPrinter */
1505 NULL, /* fpDeletePrinterDriverEx */
1506 NULL, /* fpAddPerMachineConnection */
1507 NULL, /* fpDeletePerMachineConnection */
1508 NULL, /* fpEnumPerMachineConnections */
1509 NULL, /* fpXcvData */
1510 fpAddPrinterDriverEx,
1511 NULL, /* fpSplReadPrinter */
1512 NULL, /* fpDriverUnloadComplete */
1513 NULL, /* fpGetSpoolFileInfo */
1514 NULL, /* fpCommitSpoolData */
1515 NULL, /* fpCloseSpoolFileHandle */
1516 NULL, /* fpFlushPrinter */
1517 NULL, /* fpSendRecvBidiData */
1518 NULL /* fpAddDriverCatalog */
1520 pprovider = &backend;
1524 /*****************************************************
1525 * InitializePrintProvidor (localspl.@)
1527 * Initialize the Printprovider
1530 * pPrintProvidor [I] Buffer to fill with a struct PRINTPROVIDOR
1531 * cbPrintProvidor [I] Size of Buffer in Bytes
1532 * pFullRegistryPath [I] Registry-Path for the Printprovidor
1535 * Success: TRUE and pPrintProvidor filled
1539 * The RegistryPath should be:
1540 * "System\CurrentControlSet\Control\Print\Providers\<providername>",
1541 * but this Parameter is ignored in "localspl.dll".
1545 BOOL WINAPI InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor,
1546 DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
1549 TRACE("(%p, %u, %s)\n", pPrintProvidor, cbPrintProvidor, debugstr_w(pFullRegistryPath));
1550 memcpy(pPrintProvidor, pprovider,
1551 (cbPrintProvidor < sizeof(PRINTPROVIDOR)) ? cbPrintProvidor : sizeof(PRINTPROVIDOR));