| /* | 
 |  * Routing for Spooler-Service helper DLL | 
 |  * | 
 |  * Copyright 2006-2009 Detlef Riekenberg | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
 |  */ | 
 |  | 
 | #include <stdarg.h> | 
 |  | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "winerror.h" | 
 | #include "winreg.h" | 
 |  | 
 | #include "wingdi.h" | 
 | #include "winspool.h" | 
 | #include "ddk/winsplp.h" | 
 | #include "spoolss.h" | 
 |  | 
 | #include "wine/debug.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(spoolss); | 
 |  | 
 | /* ################################ */ | 
 |  | 
 | #define MAX_BACKEND 3 | 
 |  | 
 | typedef struct { | 
 |     /* PRINTPROVIDOR functions */ | 
 |     DWORD  (WINAPI *fpOpenPrinter)(LPWSTR, HANDLE *, LPPRINTER_DEFAULTSW); | 
 |     DWORD  (WINAPI *fpSetJob)(HANDLE, DWORD, DWORD, LPBYTE, DWORD); | 
 |     DWORD  (WINAPI *fpGetJob)(HANDLE, DWORD, DWORD, LPBYTE, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpEnumJobs)(HANDLE, DWORD, DWORD, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD); | 
 |     HANDLE (WINAPI *fpAddPrinter)(LPWSTR, DWORD, LPBYTE); | 
 |     DWORD  (WINAPI *fpDeletePrinter)(HANDLE); | 
 |     DWORD  (WINAPI *fpSetPrinter)(HANDLE, DWORD, LPBYTE, DWORD); | 
 |     DWORD  (WINAPI *fpGetPrinter)(HANDLE, DWORD, LPBYTE, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpEnumPrinters)(DWORD, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpAddPrinterDriver)(LPWSTR, DWORD, LPBYTE); | 
 |     DWORD  (WINAPI *fpEnumPrinterDrivers)(LPWSTR, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpGetPrinterDriver)(HANDLE, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpGetPrinterDriverDirectory)(LPWSTR, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpDeletePrinterDriver)(LPWSTR, LPWSTR, LPWSTR); | 
 |     DWORD  (WINAPI *fpAddPrintProcessor)(LPWSTR, LPWSTR, LPWSTR, LPWSTR); | 
 |     DWORD  (WINAPI *fpEnumPrintProcessors)(LPWSTR, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpGetPrintProcessorDirectory)(LPWSTR, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpDeletePrintProcessor)(LPWSTR, LPWSTR, LPWSTR); | 
 |     DWORD  (WINAPI *fpEnumPrintProcessorDatatypes)(LPWSTR, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpStartDocPrinter)(HANDLE, DWORD, LPBYTE); | 
 |     DWORD  (WINAPI *fpStartPagePrinter)(HANDLE); | 
 |     DWORD  (WINAPI *fpWritePrinter)(HANDLE, LPVOID, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpEndPagePrinter)(HANDLE); | 
 |     DWORD  (WINAPI *fpAbortPrinter)(HANDLE); | 
 |     DWORD  (WINAPI *fpReadPrinter)(HANDLE, LPVOID, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpEndDocPrinter)(HANDLE); | 
 |     DWORD  (WINAPI *fpAddJob)(HANDLE, DWORD, LPBYTE, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpScheduleJob)(HANDLE, DWORD); | 
 |     DWORD  (WINAPI *fpGetPrinterData)(HANDLE, LPWSTR, LPDWORD, LPBYTE, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpSetPrinterData)(HANDLE, LPWSTR, DWORD, LPBYTE, DWORD); | 
 |     DWORD  (WINAPI *fpWaitForPrinterChange)(HANDLE, DWORD); | 
 |     DWORD  (WINAPI *fpClosePrinter)(HANDLE); | 
 |     DWORD  (WINAPI *fpAddForm)(HANDLE, DWORD, LPBYTE); | 
 |     DWORD  (WINAPI *fpDeleteForm)(HANDLE, LPWSTR); | 
 |     DWORD  (WINAPI *fpGetForm)(HANDLE, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpSetForm)(HANDLE, LPWSTR, DWORD, LPBYTE); | 
 |     DWORD  (WINAPI *fpEnumForms)(HANDLE, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpEnumMonitors)(LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpEnumPorts)(LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpAddPort)(LPWSTR, HWND, LPWSTR); | 
 |     DWORD  (WINAPI *fpConfigurePort)(LPWSTR, HWND, LPWSTR); | 
 |     DWORD  (WINAPI *fpDeletePort)(LPWSTR, HWND, LPWSTR); | 
 |     HANDLE (WINAPI *fpCreatePrinterIC)(HANDLE, LPDEVMODEW); | 
 |     DWORD  (WINAPI *fpPlayGdiScriptOnPrinterIC)(HANDLE, LPBYTE, DWORD, LPBYTE, DWORD, DWORD); | 
 |     DWORD  (WINAPI *fpDeletePrinterIC)(HANDLE); | 
 |     DWORD  (WINAPI *fpAddPrinterConnection)(LPWSTR); | 
 |     DWORD  (WINAPI *fpDeletePrinterConnection)(LPWSTR); | 
 |     DWORD  (WINAPI *fpPrinterMessageBox)(HANDLE, DWORD, HWND, LPWSTR, LPWSTR, DWORD); | 
 |     DWORD  (WINAPI *fpAddMonitor)(LPWSTR, DWORD, LPBYTE); | 
 |     DWORD  (WINAPI *fpDeleteMonitor)(LPWSTR, LPWSTR, LPWSTR); | 
 |     DWORD  (WINAPI *fpResetPrinter)(HANDLE, LPPRINTER_DEFAULTSW); | 
 |     DWORD  (WINAPI *fpGetPrinterDriverEx)(HANDLE, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, DWORD, DWORD, PDWORD, PDWORD); | 
 |     HANDLE (WINAPI *fpFindFirstPrinterChangeNotification)(HANDLE, DWORD, DWORD, LPVOID); | 
 |     DWORD  (WINAPI *fpFindClosePrinterChangeNotification)(HANDLE); | 
 |     DWORD  (WINAPI *fpAddPortEx)(HANDLE, LPWSTR, DWORD, LPBYTE, LPWSTR); | 
 |     DWORD  (WINAPI *fpShutDown)(LPVOID); | 
 |     DWORD  (WINAPI *fpRefreshPrinterChangeNotification)(HANDLE, DWORD, PVOID, PVOID); | 
 |     DWORD  (WINAPI *fpOpenPrinterEx)(LPWSTR, LPHANDLE, LPPRINTER_DEFAULTSW, LPBYTE, DWORD); | 
 |     HANDLE (WINAPI *fpAddPrinterEx)(LPWSTR, DWORD, LPBYTE, LPBYTE, DWORD); | 
 |     DWORD  (WINAPI *fpSetPort)(LPWSTR, LPWSTR, DWORD, LPBYTE); | 
 |     DWORD  (WINAPI *fpEnumPrinterData)(HANDLE, DWORD, LPWSTR, DWORD, LPDWORD, LPDWORD, LPBYTE, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpDeletePrinterData)(HANDLE, LPWSTR); | 
 |     DWORD  (WINAPI *fpClusterSplOpen)(LPCWSTR, LPCWSTR, PHANDLE, LPCWSTR, LPCWSTR); | 
 |     DWORD  (WINAPI *fpClusterSplClose)(HANDLE); | 
 |     DWORD  (WINAPI *fpClusterSplIsAlive)(HANDLE); | 
 |     DWORD  (WINAPI *fpSetPrinterDataEx)(HANDLE, LPCWSTR, LPCWSTR, DWORD, LPBYTE, DWORD); | 
 |     DWORD  (WINAPI *fpGetPrinterDataEx)(HANDLE, LPCWSTR, LPCWSTR, LPDWORD, LPBYTE, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpEnumPrinterDataEx)(HANDLE, LPCWSTR, LPBYTE, DWORD, LPDWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpEnumPrinterKey)(HANDLE, LPCWSTR, LPWSTR, DWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpDeletePrinterDataEx)(HANDLE, LPCWSTR, LPCWSTR); | 
 |     DWORD  (WINAPI *fpDeletePrinterKey)(HANDLE hPrinter, LPCWSTR pKeyName); | 
 |     DWORD  (WINAPI *fpSeekPrinter)(HANDLE, LARGE_INTEGER, PLARGE_INTEGER, DWORD, BOOL); | 
 |     DWORD  (WINAPI *fpDeletePrinterDriverEx)(LPWSTR, LPWSTR, LPWSTR, DWORD, DWORD); | 
 |     DWORD  (WINAPI *fpAddPerMachineConnection)(LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR); | 
 |     DWORD  (WINAPI *fpDeletePerMachineConnection)(LPCWSTR, LPCWSTR); | 
 |     DWORD  (WINAPI *fpEnumPerMachineConnections)(LPCWSTR, LPBYTE, DWORD, LPDWORD, LPDWORD); | 
 |     DWORD  (WINAPI *fpXcvData)(HANDLE, LPCWSTR, PBYTE, DWORD, PBYTE, DWORD, PDWORD, PDWORD); | 
 |     DWORD  (WINAPI *fpAddPrinterDriverEx)(LPWSTR, DWORD, LPBYTE, DWORD); | 
 |     DWORD  (WINAPI *fpSplReadPrinter)(HANDLE, LPBYTE *, DWORD); | 
 |     DWORD  (WINAPI *fpDriverUnloadComplete)(LPWSTR); | 
 |     DWORD  (WINAPI *fpGetSpoolFileInfo)(HANDLE, LPWSTR *, LPHANDLE, HANDLE, HANDLE); | 
 |     DWORD  (WINAPI *fpCommitSpoolData)(HANDLE, DWORD); | 
 |     DWORD  (WINAPI *fpCloseSpoolFileHandle)(HANDLE); | 
 |     DWORD  (WINAPI *fpFlushPrinter)(HANDLE, LPBYTE, DWORD, LPDWORD, DWORD); | 
 |     DWORD  (WINAPI *fpSendRecvBidiData)(HANDLE, LPCWSTR, LPBIDI_REQUEST_CONTAINER, LPBIDI_RESPONSE_CONTAINER *); | 
 |     DWORD  (WINAPI *fpAddDriverCatalog)(HANDLE, DWORD, VOID *, DWORD); | 
 |     /* Private Data */ | 
 |     HMODULE dll; | 
 |     LPWSTR  dllname; | 
 |     LPWSTR  name; | 
 |     LPWSTR  regroot; | 
 |     DWORD   index; | 
 | } backend_t; | 
 |  | 
 | /* ################################ */ | 
 |  | 
 | static backend_t *backend[MAX_BACKEND]; | 
 | static DWORD used_backends = 0; | 
 |  | 
 | static CRITICAL_SECTION backend_cs; | 
 | static CRITICAL_SECTION_DEBUG backend_cs_debug = | 
 | { | 
 |     0, 0, &backend_cs, | 
 |     { &backend_cs_debug.ProcessLocksList, &backend_cs_debug.ProcessLocksList }, | 
 |       0, 0, { (DWORD_PTR)(__FILE__ ": backend_cs") } | 
 | }; | 
 | static CRITICAL_SECTION backend_cs = { &backend_cs_debug, -1, 0, 0, 0, 0 }; | 
 |  | 
 | /* ################################ */ | 
 |  | 
 | static WCHAR localsplW[] = {'l','o','c','a','l','s','p','l','.','d','l','l',0}; | 
 |  | 
 | /****************************************************************** | 
 |  * strdupW [internal] | 
 |  * | 
 |  * create a copy of a unicode-string | 
 |  * | 
 |  */ | 
 |  | 
 | static LPWSTR strdupW(LPCWSTR p) | 
 | { | 
 |     LPWSTR ret; | 
 |     DWORD len; | 
 |  | 
 |     if(!p) return NULL; | 
 |     len = (lstrlenW(p) + 1) * sizeof(WCHAR); | 
 |     ret = heap_alloc(len); | 
 |     memcpy(ret, p, len); | 
 |     return ret; | 
 | } | 
 |  | 
 | /****************************************************************** | 
 |  * backend_unload_all [internal] | 
 |  * | 
 |  * unload all backends | 
 |  */ | 
 | void backend_unload_all(void) | 
 | { | 
 |     EnterCriticalSection(&backend_cs); | 
 |     while (used_backends > 0) { | 
 |         used_backends--; | 
 |         FreeLibrary(backend[used_backends]->dll); | 
 |         heap_free(backend[used_backends]->dllname); | 
 |         heap_free(backend[used_backends]->name); | 
 |         heap_free(backend[used_backends]->regroot); | 
 |         heap_free(backend[used_backends]); | 
 |         backend[used_backends] = NULL; | 
 |     } | 
 |     LeaveCriticalSection(&backend_cs); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * backend_load [internal] | 
 |  * | 
 |  * load and init a backend | 
 |  * | 
 |  * PARAMS | 
 |  *  name   [I] Printprovider to use for the backend. NULL for the local print provider | 
 |  * | 
 |  * RETURNS | 
 |  *  Success: PTR to the backend | 
 |  *  Failure: NULL | 
 |  * | 
 |  */ | 
 | static backend_t * backend_load(LPWSTR dllname, LPWSTR name, LPWSTR regroot) | 
 | { | 
 |  | 
 |     BOOL (WINAPI *pInitializePrintProvidor)(LPPRINTPROVIDOR, DWORD, LPWSTR); | 
 |     DWORD id; | 
 |     DWORD res; | 
 |  | 
 |     TRACE("(%s, %s, %s)\n", debugstr_w(dllname), debugstr_w(name), debugstr_w(regroot)); | 
 |  | 
 |     EnterCriticalSection(&backend_cs); | 
 |     id = used_backends; | 
 |  | 
 |     backend[id] = heap_alloc_zero(sizeof(backend_t)); | 
 |     if (!backend[id]) { | 
 |         LeaveCriticalSection(&backend_cs); | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     backend[id]->dllname = strdupW(dllname); | 
 |     backend[id]->name = strdupW(name); | 
 |     backend[id]->regroot = strdupW(regroot); | 
 |  | 
 |     backend[id]->dll = LoadLibraryW(dllname); | 
 |     if (backend[id]->dll) { | 
 |         pInitializePrintProvidor = (void *) GetProcAddress(backend[id]->dll, "InitializePrintProvidor"); | 
 |         if (pInitializePrintProvidor) { | 
 |  | 
 |             /* native localspl does not clear unused entries */ | 
 |             res = pInitializePrintProvidor((PRINTPROVIDOR *) backend[id], sizeof(PRINTPROVIDOR), regroot); | 
 |             if (res) { | 
 |                 used_backends++; | 
 |                 backend[id]->index = used_backends; | 
 |                 LeaveCriticalSection(&backend_cs); | 
 |                 TRACE("--> backend #%d: %p (%s)\n", id, backend[id], debugstr_w(dllname)); | 
 |                 return backend[id]; | 
 |             } | 
 |         } | 
 |         FreeLibrary(backend[id]->dll); | 
 |     } | 
 |     heap_free(backend[id]->dllname); | 
 |     heap_free(backend[id]->name); | 
 |     heap_free(backend[id]->regroot); | 
 |     heap_free(backend[id]); | 
 |     backend[id] = NULL; | 
 |     LeaveCriticalSection(&backend_cs); | 
 |     WARN("failed to init %s: %u\n", debugstr_w(dllname), GetLastError()); | 
 |     return NULL; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * backend_load_all [internal] | 
 |  * | 
 |  * load and init all backends | 
 |  * | 
 |  * RETURNS | 
 |  *  Success: TRUE | 
 |  *  Failure: FALSE | 
 |  * | 
 |  */ | 
 | BOOL backend_load_all(void) | 
 | { | 
 |     static BOOL failed = FALSE; | 
 |     backend_t * pb; | 
 |  | 
 |     EnterCriticalSection(&backend_cs); | 
 |  | 
 |     /* if we failed before, don't try again */ | 
 |     if (!failed && (used_backends == 0)) { | 
 |         pb = backend_load(localsplW, NULL, NULL); | 
 |  | 
 |         /* ToDo: parse the registry and load all other backends */ | 
 |  | 
 |         failed = (used_backends == 0); | 
 |     } | 
 |     LeaveCriticalSection(&backend_cs); | 
 |     TRACE("-> %d\n", !failed); | 
 |     return (!failed); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * backend_first [internal] | 
 |  * | 
 |  * find the first usable backend | 
 |  * | 
 |  * RETURNS | 
 |  *  Success: PTR to the backend | 
 |  *  Failure: NULL | 
 |  * | 
 |  */ | 
 | static backend_t * backend_first(LPWSTR name) | 
 | { | 
 |  | 
 |     EnterCriticalSection(&backend_cs); | 
 |     /* Load all backends, when not done yet */ | 
 |     if (used_backends || backend_load_all()) { | 
 |  | 
 |         /* test for the local system first */ | 
 |         if (!name || !name[0]) { | 
 |             LeaveCriticalSection(&backend_cs); | 
 |             return backend[0]; | 
 |         } | 
 |     } | 
 |  | 
 |     FIXME("server %s not supported in %d backends\n", debugstr_w(name), used_backends); | 
 |     LeaveCriticalSection(&backend_cs); | 
 |     return NULL; | 
 | } | 
 |  | 
 | /****************************************************************** | 
 |  * AddMonitorW (spoolss.@) | 
 |  * | 
 |  * Install a Printmonitor | 
 |  * | 
 |  * PARAMS | 
 |  *  pName       [I] Servername or NULL (local Computer) | 
 |  *  Level       [I] Structure-Level (Must be 2) | 
 |  *  pMonitors   [I] PTR to MONITOR_INFO_2 | 
 |  * | 
 |  * RETURNS | 
 |  *  Success: TRUE | 
 |  *  Failure: FALSE | 
 |  * | 
 |  * NOTES | 
 |  *  All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32") | 
 |  * | 
 |  */ | 
 | BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors) | 
 | { | 
 |     backend_t * pb; | 
 |     DWORD res = ROUTER_UNKNOWN; | 
 |  | 
 |     TRACE("(%s, %d, %p)\n", debugstr_w(pName), Level, pMonitors); | 
 |  | 
 |     if (Level != 2) { | 
 |         SetLastError(ERROR_INVALID_LEVEL); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     pb = backend_first(pName); | 
 |     if (pb && pb->fpAddMonitor) | 
 |         res = pb->fpAddMonitor(pName, Level, pMonitors); | 
 |     else | 
 |     { | 
 |         SetLastError(ERROR_PROC_NOT_FOUND); | 
 |     } | 
 |  | 
 |     TRACE("got %u with %u\n", res, GetLastError()); | 
 |     return (res == ROUTER_SUCCESS); | 
 | } | 
 |  | 
 | /****************************************************************** | 
 |  * AddPrinterDriverExW (spoolss.@) | 
 |  * | 
 |  * Install a Printer Driver with the Option to upgrade / downgrade the Files | 
 |  * | 
 |  * PARAMS | 
 |  *  pName           [I] Servername or NULL (local Computer) | 
 |  *  level           [I] Level for the supplied DRIVER_INFO_*W struct | 
 |  *  pDriverInfo     [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter | 
 |  *  dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files | 
 |  * | 
 |  * RESULTS | 
 |  *  Success: TRUE | 
 |  *  Failure: FALSE | 
 |  * | 
 |  */ | 
 | BOOL WINAPI AddPrinterDriverExW(LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags) | 
 | { | 
 |     backend_t * pb; | 
 |     DWORD res = ROUTER_UNKNOWN; | 
 |  | 
 |     TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags); | 
 |  | 
 |     if (!pDriverInfo) { | 
 |         SetLastError(ERROR_INVALID_PARAMETER); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     pb = backend_first(pName); | 
 |     if (pb && pb->fpAddPrinterDriverEx) | 
 |         res = pb->fpAddPrinterDriverEx(pName, level, pDriverInfo, dwFileCopyFlags); | 
 |     else | 
 |     { | 
 |         SetLastError(ERROR_PROC_NOT_FOUND); | 
 |     } | 
 |  | 
 |     TRACE("got %u with %u\n", res, GetLastError()); | 
 |     return (res == ROUTER_SUCCESS); | 
 | } | 
 |  | 
 | /****************************************************************** | 
 |  * DeleteMonitorW (spoolss.@) | 
 |  * | 
 |  * Delete a specific Printmonitor from a Printing-Environment | 
 |  * | 
 |  * PARAMS | 
 |  *  pName        [I] Servername or NULL (local Computer) | 
 |  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default) | 
 |  *  pMonitorName [I] Name of the Monitor, that should be deleted | 
 |  * | 
 |  * RETURNS | 
 |  *  Success: TRUE | 
 |  *  Failure: FALSE | 
 |  * | 
 |  */ | 
 | BOOL WINAPI DeleteMonitorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName) | 
 | { | 
 |     backend_t * pb; | 
 |     DWORD res = ROUTER_UNKNOWN; | 
 |  | 
 |     TRACE("(%s, %s, %s)\n", debugstr_w(pName), debugstr_w(pEnvironment), debugstr_w(pMonitorName)); | 
 |  | 
 |     pb = backend_first(pName); | 
 |     if (pb && pb->fpDeleteMonitor) | 
 |         res = pb->fpDeleteMonitor(pName, pEnvironment, pMonitorName); | 
 |     else | 
 |     { | 
 |         SetLastError(ERROR_PROC_NOT_FOUND); | 
 |     } | 
 |  | 
 |     TRACE("got %u with %u\n", res, GetLastError()); | 
 |     return (res == ROUTER_SUCCESS); | 
 | } | 
 |  | 
 | /****************************************************************** | 
 |  * EnumMonitorsW (spoolss.@) | 
 |  * | 
 |  * Enumerate available Port-Monitors | 
 |  * | 
 |  * PARAMS | 
 |  *  pName      [I] Servername or NULL (local Computer) | 
 |  *  Level      [I] Structure-Level | 
 |  *  pMonitors  [O] PTR to Buffer that receives the Result | 
 |  *  cbBuf      [I] Size of Buffer at pMonitors | 
 |  *  pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors | 
 |  *  pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors | 
 |  * | 
 |  * RETURNS | 
 |  *  Success: TRUE | 
 |  *  Failure: FALSE and in pcbNeeded the Bytes required for pMonitors, if cbBuf is too small | 
 |  * | 
 |  */ | 
 | BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors, DWORD cbBuf, | 
 |                           LPDWORD pcbNeeded, LPDWORD pcReturned) | 
 | { | 
 |     backend_t * pb; | 
 |     DWORD res = ROUTER_UNKNOWN; | 
 |  | 
 |     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors, | 
 |           cbBuf, pcbNeeded, pcReturned); | 
 |  | 
 |     if (pcbNeeded) *pcbNeeded = 0; | 
 |     if (pcReturned) *pcReturned = 0; | 
 |  | 
 |     pb = backend_first(pName); | 
 |     if (pb && pb->fpEnumMonitors) | 
 |         res = pb->fpEnumMonitors(pName, Level, pMonitors, cbBuf, pcbNeeded, pcReturned); | 
 |     else | 
 |     { | 
 |         SetLastError(ERROR_PROC_NOT_FOUND); | 
 |     } | 
 |  | 
 |     TRACE("got %u with %u (%u byte for %u entries)\n\n", res, GetLastError(), | 
 |             pcbNeeded ? *pcbNeeded : 0, pcReturned ? *pcReturned : 0); | 
 |  | 
 |     return (res == ROUTER_SUCCESS); | 
 | } | 
 |  | 
 | /****************************************************************** | 
 |  * EnumPortsW (spoolss.@) | 
 |  * | 
 |  * Enumerate available Ports | 
 |  * | 
 |  * PARAMS | 
 |  *  pName      [I] Servername or NULL (local Computer) | 
 |  *  Level      [I] Structure-Level (1 or 2) | 
 |  *  pPorts     [O] PTR to Buffer that receives the Result | 
 |  *  cbBuf      [I] Size of Buffer at pPorts | 
 |  *  pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / required for pPorts | 
 |  *  pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts | 
 |  * | 
 |  * RETURNS | 
 |  *  Success: TRUE | 
 |  *  Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small | 
 |  * | 
 |  */ | 
 | BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, | 
 |                        LPDWORD pcbNeeded, LPDWORD pcReturned) | 
 | { | 
 |     backend_t * pb; | 
 |     DWORD res = ROUTER_UNKNOWN; | 
 |  | 
 |     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts, cbBuf, | 
 |             pcbNeeded, pcReturned); | 
 |  | 
 |     if (pcbNeeded) *pcbNeeded = 0; | 
 |     if (pcReturned) *pcReturned = 0; | 
 |  | 
 |     pb = backend_first(pName); | 
 |     if (pb && pb->fpEnumPorts) | 
 |         res = pb->fpEnumPorts(pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned); | 
 |     else | 
 |     { | 
 |         SetLastError(ERROR_PROC_NOT_FOUND); | 
 |     } | 
 |  | 
 |     TRACE("got %u with %u (%u byte for %u entries)\n", res, GetLastError(), | 
 |             pcbNeeded ? *pcbNeeded : 0, pcReturned ? *pcReturned : 0); | 
 |  | 
 |     return (res == ROUTER_SUCCESS); | 
 | } | 
 |  | 
 | /****************************************************************** | 
 |  * GetPrinterDriverDirectoryW (spoolss.@) | 
 |  * | 
 |  * Return the PATH for the Printer-Drivers | 
 |  * | 
 |  * PARAMS | 
 |  *   pName            [I] Servername or NULL (local Computer) | 
 |  *   pEnvironment     [I] Printing-Environment or NULL (Default) | 
 |  *   Level            [I] Structure-Level (must be 1) | 
 |  *   pDriverDirectory [O] PTR to Buffer that receives the Result | 
 |  *   cbBuf            [I] Size of Buffer at pDriverDirectory | 
 |  *   pcbNeeded        [O] PTR to DWORD that receives the size in Bytes used / | 
 |  *                        required for pDriverDirectory | 
 |  * | 
 |  * RETURNS | 
 |  *   Success: TRUE  and in pcbNeeded the Bytes used in pDriverDirectory | 
 |  *   Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory, | 
 |  *   if cbBuf is too small | 
 |  * | 
 |  *   Native Values returned in pDriverDirectory on Success: | 
 |  *|  NT(Windows NT x86): "%winsysdir%\\spool\\DRIVERS\\w32x86" | 
 |  *|  NT(Windows x64):    "%winsysdir%\\spool\\DRIVERS\\x64" | 
 |  *|  NT(Windows 4.0):    "%winsysdir%\\spool\\DRIVERS\\win40" | 
 |  *|  win9x(Windows 4.0): "%winsysdir%" | 
 |  * | 
 |  *   "%winsysdir%" is the Value from GetSystemDirectoryW() | 
 |  * | 
 |  */ | 
 | BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment, | 
 |             DWORD Level, LPBYTE pDriverDirectory, DWORD cbBuf, LPDWORD pcbNeeded) | 
 | { | 
 |     backend_t * pb; | 
 |     DWORD res = ROUTER_UNKNOWN; | 
 |  | 
 |     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), | 
 |           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded); | 
 |  | 
 |     if (pcbNeeded) *pcbNeeded = 0; | 
 |  | 
 |     pb = backend_first(pName); | 
 |     if (pb && pb->fpGetPrinterDriverDirectory) | 
 |         res = pb->fpGetPrinterDriverDirectory(pName, pEnvironment, Level, | 
 |                                               pDriverDirectory, cbBuf, pcbNeeded); | 
 |     else | 
 |     { | 
 |         SetLastError(ERROR_PROC_NOT_FOUND); | 
 |     } | 
 |  | 
 |     TRACE("got %u with %u (%u byte)\n", | 
 |             res, GetLastError(), pcbNeeded ? *pcbNeeded : 0); | 
 |  | 
 |     return (res == ROUTER_SUCCESS); | 
 |  | 
 | } |