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 */
255 if (serverlen == 0) return 0;
257 TRACE("found %s\n", debugstr_wn(server, serverlen));
259 if (serverlen > MAX_COMPUTERNAME_LENGTH) return -serverlen;
262 memcpy(target, server, serverlen * sizeof(WCHAR));
263 target[serverlen] = '\0';
266 len = sizeof(buffer) / sizeof(buffer[0]);
267 if (GetComputerNameW(buffer, &len)) {
268 if ((serverlen == len) && (strncmpiW(server, buffer, len) == 0)) {
269 /* The requested Servername is our computername */
276 /******************************************************************
277 * monitor_unload [internal]
279 * release a printmonitor and unload it from memory, when needed
282 static void monitor_unload(monitor_t * pm)
284 if (pm == NULL) return;
285 TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
287 EnterCriticalSection(&monitor_handles_cs);
289 if (pm->refcount) pm->refcount--;
291 if (pm->refcount == 0) {
292 list_remove(&pm->entry);
293 FreeLibrary(pm->hdll);
295 heap_free(pm->dllname);
298 LeaveCriticalSection(&monitor_handles_cs);
301 /******************************************************************
302 * monitor_unloadall [internal]
304 * release all printmonitors and unload them from memory, when needed
308 static void monitor_unloadall(void)
313 EnterCriticalSection(&monitor_handles_cs);
314 /* iterate through the list, with safety against removal */
315 LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
319 LeaveCriticalSection(&monitor_handles_cs);
322 /******************************************************************
323 * monitor_load [internal]
325 * load a printmonitor, get the dllname from the registry, when needed
326 * initialize the monitor and dump found function-pointers
328 * On failure, SetLastError() is called and NULL is returned
331 static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname)
333 LPMONITOR2 (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
334 PMONITORUI (WINAPI *pInitializePrintMonitorUI)(VOID);
335 LPMONITOREX (WINAPI *pInitializePrintMonitor) (LPWSTR);
336 DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
337 DWORD (WINAPI *pInitializeMonitor) (LPWSTR);
339 monitor_t * pm = NULL;
341 LPWSTR regroot = NULL;
342 LPWSTR driver = dllname;
344 TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
345 /* Is the Monitor already loaded? */
346 EnterCriticalSection(&monitor_handles_cs);
349 LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
351 if (cursor->name && (lstrcmpW(name, cursor->name) == 0)) {
359 pm = heap_alloc_zero(sizeof(monitor_t));
360 if (pm == NULL) goto cleanup;
361 list_add_tail(&monitor_handles, &pm->entry);
365 if (pm->name == NULL) {
366 /* Load the monitor */
367 LPMONITOREX pmonitorEx;
371 len = lstrlenW(monitorsW) + lstrlenW(name) + 2;
372 regroot = heap_alloc(len * sizeof(WCHAR));
376 lstrcpyW(regroot, monitorsW);
377 lstrcatW(regroot, name);
378 /* Get the Driver from the Registry */
379 if (driver == NULL) {
382 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
383 if (RegQueryValueExW(hroot, driverW, NULL, NULL, NULL,
384 &namesize) == ERROR_SUCCESS) {
385 driver = heap_alloc(namesize);
386 RegQueryValueExW(hroot, driverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
393 pm->name = strdupW(name);
394 pm->dllname = strdupW(driver);
396 if ((name && (!regroot || !pm->name)) || !pm->dllname) {
398 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
403 pm->hdll = LoadLibraryW(driver);
404 TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
406 if (pm->hdll == NULL) {
408 SetLastError(ERROR_MOD_NOT_FOUND);
413 pInitializePrintMonitor2 = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
414 pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
415 pInitializePrintMonitor = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
416 pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
417 pInitializeMonitor = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
420 TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
421 TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
422 TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
423 TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
424 TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
426 if (pInitializePrintMonitorUI != NULL) {
427 pm->monitorUI = pInitializePrintMonitorUI();
428 TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver));
430 TRACE("0x%08x: dwMonitorSize (%d)\n",
431 pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize);
436 if (pInitializePrintMonitor && regroot) {
437 pmonitorEx = pInitializePrintMonitor(regroot);
438 TRACE("%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
439 pmonitorEx, debugstr_w(driver), debugstr_w(regroot));
442 pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
443 pm->monitor = &(pmonitorEx->Monitor);
448 TRACE("0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize);
452 if (!pm->monitor && regroot) {
453 if (pInitializePrintMonitor2 != NULL) {
454 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
456 if (pInitializeMonitorEx != NULL) {
457 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
459 if (pInitializeMonitor != NULL) {
460 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
463 if (!pm->monitor && !pm->monitorUI) {
465 SetLastError(ERROR_PROC_NOT_FOUND);
470 if ((pm_localport == NULL) && (pm != NULL) && (lstrcmpW(pm->name, localportW) == 0)) {
474 LeaveCriticalSection(&monitor_handles_cs);
475 if (driver != dllname) heap_free(driver);
477 TRACE("=> %p\n", pm);
481 /******************************************************************
482 * monitor_loadall [internal]
484 * Load all registered monitors
487 static DWORD monitor_loadall(void)
490 DWORD registered = 0;
493 WCHAR buffer[MAX_PATH];
496 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hmonitors) == ERROR_SUCCESS) {
497 RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, ®istered, NULL, NULL,
498 NULL, NULL, NULL, NULL, NULL);
500 TRACE("%d monitors registered\n", registered);
502 while (id < registered) {
504 RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
505 pm = monitor_load(buffer, NULL);
509 RegCloseKey(hmonitors);
511 TRACE("%d monitors loaded\n", loaded);
515 /******************************************************************
516 * Return the number of bytes for an multi_sz string.
517 * The result includes all \0s
518 * (specifically the extra \0, that is needed as multi_sz terminator).
520 static int multi_sz_lenW(const WCHAR *str)
522 const WCHAR *ptr = str;
526 ptr += lstrlenW(ptr) + 1;
529 return (ptr - str + 1) * sizeof(WCHAR);
532 /******************************************************************
533 * validate_envW [internal]
535 * validate the user-supplied printing-environment
538 * env [I] PTR to Environment-String or NULL
541 * Success: PTR to printenv_t
542 * Failure: NULL and ERROR_INVALID_ENVIRONMENT
545 * An empty string is handled the same way as NULL.
549 static const printenv_t * validate_envW(LPCWSTR env)
551 const printenv_t *result = NULL;
554 TRACE("(%s)\n", debugstr_w(env));
557 for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
559 if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
561 result = all_printenv[i];
565 if (result == NULL) {
566 FIXME("unsupported Environment: %s\n", debugstr_w(env));
567 SetLastError(ERROR_INVALID_ENVIRONMENT);
569 /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
573 result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
576 TRACE("=> using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
580 /*****************************************************************************
581 * enumerate the local monitors (INTERNAL)
583 * returns the needed size (in bytes) for pMonitors
584 * and *lpreturned is set to number of entries returned in pMonitors
586 * Language-Monitors are also installed in the same Registry-Location but
587 * they are filtered in Windows (not returned by EnumMonitors).
588 * We do no filtering to simplify our Code.
591 static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
596 LPMONITOR_INFO_2W mi;
597 WCHAR buffer[MAX_PATH];
598 WCHAR dllname[MAX_PATH];
606 entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
608 numentries = *lpreturned; /* this is 0, when we scan the registry */
609 len = entrysize * numentries;
610 ptr = (LPWSTR) &pMonitors[len];
613 len = sizeof(buffer)/sizeof(buffer[0]);
616 /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
617 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) == ERROR_SUCCESS) {
618 /* Scan all Monitor-Registry-Keys */
619 while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
620 TRACE("Monitor_%d: %s\n", numentries, debugstr_w(buffer));
621 dllsize = sizeof(dllname);
624 /* The Monitor must have a Driver-DLL */
625 if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
626 if (RegQueryValueExW(hentry, driverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
627 /* We found a valid DLL for this Monitor. */
628 TRACE("using Driver: %s\n", debugstr_w(dllname));
633 /* Windows returns only Port-Monitors here, but to simplify our code,
634 we do no filtering for Language-Monitors */
638 needed += (len+1) * sizeof(WCHAR); /* len is lstrlenW(monitorname) */
640 /* we install and return only monitors for "Windows NT x86" */
641 needed += (lstrlenW(x86_envnameW) +1) * sizeof(WCHAR);
645 /* required size is calculated. Now fill the user-buffer */
646 if (pMonitors && (cbBuf >= needed)){
647 mi = (LPMONITOR_INFO_2W) pMonitors;
648 pMonitors += entrysize;
650 TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi, level, numentries);
652 lstrcpyW(ptr, buffer); /* Name of the Monitor */
653 ptr += (len+1); /* len is lstrlenW(monitorname) */
655 mi->pEnvironment = ptr;
656 lstrcpyW(ptr, x86_envnameW); /* fixed to "Windows NT x86" */
657 ptr += (lstrlenW(x86_envnameW)+1);
660 lstrcpyW(ptr, dllname); /* Name of the Driver-DLL */
661 ptr += (dllsize / sizeof(WCHAR));
666 len = sizeof(buffer)/sizeof(buffer[0]);
671 *lpreturned = numentries;
672 TRACE("need %d byte for %d entries\n", needed, numentries);
676 /******************************************************************
677 * enumerate the local Ports from all loaded monitors (internal)
679 * returns the needed size (in bytes) for pPorts
680 * and *lpreturned is set to number of entries returned in pPorts
683 static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
687 LPPORT_INFO_2W cache;
689 LPBYTE pi_buffer = NULL;
690 DWORD pi_allocated = 0;
701 TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
702 entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
704 numentries = *lpreturned; /* this is 0, when we scan the registry */
705 needed = entrysize * numentries;
706 ptr = (LPWSTR) &pPorts[needed];
711 LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
713 if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
716 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
717 if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
718 /* Do not use heap_realloc (we do not need the old data in the buffer) */
719 heap_free(pi_buffer);
720 pi_buffer = heap_alloc(pi_needed);
721 pi_allocated = (pi_buffer) ? pi_needed : 0;
722 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
724 TRACE("(%s) got %d with %d (need %d byte for %d entries)\n",
725 debugstr_w(pm->name), res, GetLastError(), pi_needed, pi_returned);
727 numentries += pi_returned;
730 /* fill the output-buffer (pPorts), if we have one */
731 if (pPorts && (cbBuf >= needed ) && pi_buffer) {
733 while (pi_returned > pi_index) {
734 cache = (LPPORT_INFO_2W) &pi_buffer[pi_index * entrysize];
735 out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
736 out->pPortName = ptr;
737 lstrcpyW(ptr, cache->pPortName);
738 ptr += (lstrlenW(ptr)+1);
740 out->pMonitorName = ptr;
741 lstrcpyW(ptr, cache->pMonitorName);
742 ptr += (lstrlenW(ptr)+1);
744 out->pDescription = ptr;
745 lstrcpyW(ptr, cache->pDescription);
746 ptr += (lstrlenW(ptr)+1);
747 out->fPortType = cache->fPortType;
748 out->Reserved = cache->Reserved;
756 /* the temporary portinfo-buffer is no longer needed */
757 heap_free(pi_buffer);
759 *lpreturned = numentries;
760 TRACE("need %d byte for %d entries\n", needed, numentries);
765 /*****************************************************************************
766 * open_driver_reg [internal]
768 * opens the registry for the printer drivers depending on the given input
769 * variable pEnvironment
772 * Success: the opened hkey
775 static HKEY open_driver_reg(LPCWSTR pEnvironment)
779 const printenv_t * env;
781 TRACE("(%s)\n", debugstr_w(pEnvironment));
783 env = validate_envW(pEnvironment);
784 if (!env) return NULL;
786 buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(fmt_driversW) +
787 (lstrlenW(env->envname) + lstrlenW(env->versionregpath)) * sizeof(WCHAR));
790 wsprintfW(buffer, fmt_driversW, env->envname, env->versionregpath);
791 RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
792 HeapFree(GetProcessHeap(), 0, buffer);
797 /*****************************************************************************
798 * fpGetPrinterDriverDirectory [exported through PRINTPROVIDOR]
800 * Return the PATH for the Printer-Drivers
803 * pName [I] Servername (NT only) or NULL (local Computer)
804 * pEnvironment [I] Printing-Environment (see below) or NULL (Default)
805 * Level [I] Structure-Level (must be 1)
806 * pDriverDirectory [O] PTR to Buffer that receives the Result
807 * cbBuf [I] Size of Buffer at pDriverDirectory
808 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used /
809 * required for pDriverDirectory
812 * Success: TRUE and in pcbNeeded the Bytes used in pDriverDirectory
813 * Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
814 * if cbBuf is too small
816 * Native Values returned in pDriverDirectory on Success:
817 *| NT(Windows NT x86): "%winsysdir%\\spool\\DRIVERS\\w32x86"
818 *| NT(Windows 4.0): "%winsysdir%\\spool\\DRIVERS\\win40"
819 *| win9x(Windows 4.0): "%winsysdir%"
821 * "%winsysdir%" is the Value from GetSystemDirectoryW()
824 static BOOL WINAPI fpGetPrinterDriverDirectory(LPWSTR pName, LPWSTR pEnvironment,
825 DWORD Level, LPBYTE pDriverDirectory, DWORD cbBuf, LPDWORD pcbNeeded)
828 const printenv_t * env;
830 TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName),
831 debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
833 if (pName != NULL && pName[0]) {
834 FIXME("server %s not supported\n", debugstr_w(pName));
835 SetLastError(ERROR_INVALID_PARAMETER);
839 env = validate_envW(pEnvironment);
840 if (!env) return FALSE; /* pEnvironment invalid or unsupported */
843 /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
844 needed = GetSystemDirectoryW(NULL, 0);
845 /* add the Size for the Subdirectories */
846 needed += lstrlenW(spooldriversW);
847 needed += lstrlenW(env->subdir);
848 needed *= sizeof(WCHAR); /* return-value is size in Bytes */
852 if (needed > cbBuf) {
853 SetLastError(ERROR_INSUFFICIENT_BUFFER);
857 if (pDriverDirectory == NULL) {
858 /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
859 SetLastError(ERROR_INVALID_USER_BUFFER);
863 GetSystemDirectoryW((LPWSTR) pDriverDirectory, cbBuf/sizeof(WCHAR));
864 /* add the Subdirectories */
865 lstrcatW((LPWSTR) pDriverDirectory, spooldriversW);
866 lstrcatW((LPWSTR) pDriverDirectory, env->subdir);
868 TRACE("=> %s\n", debugstr_w((LPWSTR) pDriverDirectory));
872 /******************************************************************
873 * driver_load [internal]
875 * load a driver user interface dll
877 * On failure, NULL is returned
881 static HMODULE driver_load(const printenv_t * env, LPWSTR dllname)
883 WCHAR fullname[MAX_PATH];
887 TRACE("(%p, %s)\n", env, debugstr_w(dllname));
889 /* build the driverdir */
890 len = sizeof(fullname) -
891 (lstrlenW(env->versionsubdir) + 1 + lstrlenW(dllname) + 1) * sizeof(WCHAR);
893 if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
894 (LPBYTE) fullname, len, &len)) {
895 /* Should never Fail */
896 SetLastError(ERROR_BUFFER_OVERFLOW);
900 lstrcatW(fullname, env->versionsubdir);
901 lstrcatW(fullname, backslashW);
902 lstrcatW(fullname, dllname);
904 hui = LoadLibraryW(fullname);
905 TRACE("%p: LoadLibrary(%s) %d\n", hui, debugstr_w(fullname), GetLastError());
910 /******************************************************************************
911 * myAddPrinterDriverEx [internal]
913 * Install a Printer Driver with the Option to upgrade / downgrade the Files
914 * and a special mode with lazy error checking.
917 static BOOL myAddPrinterDriverEx(DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags, BOOL lazy)
919 static const WCHAR emptyW[1];
920 const printenv_t *env;
923 BOOL (WINAPI *pDrvDriverEvent)(DWORD, DWORD, LPBYTE, LPARAM);
933 /* we need to set all entries in the Registry, independent from the Level of
934 DRIVER_INFO, that the caller supplied */
936 ZeroMemory(&di, sizeof(di));
937 if (pDriverInfo && (level < (sizeof(di_sizeof) / sizeof(di_sizeof[0])))) {
938 memcpy(&di, pDriverInfo, di_sizeof[level]);
941 /* dump the most used infos */
942 TRACE("%p: .cVersion : 0x%x/%d\n", pDriverInfo, di.cVersion, di.cVersion);
943 TRACE("%p: .pName : %s\n", di.pName, debugstr_w(di.pName));
944 TRACE("%p: .pEnvironment: %s\n", di.pEnvironment, debugstr_w(di.pEnvironment));
945 TRACE("%p: .pDriverPath : %s\n", di.pDriverPath, debugstr_w(di.pDriverPath));
946 TRACE("%p: .pDataFile : %s\n", di.pDataFile, debugstr_w(di.pDataFile));
947 TRACE("%p: .pConfigFile : %s\n", di.pConfigFile, debugstr_w(di.pConfigFile));
948 TRACE("%p: .pHelpFile : %s\n", di.pHelpFile, debugstr_w(di.pHelpFile));
949 /* dump only the first of the additional Files */
950 TRACE("%p: .pDependentFiles: %s\n", di.pDependentFiles, debugstr_w(di.pDependentFiles));
953 /* check environment */
954 env = validate_envW(di.pEnvironment);
955 if (env == NULL) return FALSE; /* ERROR_INVALID_ENVIRONMENT */
957 /* fill the copy-data / get the driverdir */
958 len = sizeof(apd.src) - sizeof(version3_subdirW) - sizeof(WCHAR);
959 if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
960 (LPBYTE) apd.src, len, &len)) {
961 /* Should never Fail */
964 memcpy(apd.dst, apd.src, len);
965 lstrcatW(apd.src, backslashW);
966 apd.srclen = lstrlenW(apd.src);
967 lstrcatW(apd.dst, env->versionsubdir);
968 lstrcatW(apd.dst, backslashW);
969 apd.dstlen = lstrlenW(apd.dst);
970 apd.copyflags = dwFileCopyFlags;
972 CreateDirectoryW(apd.src, NULL);
973 CreateDirectoryW(apd.dst, NULL);
975 hroot = open_driver_reg(env->envname);
977 ERR("Can't create Drivers key\n");
981 /* Fill the Registry for the Driver */
982 if ((lres = RegCreateKeyExW(hroot, di.pName, 0, NULL, REG_OPTION_NON_VOLATILE,
983 KEY_WRITE | KEY_QUERY_VALUE, NULL,
984 &hdrv, &disposition)) != ERROR_SUCCESS) {
986 ERR("can't create driver %s: %u\n", debugstr_w(di.pName), lres);
993 if (disposition == REG_OPENED_EXISTING_KEY) {
994 TRACE("driver %s already installed\n", debugstr_w(di.pName));
996 SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
1000 /* Verified with the Adobe PS Driver, that w2k does not use di.Version */
1001 RegSetValueExW(hdrv, versionW, 0, REG_DWORD, (LPBYTE) &env->driverversion,
1004 RegSetValueExW(hdrv, driverW, 0, REG_SZ, (LPBYTE) di.pDriverPath,
1005 (lstrlenW(di.pDriverPath)+1)* sizeof(WCHAR));
1006 apd_copyfile(di.pDriverPath, &apd);
1008 RegSetValueExW(hdrv, data_fileW, 0, REG_SZ, (LPBYTE) di.pDataFile,
1009 (lstrlenW(di.pDataFile)+1)* sizeof(WCHAR));
1010 apd_copyfile(di.pDataFile, &apd);
1012 RegSetValueExW(hdrv, configuration_fileW, 0, REG_SZ, (LPBYTE) di.pConfigFile,
1013 (lstrlenW(di.pConfigFile)+1)* sizeof(WCHAR));
1014 apd_copyfile(di.pConfigFile, &apd);
1016 /* settings for level 3 */
1018 RegSetValueExW(hdrv, help_fileW, 0, REG_SZ, (LPBYTE) di.pHelpFile,
1019 (lstrlenW(di.pHelpFile)+1)* sizeof(WCHAR));
1021 RegSetValueExW(hdrv, help_fileW, 0, REG_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1022 apd_copyfile(di.pHelpFile, &apd);
1025 ptr = di.pDependentFiles;
1027 RegSetValueExW(hdrv, dependent_filesW, 0, REG_MULTI_SZ, (LPBYTE) di.pDependentFiles,
1028 multi_sz_lenW(di.pDependentFiles));
1030 RegSetValueExW(hdrv, dependent_filesW, 0, REG_MULTI_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1031 while ((ptr != NULL) && (ptr[0])) {
1032 if (apd_copyfile(ptr, &apd)) {
1033 ptr += lstrlenW(ptr) + 1;
1037 WARN("Failed to copy %s\n", debugstr_w(ptr));
1041 /* The language-Monitor was already copied by the caller to "%SystemRoot%\system32" */
1042 if (di.pMonitorName)
1043 RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (LPBYTE) di.pMonitorName,
1044 (lstrlenW(di.pMonitorName)+1)* sizeof(WCHAR));
1046 RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1048 if (di.pDefaultDataType)
1049 RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (LPBYTE) di.pDefaultDataType,
1050 (lstrlenW(di.pDefaultDataType)+1)* sizeof(WCHAR));
1052 RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1054 /* settings for level 4 */
1055 if (di.pszzPreviousNames)
1056 RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (LPBYTE) di.pszzPreviousNames,
1057 multi_sz_lenW(di.pszzPreviousNames));
1059 RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1061 if (level > 5) TRACE("level %u for Driver %s is incomplete\n", level, debugstr_w(di.pName));
1064 hui = driver_load(env, di.pConfigFile);
1065 pDrvDriverEvent = (void *)GetProcAddress(hui, "DrvDriverEvent");
1066 if (hui && pDrvDriverEvent) {
1068 /* Support for DrvDriverEvent is optional */
1069 TRACE("DRIVER_EVENT_INITIALIZE for %s (%s)\n", debugstr_w(di.pName), debugstr_w(di.pConfigFile));
1070 /* MSDN: level for DRIVER_INFO is 1 to 3 */
1071 res = pDrvDriverEvent(DRIVER_EVENT_INITIALIZE, 3, (LPBYTE) &di, 0);
1072 TRACE("got %d from DRIVER_EVENT_INITIALIZE\n", res);
1076 TRACE("=> TRUE with %u\n", GetLastError());
1081 /******************************************************************************
1082 * fpAddMonitor [exported through PRINTPROVIDOR]
1084 * Install a Printmonitor
1087 * pName [I] Servername or NULL (local Computer)
1088 * Level [I] Structure-Level (Must be 2)
1089 * pMonitors [I] PTR to MONITOR_INFO_2
1096 * All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1099 static BOOL WINAPI fpAddMonitor(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1101 monitor_t * pm = NULL;
1102 LPMONITOR_INFO_2W mi2w;
1108 mi2w = (LPMONITOR_INFO_2W) pMonitors;
1109 TRACE("(%s, %d, %p): %s %s %s\n", debugstr_w(pName), Level, pMonitors,
1110 debugstr_w(mi2w ? mi2w->pName : NULL),
1111 debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
1112 debugstr_w(mi2w ? mi2w->pDLLName : NULL));
1114 if (copy_servername_from_name(pName, NULL)) {
1115 FIXME("server %s not supported\n", debugstr_w(pName));
1116 SetLastError(ERROR_ACCESS_DENIED);
1120 if (!mi2w->pName || (! mi2w->pName[0])) {
1121 WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
1122 SetLastError(ERROR_INVALID_PARAMETER);
1125 if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, x86_envnameW)) {
1126 WARN("Environment %s requested (we support only %s)\n",
1127 debugstr_w(mi2w->pEnvironment), debugstr_w(x86_envnameW));
1128 SetLastError(ERROR_INVALID_ENVIRONMENT);
1132 if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
1133 WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
1134 SetLastError(ERROR_INVALID_PARAMETER);
1138 /* Load and initialize the monitor. SetLastError() is called on failure */
1139 if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
1144 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1145 ERR("unable to create key %s\n", debugstr_w(monitorsW));
1149 if (RegCreateKeyExW(hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
1150 KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
1151 &disposition) == ERROR_SUCCESS) {
1153 /* Some installers set options for the port before calling AddMonitor.
1154 We query the "Driver" entry to verify that the monitor is installed,
1155 before we return an error.
1156 When a user installs two print monitors at the same time with the
1157 same name, a race condition is possible but silently ignored. */
1161 if ((disposition == REG_OPENED_EXISTING_KEY) &&
1162 (RegQueryValueExW(hentry, driverW, NULL, NULL, NULL,
1163 &namesize) == ERROR_SUCCESS)) {
1164 TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
1165 /* 9x use ERROR_ALREADY_EXISTS */
1166 SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
1171 len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
1172 res = (RegSetValueExW(hentry, driverW, 0, REG_SZ,
1173 (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
1175 RegCloseKey(hentry);
1182 /******************************************************************************
1183 * fpAddPrinterDriverEx [exported through PRINTPROVIDOR]
1185 * Install a Printer Driver with the Option to upgrade / downgrade the Files
1188 * pName [I] Servername or NULL (local Computer)
1189 * level [I] Level for the supplied DRIVER_INFO_*W struct
1190 * pDriverInfo [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
1191 * dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
1198 static BOOL WINAPI fpAddPrinterDriverEx(LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
1202 TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
1203 lres = copy_servername_from_name(pName, NULL);
1205 FIXME("server %s not supported\n", debugstr_w(pName));
1206 SetLastError(ERROR_ACCESS_DENIED);
1210 if ((dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY) != APD_COPY_ALL_FILES) {
1211 TRACE("Flags 0x%x ignored (using APD_COPY_ALL_FILES)\n", dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY);
1214 return myAddPrinterDriverEx(level, pDriverInfo, dwFileCopyFlags, TRUE);
1216 /******************************************************************
1217 * fpDeleteMonitor [exported through PRINTPROVIDOR]
1219 * Delete a specific Printmonitor from a Printing-Environment
1222 * pName [I] Servername or NULL (local Computer)
1223 * pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1224 * pMonitorName [I] Name of the Monitor, that should be deleted
1231 * pEnvironment is ignored in Windows for the local Computer.
1235 static BOOL WINAPI fpDeleteMonitor(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1240 TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
1241 debugstr_w(pMonitorName));
1243 lres = copy_servername_from_name(pName, NULL);
1245 FIXME("server %s not supported\n", debugstr_w(pName));
1246 SetLastError(ERROR_INVALID_NAME);
1250 /* pEnvironment is ignored in Windows for the local Computer */
1251 if (!pMonitorName || !pMonitorName[0]) {
1252 TRACE("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
1253 SetLastError(ERROR_INVALID_PARAMETER);
1257 if(RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1258 ERR("unable to create key %s\n", debugstr_w(monitorsW));
1262 if(RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS) {
1263 TRACE("%s deleted\n", debugstr_w(pMonitorName));
1268 TRACE("%s does not exist\n", debugstr_w(pMonitorName));
1271 /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
1272 SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
1276 /*****************************************************************************
1277 * fpEnumMonitors [exported through PRINTPROVIDOR]
1279 * Enumerate available Port-Monitors
1282 * pName [I] Servername or NULL (local Computer)
1283 * Level [I] Structure-Level (1:Win9x+NT or 2:NT only)
1284 * pMonitors [O] PTR to Buffer that receives the Result
1285 * cbBuf [I] Size of Buffer at pMonitors
1286 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
1287 * pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
1291 * Failure: FALSE and in pcbNeeded the Bytes required for pMonitors, if cbBuf is too small
1294 * Windows reads the Registry once and cache the Results.
1297 static BOOL WINAPI fpEnumMonitors(LPWSTR pName, DWORD Level, LPBYTE pMonitors, DWORD cbBuf,
1298 LPDWORD pcbNeeded, LPDWORD pcReturned)
1300 DWORD numentries = 0;
1305 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
1306 cbBuf, pcbNeeded, pcReturned);
1308 lres = copy_servername_from_name(pName, NULL);
1310 FIXME("server %s not supported\n", debugstr_w(pName));
1311 SetLastError(ERROR_INVALID_NAME);
1315 if (!Level || (Level > 2)) {
1316 WARN("level (%d) is ignored in win9x\n", Level);
1317 SetLastError(ERROR_INVALID_LEVEL);
1321 /* Scan all Monitor-Keys */
1323 needed = get_local_monitors(Level, NULL, 0, &numentries);
1325 /* we calculated the needed buffersize. now do more error-checks */
1326 if (cbBuf < needed) {
1327 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1331 /* fill the Buffer with the Monitor-Keys */
1332 needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
1336 if (pcbNeeded) *pcbNeeded = needed;
1337 if (pcReturned) *pcReturned = numentries;
1339 TRACE("returning %d with %d (%d byte for %d entries)\n",
1340 res, GetLastError(), needed, numentries);
1345 /******************************************************************************
1346 * fpEnumPorts [exported through PRINTPROVIDOR]
1348 * Enumerate available Ports
1351 * pName [I] Servername or NULL (local Computer)
1352 * Level [I] Structure-Level (1 or 2)
1353 * pPorts [O] PTR to Buffer that receives the Result
1354 * cbBuf [I] Size of Buffer at pPorts
1355 * pcbNeeded [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
1356 * pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
1360 * Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
1363 static BOOL WINAPI fpEnumPorts(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
1364 LPDWORD pcbNeeded, LPDWORD pcReturned)
1367 DWORD numentries = 0;
1371 TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
1372 cbBuf, pcbNeeded, pcReturned);
1374 lres = copy_servername_from_name(pName, NULL);
1376 FIXME("server %s not supported\n", debugstr_w(pName));
1377 SetLastError(ERROR_INVALID_NAME);
1381 if (!Level || (Level > 2)) {
1382 SetLastError(ERROR_INVALID_LEVEL);
1386 if (!pcbNeeded || (!pPorts && (cbBuf > 0))) {
1387 SetLastError(RPC_X_NULL_REF_POINTER);
1391 EnterCriticalSection(&monitor_handles_cs);
1394 /* Scan all local Ports */
1396 needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
1398 /* we calculated the needed buffersize. now do the error-checks */
1399 if (cbBuf < needed) {
1400 monitor_unloadall();
1401 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1402 goto emP_cleanup_cs;
1404 else if (!pPorts || !pcReturned) {
1405 monitor_unloadall();
1406 SetLastError(RPC_X_NULL_REF_POINTER);
1407 goto emP_cleanup_cs;
1410 /* Fill the Buffer */
1411 needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
1413 monitor_unloadall();
1416 LeaveCriticalSection(&monitor_handles_cs);
1419 if (pcbNeeded) *pcbNeeded = needed;
1420 if (pcReturned) *pcReturned = (res) ? numentries : 0;
1422 TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
1423 (res), GetLastError(), needed, (res) ? numentries : 0, numentries);
1428 /*****************************************************
1429 * setup_provider [internal]
1431 void setup_provider(void)
1433 static const PRINTPROVIDOR backend = {
1434 NULL, /* fpOpenPrinter */
1435 NULL, /* fpSetJob */
1436 NULL, /* fpGetJob */
1437 NULL, /* fpEnumJobs */
1438 NULL, /* fpAddPrinter */
1439 NULL, /* fpDeletePrinter */
1440 NULL, /* fpSetPrinter */
1441 NULL, /* fpGetPrinter */
1442 NULL, /* fpEnumPrinters */
1443 NULL, /* fpAddPrinterDriver */
1444 NULL, /* fpEnumPrinterDrivers */
1445 NULL, /* fpGetPrinterDriver */
1446 fpGetPrinterDriverDirectory,
1447 NULL, /* fpDeletePrinterDriver */
1448 NULL, /* fpAddPrintProcessor */
1449 NULL, /* fpEnumPrintProcessors */
1450 NULL, /* fpGetPrintProcessorDirectory */
1451 NULL, /* fpDeletePrintProcessor */
1452 NULL, /* fpEnumPrintProcessorDatatypes */
1453 NULL, /* fpStartDocPrinter */
1454 NULL, /* fpStartPagePrinter */
1455 NULL, /* fpWritePrinter */
1456 NULL, /* fpEndPagePrinter */
1457 NULL, /* fpAbortPrinter */
1458 NULL, /* fpReadPrinter */
1459 NULL, /* fpEndDocPrinter */
1460 NULL, /* fpAddJob */
1461 NULL, /* fpScheduleJob */
1462 NULL, /* fpGetPrinterData */
1463 NULL, /* fpSetPrinterData */
1464 NULL, /* fpWaitForPrinterChange */
1465 NULL, /* fpClosePrinter */
1466 NULL, /* fpAddForm */
1467 NULL, /* fpDeleteForm */
1468 NULL, /* fpGetForm */
1469 NULL, /* fpSetForm */
1470 NULL, /* fpEnumForms */
1473 NULL, /* fpAddPort */
1474 NULL, /* fpConfigurePort */
1475 NULL, /* fpDeletePort */
1476 NULL, /* fpCreatePrinterIC */
1477 NULL, /* fpPlayGdiScriptOnPrinterIC */
1478 NULL, /* fpDeletePrinterIC */
1479 NULL, /* fpAddPrinterConnection */
1480 NULL, /* fpDeletePrinterConnection */
1481 NULL, /* fpPrinterMessageBox */
1484 NULL, /* fpResetPrinter */
1485 NULL, /* fpGetPrinterDriverEx */
1486 NULL, /* fpFindFirstPrinterChangeNotification */
1487 NULL, /* fpFindClosePrinterChangeNotification */
1488 NULL, /* fpAddPortEx */
1489 NULL, /* fpShutDown */
1490 NULL, /* fpRefreshPrinterChangeNotification */
1491 NULL, /* fpOpenPrinterEx */
1492 NULL, /* fpAddPrinterEx */
1493 NULL, /* fpSetPort */
1494 NULL, /* fpEnumPrinterData */
1495 NULL, /* fpDeletePrinterData */
1496 NULL, /* fpClusterSplOpen */
1497 NULL, /* fpClusterSplClose */
1498 NULL, /* fpClusterSplIsAlive */
1499 NULL, /* fpSetPrinterDataEx */
1500 NULL, /* fpGetPrinterDataEx */
1501 NULL, /* fpEnumPrinterDataEx */
1502 NULL, /* fpEnumPrinterKey */
1503 NULL, /* fpDeletePrinterDataEx */
1504 NULL, /* fpDeletePrinterKey */
1505 NULL, /* fpSeekPrinter */
1506 NULL, /* fpDeletePrinterDriverEx */
1507 NULL, /* fpAddPerMachineConnection */
1508 NULL, /* fpDeletePerMachineConnection */
1509 NULL, /* fpEnumPerMachineConnections */
1510 NULL, /* fpXcvData */
1511 fpAddPrinterDriverEx,
1512 NULL, /* fpSplReadPrinter */
1513 NULL, /* fpDriverUnloadComplete */
1514 NULL, /* fpGetSpoolFileInfo */
1515 NULL, /* fpCommitSpoolData */
1516 NULL, /* fpCloseSpoolFileHandle */
1517 NULL, /* fpFlushPrinter */
1518 NULL, /* fpSendRecvBidiData */
1519 NULL /* fpAddDriverCatalog */
1521 pprovider = &backend;
1525 /*****************************************************
1526 * InitializePrintProvidor (localspl.@)
1528 * Initialize the Printprovider
1531 * pPrintProvidor [I] Buffer to fill with a struct PRINTPROVIDOR
1532 * cbPrintProvidor [I] Size of Buffer in Bytes
1533 * pFullRegistryPath [I] Registry-Path for the Printprovidor
1536 * Success: TRUE and pPrintProvidor filled
1540 * The RegistryPath should be:
1541 * "System\CurrentControlSet\Control\Print\Providers\<providername>",
1542 * but this Parameter is ignored in "localspl.dll".
1546 BOOL WINAPI InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor,
1547 DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
1550 TRACE("(%p, %u, %s)\n", pPrintProvidor, cbPrintProvidor, debugstr_w(pFullRegistryPath));
1551 memcpy(pPrintProvidor, pprovider,
1552 (cbPrintProvidor < sizeof(PRINTPROVIDOR)) ? cbPrintProvidor : sizeof(PRINTPROVIDOR));