[PRINTING]
authorColin Finck <colin@reactos.org>
Wed, 26 Oct 2016 12:37:27 +0000 (12:37 +0000)
committerColin Finck <colin@reactos.org>
Wed, 26 Oct 2016 12:37:27 +0000 (12:37 +0000)
Finally merge my branch "colins-printing-for-freedom", giving us an initial implementation of a Win32-compatible Printing Stack (localmon, localspl, spoolss, spoolsv, winspool)
You can now send raw data to parallel port printers using documented Win32 API. An example application is in my "winspool_print" commandline tool merged to rosapps.
ReactOS folks, thanks for your support during the development of this, making my bachelor's thesis a reality! :)

Documentation/Thesis: https://svn.reactos.org/reactos/trunk/documentation/articles/Printing%20Stack%20Thesis/thesis.pdf
Video: https://www.youtube.com/watch?v=cNzePucTOLY

CORE-10489

svn path=/trunk/; revision=73039

198 files changed:
reactos/base/services/CMakeLists.txt
reactos/base/services/spoolsv/CMakeLists.txt [deleted file]
reactos/base/services/spoolsv/spoolsv.c [deleted file]
reactos/base/services/spoolsv/spoolsv.rc [deleted file]
reactos/boot/bootdata/hivesys.inf
reactos/dll/win32/CMakeLists.txt
reactos/dll/win32/localspl/CMakeLists.txt [deleted file]
reactos/dll/win32/localspl/lang/spl_Cy.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Da.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_De.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_En.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Es.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Fr.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_He.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Hu.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_It.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Ja.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Ko.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Lt.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Nl.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_No.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Pl.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Pt.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Ro.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Ru.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Si.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Sq.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Sv.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Tr.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Uk.rc [deleted file]
reactos/dll/win32/localspl/lang/spl_Zh.rc [deleted file]
reactos/dll/win32/localspl/localmon.c [deleted file]
reactos/dll/win32/localspl/localspl.spec [deleted file]
reactos/dll/win32/localspl/localspl_main.c [deleted file]
reactos/dll/win32/localspl/localspl_private.h [deleted file]
reactos/dll/win32/localspl/provider.c [deleted file]
reactos/dll/win32/localspl/resource.h [deleted file]
reactos/dll/win32/ntprint/CMakeLists.txt [deleted file]
reactos/dll/win32/ntprint/ntprint.c [deleted file]
reactos/dll/win32/ntprint/ntprint.rc [deleted file]
reactos/dll/win32/ntprint/ntprint.spec [deleted file]
reactos/dll/win32/spoolss/CMakeLists.txt [deleted file]
reactos/dll/win32/spoolss/router.c [deleted file]
reactos/dll/win32/spoolss/spoolss.h [deleted file]
reactos/dll/win32/spoolss/spoolss_main.c [deleted file]
reactos/dll/win32/winspool/CMakeLists.txt [deleted file]
reactos/dll/win32/winspool/info.c [deleted file]
reactos/dll/win32/winspool/precomp.h [deleted file]
reactos/dll/win32/winspool/stubs.c [deleted file]
reactos/dll/win32/winspool/winspool.spec [deleted file]
reactos/sdk/include/psdk/winspool.h
reactos/sdk/include/reactos/idl/winspool.idl [new file with mode: 0644]
reactos/sdk/lib/CMakeLists.txt
reactos/sdk/lib/skiplist/CMakeLists.txt [new file with mode: 0644]
reactos/sdk/lib/skiplist/skiplist.c [new file with mode: 0644]
reactos/sdk/lib/skiplist/skiplist.h [new file with mode: 0644]
reactos/sdk/lib/skiplist/skiplist_test.c [new file with mode: 0644]
reactos/win32ss/CMakeLists.txt
reactos/win32ss/printing/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/base/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/context.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/jobs.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/main.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/memory.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/monitors.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/ports.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/precomp.h [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/printers.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/printprocessors.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/spoolss.rc [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/spoolss.spec [moved from reactos/dll/win32/spoolss/spoolss.spec with 60% similarity]
reactos/win32ss/printing/base/spoolss/tools.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/forms.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/init.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/jobs.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/main.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/monitors.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/notifications.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/ports.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/precomp.h [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/printerdata.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/printerdrivers.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/printers.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/printprocessors.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/printproviders.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/rpcserver.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/rpcstubs.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/spoolsv.rc [new file with mode: 0644]
reactos/win32ss/printing/base/spoolsv/xcv.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/devmode.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/jobs.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/main.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/monitors.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/ports.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/precomp.h [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/printerdata.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/printerdrivers.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/printers.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/printprocessors.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/printproviders.c [new file with mode: 0644]
reactos/win32ss/printing/base/winspool/winspool.rc [moved from reactos/dll/win32/winspool/winspool.rc with 71% similarity]
reactos/win32ss/printing/base/winspool/winspool.spec [new file with mode: 0644]
reactos/win32ss/printing/drivers/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/include/spoolss.h [new file with mode: 0644]
reactos/win32ss/printing/monitors/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/lang/de-DE.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/lang/en-US.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/localmon.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/localmon.spec [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/main.c [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ports.c [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/precomp.h [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/resource.h [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/tools.c [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Da.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_De.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_En.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Es.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Fr.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_He.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Hu.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_It.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ja.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ko.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Lt.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Nl.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_No.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Pl.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Pt.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ro.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ru.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Si.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Sq.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Sv.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Tr.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Uk.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Zh.rc [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/localui.c [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/localui.h [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/ui/localui.rc [moved from reactos/dll/win32/localspl/localspl.rc with 62% similarity]
reactos/win32ss/printing/monitors/localmon/ui/localui.spec [new file with mode: 0644]
reactos/win32ss/printing/monitors/localmon/xcv.c [new file with mode: 0644]
reactos/win32ss/printing/monitors/tcpmon/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/processors/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/processors/winprint/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/processors/winprint/main.c [new file with mode: 0644]
reactos/win32ss/printing/processors/winprint/precomp.h [new file with mode: 0644]
reactos/win32ss/printing/processors/winprint/raw.c [new file with mode: 0644]
reactos/win32ss/printing/processors/winprint/winprint.rc [new file with mode: 0644]
reactos/win32ss/printing/processors/winprint/winprint.spec [new file with mode: 0644]
reactos/win32ss/printing/providers/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/CMakeLists.txt [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/jobs.c [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/localspl.rc [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/localspl.spec [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/main.c [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/monitors.c [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/ports.c [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/precomp.h [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/printers.c [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/printingthread.c [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/printprocessors.c [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/tools.c [new file with mode: 0644]
rosapps/applications/cmdutils/CMakeLists.txt
rosapps/applications/cmdutils/winspool_print/CMakeLists.txt [new file with mode: 0644]
rosapps/applications/cmdutils/winspool_print/main.c [new file with mode: 0644]
rostests/apitests/CMakeLists.txt
rostests/apitests/localspl/CMakeLists.txt [new file with mode: 0644]
rostests/apitests/localspl/dll/CMakeLists.txt [new file with mode: 0644]
rostests/apitests/localspl/dll/fpEnumPrinters.c [new file with mode: 0644]
rostests/apitests/localspl/dll/main.c [new file with mode: 0644]
rostests/apitests/localspl/localspl_apitest.h [new file with mode: 0644]
rostests/apitests/localspl/service.c [new file with mode: 0644]
rostests/apitests/localspl/testlist.c [new file with mode: 0644]
rostests/apitests/localspl/tests.c [new file with mode: 0644]
rostests/apitests/spoolss/CMakeLists.txt [new file with mode: 0644]
rostests/apitests/spoolss/PackStrings.c [new file with mode: 0644]
rostests/apitests/spoolss/ReallocSplStr.c [new file with mode: 0644]
rostests/apitests/spoolss/SplInitializeWinSpoolDrv.c [new file with mode: 0644]
rostests/apitests/spoolss/testlist.c [new file with mode: 0644]
rostests/apitests/winprint/CMakeLists.txt [new file with mode: 0644]
rostests/apitests/winprint/EnumPrintProcessorDatatypesW.c [new file with mode: 0644]
rostests/apitests/winprint/testlist.c [new file with mode: 0644]
rostests/apitests/winspool/CMakeLists.txt [new file with mode: 0644]
rostests/apitests/winspool/ClosePrinter.c [new file with mode: 0644]
rostests/apitests/winspool/EnumPrintProcessorDatatypes.c [new file with mode: 0644]
rostests/apitests/winspool/EnumPrinters.c [new file with mode: 0644]
rostests/apitests/winspool/GetPrintProcessorDirectory.c [new file with mode: 0644]
rostests/apitests/winspool/IsValidDevmode.c [new file with mode: 0644]
rostests/apitests/winspool/OpenPrinter.c [new file with mode: 0644]
rostests/apitests/winspool/StartDocPrinter.c [new file with mode: 0644]
rostests/apitests/winspool/testlist.c [new file with mode: 0644]
rostests/winetests/CMakeLists.txt

index 9318071..fa0c017 100644 (file)
@@ -4,7 +4,6 @@ add_subdirectory(dhcpcsvc)
 add_subdirectory(eventlog)
 add_subdirectory(rpcss)
 add_subdirectory(schedsvc)
-add_subdirectory(spoolsv)
 add_subdirectory(srvsvc)
 add_subdirectory(svchost)
 add_subdirectory(tcpsvcs)
diff --git a/reactos/base/services/spoolsv/CMakeLists.txt b/reactos/base/services/spoolsv/CMakeLists.txt
deleted file mode 100644 (file)
index 1d4686c..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-
-add_executable(spoolsv spoolsv.c spoolsv.rc)
-target_link_libraries(spoolsv wine)
-set_module_type(spoolsv win32cui UNICODE)
-add_importlibs(spoolsv advapi32 msvcrt kernel32 ntdll)
-add_cd_file(TARGET spoolsv DESTINATION reactos/system32 FOR all)
diff --git a/reactos/base/services/spoolsv/spoolsv.c b/reactos/base/services/spoolsv/spoolsv.c
deleted file mode 100644 (file)
index 657f26d..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * COPYRIGHT:        See COPYING in the top level directory
- * PROJECT:          ReactOS kernel
- * FILE:             base/services/spoolsv/spoolsv.c
- * PURPOSE:          Printer spooler
- * PROGRAMMER:       Eric Kohl
- */
-
-/* INCLUDES *****************************************************************/
-
-#include <windef.h>
-#include <winsvc.h>
-#include <wine/debug.h>
-
-WINE_DEFAULT_DEBUG_CHANNEL(spoolsv);
-
-
-/* GLOBALS ******************************************************************/
-
-static VOID CALLBACK ServiceMain(DWORD argc, LPWSTR *argv);
-static WCHAR ServiceName[] = L"Spooler";
-static SERVICE_TABLE_ENTRYW ServiceTable[] =
-{
-    {ServiceName, ServiceMain},
-    {NULL, NULL}
-};
-
-SERVICE_STATUS_HANDLE ServiceStatusHandle;
-SERVICE_STATUS ServiceStatus;
-
-
-/* FUNCTIONS *****************************************************************/
-
-static VOID
-UpdateServiceStatus(DWORD dwState)
-{
-    ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
-    ServiceStatus.dwCurrentState = dwState;
-
-    if (dwState == SERVICE_RUNNING)
-        ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
-    else
-        ServiceStatus.dwControlsAccepted = 0;
-
-    ServiceStatus.dwWin32ExitCode = 0;
-    ServiceStatus.dwServiceSpecificExitCode = 0;
-    ServiceStatus.dwCheckPoint = 0;
-
-    if (dwState == SERVICE_START_PENDING ||
-        dwState == SERVICE_STOP_PENDING)
-        ServiceStatus.dwWaitHint = 10000;
-    else
-        ServiceStatus.dwWaitHint = 0;
-
-    SetServiceStatus(ServiceStatusHandle,
-                     &ServiceStatus);
-}
-
-
-static DWORD WINAPI
-ServiceControlHandler(DWORD dwControl,
-                      DWORD dwEventType,
-                      LPVOID lpEventData,
-                      LPVOID lpContext)
-{
-    TRACE("ServiceControlHandler() called\n");
-
-    switch (dwControl)
-    {
-        case SERVICE_CONTROL_STOP:
-            TRACE("  SERVICE_CONTROL_STOP received\n");
-            UpdateServiceStatus(SERVICE_STOPPED);
-            return ERROR_SUCCESS;
-
-        case SERVICE_CONTROL_INTERROGATE:
-            TRACE("  SERVICE_CONTROL_INTERROGATE received\n");
-            SetServiceStatus(ServiceStatusHandle,
-                             &ServiceStatus);
-            return ERROR_SUCCESS;
-
-        case SERVICE_CONTROL_SHUTDOWN:
-            TRACE("  SERVICE_CONTROL_SHUTDOWN received\n");
-            UpdateServiceStatus(SERVICE_STOPPED);
-            return ERROR_SUCCESS;
-
-        default :
-            TRACE("  Control %lu received\n");
-            return ERROR_CALL_NOT_IMPLEMENTED;
-    }
-}
-
-
-static VOID CALLBACK
-ServiceMain(DWORD argc, LPWSTR *argv)
-{
-    UNREFERENCED_PARAMETER(argc);
-    UNREFERENCED_PARAMETER(argv);
-
-    TRACE("ServiceMain() called\n");
-
-    ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
-                                                        ServiceControlHandler,
-                                                        NULL);
-
-    TRACE("Calling SetServiceStatus()\n");
-    UpdateServiceStatus(SERVICE_RUNNING);
-    TRACE("SetServiceStatus() called\n");
-
-
-    TRACE("ServiceMain() done\n");
-}
-
-
-int
-wmain(int argc, WCHAR *argv[])
-{
-    UNREFERENCED_PARAMETER(argc);
-    UNREFERENCED_PARAMETER(argv);
-
-    TRACE("Spoolsv: main() started\n");
-
-    StartServiceCtrlDispatcher(ServiceTable);
-
-    TRACE("Spoolsv: main() done\n");
-
-    return 0;
-}
-
-/* EOF */
diff --git a/reactos/base/services/spoolsv/spoolsv.rc b/reactos/base/services/spoolsv/spoolsv.rc
deleted file mode 100644 (file)
index 2d35466..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#define REACTOS_STR_FILE_DESCRIPTION  "Spooler-Service"
-#define REACTOS_STR_INTERNAL_NAME     "Spoolsv"
-#define REACTOS_STR_ORIGINAL_FILENAME "Spoolsv.exe"
-#include <reactos/version.rc>
index bd5dd95..3d550a6 100644 (file)
@@ -1252,13 +1252,37 @@ HKLM,"SYSTEM\CurrentControlSet\Control\PnP\Pci\CardList","Toshiba",0x00030003,\
 HKLM,"SYSTEM\CurrentControlSet\Control\PnP\Pci\CardList","Ali",0x00030003,\
 01,00,00,00,b9,10,21,15,00,00,00,00,00,00,00,00
 
+
+; Printing
 HKLM,"SYSTEM\CurrentControlSet\Control\Print","MajorVersion",0x00010001,2
-HKLM,"SYSTEM\CurrentControlSet\Control\Print","MinorVersion",0x00010003,0
-HKLM,"SYSTEM\CurrentControlSet\Control\Print","PriorityClass",0x00010003,0
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments",,0x00000012
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Monitors",,0x00000012
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Monitors\Local Port","Driver",2,"localspl.dll"
+HKLM,"SYSTEM\CurrentControlSet\Control\Print","MinorVersion",0x00010001,0
+HKLM,"SYSTEM\CurrentControlSet\Control\Print","PriorityClass",0x00010001,0
+
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments",,0x00000010
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86","Directory",,"W32X86"
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86\Print Processors",,0x00000010
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86\Print Processors\winprint","Driver",,"winprint.dll"
+
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Monitors",,0x00000010
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Monitors\Local Port","Driver",,"localmon.dll"
+
 HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers",,0x00000012
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Attributes",0x00010001,0
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Datatype",,"RAW"
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Description",,""
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Default DevMode",1,\
+00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
+00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,dc,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
+00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
+00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
+00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Port",,"LPT1:"
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Print Processor",,"winprint"
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Printer Driver",,"Dummy Printer Driver"
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Status",0x00010001,0
+
+HKLM,"SYSTEM\CurrentControlSet\Control\Print\Providers",,0x00000010
+
 
 HKLM,"SYSTEM\CurrentControlSet\Control\ProductOptions","ProductType",2,"ServerNT"
 HKLM,"SYSTEM\CurrentControlSet\Control\ProductOptions","ProductSuite",0x00010002,"Terminal Server"
index 307ad89..54621f9 100644 (file)
@@ -72,8 +72,6 @@ add_subdirectory(jsproxy)
 add_subdirectory(kernel32)
 add_subdirectory(kernel32_vista)
 add_subdirectory(loadperf)
-add_subdirectory(localspl)
-add_subdirectory(localui)
 add_subdirectory(lpk)
 add_subdirectory(lsasrv)
 add_subdirectory(lz32)
@@ -139,7 +137,6 @@ add_subdirectory(npptools)
 add_subdirectory(ntdsapi)
 add_subdirectory(ntlanman)
 add_subdirectory(ntmarta)
-add_subdirectory(ntprint)
 add_subdirectory(objsel)
 add_subdirectory(odbc32)
 add_subdirectory(odbccp32)
@@ -194,7 +191,6 @@ add_subdirectory(smdll)
 add_subdirectory(sndblst)
 add_subdirectory(snmpapi)
 add_subdirectory(softpub)
-add_subdirectory(spoolss)
 add_subdirectory(srclient)
 add_subdirectory(stdole2.tlb)
 add_subdirectory(stdole32.tlb)
@@ -238,7 +234,6 @@ add_subdirectory(winhttp)
 add_subdirectory(wininet)
 add_subdirectory(winmm)
 add_subdirectory(winscard)
-add_subdirectory(winspool)
 add_subdirectory(winsta)
 add_subdirectory(wintrust)
 add_subdirectory(wlanapi)
diff --git a/reactos/dll/win32/localspl/CMakeLists.txt b/reactos/dll/win32/localspl/CMakeLists.txt
deleted file mode 100644 (file)
index 29830d9..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-
-remove_definitions(-D_WIN32_WINNT=0x502)
-add_definitions(-D_WIN32_WINNT=0x600)
-
-add_definitions(-D__WINESRC__)
-include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine)
-spec2def(localspl.dll localspl.spec)
-
-list(APPEND SOURCE
-    localmon.c
-    localspl_main.c
-    provider.c
-    localspl_private.h)
-
-add_library(localspl SHARED
-    ${SOURCE}
-    localspl.rc
-    ${CMAKE_CURRENT_BINARY_DIR}/localspl_stubs.c
-    ${CMAKE_CURRENT_BINARY_DIR}/localspl.def)
-
-set_module_type(localspl win32dll)
-target_link_libraries(localspl wine)
-add_importlibs(localspl spoolss user32 advapi32 advapi32_vista msvcrt kernel32 ntdll)
-add_pch(localspl localspl_private.h SOURCE)
-add_cd_file(TARGET localspl DESTINATION reactos/system32 FOR all)
diff --git a/reactos/dll/win32/localspl/lang/spl_Cy.rc b/reactos/dll/win32/localspl/lang/spl_Cy.rc
deleted file mode 100644 (file)
index ecae600..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Cefnogaeth iaith Cymraeg
- * Welsh language support
- *
- * Copyright 2010 Ken Sharp
- *
- * 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
- */
-
-LANGUAGE LANG_WELSH, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Port Lleol"
-    IDS_LOCALMONITOR "Monitor Lleol"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Da.rc b/reactos/dll/win32/localspl/lang/spl_Da.rc
deleted file mode 100644 (file)
index 2872fa4..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Danish resources for localspl
- *
- * Copyright 2008 Jens Albretsen <jens@albretsen.dk>
- *
- * 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
- */
-
-LANGUAGE LANG_DANISH, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Lokal port"
-    IDS_LOCALMONITOR "Lokal overvåger"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_De.rc b/reactos/dll/win32/localspl/lang/spl_De.rc
deleted file mode 100644 (file)
index 29191bd..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * German resources for localspl
- *
- * Copyright 2005 Henning Gerhardt
- * Copyright 2006 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
- */
-
-#pragma code_page(65001)
-
-LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Lokaler Anschluss"
-    IDS_LOCALMONITOR "Lokale Anzeige"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_En.rc b/reactos/dll/win32/localspl/lang/spl_En.rc
deleted file mode 100644 (file)
index 4052cb7..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * English resources for localspl
- *
- * Copyright 2005 Huw Davies
- * Copyright 2006 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
- */
-
-LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Local Port"
-    IDS_LOCALMONITOR "Local Monitor"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Es.rc b/reactos/dll/win32/localspl/lang/spl_Es.rc
deleted file mode 100644 (file)
index 3ae4b22..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Spanish resources for localspl
- *
- * Copyright 2010 José Manuel Ferrer Ortiz
- *
- * 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
- */
-
-/* UTF-8 */
-#pragma code_page(65001)
-
-LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Puerto local"
-    IDS_LOCALMONITOR "Monitor local"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Fr.rc b/reactos/dll/win32/localspl/lang/spl_Fr.rc
deleted file mode 100644 (file)
index 5aee221..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * French resources for localspl
- *
- * Copyright 2007 Jonathan Ernst
- *
- * 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
- */
-
-LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Port local"
-    IDS_LOCALMONITOR "Moniteur local"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_He.rc b/reactos/dll/win32/localspl/lang/spl_He.rc
deleted file mode 100644 (file)
index a93e293..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Hebrew resources for localspl
- *
- * Copyright 2005 Huw Davies
- * Copyright 2006 Detlef Riekenberg
- *
- * Translated by Baruch Rutman
- *
- * 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
- */
-
-LANGUAGE LANG_HEBREW, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "יציאה מקומית"
-    IDS_LOCALMONITOR "ניטור מקומי"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Hu.rc b/reactos/dll/win32/localspl/lang/spl_Hu.rc
deleted file mode 100644 (file)
index 5bb4422..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Hungarian resources for localspl
- *
- * Copyright 2010 Andras Kovacs
- *
- * 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
- */
-
-/* UTF-8 */
-#pragma code_page(65001)
-
-LANGUAGE LANG_HUNGARIAN, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Helyi Port"
-    IDS_LOCALMONITOR "Helyi figyelő"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_It.rc b/reactos/dll/win32/localspl/lang/spl_It.rc
deleted file mode 100644 (file)
index 80fad14..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Italian resources for localspl
- *
- * Copyright 2005 Huw Davies
- * Copyright 2006 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
- */
-
-/* UTF-8 */
-#pragma code_page(65001)
-
-LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Porta locale"
-    IDS_LOCALMONITOR "Schermo locale"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Ja.rc b/reactos/dll/win32/localspl/lang/spl_Ja.rc
deleted file mode 100644 (file)
index 2e4ecf7..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Japanese resources for localspl
- *
- * Copyright 2005 Huw Davies
- * Copyright 2006 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
- */
-
-/* UTF-8 */
-#pragma code_page(65001)
-
-LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "ローカル ポート"
-    IDS_LOCALMONITOR "ローカル モニタ"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Ko.rc b/reactos/dll/win32/localspl/lang/spl_Ko.rc
deleted file mode 100644 (file)
index f4c7e54..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Korean  resources for localspl
- *
- * Copyright 2005 Huw Davies
- * Copyright 2006 Detlef Riekenberg
- * Copyright 2006 YunSong Hwang
- *
- * 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
- */
-
-LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "지역 포트"
-    IDS_LOCALMONITOR "지역 모니터"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Lt.rc b/reactos/dll/win32/localspl/lang/spl_Lt.rc
deleted file mode 100644 (file)
index 2b242cd..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Lithuanian resources for localspl
- *
- * Copyright 2009 Aurimas Fišeras <aurimas@gmail.com>
- *
- * 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
- */
-
-/* UTF-8 */
-#pragma code_page(65001)
-
-LANGUAGE LANG_LITHUANIAN, SUBLANG_NEUTRAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Vietinis prievadas"
-    IDS_LOCALMONITOR "Vietinis monitorius"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Nl.rc b/reactos/dll/win32/localspl/lang/spl_Nl.rc
deleted file mode 100644 (file)
index ae46424..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Dutch resources for localspl
- *
- * Copyright 2008 Frans Kool
- *
- * 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
- */
-
-LANGUAGE LANG_DUTCH, SUBLANG_NEUTRAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Lokale Poort"
-    IDS_LOCALMONITOR "Lokale Monitor"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_No.rc b/reactos/dll/win32/localspl/lang/spl_No.rc
deleted file mode 100644 (file)
index 44ea1d9..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Norwegian Bokmål resources for localspl
- *
- * Copyright 2006 Alexander N. Sørnes <alex@thehandofagony.com>
- *
- * 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
- */
-
-LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Lokal port"
-    IDS_LOCALMONITOR "Lokal overvåker"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Pl.rc b/reactos/dll/win32/localspl/lang/spl_Pl.rc
deleted file mode 100644 (file)
index bd7908c..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Polish resources for localspl
- *
- * Copyright 2005 Huw Davies
- * Copyright 2006 Detlef Riekenberg
- * Copyright 2007 Mikolaj Zalewski
- *
- * 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
- */
-
-LANGUAGE LANG_POLISH, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Port lokalny"
-    IDS_LOCALMONITOR "Monitor lokalny"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Pt.rc b/reactos/dll/win32/localspl/lang/spl_Pt.rc
deleted file mode 100644 (file)
index 2675843..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Portuguese resources for localspl
- *
- * Copyright 2008 Ricardo Filipe
- *
- * 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
- */
-
-LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Porta local"
-    IDS_LOCALMONITOR "Monitor Local"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Ro.rc b/reactos/dll/win32/localspl/lang/spl_Ro.rc
deleted file mode 100644 (file)
index bfd44f0..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2005 Huw Davies
- * Copyright 2006 Detlef Riekenberg
- * Copyright 2008 Michael Stefaniuc
- *           2011 Fulea Ștefan
- *
- * 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
- */
-
-#pragma code_page(65001)
-
-LANGUAGE LANG_ROMANIAN, SUBLANG_NEUTRAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Port local"
-    IDS_LOCALMONITOR "Ecran local"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Ru.rc b/reactos/dll/win32/localspl/lang/spl_Ru.rc
deleted file mode 100644 (file)
index 50b3d7b..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Russian resources for localspl
- *
- * Copyright 2008 Vitaliy Margolen
- *
- * 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
- */
-
-/* UTF-8 */
-#pragma code_page(65001)
-
-LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Локальный порт"
-    IDS_LOCALMONITOR "Локальный монитор"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Si.rc b/reactos/dll/win32/localspl/lang/spl_Si.rc
deleted file mode 100644 (file)
index 32d7aff..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Slovenian resources for localspl
- *
- * Copyright 2008 Rok Mandeljc
- *
- * 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
- */
-
-#pragma code_page(65001)
-
-LANGUAGE LANG_SLOVENIAN, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Lokalna vrata"
-    IDS_LOCALMONITOR "Lokalen monitor"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Sq.rc b/reactos/dll/win32/localspl/lang/spl_Sq.rc
deleted file mode 100644 (file)
index e917866..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * English resources for localspl
- *
- * Copyright 2005 Huw Davies
- * Copyright 2006 Detlef Riekenberg
- * Translation: Ardit Dani (ALbanian Translation Resource File)
- *
- * 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
- */
-
-LANGUAGE LANG_ALBANIAN, SUBLANG_NEUTRAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Porti Vendor"
-    IDS_LOCALMONITOR "Monitorim Vendor"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Sv.rc b/reactos/dll/win32/localspl/lang/spl_Sv.rc
deleted file mode 100644 (file)
index afa6434..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Swedish resources for localspl
- *
- * Copyright 2007 Daniel Nylander
- *
- * 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
- */
-
-LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Lokal port"
-    IDS_LOCALMONITOR "Lokal skärm"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Tr.rc b/reactos/dll/win32/localspl/lang/spl_Tr.rc
deleted file mode 100644 (file)
index 2a9f96f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Turkish resources for localspl
- *
- * Copyright: 2014 Erdem Ersoy (eersoy93) (erdemersoy@live.com)
- *
- * 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
- */
-
-LANGUAGE LANG_TURKISH, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Yerli Giriş"
-    IDS_LOCALMONITOR "Yerli İzleyici"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Uk.rc b/reactos/dll/win32/localspl/lang/spl_Uk.rc
deleted file mode 100644 (file)
index 4b0ea3f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Ukrainian resources for localspl
- *
- * Copyright 2010 Igor Paliychuk
- *
- * 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
- */
-
-/* UTF-8 */
-#pragma code_page(65001)
-
-LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "Локальний порт"
-    IDS_LOCALMONITOR "Локальний монітор"
-}
diff --git a/reactos/dll/win32/localspl/lang/spl_Zh.rc b/reactos/dll/win32/localspl/lang/spl_Zh.rc
deleted file mode 100644 (file)
index 2e2f54f..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * localspl (Simplified and Traditional Chinese Resources)
- *
- * Copyright 2008 Hongbo Ni <hongbo.at.njstar.com>
- *
- * 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
- */
-
-/* Chinese text is encoded in UTF-8 */
-#pragma code_page(65001)
-
-LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "本地端口"
-    IDS_LOCALMONITOR "本地监视器"
-}
-
-LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL
-
-STRINGTABLE
-{
-    IDS_LOCALPORT "本地端口"
-    IDS_LOCALMONITOR "本地監視器"
-}
diff --git a/reactos/dll/win32/localspl/localmon.c b/reactos/dll/win32/localspl/localmon.c
deleted file mode 100644 (file)
index e78b612..0000000
+++ /dev/null
@@ -1,793 +0,0 @@
-/*
- * Implementation of the Local Printmonitor
- *
- * Copyright 2006 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 "localspl_private.h"
-
-#include "resource.h"
-
-/*****************************************************/
-
-static CRITICAL_SECTION port_handles_cs;
-static CRITICAL_SECTION_DEBUG port_handles_cs_debug =
-{
-    0, 0, &port_handles_cs,
-    { &port_handles_cs_debug.ProcessLocksList, &port_handles_cs_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": port_handles_cs") }
-};
-static CRITICAL_SECTION port_handles_cs = { &port_handles_cs_debug, -1, 0, 0, 0, 0 };
-
-
-static CRITICAL_SECTION xcv_handles_cs;
-static CRITICAL_SECTION_DEBUG xcv_handles_cs_debug =
-{
-    0, 0, &xcv_handles_cs,
-    { &xcv_handles_cs_debug.ProcessLocksList, &xcv_handles_cs_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": xcv_handles_cs") }
-};
-static CRITICAL_SECTION xcv_handles_cs = { &xcv_handles_cs_debug, -1, 0, 0, 0, 0 };
-
-/* ############################### */
-
-typedef struct {
-    struct list entry;
-    DWORD   type;
-    WCHAR   nameW[1];
-} port_t;
-
-typedef struct {
-    struct list entry;
-    ACCESS_MASK GrantedAccess;
-    WCHAR       nameW[1];
-} xcv_t;
-
-static struct list port_handles = LIST_INIT( port_handles );
-static struct list xcv_handles = LIST_INIT( xcv_handles );
-
-/* ############################### */
-
-static const WCHAR cmd_AddPortW[] = {'A','d','d','P','o','r','t',0};
-static const WCHAR cmd_DeletePortW[] = {'D','e','l','e','t','e','P','o','r','t',0};
-static const WCHAR cmd_ConfigureLPTPortCommandOKW[] = {'C','o','n','f','i','g','u','r','e',
-                                    'L','P','T','P','o','r','t',
-                                    'C','o','m','m','a','n','d','O','K',0};
-
-static const WCHAR cmd_GetDefaultCommConfigW[] = {'G','e','t',
-                                    'D','e','f','a','u','l','t',
-                                    'C','o','m','m','C','o','n','f','i','g',0};
-
-static const WCHAR cmd_GetTransmissionRetryTimeoutW[] = {'G','e','t',
-                                    'T','r','a','n','s','m','i','s','s','i','o','n',
-                                    'R','e','t','r','y','T','i','m','e','o','u','t',0};
-
-static const WCHAR cmd_MonitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
-static const WCHAR cmd_PortIsValidW[] = {'P','o','r','t','I','s','V','a','l','i','d',0};
-static const WCHAR cmd_SetDefaultCommConfigW[] = {'S','e','t',
-                                    'D','e','f','a','u','l','t',
-                                    'C','o','m','m','C','o','n','f','i','g',0};
-
-static const WCHAR dllnameuiW[] = {'l','o','c','a','l','u','i','.','d','l','l',0};
-static const WCHAR emptyW[] = {0};
-static const WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
-
-static const WCHAR portname_LPT[]  = {'L','P','T',0};
-static const WCHAR portname_COM[]  = {'C','O','M',0};
-static const WCHAR portname_FILE[] = {'F','I','L','E',':',0};
-static const WCHAR portname_CUPS[] = {'C','U','P','S',':',0};
-static const WCHAR portname_LPR[]  = {'L','P','R',':',0};
-
-static const WCHAR TransmissionRetryTimeoutW[] = {'T','r','a','n','s','m','i','s','s','i','o','n',
-                                    'R','e','t','r','y','T','i','m','e','o','u','t',0};
-
-static const WCHAR WinNT_CV_PortsW[] = {'S','o','f','t','w','a','r','e','\\',
-                                        'M','i','c','r','o','s','o','f','t','\\',
-                                        'W','i','n','d','o','w','s',' ','N','T','\\',
-                                        'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
-                                        'P','o','r','t','s',0};
-
-static const WCHAR WinNT_CV_WindowsW[] = {'S','o','f','t','w','a','r','e','\\',
-                                        'M','i','c','r','o','s','o','f','t','\\',
-                                        'W','i','n','d','o','w','s',' ','N','T','\\',
-                                        'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
-                                        'W','i','n','d','o','w','s',0};
-
-
-/******************************************************************
- * does_port_exist (internal)
- *
- * returns TRUE, when the Port already exists
- *
- */
-static BOOL does_port_exist(LPCWSTR myname)
-{
-
-    LPPORT_INFO_1W  pi;
-    DWORD   needed = 0;
-    DWORD   returned;
-    DWORD   id;
-
-    TRACE("(%s)\n", debugstr_w(myname));
-
-    id = EnumPortsW(NULL, 1, NULL, 0, &needed, &returned);
-    pi = heap_alloc(needed);
-    returned = 0;
-    if (pi)
-        id = EnumPortsW(NULL, 1, (LPBYTE) pi, needed, &needed, &returned);
-
-    if (id && returned > 0) {
-        /* we got a number of valid names. */
-        for (id = 0; id < returned; id++)
-        {
-            if (lstrcmpiW(myname, pi[id].pName) == 0) {
-                TRACE("(%u) found %s\n", id, debugstr_w(pi[id].pName));
-                heap_free(pi);
-                return TRUE;
-            }
-        }
-    }
-
-    heap_free(pi);
-    return FALSE;
-}
-
-/******************************************************************
- * enumerate the local Ports from the Registry (internal)  
- *
- * See localmon_EnumPortsW.
- *
- * NOTES
- *  returns the needed size (in bytes) for pPorts
- *  and *lpreturned is set to number of entries returned in pPorts
- *
- */
-
-static DWORD get_ports_from_reg(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
-{
-    HKEY    hroot = 0;
-    LPWSTR  ptr;
-    LPPORT_INFO_2W out;
-    WCHAR   portname[MAX_PATH];
-    WCHAR   res_PortW[IDS_LOCALPORT_MAXLEN];
-    WCHAR   res_MonitorW[IDS_LOCALMONITOR_MAXLEN];
-    INT     reslen_PortW;
-    INT     reslen_MonitorW;
-    DWORD   len;
-    DWORD   res;
-    DWORD   needed = 0;
-    DWORD   numentries;
-    DWORD   entrysize;
-    DWORD   id = 0;
-
-    TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
-
-    entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
-
-    numentries = *lpreturned;           /* this is 0, when we scan the registry */
-    needed = entrysize * numentries;
-    ptr = (LPWSTR) &pPorts[needed];
-
-    if (needed > cbBuf) pPorts = NULL;  /* No buffer for the structs */
-
-    numentries = 0;
-    needed = 0;
-
-    /* we do not check more parameters as done in windows */
-    if ((level < 1) || (level > 2)) {
-        goto getports_cleanup;
-    }
-
-    /* "+1" for '\0' */
-    reslen_MonitorW = LoadStringW(LOCALSPL_hInstance, IDS_LOCALMONITOR, res_MonitorW, IDS_LOCALMONITOR_MAXLEN) + 1;  
-    reslen_PortW = LoadStringW(LOCALSPL_hInstance, IDS_LOCALPORT, res_PortW, IDS_LOCALPORT_MAXLEN) + 1;  
-
-    res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
-    if (res == ERROR_SUCCESS) {
-
-        /* Scan all Port-Names */
-        while (res == ERROR_SUCCESS) {
-            len = MAX_PATH;
-            portname[0] = '\0';
-            res = RegEnumValueW(hroot, id, portname, &len, NULL, NULL, NULL, NULL);
-
-            if ((res == ERROR_SUCCESS) && (portname[0])) {
-                numentries++;
-                /* calculate the required size */
-                needed += entrysize;
-                needed += (len + 1) * sizeof(WCHAR);
-                if (level > 1) {
-                    needed += (reslen_MonitorW + reslen_PortW) * sizeof(WCHAR);
-                }
-
-                /* Now fill the user-buffer, if available */
-                if (pPorts && (cbBuf >= needed)){
-                    out = (LPPORT_INFO_2W) pPorts;
-                    pPorts += entrysize;
-                    TRACE("%p: writing PORT_INFO_%dW #%d (%s)\n", out, level, numentries, debugstr_w(portname));
-                    out->pPortName = ptr;
-                    lstrcpyW(ptr, portname);            /* Name of the Port */
-                    ptr += (len + 1);
-                    if (level > 1) {
-                        out->pMonitorName = ptr;
-                        lstrcpyW(ptr, res_MonitorW);    /* Name of the Monitor */
-                        ptr += reslen_MonitorW;
-
-                        out->pDescription = ptr;
-                        lstrcpyW(ptr, res_PortW);       /* Port Description */
-                        ptr += reslen_PortW;
-
-                        out->fPortType = PORT_TYPE_WRITE;
-                        out->Reserved = 0;
-                    }
-                }
-                id++;
-            }
-        }
-        RegCloseKey(hroot);
-    }
-    else
-    {
-        ERR("failed with %d for %s\n", res, debugstr_w(WinNT_CV_PortsW));
-        SetLastError(res);
-    }
-
-getports_cleanup:
-    *lpreturned = numentries;
-    TRACE("need %d byte for %d entries (%d)\n", needed, numentries, GetLastError());
-    return needed;
-}
-
-/*****************************************************
- * get_type_from_name (internal)
- * 
- */
-
-static DWORD get_type_from_name(LPCWSTR name)
-{
-    HANDLE  hfile;
-
-    if (!strncmpW(name, portname_LPT, sizeof(portname_LPT) / sizeof(WCHAR) -1))
-        return PORT_IS_LPT;
-
-    if (!strncmpW(name, portname_COM, sizeof(portname_COM) / sizeof(WCHAR) -1))
-        return PORT_IS_COM;
-
-    if (!strcmpW(name, portname_FILE))
-        return PORT_IS_FILE;
-
-    if (name[0] == '/')
-        return PORT_IS_UNIXNAME;
-
-    if (name[0] == '|')
-        return PORT_IS_PIPE;
-
-    if (!strncmpW(name, portname_CUPS, sizeof(portname_CUPS) / sizeof(WCHAR) -1))
-        return PORT_IS_CUPS;
-
-    if (!strncmpW(name, portname_LPR, sizeof(portname_LPR) / sizeof(WCHAR) -1))
-        return PORT_IS_LPR;
-
-    /* Must be a file or a directory. Does the file exist ? */
-    hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-    TRACE("%p for OPEN_EXISTING on %s\n", hfile, debugstr_w(name));
-    if (hfile == INVALID_HANDLE_VALUE) {
-        /* Can we create the file? */
-        hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
-        TRACE("%p for OPEN_ALWAYS\n", hfile);
-    }
-    if (hfile != INVALID_HANDLE_VALUE) {
-        CloseHandle(hfile);
-        return PORT_IS_FILENAME;
-    }
-    /* We can't use the name. use GetLastError() for the reason */
-    return PORT_IS_UNKNOWN;
-}
-
-/*****************************************************
- * get_type_from_local_name (internal)
- *
- */
-
-static DWORD get_type_from_local_name(LPCWSTR nameW)
-{
-    LPPORT_INFO_1W  pi;
-    LPWSTR  myname = NULL;
-    DWORD   needed = 0;
-    DWORD   numentries = 0;
-    DWORD   id = 0;
-
-    TRACE("(%s)\n", debugstr_w(myname));
-
-    needed = get_ports_from_reg(1, NULL, 0, &numentries);
-    pi = heap_alloc(needed);
-    if (pi)
-        needed = get_ports_from_reg(1, (LPBYTE) pi, needed, &numentries);
-
-    if (pi && needed && numentries > 0) {
-        /* we got a number of valid ports. */
-
-        while ((myname == NULL) && (id < numentries))
-        {
-            if (lstrcmpiW(nameW, pi[id].pName) == 0) {
-                TRACE("(%u) found %s\n", id, debugstr_w(pi[id].pName));
-                myname = pi[id].pName;
-            }
-            id++;
-        }
-    }
-
-    id = (myname) ? get_type_from_name(myname) : PORT_IS_UNKNOWN;
-
-    heap_free(pi);
-    return id;
-
-}
-/******************************************************************************
- *   localmon_AddPortExW [exported through MONITOREX]
- *
- * Add a Port, without presenting a user interface
- *
- * PARAMS
- *  pName         [I] Servername or NULL (local Computer)
- *  level         [I] Structure-Level (1) for pBuffer
- *  pBuffer       [I] PTR to the Input-Data (PORT_INFO_1)
- *  pMonitorName  [I] Name of the Monitor that manage the Port
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- * NOTES
- *  Level 2 is documented on MSDN for Portmonitors, but not supported by the
- *  "Local Port" Portmonitor (localspl.dll / localmon.dll)
- */
-static BOOL WINAPI localmon_AddPortExW(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
-{
-    PORT_INFO_1W * pi;
-    HKEY  hroot;
-    DWORD res;
-
-    pi = (PORT_INFO_1W *) pBuffer;
-    TRACE("(%s, %d, %p, %s) => %s\n", debugstr_w(pName), level, pBuffer,
-            debugstr_w(pMonitorName), debugstr_w(pi ? pi->pName : NULL));
-
-
-    if ((pMonitorName == NULL) || (lstrcmpiW(pMonitorName, LocalPortW) != 0 ) ||
-        (pi == NULL) || (pi->pName == NULL) || (pi->pName[0] == '\0') ) {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-
-    if (level != 1) {
-        SetLastError(ERROR_INVALID_LEVEL);
-        return FALSE;
-    }
-
-    res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
-    if (res == ERROR_SUCCESS) {
-        if (does_port_exist(pi->pName)) {
-            RegCloseKey(hroot);
-            TRACE("=> FALSE with %u\n", ERROR_INVALID_PARAMETER);
-            SetLastError(ERROR_INVALID_PARAMETER);
-            return FALSE;
-        }
-        res = RegSetValueExW(hroot, pi->pName, 0, REG_SZ, (const BYTE *) emptyW, sizeof(emptyW));
-        RegCloseKey(hroot);
-    }
-    if (res != ERROR_SUCCESS) SetLastError(ERROR_INVALID_PARAMETER);
-    TRACE("=> %u with %u\n", (res == ERROR_SUCCESS), GetLastError());
-    return (res == ERROR_SUCCESS);
-}
-
-/*****************************************************
- * localmon_ClosePort [exported through MONITOREX]
- *
- * Close a
- *
- * PARAMS
- *  hPort  [i] The Handle to close
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- */
-static BOOL WINAPI localmon_ClosePort(HANDLE hPort)
-{
-    port_t * port = hPort;
-
-    TRACE("(%p)\n", port);
-    EnterCriticalSection(&port_handles_cs);
-    list_remove(&port->entry);
-    LeaveCriticalSection(&port_handles_cs);
-    heap_free(port);
-    return TRUE;
-}
-
-/*****************************************************
- *   localmon_EnumPortsW [exported through MONITOREX]
- *
- * Enumerate all local Ports
- *
- * PARAMS
- *  pName       [I] Servername (ignored)
- *  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
- *
- * NOTES
- *|  Windows ignores pName
- *|  Windows crash the app, when pPorts, pcbNeeded or pcReturned are NULL
- *|  Windows >NT4.0 does not check for illegal levels (TRUE is returned)
- *
- * ToDo
- *   "HCU\Software\Wine\Spooler\<portname>" - redirection
- *
- */
-static BOOL WINAPI localmon_EnumPortsW(LPWSTR pName, DWORD level, LPBYTE pPorts,
-                                       DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
-{
-    BOOL    res = FALSE;
-    DWORD   needed;
-    DWORD   numentries;
-
-    TRACE("(%s, %d, %p, %d, %p, %p)\n",
-          debugstr_w(pName), level, pPorts, cbBuf, pcbNeeded, pcReturned);
-
-    numentries = 0;
-    needed = get_ports_from_reg(level, NULL, 0, &numentries);
-    /* we calculated the needed buffersize. now do the error-checks */
-    if (cbBuf < needed) {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        goto cleanup;
-    }
-
-    /* fill the buffer with the Port-Names */
-    needed = get_ports_from_reg(level, pPorts, cbBuf, &numentries);
-    res = TRUE;
-
-    if (pcReturned) *pcReturned = numentries;
-
-cleanup:
-    if (pcbNeeded)  *pcbNeeded = needed;
-
-    TRACE("returning %d with %d (%d byte for %d entries)\n", 
-            res, GetLastError(), needed, numentries);
-
-    return (res);
-}
-
-/*****************************************************
- * localmon_OpenPort [exported through MONITOREX]
- *
- * Open a Data-Channel for a Port
- *
- * PARAMS
- *  pName     [i] Name of selected Object
- *  phPort    [o] The resulting Handle is stored here
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- */
-static BOOL WINAPI localmon_OpenPortW(LPWSTR pName, PHANDLE phPort)
-{
-    port_t * port;
-    DWORD   type;
-
-    TRACE("%s, %p)\n", debugstr_w(pName), phPort);
-
-    /* an empty name is invalid */
-    if (!pName[0]) return FALSE;
-
-    /* does the port exist? */
-    type = get_type_from_local_name(pName);
-    if (!type) return FALSE;
-
-    port = heap_alloc(FIELD_OFFSET(port_t, nameW[lstrlenW(pName) + 1]));
-    if (!port) return FALSE;
-
-    port->type = type;
-    lstrcpyW(port->nameW, pName);
-    *phPort = port;
-
-    EnterCriticalSection(&port_handles_cs);
-    list_add_tail(&port_handles, &port->entry);
-    LeaveCriticalSection(&port_handles_cs);
-
-    TRACE("=> %p\n", port);
-    return TRUE;
-}
-
-/*****************************************************
- * localmon_XcvClosePort [exported through MONITOREX]
- *
- * Close a Communication-Channel
- *
- * PARAMS
- *  hXcv  [i] The Handle to close
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- */
-static BOOL WINAPI localmon_XcvClosePort(HANDLE hXcv)
-{
-    xcv_t * xcv = hXcv;
-
-    TRACE("(%p)\n", xcv);
-    /* No checks are done in Windows */
-    EnterCriticalSection(&xcv_handles_cs);
-    list_remove(&xcv->entry);
-    LeaveCriticalSection(&xcv_handles_cs);
-    heap_free(xcv);
-    return TRUE;
-}
-
-/*****************************************************
- * localmon_XcvDataPort [exported through MONITOREX]
- *
- * Execute command through a Communication-Channel
- *
- * PARAMS
- *  hXcv            [i] The Handle to work with
- *  pszDataName     [i] Name of the command to execute
- *  pInputData      [i] Buffer for extra Input Data (needed only for some commands)
- *  cbInputData     [i] Size in Bytes of Buffer at pInputData
- *  pOutputData     [o] Buffer to receive additional Data (needed only for some commands)
- *  cbOutputData    [i] Size in Bytes of Buffer at pOutputData
- *  pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
- *
- * RETURNS
- *  Success: ERROR_SUCCESS
- *  Failure: win32 error code
- *
- * NOTES
- *
- *  Minimal List of commands, that every Printmonitor DLL should support:
- *
- *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
- *| "AddPort"   : Add a Port (Name as WSTR in pInputData)
- *| "DeletePort": Delete a Port (Name as WSTR in pInputData)
- *
- *
- */
-static DWORD WINAPI localmon_XcvDataPort(HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData,
-                                         PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
-{
-    WCHAR   buffer[16];     /* buffer for a decimal number */
-    LPWSTR  ptr;
-    DWORD   res;
-    DWORD   needed;
-    HKEY    hroot;
-
-    TRACE("(%p, %s, %p, %d, %p, %d, %p)\n", hXcv, debugstr_w(pszDataName),
-          pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
-
-    if (!lstrcmpW(pszDataName, cmd_AddPortW)) {
-        TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
-        res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
-        if (res == ERROR_SUCCESS) {
-            if (does_port_exist((LPWSTR) pInputData)) {
-                RegCloseKey(hroot);
-                TRACE("=> %u\n", ERROR_ALREADY_EXISTS);
-                return ERROR_ALREADY_EXISTS;
-            }
-            res = RegSetValueExW(hroot, (LPWSTR) pInputData, 0, REG_SZ, (const BYTE *) emptyW, sizeof(emptyW));
-            RegCloseKey(hroot);
-        }
-        TRACE("=> %u\n", res);
-        return res;
-    }
-
-
-    if (!lstrcmpW(pszDataName, cmd_ConfigureLPTPortCommandOKW)) {
-        TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
-        res = RegCreateKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsW, &hroot);
-        if (res == ERROR_SUCCESS) {
-            res = RegSetValueExW(hroot, TransmissionRetryTimeoutW, 0, REG_SZ, pInputData, cbInputData);
-            RegCloseKey(hroot);
-        }
-        return res;
-    }
-
-    if (!lstrcmpW(pszDataName, cmd_DeletePortW)) {
-        TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
-        res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
-        if (res == ERROR_SUCCESS) {
-            res = RegDeleteValueW(hroot, (LPWSTR) pInputData);
-            RegCloseKey(hroot);
-            TRACE("=> %u with %u\n", res, GetLastError() );
-            return res;
-        }
-        return ERROR_FILE_NOT_FOUND;
-    }
-
-    if (!lstrcmpW(pszDataName, cmd_GetDefaultCommConfigW)) {
-        TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
-        *pcbOutputNeeded = cbOutputData;
-        res = GetDefaultCommConfigW((LPWSTR) pInputData, (LPCOMMCONFIG) pOutputData, pcbOutputNeeded);
-        TRACE("got %u with %u\n", res, GetLastError() );
-        return res ? ERROR_SUCCESS : GetLastError();
-    }
-
-    if (!lstrcmpW(pszDataName, cmd_GetTransmissionRetryTimeoutW)) {
-        * pcbOutputNeeded = sizeof(DWORD);
-        if (cbOutputData >= sizeof(DWORD)) {
-            /* the w2k resource kit documented a default of 90, but that's wrong */
-            *((LPDWORD) pOutputData) = 45;
-
-            res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsW, &hroot);
-            if (res == ERROR_SUCCESS) {
-                needed = sizeof(buffer) - sizeof(WCHAR);
-                res = RegQueryValueExW(hroot, TransmissionRetryTimeoutW, NULL, NULL, (LPBYTE) buffer, &needed);
-                if ((res == ERROR_SUCCESS) && (buffer[0])) {
-                    *((LPDWORD) pOutputData) = strtoulW(buffer, NULL, 0);
-                }
-                RegCloseKey(hroot);
-            }
-            return ERROR_SUCCESS;
-        }
-        return ERROR_INSUFFICIENT_BUFFER;
-    }
-
-
-    if (!lstrcmpW(pszDataName, cmd_MonitorUIW)) {
-        * pcbOutputNeeded = sizeof(dllnameuiW);
-        if (cbOutputData >= sizeof(dllnameuiW)) {
-            memcpy(pOutputData, dllnameuiW, sizeof(dllnameuiW));
-            return ERROR_SUCCESS;
-        }
-        return ERROR_INSUFFICIENT_BUFFER;
-    }
-
-    if (!lstrcmpW(pszDataName, cmd_PortIsValidW)) {
-        TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
-        res = get_type_from_name((LPCWSTR) pInputData);
-        TRACE("detected as %u\n",  res);
-        /* names, that we have recognized, are valid */
-        if (res) return ERROR_SUCCESS;
-
-        /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND or something else */
-        TRACE("=> %u\n", GetLastError());
-        return GetLastError();
-    }
-
-    if (!lstrcmpW(pszDataName, cmd_SetDefaultCommConfigW)) {
-        /* get the portname from the Handle */
-        ptr =  strchrW(((xcv_t *)hXcv)->nameW, ' ');
-        if (ptr) {
-            ptr++;  /* skip the space */
-        }
-        else
-        {
-            ptr =  ((xcv_t *)hXcv)->nameW;
-        }
-        lstrcpynW(buffer, ptr, sizeof(buffer)/sizeof(WCHAR));
-        if (buffer[0]) buffer[lstrlenW(buffer)-1] = '\0';  /* remove the ':' */
-        res = SetDefaultCommConfigW(buffer, (LPCOMMCONFIG) pInputData, cbInputData);
-        TRACE("got %u with %u\n", res, GetLastError() );
-        return res ? ERROR_SUCCESS : GetLastError();
-    }
-
-    FIXME("command not supported: %s\n", debugstr_w(pszDataName));
-    return ERROR_INVALID_PARAMETER;
-}
-
-/*****************************************************
- * localmon_XcvOpenPort [exported through MONITOREX]
- *
- * Open a Communication-Channel
- *
- * PARAMS
- *  pName         [i] Name of selected Object
- *  GrantedAccess [i] Access-Rights to use
- *  phXcv         [o] The resulting Handle is stored here
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- */
-static BOOL WINAPI localmon_XcvOpenPort(LPCWSTR pName, ACCESS_MASK GrantedAccess, PHANDLE phXcv)
-{
-    xcv_t * xcv;
-
-    TRACE("%s, 0x%x, %p)\n", debugstr_w(pName), GrantedAccess, phXcv);
-    /* No checks for any field is done in Windows */
-    xcv = heap_alloc(FIELD_OFFSET(xcv_t, nameW[lstrlenW(pName) + 1]));
-    if (xcv) {
-        xcv->GrantedAccess = GrantedAccess;
-        lstrcpyW(xcv->nameW, pName);
-        *phXcv = xcv;
-        EnterCriticalSection(&xcv_handles_cs);
-        list_add_tail(&xcv_handles, &xcv->entry);
-        LeaveCriticalSection(&xcv_handles_cs);
-        TRACE("=> %p\n", xcv);
-        return TRUE;
-    }
-    else
-    {
-        *phXcv = NULL;
-        return FALSE;
-    }
-}
-
-/*****************************************************
- *      InitializePrintMonitor  (LOCALSPL.@)
- *
- * Initialize the Monitor for the Local Ports
- *
- * PARAMS
- *  regroot [I] Registry-Path, where the settings are stored
- *
- * RETURNS
- *  Success: Pointer to a MONITOREX Structure
- *  Failure: NULL
- *
- * NOTES
- *  The fixed location "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Ports"
- *  is used to store the Ports (IniFileMapping from "win.ini", Section "Ports").
- *  Native localspl.dll fails, when no valid Port-Entry is present.
- *
- */
-
-LPMONITOREX WINAPI InitializePrintMonitor(LPWSTR regroot)
-{
-    static MONITOREX mymonitorex =
-    {
-        sizeof(MONITOREX) - sizeof(DWORD),
-        {
-            localmon_EnumPortsW,
-            localmon_OpenPortW,
-            NULL,       /* localmon_OpenPortExW */ 
-            NULL,       /* localmon_StartDocPortW */
-            NULL,       /* localmon_WritePortW */
-            NULL,       /* localmon_ReadPortW */
-            NULL,       /* localmon_EndDocPortW */
-            localmon_ClosePort,
-            NULL,       /* Use AddPortUI in localui.dll */
-            localmon_AddPortExW,
-            NULL,       /* Use ConfigurePortUI in localui.dll */
-            NULL,       /* Use DeletePortUI in localui.dll */
-            NULL,       /* localmon_GetPrinterDataFromPort */
-            NULL,       /* localmon_SetPortTimeOuts */
-            localmon_XcvOpenPort,
-            localmon_XcvDataPort,
-            localmon_XcvClosePort
-        }
-    };
-
-    TRACE("(%s)\n", debugstr_w(regroot));
-    /* Parameter "regroot" is ignored on NT4.0 (localmon.dll) */
-    if (!regroot || !regroot[0]) {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return NULL;
-    }
-    TRACE("=> %p\n", &mymonitorex);
-    /* Native windows returns always the same pointer on success */
-    return &mymonitorex;
-}
diff --git a/reactos/dll/win32/localspl/localspl.spec b/reactos/dll/win32/localspl/localspl.spec
deleted file mode 100644 (file)
index 651d20d..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-@ stub ClosePrintProcessor
-@ stub ControlPrintProcessor
-@ stub EnumPrintProcessorDatatypesW
-@ stub GetPrintProcessorCapabilities
-@ stdcall InitializePrintMonitor(wstr)
-# "Providor" is no typo here
-@ stdcall InitializePrintProvidor(ptr long wstr)
-@ stub OpenPrintProcessor
-@ stub PrintDocumentOnPrintProcessor
-@ stub PrintProcLogEvent
-@ stub SplAddForm
-@ stub SplAddMonitor
-@ stub SplAddPort
-@ stub SplAddPortEx
-@ stub SplAddPrinter
-@ stub SplAddPrinterDriverEx
-@ stub SplAddPrintProcessor
-@ stub SplBroadcastChange
-@ stub SplClosePrinter
-@ stub SplCloseSpooler
-@ stub SplConfigChange
-@ stub SplCopyFileEvent
-@ stub SplCopyNumberOfFiles
-@ stub SplCreateSpooler
-@ stub SplDeleteForm
-@ stub SplDeleteMonitor
-@ stub SplDeletePort
-@ stub SplDeletePrinter
-@ stub SplDeletePrinterDriverEx
-@ stub SplDeletePrinterKey
-@ stub SplDeletePrintProcessor
-@ stub SplDeleteSpooler
-@ stub SplDriverEvent
-@ stub SplEnumForms
-@ stub SplEnumMonitors
-@ stub SplEnumPorts
-@ stub SplEnumPrinterDataEx
-@ stub SplEnumPrinterKey
-@ stub SplEnumPrinters
-@ stub SplEnumPrintProcessorDatatypes
-@ stub SplEnumPrintProcessors
-@ stub SplGetDriverDir
-@ stub SplGetForm
-@ stub SplGetPrinter
-@ stub SplGetPrinterData
-@ stub SplGetPrinterDataEx
-@ stub SplGetPrinterDriver
-@ stub SplGetPrinterDriverDirectory
-@ stub SplGetPrinterDriverEx
-@ stub SplGetPrinterExtra
-@ stub SplGetPrinterExtraEx
-@ stub SplGetPrintProcessorDirectory
-@ stub SplLoadLibraryTheCopyFileModule
-@ stub SplMonitorIsInstalled
-@ stub SplOpenPrinter
-@ stub SplReenumeratePorts
-@ stub SplResetPrinter
-@ stub SplSetForm
-@ stub SplSetPrinter
-@ stub SplSetPrinterData
-@ stub SplSetPrinterDataEx
-@ stub SplSetPrinterExtra
-@ stub SplSetPrinterExtraEx
-@ stub SplXcvData
diff --git a/reactos/dll/win32/localspl/localspl_main.c b/reactos/dll/win32/localspl/localspl_main.c
deleted file mode 100644 (file)
index 6e594c7..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Implementation of the Local Printprovider/ Printmonitor/ Prontprocessor
- *
- * 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 "localspl_private.h"
-
-HINSTANCE LOCALSPL_hInstance = NULL;
-
-/*****************************************************
- *      DllMain
- */
-BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
-{
-    TRACE("(%p, %d, %p)\n",hinstDLL, fdwReason, lpvReserved);
-
-    switch(fdwReason)
-    {
-        case DLL_WINE_PREATTACH:
-            return FALSE;           /* prefer native version */
-
-        case DLL_PROCESS_ATTACH:
-            DisableThreadLibraryCalls( hinstDLL );
-            LOCALSPL_hInstance = hinstDLL;
-            setup_provider();
-            break;
-    }
-    return TRUE;
-}
diff --git a/reactos/dll/win32/localspl/localspl_private.h b/reactos/dll/win32/localspl/localspl_private.h
deleted file mode 100644 (file)
index 359b30b..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Implementation of the Local Printmonitor: internal include file
- *
- * Copyright 2006 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
- */
-
-#ifndef __WINE_LOCALSPL_PRIVATE__
-#define __WINE_LOCALSPL_PRIVATE__
-
-#include <stdarg.h>
-
-#define WIN32_NO_STATUS
-#define _INC_WINDOWS
-#define COM_NO_WINDOWS_H
-
-#define COBJMACROS
-#define NONAMELESSUNION
-
-#include <windef.h>
-#include <winbase.h>
-#include <wingdi.h>
-#include <winuser.h>
-#include <winreg.h>
-#include <winspool.h>
-
-#include <wine/list.h>
-#include <wine/unicode.h>
-#include <ddk/winsplp.h>
-
-#include <wine/debug.h>
-WINE_DEFAULT_DEBUG_CHANNEL(localspl);
-
-/* ## DLL-wide Globals ## */
-extern HINSTANCE LOCALSPL_hInstance DECLSPEC_HIDDEN;
-void setup_provider(void) DECLSPEC_HIDDEN;
-
-/* ## Type of Ports ## */
-/* windows types */
-#define PORT_IS_UNKNOWN  0
-#define PORT_IS_LPT      1
-#define PORT_IS_COM      2
-#define PORT_IS_FILE     3
-#define PORT_IS_FILENAME 4
-
-/* wine extensions */
-#define PORT_IS_WINE     5
-#define PORT_IS_UNIXNAME 5
-#define PORT_IS_PIPE     6
-#define PORT_IS_CUPS     7
-#define PORT_IS_LPR      8
-
-
-/* ## Memory allocation functions ## */
-
-static inline void * __WINE_ALLOC_SIZE(1) heap_alloc( size_t len )
-{
-    return HeapAlloc( GetProcessHeap(), 0, len );
-}
-
-static inline void * __WINE_ALLOC_SIZE(1) heap_alloc_zero( size_t len )
-{
-    return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len );
-}
-
-static inline void * __WINE_ALLOC_SIZE(2) heap_realloc_zero( void * mem, size_t len )
-{
-    return HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, mem, len );
-}
-
-static inline BOOL heap_free( void *mem )
-{
-    return HeapFree( GetProcessHeap(), 0, mem );
-}
-
-#endif /* __WINE_LOCALSPL_PRIVATE__ */
diff --git a/reactos/dll/win32/localspl/provider.c b/reactos/dll/win32/localspl/provider.c
deleted file mode 100644 (file)
index 4546a22..0000000
+++ /dev/null
@@ -1,2456 +0,0 @@
-/*
- * Implementation of the Local Printprovider
- *
- * 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 "localspl_private.h"
-
-#include <shlwapi.h>
-#include <ddk/winddiui.h>
-
-/* ############################### */
-
-static CRITICAL_SECTION monitor_handles_cs;
-static CRITICAL_SECTION_DEBUG monitor_handles_cs_debug =
-{
-    0, 0, &monitor_handles_cs,
-    { &monitor_handles_cs_debug.ProcessLocksList, &monitor_handles_cs_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": monitor_handles_cs") }
-};
-static CRITICAL_SECTION monitor_handles_cs = { &monitor_handles_cs_debug, -1, 0, 0, 0, 0 };
-
-/* ############################### */
-
-typedef struct {
-    WCHAR   src[MAX_PATH+MAX_PATH];
-    WCHAR   dst[MAX_PATH+MAX_PATH];
-    DWORD   srclen;
-    DWORD   dstlen;
-    DWORD   copyflags;
-    BOOL    lazy;
-} apd_data_t;
-
-typedef struct {
-    struct list     entry;
-    LPWSTR          name;
-    LPWSTR          dllname;
-    PMONITORUI      monitorUI;
-    LPMONITOR       monitor;
-    HMODULE         hdll;
-    DWORD           refcount;
-    DWORD           dwMonitorSize;
-} monitor_t;
-
-typedef struct {
-    LPCWSTR  envname;
-    LPCWSTR  subdir;
-    DWORD    driverversion;
-    LPCWSTR  versionregpath;
-    LPCWSTR  versionsubdir;
-} printenv_t;
-
-typedef struct {
-    LPWSTR name;
-    LPWSTR printername;
-    monitor_t * pm;
-    HANDLE hXcv;
-} printer_t;
-
-/* ############################### */
-
-static struct list monitor_handles = LIST_INIT( monitor_handles );
-static monitor_t * pm_localport;
-
-static const PRINTPROVIDOR * pprovider = NULL;
-
-static const WCHAR backslashW[] = {'\\',0};
-static const WCHAR bs_ports_bsW[] = {'\\','P','o','r','t','s','\\',0};
-static const WCHAR configuration_fileW[] = {'C','o','n','f','i','g','u','r','a','t','i','o','n',' ','F','i','l','e',0};
-static const WCHAR datatypeW[] = {'D','a','t','a','t','y','p','e',0};
-static const WCHAR data_fileW[] = {'D','a','t','a',' ','F','i','l','e',0};
-static const WCHAR dependent_filesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
-static const WCHAR driverW[] = {'D','r','i','v','e','r',0};
-static const WCHAR emptyW[] = {0};
-static const WCHAR fmt_driversW[] = { 'S','y','s','t','e','m','\\',
-                                  'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
-                                  'c','o','n','t','r','o','l','\\',
-                                  'P','r','i','n','t','\\',
-                                  'E','n','v','i','r','o','n','m','e','n','t','s','\\',
-                                  '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
-static const WCHAR fmt_printprocessorsW[] = { 'S','y','s','t','e','m','\\',
-                                  'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
-                                  'C','o','n','t','r','o','l','\\',
-                                  'P','r','i','n','t','\\',
-                                  'E','n','v','i','r','o','n','m','e','n','t','s','\\','%','s','\\',
-                                  'P','r','i','n','t',' ','P','r','o','c','e','s','s','o','r','s',0 };
-static const WCHAR help_fileW[] = {'H','e','l','p',' ','F','i','l','e',0};
-static const WCHAR ia64_envnameW[] = {'W','i','n','d','o','w','s',' ','I','A','6','4',0};
-static const WCHAR ia64_subdirW[] = {'i','a','6','4',0};
-static const WCHAR localportW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
-static const WCHAR monitorW[] = {'M','o','n','i','t','o','r',0};
-static const WCHAR monitorsW[] = {'S','y','s','t','e','m','\\',
-                                'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
-                                'C','o','n','t','r','o','l','\\',
-                                'P','r','i','n','t','\\',
-                                'M','o','n','i','t','o','r','s','\\',0};
-static const WCHAR monitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
-static const WCHAR previous_namesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
-static const WCHAR printersW[] = {'S','y','s','t','e','m','\\',
-                                  'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
-                                  'C','o','n','t','r','o','l','\\',
-                                  'P','r','i','n','t','\\',
-                                  'P','r','i','n','t','e','r','s',0};
-static const WCHAR spoolW[] = {'\\','s','p','o','o','l',0};
-static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0};
-static const WCHAR spoolprtprocsW[] = {'\\','s','p','o','o','l','\\','p','r','t','p','r','o','c','s','\\',0};
-static const WCHAR version0_regpathW[] = {'\\','V','e','r','s','i','o','n','-','0',0};
-static const WCHAR version0_subdirW[] = {'\\','0',0};
-static const WCHAR version3_regpathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
-static const WCHAR version3_subdirW[] = {'\\','3',0};
-static const WCHAR versionW[] = {'V','e','r','s','i','o','n',0};
-static const WCHAR win40_envnameW[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
-static const WCHAR win40_subdirW[] = {'w','i','n','4','0',0};
-static const WCHAR winnt_cv_portsW[] = {'S','o','f','t','w','a','r','e','\\',
-                                        'M','i','c','r','o','s','o','f','t','\\',
-                                        'W','i','n','d','o','w','s',' ','N','T','\\',
-                                        'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
-                                        'P','o','r','t','s',0};
-static const WCHAR winprintW[] = {'w','i','n','p','r','i','n','t',0};
-static const WCHAR x64_envnameW[] = {'W','i','n','d','o','w','s',' ','x','6','4',0};
-static const WCHAR x64_subdirW[] = {'x','6','4',0};
-static const WCHAR x86_envnameW[] = {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
-static const WCHAR x86_subdirW[] = {'w','3','2','x','8','6',0};
-static const WCHAR XcvMonitorW[] = {',','X','c','v','M','o','n','i','t','o','r',' ',0};
-static const WCHAR XcvPortW[] = {',','X','c','v','P','o','r','t',' ',0};
-
-
-static const printenv_t env_ia64 =  {ia64_envnameW, ia64_subdirW, 3,
-                                     version3_regpathW, version3_subdirW};
-
-static const printenv_t env_x86 =   {x86_envnameW, x86_subdirW, 3,
-                                     version3_regpathW, version3_subdirW};
-
-static const printenv_t env_x64 =   {x64_envnameW, x64_subdirW, 3,
-                                     version3_regpathW, version3_subdirW};
-
-static const printenv_t env_win40 = {win40_envnameW, win40_subdirW, 0,
-                                     version0_regpathW, version0_subdirW};
-
-static const printenv_t * const all_printenv[] = {&env_x86, &env_x64, &env_ia64, &env_win40};
-
-
-static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
-                                     sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
-                                     sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
-                                  0, sizeof(DRIVER_INFO_8W)};
-
-
-/******************************************************************
- * 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);
-    if (ret) memcpy(ret, p, len);
-    return ret;
-}
-
-/******************************************************************
- *  apd_copyfile [internal]
- *
- * Copy a file from the driverdirectory to the versioned directory
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- */
-static BOOL apd_copyfile( WCHAR *pathname, WCHAR *file_part, apd_data_t *apd )
-{
-    WCHAR *srcname;
-    BOOL res;
-
-    apd->src[apd->srclen] = '\0';
-    apd->dst[apd->dstlen] = '\0';
-
-    if (!pathname || !pathname[0]) {
-        /* nothing to copy */
-        return TRUE;
-    }
-
-    if (apd->copyflags & APD_COPY_FROM_DIRECTORY)
-        srcname = pathname;
-    else
-    {
-        srcname = apd->src;
-        strcatW( srcname, file_part );
-    }
-    strcatW( apd->dst, file_part );
-
-    TRACE("%s => %s\n", debugstr_w(srcname), debugstr_w(apd->dst));
-
-    /* FIXME: handle APD_COPY_NEW_FILES */
-    res = CopyFileW(srcname, apd->dst, FALSE);
-    TRACE("got %d with %u\n", res, GetLastError());
-
-    return apd->lazy || res;
-}
-
-/******************************************************************
- * copy_servername_from_name  (internal)
- *
- * for an external server, the serverpart from the name is copied.
- *
- * RETURNS
- *  the length (in WCHAR) of the serverpart (0 for the local computer)
- *  (-length), when the name is too long
- *
- */
-static LONG copy_servername_from_name(LPCWSTR name, LPWSTR target)
-{
-    LPCWSTR server;
-    LPWSTR  ptr;
-    WCHAR   buffer[MAX_COMPUTERNAME_LENGTH +1];
-    DWORD   len;
-    DWORD   serverlen;
-
-    if (target) *target = '\0';
-
-    if (name == NULL) return 0;
-    if ((name[0] != '\\') || (name[1] != '\\')) return 0;
-
-    server = &name[2];
-    /* skip over both backslash, find separator '\' */
-    ptr = strchrW(server, '\\');
-    serverlen = (ptr) ? ptr - server : lstrlenW(server);
-
-    /* servername is empty */
-    if (serverlen == 0) return 0;
-
-    TRACE("found %s\n", debugstr_wn(server, serverlen));
-
-    if (serverlen > MAX_COMPUTERNAME_LENGTH) return -serverlen;
-
-    if (target) {
-        memcpy(target, server, serverlen * sizeof(WCHAR));
-        target[serverlen] = '\0';
-    }
-
-    len = sizeof(buffer) / sizeof(buffer[0]);
-    if (GetComputerNameW(buffer, &len)) {
-        if ((serverlen == len) && (strncmpiW(server, buffer, len) == 0)) {
-            /* The requested Servername is our computername */
-            return 0;
-        }
-    }
-    return serverlen;
-}
-
-/******************************************************************
- * get_basename_from_name  (internal)
- *
- * skip over the serverpart from the full name
- *
- */
-static LPCWSTR get_basename_from_name(LPCWSTR name)
-{
-    if (name == NULL)  return NULL;
-    if ((name[0] == '\\') && (name[1] == '\\')) {
-        /* skip over the servername and search for the following '\'  */
-        name = strchrW(&name[2], '\\');
-        if ((name) && (name[1])) {
-            /* found a separator ('\') followed by a name:
-               skip over the separator and return the rest */
-            name++;
-        }
-        else
-        {
-            /* no basename present (we found only a servername) */
-            return NULL;
-        }
-    }
-    return name;
-}
-
-/******************************************************************
- * monitor_unload [internal]
- *
- * release a printmonitor and unload it from memory, when needed
- *
- */
-static void monitor_unload(monitor_t * pm)
-{
-    if (pm == NULL) return;
-    TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
-
-    EnterCriticalSection(&monitor_handles_cs);
-
-    if (pm->refcount) pm->refcount--;
-
-    if (pm->refcount == 0) {
-        list_remove(&pm->entry);
-        FreeLibrary(pm->hdll);
-        heap_free(pm->name);
-        heap_free(pm->dllname);
-        heap_free(pm);
-    }
-    LeaveCriticalSection(&monitor_handles_cs);
-}
-
-/******************************************************************
- * monitor_unloadall [internal]
- *
- * release all registered printmonitors and unload them from memory, when needed
- *
- */
-
-static void monitor_unloadall(void)
-{
-    monitor_t * pm;
-    monitor_t * next;
-
-    EnterCriticalSection(&monitor_handles_cs);
-    /* iterate through the list, with safety against removal */
-    LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
-    {
-        /* skip monitorui dlls */
-        if (pm->monitor) monitor_unload(pm);
-    }
-    LeaveCriticalSection(&monitor_handles_cs);
-}
-
-/******************************************************************
- * monitor_load [internal]
- *
- * load a printmonitor, get the dllname from the registry, when needed
- * initialize the monitor and dump found function-pointers
- *
- * On failure, SetLastError() is called and NULL is returned
- */
-
-static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname)
-{
-    LPMONITOR2  (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
-    PMONITORUI  (WINAPI *pInitializePrintMonitorUI)(VOID);
-    LPMONITOREX (WINAPI *pInitializePrintMonitor)  (LPWSTR);
-    DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
-    DWORD (WINAPI *pInitializeMonitor)  (LPWSTR);
-
-    monitor_t * pm = NULL;
-    monitor_t * cursor;
-    LPWSTR  regroot = NULL;
-    LPWSTR  driver = dllname;
-
-    TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
-    /* Is the Monitor already loaded? */
-    EnterCriticalSection(&monitor_handles_cs);
-
-    if (name) {
-        LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
-        {
-            if (cursor->name && (lstrcmpW(name, cursor->name) == 0)) {
-                pm = cursor;
-                break;
-            }
-        }
-    }
-
-    if (pm == NULL) {
-        pm = heap_alloc_zero(sizeof(monitor_t));
-        if (pm == NULL) goto cleanup;
-        list_add_tail(&monitor_handles, &pm->entry);
-    }
-    pm->refcount++;
-
-    if (pm->name == NULL) {
-        /* Load the monitor */
-        LPMONITOREX pmonitorEx;
-        DWORD   len;
-
-        if (name) {
-            len = lstrlenW(monitorsW) + lstrlenW(name) + 2;
-            regroot = heap_alloc(len * sizeof(WCHAR));
-        }
-
-        if (regroot) {
-            lstrcpyW(regroot, monitorsW);
-            lstrcatW(regroot, name);
-            /* Get the Driver from the Registry */
-            if (driver == NULL) {
-                HKEY    hroot;
-                DWORD   namesize;
-                if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
-                    if (RegQueryValueExW(hroot, driverW, NULL, NULL, NULL,
-                                        &namesize) == ERROR_SUCCESS) {
-                        driver = heap_alloc(namesize);
-                        RegQueryValueExW(hroot, driverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
-                    }
-                    RegCloseKey(hroot);
-                }
-            }
-        }
-
-        pm->name = strdupW(name);
-        pm->dllname = strdupW(driver);
-
-        if ((name && (!regroot || !pm->name)) || !pm->dllname) {
-            monitor_unload(pm);
-            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-            pm = NULL;
-            goto cleanup;
-        }
-
-        pm->hdll = LoadLibraryW(driver);
-        TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
-
-        if (pm->hdll == NULL) {
-            monitor_unload(pm);
-            SetLastError(ERROR_MOD_NOT_FOUND);
-            pm = NULL;
-            goto cleanup;
-        }
-
-        pInitializePrintMonitor2  = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
-        pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
-        pInitializePrintMonitor   = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
-        pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
-        pInitializeMonitor   = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
-
-
-        TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
-        TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
-        TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
-        TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
-        TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
-
-        if (pInitializePrintMonitorUI  != NULL) {
-            pm->monitorUI = pInitializePrintMonitorUI();
-            TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver));
-            if (pm->monitorUI) {
-                TRACE("0x%08x: dwMonitorSize (%d)\n",
-                        pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize);
-
-            }
-        }
-
-        if (pInitializePrintMonitor && regroot) {
-            pmonitorEx = pInitializePrintMonitor(regroot);
-            TRACE("%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
-                    pmonitorEx, debugstr_w(driver), debugstr_w(regroot));
-
-            if (pmonitorEx) {
-                pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
-                pm->monitor = &(pmonitorEx->Monitor);
-            }
-        }
-
-        if (pm->monitor) {
-            TRACE("0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize);
-
-        }
-
-        if (!pm->monitor && regroot) {
-            if (pInitializePrintMonitor2 != NULL) {
-                FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
-            }
-            if (pInitializeMonitorEx != NULL) {
-                FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
-            }
-            if (pInitializeMonitor != NULL) {
-                FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
-            }
-        }
-        if (!pm->monitor && !pm->monitorUI) {
-            monitor_unload(pm);
-            SetLastError(ERROR_PROC_NOT_FOUND);
-            pm = NULL;
-        }
-    }
-cleanup:
-    if ((pm_localport ==  NULL) && (pm != NULL) && (lstrcmpW(pm->name, localportW) == 0)) {
-        pm->refcount++;
-        pm_localport = pm;
-    }
-    LeaveCriticalSection(&monitor_handles_cs);
-    if (driver != dllname) heap_free(driver);
-    heap_free(regroot);
-    TRACE("=> %p\n", pm);
-    return pm;
-}
-
-/******************************************************************
- * monitor_loadall [internal]
- *
- * Load all registered monitors
- *
- */
-static DWORD monitor_loadall(void)
-{
-    monitor_t * pm;
-    DWORD   registered = 0;
-    DWORD   loaded = 0;
-    HKEY    hmonitors;
-    WCHAR   buffer[MAX_PATH];
-    DWORD   id = 0;
-
-    if (RegOpenKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hmonitors) == ERROR_SUCCESS) {
-        RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, &registered, NULL, NULL,
-                        NULL, NULL, NULL, NULL, NULL);
-
-        TRACE("%d monitors registered\n", registered);
-
-        while (id < registered) {
-            buffer[0] = '\0';
-            RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
-            pm = monitor_load(buffer, NULL);
-            if (pm) loaded++;
-            id++;
-        }
-        RegCloseKey(hmonitors);
-    }
-    TRACE("%d monitors loaded\n", loaded);
-    return loaded;
-}
-
-/******************************************************************
- * monitor_loadui [internal]
- *
- * load the userinterface-dll for a given portmonitor
- *
- * On failure, NULL is returned
- */
-static monitor_t * monitor_loadui(monitor_t * pm)
-{
-    monitor_t * pui = NULL;
-    WCHAR   buffer[MAX_PATH];
-    HANDLE  hXcv;
-    DWORD   len;
-    DWORD   res;
-
-    if (pm == NULL) return NULL;
-    TRACE("(%p) => dllname: %s\n", pm, debugstr_w(pm->dllname));
-
-    /* Try the Portmonitor first; works for many monitors */
-    if (pm->monitorUI) {
-        EnterCriticalSection(&monitor_handles_cs);
-        pm->refcount++;
-        LeaveCriticalSection(&monitor_handles_cs);
-        return pm;
-    }
-
-    /* query the userinterface-dllname from the Portmonitor */
-    if ((pm->monitor) && (pm->monitor->pfnXcvDataPort)) {
-        /* building (",XcvMonitor %s",pm->name) not needed yet */
-        res = pm->monitor->pfnXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, &hXcv);
-        TRACE("got %u with %p\n", res, hXcv);
-        if (res) {
-            res = pm->monitor->pfnXcvDataPort(hXcv, monitorUIW, NULL, 0, (BYTE *) buffer, sizeof(buffer), &len);
-            TRACE("got %u with %s\n", res, debugstr_w(buffer));
-            if (res == ERROR_SUCCESS) pui = monitor_load(NULL, buffer);
-            pm->monitor->pfnXcvClosePort(hXcv);
-        }
-    }
-    return pui;
-}
-
-/******************************************************************
- * monitor_load_by_port [internal]
- *
- * load a printmonitor for a given port
- *
- * On failure, NULL is returned
- */
-
-static monitor_t * monitor_load_by_port(LPCWSTR portname)
-{
-    HKEY    hroot;
-    HKEY    hport;
-    LPWSTR  buffer;
-    monitor_t * pm = NULL;
-    DWORD   registered = 0;
-    DWORD   id = 0;
-    DWORD   len;
-
-    TRACE("(%s)\n", debugstr_w(portname));
-
-    /* Try the Local Monitor first */
-    if (RegOpenKeyW(HKEY_LOCAL_MACHINE, winnt_cv_portsW, &hroot) == ERROR_SUCCESS) {
-        if (RegQueryValueExW(hroot, portname, NULL, NULL, NULL, &len) == ERROR_SUCCESS) {
-            /* found the portname */
-            RegCloseKey(hroot);
-            return monitor_load(localportW, NULL);
-        }
-        RegCloseKey(hroot);
-    }
-
-    len = MAX_PATH + lstrlenW(bs_ports_bsW) + lstrlenW(portname) + 1;
-    buffer = heap_alloc(len * sizeof(WCHAR));
-    if (buffer == NULL) return NULL;
-
-    if (RegOpenKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) == ERROR_SUCCESS) {
-        EnterCriticalSection(&monitor_handles_cs);
-        RegQueryInfoKeyW(hroot, NULL, NULL, NULL, &registered, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
-
-        while ((pm == NULL) && (id < registered)) {
-            buffer[0] = '\0';
-            RegEnumKeyW(hroot, id, buffer, MAX_PATH);
-            TRACE("testing %s\n", debugstr_w(buffer));
-            len = lstrlenW(buffer);
-            lstrcatW(buffer, bs_ports_bsW);
-            lstrcatW(buffer, portname);
-            if (RegOpenKeyW(hroot, buffer, &hport) == ERROR_SUCCESS) {
-                RegCloseKey(hport);
-                buffer[len] = '\0';             /* use only the Monitor-Name */
-                pm = monitor_load(buffer, NULL);
-            }
-            id++;
-        }
-        LeaveCriticalSection(&monitor_handles_cs);
-        RegCloseKey(hroot);
-    }
-    heap_free(buffer);
-    return pm;
-}
-
-/******************************************************************
- * Return the number of bytes for an multi_sz string.
- * The result includes all \0s
- * (specifically the extra \0, that is needed as multi_sz terminator).
- */
-static int multi_sz_lenW(const WCHAR *str)
-{
-    const WCHAR *ptr = str;
-    if (!str) return 0;
-    do
-    {
-        ptr += lstrlenW(ptr) + 1;
-    } while (*ptr);
-
-    return (ptr - str + 1) * sizeof(WCHAR);
-}
-
-/******************************************************************
- * validate_envW [internal]
- *
- * validate the user-supplied printing-environment
- *
- * PARAMS
- *  env  [I] PTR to Environment-String or NULL
- *
- * RETURNS
- *  Success:  PTR to printenv_t
- *  Failure:  NULL and ERROR_INVALID_ENVIRONMENT
- *
- * NOTES
- *  An empty string is handled the same way as NULL.
- *
- */
-
-static const  printenv_t * validate_envW(LPCWSTR env)
-{
-    const printenv_t *result = NULL;
-    unsigned int i;
-
-    TRACE("(%s)\n", debugstr_w(env));
-    if (env && env[0])
-    {
-        for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
-        {
-            if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
-            {
-                result = all_printenv[i];
-                break;
-            }
-        }
-        if (result == NULL) {
-            FIXME("unsupported Environment: %s\n", debugstr_w(env));
-            SetLastError(ERROR_INVALID_ENVIRONMENT);
-        }
-        /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
-    }
-    else
-    {
-        result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
-    }
-
-    TRACE("=> using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
-    return result;
-}
-
-/*****************************************************************************
- * enumerate the local monitors (INTERNAL)
- *
- * returns the needed size (in bytes) for pMonitors
- * and  *lpreturned is set to number of entries returned in pMonitors
- *
- * Language-Monitors are also installed in the same Registry-Location but
- * they are filtered in Windows (not returned by EnumMonitors).
- * We do no filtering to simplify our Code.
- *
- */
-static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
-{
-    HKEY    hroot = NULL;
-    HKEY    hentry = NULL;
-    LPWSTR  ptr;
-    LPMONITOR_INFO_2W mi;
-    WCHAR   buffer[MAX_PATH];
-    WCHAR   dllname[MAX_PATH];
-    DWORD   dllsize;
-    DWORD   len;
-    DWORD   index = 0;
-    DWORD   needed = 0;
-    DWORD   numentries;
-    DWORD   entrysize;
-
-    entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
-
-    numentries = *lpreturned;       /* this is 0, when we scan the registry */
-    len = entrysize * numentries;
-    ptr = (LPWSTR) &pMonitors[len];
-
-    numentries = 0;
-    len = sizeof(buffer)/sizeof(buffer[0]);
-    buffer[0] = '\0';
-
-    /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
-    if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) == ERROR_SUCCESS) {
-        /* Scan all Monitor-Registry-Keys */
-        while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
-            TRACE("Monitor_%d: %s\n", numentries, debugstr_w(buffer));
-            dllsize = sizeof(dllname);
-            dllname[0] = '\0';
-
-            /* The Monitor must have a Driver-DLL */
-            if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
-                if (RegQueryValueExW(hentry, driverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
-                    /* We found a valid DLL for this Monitor. */
-                    TRACE("using Driver: %s\n", debugstr_w(dllname));
-                }
-                RegCloseKey(hentry);
-            }
-
-            /* Windows returns only Port-Monitors here, but to simplify our code,
-               we do no filtering for Language-Monitors */
-            if (dllname[0]) {
-                numentries++;
-                needed += entrysize;
-                needed += (len+1) * sizeof(WCHAR);  /* len is lstrlenW(monitorname) */
-                if (level > 1) {
-                    /* we install and return only monitors for "Windows NT x86" */
-                    needed += (lstrlenW(x86_envnameW) +1) * sizeof(WCHAR);
-                    needed += dllsize;
-                }
-
-                /* required size is calculated. Now fill the user-buffer */
-                if (pMonitors && (cbBuf >= needed)){
-                    mi = (LPMONITOR_INFO_2W) pMonitors;
-                    pMonitors += entrysize;
-
-                    TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi, level, numentries);
-                    mi->pName = ptr;
-                    lstrcpyW(ptr, buffer);      /* Name of the Monitor */
-                    ptr += (len+1);               /* len is lstrlenW(monitorname) */
-                    if (level > 1) {
-                        mi->pEnvironment = ptr;
-                        lstrcpyW(ptr, x86_envnameW); /* fixed to "Windows NT x86" */
-                        ptr += (lstrlenW(x86_envnameW)+1);
-
-                        mi->pDLLName = ptr;
-                        lstrcpyW(ptr, dllname);         /* Name of the Driver-DLL */
-                        ptr += (dllsize / sizeof(WCHAR));
-                    }
-                }
-            }
-            index++;
-            len = sizeof(buffer)/sizeof(buffer[0]);
-            buffer[0] = '\0';
-        }
-        RegCloseKey(hroot);
-    }
-    *lpreturned = numentries;
-    TRACE("need %d byte for %d entries\n", needed, numentries);
-    return needed;
-}
-
-/*****************************************************************************
- * enumerate the local print processors (INTERNAL)
- *
- * returns the needed size (in bytes) for pPPInfo
- * and  *lpreturned is set to number of entries returned in pPPInfo
- *
- */
-static DWORD get_local_printprocessors(LPWSTR regpathW, LPBYTE pPPInfo, DWORD cbBuf, LPDWORD lpreturned)
-{
-    HKEY    hroot = NULL;
-    HKEY    hentry = NULL;
-    LPWSTR  ptr;
-    PPRINTPROCESSOR_INFO_1W ppi;
-    WCHAR   buffer[MAX_PATH];
-    WCHAR   dllname[MAX_PATH];
-    DWORD   dllsize;
-    DWORD   len;
-    DWORD   index = 0;
-    DWORD   needed = 0;
-    DWORD   numentries;
-
-    numentries = *lpreturned;       /* this is 0, when we scan the registry */
-    len = numentries * sizeof(PRINTPROCESSOR_INFO_1W);
-    ptr = (LPWSTR) &pPPInfo[len];
-
-    numentries = 0;
-    len = sizeof(buffer)/sizeof(buffer[0]);
-    buffer[0] = '\0';
-
-    if (RegCreateKeyW(HKEY_LOCAL_MACHINE, regpathW, &hroot) == ERROR_SUCCESS) {
-        /* add "winprint" first */
-        numentries++;
-        needed = sizeof(PRINTPROCESSOR_INFO_1W) + sizeof(winprintW);
-        if (pPPInfo && (cbBuf >= needed)){
-            ppi = (PPRINTPROCESSOR_INFO_1W) pPPInfo;
-            pPPInfo += sizeof(PRINTPROCESSOR_INFO_1W);
-
-            TRACE("%p: writing PRINTPROCESSOR_INFO_1W #%d\n", ppi, numentries);
-            ppi->pName = ptr;
-            lstrcpyW(ptr, winprintW);      /* Name of the Print Processor */
-            ptr += sizeof(winprintW) / sizeof(WCHAR);
-        }
-
-        /* Scan all Printprocessor Keys */
-        while ((RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) &&
-            (lstrcmpiW(buffer, winprintW) != 0)) {
-            TRACE("PrintProcessor_%d: %s\n", numentries, debugstr_w(buffer));
-            dllsize = sizeof(dllname);
-            dllname[0] = '\0';
-
-            /* The Print Processor must have a Driver-DLL */
-            if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
-                if (RegQueryValueExW(hentry, driverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
-                    /* We found a valid DLL for this Print Processor */
-                    TRACE("using Driver: %s\n", debugstr_w(dllname));
-                }
-                RegCloseKey(hentry);
-            }
-
-            if (dllname[0]) {
-                numentries++;
-                needed += sizeof(PRINTPROCESSOR_INFO_1W);
-                needed += (len+1) * sizeof(WCHAR);  /* len is lstrlenW(printprocessor name) */
-
-                /* required size is calculated. Now fill the user-buffer */
-                if (pPPInfo && (cbBuf >= needed)){
-                    ppi = (PPRINTPROCESSOR_INFO_1W) pPPInfo;
-                    pPPInfo += sizeof(PRINTPROCESSOR_INFO_1W);
-
-                    TRACE("%p: writing PRINTPROCESSOR_INFO_1W #%d\n", ppi, numentries);
-                    ppi->pName = ptr;
-                    lstrcpyW(ptr, buffer);      /* Name of the Print Processor */
-                    ptr += (len+1);             /* len is lstrlenW(printprosessor name) */
-                }
-            }
-            index++;
-            len = sizeof(buffer)/sizeof(buffer[0]);
-            buffer[0] = '\0';
-        }
-        RegCloseKey(hroot);
-    }
-    *lpreturned = numentries;
-    TRACE("need %d byte for %d entries\n", needed, numentries);
-    return needed;
-}
-
-/******************************************************************
- * enumerate the local Ports from all loaded monitors (internal)
- *
- * returns the needed size (in bytes) for pPorts
- * and  *lpreturned is set to number of entries returned in pPorts
- *
- */
-static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
-{
-    monitor_t * pm;
-    LPWSTR      ptr;
-    LPPORT_INFO_2W cache;
-    LPPORT_INFO_2W out;
-    LPBYTE  pi_buffer = NULL;
-    DWORD   pi_allocated = 0;
-    DWORD   pi_needed;
-    DWORD   pi_index;
-    DWORD   pi_returned;
-    DWORD   res;
-    DWORD   outindex = 0;
-    DWORD   needed;
-    DWORD   numentries;
-    DWORD   entrysize;
-
-
-    TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
-    entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
-
-    numentries = *lpreturned;       /* this is 0, when we scan the registry */
-    needed = entrysize * numentries;
-    ptr = (LPWSTR) &pPorts[needed];
-
-    numentries = 0;
-    needed = 0;
-
-    LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
-    {
-        if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
-            pi_needed = 0;
-            pi_returned = 0;
-            res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
-            if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
-                /* Do not use heap_realloc (we do not need the old data in the buffer) */
-                heap_free(pi_buffer);
-                pi_buffer = heap_alloc(pi_needed);
-                pi_allocated = (pi_buffer) ? pi_needed : 0;
-                res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
-            }
-            TRACE("(%s) got %d with %d (need %d byte for %d entries)\n",
-                  debugstr_w(pm->name), res, GetLastError(), pi_needed, pi_returned);
-
-            numentries += pi_returned;
-            needed += pi_needed;
-
-            /* fill the output-buffer (pPorts), if we have one */
-            if (pPorts && (cbBuf >= needed ) && pi_buffer) {
-                pi_index = 0;
-                while (pi_returned > pi_index) {
-                    cache = (LPPORT_INFO_2W) &pi_buffer[pi_index * entrysize];
-                    out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
-                    out->pPortName = ptr;
-                    lstrcpyW(ptr, cache->pPortName);
-                    ptr += (lstrlenW(ptr)+1);
-                    if (level > 1) {
-                        out->pMonitorName = ptr;
-                        lstrcpyW(ptr,  cache->pMonitorName);
-                        ptr += (lstrlenW(ptr)+1);
-
-                        out->pDescription = ptr;
-                        lstrcpyW(ptr,  cache->pDescription);
-                        ptr += (lstrlenW(ptr)+1);
-                        out->fPortType = cache->fPortType;
-                        out->Reserved = cache->Reserved;
-                    }
-                    pi_index++;
-                    outindex++;
-                }
-            }
-        }
-    }
-    /* the temporary portinfo-buffer is no longer needed */
-    heap_free(pi_buffer);
-
-    *lpreturned = numentries;
-    TRACE("need %d byte for %d entries\n", needed, numentries);
-    return needed;
-}
-
-
-/*****************************************************************************
- * open_driver_reg [internal]
- *
- * opens the registry for the printer drivers depending on the given input
- * variable pEnvironment
- *
- * RETURNS:
- *    Success: the opened hkey
- *    Failure: NULL
- */
-static HKEY open_driver_reg(LPCWSTR pEnvironment)
-{
-    HKEY  retval = NULL;
-    LPWSTR buffer;
-    const printenv_t * env;
-
-    TRACE("(%s)\n", debugstr_w(pEnvironment));
-
-    env = validate_envW(pEnvironment);
-    if (!env) return NULL;
-
-    buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(fmt_driversW) +
-                (lstrlenW(env->envname) + lstrlenW(env->versionregpath)) * sizeof(WCHAR));
-
-    if (buffer) {
-        wsprintfW(buffer, fmt_driversW, env->envname, env->versionregpath);
-        RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
-        HeapFree(GetProcessHeap(), 0, buffer);
-    }
-    return retval;
-}
-
-/*****************************************************************************
- * fpGetPrinterDriverDirectory [exported through PRINTPROVIDOR]
- *
- * Return the PATH for the Printer-Drivers
- *
- * PARAMS
- *   pName            [I] Servername (NT only) or NULL (local Computer)
- *   pEnvironment     [I] Printing-Environment (see below) 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 4.0):     "%winsysdir%\\spool\\DRIVERS\\win40"
- *|  win9x(Windows 4.0):  "%winsysdir%"
- *
- *   "%winsysdir%" is the Value from GetSystemDirectoryW()
- *
- */
-static BOOL WINAPI fpGetPrinterDriverDirectory(LPWSTR pName, LPWSTR pEnvironment,
-            DWORD Level, LPBYTE pDriverDirectory, DWORD cbBuf, LPDWORD pcbNeeded)
-{
-    DWORD needed;
-    const printenv_t * env;
-    WCHAR * const dir = (WCHAR *)pDriverDirectory;
-
-    TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName),
-          debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
-
-    if (pName != NULL && pName[0]) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-
-    env = validate_envW(pEnvironment);
-    if (!env) return FALSE;  /* pEnvironment invalid or unsupported */
-
-
-    /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
-    needed = GetSystemDirectoryW(NULL, 0);
-    /* add the Size for the Subdirectories */
-    needed += lstrlenW(spoolW);
-    needed += lstrlenW(driversW);
-    needed += lstrlenW(env->subdir);
-    needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
-
-    *pcbNeeded = needed;
-
-    if (needed > cbBuf) {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        return FALSE;
-    }
-
-    if (dir == NULL) {
-        /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
-        SetLastError(ERROR_INVALID_USER_BUFFER);
-        return FALSE;
-    }
-
-    GetSystemDirectoryW( dir, cbBuf / sizeof(WCHAR) );
-    /* add the Subdirectories */
-    lstrcatW( dir, spoolW );
-    CreateDirectoryW( dir, NULL );
-    lstrcatW( dir, driversW );
-    CreateDirectoryW( dir, NULL );
-    lstrcatW( dir, env->subdir );
-    CreateDirectoryW( dir, NULL );
-
-    TRACE( "=> %s\n", debugstr_w( dir ) );
-    return TRUE;
-}
-
-/******************************************************************
- * driver_load [internal]
- *
- * load a driver user interface dll
- *
- * On failure, NULL is returned
- *
- */
-
-static HMODULE driver_load(const printenv_t * env, LPWSTR dllname)
-{
-    WCHAR fullname[MAX_PATH];
-    HMODULE hui;
-    DWORD len;
-
-    TRACE("(%p, %s)\n", env, debugstr_w(dllname));
-
-    /* build the driverdir */
-    len = sizeof(fullname) -
-          (lstrlenW(env->versionsubdir) + 1 + lstrlenW(dllname) + 1) * sizeof(WCHAR);
-
-    if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
-                                     (LPBYTE) fullname, len, &len)) {
-        /* Should never fail */
-        SetLastError(ERROR_BUFFER_OVERFLOW);
-        return NULL;
-    }
-
-    lstrcatW(fullname, env->versionsubdir);
-    lstrcatW(fullname, backslashW);
-    lstrcatW(fullname, dllname);
-
-    hui = LoadLibraryW(fullname);
-    TRACE("%p: LoadLibrary(%s) %d\n", hui, debugstr_w(fullname), GetLastError());
-
-    return hui;
-}
-
-/******************************************************************
- *  printer_free
- *  free the data pointer of an opened printer
- */
-static VOID printer_free(printer_t * printer)
-{
-    if (printer->hXcv)
-        printer->pm->monitor->pfnXcvClosePort(printer->hXcv);
-
-    monitor_unload(printer->pm);
-
-    heap_free(printer->printername);
-    heap_free(printer->name);
-    heap_free(printer);
-}
-
-/******************************************************************
- *  printer_alloc_handle
- *  alloc a printer handle and remember the data pointer in the printer handle table
- *
- */
-static HANDLE printer_alloc_handle(LPCWSTR name, LPPRINTER_DEFAULTSW pDefault)
-{
-    WCHAR servername[MAX_COMPUTERNAME_LENGTH + 1];
-    printer_t *printer = NULL;
-    LPCWSTR printername;
-    HKEY    hkeyPrinters;
-    HKEY    hkeyPrinter;
-    DWORD   len;
-
-    if (copy_servername_from_name(name, servername)) {
-        FIXME("server %s not supported\n", debugstr_w(servername));
-        SetLastError(ERROR_INVALID_PRINTER_NAME);
-        return NULL;
-    }
-
-    printername = get_basename_from_name(name);
-    if (name != printername) TRACE("converted %s to %s\n", debugstr_w(name), debugstr_w(printername));
-
-    /* an empty printername is invalid */
-    if (printername && (!printername[0])) {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return NULL;
-    }
-
-    printer = heap_alloc_zero(sizeof(printer_t));
-    if (!printer) goto end;
-
-    /* clone the base name. This is NULL for the printserver */
-    printer->printername = strdupW(printername);
-
-    /* clone the full name */
-    printer->name = strdupW(name);
-    if (name && (!printer->name)) {
-        printer_free(printer);
-        printer = NULL;
-    }
-    if (printername) {
-        len = sizeof(XcvMonitorW)/sizeof(WCHAR) - 1;
-        if (strncmpW(printername, XcvMonitorW, len) == 0) {
-            /* OpenPrinter(",XcvMonitor ", ...) detected */
-            TRACE(",XcvMonitor: %s\n", debugstr_w(&printername[len]));
-            printer->pm = monitor_load(&printername[len], NULL);
-            if (printer->pm == NULL) {
-                printer_free(printer);
-                SetLastError(ERROR_UNKNOWN_PORT);
-                printer = NULL;
-                goto end;
-            }
-        }
-        else
-        {
-            len = sizeof(XcvPortW)/sizeof(WCHAR) - 1;
-            if (strncmpW( printername, XcvPortW, len) == 0) {
-                /* OpenPrinter(",XcvPort ", ...) detected */
-                TRACE(",XcvPort: %s\n", debugstr_w(&printername[len]));
-                printer->pm = monitor_load_by_port(&printername[len]);
-                if (printer->pm == NULL) {
-                    printer_free(printer);
-                    SetLastError(ERROR_UNKNOWN_PORT);
-                    printer = NULL;
-                    goto end;
-                }
-            }
-        }
-
-        if (printer->pm) {
-            if ((printer->pm->monitor) && (printer->pm->monitor->pfnXcvOpenPort)) {
-                printer->pm->monitor->pfnXcvOpenPort(&printername[len],
-                                                    pDefault ? pDefault->DesiredAccess : 0,
-                                                    &printer->hXcv);
-            }
-            if (printer->hXcv == NULL) {
-                printer_free(printer);
-                SetLastError(ERROR_INVALID_PARAMETER);
-                printer = NULL;
-                goto end;
-            }
-        }
-        else
-        {
-            /* Does the Printer exist? */
-            if (RegCreateKeyW(HKEY_LOCAL_MACHINE, printersW, &hkeyPrinters) != ERROR_SUCCESS) {
-                ERR("Can't create Printers key\n");
-                printer_free(printer);
-                SetLastError(ERROR_INVALID_PRINTER_NAME);
-                printer = NULL;
-                goto end;
-            }
-            if (RegOpenKeyW(hkeyPrinters, printername, &hkeyPrinter) != ERROR_SUCCESS) {
-                WARN("Printer not found in Registry: %s\n", debugstr_w(printername));
-                RegCloseKey(hkeyPrinters);
-                printer_free(printer);
-                SetLastError(ERROR_INVALID_PRINTER_NAME);
-                printer = NULL;
-                goto end;
-            }
-            RegCloseKey(hkeyPrinter);
-            RegCloseKey(hkeyPrinters);
-        }
-    }
-    else
-    {
-        TRACE("using the local printserver\n");
-    }
-
-end:
-
-    TRACE("==> %p\n", printer);
-    return (HANDLE)printer;
-}
-
-static inline WCHAR *get_file_part( WCHAR *name )
-{
-    WCHAR *ptr = strrchrW( name, '\\' );
-    if (ptr) return ptr + 1;
-    return name;
-}
-
-/******************************************************************************
- *  myAddPrinterDriverEx [internal]
- *
- * Install a Printer Driver with the Option to upgrade / downgrade the Files
- * and a special mode with lazy error checking.
- *
- */
-static BOOL myAddPrinterDriverEx(DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags, BOOL lazy)
-{
-    const printenv_t *env;
-    apd_data_t apd;
-    DRIVER_INFO_8W di;
-    BOOL    (WINAPI *pDrvDriverEvent)(DWORD, DWORD, LPBYTE, LPARAM);
-    HMODULE hui;
-    WCHAR *file;
-    HKEY    hroot;
-    HKEY    hdrv;
-    DWORD   disposition;
-    DWORD   len;
-    LONG    lres;
-    BOOL    res;
-
-    /* we need to set all entries in the Registry, independent from the Level of
-       DRIVER_INFO, that the caller supplied */
-
-    ZeroMemory(&di, sizeof(di));
-    if (pDriverInfo && (level < (sizeof(di_sizeof) / sizeof(di_sizeof[0])))) {
-        memcpy(&di, pDriverInfo, di_sizeof[level]);
-    }
-
-    /* dump the most used infos */
-    TRACE("%p: .cVersion    : 0x%x/%d\n", pDriverInfo, di.cVersion, di.cVersion);
-    TRACE("%p: .pName       : %s\n", di.pName, debugstr_w(di.pName));
-    TRACE("%p: .pEnvironment: %s\n", di.pEnvironment, debugstr_w(di.pEnvironment));
-    TRACE("%p: .pDriverPath : %s\n", di.pDriverPath, debugstr_w(di.pDriverPath));
-    TRACE("%p: .pDataFile   : %s\n", di.pDataFile, debugstr_w(di.pDataFile));
-    TRACE("%p: .pConfigFile : %s\n", di.pConfigFile, debugstr_w(di.pConfigFile));
-    TRACE("%p: .pHelpFile   : %s\n", di.pHelpFile, debugstr_w(di.pHelpFile));
-    /* dump only the first of the additional Files */
-    TRACE("%p: .pDependentFiles: %s\n", di.pDependentFiles, debugstr_w(di.pDependentFiles));
-
-
-    /* check environment */
-    env = validate_envW(di.pEnvironment);
-    if (env == NULL) return FALSE;        /* ERROR_INVALID_ENVIRONMENT */
-
-    /* fill the copy-data / get the driverdir */
-    len = sizeof(apd.src) - sizeof(version3_subdirW) - sizeof(WCHAR);
-    if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
-                                    (LPBYTE) apd.src, len, &len)) {
-        /* Should never fail */
-        return FALSE;
-    }
-    memcpy(apd.dst, apd.src, len);
-    lstrcatW(apd.src, backslashW);
-    apd.srclen = lstrlenW(apd.src);
-    lstrcatW(apd.dst, env->versionsubdir);
-    lstrcatW(apd.dst, backslashW);
-    apd.dstlen = lstrlenW(apd.dst);
-    apd.copyflags = dwFileCopyFlags;
-    apd.lazy = lazy;
-    CreateDirectoryW(apd.src, NULL);
-    CreateDirectoryW(apd.dst, NULL);
-
-    hroot = open_driver_reg(env->envname);
-    if (!hroot) {
-        ERR("Can't create Drivers key\n");
-        return FALSE;
-    }
-
-    /* Fill the Registry for the Driver */
-    if ((lres = RegCreateKeyExW(hroot, di.pName, 0, NULL, REG_OPTION_NON_VOLATILE,
-                                KEY_WRITE | KEY_QUERY_VALUE, NULL,
-                                &hdrv, &disposition)) != ERROR_SUCCESS) {
-
-        ERR("can't create driver %s: %u\n", debugstr_w(di.pName), lres);
-        RegCloseKey(hroot);
-        SetLastError(lres);
-        return FALSE;
-    }
-    RegCloseKey(hroot);
-
-    /* Verified with the Adobe PS Driver, that w2k does not use di.Version */
-    RegSetValueExW(hdrv, versionW, 0, REG_DWORD, (const BYTE*) &env->driverversion,
-                   sizeof(DWORD));
-
-    file = get_file_part( di.pDriverPath );
-    RegSetValueExW( hdrv, driverW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
-    apd_copyfile( di.pDriverPath, file, &apd );
-
-    file = get_file_part( di.pDataFile );
-    RegSetValueExW( hdrv, data_fileW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
-    apd_copyfile( di.pDataFile, file, &apd );
-
-    file = get_file_part( di.pConfigFile );
-    RegSetValueExW( hdrv, configuration_fileW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
-    apd_copyfile( di.pConfigFile, file, &apd );
-
-    /* settings for level 3 */
-    if (di.pHelpFile)
-    {
-        file = get_file_part( di.pHelpFile );
-        RegSetValueExW( hdrv, help_fileW, 0, REG_SZ, (LPBYTE)file, (strlenW( file ) + 1) * sizeof(WCHAR) );
-        apd_copyfile( di.pHelpFile, file, &apd );
-    }
-    else
-        RegSetValueExW( hdrv, help_fileW, 0, REG_SZ, (const BYTE*)emptyW, sizeof(emptyW) );
-
-    if (di.pDependentFiles && *di.pDependentFiles)
-    {
-        WCHAR *reg, *reg_ptr, *in_ptr;
-        reg = reg_ptr = HeapAlloc( GetProcessHeap(), 0, multi_sz_lenW( di.pDependentFiles ) );
-
-        for (in_ptr = di.pDependentFiles; *in_ptr; in_ptr += strlenW( in_ptr ) + 1)
-        {
-            file = get_file_part( in_ptr );
-            len = strlenW( file ) + 1;
-            memcpy( reg_ptr, file, len * sizeof(WCHAR) );
-            reg_ptr += len;
-            apd_copyfile( in_ptr, file, &apd );
-        }
-        *reg_ptr = 0;
-
-        RegSetValueExW( hdrv, dependent_filesW, 0, REG_MULTI_SZ, (LPBYTE)reg, (reg_ptr - reg + 1) * sizeof(WCHAR) );
-        HeapFree( GetProcessHeap(), 0, reg );
-    }
-    else
-        RegSetValueExW(hdrv, dependent_filesW, 0, REG_MULTI_SZ, (const BYTE*)emptyW, sizeof(emptyW));
-
-    /* The language-Monitor was already copied by the caller to "%SystemRoot%\system32" */
-    if (di.pMonitorName)
-        RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (LPBYTE) di.pMonitorName,
-                       (lstrlenW(di.pMonitorName)+1)* sizeof(WCHAR));
-    else
-        RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (const BYTE*)emptyW, sizeof(emptyW));
-
-    if (di.pDefaultDataType)
-        RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (LPBYTE) di.pDefaultDataType,
-                       (lstrlenW(di.pDefaultDataType)+1)* sizeof(WCHAR));
-    else
-        RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (const BYTE*)emptyW, sizeof(emptyW));
-
-    /* settings for level 4 */
-    if (di.pszzPreviousNames)
-        RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (LPBYTE) di.pszzPreviousNames,
-                       multi_sz_lenW(di.pszzPreviousNames));
-    else
-        RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (const BYTE*)emptyW, sizeof(emptyW));
-
-    if (level > 5) TRACE("level %u for Driver %s is incomplete\n", level, debugstr_w(di.pName));
-
-    RegCloseKey(hdrv);
-    hui = driver_load(env, di.pConfigFile);
-    pDrvDriverEvent = (void *)GetProcAddress(hui, "DrvDriverEvent");
-    if (hui && pDrvDriverEvent) {
-
-        /* Support for DrvDriverEvent is optional */
-        TRACE("DRIVER_EVENT_INITIALIZE for %s (%s)\n", debugstr_w(di.pName), debugstr_w(di.pConfigFile));
-        /* MSDN: level for DRIVER_INFO is 1 to 3 */
-        res = pDrvDriverEvent(DRIVER_EVENT_INITIALIZE, 3, (LPBYTE) &di, 0);
-        TRACE("got %d from DRIVER_EVENT_INITIALIZE\n", res);
-    }
-    FreeLibrary(hui);
-
-    TRACE("=> TRUE with %u\n", GetLastError());
-    return TRUE;
-
-}
-
-/******************************************************************************
- * fpAddMonitor [exported through PRINTPROVIDOR]
- *
- * 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")
- *
- */
-static BOOL WINAPI fpAddMonitor(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
-{
-    monitor_t * pm = NULL;
-    LPMONITOR_INFO_2W mi2w;
-    HKEY    hroot = NULL;
-    HKEY    hentry = NULL;
-    DWORD   disposition;
-    BOOL    res = FALSE;
-
-    mi2w = (LPMONITOR_INFO_2W) pMonitors;
-    TRACE("(%s, %d, %p): %s %s %s\n", debugstr_w(pName), Level, pMonitors,
-            debugstr_w(mi2w ? mi2w->pName : NULL),
-            debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
-            debugstr_w(mi2w ? mi2w->pDLLName : NULL));
-
-    if (copy_servername_from_name(pName, NULL)) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_ACCESS_DENIED);
-        return FALSE;
-    }
-
-    if (!mi2w->pName || (! mi2w->pName[0])) {
-        WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-    if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, x86_envnameW)) {
-        WARN("Environment %s requested (we support only %s)\n",
-                debugstr_w(mi2w->pEnvironment), debugstr_w(x86_envnameW));
-        SetLastError(ERROR_INVALID_ENVIRONMENT);
-        return FALSE;
-    }
-
-    if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
-        WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-
-    /* Load and initialize the monitor. SetLastError() is called on failure */
-    if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
-        return FALSE;
-    }
-    monitor_unload(pm);
-
-    if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
-        ERR("unable to create key %s\n", debugstr_w(monitorsW));
-        return FALSE;
-    }
-
-    if (RegCreateKeyExW(hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
-                        KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
-                        &disposition) == ERROR_SUCCESS) {
-
-        /* Some installers set options for the port before calling AddMonitor.
-           We query the "Driver" entry to verify that the monitor is installed,
-           before we return an error.
-           When a user installs two print monitors at the same time with the
-           same name, a race condition is possible but silently ignored. */
-
-        DWORD   namesize = 0;
-
-        if ((disposition == REG_OPENED_EXISTING_KEY) &&
-            (RegQueryValueExW(hentry, driverW, NULL, NULL, NULL,
-                              &namesize) == ERROR_SUCCESS)) {
-            TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
-            /* 9x use ERROR_ALREADY_EXISTS */
-            SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
-        }
-        else
-        {
-            INT len;
-            len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
-            res = (RegSetValueExW(hentry, driverW, 0, REG_SZ,
-                    (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
-        }
-        RegCloseKey(hentry);
-    }
-
-    RegCloseKey(hroot);
-    return (res);
-}
-
-/******************************************************************************
- * fpAddPort [exported through PRINTPROVIDOR]
- *
- * Add a Port for a specific Monitor
- *
- * PARAMS
- *  pName        [I] Servername or NULL (local Computer)
- *  hWnd         [I] Handle to parent Window for the Dialog-Box
- *  pMonitorName [I] Name of the Monitor that manage the Port
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- */
-static BOOL WINAPI fpAddPort(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
-{
-    monitor_t * pm;
-    monitor_t * pui;
-    LONG        lres;
-    DWORD       res;
-
-    TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
-
-    lres = copy_servername_from_name(pName, NULL);
-    if (lres) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-
-    /* an empty Monitorname is Invalid */
-    if (!pMonitorName[0]) {
-        SetLastError(ERROR_NOT_SUPPORTED);
-        return FALSE;
-    }
-
-    pm = monitor_load(pMonitorName, NULL);
-    if (pm && pm->monitor && pm->monitor->pfnAddPort) {
-        res = pm->monitor->pfnAddPort(pName, hWnd, pMonitorName);
-        TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pm->dllname));
-    }
-    else
-    {
-        pui = monitor_loadui(pm);
-        if (pui && pui->monitorUI && pui->monitorUI->pfnAddPortUI) {
-            res = pui->monitorUI->pfnAddPortUI(pName, hWnd, pMonitorName, NULL);
-            TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pui->dllname));
-        }
-        else
-        {
-            FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
-                    debugstr_w(pMonitorName), pm, debugstr_w(pm ? pm->dllname : NULL),
-                    pui, debugstr_w(pui ? pui->dllname : NULL));
-
-            SetLastError(ERROR_NOT_SUPPORTED);
-            res = FALSE;
-        }
-        monitor_unload(pui);
-    }
-    monitor_unload(pm);
-
-    TRACE("returning %d with %u\n", res, GetLastError());
-    return res;
-}
-
-/******************************************************************************
- * fpAddPortEx [exported through PRINTPROVIDOR]
- *
- * Add a Port for a specific Monitor, without presenting a user interface
- *
- * PARAMS
- *  pName         [I] Servername or NULL (local Computer)
- *  level         [I] Structure-Level (1 or 2) for pBuffer
- *  pBuffer       [I] PTR to: PORT_INFO_1 or PORT_INFO_2
- *  pMonitorName  [I] Name of the Monitor that manage the Port
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- */
-static BOOL WINAPI fpAddPortEx(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
-{
-    PORT_INFO_2W * pi2;
-    monitor_t * pm;
-    DWORD lres;
-    DWORD res;
-
-    pi2 = (PORT_INFO_2W *) pBuffer;
-
-    TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
-            debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
-            debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
-            debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
-
-    lres = copy_servername_from_name(pName, NULL);
-    if (lres) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-
-    if ((level < 1) || (level > 2)) {
-        SetLastError(ERROR_INVALID_LEVEL);
-        return FALSE;
-    }
-
-    if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-
-    /* load the Monitor */
-    pm = monitor_load(pMonitorName, NULL);
-    if (pm && pm->monitor && pm->monitor->pfnAddPortEx) {
-        res = pm->monitor->pfnAddPortEx(pName, level, pBuffer, pMonitorName);
-        TRACE("got %d with %u (%s)\n", res, GetLastError(), debugstr_w(pm->dllname));
-    }
-    else
-    {
-        FIXME("not implemented for %s (monitor %p: %s)\n",
-            debugstr_w(pMonitorName), pm, pm ? debugstr_w(pm->dllname) : "(null)");
-            SetLastError(ERROR_INVALID_PARAMETER);
-            res = FALSE;
-    }
-    monitor_unload(pm);
-    return res;
-}
-
-/******************************************************************************
- * fpAddPrinterDriverEx [exported through PRINTPROVIDOR]
- *
- * 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
- *
- */
-static BOOL WINAPI fpAddPrinterDriverEx(LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
-{
-    LONG lres;
-
-    TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
-    lres = copy_servername_from_name(pName, NULL);
-    if (lres) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_ACCESS_DENIED);
-        return FALSE;
-    }
-
-    if ((dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY) != APD_COPY_ALL_FILES) {
-        TRACE("Flags 0x%x ignored (using APD_COPY_ALL_FILES)\n", dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY);
-    }
-
-    return myAddPrinterDriverEx(level, pDriverInfo, dwFileCopyFlags, TRUE);
-}
-
-/******************************************************************************
- * fpClosePrinter [exported through PRINTPROVIDOR]
- *
- * Close a printer handle and free associated resources
- *
- * PARAMS
- *  hPrinter [I] Printerhandle to close
- *
- * RESULTS
- *  Success: TRUE
- *  Failure: FALSE
- *
- */
-static BOOL WINAPI fpClosePrinter(HANDLE hPrinter)
-{
-    printer_t *printer = (printer_t *) hPrinter;
-
-    TRACE("(%p)\n", hPrinter);
-
-    if (printer) {
-        printer_free(printer);
-        return TRUE;
-    }
-    return FALSE;
-}
-
-/******************************************************************************
- * fpConfigurePort [exported through PRINTPROVIDOR]
- *
- * Display the Configuration-Dialog for a specific Port
- *
- * PARAMS
- *  pName     [I] Servername or NULL (local Computer)
- *  hWnd      [I] Handle to parent Window for the Dialog-Box
- *  pPortName [I] Name of the Port, that should be configured
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- */
-static BOOL WINAPI fpConfigurePort(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
-{
-    monitor_t * pm;
-    monitor_t * pui;
-    LONG        lres;
-    DWORD       res;
-
-    TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
-
-    lres = copy_servername_from_name(pName, NULL);
-    if (lres) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_INVALID_NAME);
-        return FALSE;
-    }
-
-    /* an empty Portname is Invalid, but can popup a Dialog */
-    if (!pPortName[0]) {
-        SetLastError(ERROR_NOT_SUPPORTED);
-        return FALSE;
-    }
-
-    pm = monitor_load_by_port(pPortName);
-    if (pm && pm->monitor && pm->monitor->pfnConfigurePort) {
-        TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm->name),
-                debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
-        res = pm->monitor->pfnConfigurePort(pName, hWnd, pPortName);
-        TRACE("got %d with %u\n", res, GetLastError());
-    }
-    else
-    {
-        pui = monitor_loadui(pm);
-        if (pui && pui->monitorUI && pui->monitorUI->pfnConfigurePortUI) {
-            TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui->name),
-                        debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
-            res = pui->monitorUI->pfnConfigurePortUI(pName, hWnd, pPortName);
-            TRACE("got %d with %u\n", res, GetLastError());
-        }
-        else
-        {
-            FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
-                    debugstr_w(pPortName), pm, debugstr_w(pm ? pm->dllname : NULL),
-                    pui, debugstr_w(pui ? pui->dllname : NULL));
-
-            SetLastError(ERROR_NOT_SUPPORTED);
-            res = FALSE;
-        }
-        monitor_unload(pui);
-    }
-    monitor_unload(pm);
-
-    TRACE("returning %d with %u\n", res, GetLastError());
-    return res;
-}
-
-/******************************************************************
- * fpDeleteMonitor [exported through PRINTPROVIDOR]
- *
- * 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
- *
- * NOTES
- *  pEnvironment is ignored in Windows for the local Computer.
- *
- */
-
-static BOOL WINAPI fpDeleteMonitor(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
-{
-    HKEY    hroot = NULL;
-    LONG    lres;
-
-    TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
-           debugstr_w(pMonitorName));
-
-    lres = copy_servername_from_name(pName, NULL);
-    if (lres) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_INVALID_NAME);
-        return FALSE;
-    }
-
-    /*  pEnvironment is ignored in Windows for the local Computer */
-    if (!pMonitorName || !pMonitorName[0]) {
-        TRACE("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-
-    if(RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
-        ERR("unable to create key %s\n", debugstr_w(monitorsW));
-        return FALSE;
-    }
-
-    if(RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS) {
-        TRACE("%s deleted\n", debugstr_w(pMonitorName));
-        RegCloseKey(hroot);
-        return TRUE;
-    }
-
-    TRACE("%s does not exist\n", debugstr_w(pMonitorName));
-    RegCloseKey(hroot);
-
-    /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
-    SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
-    return FALSE;
-}
-
-/*****************************************************************************
- * fpDeletePort [exported through PRINTPROVIDOR]
- *
- * Delete a specific Port
- *
- * PARAMS
- *  pName     [I] Servername or NULL (local Computer)
- *  hWnd      [I] Handle to parent Window for the Dialog-Box
- *  pPortName [I] Name of the Port, that should be deleted
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- */
-static BOOL WINAPI fpDeletePort(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
-{
-    monitor_t * pm;
-    monitor_t * pui;
-    LONG        lres;
-    DWORD       res;
-
-    TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
-
-    lres = copy_servername_from_name(pName, NULL);
-    if (lres) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_INVALID_NAME);
-        return FALSE;
-    }
-
-    /* an empty Portname is Invalid */
-    if (!pPortName[0]) {
-        SetLastError(ERROR_NOT_SUPPORTED);
-        return FALSE;
-    }
-
-    pm = monitor_load_by_port(pPortName);
-    if (pm && pm->monitor && pm->monitor->pfnDeletePort) {
-        TRACE("use %s for %s (monitor %p: %s)\n", debugstr_w(pm->name),
-                debugstr_w(pPortName), pm, debugstr_w(pm->dllname));
-        res = pm->monitor->pfnDeletePort(pName, hWnd, pPortName);
-        TRACE("got %d with %u\n", res, GetLastError());
-    }
-    else
-    {
-        pui = monitor_loadui(pm);
-        if (pui && pui->monitorUI && pui->monitorUI->pfnDeletePortUI) {
-            TRACE("use %s for %s (monitorui %p: %s)\n", debugstr_w(pui->name),
-                        debugstr_w(pPortName), pui, debugstr_w(pui->dllname));
-            res = pui->monitorUI->pfnDeletePortUI(pName, hWnd, pPortName);
-            TRACE("got %d with %u\n", res, GetLastError());
-        }
-        else
-        {
-            FIXME("not implemented for %s (monitor %p: %s / monitorui %p: %s)\n",
-                    debugstr_w(pPortName), pm, debugstr_w(pm ? pm->dllname : NULL),
-                    pui, debugstr_w(pui ? pui->dllname : NULL));
-
-            SetLastError(ERROR_NOT_SUPPORTED);
-            res = FALSE;
-        }
-        monitor_unload(pui);
-    }
-    monitor_unload(pm);
-
-    TRACE("returning %d with %u\n", res, GetLastError());
-    return res;
-}
-
-/*****************************************************************************
- * fpEnumMonitors [exported through PRINTPROVIDOR]
- *
- * Enumerate available Port-Monitors
- *
- * PARAMS
- *  pName      [I] Servername or NULL (local Computer)
- *  Level      [I] Structure-Level (1:Win9x+NT or 2:NT only)
- *  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
- *
- * NOTES
- *  Windows reads the Registry once and cache the Results.
- *
- */
-static BOOL WINAPI fpEnumMonitors(LPWSTR pName, DWORD Level, LPBYTE pMonitors, DWORD cbBuf,
-                                  LPDWORD pcbNeeded, LPDWORD pcReturned)
-{
-    DWORD   numentries = 0;
-    DWORD   needed = 0;
-    LONG    lres;
-    BOOL    res = FALSE;
-
-    TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
-          cbBuf, pcbNeeded, pcReturned);
-
-    lres = copy_servername_from_name(pName, NULL);
-    if (lres) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_INVALID_NAME);
-        goto em_cleanup;
-    }
-
-    if (!Level || (Level > 2)) {
-        WARN("level (%d) is ignored in win9x\n", Level);
-        SetLastError(ERROR_INVALID_LEVEL);
-        return FALSE;
-    }
-
-    /* Scan all Monitor-Keys */
-    numentries = 0;
-    needed = get_local_monitors(Level, NULL, 0, &numentries);
-
-    /* we calculated the needed buffersize. now do more error-checks */
-    if (cbBuf < needed) {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        goto em_cleanup;
-    }
-
-    /* fill the Buffer with the Monitor-Keys */
-    needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
-    res = TRUE;
-
-em_cleanup:
-    if (pcbNeeded)  *pcbNeeded = needed;
-    if (pcReturned) *pcReturned = numentries;
-
-    TRACE("returning %d with %d (%d byte for %d entries)\n",
-            res, GetLastError(), needed, numentries);
-
-    return (res);
-}
-
-/******************************************************************************
- * fpEnumPorts [exported through PRINTPROVIDOR]
- *
- * 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
- *
- */
-static BOOL WINAPI fpEnumPorts(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
-                               LPDWORD pcbNeeded, LPDWORD pcReturned)
-{
-    DWORD   needed = 0;
-    DWORD   numentries = 0;
-    LONG    lres;
-    BOOL    res = FALSE;
-
-    TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
-          cbBuf, pcbNeeded, pcReturned);
-
-    lres = copy_servername_from_name(pName, NULL);
-    if (lres) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_INVALID_NAME);
-        goto emP_cleanup;
-    }
-
-    if (!Level || (Level > 2)) {
-        SetLastError(ERROR_INVALID_LEVEL);
-        goto emP_cleanup;
-    }
-
-    if (!pcbNeeded || (!pPorts && (cbBuf > 0))) {
-        SetLastError(RPC_X_NULL_REF_POINTER);
-        goto emP_cleanup;
-    }
-
-    EnterCriticalSection(&monitor_handles_cs);
-    monitor_loadall();
-
-    /* Scan all local Ports */
-    numentries = 0;
-    needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
-
-    /* we calculated the needed buffersize. now do the error-checks */
-    if (cbBuf < needed) {
-        monitor_unloadall();
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        goto emP_cleanup_cs;
-    }
-    else if (!pPorts || !pcReturned) {
-        monitor_unloadall();
-        SetLastError(RPC_X_NULL_REF_POINTER);
-        goto emP_cleanup_cs;
-    }
-
-    /* Fill the Buffer */
-    needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
-    res = TRUE;
-    monitor_unloadall();
-
-emP_cleanup_cs:
-    LeaveCriticalSection(&monitor_handles_cs);
-
-emP_cleanup:
-    if (pcbNeeded)  *pcbNeeded = needed;
-    if (pcReturned) *pcReturned = (res) ? numentries : 0;
-
-    TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
-          (res), GetLastError(), needed, (res) ? numentries : 0, numentries);
-
-    return (res);
-}
-
-/*****************************************************************************
- * fpEnumPrintProcessors [exported through PRINTPROVIDOR]
- *
- * Enumerate available Print Processors
- *
- * PARAMS
- *  pName        [I] Servername or NULL (local Computer)
- *  pEnvironment [I] Printing-Environment or NULL (Default)
- *  Level        [I] Structure-Level (Only 1 is allowed)
- *  pPPInfo      [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 pPPInfo
- *  pcReturned   [O] PTR to DWORD that receives the number of Print Processors in pPPInfo
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE and in pcbNeeded the Bytes required for pPPInfo, if cbBuf is too small
- *
- */
-static BOOL WINAPI fpEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
-                            LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
-{
-    const printenv_t * env;
-    LPWSTR  regpathW = NULL;
-    DWORD   numentries = 0;
-    DWORD   needed = 0;
-    LONG    lres;
-    BOOL    res = FALSE;
-
-    TRACE("(%s, %s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), debugstr_w(pEnvironment),
-                                Level, pPPInfo, cbBuf, pcbNeeded, pcReturned);
-
-    lres = copy_servername_from_name(pName, NULL);
-    if (lres) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(ERROR_INVALID_NAME);
-        goto epp_cleanup;
-    }
-
-    if (Level != 1) {
-        SetLastError(ERROR_INVALID_LEVEL);
-        goto epp_cleanup;
-    }
-
-    env = validate_envW(pEnvironment);
-    if (!env)
-        goto epp_cleanup;   /* ERROR_INVALID_ENVIRONMENT */
-
-    regpathW = heap_alloc(sizeof(fmt_printprocessorsW) +
-                            (lstrlenW(env->envname) * sizeof(WCHAR)));
-
-    if (!regpathW)
-        goto epp_cleanup;
-
-    wsprintfW(regpathW, fmt_printprocessorsW, env->envname);
-
-    /* Scan all Printprocessor-Keys */
-    numentries = 0;
-    needed = get_local_printprocessors(regpathW, NULL, 0, &numentries);
-
-    /* we calculated the needed buffersize. now do more error-checks */
-    if (cbBuf < needed) {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        goto epp_cleanup;
-    }
-
-    /* fill the Buffer with the Printprocessor Infos */
-    needed = get_local_printprocessors(regpathW, pPPInfo, cbBuf, &numentries);
-    res = TRUE;
-
-epp_cleanup:
-    heap_free(regpathW);
-    if (pcbNeeded)  *pcbNeeded = needed;
-    if (pcReturned) *pcReturned = numentries;
-
-    TRACE("returning %d with %d (%d byte for %d entries)\n",
-            res, GetLastError(), needed, numentries);
-
-    return (res);
-}
-
-/******************************************************************************
- * fpGetPrintProcessorDirectory [exported through PRINTPROVIDOR]
- *
- * Return the PATH for the Print-Processors
- *
- * PARAMS
- *  pName        [I] Servername or NULL (this computer)
- *  pEnvironment [I] Printing-Environment or NULL (Default)
- *  level        [I] Structure-Level (must be 1)
- *  pPPInfo      [O] PTR to Buffer that receives the Result
- *  cbBuf        [I] Size of Buffer at pPPInfo
- *  pcbNeeded    [O] PTR to DWORD that receives the size in Bytes used / required for pPPInfo
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE and in pcbNeeded the Bytes required for pPPInfo, if cbBuf is too small
- *
- *  Native Values returned in pPPInfo on Success for this computer:
- *| NT(Windows x64):    "%winsysdir%\\spool\\PRTPROCS\\x64"
- *| NT(Windows NT x86): "%winsysdir%\\spool\\PRTPROCS\\w32x86"
- *| NT(Windows 4.0):    "%winsysdir%\\spool\\PRTPROCS\\win40"
- *
- *  "%winsysdir%" is the Value from GetSystemDirectoryW()
- *
- */
-static BOOL WINAPI fpGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD level,
-                                                LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded)
-{
-    const printenv_t * env;
-    DWORD needed;
-    LONG  lres;
-
-    TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), debugstr_w(pEnvironment),
-                                        level, pPPInfo, cbBuf, pcbNeeded);
-
-    *pcbNeeded = 0;
-    lres = copy_servername_from_name(pName, NULL);
-    if (lres) {
-        FIXME("server %s not supported\n", debugstr_w(pName));
-        SetLastError(RPC_S_SERVER_UNAVAILABLE);
-        return FALSE;
-    }
-
-    env = validate_envW(pEnvironment);
-    if (!env)
-        return FALSE;   /* ERROR_INVALID_ENVIRONMENT */
-
-    /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
-    needed = GetSystemDirectoryW(NULL, 0);
-    /* add the Size for the Subdirectories */
-    needed += lstrlenW(spoolprtprocsW);
-    needed += lstrlenW(env->subdir);
-    needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
-
-    *pcbNeeded = needed;
-
-    if (needed > cbBuf) {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        return FALSE;
-    }
-
-    GetSystemDirectoryW((LPWSTR) pPPInfo, cbBuf/sizeof(WCHAR));
-    /* add the Subdirectories */
-    lstrcatW((LPWSTR) pPPInfo, spoolprtprocsW);
-    lstrcatW((LPWSTR) pPPInfo, env->subdir);
-    TRACE("==> %s\n", debugstr_w((LPWSTR) pPPInfo));
-    return TRUE;
-}
-
-/******************************************************************************
- * fpOpenPrinter [exported through PRINTPROVIDOR]
- *
- * Open a Printer / Printserver or a Printer-Object
- *
- * PARAMS
- *  lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
- *  pPrinter      [O] The resulting Handle is stored here
- *  pDefaults     [I] PTR to Default Printer Settings or NULL
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- * NOTES
- *  lpPrinterName is one of:
- *|  Printserver (NT only): "Servername" or NULL for the local Printserver
- *|  Printer: "PrinterName"
- *|  Printer-Object: "PrinterName,Job xxx"
- *|  XcvMonitor: "Servername,XcvMonitor MonitorName"
- *|  XcvPort: "Servername,XcvPort PortName"
- *
- *
- */
-static BOOL WINAPI fpOpenPrinter(LPWSTR lpPrinterName, HANDLE *pPrinter,
-                                 LPPRINTER_DEFAULTSW pDefaults)
-{
-
-    TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), pPrinter, pDefaults);
-
-    *pPrinter = printer_alloc_handle(lpPrinterName, pDefaults);
-
-    return (*pPrinter != 0);
-}
-
-/******************************************************************************
- * fpXcvData [exported through PRINTPROVIDOR]
- *
- * Execute commands in the Printmonitor DLL
- *
- * PARAMS
- *  hXcv            [i] Handle from fpOpenPrinter (with XcvMonitor or XcvPort)
- *  pszDataName     [i] Name of the command to execute
- *  pInputData      [i] Buffer for extra Input Data (needed only for some commands)
- *  cbInputData     [i] Size in Bytes of Buffer at pInputData
- *  pOutputData     [o] Buffer to receive additional Data (needed only for some commands)
- *  cbOutputData    [i] Size in Bytes of Buffer at pOutputData
- *  pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
- *  pdwStatus       [o] PTR to receive the win32 error code from the Printmonitor DLL
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- * NOTES
- *  Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
- *  The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
- *
- *  Minimal List of commands, that a Printmonitor DLL should support:
- *
- *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
- *| "AddPort"   : Add a Port
- *| "DeletePort": Delete a Port
- *
- *  Many Printmonitors support additional commands. Examples for localspl.dll:
- *  "GetDefaultCommConfig", "SetDefaultCommConfig",
- *  "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
- *
- */
-static BOOL WINAPI fpXcvData(HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
-                    DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
-                    PDWORD pcbOutputNeeded, PDWORD pdwStatus)
-{
-    printer_t *printer = (printer_t * ) hXcv;
-
-    TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
-          pInputData, cbInputData, pOutputData,
-          cbOutputData, pcbOutputNeeded, pdwStatus);
-
-    if (!printer || (!printer->hXcv)) {
-        SetLastError(ERROR_INVALID_HANDLE);
-        return FALSE;
-    }
-
-    if (!pcbOutputNeeded) {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-
-    if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
-        SetLastError(RPC_X_NULL_REF_POINTER);
-        return FALSE;
-    }
-
-    *pcbOutputNeeded = 0;
-
-    *pdwStatus = printer->pm->monitor->pfnXcvDataPort(printer->hXcv, pszDataName,
-            pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
-
-    return TRUE;
-}
-
-/*****************************************************
- *  setup_provider [internal]
- */
-void setup_provider(void)
-{
-    static const PRINTPROVIDOR backend = {
-        fpOpenPrinter,
-        NULL,   /* fpSetJob */
-        NULL,   /* fpGetJob */
-        NULL,   /* fpEnumJobs */
-        NULL,   /* fpAddPrinter */
-        NULL,   /* fpDeletePrinter */
-        NULL,   /* fpSetPrinter */
-        NULL,   /* fpGetPrinter */
-        NULL,   /* fpEnumPrinters */
-        NULL,   /* fpAddPrinterDriver */
-        NULL,   /* fpEnumPrinterDrivers */
-        NULL,   /* fpGetPrinterDriver */
-        fpGetPrinterDriverDirectory,
-        NULL,   /* fpDeletePrinterDriver */
-        NULL,   /* fpAddPrintProcessor */
-        fpEnumPrintProcessors,
-        fpGetPrintProcessorDirectory,
-        NULL,   /* fpDeletePrintProcessor */
-        NULL,   /* fpEnumPrintProcessorDatatypes */
-        NULL,   /* fpStartDocPrinter */
-        NULL,   /* fpStartPagePrinter */
-        NULL,   /* fpWritePrinter */
-        NULL,   /* fpEndPagePrinter */
-        NULL,   /* fpAbortPrinter */
-        NULL,   /* fpReadPrinter */
-        NULL,   /* fpEndDocPrinter */
-        NULL,   /* fpAddJob */
-        NULL,   /* fpScheduleJob */
-        NULL,   /* fpGetPrinterData */
-        NULL,   /* fpSetPrinterData */
-        NULL,   /* fpWaitForPrinterChange */
-        fpClosePrinter,
-        NULL,   /* fpAddForm */
-        NULL,   /* fpDeleteForm */
-        NULL,   /* fpGetForm */
-        NULL,   /* fpSetForm */
-        NULL,   /* fpEnumForms */
-        fpEnumMonitors,
-        fpEnumPorts,
-        fpAddPort,
-        fpConfigurePort,
-        fpDeletePort,
-        NULL,   /* fpCreatePrinterIC */
-        NULL,   /* fpPlayGdiScriptOnPrinterIC */
-        NULL,   /* fpDeletePrinterIC */
-        NULL,   /* fpAddPrinterConnection */
-        NULL,   /* fpDeletePrinterConnection */
-        NULL,   /* fpPrinterMessageBox */
-        fpAddMonitor,
-        fpDeleteMonitor,
-        NULL,   /* fpResetPrinter */
-        NULL,   /* fpGetPrinterDriverEx */
-        NULL,   /* fpFindFirstPrinterChangeNotification */
-        NULL,   /* fpFindClosePrinterChangeNotification */
-        fpAddPortEx,
-        NULL,   /* fpShutDown */
-        NULL,   /* fpRefreshPrinterChangeNotification */
-        NULL,   /* fpOpenPrinterEx */
-        NULL,   /* fpAddPrinterEx */
-        NULL,   /* fpSetPort */
-        NULL,   /* fpEnumPrinterData */
-        NULL,   /* fpDeletePrinterData */
-        NULL,   /* fpClusterSplOpen */
-        NULL,   /* fpClusterSplClose */
-        NULL,   /* fpClusterSplIsAlive */
-        NULL,   /* fpSetPrinterDataEx */
-        NULL,   /* fpGetPrinterDataEx */
-        NULL,   /* fpEnumPrinterDataEx */
-        NULL,   /* fpEnumPrinterKey */
-        NULL,   /* fpDeletePrinterDataEx */
-        NULL,   /* fpDeletePrinterKey */
-        NULL,   /* fpSeekPrinter */
-        NULL,   /* fpDeletePrinterDriverEx */
-        NULL,   /* fpAddPerMachineConnection */
-        NULL,   /* fpDeletePerMachineConnection */
-        NULL,   /* fpEnumPerMachineConnections */
-        fpXcvData,
-        fpAddPrinterDriverEx,
-        NULL,   /* fpSplReadPrinter */
-        NULL,   /* fpDriverUnloadComplete */
-        NULL,   /* fpGetSpoolFileInfo */
-        NULL,   /* fpCommitSpoolData */
-        NULL,   /* fpCloseSpoolFileHandle */
-        NULL,   /* fpFlushPrinter */
-        NULL,   /* fpSendRecvBidiData */
-        NULL    /* fpAddDriverCatalog */
-    };
-    pprovider = &backend;
-
-}
-
-/*****************************************************
- * InitializePrintProvidor     (localspl.@)
- *
- * Initialize the Printprovider
- *
- * PARAMS
- *  pPrintProvidor    [I] Buffer to fill with a struct PRINTPROVIDOR
- *  cbPrintProvidor   [I] Size of Buffer in Bytes
- *  pFullRegistryPath [I] Registry-Path for the Printprovidor
- *
- * RETURNS
- *  Success: TRUE and pPrintProvidor filled
- *  Failure: FALSE
- *
- * NOTES
- *  The RegistryPath should be:
- *  "System\CurrentControlSet\Control\Print\Providers\<providername>",
- *  but this Parameter is ignored in "localspl.dll".
- *
- */
-
-BOOL WINAPI InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor,
-                                    DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
-{
-
-    TRACE("(%p, %u, %s)\n", pPrintProvidor, cbPrintProvidor, debugstr_w(pFullRegistryPath));
-    memcpy(pPrintProvidor, pprovider,
-          (cbPrintProvidor < sizeof(PRINTPROVIDOR)) ? cbPrintProvidor : sizeof(PRINTPROVIDOR));
-
-    return TRUE;
-}
diff --git a/reactos/dll/win32/localspl/resource.h b/reactos/dll/win32/localspl/resource.h
deleted file mode 100644 (file)
index 6a961f9..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-/* ## Resource-ID ## */
-#define IDS_LOCALPORT       500
-#define IDS_LOCALMONITOR    507
-
-/* ## Reserved memorysize for the strings (in WCHAR) ## */
-#define IDS_LOCALMONITOR_MAXLEN 64
-#define IDS_LOCALPORT_MAXLEN 32
diff --git a/reactos/dll/win32/ntprint/CMakeLists.txt b/reactos/dll/win32/ntprint/CMakeLists.txt
deleted file mode 100644 (file)
index 3e5809b..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-
-include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine)
-add_definitions(-D__WINESRC__)
-spec2def(ntprint.dll ntprint.spec)
-
-list(APPEND SOURCE
-    ntprint.c
-    ntprint.rc
-    ${CMAKE_CURRENT_BINARY_DIR}/ntprint_stubs.c
-    ${CMAKE_CURRENT_BINARY_DIR}/ntprint.def)
-
-add_library(ntprint SHARED ${SOURCE})
-set_module_type(ntprint win32dll)
-target_link_libraries(ntprint wine)
-add_importlibs(ntprint winspool msvcrt kernel32 ntdll)
-add_cd_file(TARGET ntprint DESTINATION reactos/system32 FOR all)
diff --git a/reactos/dll/win32/ntprint/ntprint.c b/reactos/dll/win32/ntprint/ntprint.c
deleted file mode 100644 (file)
index ec05d98..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Implementation of the Spooler Setup API (Printing)
- *
- * Copyright 2007 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
- */
-
-#define WIN32_NO_STATUS
-
-#include <stdarg.h>
-
-#define COBJMACROS
-#define NONAMELESSUNION
-
-#include <windef.h>
-#include <winbase.h>
-//#include "winerror.h"
-#include <wingdi.h>
-//#include "winnls.h"
-//#include "winver.h"
-#include <winspool.h>
-
-//#include "wine/unicode.h"
-#include <wine/debug.h>
-
-WINE_DEFAULT_DEBUG_CHANNEL(ntprint);
-
-typedef struct {
-  LPMONITOR_INFO_2W mi2;    /* Buffer for installed Monitors */
-  DWORD installed;          /* Number of installed Monitors */
-} monitorinfo_t;
-
-/*****************************************************
- *      DllMain
- */
-BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
-{
-    TRACE("(%p, %d, %p)\n",hinstDLL, fdwReason, lpvReserved);
-
-    switch(fdwReason)
-    {
-        case DLL_WINE_PREATTACH:
-            return FALSE;           /* prefer native version */
-
-        case DLL_PROCESS_ATTACH:
-            DisableThreadLibraryCalls( hinstDLL );
-            break;
-    }
-    return TRUE;
-}
-
-/*****************************************************
- *  PSetupCreateMonitorInfo  [NTPRINT.@]
- *
- *
- */
-
-HANDLE WINAPI PSetupCreateMonitorInfo(LPVOID unknown1, LPVOID  unknown2,LPVOID unknown3)
-{
-    monitorinfo_t * mi=NULL;
-    DWORD needed;
-    DWORD res;
-
-    TRACE("(%p, %p, %p)\n", unknown1, unknown2, unknown3);
-
-    if ((unknown2 != NULL) || (unknown3 != NULL)) {
-        FIXME("got unknown parameter: (%p, %p, %p)\n", unknown1, unknown2, unknown3);
-        return NULL;
-    }
-
-    mi = HeapAlloc(GetProcessHeap(), 0, sizeof(monitorinfo_t));
-    if (!mi) {
-        /* FIXME: SetLastError() needed? */
-        return NULL;
-    }
-
-    /* Get the needed size for all Monitors */
-    res = EnumMonitorsW(NULL, 2, NULL, 0, &needed, &mi->installed);
-    if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
-        mi->mi2 = HeapAlloc(GetProcessHeap(), 0, needed);
-        res = EnumMonitorsW(NULL, 2, (LPBYTE) mi->mi2, needed, &needed, &mi->installed);
-    }
-
-    if (!res) {
-        HeapFree(GetProcessHeap(), 0, mi);
-        /* FIXME: SetLastError() needed? */
-        return NULL;
-    }
-
-    TRACE("=> %p (%u monitors installed)\n", mi, mi->installed);
-    return mi;
-}
-
-/*****************************************************
- *  PSetupDestroyMonitorInfo  [NTPRINT.@]
- *
- */
-
-VOID WINAPI PSetupDestroyMonitorInfo(HANDLE monitorinfo)
-{
-    monitorinfo_t * mi = monitorinfo;
-
-    TRACE("(%p)\n", mi);
-    if (mi) {
-        if (mi->installed) HeapFree(GetProcessHeap(), 0, mi->mi2);
-        HeapFree(GetProcessHeap(), 0, mi);
-    }
-}
-
-/*****************************************************
- *  PSetupEnumMonitor  [NTPRINT.@]
- *
- * Copy the selected Monitorname to a buffer
- *
- * PARAMS
- *  monitorinfo [I]  HANDLE from PSetupCreateMonitorInfo
- *  index       [I]  Nr. of the Monitorname to copy
- *  buffer      [I]  Target, that receive the Monitorname
- *  psize       [IO] PTR to a DWORD that hold the size of the buffer and receive
- *                   the needed size, when the buffer is too small
- *
- * RETURNS
- *  Success:  TRUE
- *  Failure:  FALSE
- *
- * NOTES
- *   size is in Bytes on w2k and WCHAR on XP
- *
- */
-
-BOOL WINAPI PSetupEnumMonitor(HANDLE monitorinfo, DWORD index, LPWSTR buffer, LPDWORD psize)
-{
-    monitorinfo_t * mi = monitorinfo;
-    LPWSTR  nameW;
-    DWORD   len;
-
-    TRACE("(%p, %u, %p, %p) => %d\n", mi, index, buffer, psize, psize ? *psize : 0);
-
-    if (index < mi->installed) {
-        nameW = mi->mi2[index].pName;
-        len = lstrlenW(nameW) + 1;
-        if (len <= *psize) {
-            memcpy(buffer, nameW, len * sizeof(WCHAR));
-            TRACE("#%u: %s\n", index, debugstr_w(buffer));
-            return TRUE;
-        }
-        *psize = len;
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        return FALSE;
-    }
-    SetLastError(ERROR_NO_MORE_ITEMS);
-    return FALSE;
-}
diff --git a/reactos/dll/win32/ntprint/ntprint.rc b/reactos/dll/win32/ntprint/ntprint.rc
deleted file mode 100644 (file)
index 7cafb61..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Top level resource file for ntprint.dll
- *
- * Copyright 2007 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 "winver.h"
-
-#define WINE_FILENAME_STR "ntprint.dll"
-#define WINE_FILEDESCRIPTION_STR "Spooler Setup API (Printing)"
-
-/* Same Version as WinXP_sp2 */
-#define WINE_FILEVERSION      5,1,2600,2180
-#define WINE_FILEVERSION_STR "5.1.2600.2180"
-
-#define WINE_PRODUCTVERSION      5,1,2600,2180
-#define WINE_PRODUCTVERSION_STR "5.1.2600.2180"
-
-#include <wine/wine_common_ver.rc>
diff --git a/reactos/dll/win32/ntprint/ntprint.spec b/reactos/dll/win32/ntprint/ntprint.spec
deleted file mode 100644 (file)
index 8869776..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-@ stub ClassInstall32
-@ stub PSetupAssociateICMProfiles
-@ stub PSetupBuildDriversFromPath
-@ stub PSetupCreateDrvSetupPage
-@ stdcall PSetupCreateMonitorInfo(long ptr ptr)
-@ stub PSetupCreatePrinterDeviceInfoList
-@ stub PSetupDestroyDriverInfo3
-@ stdcall PSetupDestroyMonitorInfo(long)
-@ stub PSetupDestroyPrinterDeviceInfoList
-@ stub PSetupDestroySelectedDriverInfo
-@ stub PSetupDriverInfoFromName
-@ stdcall PSetupEnumMonitor(long long ptr ptr)
-@ stub PSetupFreeDrvField
-@ stub PSetupGetDriverInfForPrinter
-@ stub PSetupGetDriverInfo3
-@ stub PSetupGetLocalDataField
-@ stub PSetupGetPathToSearch
-@ stub PSetupGetSelectedDriverInfo
-@ stub PSetupInstallICMProfiles
-@ stub PSetupInstallMonitor
-@ stub PSetupInstallPrinterDriver
-@ stub PSetupInstallPrinterDriverFromTheWeb
-@ stub PSetupIsCompatibleDriver
-@ stub PSetupIsDriverInstalled
-@ stub PSetupIsMonitorInstalled
-@ stub PSetupIsOemDriver
-@ stub PSetupIsTheDriverFoundInInfInstalled
-@ stub PSetupKillBadUserConnections
-@ stub PSetupPreSelectDriver
-@ stub PSetupProcessPrinterAdded
-@ stub PSetupRefreshDriverList
-@ stub PSetupSelectDeviceButtons
-@ stub PSetupSelectDriver
-@ stub PSetupSetSelectDevTitleAndInstructions
-@ stub PSetupThisPlatform
diff --git a/reactos/dll/win32/spoolss/CMakeLists.txt b/reactos/dll/win32/spoolss/CMakeLists.txt
deleted file mode 100644 (file)
index 219e85f..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-
-add_definitions(-D__WINESRC__)
-include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine)
-spec2def(spoolss.dll spoolss.spec ADD_IMPORTLIB)
-
-list(APPEND SOURCE
-    spoolss_main.c
-    router.c
-    spoolss.h
-    ${CMAKE_CURRENT_BINARY_DIR}/spoolss_stubs.c)
-
-add_library(spoolss SHARED
-    ${SOURCE}
-    ${CMAKE_CURRENT_BINARY_DIR}/spoolss.def)
-
-set_module_type(spoolss win32dll)
-target_link_libraries(spoolss wine)
-add_importlibs(spoolss msvcrt kernel32 ntdll)
-add_pch(spoolss spoolss.h SOURCE)
-add_cd_file(TARGET spoolss DESTINATION reactos/system32 FOR all)
diff --git a/reactos/dll/win32/spoolss/router.c b/reactos/dll/win32/spoolss/router.c
deleted file mode 100644 (file)
index 53a2d3f..0000000
+++ /dev/null
@@ -1,563 +0,0 @@
-/*
- * 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 "spoolss.h"
-
-#include <winreg.h>
-#include <wingdi.h>
-#include <winspool.h>
-
-#include <ddk/winsplp.h>
-
-/* ################################ */
-
-#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;
-
-    EnterCriticalSection(&backend_cs);
-
-    /* if we failed before, don't try again */
-    if (!failed && (used_backends == 0)) {
-        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);
-
-}
diff --git a/reactos/dll/win32/spoolss/spoolss.h b/reactos/dll/win32/spoolss/spoolss.h
deleted file mode 100644 (file)
index 80fae4d..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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
- */
-
-#ifndef _SPOOLSS_H_
-#define _SPOOLSS_H_
-
-#include <stdarg.h>
-
-#define WIN32_NO_STATUS
-#include <windef.h>
-#include <winbase.h>
-
-#include <wine/debug.h>
-WINE_DEFAULT_DEBUG_CHANNEL(spoolss);
-
-/* ################################ */
-
-BOOL backend_load_all(void) DECLSPEC_HIDDEN;
-void backend_unload_all(void) DECLSPEC_HIDDEN;
-
-/* ## Memory allocation functions ## */
-
-static inline void * __WINE_ALLOC_SIZE(1) heap_alloc( size_t len )
-{
-    return HeapAlloc( GetProcessHeap(), 0, len );
-}
-
-static inline void * __WINE_ALLOC_SIZE(1) heap_alloc_zero( size_t len )
-{
-    return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len );
-}
-
-static inline BOOL heap_free( void *mem )
-{
-    return HeapFree( GetProcessHeap(), 0, mem );
-}
-
-#endif /* _SPOOLSS_H_ */
diff --git a/reactos/dll/win32/spoolss/spoolss_main.c b/reactos/dll/win32/spoolss/spoolss_main.c
deleted file mode 100644 (file)
index 79aa77d..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Implementation of the Spooler-Service helper DLL
- *
- * Copyright 2006 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 "spoolss.h"
-
-/* ################################ */
-
-static HMODULE hwinspool;
-
-static const WCHAR winspooldrvW[] = {'w','i','n','s','p','o','o','l','.','d','r','v',0};
-
-/******************************************************************************
- *
- */
-BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
-{
-    TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
-
-    switch (fdwReason) {
-        case DLL_WINE_PREATTACH:
-            return FALSE;  /* prefer native version */
-        case DLL_PROCESS_ATTACH: {
-            DisableThreadLibraryCalls(hinstDLL);
-            break;
-
-        case DLL_PROCESS_DETACH:
-            if (lpvReserved) break;
-            backend_unload_all();
-            break;
-        }
-    }
-    return TRUE;
-}
-
-/******************************************************************
- *   AllocSplStr   [SPOOLSS.@]
- *
- * Create a copy from the String on the Spooler-Heap
- *
- * PARAMS
- *  pwstr [I] PTR to the String to copy
- *
- * RETURNS
- *  Failure: NULL
- *  Success: PTR to the copied String
- *
- */
-LPWSTR WINAPI AllocSplStr(LPCWSTR pwstr)
-{
-    LPWSTR  res = NULL;
-    DWORD   len;
-
-    TRACE("(%s)\n", debugstr_w(pwstr));
-    if (!pwstr) return NULL;
-
-    len = (lstrlenW(pwstr) + 1) * sizeof(WCHAR);
-    res = HeapAlloc(GetProcessHeap(), 0, len);
-    if (res) lstrcpyW(res, pwstr);
-        
-    TRACE("returning %p\n", res);
-    return res;
-}
-
-/******************************************************************
- *   BuildOtherNamesFromMachineName   [SPOOLSS.@]
- */
-BOOL WINAPI BuildOtherNamesFromMachineName(LPVOID * ptr1, LPVOID * ptr2)
-{
-    FIXME("(%p, %p) stub\n", ptr1, ptr2);
-
-    *ptr1 = NULL;
-    *ptr2 = NULL;
-    return FALSE;
-}
-
-/******************************************************************
- *   DllAllocSplMem   [SPOOLSS.@]
- *
- * Allocate cleared memory from the spooler heap
- *
- * PARAMS
- *  size [I] Number of bytes to allocate
- *
- * RETURNS
- *  Failure: NULL
- *  Success: PTR to the allocated memory
- *
- * NOTES
- *  We use the process heap (Windows use a separate spooler heap)
- *
- */
-LPVOID WINAPI DllAllocSplMem(DWORD size)
-{
-    LPVOID  res;
-
-    res = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
-    TRACE("(%d) => %p\n", size, res);
-    return res;
-}
-
-/******************************************************************
- *   DllFreeSplMem   [SPOOLSS.@]
- *
- * Free the allocated spooler memory
- *
- * PARAMS
- *  memory [I] PTR to the memory allocated by DllAllocSplMem
- *
- * RETURNS
- *  Failure: FALSE
- *  Success: TRUE
- *
- * NOTES
- *  We use the process heap (Windows use a separate spooler heap)
- *
- */
-
-BOOL WINAPI DllFreeSplMem(LPBYTE memory)
-{
-    TRACE("(%p)\n", memory);
-    return HeapFree(GetProcessHeap(), 0, memory);
-}
-
-/******************************************************************
- *   DllFreeSplStr   [SPOOLSS.@]
- *
- * Free the allocated Spooler-String
- *
- * PARAMS
- *  pwstr [I] PTR to the WSTR, allocated by AllocSplStr
- *
- * RETURNS
- *  Failure: FALSE
- *  Success: TRUE
- *
- */
-
-BOOL WINAPI DllFreeSplStr(LPWSTR pwstr)
-{
-    TRACE("(%s) PTR: %p\n", debugstr_w(pwstr), pwstr);
-    return HeapFree(GetProcessHeap(), 0, pwstr);
-}
-
-
-/******************************************************************
- *   ImpersonatePrinterClient   [SPOOLSS.@]
- */
-BOOL WINAPI ImpersonatePrinterClient(HANDLE hToken)
-{
-    FIXME("(%p) stub\n", hToken);
-    return TRUE;
-}
-
-/******************************************************************
- *   InitializeRouter   [SPOOLSS.@]
- */
-BOOL WINAPI InitializeRouter(void)
-{
-    TRACE("()\n");
-    return backend_load_all();
-}
-
-/******************************************************************
- *   IsLocalCall    [SPOOLSS.@]
- */
-BOOL WINAPI IsLocalCall(void)
-{
-    FIXME("() stub\n");
-    return TRUE;
-}
-
-/******************************************************************
- *   RevertToPrinterSelf   [SPOOLSS.@]
- */
-HANDLE WINAPI RevertToPrinterSelf(void)
-{
-    FIXME("() stub\n");
-    return (HANDLE) 0xdead0947;
-}
-
-/******************************************************************
- *   SplInitializeWinSpoolDrv   [SPOOLSS.@]
- *
- * Dynamic load "winspool.drv" and fill an array with some function-pointer
- *
- * PARAMS
- *  table  [I] array of function-pointer to fill
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- *
- * NOTES
- *  Native "spoolss.dll" from w2k fill the table with 11 Function-Pointer.
- *  We implement the XP-Version (The table has only 9 Pointer)
- *
- */
-BOOL WINAPI SplInitializeWinSpoolDrv(LPVOID * table)
-{
-    DWORD res;
-
-    TRACE("(%p)\n", table);
-
-    hwinspool = LoadLibraryW(winspooldrvW);
-    if (!hwinspool) return FALSE;
-
-    table[0] = (void *) GetProcAddress(hwinspool, "OpenPrinterW");
-    table[1] = (void *) GetProcAddress(hwinspool, "ClosePrinter");
-    table[2] = (void *) GetProcAddress(hwinspool, "SpoolerDevQueryPrintW");
-    table[3] = (void *) GetProcAddress(hwinspool, "SpoolerPrinterEvent");
-    table[4] = (void *) GetProcAddress(hwinspool, "DocumentPropertiesW");
-    table[5] = (void *) GetProcAddress(hwinspool, (LPSTR) 212);  /* LoadPrinterDriver */
-    table[6] = (void *) GetProcAddress(hwinspool, (LPSTR) 213);  /* RefCntLoadDriver */
-    table[7] = (void *) GetProcAddress(hwinspool, (LPSTR) 214);  /* RefCntUnloadDriver */
-    table[8] = (void *) GetProcAddress(hwinspool, (LPSTR) 215);  /* ForceUnloadDriver */
-
-    for (res = 0; res < 9; res++) {
-        if (table[res] == NULL) return FALSE;
-    }
-
-    return TRUE;
-
-}
-
-/******************************************************************
- *   SplIsUpgrade   [SPOOLSS.@]
- */
-BOOL WINAPI SplIsUpgrade(void)
-{
-    FIXME("() stub\n");
-    return FALSE;
-}
-
-/******************************************************************
- *   SpoolerHasInitialized  [SPOOLSS.@]
- */
-BOOL WINAPI SpoolerHasInitialized(void)
-{
-    FIXME("() stub\n");
-    return TRUE;
-}
-
-/******************************************************************
- *   SpoolerInit   [SPOOLSS.@]
- */
-BOOL WINAPI SpoolerInit(void)
-{
-    FIXME("() stub\n");
-    return TRUE;
-}
-
-/******************************************************************
- *   WaitForSpoolerInitialization   [SPOOLSS.@]
- */
-BOOL WINAPI WaitForSpoolerInitialization(void)
-{
-    FIXME("() stub\n");
-    return TRUE;
-}
diff --git a/reactos/dll/win32/winspool/CMakeLists.txt b/reactos/dll/win32/winspool/CMakeLists.txt
deleted file mode 100644 (file)
index 044c0ca..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-
-include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine)
-spec2def(winspool.drv winspool.spec ADD_IMPORTLIB)
-
-list(APPEND SOURCE
-    info.c
-    stubs.c
-    precomp.h)
-
-add_library(winspool.drv SHARED
-    ${SOURCE}
-    winspool.rc
-    ${CMAKE_CURRENT_BINARY_DIR}/winspool_stubs.c
-    ${CMAKE_CURRENT_BINARY_DIR}/winspool.def)
-
-set_target_properties(winspool.drv PROPERTIES SUFFIX "")
-set_module_type(winspool.drv win32dll UNICODE)
-target_link_libraries(winspool.drv wine)
-add_importlibs(winspool.drv advapi32 shlwapi msvcrt kernel32 ntdll)
-add_pch(winspool.drv precomp.h SOURCE)
-add_cd_file(TARGET winspool.drv DESTINATION reactos/system32 FOR all)
diff --git a/reactos/dll/win32/winspool/info.c b/reactos/dll/win32/winspool/info.c
deleted file mode 100644 (file)
index c12f162..0000000
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * WINSPOOL functions
- *
- * Copyright 1996 John Harvey
- * Copyright 1998 Andreas Mohr
- * Copyright 1999 Klaas van Gend
- * Copyright 1999, 2000 Huw D M Davies
- * Copyright 2001 Marcus Meissner
- *
- * 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 Street, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include "precomp.h"
-
-#include <winreg.h>
-#include <winnls.h>
-#include <shlwapi.h>
-
-/******************************************************************************
- *             GetDefaultPrinterA   (WINSPOOL.@)
- */
-BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
-{
-   char *ptr;
-
-   if (*namesize < 1)
-   {
-      SetLastError (ERROR_INSUFFICIENT_BUFFER);
-      return FALSE;
-   }
-
-   if (!GetProfileStringA ("windows", "device", "", name, *namesize))
-   {
-      SetLastError (ERROR_FILE_NOT_FOUND);
-      return FALSE;
-   }
-
-   if ((ptr = strchr (name, ',')) == NULL)
-   {
-      SetLastError (ERROR_FILE_NOT_FOUND);
-      return FALSE;
-   }
-
-   *ptr = '\0';
-   *namesize = strlen (name) + 1;
-   return TRUE;
-}
-
-
-/******************************************************************************
- *             GetDefaultPrinterW   (WINSPOOL.@)
- */
-BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
-{
-   char *buf;
-   BOOL  ret;
-
-   if (*namesize < 1)
-   {
-      SetLastError (ERROR_INSUFFICIENT_BUFFER);
-      return FALSE;
-   }
-
-   buf = HeapAlloc (GetProcessHeap (), 0, *namesize);
-   ret = GetDefaultPrinterA (buf, namesize);
-   if (ret)
-   {
-       DWORD len = MultiByteToWideChar (CP_ACP, 0, buf, -1, name, *namesize);
-       if (!len)
-       {
-           SetLastError (ERROR_INSUFFICIENT_BUFFER);
-           ret = FALSE;
-       }
-       else *namesize = len;
-   }
-
-   HeapFree (GetProcessHeap (), 0, buf);
-   return ret;
-}
-
-/******************************************************************************
- *             AddPrintProvidorA   (WINSPOOL.@)
- */
-BOOL
-WINAPI
-AddPrintProvidorA(LPSTR Name, DWORD Level, PBYTE Buffer)
-{
-   if (Name || Level > 2 || Buffer == NULL)
-   {
-      SetLastError(ERROR_INVALID_PARAMETER);
-      return FALSE;
-   }
-
-   if (Level == 1)
-   {
-      BOOL bRet;
-      PROVIDOR_INFO_1W Provider;
-      PROVIDOR_INFO_1A  *Prov = (PROVIDOR_INFO_1A*)Buffer;
-
-      if (Prov->pName == NULL || Prov->pDLLName == NULL || Prov->pEnvironment == NULL)
-      {
-         return FALSE;
-      }
-
-      Provider.pDLLName = HeapAlloc(GetProcessHeap(), 0, (strlen(Prov->pDLLName)+1) * sizeof(WCHAR));
-      if (Provider.pDLLName)
-      {
-          MultiByteToWideChar(CP_ACP, 0, Prov->pDLLName, -1, Provider.pDLLName, strlen(Prov->pDLLName)+1);
-          Provider.pDLLName[strlen(Prov->pDLLName)] = L'\0';
-      }
-
-      Provider.pEnvironment = HeapAlloc(GetProcessHeap(), 0, (strlen(Prov->pEnvironment)+1) * sizeof(WCHAR));
-      if (Provider.pEnvironment)
-      {
-          MultiByteToWideChar(CP_ACP, 0, Prov->pEnvironment, -1, Provider.pEnvironment, strlen(Prov->pEnvironment)+1);
-          Provider.pEnvironment[strlen(Prov->pEnvironment)] = L'\0';
-      }
-
-      Provider.pName = HeapAlloc(GetProcessHeap(), 0, (strlen(Prov->pName)+1) * sizeof(WCHAR));
-      if (Provider.pName)
-      {
-          MultiByteToWideChar(CP_ACP, 0, Prov->pName, -1, Provider.pName, strlen(Prov->pName)+1);
-          Provider.pName[strlen(Prov->pName)] = L'\0';
-      }
-
-      bRet = AddPrintProvidorW(NULL, Level, (LPBYTE)&Provider);
-
-      if (Provider.pDLLName)
-          HeapFree(GetProcessHeap(), 0, Provider.pDLLName);
-
-      if (Provider.pEnvironment)
-          HeapFree(GetProcessHeap(), 0, Provider.pEnvironment);
-
-      if (Provider.pName)
-          HeapFree(GetProcessHeap(), 0, Provider.pName);
-
-      return bRet;
-   }
-   else
-   {
-      PROVIDOR_INFO_2W Provider;
-      PROVIDOR_INFO_2A  *Prov = (PROVIDOR_INFO_2A*)Buffer;
-
-      Provider.pOrder = HeapAlloc(GetProcessHeap(), 0, (strlen(Prov->pOrder)+1) * sizeof(WCHAR));
-      if (Provider.pOrder)
-      {
-          BOOL bRet;
-          MultiByteToWideChar(CP_ACP, 0, Prov->pOrder, -1, Provider.pOrder, strlen(Prov->pOrder)+1);
-          Provider.pOrder[strlen(Prov->pOrder)] = L'\0';
-
-          bRet = AddPrintProvidorW(NULL, Level, (LPBYTE)&Provider);
-          HeapFree(GetProcessHeap(), 0, Provider.pOrder);
-          return bRet;
-      }
-   }
-
-  return FALSE;
-}
-
-
-/******************************************************************************
- *             AddPrintProvidorW   (WINSPOOL.@)
- */
-BOOL
-WINAPI
-AddPrintProvidorW(LPWSTR Name, DWORD Level, PBYTE Buffer)
-{
-   HKEY hKey;
-   LPWSTR pOrder;
-   DWORD dwSize, dwType;
-   BOOL bRet = FALSE;
-
-   if (Name || Level > 2 || Buffer == NULL)
-   {
-      SetLastError(ERROR_INVALID_PARAMETER);
-      return FALSE;
-   }
-
-
-   if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Print\\Providers", 0, KEY_READ | KEY_WRITE, &hKey) != ERROR_SUCCESS)
-   {
-      return FALSE;
-   }
-
-   if (RegQueryValueExW(hKey, L"Order", NULL, &dwType, NULL, &dwSize) != ERROR_SUCCESS || dwType != REG_MULTI_SZ)
-   {
-      RegCloseKey(hKey);
-      return FALSE;
-   }
-
-   pOrder = HeapAlloc(GetProcessHeap(), 0, dwSize);
-   if (!pOrder)
-   {
-      RegCloseKey(hKey);
-      return FALSE;
-   }
-
-   if (RegQueryValueExW(hKey, L"Order", NULL, &dwType, (LPBYTE)pOrder, &dwSize) != ERROR_SUCCESS || dwType != REG_MULTI_SZ)
-   {
-      RegCloseKey(hKey);
-      return FALSE;
-   }
-
-   if (Level == 1)
-   {
-      LPWSTR pBuffer;
-      BOOL bFound = FALSE;
-      PROVIDOR_INFO_1W * Prov = (PROVIDOR_INFO_1W*)Buffer;
-
-      if (Prov->pName == NULL || Prov->pDLLName == NULL || Prov->pEnvironment == NULL)
-      {
-         SetLastError(ERROR_INVALID_PARAMETER);
-         RegCloseKey(hKey);
-         return FALSE;
-      }
-
-      pBuffer = pOrder;
-
-      while(pBuffer[0])
-      {
-         if (!wcsicmp(pBuffer, Prov->pName))
-         {
-            bFound = TRUE;
-            break;
-         }
-         pBuffer += wcslen(pBuffer) + 1;
-      }
-
-      if (!bFound)
-      {
-         HKEY hSubKey;
-         DWORD dwFullSize = dwSize + (wcslen(Prov->pName)+1) * sizeof(WCHAR);
-
-         if (RegCreateKeyExW(hKey, Prov->pName, 0, NULL, 0, KEY_WRITE, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
-         {
-            RegSetValueExW(hSubKey, L"Name", 0, REG_SZ, (LPBYTE)Prov->pDLLName, (wcslen(Prov->pDLLName)+1) * sizeof(WCHAR));
-            RegCloseKey(hSubKey);
-         }
-
-         pBuffer = HeapAlloc(GetProcessHeap(), 0, dwFullSize);
-         if (pBuffer)
-         {
-             CopyMemory(pBuffer, pOrder, dwSize);
-             wcscpy(&pBuffer[(dwSize/sizeof(WCHAR))-1], Prov->pName);
-             pBuffer[(dwSize/sizeof(WCHAR)) + wcslen(Prov->pName)] = L'\0';
-             RegSetValueExW(hKey, L"Order", 0, REG_MULTI_SZ, (LPBYTE)pBuffer, dwFullSize);
-             HeapFree(GetProcessHeap(), 0, pBuffer);
-         }
-         bRet = TRUE;
-      }
-
-   }
-
-   RegCloseKey(hKey);
-   HeapFree(GetProcessHeap(), 0, pOrder);
-
-  return bRet;
-}
-
-/******************************************************************************
- *    DeletePrintProvidorA   (WINSPOOL.@)
- */
-BOOL
-WINAPI
-DeletePrintProvidorA(LPSTR Name, LPSTR Environment, LPSTR PrintProvidor)
-{
-   BOOL bRet;
-   LPWSTR Env, Prov;
-
-   if (Name || !Environment || !PrintProvidor)
-   {
-      SetLastError(ERROR_INVALID_PARAMETER);
-      return FALSE;
-   }
-
-   Env = HeapAlloc(GetProcessHeap(), 0, (strlen(Environment)+1) * sizeof(WCHAR));
-   if (!Env)
-   {
-      return FALSE;
-   }
-
-   MultiByteToWideChar(CP_ACP, 0, Environment, -1, Env, strlen(Environment)+1);
-   Env[strlen(Environment)] = L'\0';
-
-   Prov = HeapAlloc(GetProcessHeap(), 0, (strlen(PrintProvidor)+1) * sizeof(WCHAR));
-   if (!Prov)
-   {
-      HeapFree(GetProcessHeap(), 0, Env);
-      return FALSE;
-   }
-
-   MultiByteToWideChar(CP_ACP, 0, PrintProvidor, -1, Prov, strlen(PrintProvidor)+1);
-   Prov[strlen(PrintProvidor)] = L'\0';
-
-   bRet = DeletePrintProvidorW(NULL, Env, Prov);
-   HeapFree(GetProcessHeap(), 0, Env);
-   HeapFree(GetProcessHeap(), 0, Prov);
-
-  return bRet;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePrintProvidorW(LPWSTR Name, LPWSTR Environment, LPWSTR PrintProvidor)
-{
-   HKEY hKey;
-   BOOL bFound;
-   DWORD dwType, dwSize, dwOffset, dwLength;
-   LPWSTR pOrder, pBuffer, pNew;
-
-   if (Name || !Environment || !PrintProvidor)
-   {
-      SetLastError(ERROR_INVALID_PARAMETER);
-      return FALSE;
-   }
-
-   if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Print\\Providers", 0, KEY_READ | KEY_WRITE, &hKey) != ERROR_SUCCESS)
-   {
-      return FALSE;
-   }
-
-   if (RegQueryValueExW(hKey, L"Order", NULL, &dwType, NULL, &dwSize) != ERROR_SUCCESS || dwType != REG_MULTI_SZ)
-   {
-      RegCloseKey(hKey);
-      return FALSE;
-   }
-
-   pOrder = HeapAlloc(GetProcessHeap(), 0, dwSize);
-   if (!pOrder)
-   {
-      RegCloseKey(hKey);
-      return FALSE;
-   }
-
-   if (RegQueryValueExW(hKey, L"Order", NULL, &dwType, (LPBYTE)pOrder, &dwSize) != ERROR_SUCCESS || dwType != REG_MULTI_SZ)
-   {
-      RegCloseKey(hKey);
-      return FALSE;
-   }
-
-
-   pBuffer = pOrder;
-   bFound = FALSE;
-   while(pBuffer[0])
-   {
-       if (!wcsicmp(pBuffer, PrintProvidor))
-       {
-            bFound = TRUE;
-            break;
-         }
-         pBuffer += wcslen(pBuffer) + 1;
-   }
-
-   if (!bFound)
-   {
-      RegCloseKey(hKey);
-      HeapFree(GetProcessHeap(), 0, pOrder);
-      return FALSE;
-   }
-
-   pNew = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
-   if (!pNew)
-   {
-      RegCloseKey(hKey);
-      HeapFree(GetProcessHeap(), 0, pOrder);
-      return FALSE;
-   }
-
-   dwOffset = pBuffer - pOrder;
-   dwLength = (dwSize / sizeof(WCHAR)) - (dwOffset + wcslen(pBuffer) + 1);
-   CopyMemory(pNew, pOrder, dwOffset * sizeof(WCHAR));
-   CopyMemory(&pNew[dwOffset], pBuffer + wcslen(pBuffer) + 1, dwLength);
-
-   RegSetValueExW(hKey, L"Order", 0, REG_MULTI_SZ, (LPBYTE)pNew, (dwOffset + dwLength) * sizeof(WCHAR));
-   RegDeleteKey(hKey, PrintProvidor);
-
-   HeapFree(GetProcessHeap(), 0, pOrder);
-   HeapFree(GetProcessHeap(), 0, pNew);
-   RegCloseKey(hKey);
-
-   return TRUE;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddMonitorA(LPSTR Name, DWORD Level, PBYTE Monitors)
-{
-   LPWSTR szName = NULL;
-   MONITOR_INFO_2W Monitor;
-   MONITOR_INFO_2A *pMonitor;
-   BOOL bRet = FALSE;
-
-   if (Level != 2 || !Monitors)
-   {
-      SetLastError(ERROR_INVALID_PARAMETER);
-      return FALSE;
-   }
-
-   pMonitor = (MONITOR_INFO_2A*)Monitors;
-   if (pMonitor->pDLLName == NULL || pMonitor->pName == NULL)
-   {
-      SetLastError(ERROR_INVALID_PARAMETER);
-      return FALSE;
-   }
-
-   ZeroMemory(&Monitor, sizeof(Monitor));
-
-   if (Name)
-   {
-      szName = HeapAlloc(GetProcessHeap(), 0, (strlen(Name) + 1) * sizeof(WCHAR));
-      if (!szName)
-      {
-         return FALSE;
-      }
-      MultiByteToWideChar(CP_ACP, 0, Name, -1, szName, strlen(Name)+1);
-      szName[strlen(Name)] = L'\0';
-   }
-
-   Monitor.pDLLName = HeapAlloc(GetProcessHeap(), 0, (strlen(pMonitor->pDLLName)+1) * sizeof(WCHAR));
-   if (!Monitor.pDLLName)
-   {
-      goto cleanup;
-   }
-   MultiByteToWideChar(CP_ACP, 0, pMonitor->pDLLName, -1, Monitor.pDLLName, strlen(pMonitor->pDLLName)+1);
-   pMonitor->pDLLName[strlen(pMonitor->pDLLName)] = L'\0';
-
-   Monitor.pName = HeapAlloc(GetProcessHeap(), 0, (strlen(pMonitor->pName)+1) * sizeof(WCHAR));
-   if (!Monitor.pName)
-   {
-      goto cleanup;
-   }
-   MultiByteToWideChar(CP_ACP, 0, pMonitor->pName, -1, Monitor.pName, strlen(pMonitor->pName)+1);
-   pMonitor->pName[strlen(pMonitor->pName)] = L'\0';
-
-
-   if (pMonitor->pEnvironment)
-   {
-      Monitor.pEnvironment = HeapAlloc(GetProcessHeap(), 0, (strlen(pMonitor->pEnvironment)+1) * sizeof(WCHAR));
-      if (!Monitor.pEnvironment)
-      {
-         goto cleanup;
-      }
-      MultiByteToWideChar(CP_ACP, 0, pMonitor->pEnvironment, -1, Monitor.pEnvironment, strlen(pMonitor->pEnvironment)+1);
-      pMonitor->pEnvironment[strlen(pMonitor->pEnvironment)] = L'\0';
-   }
-
-   bRet = AddMonitorW(szName, Level, (LPBYTE)&Monitor);
-
-cleanup:
-
-  if (szName)
-     HeapFree(GetProcessHeap(), 0, szName);
-
-  if (Monitor.pDLLName)
-     HeapFree(GetProcessHeap(), 0, Monitor.pDLLName);
-
-  if (Monitor.pEnvironment)
-     HeapFree(GetProcessHeap(), 0, Monitor.pEnvironment);
-
-  if (Monitor.pName)
-     HeapFree(GetProcessHeap(), 0, Monitor.pName);
-
-  return bRet;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddMonitorW(LPWSTR Name, DWORD Level, PBYTE Monitors)
-{
-   WCHAR szPath[MAX_PATH];
-   HMODULE hLibrary = NULL;
-   FARPROC InitProc;
-   HKEY hKey, hSubKey;
-   MONITOR_INFO_2W * pMonitor;
-
-
-   if (Level != 2 || !Monitors)
-   {
-      SetLastError(ERROR_INVALID_PARAMETER);
-      return FALSE;
-   }
-
-   pMonitor = (MONITOR_INFO_2W*)Monitors;
-
-   if (pMonitor->pDLLName == NULL || pMonitor->pName == NULL)
-   {
-      SetLastError(ERROR_INVALID_PARAMETER);
-      return FALSE;
-   }
-
-   if (wcschr(pMonitor->pDLLName, L'\\'))
-   {
-       hLibrary = LoadLibraryExW(pMonitor->pDLLName, NULL, 0);
-   }
-   else if (GetSystemDirectoryW(szPath, MAX_PATH) && PathAddBackslashW(szPath))
-   {
-      wcscat(szPath, pMonitor->pDLLName);
-      hLibrary = LoadLibraryExW(szPath, NULL, 0);
-   }
-
-   if (!hLibrary)
-   {
-      return FALSE;
-   }
-
-   InitProc = GetProcAddress(hLibrary, "InitializePrintMonitor");
-   if (!InitProc)
-   {
-      InitProc = GetProcAddress(hLibrary, "InitializePrintMonitor2");
-      if (!InitProc)
-      {
-         FreeLibrary(hLibrary);
-         SetLastError(ERROR_PROC_NOT_FOUND);
-         return FALSE;
-      }
-   }
-
-   // FIXME
-   // Initialize monitor
-   FreeLibrary(hLibrary);
-
-   if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors", 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
-   {
-      if (RegCreateKeyExW(hKey, pMonitor->pName, 0, NULL, 0, KEY_WRITE, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
-      {
-         RegSetValueExW(hSubKey, L"Driver", 0, REG_SZ, (LPBYTE)pMonitor->pDLLName, (wcslen(pMonitor->pDLLName)+1)*sizeof(WCHAR));
-         RegCloseKey(hSubKey);
-      }
-      RegCloseKey(hKey);
-   }
-   return TRUE;
-}
-
diff --git a/reactos/dll/win32/winspool/precomp.h b/reactos/dll/win32/winspool/precomp.h
deleted file mode 100644 (file)
index ef6c1bd..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef _WINSPOOL_DRV_PCH_
-#define _WINSPOOL_DRV_PCH_
-
-#include <wine/config.h>
-
-#include <stdarg.h>
-
-#define WIN32_NO_STATUS
-#define _INC_WINDOWS
-#define COM_NO_WINDOWS_H
-
-#include <windef.h>
-#include <winbase.h>
-#include <wingdi.h>
-#include <winspool.h>
-
-#endif /* _WINSPOOL_DRV_PCH_ */
diff --git a/reactos/dll/win32/winspool/stubs.c b/reactos/dll/win32/winspool/stubs.c
deleted file mode 100644 (file)
index 435b555..0000000
+++ /dev/null
@@ -1,1773 +0,0 @@
-/*
- * COPYRIGHT:   See COPYING in the top level directory
- * PROJECT:     ReactOS winspool DRV
- * FILE:        stubs.c
- * PURPOSE:     Stub functions
- * PROGRAMMERS: Ge van Geldorp (ge@gse.nl)
- * REVISIONS:
- */
-
-#include "precomp.h"
-
-#include <winuser.h>
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DllMain(HINSTANCE InstDLL,
-        DWORD Reason,
-        LPVOID Reserved)
-{
-  return TRUE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AbortPrinter(HANDLE Printer)
-{
-  OutputDebugStringW(L"winspool AbortPrinter stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddFormA(HANDLE Printer, DWORD Level, PBYTE Form)
-{
-  OutputDebugStringW(L"winspool AddFormA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddFormW(HANDLE Printer, DWORD Level, PBYTE Form)
-{
-  OutputDebugStringW(L"winspool AddFormW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddJobA(HANDLE Printer, DWORD Level, PBYTE Data, DWORD BufSize, PDWORD Needed)
-{
-  OutputDebugStringW(L"winspool AddJobA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddJobW(HANDLE Printer, DWORD Level, PBYTE Data, DWORD BufSize, PDWORD Needed)
-{
-  OutputDebugStringW(L"winspool AddJobW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPortA(LPSTR Name, HWND Wnd, LPSTR MonitorName)
-{
-  OutputDebugStringW(L"winspool  stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPortW(LPWSTR Name, HWND Wnd, LPWSTR MonitorName)
-{
-  OutputDebugStringW(L"winspool AddPortW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-HANDLE
-WINAPI
-AddPrinterA(LPSTR Name, DWORD Level, PBYTE Buffer)
-{
-  OutputDebugStringW(L"winspool AddPrinterA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return NULL;
-}
-
-
-/*
- * @unimplemented
- */
-HANDLE
-WINAPI
-AddPrinterW(LPWSTR Name, DWORD Level, PBYTE Buffer)
-{
-  OutputDebugStringW(L"winspool AddPrinterW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return NULL;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPrinterConnectionA(LPSTR Name)
-{
-  OutputDebugStringW(L"winspool AddPrinterConnectionA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPrinterConnectionW(LPWSTR Name)
-{
-  OutputDebugStringW(L"winspool AddPrinterConnectionW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPrinterDriverA(LPSTR Name, DWORD Level, PBYTE Buffer)
-{
-  OutputDebugStringW(L"winspool AddPrinterDriverA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPrinterDriverW(LPWSTR Name, DWORD Level, PBYTE Buffer)
-{
-  OutputDebugStringW(L"winspool AddPrinterDriverW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPrintProcessorA(LPSTR Name, LPSTR Environment, LPSTR PathName, LPSTR PrintProcessorName)
-{
-  OutputDebugStringW(L"winspool AddPrintProcessorA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPrintProcessorW(LPWSTR Name, LPWSTR Environment, LPWSTR PathName, LPWSTR PrintProcessorName)
-{
-  OutputDebugStringW(L"winspool AddPrintProcessorW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-/*
- * @unimplemented
- */
-LONG
-WINAPI
-AdvancedDocumentPropertiesA(HWND Wnd, HANDLE Printer, LPSTR DeviceName, PDEVMODEA DevModeOut, PDEVMODEA DevModeIn)
-{
-  OutputDebugStringW(L"winspool AdvancedDocumentPropertiesA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-LONG
-WINAPI
-AdvancedDocumentPropertiesW(HWND Wnd, HANDLE Printer, LPWSTR DeviceName, PDEVMODEW DevModeOut, PDEVMODEW DevModeIn)
-{
-  OutputDebugStringW(L"winspool AdvancedDocumentPropertiesW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-ClosePrinter(HANDLE Printer)
-{
-  OutputDebugStringW(L"winspool ClosePrinter stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-ConfigurePortA(LPSTR Name, HWND Wnd, LPSTR PortName)
-{
-  OutputDebugStringW(L"winspool ConfigurePortA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-ConfigurePortW(LPWSTR Name, HWND Wnd, LPWSTR PortName)
-{
-  OutputDebugStringW(L"winspool ConfigurePortW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-HANDLE
-WINAPI
-ConnectToPrinterDlg(HWND Wnd, DWORD Flags)
-{
-  OutputDebugStringW(L"winspool ConnectToPrinterDlg stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return NULL;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeleteFormA(HANDLE Printer, LPSTR Name)
-{
-  OutputDebugStringW(L"winspool DeleteFormA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeleteFormW(HANDLE Printer, LPWSTR Name)
-{
-  OutputDebugStringW(L"winspool DeleteFormW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeleteMonitorA(LPSTR Name, LPSTR Environment, LPSTR MonitorName)
-{
-  OutputDebugStringW(L"winspool DeleteMonitorA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeleteMonitorW(LPWSTR Name, LPWSTR Environment, LPWSTR MonitorName)
-{
-  OutputDebugStringW(L"winspool DeleteMonitorW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePortA(LPSTR Name, HWND Wnd, LPSTR PortName)
-{
-  OutputDebugStringW(L"winspool DeletePortA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePortW(LPWSTR Name, HWND Wnd, LPWSTR PortName)
-{
-  OutputDebugStringW(L"winspool DeletePortW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePrinter(HANDLE Printer)
-{
-  OutputDebugStringW(L"winspool DeletePrinter stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePrinterConnectionA(LPSTR Name)
-{
-  OutputDebugStringW(L"winspool DeletePrinterConnectionA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePrinterConnectionW(LPWSTR Name)
-{
-  OutputDebugStringW(L"winspool DeletePrinterConnectionW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-DeletePrinterDataA(HANDLE Printer, LPSTR Name)
-{
-  OutputDebugStringW(L"winspool DeletePrinterDataA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-DeletePrinterDataW(HANDLE Printer, LPWSTR Name)
-{
-  OutputDebugStringW(L"winspool DeletePrinterDataW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePrinterDriverA(LPSTR Name, LPSTR Environment, LPSTR Driver)
-{
-  OutputDebugStringW(L"winspool DeletePrinterDriverA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePrinterDriverW(LPWSTR Name, LPWSTR Environment, LPWSTR Driver)
-{
-  OutputDebugStringW(L"winspool DeletePrinterDriverW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePrintProcessorA(LPSTR Name, LPSTR Environment, LPSTR PrintProcessor)
-{
-  OutputDebugStringW(L"winspool DeletePrintProcessorA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePrintProcessorW(LPWSTR Name, LPWSTR Environment, LPWSTR PrintProcessor)
-{
-  OutputDebugStringW(L"winspool DeletePrintProcessorW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-DeviceCapabilitiesA(LPCSTR Device, LPCSTR Port, WORD Capability, LPSTR Buffer, CONST DEVMODEA *DevMode)
-{
-  OutputDebugStringW(L"winspool DeviceCapabilitiesA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return -1;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-DeviceCapabilitiesW(LPCWSTR Device, LPCWSTR Port, WORD Capability, LPWSTR Buffer, CONST DEVMODEW *DevMode)
-{
-  OutputDebugStringW(L"winspool DeviceCapabilitiesW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return -1;
-}
-
-
-/*
- * @unimplemented
- */
-LONG
-WINAPI
-DocumentPropertiesA(HWND Wnd, HANDLE Printer, LPSTR Device, PDEVMODEA DevModeOut, PDEVMODEA DevModeIn, DWORD Mode)
-{
-  OutputDebugStringW(L"winspool DocumentPropertiesA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return -1L;
-}
-
-
-/*
- * @unimplemented
- */
-LONG
-WINAPI
-DocumentPropertiesW(HWND Wnd, HANDLE Printer, LPWSTR Device, PDEVMODEW DevModeOut, PDEVMODEW DevModeIn, DWORD Mode)
-{
-  OutputDebugStringW(L"winspool DocumentPropertiesW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EndDocPrinter(HANDLE Printer)
-{
-  OutputDebugStringW(L"winspool EndDocPrinter stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EndPagePrinter(HANDLE Printer)
-{
-  OutputDebugStringW(L"winspool EndPagePrinter stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumFormsA(HANDLE Printer, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumFormsA stub called\n");
-  *Needed = 0;
-  *Returned = 0;
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumFormsW(HANDLE Printer, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumFormsW stub called\n");
-  *Needed = 0;
-  *Returned = 0;
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumJobsA(HANDLE Printer, DWORD First, DWORD NoJobs, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumJobsA stub called\n");
-  *Needed = 0;
-  *Returned = 0;
-
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumJobsW(HANDLE Printer, DWORD First, DWORD NoJobs, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumJobsW stub called\n");
-  *Needed = 0;
-  *Returned = 0;
-
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumMonitorsA(LPSTR Name, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumMonitorsA stub called\n");
-  *Needed = 0;
-  *Returned = 0;
-
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumMonitorsW(LPWSTR Name, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumMonitorsW stub called\n");
-  *Needed = 0;
-  *Returned = 0;
-
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumPortsA(LPSTR Name, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumPortsA stub called\n");
-  *Needed = 0;
-  *Returned = 0;
-
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumPortsW(LPWSTR Name, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumPortsW stub called\n");
-  *Needed = 0;
-  *Returned = 0;
-
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-EnumPrinterDataA(HANDLE Printer, DWORD Index, LPSTR Name, DWORD NameSize, PDWORD NameReturned, PDWORD Type, PBYTE Buffer, DWORD BufSize, PDWORD BufReturned)
-{
-  OutputDebugStringW(L"winspool EnumPrinterDataA stub called\n");
-
-  return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-EnumPrinterDataW(HANDLE Printer, DWORD Index, LPWSTR Name, DWORD NameSize, PDWORD NameReturned, PDWORD Type, PBYTE Buffer, DWORD BufSize, PDWORD BufReturned)
-{
-  OutputDebugStringW(L"winspool EnumPrinterDataW stub called\n");
-
-  return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumPrinterDriversA(LPSTR Name, LPSTR Environment, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumPrinterDriversA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-  *Needed = 0;
-  *Returned = 0;
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumPrinterDriversW(LPWSTR Name, LPWSTR Environment, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumPrinterDriversW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-  *Needed = 0;
-  *Returned = 0;
-
-  return FALSE;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-EnumPrinterKeyA(HANDLE Printer, LPSTR KeyName, LPTSTR Subkey, DWORD SubkeySize, PDWORD SubkeyReturned)
-{
-  OutputDebugStringW(L"winspool EnumPrinterKeyA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  *SubkeyReturned = 0;
-
-  return ERROR_FILE_NOT_FOUND;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-EnumPrinterKeyW(HANDLE Printer, LPWSTR KeyName, LPTSTR Subkey, DWORD SubkeySize, PDWORD SubkeyReturned)
-{
-  OutputDebugStringW(L"winspool EnumPrinterKeyW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  *SubkeyReturned = 0;
-
-  return ERROR_FILE_NOT_FOUND;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumPrintersA(DWORD Flags, LPSTR Name, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumPrintersA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-  *Needed = 0;
-  *Returned = 0;
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumPrintersW(DWORD Flags, LPWSTR Name, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumPrintersW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-  *Needed = 0;
-  *Returned = 0;
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumPrintProcessorDatatypesA(LPSTR Name, LPSTR PrintProcessor, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumPrintProcessorDatatypesA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-  *Needed = 0;
-  *Returned = 0;
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumPrintProcessorDatatypesW(LPWSTR Name, LPWSTR PrintProcessor, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumPrintProcessorDatatypesW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-  *Needed = 0;
-  *Returned = 0;
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumPrintProcessorsA(LPSTR Name, LPSTR Environment, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumPrintProcessorsA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-  *Needed = 0;
-  *Returned = 0;
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-EnumPrintProcessorsW(LPWSTR Name, LPWSTR Environment, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed, PDWORD Returned)
-{
-  OutputDebugStringW(L"winspool EnumPrintProcessorsW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-  *Needed = 0;
-  *Returned = 0;
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-FindClosePrinterChangeNotification(HANDLE Printer)
-{
-  OutputDebugStringW(L"winspool FindClosePrinterChangeNotification stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-HANDLE
-WINAPI
-FindFirstPrinterChangeNotification(HANDLE Printer, DWORD Flags, DWORD Options, PVOID NotifyOptions)
-{
-  OutputDebugStringW(L"winspool FindFirstPrinterChangeNotification stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return INVALID_HANDLE_VALUE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-FindNextPrinterChangeNotification(HANDLE Printer, PDWORD Change, LPVOID NotifyOptions, LPVOID* NotifyInfo)
-{
-  OutputDebugStringW(L"winspool FindNextPrinterChangeNotification stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-FreePrinterNotifyInfo(PPRINTER_NOTIFY_INFO NotifyInfo)
-{
-  OutputDebugStringW(L"winspool FreePrinterNotifyInfo stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetFormA(HANDLE Printer, LPSTR Name, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetFormA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetFormW(HANDLE Printer, LPWSTR Name, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetFormW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetJobA(HANDLE Printer, DWORD Job, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetJobA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetJobW(HANDLE Printer, DWORD Job, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetJobW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetPrinterA(HANDLE Printer, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetPrinterA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetPrinterW(HANDLE Printer, DWORD Level, PBYTE Buffer, DWORD BufSize, PDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetPrinterW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-GetPrinterDataA(HANDLE Printer, LPSTR Name, PDWORD Type, PBYTE Buffer, DWORD BufSize, PDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetPrinterDataA stub called\n");
-
-  return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-GetPrinterDataW(HANDLE Printer, LPWSTR Name, PDWORD Type, PBYTE Buffer, DWORD BufSize, PDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetPrinterDataW stub called\n");
-
-  return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetPrinterDriverA(HANDLE Printer, LPSTR Environment, DWORD Level, LPBYTE Buffer, DWORD BufSize, LPDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetPrinterDriverA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetPrinterDriverW(HANDLE Printer, LPWSTR Environment, DWORD Level, LPBYTE Buffer, DWORD BufSize, LPDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetPrinterDriverW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetPrinterDriverDirectoryA(LPSTR Name, LPSTR Environment, DWORD Level, LPBYTE Buffer, DWORD BufSize, LPDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetPrinterDriverDirectoryA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetPrinterDriverDirectoryW(LPWSTR Name, LPWSTR Environment, DWORD Level, LPBYTE Buffer, DWORD BufSize, LPDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetPrinterDriverDirectoryW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetPrintProcessorDirectoryA(LPSTR Name, LPSTR Environment, DWORD Level, LPBYTE Buffer, DWORD BufSize, LPDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetPrintProcessorDirectoryA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-GetPrintProcessorDirectoryW(LPWSTR Name, LPWSTR Environment, DWORD Level, LPBYTE Buffer, DWORD BufSize, LPDWORD Needed)
-{
-  OutputDebugStringW(L"winspool GetPrintProcessorDirectoryW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-OpenPrinterA(LPSTR Name, PHANDLE Printer, LPPRINTER_DEFAULTSA Defaults)
-{
-  OutputDebugStringW(L"winspool OpenPrinterA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-OpenPrinterW(LPWSTR Name, PHANDLE Printer, LPPRINTER_DEFAULTSW Defaults)
-{
-  OutputDebugStringW(L"winspool OpenPrinterW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-PrinterMessageBoxA(HANDLE Printer, DWORD Error, HWND Wnd, LPSTR Text, LPSTR Caption, DWORD Type)
-{
-  OutputDebugStringW(L"winspool PrinterMessageBoxA stub called\n");
-
-  return IDCANCEL;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-PrinterMessageBoxW(HANDLE Printer, DWORD Error, HWND Wnd, LPWSTR Text, LPWSTR Caption, DWORD Type)
-{
-  OutputDebugStringW(L"winspool PrinterMessageBoxW stub called\n");
-
-  return IDCANCEL;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-PrinterProperties(HWND Wnd, HANDLE Printer)
-{
-  OutputDebugStringW(L"winspool PrinterProperties stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-ReadPrinter(HANDLE Printer, PVOID Buffer, DWORD BufSize, PDWORD Received)
-{
-  OutputDebugStringW(L"winspool ReadPrinter stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-ResetPrinterA(HANDLE Printer, LPPRINTER_DEFAULTSA Defaults)
-{
-  OutputDebugStringW(L"winspool ResetPrinterA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-ResetPrinterW(HANDLE Printer, LPPRINTER_DEFAULTSW Defaults)
-{
-  OutputDebugStringW(L"winspool ResetPrinterW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-ScheduleJob(HANDLE Printer, DWORD Job)
-{
-  OutputDebugStringW(L"winspool ScheduleJob stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SetFormA(HANDLE Printer, LPSTR Form, DWORD Level, PBYTE Buffer)
-{
-  OutputDebugStringW(L"winspool SetFormA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SetFormW(HANDLE Printer, LPWSTR Form, DWORD Level, PBYTE Buffer)
-{
-  OutputDebugStringW(L"winspool SetFormW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SetJobA(HANDLE Printer, DWORD Job, DWORD Level, PBYTE Buffer, DWORD Command)
-{
-  OutputDebugStringW(L"winspool SetJobA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SetJobW(HANDLE Printer, DWORD Job, DWORD Level, PBYTE Buffer, DWORD Command)
-{
-  OutputDebugStringW(L"winspool SetJobW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SetPrinterA(HANDLE Printer, DWORD Level, PBYTE Buffer, DWORD Command)
-{
-  OutputDebugStringW(L"winspool SetPrinterA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SetPrinterW(HANDLE Printer, DWORD Level, PBYTE Buffer, DWORD Command)
-{
-  OutputDebugStringW(L"winspool SetPrinterW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SetPrinterDataA(HANDLE Printer, LPSTR Name, DWORD Type, PBYTE Buffer, DWORD BufSize)
-{
-  OutputDebugStringW(L"winspool SetPrinterDataA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SetPrinterDataW(HANDLE Printer, LPWSTR Name, DWORD Type, PBYTE Buffer, DWORD BufSize)
-{
-  OutputDebugStringW(L"winspool SetPrinterDataW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-StartDocPrinterA(HANDLE Printer, DWORD Level, PBYTE Buffer)
-{
-  OutputDebugStringW(L"winspool StartDocPrinterA stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-StartDocPrinterW(HANDLE Printer, DWORD Level, PBYTE Buffer)
-{
-  OutputDebugStringW(L"winspool StartDocPrinterW stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-StartPagePrinter(HANDLE Printer)
-{
-  OutputDebugStringW(L"winspool StartPagePrinter stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-WaitForPrinterChange(HANDLE Printer, DWORD Flags)
-{
-  OutputDebugStringW(L"winspool WaitForPrinterChange stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return 0;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-WritePrinter(HANDLE Printer, PVOID Buffer, DWORD BufSize, PDWORD Written)
-{
-  OutputDebugStringW(L"winspool WritePrinter stub called\n");
-  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-  return FALSE;
-}
-
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-XcvDataW(HANDLE hXcv,
-         LPCWSTR pszDataName,
-         PBYTE pInputData,
-         DWORD cbInputData,
-         PBYTE pOutputData,
-         DWORD cbOutputData,
-         PDWORD pcbOutputNeeded,
-         PDWORD pdwStatus)
-{
-    OutputDebugStringW(L"winspool XcvDataW stub called\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SetDefaultPrinterA(LPCSTR pszPrinter)
-{
-    OutputDebugStringW(L"winspool SetDefaultPrinterA stub called\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SetDefaultPrinterW(LPCWSTR pszPrinter)
-{
-    OutputDebugStringW(L"winspool SetDefaultPrinterW stub called\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPortExA(LPSTR pName,
-           DWORD dwLevel,
-           LPBYTE pBuffer,
-           LPSTR pMonitorName)
-{
-    OutputDebugStringW(L"winspool AddPortExA stub called\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPortExW(LPWSTR pName,
-           DWORD dwLevel,
-           LPBYTE pBuffer,
-           LPWSTR pMonitorName)
-{
-    OutputDebugStringW(L"winspool AddPortExW stub called\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPrinterDriverExA(LPSTR pName,
-                    DWORD dwLevel,
-                    LPBYTE pDriverInfo,
-                    DWORD dwFileCopyFlags)
-{
-    OutputDebugStringW(L"winspool AddPrinterDriverExA stub called\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-AddPrinterDriverExW(LPWSTR pName,
-                    DWORD dwLevel,
-                    LPBYTE pDriverInfo,
-                    DWORD dwFileCopyFlags)
-{
-    OutputDebugStringW(L"winspool AddPrinterDriverExW stub called\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-DeletePrinterDataExA(HANDLE hPrinter,
-                     LPCSTR pKeyName,
-                     LPCSTR pValueName)
-{
-    OutputDebugStringW(L"winspool DeletePrinterDataExA stub called\n");
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-DeletePrinterDataExW(HANDLE hPrinter,
-                     LPCWSTR pKeyName,
-                     LPCWSTR pValueName)
-{
-    OutputDebugStringW(L"winspool DeletePrinterDataExW stub called\n");
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePrinterDriverExA(LPSTR pName,
-                       LPSTR pEnvironment,
-                       LPSTR pDriverName,
-                       DWORD dwDeleteFlag,
-                       DWORD dwVersionFlag)
-{
-    OutputDebugStringW(L"winspool DeletePrinterDriverExA stub called\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-DeletePrinterDriverExW(LPWSTR pName,
-                       LPWSTR pEnvironment,
-                       LPWSTR pDriverName,
-                       DWORD dwDeleteFlag,
-                       DWORD dwVersionFlag)
-{
-    OutputDebugStringW(L"winspool DeletePrinterDriverExW stub called\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-EnumPrinterDataExA(HANDLE hPrinter,
-                   LPCSTR pKeyName,
-                   LPBYTE pEnumValues,
-                   DWORD cbEnumValues,
-                   LPDWORD pcbEnumValues,
-                   LPDWORD pnEnumValues)
-{
-    OutputDebugStringW(L"winspool EnumPrinterDataExA stub called\n");
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-EnumPrinterDataExW(HANDLE hPrinter,
-                   LPCWSTR pKeyName,
-                   LPBYTE pEnumValues,
-                   DWORD cbEnumValues,
-                   LPDWORD pcbEnumValues,
-                   LPDWORD pnEnumValues)
-{
-    OutputDebugStringW(L"winspool EnumPrinterDataExW stub called\n");
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-/*
- * @unimplemented
- */
-LONG
-WINAPI
-ExtDeviceMode(HWND hWnd,
-              HANDLE hInst,
-              LPDEVMODEA pDevModeOutput,
-              LPSTR pDeviceName,
-              LPSTR pPort,
-              LPDEVMODEA pDevModeInput,
-              LPSTR pProfile,
-              DWORD fMode)
-{
-    OutputDebugStringW(L"winspool ExtDeviceMode stub called\n");
-    return -1;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-GetPrinterDataExA(HANDLE hPrinter,
-                  LPCSTR pKeyName,
-                  LPCSTR pValueName,
-                  LPDWORD pType,
-                  LPBYTE pData,
-                  DWORD nSize,
-                  LPDWORD pcbNeeded)
-{
-    OutputDebugStringW(L"winspool GetPrinterDataExA stub called\n");
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-GetPrinterDataExW(HANDLE hPrinter,
-                  LPCWSTR pKeyName,
-                  LPCWSTR pValueName,
-                  LPDWORD pType,
-                  LPBYTE pData,
-                  DWORD nSize,
-                  LPDWORD pcbNeeded)
-{
-    OutputDebugStringW(L"winspool GetPrinterDataExW stub called\n");
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-SetPrinterDataExA(HANDLE hPrinter,
-                  LPCSTR pKeyName,
-                  LPCSTR pValueName,
-                  DWORD dwType,
-                  LPBYTE pData,
-                  DWORD cbData)
-{
-    OutputDebugStringW(L"winspool SetPrinterDataExA stub called\n");
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-/*
- * @unimplemented
- */
-DWORD
-WINAPI
-SetPrinterDataExW(HANDLE hPrinter,
-                  LPCWSTR pKeyName,
-                  LPCWSTR pValueName,
-                  DWORD dwType,
-                  LPBYTE pData,
-                  DWORD cbData)
-{
-    OutputDebugStringW(L"winspool SetPrinterDataExW stub called\n");
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-/*
- * @unimplemented
- */
-BOOL
-WINAPI
-SpoolerInit(VOID)
-{
-    OutputDebugStringW(L"winspool SpoolerInit stub called\n");
-    return FALSE;
-}
diff --git a/reactos/dll/win32/winspool/winspool.spec b/reactos/dll/win32/winspool/winspool.spec
deleted file mode 100644 (file)
index 1dc1fd2..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-100 stub -noname EnumPrinterPropertySheets
-101 stub -noname ClusterSplOpen
-102 stub -noname ClusterSplClose
-103 stub -noname ClusterSplIsAlive
-104 stub PerfClose
-105 stub PerfCollect
-106 stub PerfOpen
-201 stdcall GetDefaultPrinterA(ptr ptr)
-202 stdcall SetDefaultPrinterA(str)
-203 stdcall GetDefaultPrinterW(ptr ptr)
-204 stdcall SetDefaultPrinterW(wstr)
-205 stub -noname SplReadPrinter
-206 stub -noname AddPerMachineConnectionA
-207 stub -noname AddPerMachineConnectionW
-208 stub -noname DeletePerMachineConnectionA
-209 stub -noname DeletePerMachineConnectionW
-210 stub -noname EnumPerMachineConnectionsA
-211 stub -noname EnumPerMachineConnectionsW
-212 stub -noname LoadPrinterDriver
-213 stub -noname RefCntLoadDriver
-214 stub -noname RefCntUnloadDriver
-215 stub -noname ForceUnloadDriver
-216 stub -noname PublishPrinterA
-217 stub -noname PublishPrinterW
-218 stub -noname CallCommonPropertySheetUI
-219 stub -noname PrintUIQueueCreate
-220 stub -noname PrintUIPrinterPropPages
-221 stub -noname PrintUIDocumentDefaults
-222 stub -noname SendRecvBidiData
-223 stub -noname RouterFreeBidiResponseContainer
-224 stub -noname ExternalConnectToLd64In32Server
-226 stub -noname PrintUIWebPnpEntry
-227 stub -noname PrintUIWebPnpPostEntry
-228 stub -noname PrintUICreateInstance
-229 stub -noname PrintUIDocumentPropertiesWrap
-230 stub -noname PrintUIPrinterSetup
-231 stub -noname PrintUIServerPropPages
-232 stub -noname AddDriverCatalog
-
-@ stub ADVANCEDSETUPDIALOG
-@ stdcall AbortPrinter(long)
-@ stdcall AddFormA(long long ptr)
-@ stdcall AddFormW(long long ptr)
-@ stdcall AddJobA(long long ptr long ptr)
-@ stdcall AddJobW(long long ptr long ptr)
-@ stdcall AddMonitorA(str long ptr)
-@ stdcall AddMonitorW(wstr long ptr)
-@ stdcall AddPortA(str ptr str)
-@ stdcall AddPortExA(str long ptr str)
-@ stdcall AddPortExW(wstr long ptr wstr)
-@ stdcall AddPortW(wstr long wstr)
-@ stdcall AddPrinterA(str long ptr)
-@ stdcall AddPrinterConnectionA(str)
-@ stdcall AddPrinterConnectionW(wstr)
-@ stdcall AddPrinterDriverA(str long ptr)
-@ stdcall AddPrinterDriverExA(str long ptr long)
-@ stdcall AddPrinterDriverExW(wstr long ptr long)
-@ stdcall AddPrinterDriverW(wstr long ptr)
-@ stdcall AddPrinterW(wstr long ptr)
-@ stdcall AddPrintProcessorA(str str str str)
-@ stdcall AddPrintProcessorW(wstr wstr wstr wstr)
-@ stdcall AddPrintProvidorA(str long ptr)
-@ stdcall AddPrintProvidorW(wstr long ptr)
-@ stdcall AdvancedDocumentPropertiesA(long long str ptr ptr)
-@ stdcall AdvancedDocumentPropertiesW(long long wstr ptr ptr)
-@ stub AdvancedSetupDialog
-@ stdcall ClosePrinter(long)
-@ stub CloseSpoolFileHandle
-@ stdcall ConfigurePortA(str long str)
-@ stdcall ConfigurePortW(wstr long wstr)
-@ stdcall ConnectToPrinterDlg(long long)
-@ stub ConvertAnsiDevModeToUnicodeDevMode
-@ stub ConvertUnicodeDevModeToAnsiDevMode
-@ stub CommitSpoolData
-@ stub CreatePrinterIC
-@ stub DEVICECAPABILITIES
-@ stub DEVICEMODE
-@ stdcall DeleteFormA(long str)
-@ stdcall DeleteFormW(long wstr)
-@ stdcall DeleteMonitorA(str str str)
-@ stdcall DeleteMonitorW(wstr wstr wstr)
-@ stdcall DeletePortA(str long str)
-@ stdcall DeletePortW(wstr long wstr)
-@ stdcall DeletePrinter(long)
-@ stdcall DeletePrinterConnectionA(str)
-@ stdcall DeletePrinterConnectionW(wstr)
-@ stdcall DeletePrinterDataExA(long str str)
-@ stdcall DeletePrinterDataExW(long wstr wstr)
-@ stdcall DeletePrinterDataA(ptr str)
-@ stdcall DeletePrinterDataW(ptr wstr)
-@ stdcall DeletePrinterDriverA(str str str)
-@ stdcall DeletePrinterDriverExA(str str str long long)
-@ stdcall DeletePrinterDriverExW(wstr wstr wstr long long)
-@ stdcall DeletePrinterDriverW(wstr wstr wstr)
-@ stub DeletePrinterIC
-@ stub DevQueryPrint
-@ stdcall DeletePrintProcessorA(str str str)
-@ stdcall DeletePrintProcessorW(wstr wstr wstr)
-@ stdcall DeletePrintProvidorA(str str str)
-@ stdcall DeletePrintProvidorW(wstr wstr wstr)
-@ stdcall DeviceCapabilitiesA(str str long ptr ptr)
-@ stdcall DeviceCapabilitiesW(wstr wstr long ptr ptr)
-@ stub DeviceMode
-@ stub DocumentEvent
-@ stdcall DllMain(ptr long ptr)
-@ stdcall DocumentPropertiesA(long long ptr ptr ptr long)
-@ stdcall DocumentPropertiesW(long long ptr ptr ptr long)
-@ stub EXTDEVICEMODE
-@ stdcall EndDocPrinter(long)
-@ stdcall EndPagePrinter(long)
-@ stdcall EnumFormsA(long long ptr long ptr ptr)
-@ stdcall EnumFormsW(long long ptr long ptr ptr)
-@ stdcall EnumJobsA(long long long long ptr long ptr ptr)
-@ stdcall EnumJobsW(long long long long ptr long ptr ptr)
-@ stdcall EnumMonitorsA(str long ptr long long long)
-@ stdcall EnumMonitorsW(wstr long ptr long long long)
-@ stdcall EnumPortsA(str long ptr ptr ptr ptr)
-@ stdcall EnumPortsW(wstr long ptr ptr ptr ptr)
-@ stdcall EnumPrinterDataA(long long ptr long ptr ptr ptr long ptr)
-@ stdcall EnumPrinterDataExA(long str ptr long ptr ptr)
-@ stdcall EnumPrinterDataExW(long wstr ptr long ptr ptr)
-@ stdcall EnumPrinterDataW(long long ptr long ptr ptr ptr long ptr)
-@ stdcall EnumPrinterDriversA(str str long ptr long ptr ptr)
-@ stdcall EnumPrinterDriversW(wstr wstr long ptr long ptr ptr)
-@ stdcall EnumPrintersA(long ptr long ptr long ptr ptr)
-@ stdcall EnumPrintersW(long ptr long ptr long ptr ptr)
-@ stdcall EnumPrinterKeyA(long str str long ptr)
-@ stdcall EnumPrinterKeyW(long wstr wstr long ptr)
-@ stdcall ExtDeviceMode(long long ptr str str ptr str long)
-@ stdcall EnumPrintProcessorDatatypesA(str str long ptr long ptr ptr)
-@ stdcall EnumPrintProcessorDatatypesW(wstr wstr long ptr long ptr ptr)
-@ stdcall EnumPrintProcessorsA(str str long ptr long ptr ptr)
-@ stdcall EnumPrintProcessorsW(wstr wstr long ptr long ptr ptr)
-@ stdcall FindClosePrinterChangeNotification(long)
-@ stdcall FindFirstPrinterChangeNotification(long long long ptr)
-@ stdcall FindNextPrinterChangeNotification(long ptr ptr ptr)
-@ stdcall FreePrinterNotifyInfo(ptr)
-@ stdcall GetFormA(long str long ptr long ptr)
-@ stdcall GetFormW(long wstr long ptr long ptr)
-@ stdcall GetJobA(long long long ptr long ptr)
-@ stdcall GetJobW(long long long ptr long ptr)
-@ stdcall GetPrinterA(long long ptr long ptr)
-@ stdcall GetPrinterDataA(long str ptr ptr long ptr)
-@ stdcall GetPrinterDataExA(long str str ptr ptr long ptr)
-@ stdcall GetPrinterDataExW(long wstr wstr ptr ptr long ptr)
-@ stdcall GetPrinterDataW(long wstr ptr ptr long ptr)
-@ stdcall GetPrinterDriverA(long str long ptr long ptr)
-@ stdcall GetPrinterDriverDirectoryA(str str long ptr long ptr)
-@ stdcall GetPrinterDriverDirectoryW(wstr wstr long ptr long ptr)
-@ stdcall GetPrinterDriverW(long str long ptr long ptr)
-@ stdcall GetPrinterW(long long ptr long ptr)
-@ stdcall GetPrintProcessorDirectoryA(str str long ptr long ptr)
-@ stdcall GetPrintProcessorDirectoryW(wstr wstr long ptr long ptr)
-@ stub GetSpoolFileHandle
-@ stub IsValidDevmodeA
-@ stdcall -stub IsValidDevmodeW(ptr long)
-@ stdcall OpenPrinterA(str ptr ptr)
-@ stdcall OpenPrinterW(wstr ptr ptr)
-@ stub PlayGdiScriptOnPrinterIC
-@ stdcall PrinterMessageBoxA(ptr long ptr str str long)
-@ stdcall PrinterMessageBoxW(ptr long ptr wstr wstr long)
-@ stdcall PrinterProperties(long long)
-@ stub QueryColorProfile
-@ stub QuerySpoolMode
-@ stub QueryRemoteFonts
-@ stdcall ReadPrinter(long ptr long ptr)
-@ stdcall ResetPrinterA(long ptr)
-@ stdcall ResetPrinterW(long ptr)
-@ stdcall ScheduleJob(long long)
-@ stub SeekPrinter
-@ stub SetAllocFailCount
-@ stdcall SetFormA(long str long ptr)
-@ stdcall SetFormW(long wstr long ptr)
-@ stdcall SetJobA(long long long ptr long)
-@ stdcall SetJobW(long long long ptr long)
-@ stdcall SetPrinterA(long long ptr long)
-@ stdcall SetPrinterDataA(long str long ptr long)
-@ stdcall SetPrinterDataExA(long str str long ptr long)
-@ stdcall SetPrinterDataExW(long wstr wstr long ptr long)
-@ stdcall SetPrinterDataW(long wstr long ptr long)
-@ stdcall SetPrinterW(long long ptr long)
-@ stub SplDriverUnloadComplete
-@ stub SpoolerDevQueryPrintW
-@ stdcall SpoolerInit()
-@ stub SpoolerPrinterEvent
-@ stub StartDocDlgA
-@ stub StartDocDlgW
-@ stdcall StartDocPrinterA(long long ptr)
-@ stdcall StartDocPrinterW(long long ptr)
-@ stdcall StartPagePrinter(long)
-@ stdcall WaitForPrinterChange(ptr long)
-@ stdcall WritePrinter(long ptr long ptr)
-@ stdcall XcvDataW(long wstr ptr long ptr long ptr ptr)
index 59faf4e..0116d18 100644 (file)
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
 #ifndef _WINSPOOL_
 #define _WINSPOOL_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <_mingw_unicode.h>
 
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4820)
+#ifdef _WINUSER_
+#include <prsht.h>
 #endif
 
-#define DI_CHANNEL        1
-#define DI_CHANNEL_WRITE  2
-#define DI_READ_SPOOL_JOB 3
-
-#define FORM_BUILTIN      1
-
-#define JOB_CONTROL_PAUSE   1
-#define JOB_CONTROL_RESUME  2
-#define JOB_CONTROL_CANCEL  3
-#define JOB_CONTROL_RESTART 4
-#define JOB_CONTROL_DELETE  5
-
-#define JOB_STATUS_PAUSED            1
-#define JOB_STATUS_ERROR             2
-#define JOB_STATUS_DELETING          4
-#define JOB_STATUS_SPOOLING          8
-#define JOB_STATUS_PRINTING          16
-#define JOB_STATUS_OFFLINE           32
-#define JOB_STATUS_PAPEROUT          0x40
-#define JOB_STATUS_PRINTED           0x80
-#define JOB_STATUS_DELETED           0x100
-#define JOB_STATUS_BLOCKED_DEVQ      0x200
-#define JOB_STATUS_USER_INTERVENTION 0x400
-
-#define JOB_POSITION_UNSPECIFIED     0
-
-#define JOB_NOTIFY_TYPE              1
-
-#define JOB_NOTIFY_FIELD_PRINTER_NAME        0
-#define JOB_NOTIFY_FIELD_MACHINE_NAME        1
-#define JOB_NOTIFY_FIELD_PORT_NAME           2
-#define JOB_NOTIFY_FIELD_USER_NAME           3
-#define JOB_NOTIFY_FIELD_NOTIFY_NAME         4
-#define JOB_NOTIFY_FIELD_DATATYPE            5
-#define JOB_NOTIFY_FIELD_PRINT_PROCESSOR     6
-#define JOB_NOTIFY_FIELD_PARAMETERS          7
-#define JOB_NOTIFY_FIELD_DRIVER_NAME         8
-#define JOB_NOTIFY_FIELD_DEVMODE             9
-#define JOB_NOTIFY_FIELD_STATUS              10
-#define JOB_NOTIFY_FIELD_STATUS_STRING       11
-#define JOB_NOTIFY_FIELD_SECURITY_DESCRIPTOR 12
-#define JOB_NOTIFY_FIELD_DOCUMENT            13
-#define JOB_NOTIFY_FIELD_PRIORITY            14
-#define JOB_NOTIFY_FIELD_POSITION            15
-#define JOB_NOTIFY_FIELD_SUBMITTED           16
-#define JOB_NOTIFY_FIELD_START_TIME          17
-#define JOB_NOTIFY_FIELD_UNTIL_TIME          18
-#define JOB_NOTIFY_FIELD_TIME                19
-#define JOB_NOTIFY_FIELD_TOTAL_PAGES         20
-#define JOB_NOTIFY_FIELD_PAGES_PRINTED       21
-#define JOB_NOTIFY_FIELD_TOTAL_BYTES         22
-#define JOB_NOTIFY_FIELD_BYTES_PRINTED       23
-
-#define JOB_ACCESS_ADMINISTER 16
-#define JOB_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|JOB_ACCESS_ADMINISTER)
-#define JOB_READ (STANDARD_RIGHTS_READ|JOB_ACCESS_ADMINISTER)
-#define JOB_WRITE (STANDARD_RIGHTS_WRITE|JOB_ACCESS_ADMINISTER)
-#define JOB_EXECUTE (STANDARD_RIGHTS_EXECUTE|JOB_ACCESS_ADMINISTER)
-
-#define PRINTER_NOTIFY_OPTIONS_REFRESH 1
-#define PRINTER_ACCESS_ADMINISTER      4
-#define PRINTER_ACCESS_USE             8
+#ifdef __cplusplus
+extern "C" {
+#endif
 
-#define PRINTER_ERROR_INFORMATION 0x80000000
-#define PRINTER_ERROR_WARNING     0x40000000
-#define PRINTER_ERROR_SEVERE      0x20000000
-#define PRINTER_ERROR_OUTOFPAPER  1
-#define PRINTER_ERROR_JAM         2
-#define PRINTER_ERROR_OUTOFTONER  4
-
-#define PRINTER_CONTROL_PAUSE      1
-#define PRINTER_CONTROL_RESUME     2
-#define PRINTER_CONTROL_PURGE      3
+  typedef struct _PRINTER_INFO_1A {
+    DWORD Flags;
+    LPSTR pDescription;
+    LPSTR pName;
+    LPSTR pComment;
+  } PRINTER_INFO_1A,*PPRINTER_INFO_1A,*LPPRINTER_INFO_1A;
+
+  typedef struct _PRINTER_INFO_1W {
+    DWORD Flags;
+    LPWSTR pDescription;
+    LPWSTR pName;
+    LPWSTR pComment;
+  } PRINTER_INFO_1W,*PPRINTER_INFO_1W,*LPPRINTER_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(PRINTER_INFO_1)
+  __MINGW_TYPEDEF_AW(PPRINTER_INFO_1)
+  __MINGW_TYPEDEF_AW(LPPRINTER_INFO_1)
+
+  typedef struct _PRINTER_INFO_2A {
+    LPSTR pServerName;
+    LPSTR pPrinterName;
+    LPSTR pShareName;
+    LPSTR pPortName;
+    LPSTR pDriverName;
+    LPSTR pComment;
+    LPSTR pLocation;
+    LPDEVMODEA pDevMode;
+    LPSTR pSepFile;
+    LPSTR pPrintProcessor;
+    LPSTR pDatatype;
+    LPSTR pParameters;
+    PSECURITY_DESCRIPTOR pSecurityDescriptor;
+    DWORD Attributes;
+    DWORD Priority;
+    DWORD DefaultPriority;
+    DWORD StartTime;
+    DWORD UntilTime;
+    DWORD Status;
+    DWORD cJobs;
+    DWORD AveragePPM;
+  } PRINTER_INFO_2A,*PPRINTER_INFO_2A,*LPPRINTER_INFO_2A;
+
+  typedef struct _PRINTER_INFO_2W {
+    LPWSTR pServerName;
+    LPWSTR pPrinterName;
+    LPWSTR pShareName;
+    LPWSTR pPortName;
+    LPWSTR pDriverName;
+    LPWSTR pComment;
+    LPWSTR pLocation;
+    LPDEVMODEW pDevMode;
+    LPWSTR pSepFile;
+    LPWSTR pPrintProcessor;
+    LPWSTR pDatatype;
+    LPWSTR pParameters;
+    PSECURITY_DESCRIPTOR pSecurityDescriptor;
+    DWORD Attributes;
+    DWORD Priority;
+    DWORD DefaultPriority;
+    DWORD StartTime;
+    DWORD UntilTime;
+    DWORD Status;
+    DWORD cJobs;
+    DWORD AveragePPM;
+  } PRINTER_INFO_2W,*PPRINTER_INFO_2W,*LPPRINTER_INFO_2W;
+
+  __MINGW_TYPEDEF_AW(PRINTER_INFO_2)
+  __MINGW_TYPEDEF_AW(PPRINTER_INFO_2)
+  __MINGW_TYPEDEF_AW(LPPRINTER_INFO_2)
+
+  typedef struct _PRINTER_INFO_3 {
+    PSECURITY_DESCRIPTOR pSecurityDescriptor;
+  } PRINTER_INFO_3,*PPRINTER_INFO_3,*LPPRINTER_INFO_3;
+
+  typedef struct _PRINTER_INFO_4A {
+    LPSTR pPrinterName;
+    LPSTR pServerName;
+    DWORD Attributes;
+  } PRINTER_INFO_4A,*PPRINTER_INFO_4A,*LPPRINTER_INFO_4A;
+
+  typedef struct _PRINTER_INFO_4W {
+    LPWSTR pPrinterName;
+    LPWSTR pServerName;
+    DWORD Attributes;
+  } PRINTER_INFO_4W,*PPRINTER_INFO_4W,*LPPRINTER_INFO_4W;
+
+  __MINGW_TYPEDEF_AW(PRINTER_INFO_4)
+  __MINGW_TYPEDEF_AW(PPRINTER_INFO_4)
+  __MINGW_TYPEDEF_AW(LPPRINTER_INFO_4)
+
+  typedef struct _PRINTER_INFO_5A {
+    LPSTR pPrinterName;
+    LPSTR pPortName;
+    DWORD Attributes;
+    DWORD DeviceNotSelectedTimeout;
+    DWORD TransmissionRetryTimeout;
+  } PRINTER_INFO_5A,*PPRINTER_INFO_5A,*LPPRINTER_INFO_5A;
+
+  typedef struct _PRINTER_INFO_5W {
+    LPWSTR pPrinterName;
+    LPWSTR pPortName;
+    DWORD Attributes;
+    DWORD DeviceNotSelectedTimeout;
+    DWORD TransmissionRetryTimeout;
+  } PRINTER_INFO_5W,*PPRINTER_INFO_5W,*LPPRINTER_INFO_5W;
+
+  __MINGW_TYPEDEF_AW(PRINTER_INFO_5)
+  __MINGW_TYPEDEF_AW(PPRINTER_INFO_5)
+  __MINGW_TYPEDEF_AW(LPPRINTER_INFO_5)
+
+  typedef struct _PRINTER_INFO_6 {
+    DWORD dwStatus;
+  } PRINTER_INFO_6,*PPRINTER_INFO_6,*LPPRINTER_INFO_6;
+
+  typedef struct _PRINTER_INFO_7A {
+    LPSTR pszObjectGUID;
+    DWORD dwAction;
+  } PRINTER_INFO_7A,*PPRINTER_INFO_7A,*LPPRINTER_INFO_7A;
+
+  typedef struct _PRINTER_INFO_7W {
+    LPWSTR pszObjectGUID;
+    DWORD dwAction;
+  } PRINTER_INFO_7W,*PPRINTER_INFO_7W,*LPPRINTER_INFO_7W;
+
+  __MINGW_TYPEDEF_AW(PRINTER_INFO_7)
+  __MINGW_TYPEDEF_AW(PPRINTER_INFO_7)
+  __MINGW_TYPEDEF_AW(LPPRINTER_INFO_7)
+
+#define DSPRINT_PUBLISH 0x00000001
+#define DSPRINT_UPDATE 0x00000002
+#define DSPRINT_UNPUBLISH 0x00000004
+#define DSPRINT_REPUBLISH 0x00000008
+#define DSPRINT_PENDING 0x80000000
+
+  typedef struct _PRINTER_INFO_8A {
+    LPDEVMODEA pDevMode;
+  } PRINTER_INFO_8A,*PPRINTER_INFO_8A,*LPPRINTER_INFO_8A;
+
+  typedef struct _PRINTER_INFO_8W {
+    LPDEVMODEW pDevMode;
+  } PRINTER_INFO_8W,*PPRINTER_INFO_8W,*LPPRINTER_INFO_8W;
+
+  __MINGW_TYPEDEF_AW(PRINTER_INFO_8)
+  __MINGW_TYPEDEF_AW(PPRINTER_INFO_8)
+  __MINGW_TYPEDEF_AW(LPPRINTER_INFO_8)
+
+  typedef struct _PRINTER_INFO_9A {
+    LPDEVMODEA pDevMode;
+  } PRINTER_INFO_9A,*PPRINTER_INFO_9A,*LPPRINTER_INFO_9A;
+
+  typedef struct _PRINTER_INFO_9W {
+    LPDEVMODEW pDevMode;
+  } PRINTER_INFO_9W,*PPRINTER_INFO_9W,*LPPRINTER_INFO_9W;
+
+  __MINGW_TYPEDEF_AW(PRINTER_INFO_9)
+  __MINGW_TYPEDEF_AW(PPRINTER_INFO_9)
+  __MINGW_TYPEDEF_AW(LPPRINTER_INFO_9)
+
+#define PRINTER_CONTROL_PAUSE 1
+#define PRINTER_CONTROL_RESUME 2
+#define PRINTER_CONTROL_PURGE 3
 #define PRINTER_CONTROL_SET_STATUS 4
 
-#define PRINTER_STATUS_PAUSED            1
-#define PRINTER_STATUS_ERROR             2
-#define PRINTER_STATUS_PENDING_DELETION  4
-#define PRINTER_STATUS_PAPER_JAM         8
-#define PRINTER_STATUS_PAPER_OUT         0x10
-#define PRINTER_STATUS_MANUAL_FEED       0x20
-#define PRINTER_STATUS_PAPER_PROBLEM     0x40
-#define PRINTER_STATUS_OFFLINE           0x80
-#define PRINTER_STATUS_IO_ACTIVE         0x100
-#define PRINTER_STATUS_BUSY              0x200
-#define PRINTER_STATUS_PRINTING          0x400
-#define PRINTER_STATUS_OUTPUT_BIN_FULL   0x800
-#define PRINTER_STATUS_NOT_AVAILABLE     0x1000
-#define PRINTER_STATUS_WAITING           0x2000
-#define PRINTER_STATUS_PROCESSING        0x4000
-#define PRINTER_STATUS_INITIALIZING      0x8000
-#define PRINTER_STATUS_WARMING_UP        0x10000
-#define PRINTER_STATUS_TONER_LOW         0x20000
-#define PRINTER_STATUS_NO_TONER          0x40000
-#define PRINTER_STATUS_PAGE_PUNT         0x80000
-#define PRINTER_STATUS_USER_INTERVENTION 0x100000
-#define PRINTER_STATUS_OUT_OF_MEMORY     0x200000
-#define PRINTER_STATUS_DOOR_OPEN         0x400000
-#define PRINTER_STATUS_SERVER_UNKNOWN    0x800000
-#define PRINTER_STATUS_POWER_SAVE        0x1000000
-
-#define PRINTER_ATTRIBUTE_QUEUED            1
-#define PRINTER_ATTRIBUTE_DIRECT            2
-#define PRINTER_ATTRIBUTE_DEFAULT           4
-#define PRINTER_ATTRIBUTE_SHARED            8
-#define PRINTER_ATTRIBUTE_NETWORK           0x10
-#define PRINTER_ATTRIBUTE_HIDDEN            0x20
-#define PRINTER_ATTRIBUTE_LOCAL             0x40
-#define PRINTER_ATTRIBUTE_ENABLE_DEVQ       0x80
-#define PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS   0x100
-#define PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST 0x200
-#define PRINTER_ATTRIBUTE_WORK_OFFLINE      0x400
-#define PRINTER_ATTRIBUTE_ENABLE_BIDI       0x800
-#define PRINTER_ATTRIBUTE_RAW_ONLY          0x1000
-#define PRINTER_ATTRIBUTE_PUBLISHED         0x2000
-
-#define PRINTER_ENUM_DEFAULT     1
-#define PRINTER_ENUM_LOCAL       2
-#define PRINTER_ENUM_CONNECTIONS 4
-#define PRINTER_ENUM_FAVORITE    4
-#define PRINTER_ENUM_NAME        8
-#define PRINTER_ENUM_REMOTE      16
-#define PRINTER_ENUM_SHARED      32
-#define PRINTER_ENUM_NETWORK     0x40
-#define PRINTER_ENUM_EXPAND      0x4000
-#define PRINTER_ENUM_CONTAINER   0x8000
-#define PRINTER_ENUM_ICONMASK    0xff0000
-#define PRINTER_ENUM_ICON1       0x10000
-#define PRINTER_ENUM_ICON2       0x20000
-#define PRINTER_ENUM_ICON3       0x40000
-#define PRINTER_ENUM_ICON4       0x80000
-#define PRINTER_ENUM_ICON5       0x100000
-#define PRINTER_ENUM_ICON6       0x200000
-#define PRINTER_ENUM_ICON7       0x400000
-#define PRINTER_ENUM_ICON8       0x800000
-
-#define PRINTER_NOTIFY_TYPE                      0
-#define PRINTER_NOTIFY_FIELD_SERVER_NAME         0
-#define PRINTER_NOTIFY_FIELD_PRINTER_NAME        1
-#define PRINTER_NOTIFY_FIELD_SHARE_NAME          2
-#define PRINTER_NOTIFY_FIELD_PORT_NAME           3
-#define PRINTER_NOTIFY_FIELD_DRIVER_NAME         4
-#define PRINTER_NOTIFY_FIELD_COMMENT             5
-#define PRINTER_NOTIFY_FIELD_LOCATION            6
-#define PRINTER_NOTIFY_FIELD_DEVMODE             7
-#define PRINTER_NOTIFY_FIELD_SEPFILE             8
-#define PRINTER_NOTIFY_FIELD_PRINT_PROCESSOR     9
-#define PRINTER_NOTIFY_FIELD_PARAMETERS          10
-#define PRINTER_NOTIFY_FIELD_DATATYPE            11
-#define PRINTER_NOTIFY_FIELD_SECURITY_DESCRIPTOR 12
-#define PRINTER_NOTIFY_FIELD_ATTRIBUTES          13
-#define PRINTER_NOTIFY_FIELD_PRIORITY            14
-#define PRINTER_NOTIFY_FIELD_DEFAULT_PRIORITY    15
-#define PRINTER_NOTIFY_FIELD_START_TIME          16
-#define PRINTER_NOTIFY_FIELD_UNTIL_TIME          17
-#define PRINTER_NOTIFY_FIELD_STATUS              18
-#define PRINTER_NOTIFY_FIELD_STATUS_STRING       19
-#define PRINTER_NOTIFY_FIELD_CJOBS               20
-#define PRINTER_NOTIFY_FIELD_AVERAGE_PPM         21
-#define PRINTER_NOTIFY_FIELD_TOTAL_PAGES         22
-#define PRINTER_NOTIFY_FIELD_PAGES_PRINTED       23
-#define PRINTER_NOTIFY_FIELD_TOTAL_BYTES         24
-#define PRINTER_NOTIFY_FIELD_BYTES_PRINTED       25
-
-#define PRINTER_CHANGE_ADD_PRINTER               1
-#define PRINTER_CHANGE_SET_PRINTER               2
-#define PRINTER_CHANGE_DELETE_PRINTER            4
-#define PRINTER_CHANGE_FAILED_CONNECTION_PRINTER 8
-#define PRINTER_CHANGE_PRINTER                   0xFF
-#define PRINTER_CHANGE_ADD_JOB                   0x100
-#define PRINTER_CHANGE_SET_JOB                   0x200
-#define PRINTER_CHANGE_DELETE_JOB                0x400
-#define PRINTER_CHANGE_WRITE_JOB                 0x800
-#define PRINTER_CHANGE_JOB                       0xFF00
-#define PRINTER_CHANGE_ADD_FORM                  0x10000
-#define PRINTER_CHANGE_SET_FORM                  0x20000
-#define PRINTER_CHANGE_DELETE_FORM               0x40000
-#define PRINTER_CHANGE_FORM                      0x70000
-#define PRINTER_CHANGE_ADD_PORT                  0x100000
-#define PRINTER_CHANGE_CONFIGURE_PORT            0x200000
-#define PRINTER_CHANGE_DELETE_PORT               0x400000
-#define PRINTER_CHANGE_PORT                      0x700000
-#define PRINTER_CHANGE_ADD_PRINT_PROCESSOR       0x1000000
-#define PRINTER_CHANGE_DELETE_PRINT_PROCESSOR    0x4000000
-#define PRINTER_CHANGE_PRINT_PROCESSOR           0x7000000
-#define PRINTER_CHANGE_ADD_PRINTER_DRIVER        0x10000000
-#define PRINTER_CHANGE_SET_PRINTER_DRIVER        0x20000000
-#define PRINTER_CHANGE_DELETE_PRINTER_DRIVER     0x40000000
-#define PRINTER_CHANGE_PRINTER_DRIVER            0x70000000
-#define PRINTER_CHANGE_TIMEOUT                   0x80000000
-#define PRINTER_CHANGE_ALL                       0x7777FFFF
-
-#define PRINTER_NOTIFY_INFO_DISCARDED 1
-#define PRINTER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|PRINTER_ACCESS_ADMINISTER|PRINTER_ACCESS_USE)
-#define PRINTER_READ (STANDARD_RIGHTS_READ|PRINTER_ACCESS_USE)
-#define PRINTER_WRITE (STANDARD_RIGHTS_WRITE|PRINTER_ACCESS_USE)
-#define PRINTER_EXECUTE (STANDARD_RIGHTS_EXECUTE|PRINTER_ACCESS_USE)
-
-#define NO_PRIORITY  0
+#define PRINTER_STATUS_PAUSED 0x00000001
+#define PRINTER_STATUS_ERROR 0x00000002
+#define PRINTER_STATUS_PENDING_DELETION 0x00000004
+#define PRINTER_STATUS_PAPER_JAM 0x00000008
+#define PRINTER_STATUS_PAPER_OUT 0x00000010
+#define PRINTER_STATUS_MANUAL_FEED 0x00000020
+#define PRINTER_STATUS_PAPER_PROBLEM 0x00000040
+#define PRINTER_STATUS_OFFLINE 0x00000080
+#define PRINTER_STATUS_IO_ACTIVE 0x00000100
+#define PRINTER_STATUS_BUSY 0x00000200
+#define PRINTER_STATUS_PRINTING 0x00000400
+#define PRINTER_STATUS_OUTPUT_BIN_FULL 0x00000800
+#define PRINTER_STATUS_NOT_AVAILABLE 0x00001000
+#define PRINTER_STATUS_WAITING 0x00002000
+#define PRINTER_STATUS_PROCESSING 0x00004000
+#define PRINTER_STATUS_INITIALIZING 0x00008000
+#define PRINTER_STATUS_WARMING_UP 0x00010000
+#define PRINTER_STATUS_TONER_LOW 0x00020000
+#define PRINTER_STATUS_NO_TONER 0x00040000
+#define PRINTER_STATUS_PAGE_PUNT 0x00080000
+#define PRINTER_STATUS_USER_INTERVENTION 0x00100000
+#define PRINTER_STATUS_OUT_OF_MEMORY 0x00200000
+#define PRINTER_STATUS_DOOR_OPEN 0x00400000
+#define PRINTER_STATUS_SERVER_UNKNOWN 0x00800000
+#define PRINTER_STATUS_POWER_SAVE 0x01000000
+
+#define PRINTER_ATTRIBUTE_QUEUED 0x00000001
+#define PRINTER_ATTRIBUTE_DIRECT 0x00000002
+#define PRINTER_ATTRIBUTE_DEFAULT 0x00000004
+#define PRINTER_ATTRIBUTE_SHARED 0x00000008
+#define PRINTER_ATTRIBUTE_NETWORK 0x00000010
+#define PRINTER_ATTRIBUTE_HIDDEN 0x00000020
+#define PRINTER_ATTRIBUTE_LOCAL 0x00000040
+
+#define PRINTER_ATTRIBUTE_ENABLE_DEVQ 0x00000080
+#define PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS 0x00000100
+#define PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST 0x00000200
+
+#define PRINTER_ATTRIBUTE_WORK_OFFLINE 0x00000400
+#define PRINTER_ATTRIBUTE_ENABLE_BIDI 0x00000800
+#define PRINTER_ATTRIBUTE_RAW_ONLY 0x00001000
+#define PRINTER_ATTRIBUTE_PUBLISHED 0x00002000
+#define PRINTER_ATTRIBUTE_FAX 0x00004000
+#define PRINTER_ATTRIBUTE_TS 0x00008000
+
+#define NO_PRIORITY 0
 #define MAX_PRIORITY 99
 #define MIN_PRIORITY 1
 #define DEF_PRIORITY 1
 
-#define PORT_TYPE_WRITE        1
-#define PORT_TYPE_READ         2
-#define PORT_TYPE_REDIRECTED   4
-#define PORT_TYPE_NET_ATTACHED 8
-
-#define SERVER_ACCESS_ADMINISTER 1
-#define SERVER_ACCESS_ENUMERATE  2
-#define SERVER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SERVER_ACCESS_ADMINISTER|SERVER_ACCESS_ENUMERATE)
-#define SERVER_READ (STANDARD_RIGHTS_READ|SERVER_ACCESS_ENUMERATE)
-#define SERVER_WRITE (STANDARD_RIGHTS_WRITE|SERVER_ACCESS_ADMINISTER|SERVER_ACCESS_ENUMERATE)
-#define SERVER_EXECUTE (STANDARD_RIGHTS_EXECUTE|SERVER_ACCESS_ENUMERATE)
-
-#define PORT_STATUS_TYPE_ERROR        1
-#define PORT_STATUS_TYPE_WARNING      2
-#define PORT_STATUS_TYPE_INFO         3
-#define PORT_STATUS_OFFLINE           1
-#define PORT_STATUS_PAPER_JAM         2
-#define PORT_STATUS_PAPER_OUT         3
-#define PORT_STATUS_OUTPUT_BIN_FULL   4
-#define PORT_STATUS_PAPER_PROBLEM     5
-#define PORT_STATUS_NO_TONER          6
-#define PORT_STATUS_DOOR_OPEN         7
-#define PORT_STATUS_USER_INTERVENTION 8
-#define PORT_STATUS_OUT_OF_MEMORY     9
-#define PORT_STATUS_TONER_LOW         10
-#define PORT_STATUS_WARMING_UP        11
-#define PORT_STATUS_POWER_SAVE        12
-
-typedef struct _PRINTER_NOTIFY_OPTIONS_TYPE {
-  WORD Type;
-  WORD Reserved0;
-  DWORD Reserved1;
-  DWORD Reserved2;
-  DWORD Count;
-  PWORD pFields;
-} PRINTER_NOTIFY_OPTIONS_TYPE, *PPRINTER_NOTIFY_OPTIONS_TYPE, *LPPRINTER_NOTIFY_OPTIONS_TYPE;
-
-typedef struct _PRINTER_NOTIFY_OPTIONS {
-  DWORD Version;
-  DWORD Flags;
-  DWORD Count;
-  PPRINTER_NOTIFY_OPTIONS_TYPE pTypes;
-} PRINTER_NOTIFY_OPTIONS, *PPRINTER_NOTIFY_OPTIONS, *LPPRINTER_NOTIFY_OPTIONS;
-
-#ifndef RC_INVOKED
-
-typedef struct _ADDJOB_INFO_1A {
-  LPSTR Path;
-  DWORD JobId;
-} ADDJOB_INFO_1A, *PADDJOB_INFO_1A, *LPADDJOB_INFO_1A;
-
-typedef struct _ADDJOB_INFO_1W {
-  LPWSTR Path;
-  DWORD JobId;
-} ADDJOB_INFO_1W, *PADDJOB_INFO_1W, *LPADDJOB_INFO_1W;
-
-typedef struct _DATATYPES_INFO_1A {
-  LPSTR pName;
-} DATATYPES_INFO_1A, *PDATATYPES_INFO_1A, *LPDATATYPES_INFO_1A;
-
-typedef struct _DATATYPES_INFO_1W {
-  LPWSTR pName;
-} DATATYPES_INFO_1W, *PDATATYPES_INFO_1W, *LPDATATYPES_INFO_1W;
-
-typedef struct _JOB_INFO_1A {
-  DWORD JobId;
-  LPSTR pPrinterName;
-  LPSTR pMachineName;
-  LPSTR pUserName;
-  LPSTR pDocument;
-  LPSTR pDatatype;
-  LPSTR pStatus;
-  DWORD Status;
-  DWORD Priority;
-  DWORD Position;
-  DWORD TotalPages;
-  DWORD PagesPrinted;
-  SYSTEMTIME Submitted;
-} JOB_INFO_1A, *PJOB_INFO_1A, *LPJOB_INFO_1A;
-
-typedef struct _JOB_INFO_1W {
-  DWORD JobId;
-  LPWSTR pPrinterName;
-  LPWSTR pMachineName;
-  LPWSTR pUserName;
-  LPWSTR pDocument;
-  LPWSTR pDatatype;
-  LPWSTR pStatus;
-  DWORD Status;
-  DWORD Priority;
-  DWORD Position;
-  DWORD TotalPages;
-  DWORD PagesPrinted;
-  SYSTEMTIME Submitted;
-} JOB_INFO_1W, *PJOB_INFO_1W, *LPJOB_INFO_1W;
-
-typedef struct _JOB_INFO_2A {
-  DWORD JobId;
-  LPSTR pPrinterName;
-  LPSTR pMachineName;
-  LPSTR pUserName;
-  LPSTR pDocument;
-  LPSTR pNotifyName;
-  LPSTR pDatatype;
-  LPSTR pPrintProcessor;
-  LPSTR pParameters;
-  LPSTR pDriverName;
-  LPDEVMODEA pDevMode;
-  LPSTR pStatus;
-  PSECURITY_DESCRIPTOR pSecurityDescriptor;
-  DWORD Status;
-  DWORD Priority;
-  DWORD Position;
-  DWORD StartTime;
-  DWORD UntilTime;
-  DWORD TotalPages;
-  DWORD Size;
-  SYSTEMTIME Submitted;
-  DWORD Time;
-  DWORD PagesPrinted;
-} JOB_INFO_2A, *PJOB_INFO_2A, *LPJOB_INFO_2A;
-
-typedef struct _JOB_INFO_2W {
-  DWORD JobId;
-  LPWSTR pPrinterName;
-  LPWSTR pMachineName;
-  LPWSTR pUserName;
-  LPWSTR pDocument;
-  LPWSTR pNotifyName;
-  LPWSTR pDatatype;
-  LPWSTR pPrintProcessor;
-  LPWSTR pParameters;
-  LPWSTR pDriverName;
-  LPDEVMODEW pDevMode;
-  LPWSTR pStatus;
-  PSECURITY_DESCRIPTOR pSecurityDescriptor;
-  DWORD Status;
-  DWORD Priority;
-  DWORD Position;
-  DWORD StartTime;
-  DWORD UntilTime;
-  DWORD TotalPages;
-  DWORD Size;
-  SYSTEMTIME Submitted;
-  DWORD Time;
-  DWORD PagesPrinted;
-} JOB_INFO_2W, *PJOB_INFO_2W, *LPJOB_INFO_2W;
-
-typedef struct _JOB_INFO_3 {
-  DWORD JobId;
-  DWORD NextJobId;
-  DWORD Reserved;
-} JOB_INFO_3, *PJOB_INFO_3, *LPJOB_INFO_3;
-
-typedef struct _JOB_INFO_4A {
-  DWORD JobId;
-  LPSTR pPrinterName;
-  LPSTR pMachineName;
-  LPSTR pUserName;
-  LPSTR pDocument;
-  LPSTR pNotifyName;
-  LPSTR pDatatype;
-  LPSTR pPrintProcessor;
-  LPSTR pParameters;
-  LPSTR pDriverName;
-  LPDEVMODEA pDevMode;
-  LPSTR pStatus;
-  PSECURITY_DESCRIPTOR pSecurityDescriptor;
-  DWORD Status;
-  DWORD Priority;
-  DWORD Position;
-  DWORD StartTime;
-  DWORD UntilTime;
-  DWORD TotalPages;
-  DWORD Size;
-  SYSTEMTIME Submitted;
-  DWORD Time;
-  DWORD PagesPrinted;
-  LONG SizeHigh;
-} JOB_INFO_4A, *PJOB_INFO_4A, *LPJOB_INFO_4A;
-
-typedef struct _JOB_INFO_4W {
-  DWORD JobId;
-  LPWSTR pPrinterName;
-  LPWSTR pMachineName;
-  LPWSTR pUserName;
-  LPWSTR pDocument;
-  LPWSTR pNotifyName;
-  LPWSTR pDatatype;
-  LPWSTR pPrintProcessor;
-  LPWSTR pParameters;
-  LPWSTR pDriverName;
-  LPDEVMODEW pDevMode;
-  LPWSTR pStatus;
-  PSECURITY_DESCRIPTOR pSecurityDescriptor;
-  DWORD Status;
-  DWORD Priority;
-  DWORD Position;
-  DWORD StartTime;
-  DWORD UntilTime;
-  DWORD TotalPages;
-  DWORD Size;
-  SYSTEMTIME Submitted;
-  DWORD Time;
-  DWORD PagesPrinted;
-  LONG SizeHigh;
-} JOB_INFO_4W, *PJOB_INFO_4W, *LPJOB_INFO_4W;
-
-typedef struct _DOC_INFO_1A {
-  LPSTR pDocName;
-  LPSTR pOutputFile;
-  LPSTR pDatatype;
-} DOC_INFO_1A, *PDOC_INFO_1A, *LPDOC_INFO_1A;
-
-typedef struct _DOC_INFO_1W {
-  LPWSTR pDocName;
-  LPWSTR pOutputFile;
-  LPWSTR pDatatype;
-} DOC_INFO_1W, *PDOC_INFO_1W, *LPDOC_INFO_1W;
-
-typedef struct _DOC_INFO_2A {
-  LPSTR pDocName;
-  LPSTR pOutputFile;
-  LPSTR pDatatype;
-  DWORD dwMode;
-  DWORD JobId;
-} DOC_INFO_2A, *PDOC_INFO_2A, *LPDOC_INFO_2A;
-
-typedef struct _DOC_INFO_2W {
-  LPWSTR pDocName;
-  LPWSTR pOutputFile;
-  LPWSTR pDatatype;
-  DWORD dwMode;
-  DWORD JobId;
-} DOC_INFO_2W, *PDOC_INFO_2W, *LPDOC_INFO_2W;
-
-typedef struct _DRIVER_INFO_1A {
-  LPSTR pName;
-} DRIVER_INFO_1A, *PDRIVER_INFO_1A, *LPDRIVER_INFO_1A;
-
-typedef struct _DRIVER_INFO_1W {
-  LPWSTR pName;
-} DRIVER_INFO_1W, *PDRIVER_INFO_1W, *LPDRIVER_INFO_1W;
-
-typedef struct _DRIVER_INFO_2A {
-  DWORD cVersion;
-  LPSTR pName;
-  LPSTR pEnvironment;
-  LPSTR pDriverPath;
-  LPSTR pDataFile;
-  LPSTR pConfigFile;
-} DRIVER_INFO_2A, *PDRIVER_INFO_2A, *LPDRIVER_INFO_2A;
-
-typedef struct _DRIVER_INFO_2W {
-  DWORD cVersion;
-  LPWSTR pName;
-  LPWSTR pEnvironment;
-  LPWSTR pDriverPath;
-  LPWSTR pDataFile;
-  LPWSTR pConfigFile;
-} DRIVER_INFO_2W, *PDRIVER_INFO_2W, *LPDRIVER_INFO_2W;
-
-typedef struct _DRIVER_INFO_3A {
-  DWORD cVersion;
-  LPSTR pName;
-  LPSTR pEnvironment;
-  LPSTR pDriverPath;
-  LPSTR pDataFile;
-  LPSTR pConfigFile;
-  LPSTR pHelpFile;
-  LPSTR pDependentFiles;
-  LPSTR pMonitorName;
-  LPSTR pDefaultDataType;
-} DRIVER_INFO_3A, *PDRIVER_INFO_3A, *LPDRIVER_INFO_3A;
-
-typedef struct _DRIVER_INFO_3W {
-  DWORD cVersion;
-  LPWSTR pName;
-  LPWSTR pEnvironment;
-  LPWSTR pDriverPath;
-  LPWSTR pDataFile;
-  LPWSTR pConfigFile;
-  LPWSTR pHelpFile;
-  LPWSTR pDependentFiles;
-  LPWSTR pMonitorName;
-  LPWSTR pDefaultDataType;
-} DRIVER_INFO_3W, *PDRIVER_INFO_3W, *LPDRIVER_INFO_3W;
-
-typedef struct _DRIVER_INFO_4A {
-  DWORD cVersion;          // SDK examples:
-  LPSTR pName;             // QMS 810
-  LPSTR pEnvironment;      // Win32 x86
-  LPSTR pDriverPath;       // c:\drivers\pscript.dll
-  LPSTR pDataFile;         // c:\drivers\QMS810.PPD
-  LPSTR pConfigFile;       // c:\drivers\PSCRPTUI.DLL
-  LPSTR pHelpFile;         // c:\drivers\PSCRPTUI.HLP
-  LPSTR pDependentFiles;   // PSCRIPT.DLL\0QMS810.PPD\0PSCRIPTUI.DLL\0PSCRIPTUI.HLP\0PSTEST.TXT\0\0
-  LPSTR pMonitorName;      // "PJL monitor"
-  LPSTR pDefaultDataType;  // "EMF"
-  LPSTR pszzPreviousNames; // "OldName1\0OldName2\0\0
-} DRIVER_INFO_4A, *PDRIVER_INFO_4A, *LPDRIVER_INFO_4A;
-
-typedef struct _DRIVER_INFO_4W {
-  DWORD cVersion;
-  LPWSTR pName;
-  LPWSTR pEnvironment;
-  LPWSTR pDriverPath;
-  LPWSTR pDataFile;
-  LPWSTR pConfigFile;
-  LPWSTR pHelpFile;
-  LPWSTR pDependentFiles;
-  LPWSTR pMonitorName;
-  LPWSTR pDefaultDataType;
-  LPWSTR pszzPreviousNames;
-} DRIVER_INFO_4W, *PDRIVER_INFO_4W, *LPDRIVER_INFO_4W;
-
-typedef struct _DRIVER_INFO_5A {
-  DWORD cVersion;           // SDK examples:
-  LPSTR pName;              // QMS 810
-  LPSTR pEnvironment;       // Win32 x86
-  LPSTR pDriverPath;        // c:\drivers\pscript.dll
-  LPSTR pDataFile;          // c:\drivers\QMS810.PPD
-  LPSTR pConfigFile;        // c:\drivers\PSCRPTUI.DLL
-  DWORD dwDriverAttributes; // driver attributes (like UMPD/KMPD)
-  DWORD dwConfigVersion;    // version number of the config file since reboot
-  DWORD dwDriverVersion;    // version number of the driver file since reboot
-} DRIVER_INFO_5A, *PDRIVER_INFO_5A, *LPDRIVER_INFO_5A;
-
-typedef struct _DRIVER_INFO_5W {
-  DWORD cVersion;
-  LPWSTR pName;
-  LPWSTR pEnvironment;
-  LPWSTR pDriverPath;
-  LPWSTR pDataFile;
-  LPWSTR pConfigFile;
-  DWORD dwDriverAttributes;
-  DWORD dwConfigVersion;
-  DWORD dwDriverVersion;
-} DRIVER_INFO_5W, *PDRIVER_INFO_5W, *LPDRIVER_INFO_5W;
-
-typedef struct _DRIVER_INFO_6A {
-  DWORD cVersion;
-  LPSTR pName;
-  LPSTR pEnvironment;
-  LPSTR pDriverPath;
-  LPSTR pDataFile;
-  LPSTR pConfigFile;
-  LPSTR pHelpFile;
-  LPSTR pDependentFiles;
-  LPSTR pMonitorName;
-  LPSTR pDefaultDataType;
-  LPSTR pszzPreviousNames;
-  FILETIME ftDriverDate;
-  DWORDLONG dwlDriverVersion;
-  LPSTR pszMfgName;
-  LPSTR pszOEMUrl;
-  LPSTR pszHardwareID;
-  LPSTR pszProvider;
-} DRIVER_INFO_6A, *PDRIVER_INFO_6A, *LPDRIVER_INFO_6A;
-
-typedef struct _DRIVER_INFO_6W {
-  DWORD cVersion;
-  LPWSTR pName;
-  LPWSTR pEnvironment;
-  LPWSTR pDriverPath;
-  LPWSTR pDataFile;
-  LPWSTR pConfigFile;
-  LPWSTR pHelpFile;
-  LPWSTR pDependentFiles;
-  LPWSTR pMonitorName;
-  LPWSTR pDefaultDataType;
-  LPWSTR pszzPreviousNames;
-  FILETIME ftDriverDate;
-  DWORDLONG dwlDriverVersion;
-  LPWSTR pszMfgName;
-  LPWSTR pszOEMUrl;
-  LPWSTR pszHardwareID;
-  LPWSTR pszProvider;
-} DRIVER_INFO_6W, *PDRIVER_INFO_6W, *LPDRIVER_INFO_6W;
+  typedef struct _JOB_INFO_1A {
+    DWORD JobId;
+    LPSTR pPrinterName;
+    LPSTR pMachineName;
+    LPSTR pUserName;
+    LPSTR pDocument;
+    LPSTR pDatatype;
+    LPSTR pStatus;
+    DWORD Status;
+    DWORD Priority;
+    DWORD Position;
+    DWORD TotalPages;
+    DWORD PagesPrinted;
+    SYSTEMTIME Submitted;
+  } JOB_INFO_1A,*PJOB_INFO_1A,*LPJOB_INFO_1A;
+
+  typedef struct _JOB_INFO_1W {
+    DWORD JobId;
+    LPWSTR pPrinterName;
+    LPWSTR pMachineName;
+    LPWSTR pUserName;
+    LPWSTR pDocument;
+    LPWSTR pDatatype;
+    LPWSTR pStatus;
+    DWORD Status;
+    DWORD Priority;
+    DWORD Position;
+    DWORD TotalPages;
+    DWORD PagesPrinted;
+    SYSTEMTIME Submitted;
+  } JOB_INFO_1W,*PJOB_INFO_1W,*LPJOB_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(JOB_INFO_1)
+  __MINGW_TYPEDEF_AW(PJOB_INFO_1)
+  __MINGW_TYPEDEF_AW(LPJOB_INFO_1)
+
+  typedef struct _JOB_INFO_2A {
+    DWORD JobId;
+    LPSTR pPrinterName;
+    LPSTR pMachineName;
+    LPSTR pUserName;
+    LPSTR pDocument;
+    LPSTR pNotifyName;
+    LPSTR pDatatype;
+    LPSTR pPrintProcessor;
+    LPSTR pParameters;
+    LPSTR pDriverName;
+    LPDEVMODEA pDevMode;
+    LPSTR pStatus;
+    PSECURITY_DESCRIPTOR pSecurityDescriptor;
+    DWORD Status;
+    DWORD Priority;
+    DWORD Position;
+    DWORD StartTime;
+    DWORD UntilTime;
+    DWORD TotalPages;
+    DWORD Size;
+    SYSTEMTIME Submitted;
+    DWORD Time;
+    DWORD PagesPrinted;
+  } JOB_INFO_2A,*PJOB_INFO_2A,*LPJOB_INFO_2A;
+
+  typedef struct _JOB_INFO_2W {
+    DWORD JobId;
+    LPWSTR pPrinterName;
+    LPWSTR pMachineName;
+    LPWSTR pUserName;
+    LPWSTR pDocument;
+    LPWSTR pNotifyName;
+    LPWSTR pDatatype;
+    LPWSTR pPrintProcessor;
+    LPWSTR pParameters;
+    LPWSTR pDriverName;
+    LPDEVMODEW pDevMode;
+    LPWSTR pStatus;
+    PSECURITY_DESCRIPTOR pSecurityDescriptor;
+    DWORD Status;
+    DWORD Priority;
+    DWORD Position;
+    DWORD StartTime;
+    DWORD UntilTime;
+    DWORD TotalPages;
+    DWORD Size;
+    SYSTEMTIME Submitted;
+    DWORD Time;
+    DWORD PagesPrinted;
+  } JOB_INFO_2W,*PJOB_INFO_2W,*LPJOB_INFO_2W;
+
+  __MINGW_TYPEDEF_AW(JOB_INFO_2)
+  __MINGW_TYPEDEF_AW(PJOB_INFO_2)
+  __MINGW_TYPEDEF_AW(LPJOB_INFO_2)
+
+  typedef struct _JOB_INFO_3 {
+    DWORD JobId;
+    DWORD NextJobId;
+    DWORD Reserved;
+  } JOB_INFO_3,*PJOB_INFO_3,*LPJOB_INFO_3;
+
+#define JOB_CONTROL_PAUSE 1
+#define JOB_CONTROL_RESUME 2
+#define JOB_CONTROL_CANCEL 3
+#define JOB_CONTROL_RESTART 4
+#define JOB_CONTROL_DELETE 5
+#define JOB_CONTROL_SENT_TO_PRINTER 6
+#define JOB_CONTROL_LAST_PAGE_EJECTED 7
+
+#define JOB_STATUS_PAUSED 0x00000001
+#define JOB_STATUS_ERROR 0x00000002
+#define JOB_STATUS_DELETING 0x00000004
+#define JOB_STATUS_SPOOLING 0x00000008
+#define JOB_STATUS_PRINTING 0x00000010
+#define JOB_STATUS_OFFLINE 0x00000020
+#define JOB_STATUS_PAPEROUT 0x00000040
+#define JOB_STATUS_PRINTED 0x00000080
+#define JOB_STATUS_DELETED 0x00000100
+#define JOB_STATUS_BLOCKED_DEVQ 0x00000200
+#define JOB_STATUS_USER_INTERVENTION 0x00000400
+#define JOB_STATUS_RESTART 0x00000800
+#define JOB_STATUS_COMPLETE 0x00001000
+
+#define JOB_POSITION_UNSPECIFIED 0
+
+  typedef struct _ADDJOB_INFO_1A {
+    LPSTR Path;
+    DWORD JobId;
+  } ADDJOB_INFO_1A,*PADDJOB_INFO_1A,*LPADDJOB_INFO_1A;
+
+  typedef struct _ADDJOB_INFO_1W {
+    LPWSTR Path;
+    DWORD JobId;
+  } ADDJOB_INFO_1W,*PADDJOB_INFO_1W,*LPADDJOB_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(ADDJOB_INFO_1)
+  __MINGW_TYPEDEF_AW(PADDJOB_INFO_1)
+  __MINGW_TYPEDEF_AW(LPADDJOB_INFO_1)
+
+  typedef struct _DRIVER_INFO_1A {
+    LPSTR pName;
+  } DRIVER_INFO_1A,*PDRIVER_INFO_1A,*LPDRIVER_INFO_1A;
+
+  typedef struct _DRIVER_INFO_1W {
+    LPWSTR pName;
+  } DRIVER_INFO_1W,*PDRIVER_INFO_1W,*LPDRIVER_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(DRIVER_INFO_1)
+  __MINGW_TYPEDEF_AW(PDRIVER_INFO_1)
+  __MINGW_TYPEDEF_AW(LPDRIVER_INFO_1)
+
+  typedef struct _DRIVER_INFO_2A {
+    DWORD cVersion;
+    LPSTR pName;
+    LPSTR pEnvironment;
+    LPSTR pDriverPath;
+    LPSTR pDataFile;
+    LPSTR pConfigFile;
+  } DRIVER_INFO_2A,*PDRIVER_INFO_2A,*LPDRIVER_INFO_2A;
+
+  typedef struct _DRIVER_INFO_2W {
+    DWORD cVersion;
+    LPWSTR pName;
+    LPWSTR pEnvironment;
+    LPWSTR pDriverPath;
+    LPWSTR pDataFile;
+    LPWSTR pConfigFile;
+  } DRIVER_INFO_2W,*PDRIVER_INFO_2W,*LPDRIVER_INFO_2W;
+
+  __MINGW_TYPEDEF_AW(DRIVER_INFO_2)
+  __MINGW_TYPEDEF_AW(PDRIVER_INFO_2)
+  __MINGW_TYPEDEF_AW(LPDRIVER_INFO_2)
+
+  typedef struct _DRIVER_INFO_3A {
+    DWORD cVersion;
+    LPSTR pName;
+    LPSTR pEnvironment;
+    LPSTR pDriverPath;
+    LPSTR pDataFile;
+    LPSTR pConfigFile;
+    LPSTR pHelpFile;
+    LPSTR pDependentFiles;
+    LPSTR pMonitorName;
+    LPSTR pDefaultDataType;
+  } DRIVER_INFO_3A,*PDRIVER_INFO_3A,*LPDRIVER_INFO_3A;
+
+  typedef struct _DRIVER_INFO_3W {
+    DWORD cVersion;
+    LPWSTR pName;
+    LPWSTR pEnvironment;
+    LPWSTR pDriverPath;
+    LPWSTR pDataFile;
+    LPWSTR pConfigFile;
+    LPWSTR pHelpFile;
+    LPWSTR pDependentFiles;
+    LPWSTR pMonitorName;
+    LPWSTR pDefaultDataType;
+  } DRIVER_INFO_3W,*PDRIVER_INFO_3W,*LPDRIVER_INFO_3W;
+
+  __MINGW_TYPEDEF_AW(DRIVER_INFO_3)
+  __MINGW_TYPEDEF_AW(PDRIVER_INFO_3)
+  __MINGW_TYPEDEF_AW(LPDRIVER_INFO_3)
+
+  typedef struct _DRIVER_INFO_4A {
+    DWORD cVersion;
+    LPSTR pName;
+    LPSTR pEnvironment;
+    LPSTR pDriverPath;
+    LPSTR pDataFile;
+    LPSTR pConfigFile;
+    LPSTR pHelpFile;
+    LPSTR pDependentFiles;
+    LPSTR pMonitorName;
+    LPSTR pDefaultDataType;
+    LPSTR pszzPreviousNames;
+  } DRIVER_INFO_4A,*PDRIVER_INFO_4A,*LPDRIVER_INFO_4A;
+
+  typedef struct _DRIVER_INFO_4W {
+    DWORD cVersion;
+    LPWSTR pName;
+    LPWSTR pEnvironment;
+    LPWSTR pDriverPath;
+    LPWSTR pDataFile;
+    LPWSTR pConfigFile;
+    LPWSTR pHelpFile;
+    LPWSTR pDependentFiles;
+    LPWSTR pMonitorName;
+    LPWSTR pDefaultDataType;
+    LPWSTR pszzPreviousNames;
+  } DRIVER_INFO_4W,*PDRIVER_INFO_4W,*LPDRIVER_INFO_4W;
+
+  __MINGW_TYPEDEF_AW(DRIVER_INFO_4)
+  __MINGW_TYPEDEF_AW(PDRIVER_INFO_4)
+  __MINGW_TYPEDEF_AW(LPDRIVER_INFO_4)
+
+  typedef struct _DRIVER_INFO_5A {
+    DWORD cVersion;
+    LPSTR pName;
+    LPSTR pEnvironment;
+    LPSTR pDriverPath;
+    LPSTR pDataFile;
+    LPSTR pConfigFile;
+    DWORD dwDriverAttributes;
+    DWORD dwConfigVersion;
+    DWORD dwDriverVersion;
+  } DRIVER_INFO_5A,*PDRIVER_INFO_5A,*LPDRIVER_INFO_5A;
+
+  typedef struct _DRIVER_INFO_5W {
+    DWORD cVersion;
+    LPWSTR pName;
+    LPWSTR pEnvironment;
+    LPWSTR pDriverPath;
+    LPWSTR pDataFile;
+    LPWSTR pConfigFile;
+    DWORD dwDriverAttributes;
+    DWORD dwConfigVersion;
+    DWORD dwDriverVersion;
+  } DRIVER_INFO_5W,*PDRIVER_INFO_5W,*LPDRIVER_INFO_5W;
+
+  __MINGW_TYPEDEF_AW(DRIVER_INFO_5)
+  __MINGW_TYPEDEF_AW(PDRIVER_INFO_5)
+  __MINGW_TYPEDEF_AW(LPDRIVER_INFO_5)
+
+  typedef struct _DRIVER_INFO_6A {
+    DWORD cVersion;
+    LPSTR pName;
+    LPSTR pEnvironment;
+    LPSTR pDriverPath;
+    LPSTR pDataFile;
+    LPSTR pConfigFile;
+    LPSTR pHelpFile;
+    LPSTR pDependentFiles;
+    LPSTR pMonitorName;
+    LPSTR pDefaultDataType;
+    LPSTR pszzPreviousNames;
+    FILETIME ftDriverDate;
+    DWORDLONG dwlDriverVersion;
+    LPSTR pszMfgName;
+    LPSTR pszOEMUrl;
+    LPSTR pszHardwareID;
+    LPSTR pszProvider;
+  } DRIVER_INFO_6A,*PDRIVER_INFO_6A,*LPDRIVER_INFO_6A;
+
+  typedef struct _DRIVER_INFO_6W {
+    DWORD cVersion;
+    LPWSTR pName;
+    LPWSTR pEnvironment;
+    LPWSTR pDriverPath;
+    LPWSTR pDataFile;
+    LPWSTR pConfigFile;
+    LPWSTR pHelpFile;
+    LPWSTR pDependentFiles;
+    LPWSTR pMonitorName;
+    LPWSTR pDefaultDataType;
+    LPWSTR pszzPreviousNames;
+    FILETIME ftDriverDate;
+    DWORDLONG dwlDriverVersion;
+    LPWSTR pszMfgName;
+    LPWSTR pszOEMUrl;
+    LPWSTR pszHardwareID;
+    LPWSTR pszProvider;
+  } DRIVER_INFO_6W,*PDRIVER_INFO_6W,*LPDRIVER_INFO_6W;
+
+  __MINGW_TYPEDEF_AW(DRIVER_INFO_6)
+  __MINGW_TYPEDEF_AW(PDRIVER_INFO_6)
+  __MINGW_TYPEDEF_AW(LPDRIVER_INFO_6)
 
-#define PRINTER_DRIVER_PACKAGE_AWARE 0x00000001
+#define DRIVER_KERNELMODE 0x00000001
+#define DRIVER_USERMODE 0x00000002
 
-typedef struct _DRIVER_INFO_8A {
-  DWORD cVersion;
-  LPSTR pName;
-  LPSTR pEnvironment;
-  LPSTR pDriverPath;
-  LPSTR pDataFile;
-  LPSTR pConfigFile;
-  LPSTR pHelpFile;
-  LPSTR pDependentFiles;
-  LPSTR pMonitorName;
-  LPSTR pDefaultDataType;
-  LPSTR pszzPreviousNames;
-  FILETIME ftDriverDate;
-  DWORDLONG dwlDriverVersion;
-  LPSTR pszMfgName;
-  LPSTR pszOEMUrl;
-  LPSTR pszHardwareID;
-  LPSTR pszProvider;
-  LPSTR pszPrintProcessor;
-  LPSTR pszVendorSetup;
-  LPSTR pszzColorProfiles;
-  LPSTR pszInfPath;
-  DWORD dwPrinterDriverAttributes;
-  LPSTR pszzCoreDriverDependencies;
-  FILETIME ftMinInboxDriverVerDate;
-  DWORDLONG dwlMinInboxDriverVerVersion;
-} DRIVER_INFO_8A, *PDRIVER_INFO_8A, *LPDRIVER_INFO_8A;
+#define DPD_DELETE_UNUSED_FILES 0x00000001
+#define DPD_DELETE_SPECIFIC_VERSION 0x00000002
+#define DPD_DELETE_ALL_FILES 0x00000004
+
+#define APD_STRICT_UPGRADE 0x00000001
+#define APD_STRICT_DOWNGRADE 0x00000002
+#define APD_COPY_ALL_FILES 0x00000004
+#define APD_COPY_NEW_FILES 0x00000008
+#define APD_COPY_FROM_DIRECTORY 0x00000010
+
+  typedef struct _DOC_INFO_1A {
+    LPSTR pDocName;
+    LPSTR pOutputFile;
+    LPSTR pDatatype;
+  } DOC_INFO_1A,*PDOC_INFO_1A,*LPDOC_INFO_1A;
+
+  typedef struct _DOC_INFO_1W {
+    LPWSTR pDocName;
+    LPWSTR pOutputFile;
+    LPWSTR pDatatype;
+  } DOC_INFO_1W,*PDOC_INFO_1W,*LPDOC_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(DOC_INFO_1)
+  __MINGW_TYPEDEF_AW(PDOC_INFO_1)
+  __MINGW_TYPEDEF_AW(LPDOC_INFO_1)
+
+  typedef struct _FORM_INFO_1A {
+    DWORD Flags;
+    LPSTR pName;
+    SIZEL Size;
+    RECTL ImageableArea;
+  } FORM_INFO_1A,*PFORM_INFO_1A,*LPFORM_INFO_1A;
+
+  typedef struct _FORM_INFO_1W {
+    DWORD Flags;
+    LPWSTR pName;
+    SIZEL Size;
+    RECTL ImageableArea;
+  } FORM_INFO_1W,*PFORM_INFO_1W,*LPFORM_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(FORM_INFO_1)
+  __MINGW_TYPEDEF_AW(PFORM_INFO_1)
+  __MINGW_TYPEDEF_AW(LPFORM_INFO_1)
+
+  typedef struct _DOC_INFO_2A {
+    LPSTR pDocName;
+    LPSTR pOutputFile;
+    LPSTR pDatatype;
+    DWORD dwMode;
+    DWORD JobId;
+  } DOC_INFO_2A,*PDOC_INFO_2A,*LPDOC_INFO_2A;
+
+  typedef struct _DOC_INFO_2W {
+    LPWSTR pDocName;
+    LPWSTR pOutputFile;
+    LPWSTR pDatatype;
+    DWORD dwMode;
+    DWORD JobId;
+  } DOC_INFO_2W,*PDOC_INFO_2W,*LPDOC_INFO_2W;
+
+  __MINGW_TYPEDEF_AW(DOC_INFO_2)
+  __MINGW_TYPEDEF_AW(PDOC_INFO_2)
+  __MINGW_TYPEDEF_AW(LPDOC_INFO_2)
+
+#define DI_CHANNEL 1
+#define DI_READ_SPOOL_JOB 3
+
+  typedef struct _DOC_INFO_3A {
+    LPSTR pDocName;
+    LPSTR pOutputFile;
+    LPSTR pDatatype;
+    DWORD dwFlags;
+  } DOC_INFO_3A,*PDOC_INFO_3A,*LPDOC_INFO_3A;
+
+  typedef struct _DOC_INFO_3W {
+    LPWSTR pDocName;
+    LPWSTR pOutputFile;
+    LPWSTR pDatatype;
+    DWORD dwFlags;
+  } DOC_INFO_3W,*PDOC_INFO_3W,*LPDOC_INFO_3W;
+
+  __MINGW_TYPEDEF_AW(DOC_INFO_3)
+  __MINGW_TYPEDEF_AW(PDOC_INFO_3)
+  __MINGW_TYPEDEF_AW(LPDOC_INFO_3)
+
+#define DI_MEMORYMAP_WRITE 0x00000001
+
+#define FORM_USER 0x00000000
+#define FORM_BUILTIN 0x00000001
+#define FORM_PRINTER 0x00000002
+
+  typedef struct _PRINTPROCESSOR_INFO_1A {
+    LPSTR pName;
+  } PRINTPROCESSOR_INFO_1A,*PPRINTPROCESSOR_INFO_1A,*LPPRINTPROCESSOR_INFO_1A;
+
+  typedef struct _PRINTPROCESSOR_INFO_1W {
+    LPWSTR pName;
+  } PRINTPROCESSOR_INFO_1W,*PPRINTPROCESSOR_INFO_1W,*LPPRINTPROCESSOR_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(PRINTPROCESSOR_INFO_1)
+  __MINGW_TYPEDEF_AW(PPRINTPROCESSOR_INFO_1)
+  __MINGW_TYPEDEF_AW(LPPRINTPROCESSOR_INFO_1)
+
+  typedef struct _PRINTPROCESSOR_CAPS_1 {
+    DWORD dwLevel;
+    DWORD dwNupOptions;
+    DWORD dwPageOrderFlags;
+    DWORD dwNumberOfCopies;
+  } PRINTPROCESSOR_CAPS_1,*PPRINTPROCESSOR_CAPS_1;
+
+#define NORMAL_PRINT 0x00000000
+#define REVERSE_PRINT 0x00000001
+
+  typedef struct _PORT_INFO_1A {
+    LPSTR pName;
+  } PORT_INFO_1A,*PPORT_INFO_1A,*LPPORT_INFO_1A;
+  typedef struct _PORT_INFO_1W {
+    LPWSTR pName;
+  } PORT_INFO_1W,*PPORT_INFO_1W,*LPPORT_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(PORT_INFO_1)
+  __MINGW_TYPEDEF_AW(PPORT_INFO_1)
+  __MINGW_TYPEDEF_AW(LPPORT_INFO_1)
+
+  typedef struct _PORT_INFO_2A {
+    LPSTR pPortName;
+    LPSTR pMonitorName;
+    LPSTR pDescription;
+    DWORD fPortType;
+    DWORD Reserved;
+  } PORT_INFO_2A,*PPORT_INFO_2A,*LPPORT_INFO_2A;
+
+  typedef struct _PORT_INFO_2W {
+    LPWSTR pPortName;
+    LPWSTR pMonitorName;
+    LPWSTR pDescription;
+    DWORD fPortType;
+    DWORD Reserved;
+  } PORT_INFO_2W,*PPORT_INFO_2W,*LPPORT_INFO_2W;
+
+  __MINGW_TYPEDEF_AW(PORT_INFO_2)
+  __MINGW_TYPEDEF_AW(PPORT_INFO_2)
+  __MINGW_TYPEDEF_AW(LPPORT_INFO_2)
+
+#define PORT_TYPE_WRITE 0x0001
+#define PORT_TYPE_READ 0x0002
+#define PORT_TYPE_REDIRECTED 0x0004
+#define PORT_TYPE_NET_ATTACHED 0x0008
+
+  typedef struct _PORT_INFO_3A {
+    DWORD dwStatus;
+    LPSTR pszStatus;
+    DWORD dwSeverity;
+  } PORT_INFO_3A,*PPORT_INFO_3A,*LPPORT_INFO_3A;
+
+  typedef struct _PORT_INFO_3W {
+    DWORD dwStatus;
+    LPWSTR pszStatus;
+    DWORD dwSeverity;
+  } PORT_INFO_3W,*PPORT_INFO_3W,*LPPORT_INFO_3W;
+
+  __MINGW_TYPEDEF_AW(PORT_INFO_3)
+  __MINGW_TYPEDEF_AW(PPORT_INFO_3)
+  __MINGW_TYPEDEF_AW(LPPORT_INFO_3)
+
+#define PORT_STATUS_TYPE_ERROR 1
+#define PORT_STATUS_TYPE_WARNING 2
+#define PORT_STATUS_TYPE_INFO 3
+
+#define PORT_STATUS_OFFLINE 1
+#define PORT_STATUS_PAPER_JAM 2
+#define PORT_STATUS_PAPER_OUT 3
+#define PORT_STATUS_OUTPUT_BIN_FULL 4
+#define PORT_STATUS_PAPER_PROBLEM 5
+#define PORT_STATUS_NO_TONER 6
+#define PORT_STATUS_DOOR_OPEN 7
+#define PORT_STATUS_USER_INTERVENTION 8
+#define PORT_STATUS_OUT_OF_MEMORY 9
+#define PORT_STATUS_TONER_LOW 10
+#define PORT_STATUS_WARMING_UP 11
+#define PORT_STATUS_POWER_SAVE 12
+
+  typedef struct _MONITOR_INFO_1A{
+    LPSTR pName;
+  } MONITOR_INFO_1A,*PMONITOR_INFO_1A,*LPMONITOR_INFO_1A;
+
+  typedef struct _MONITOR_INFO_1W{
+    LPWSTR pName;
+  } MONITOR_INFO_1W,*PMONITOR_INFO_1W,*LPMONITOR_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(MONITOR_INFO_1)
+  __MINGW_TYPEDEF_AW(PMONITOR_INFO_1)
+  __MINGW_TYPEDEF_AW(LPMONITOR_INFO_1)
+
+  typedef struct _MONITOR_INFO_2A {
+    LPSTR pName;
+    LPSTR pEnvironment;
+    LPSTR pDLLName;
+  } MONITOR_INFO_2A,*PMONITOR_INFO_2A,*LPMONITOR_INFO_2A;
+
+  typedef struct _MONITOR_INFO_2W {
+    LPWSTR pName;
+    LPWSTR pEnvironment;
+    LPWSTR pDLLName;
+  } MONITOR_INFO_2W,*PMONITOR_INFO_2W,*LPMONITOR_INFO_2W;
+
+  __MINGW_TYPEDEF_AW(MONITOR_INFO_2)
+  __MINGW_TYPEDEF_AW(PMONITOR_INFO_2)
+  __MINGW_TYPEDEF_AW(LPMONITOR_INFO_2)
+
+  typedef struct _DATATYPES_INFO_1A {
+    LPSTR pName;
+  } DATATYPES_INFO_1A,*PDATATYPES_INFO_1A,*LPDATATYPES_INFO_1A;
+
+  typedef struct _DATATYPES_INFO_1W {
+    LPWSTR pName;
+  } DATATYPES_INFO_1W,*PDATATYPES_INFO_1W,*LPDATATYPES_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(DATATYPES_INFO_1)
+  __MINGW_TYPEDEF_AW(PDATATYPES_INFO_1)
+  __MINGW_TYPEDEF_AW(LPDATATYPES_INFO_1)
+
+  typedef struct _PRINTER_DEFAULTSA {
+    LPSTR pDatatype;
+    LPDEVMODEA pDevMode;
+    ACCESS_MASK DesiredAccess;
+  } PRINTER_DEFAULTSA,*PPRINTER_DEFAULTSA,*LPPRINTER_DEFAULTSA;
+
+  typedef struct _PRINTER_DEFAULTSW {
+    LPWSTR pDatatype;
+    LPDEVMODEW pDevMode;
+    ACCESS_MASK DesiredAccess;
+  } PRINTER_DEFAULTSW,*PPRINTER_DEFAULTSW,*LPPRINTER_DEFAULTSW;
+
+  __MINGW_TYPEDEF_AW(PRINTER_DEFAULTS)
+  __MINGW_TYPEDEF_AW(PPRINTER_DEFAULTS)
+  __MINGW_TYPEDEF_AW(LPPRINTER_DEFAULTS)
+
+  typedef struct _PRINTER_ENUM_VALUESA {
+    LPSTR pValueName;
+    DWORD cbValueName;
+    DWORD dwType;
+    LPBYTE pData;
+    DWORD cbData;
+  } PRINTER_ENUM_VALUESA,*PPRINTER_ENUM_VALUESA,*LPPRINTER_ENUM_VALUESA;
+
+  typedef struct _PRINTER_ENUM_VALUESW {
+    LPWSTR pValueName;
+    DWORD cbValueName;
+    DWORD dwType;
+    LPBYTE pData;
+    DWORD cbData;
+  } PRINTER_ENUM_VALUESW,*PPRINTER_ENUM_VALUESW,*LPPRINTER_ENUM_VALUESW;
+
+  __MINGW_TYPEDEF_AW(PRINTER_ENUM_VALUES)
+  __MINGW_TYPEDEF_AW(PPRINTER_ENUM_VALUES)
+  __MINGW_TYPEDEF_AW(LPPRINTER_ENUM_VALUES)
+
+#define EnumPrinters __MINGW_NAME_AW(EnumPrinters)
+
+  WINBOOL WINAPI EnumPrintersA(DWORD Flags,LPSTR Name,DWORD Level,LPBYTE pPrinterEnum,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI EnumPrintersW(DWORD Flags,LPWSTR Name,DWORD Level,LPBYTE pPrinterEnum,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+
+#define PRINTER_ENUM_DEFAULT 0x00000001
+#define PRINTER_ENUM_LOCAL 0x00000002
+#define PRINTER_ENUM_CONNECTIONS 0x00000004
+#define PRINTER_ENUM_FAVORITE 0x00000004
+#define PRINTER_ENUM_NAME 0x00000008
+#define PRINTER_ENUM_REMOTE 0x00000010
+#define PRINTER_ENUM_SHARED 0x00000020
+#define PRINTER_ENUM_NETWORK 0x00000040
+
+#define PRINTER_ENUM_EXPAND 0x00004000
+#define PRINTER_ENUM_CONTAINER 0x00008000
+
+#define PRINTER_ENUM_ICONMASK 0x00ff0000
+#define PRINTER_ENUM_ICON1 0x00010000
+#define PRINTER_ENUM_ICON2 0x00020000
+#define PRINTER_ENUM_ICON3 0x00040000
+#define PRINTER_ENUM_ICON4 0x00080000
+#define PRINTER_ENUM_ICON5 0x00100000
+#define PRINTER_ENUM_ICON6 0x00200000
+#define PRINTER_ENUM_ICON7 0x00400000
+#define PRINTER_ENUM_ICON8 0x00800000
+#define PRINTER_ENUM_HIDE 0x01000000
+
+#define SPOOL_FILE_PERSISTENT 0x00000001
+#define SPOOL_FILE_TEMPORARY 0x00000002
+
+#define OpenPrinter __MINGW_NAME_AW(OpenPrinter)
+#define ResetPrinter __MINGW_NAME_AW(ResetPrinter)
+#define SetJob __MINGW_NAME_AW(SetJob)
+#define GetJob __MINGW_NAME_AW(GetJob)
+#define EnumJobs __MINGW_NAME_AW(EnumJobs)
+#define AddPrinter __MINGW_NAME_AW(AddPrinter)
+#define SetPrinter __MINGW_NAME_AW(SetPrinter)
+#define GetPrinter __MINGW_NAME_AW(GetPrinter)
+#define AddPrinterDriver __MINGW_NAME_AW(AddPrinterDriver)
+#define AddPrinterDriverEx __MINGW_NAME_AW(AddPrinterDriverEx)
+#define EnumPrinterDrivers __MINGW_NAME_AW(EnumPrinterDrivers)
+#define GetPrinterDriver __MINGW_NAME_AW(GetPrinterDriver)
+#define GetPrinterDriverDirectory __MINGW_NAME_AW(GetPrinterDriverDirectory)
+#define DeletePrinterDriver __MINGW_NAME_AW(DeletePrinterDriver)
+#define DeletePrinterDriverEx __MINGW_NAME_AW(DeletePrinterDriverEx)
+#define AddPrintProcessor __MINGW_NAME_AW(AddPrintProcessor)
+#define EnumPrintProcessors __MINGW_NAME_AW(EnumPrintProcessors)
+#define GetPrintProcessorDirectory __MINGW_NAME_AW(GetPrintProcessorDirectory)
+#define EnumPrintProcessorDatatypes __MINGW_NAME_AW(EnumPrintProcessorDatatypes)
+#define DeletePrintProcessor __MINGW_NAME_AW(DeletePrintProcessor)
+#define StartDocPrinter __MINGW_NAME_AW(StartDocPrinter)
+#define AddJob __MINGW_NAME_AW(AddJob)
+#define DocumentProperties __MINGW_NAME_AW(DocumentProperties)
+#define AdvancedDocumentProperties __MINGW_NAME_AW(AdvancedDocumentProperties)
+#define GetPrinterData __MINGW_NAME_AW(GetPrinterData)
+#define GetPrinterDataEx __MINGW_NAME_AW(GetPrinterDataEx)
+#define EnumPrinterData __MINGW_NAME_AW(EnumPrinterData)
+#define EnumPrinterDataEx __MINGW_NAME_AW(EnumPrinterDataEx)
+#define EnumPrinterKey __MINGW_NAME_AW(EnumPrinterKey)
+#define SetPrinterData __MINGW_NAME_AW(SetPrinterData)
+#define SetPrinterDataEx __MINGW_NAME_AW(SetPrinterDataEx)
+#define DeletePrinterData __MINGW_NAME_AW(DeletePrinterData)
+#define DeletePrinterDataEx __MINGW_NAME_AW(DeletePrinterDataEx)
+#define DeletePrinterKey __MINGW_NAME_AW(DeletePrinterKey)
+
+  WINBOOL WINAPI OpenPrinterA(LPSTR pPrinterName,LPHANDLE phPrinter,LPPRINTER_DEFAULTSA pDefault);
+  WINBOOL WINAPI OpenPrinterW(LPWSTR pPrinterName,LPHANDLE phPrinter,LPPRINTER_DEFAULTSW pDefault);
+  WINBOOL WINAPI ResetPrinterA(HANDLE hPrinter,LPPRINTER_DEFAULTSA pDefault);
+  WINBOOL WINAPI ResetPrinterW(HANDLE hPrinter,LPPRINTER_DEFAULTSW pDefault);
+  WINBOOL WINAPI SetJobA(HANDLE hPrinter,DWORD JobId,DWORD Level,LPBYTE pJob,DWORD Command);
+  WINBOOL WINAPI SetJobW(HANDLE hPrinter,DWORD JobId,DWORD Level,LPBYTE pJob,DWORD Command);
+  WINBOOL WINAPI GetJobA(HANDLE hPrinter,DWORD JobId,DWORD Level,LPBYTE pJob,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI GetJobW(HANDLE hPrinter,DWORD JobId,DWORD Level,LPBYTE pJob,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI EnumJobsA(HANDLE hPrinter,DWORD FirstJob,DWORD NoJobs,DWORD Level,LPBYTE pJob,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI EnumJobsW(HANDLE hPrinter,DWORD FirstJob,DWORD NoJobs,DWORD Level,LPBYTE pJob,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  HANDLE WINAPI AddPrinterA(LPSTR pName,DWORD Level,LPBYTE pPrinter);
+  HANDLE WINAPI AddPrinterW(LPWSTR pName,DWORD Level,LPBYTE pPrinter);
+  WINBOOL WINAPI DeletePrinter(HANDLE hPrinter);
+  WINBOOL WINAPI SetPrinterA(HANDLE hPrinter,DWORD Level,LPBYTE pPrinter,DWORD Command);
+  WINBOOL WINAPI SetPrinterW(HANDLE hPrinter,DWORD Level,LPBYTE pPrinter,DWORD Command);
+  WINBOOL WINAPI GetPrinterA(HANDLE hPrinter,DWORD Level,LPBYTE pPrinter,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI GetPrinterW(HANDLE hPrinter,DWORD Level,LPBYTE pPrinter,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI AddPrinterDriverA(LPSTR pName,DWORD Level,LPBYTE pDriverInfo);
+  WINBOOL WINAPI AddPrinterDriverW(LPWSTR pName,DWORD Level,LPBYTE pDriverInfo);
+  WINBOOL WINAPI AddPrinterDriverExA(LPSTR pName,DWORD Level,LPBYTE pDriverInfo,DWORD dwFileCopyFlags);
+  WINBOOL WINAPI AddPrinterDriverExW(LPWSTR pName,DWORD Level,LPBYTE pDriverInfo,DWORD dwFileCopyFlags);
+  WINBOOL WINAPI EnumPrinterDriversA(LPSTR pName,LPSTR pEnvironment,DWORD Level,LPBYTE pDriverInfo,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI EnumPrinterDriversW(LPWSTR pName,LPWSTR pEnvironment,DWORD Level,LPBYTE pDriverInfo,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI GetPrinterDriverA(HANDLE hPrinter,LPSTR pEnvironment,DWORD Level,LPBYTE pDriverInfo,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI GetPrinterDriverW(HANDLE hPrinter,LPWSTR pEnvironment,DWORD Level,LPBYTE pDriverInfo,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName,LPSTR pEnvironment,DWORD Level,LPBYTE pDriverDirectory,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName,LPWSTR pEnvironment,DWORD Level,LPBYTE pDriverDirectory,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI DeletePrinterDriverA(LPSTR pName,LPSTR pEnvironment,LPSTR pDriverName);
+  WINBOOL WINAPI DeletePrinterDriverW(LPWSTR pName,LPWSTR pEnvironment,LPWSTR pDriverName);
+  WINBOOL WINAPI DeletePrinterDriverExA(LPSTR pName,LPSTR pEnvironment,LPSTR pDriverName,DWORD dwDeleteFlag,DWORD dwVersionFlag);
+  WINBOOL WINAPI DeletePrinterDriverExW(LPWSTR pName,LPWSTR pEnvironment,LPWSTR pDriverName,DWORD dwDeleteFlag,DWORD dwVersionFlag);
+  WINBOOL WINAPI AddPrintProcessorA(LPSTR pName,LPSTR pEnvironment,LPSTR pPathName,LPSTR pPrintProcessorName);
+  WINBOOL WINAPI AddPrintProcessorW(LPWSTR pName,LPWSTR pEnvironment,LPWSTR pPathName,LPWSTR pPrintProcessorName);
+  WINBOOL WINAPI EnumPrintProcessorsA(LPSTR pName,LPSTR pEnvironment,DWORD Level,LPBYTE pPrintProcessorInfo,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI EnumPrintProcessorsW(LPWSTR pName,LPWSTR pEnvironment,DWORD Level,LPBYTE pPrintProcessorInfo,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI GetPrintProcessorDirectoryA(LPSTR pName,LPSTR pEnvironment,DWORD Level,LPBYTE pPrintProcessorInfo,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR pName,LPWSTR pEnvironment,DWORD Level,LPBYTE pPrintProcessorInfo,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName,LPSTR pPrintProcessorName,DWORD Level,LPBYTE pDatatypes,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName,LPWSTR pPrintProcessorName,DWORD Level,LPBYTE pDatatypes,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI DeletePrintProcessorA(LPSTR pName,LPSTR pEnvironment,LPSTR pPrintProcessorName);
+  WINBOOL WINAPI DeletePrintProcessorW(LPWSTR pName,LPWSTR pEnvironment,LPWSTR pPrintProcessorName);
+  DWORD WINAPI StartDocPrinterA(HANDLE hPrinter,DWORD Level,LPBYTE pDocInfo);
+  DWORD WINAPI StartDocPrinterW(HANDLE hPrinter,DWORD Level,LPBYTE pDocInfo);
+  WINBOOL WINAPI StartPagePrinter(HANDLE hPrinter);
+  WINBOOL WINAPI WritePrinter(HANDLE hPrinter,LPVOID pBuf,DWORD cbBuf,LPDWORD pcWritten);
+  WINBOOL WINAPI FlushPrinter(HANDLE hPrinter,LPVOID pBuf,DWORD cbBuf,LPDWORD pcWritten,DWORD cSleep);
+  WINBOOL WINAPI EndPagePrinter(HANDLE hPrinter);
+  WINBOOL WINAPI AbortPrinter(HANDLE hPrinter);
+  WINBOOL WINAPI ReadPrinter(HANDLE hPrinter,LPVOID pBuf,DWORD cbBuf,LPDWORD pNoBytesRead);
+  WINBOOL WINAPI EndDocPrinter(HANDLE hPrinter);
+  WINBOOL WINAPI AddJobA(HANDLE hPrinter,DWORD Level,LPBYTE pData,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI AddJobW(HANDLE hPrinter,DWORD Level,LPBYTE pData,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI ScheduleJob(HANDLE hPrinter,DWORD JobId);
+  WINBOOL WINAPI PrinterProperties(HWND hWnd,HANDLE hPrinter);
+  LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,LPSTR pDeviceName,PDEVMODEA pDevModeOutput,PDEVMODEA pDevModeInput,DWORD fMode);
+  LONG WINAPI DocumentPropertiesW(HWND hWnd,HANDLE hPrinter,LPWSTR pDeviceName,PDEVMODEW pDevModeOutput,PDEVMODEW pDevModeInput,DWORD fMode);
+  LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd,HANDLE hPrinter,LPSTR pDeviceName,PDEVMODEA pDevModeOutput,PDEVMODEA pDevModeInput);
+  LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd,HANDLE hPrinter,LPWSTR pDeviceName,PDEVMODEW pDevModeOutput,PDEVMODEW pDevModeInput);
+  LONG ExtDeviceMode(HWND hWnd,HANDLE hInst,LPDEVMODEA pDevModeOutput,LPSTR pDeviceName,LPSTR pPort,LPDEVMODEA pDevModeInput,LPSTR pProfile,DWORD fMode);
+  DWORD WINAPI GetPrinterDataA(HANDLE hPrinter,LPSTR pValueName,LPDWORD pType,LPBYTE pData,DWORD nSize,LPDWORD pcbNeeded);
+  DWORD WINAPI GetPrinterDataW(HANDLE hPrinter,LPWSTR pValueName,LPDWORD pType,LPBYTE pData,DWORD nSize,LPDWORD pcbNeeded);
+  DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter,LPCSTR pKeyName,LPCSTR pValueName,LPDWORD pType,LPBYTE pData,DWORD nSize,LPDWORD pcbNeeded);
+  DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter,LPCWSTR pKeyName,LPCWSTR pValueName,LPDWORD pType,LPBYTE pData,DWORD nSize,LPDWORD pcbNeeded);
+  DWORD WINAPI EnumPrinterDataA(HANDLE hPrinter,DWORD dwIndex,LPSTR pValueName,DWORD cbValueName,LPDWORD pcbValueName,LPDWORD pType,LPBYTE pData,DWORD cbData,LPDWORD pcbData);
+  DWORD WINAPI EnumPrinterDataW(HANDLE hPrinter,DWORD dwIndex,LPWSTR pValueName,DWORD cbValueName,LPDWORD pcbValueName,LPDWORD pType,LPBYTE pData,DWORD cbData,LPDWORD pcbData);
+  DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter,LPCSTR pKeyName,LPBYTE pEnumValues,DWORD cbEnumValues,LPDWORD pcbEnumValues,LPDWORD pnEnumValues);
+  DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter,LPCWSTR pKeyName,LPBYTE pEnumValues,DWORD cbEnumValues,LPDWORD pcbEnumValues,LPDWORD pnEnumValues);
+  DWORD WINAPI EnumPrinterKeyA(HANDLE hPrinter,LPCSTR pKeyName,LPSTR pSubkey,DWORD cbSubkey,LPDWORD pcbSubkey);
+  DWORD WINAPI EnumPrinterKeyW(HANDLE hPrinter,LPCWSTR pKeyName,LPWSTR pSubkey,DWORD cbSubkey,LPDWORD pcbSubkey);
+  DWORD WINAPI SetPrinterDataA(HANDLE hPrinter,LPSTR pValueName,DWORD Type,LPBYTE pData,DWORD cbData);
+  DWORD WINAPI SetPrinterDataW(HANDLE hPrinter,LPWSTR pValueName,DWORD Type,LPBYTE pData,DWORD cbData);
+  DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter,LPCSTR pKeyName,LPCSTR pValueName,DWORD Type,LPBYTE pData,DWORD cbData);
+  DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter,LPCWSTR pKeyName,LPCWSTR pValueName,DWORD Type,LPBYTE pData,DWORD cbData);
+  DWORD WINAPI DeletePrinterDataA(HANDLE hPrinter,LPSTR pValueName);
+  DWORD WINAPI DeletePrinterDataW(HANDLE hPrinter,LPWSTR pValueName);
+  DWORD WINAPI DeletePrinterDataExA(HANDLE hPrinter,LPCSTR pKeyName,LPCSTR pValueName);
+  DWORD WINAPI DeletePrinterDataExW(HANDLE hPrinter,LPCWSTR pKeyName,LPCWSTR pValueName);
+  DWORD WINAPI DeletePrinterKeyA(HANDLE hPrinter,LPCSTR pKeyName);
+  DWORD WINAPI DeletePrinterKeyW(HANDLE hPrinter,LPCWSTR pKeyName);
+
+#define PRINTER_NOTIFY_TYPE 0x00
+#define JOB_NOTIFY_TYPE 0x01
+
+#define PRINTER_NOTIFY_FIELD_SERVER_NAME 0x00
+#define PRINTER_NOTIFY_FIELD_PRINTER_NAME 0x01
+#define PRINTER_NOTIFY_FIELD_SHARE_NAME 0x02
+#define PRINTER_NOTIFY_FIELD_PORT_NAME 0x03
+#define PRINTER_NOTIFY_FIELD_DRIVER_NAME 0x04
+#define PRINTER_NOTIFY_FIELD_COMMENT 0x05
+#define PRINTER_NOTIFY_FIELD_LOCATION 0x06
+#define PRINTER_NOTIFY_FIELD_DEVMODE 0x07
+#define PRINTER_NOTIFY_FIELD_SEPFILE 0x08
+#define PRINTER_NOTIFY_FIELD_PRINT_PROCESSOR 0x09
+#define PRINTER_NOTIFY_FIELD_PARAMETERS 0x0A
+#define PRINTER_NOTIFY_FIELD_DATATYPE 0x0B
+#define PRINTER_NOTIFY_FIELD_SECURITY_DESCRIPTOR 0x0C
+#define PRINTER_NOTIFY_FIELD_ATTRIBUTES 0x0D
+#define PRINTER_NOTIFY_FIELD_PRIORITY 0x0E
+#define PRINTER_NOTIFY_FIELD_DEFAULT_PRIORITY 0x0F
+#define PRINTER_NOTIFY_FIELD_START_TIME 0x10
+#define PRINTER_NOTIFY_FIELD_UNTIL_TIME 0x11
+#define PRINTER_NOTIFY_FIELD_STATUS 0x12
+#define PRINTER_NOTIFY_FIELD_STATUS_STRING 0x13
+#define PRINTER_NOTIFY_FIELD_CJOBS 0x14
+#define PRINTER_NOTIFY_FIELD_AVERAGE_PPM 0x15
+#define PRINTER_NOTIFY_FIELD_TOTAL_PAGES 0x16
+#define PRINTER_NOTIFY_FIELD_PAGES_PRINTED 0x17
+#define PRINTER_NOTIFY_FIELD_TOTAL_BYTES 0x18
+#define PRINTER_NOTIFY_FIELD_BYTES_PRINTED 0x19
+#define PRINTER_NOTIFY_FIELD_OBJECT_GUID 0x1A
+
+#define JOB_NOTIFY_FIELD_PRINTER_NAME 0x00
+#define JOB_NOTIFY_FIELD_MACHINE_NAME 0x01
+#define JOB_NOTIFY_FIELD_PORT_NAME 0x02
+#define JOB_NOTIFY_FIELD_USER_NAME 0x03
+#define JOB_NOTIFY_FIELD_NOTIFY_NAME 0x04
+#define JOB_NOTIFY_FIELD_DATATYPE 0x05
+#define JOB_NOTIFY_FIELD_PRINT_PROCESSOR 0x06
+#define JOB_NOTIFY_FIELD_PARAMETERS 0x07
+#define JOB_NOTIFY_FIELD_DRIVER_NAME 0x08
+#define JOB_NOTIFY_FIELD_DEVMODE 0x09
+#define JOB_NOTIFY_FIELD_STATUS 0x0A
+#define JOB_NOTIFY_FIELD_STATUS_STRING 0x0B
+#define JOB_NOTIFY_FIELD_SECURITY_DESCRIPTOR 0x0C
+#define JOB_NOTIFY_FIELD_DOCUMENT 0x0D
+#define JOB_NOTIFY_FIELD_PRIORITY 0x0E
+#define JOB_NOTIFY_FIELD_POSITION 0x0F
+#define JOB_NOTIFY_FIELD_SUBMITTED 0x10
+#define JOB_NOTIFY_FIELD_START_TIME 0x11
+#define JOB_NOTIFY_FIELD_UNTIL_TIME 0x12
+#define JOB_NOTIFY_FIELD_TIME 0x13
+#define JOB_NOTIFY_FIELD_TOTAL_PAGES 0x14
+#define JOB_NOTIFY_FIELD_PAGES_PRINTED 0x15
+#define JOB_NOTIFY_FIELD_TOTAL_BYTES 0x16
+#define JOB_NOTIFY_FIELD_BYTES_PRINTED 0x17
+
+  typedef struct _PRINTER_NOTIFY_OPTIONS_TYPE {
+    WORD Type;
+    WORD Reserved0;
+    DWORD Reserved1;
+    DWORD Reserved2;
+    DWORD Count;
+    PWORD pFields;
+  } PRINTER_NOTIFY_OPTIONS_TYPE,*PPRINTER_NOTIFY_OPTIONS_TYPE,*LPPRINTER_NOTIFY_OPTIONS_TYPE;
+
+#define PRINTER_NOTIFY_OPTIONS_REFRESH 0x01
+
+  typedef struct _PRINTER_NOTIFY_OPTIONS {
+    DWORD Version;
+    DWORD Flags;
+    DWORD Count;
+    PPRINTER_NOTIFY_OPTIONS_TYPE pTypes;
+  } PRINTER_NOTIFY_OPTIONS,*PPRINTER_NOTIFY_OPTIONS,*LPPRINTER_NOTIFY_OPTIONS;
+
+#define PRINTER_NOTIFY_INFO_DISCARDED 0x01
+
+  typedef struct _PRINTER_NOTIFY_INFO_DATA {
+    WORD Type;
+    WORD Field;
+    DWORD Reserved;
+    DWORD Id;
+    union {
+      DWORD adwData[2];
+      struct {
+       DWORD cbBuf;
+       LPVOID pBuf;
+      } Data;
+    } NotifyData;
+  } PRINTER_NOTIFY_INFO_DATA,*PPRINTER_NOTIFY_INFO_DATA,*LPPRINTER_NOTIFY_INFO_DATA;
+
+  typedef struct _PRINTER_NOTIFY_INFO {
+    DWORD Version;
+    DWORD Flags;
+    DWORD Count;
+    PRINTER_NOTIFY_INFO_DATA aData[1];
+  } PRINTER_NOTIFY_INFO,*PPRINTER_NOTIFY_INFO,*LPPRINTER_NOTIFY_INFO;
+
+  typedef struct _BINARY_CONTAINER{
+    DWORD cbBuf;
+    LPBYTE pData;
+  } BINARY_CONTAINER,*PBINARY_CONTAINER;
+
+  typedef struct _BIDI_DATA{
+    DWORD dwBidiType;
+    union {
+      WINBOOL bData;
+      LONG iData;
+      LPWSTR sData;
+      FLOAT fData;
+      BINARY_CONTAINER biData;
+    } u;
+  } BIDI_DATA,*PBIDI_DATA,*LPBIDI_DATA;
+
+  typedef struct _BIDI_REQUEST_DATA{
+    DWORD dwReqNumber;
+    LPWSTR pSchema;
+    BIDI_DATA data;
+  } BIDI_REQUEST_DATA ,*PBIDI_REQUEST_DATA ,*LPBIDI_REQUEST_DATA;
+
+  typedef struct _BIDI_REQUEST_CONTAINER{
+    DWORD Version;
+    DWORD Flags;
+    DWORD Count;
+    BIDI_REQUEST_DATA aData[1 ];
+  }BIDI_REQUEST_CONTAINER,*PBIDI_REQUEST_CONTAINER,*LPBIDI_REQUEST_CONTAINER;
+
+  typedef struct _BIDI_RESPONSE_DATA{
+    DWORD dwResult;
+    DWORD dwReqNumber;
+    LPWSTR pSchema;
+    BIDI_DATA data;
+  } BIDI_RESPONSE_DATA,*PBIDI_RESPONSE_DATA,*LPBIDI_RESPONSE_DATA;
+
+  typedef struct _BIDI_RESPONSE_CONTAINER{
+    DWORD Version;
+    DWORD Flags;
+    DWORD Count;
+    BIDI_RESPONSE_DATA aData[1 ];
+  } BIDI_RESPONSE_CONTAINER,*PBIDI_RESPONSE_CONTAINER,*LPBIDI_RESPONSE_CONTAINER;
+
+#define BIDI_ACTION_ENUM_SCHEMA L"EnumSchema"
+#define BIDI_ACTION_GET L"Get"
+#define BIDI_ACTION_SET L"Set"
+#define BIDI_ACTION_GET_ALL L"GetAll"
+
+  typedef enum {
+    BIDI_NULL = 0,BIDI_INT = 1,BIDI_FLOAT = 2,BIDI_BOOL = 3,BIDI_STRING = 4,BIDI_TEXT = 5,BIDI_ENUM = 6,BIDI_BLOB = 7
+  } BIDI_TYPE;
+
+#define BIDI_ACCESS_ADMINISTRATOR 0x1
+#define BIDI_ACCESS_USER 0x2
+
+#define ERROR_BIDI_STATUS_OK 0
+#define ERROR_BIDI_NOT_SUPPORTED ERROR_NOT_SUPPORTED
+
+#define ERROR_BIDI_ERROR_BASE 13000
+#define ERROR_BIDI_STATUS_WARNING (ERROR_BIDI_ERROR_BASE + 1)
+#define ERROR_BIDI_SCHEMA_READ_ONLY (ERROR_BIDI_ERROR_BASE + 2)
+#define ERROR_BIDI_SERVER_OFFLINE (ERROR_BIDI_ERROR_BASE + 3)
+#define ERROR_BIDI_DEVICE_OFFLINE (ERROR_BIDI_ERROR_BASE + 4)
+#define ERROR_BIDI_SCHEMA_NOT_SUPPORTED (ERROR_BIDI_ERROR_BASE + 5)
+
+  DWORD WINAPI WaitForPrinterChange(HANDLE hPrinter,DWORD Flags);
+  HANDLE WINAPI FindFirstPrinterChangeNotification(HANDLE hPrinter,DWORD fdwFlags,DWORD fdwOptions,LPVOID pPrinterNotifyOptions);
+  WINBOOL WINAPI FindNextPrinterChangeNotification(HANDLE hChange,PDWORD pdwChange,LPVOID pPrinterNotifyOptions,LPVOID *ppPrinterNotifyInfo);
+  WINBOOL WINAPI FreePrinterNotifyInfo (PPRINTER_NOTIFY_INFO pPrinterNotifyInfo);
+  WINBOOL WINAPI FindClosePrinterChangeNotification(HANDLE hChange);
+
+#define PRINTER_CHANGE_ADD_PRINTER 0x00000001
+#define PRINTER_CHANGE_SET_PRINTER 0x00000002
+#define PRINTER_CHANGE_DELETE_PRINTER 0x00000004
+#define PRINTER_CHANGE_FAILED_CONNECTION_PRINTER 0x00000008
+#define PRINTER_CHANGE_PRINTER 0x000000FF
+#define PRINTER_CHANGE_ADD_JOB 0x00000100
+#define PRINTER_CHANGE_SET_JOB 0x00000200
+#define PRINTER_CHANGE_DELETE_JOB 0x00000400
+#define PRINTER_CHANGE_WRITE_JOB 0x00000800
+#define PRINTER_CHANGE_JOB 0x0000FF00
+#define PRINTER_CHANGE_ADD_FORM 0x00010000
+#define PRINTER_CHANGE_SET_FORM 0x00020000
+#define PRINTER_CHANGE_DELETE_FORM 0x00040000
+#define PRINTER_CHANGE_FORM 0x00070000
+#define PRINTER_CHANGE_ADD_PORT 0x00100000
+#define PRINTER_CHANGE_CONFIGURE_PORT 0x00200000
+#define PRINTER_CHANGE_DELETE_PORT 0x00400000
+#define PRINTER_CHANGE_PORT 0x00700000
+#define PRINTER_CHANGE_ADD_PRINT_PROCESSOR 0x01000000
+#define PRINTER_CHANGE_DELETE_PRINT_PROCESSOR 0x04000000
+#define PRINTER_CHANGE_PRINT_PROCESSOR 0x07000000
+#define PRINTER_CHANGE_ADD_PRINTER_DRIVER 0x10000000
+#define PRINTER_CHANGE_SET_PRINTER_DRIVER 0x20000000
+#define PRINTER_CHANGE_DELETE_PRINTER_DRIVER 0x40000000
+#define PRINTER_CHANGE_PRINTER_DRIVER 0x70000000
+#define PRINTER_CHANGE_TIMEOUT 0x80000000
+#define PRINTER_CHANGE_ALL 0x7777FFFF
+
+#define PrinterMessageBox __MINGW_NAME_AW(PrinterMessageBox)
+#define AddForm __MINGW_NAME_AW(AddForm)
+#define DeleteForm __MINGW_NAME_AW(DeleteForm)
+#define GetForm __MINGW_NAME_AW(GetForm)
+#define SetForm __MINGW_NAME_AW(SetForm)
+#define EnumForms __MINGW_NAME_AW(EnumForms)
+#define EnumMonitors __MINGW_NAME_AW(EnumMonitors)
+#define AddMonitor __MINGW_NAME_AW(AddMonitor)
+#define DeleteMonitor __MINGW_NAME_AW(DeleteMonitor)
+#define EnumPorts __MINGW_NAME_AW(EnumPorts)
+#define AddPort __MINGW_NAME_AW(AddPort)
+#define ConfigurePort __MINGW_NAME_AW(ConfigurePort)
+#define DeletePort __MINGW_NAME_AW(DeletePort)
+#define GetDefaultPrinter __MINGW_NAME_AW(GetDefaultPrinter)
+#define SetDefaultPrinter __MINGW_NAME_AW(SetDefaultPrinter)
+#define SetPort __MINGW_NAME_AW(SetPort)
+#define AddPrinterConnection __MINGW_NAME_AW(AddPrinterConnection)
+#define DeletePrinterConnection __MINGW_NAME_AW(DeletePrinterConnection)
+
+  DWORD WINAPI PrinterMessageBoxA(HANDLE hPrinter,DWORD Error,HWND hWnd,LPSTR pText,LPSTR pCaption,DWORD dwType);
+  DWORD WINAPI PrinterMessageBoxW(HANDLE hPrinter,DWORD Error,HWND hWnd,LPWSTR pText,LPWSTR pCaption,DWORD dwType);
+
+#define PRINTER_ERROR_INFORMATION 0x80000000
+#define PRINTER_ERROR_WARNING 0x40000000
+#define PRINTER_ERROR_SEVERE 0x20000000
+
+#define PRINTER_ERROR_OUTOFPAPER 0x00000001
+#define PRINTER_ERROR_JAM 0x00000002
+#define PRINTER_ERROR_OUTOFTONER 0x00000004
+
+  WINBOOL WINAPI ClosePrinter(HANDLE hPrinter);
+  WINBOOL WINAPI AddFormA(HANDLE hPrinter,DWORD Level,LPBYTE pForm);
+  WINBOOL WINAPI AddFormW(HANDLE hPrinter,DWORD Level,LPBYTE pForm);
+  WINBOOL WINAPI DeleteFormA(HANDLE hPrinter,LPSTR pFormName);
+  WINBOOL WINAPI DeleteFormW(HANDLE hPrinter,LPWSTR pFormName);
+  WINBOOL WINAPI GetFormA(HANDLE hPrinter,LPSTR pFormName,DWORD Level,LPBYTE pForm,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI GetFormW(HANDLE hPrinter,LPWSTR pFormName,DWORD Level,LPBYTE pForm,DWORD cbBuf,LPDWORD pcbNeeded);
+  WINBOOL WINAPI SetFormA(HANDLE hPrinter,LPSTR pFormName,DWORD Level,LPBYTE pForm);
+  WINBOOL WINAPI SetFormW(HANDLE hPrinter,LPWSTR pFormName,DWORD Level,LPBYTE pForm);
+  WINBOOL WINAPI EnumFormsA(HANDLE hPrinter,DWORD Level,LPBYTE pForm,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI EnumFormsW(HANDLE hPrinter,DWORD Level,LPBYTE pForm,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI EnumMonitorsA(LPSTR pName,DWORD Level,LPBYTE pMonitor,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI EnumMonitorsW(LPWSTR pName,DWORD Level,LPBYTE pMonitor,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI AddMonitorA(LPSTR pName,DWORD Level,LPBYTE pMonitorInfo);
+  WINBOOL WINAPI AddMonitorW(LPWSTR pName,DWORD Level,LPBYTE pMonitorInfo);
+  WINBOOL WINAPI DeleteMonitorA(LPSTR pName,LPSTR pEnvironment,LPSTR pMonitorName);
+  WINBOOL WINAPI DeleteMonitorW(LPWSTR pName,LPWSTR pEnvironment,LPWSTR pMonitorName);
+  WINBOOL WINAPI EnumPortsA(LPSTR pName,DWORD Level,LPBYTE pPorts,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI EnumPortsW(LPWSTR pName,DWORD Level,LPBYTE pPorts,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned);
+  WINBOOL WINAPI AddPortA(LPSTR pName,HWND hWnd,LPSTR pMonitorName);
+  WINBOOL WINAPI AddPortW(LPWSTR pName,HWND hWnd,LPWSTR pMonitorName);
+  WINBOOL WINAPI ConfigurePortA(LPSTR pName,HWND hWnd,LPSTR pPortName);
+  WINBOOL WINAPI ConfigurePortW(LPWSTR pName,HWND hWnd,LPWSTR pPortName);
+  WINBOOL WINAPI DeletePortA(LPSTR pName,HWND hWnd,LPSTR pPortName);
+  WINBOOL WINAPI DeletePortW(LPWSTR pName,HWND hWnd,LPWSTR pPortName);
+  WINBOOL WINAPI XcvDataW(HANDLE hXcv,PCWSTR pszDataName,PBYTE pInputData,DWORD cbInputData,PBYTE pOutputData,DWORD cbOutputData,PDWORD pcbOutputNeeded,PDWORD pdwStatus);
+
+#define XcvData XcvDataW
+
+  WINBOOL WINAPI GetDefaultPrinterA(LPSTR pszBuffer,LPDWORD pcchBuffer);
+  WINBOOL WINAPI GetDefaultPrinterW(LPWSTR pszBuffer,LPDWORD pcchBuffer);
+  WINBOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter);
+  WINBOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter);
+  WINBOOL WINAPI SetPortA(LPSTR pName,LPSTR pPortName,DWORD dwLevel,LPBYTE pPortInfo);
+  WINBOOL WINAPI SetPortW(LPWSTR pName,LPWSTR pPortName,DWORD dwLevel,LPBYTE pPortInfo);
+  WINBOOL WINAPI AddPrinterConnectionA(LPSTR pName);
+  WINBOOL WINAPI AddPrinterConnectionW(LPWSTR pName);
+  WINBOOL WINAPI DeletePrinterConnectionA(LPSTR pName);
+  WINBOOL WINAPI DeletePrinterConnectionW(LPWSTR pName);
+  HANDLE WINAPI ConnectToPrinterDlg(HWND hwnd,DWORD Flags);
+
+  typedef struct _PROVIDOR_INFO_1A{
+    LPSTR pName;
+    LPSTR pEnvironment;
+    LPSTR pDLLName;
+  } PROVIDOR_INFO_1A,*PPROVIDOR_INFO_1A,*LPPROVIDOR_INFO_1A;
+
+  typedef struct _PROVIDOR_INFO_1W{
+    LPWSTR pName;
+    LPWSTR pEnvironment;
+    LPWSTR pDLLName;
+  } PROVIDOR_INFO_1W,*PPROVIDOR_INFO_1W,*LPPROVIDOR_INFO_1W;
+
+  __MINGW_TYPEDEF_AW(PROVIDOR_INFO_1)
+  __MINGW_TYPEDEF_AW(PPROVIDOR_INFO_1)
+  __MINGW_TYPEDEF_AW(LPPROVIDOR_INFO_1)
+
+  typedef struct _PROVIDOR_INFO_2A{
+    LPSTR pOrder;
+  } PROVIDOR_INFO_2A,*PPROVIDOR_INFO_2A,*LPPROVIDOR_INFO_2A;
+
+  typedef struct _PROVIDOR_INFO_2W{
+    LPWSTR pOrder;
+  } PROVIDOR_INFO_2W,*PPROVIDOR_INFO_2W,*LPPROVIDOR_INFO_2W;
+
+  __MINGW_TYPEDEF_AW(PROVIDOR_INFO_2)
+  __MINGW_TYPEDEF_AW(PPROVIDOR_INFO_2)
+  __MINGW_TYPEDEF_AW(LPPROVIDOR_INFO_2)
+
+#define AddPrintProvidor __MINGW_NAME_AW(AddPrintProvidor)
+#define DeletePrintProvidor __MINGW_NAME_AW(DeletePrintProvidor)
+#define IsValidDevmode __MINGW_NAME_AW(IsValidDevmode)
+
+  WINBOOL WINAPI AddPrintProvidorA(LPSTR pName,DWORD level,LPBYTE pProvidorInfo);
+  WINBOOL WINAPI AddPrintProvidorW(LPWSTR pName,DWORD level,LPBYTE pProvidorInfo);
+  WINBOOL WINAPI DeletePrintProvidorA(LPSTR pName,LPSTR pEnvironment,LPSTR pPrintProvidorName);
+  WINBOOL WINAPI DeletePrintProvidorW(LPWSTR pName,LPWSTR pEnvironment,LPWSTR pPrintProvidorName);
+  WINBOOL WINAPI IsValidDevmodeA (PDEVMODEA pDevmode,size_t DevmodeSize);
+  WINBOOL WINAPI IsValidDevmodeW (PDEVMODEW pDevmode,size_t DevmodeSize);
+
+#define SPLREG_DEFAULT_SPOOL_DIRECTORY TEXT("DefaultSpoolDirectory")
+#define SPLREG_PORT_THREAD_PRIORITY_DEFAULT TEXT("PortThreadPriorityDefault")
+#define SPLREG_PORT_THREAD_PRIORITY TEXT("PortThreadPriority")
+#define SPLREG_SCHEDULER_THREAD_PRIORITY_DEFAULT TEXT("SchedulerThreadPriorityDefault")
+#define SPLREG_SCHEDULER_THREAD_PRIORITY TEXT("SchedulerThreadPriority")
+#define SPLREG_BEEP_ENABLED TEXT("BeepEnabled")
+#define SPLREG_NET_POPUP TEXT("NetPopup")
+#define SPLREG_RETRY_POPUP TEXT("RetryPopup")
+#define SPLREG_NET_POPUP_TO_COMPUTER TEXT("NetPopupToComputer")
+#define SPLREG_EVENT_LOG TEXT("EventLog")
+#define SPLREG_MAJOR_VERSION TEXT("MajorVersion")
+#define SPLREG_MINOR_VERSION TEXT("MinorVersion")
+#define SPLREG_ARCHITECTURE TEXT("Architecture")
+#define SPLREG_OS_VERSION TEXT("OSVersion")
+#define SPLREG_OS_VERSIONEX TEXT("OSVersionEx")
+#define SPLREG_DS_PRESENT TEXT("DsPresent")
+#define SPLREG_DS_PRESENT_FOR_USER TEXT("DsPresentForUser")
+#define SPLREG_REMOTE_FAX TEXT("RemoteFax")
+#define SPLREG_RESTART_JOB_ON_POOL_ERROR TEXT("RestartJobOnPoolError")
+#define SPLREG_RESTART_JOB_ON_POOL_ENABLED TEXT("RestartJobOnPoolEnabled")
+#define SPLREG_DNS_MACHINE_NAME TEXT("DNSMachineName")
+#define SPLREG_ALLOW_USER_MANAGEFORMS TEXT("AllowUserManageForms")
+#define SPLREG_WEBSHAREMGMT TEXT("WebShareMgmt")
+
+#define SERVER_ACCESS_ADMINISTER 0x00000001
+#define SERVER_ACCESS_ENUMERATE 0x00000002
+
+#define PRINTER_ACCESS_ADMINISTER 0x00000004
+#define PRINTER_ACCESS_USE 0x00000008
+
+#define JOB_ACCESS_ADMINISTER 0x00000010
+#define JOB_ACCESS_READ 0x00000020
+
+#define SERVER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SERVER_ACCESS_ADMINISTER | SERVER_ACCESS_ENUMERATE)
+#define SERVER_READ (STANDARD_RIGHTS_READ | SERVER_ACCESS_ENUMERATE)
+#define SERVER_WRITE (STANDARD_RIGHTS_WRITE | SERVER_ACCESS_ADMINISTER | SERVER_ACCESS_ENUMERATE)
+#define SERVER_EXECUTE (STANDARD_RIGHTS_EXECUTE | SERVER_ACCESS_ENUMERATE)
+#define PRINTER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE)
+#define PRINTER_READ (STANDARD_RIGHTS_READ | PRINTER_ACCESS_USE)
+#define PRINTER_WRITE (STANDARD_RIGHTS_WRITE | PRINTER_ACCESS_USE)
+#define PRINTER_EXECUTE (STANDARD_RIGHTS_EXECUTE | PRINTER_ACCESS_USE)
+#define JOB_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | JOB_ACCESS_ADMINISTER | JOB_ACCESS_READ)
+#define JOB_READ (STANDARD_RIGHTS_READ | JOB_ACCESS_READ)
+#define JOB_WRITE (STANDARD_RIGHTS_WRITE | JOB_ACCESS_ADMINISTER)
+#define JOB_EXECUTE (STANDARD_RIGHTS_EXECUTE | JOB_ACCESS_ADMINISTER)
+
+#define SPLDS_SPOOLER_KEY TEXT("DsSpooler")
+#define SPLDS_DRIVER_KEY TEXT("DsDriver")
+#define SPLDS_USER_KEY TEXT("DsUser")
+
+#define SPLDS_ASSET_NUMBER TEXT("assetNumber")
+#define SPLDS_BYTES_PER_MINUTE TEXT("bytesPerMinute")
+#define SPLDS_DESCRIPTION TEXT("description")
+#define SPLDS_DRIVER_NAME TEXT("driverName")
+#define SPLDS_DRIVER_VERSION TEXT("driverVersion")
+#define SPLDS_LOCATION TEXT("location")
+#define SPLDS_PORT_NAME TEXT("portName")
+#define SPLDS_PRINT_ATTRIBUTES TEXT("printAttributes")
+#define SPLDS_PRINT_BIN_NAMES TEXT("printBinNames")
+#define SPLDS_PRINT_COLLATE TEXT("printCollate")
+#define SPLDS_PRINT_COLOR TEXT("printColor")
+#define SPLDS_PRINT_DUPLEX_SUPPORTED TEXT("printDuplexSupported")
+#define SPLDS_PRINT_END_TIME TEXT("printEndTime")
+#define SPLDS_PRINTER_CLASS TEXT("printQueue")
+#define SPLDS_PRINTER_NAME TEXT("printerName")
+#define SPLDS_PRINT_KEEP_PRINTED_JOBS TEXT("printKeepPrintedJobs")
+#define SPLDS_PRINT_LANGUAGE TEXT("printLanguage")
+#define SPLDS_PRINT_MAC_ADDRESS TEXT("printMACAddress")
+#define SPLDS_PRINT_MAX_X_EXTENT TEXT("printMaxXExtent")
+#define SPLDS_PRINT_MAX_Y_EXTENT TEXT("printMaxYExtent")
+#define SPLDS_PRINT_MAX_RESOLUTION_SUPPORTED TEXT("printMaxResolutionSupported")
+#define SPLDS_PRINT_MEDIA_READY TEXT("printMediaReady")
+#define SPLDS_PRINT_MEDIA_SUPPORTED TEXT("printMediaSupported")
+#define SPLDS_PRINT_MEMORY TEXT("printMemory")
+#define SPLDS_PRINT_MIN_X_EXTENT TEXT("printMinXExtent")
+#define SPLDS_PRINT_MIN_Y_EXTENT TEXT("printMinYExtent")
+#define SPLDS_PRINT_NETWORK_ADDRESS TEXT("printNetworkAddress")
+#define SPLDS_PRINT_NOTIFY TEXT("printNotify")
+#define SPLDS_PRINT_NUMBER_UP TEXT("printNumberUp")
+#define SPLDS_PRINT_ORIENTATIONS_SUPPORTED TEXT("printOrientationsSupported")
+#define SPLDS_PRINT_OWNER TEXT("printOwner")
+#define SPLDS_PRINT_PAGES_PER_MINUTE TEXT("printPagesPerMinute")
+#define SPLDS_PRINT_RATE TEXT("printRate")
+#define SPLDS_PRINT_RATE_UNIT TEXT("printRateUnit")
+#define SPLDS_PRINT_SEPARATOR_FILE TEXT("printSeparatorFile")
+#define SPLDS_PRINT_SHARE_NAME TEXT("printShareName")
+#define SPLDS_PRINT_SPOOLING TEXT("printSpooling")
+#define SPLDS_PRINT_STAPLING_SUPPORTED TEXT("printStaplingSupported")
+#define SPLDS_PRINT_START_TIME TEXT("printStartTime")
+#define SPLDS_PRINT_STATUS TEXT("printStatus")
+#define SPLDS_PRIORITY TEXT("priority")
+#define SPLDS_SERVER_NAME TEXT("serverName")
+#define SPLDS_SHORT_SERVER_NAME TEXT("shortServerName")
+#define SPLDS_UNC_NAME TEXT("uNCName")
+#define SPLDS_URL TEXT("url")
+#define SPLDS_FLAGS TEXT("flags")
+#define SPLDS_VERSION_NUMBER TEXT("versionNumber")
+
+#define SPLDS_PRINTER_NAME_ALIASES TEXT("printerNameAliases")
+#define SPLDS_PRINTER_LOCATIONS TEXT("printerLocations")
+#define SPLDS_PRINTER_MODEL TEXT("printerModel")
+
+#if (_WIN32_WINNT >= 0x0600)
+#define AddPrinterConnection2 __MINGW_NAME_AW(AddPrinterConnection2)
+#define DeletePrinterDriverPackage __MINGW_NAME_AW(DeletePrinterDriverPackage)
+#define DocumentEvent __MINGW_NAME_AW(DocumentEvent)
+
+#define PRINTER_CONNECTION_MISMATCH 0x00000020
+#define PRINTER_CONNECTION_NO_UI    0x00000040
+
+typedef enum tagPRINTER_OPTION_FLAGS {
+  PRINTER_OPTION_NO_CACHE,
+  PRINTER_OPTION_CACHE,
+  PRINTER_OPTION_CLIENT_CHANGE 
+} PRINTER_OPTION_FLAGS;
+
+typedef enum tagEPrintXPSJobOperation {
+  kJobProduction,
+  kJobConsumption 
+} EPrintXPSJobOperation;
+
+typedef enum tagEPrintXPSJobProgress {
+  kAddingDocumentSequence,
+  kDocumentSequenceAdded,
+  kAddingFixedDocument,
+  kFixedDocumentAdded,
+  kAddingFixedPage,
+  kFixedPageAdded,
+  kResourceAdded,
+  kFontAdded,
+  kImageAdded,
+  kXpsDocumentCommitted 
+} EPrintXPSJobProgress;
+
+typedef struct _PRINTER_CONNECTION_INFO_1 {
+  DWORD  dwFlags;
+  LPTSTR pszDriverName;
+} PRINTER_CONNECTION_INFO_1, *PPRINTER_CONNECTION_INFO_1;
+
+  WINBOOL AddPrinterConnection2W(HWND hWnd,LPCWSTR pszName,DWORD dwLevel,PVOID pConnectionInfo);
+  WINBOOL AddPrinterConnection2A(HWND hWnd,LPCSTR pszName,DWORD dwLevel,PVOID pConnectionInfo); /*Not supported and returns ERROR_NOT_SUPPORTED.*/
+
+HRESULT WINAPI DeletePrinterDriverPackageA(
+  LPCSTR pszServer,
+  LPCSTR pszInfPath,
+  LPCSTR pszEnvironment
+);
+
+HRESULT WINAPI DeletePrinterDriverPackageW(
+  LPCWSTR pszServer,
+  LPCWSTR pszInfPath,
+  LPCWSTR pszEnvironment
+);
+
+HRESULT DocumentEventA(
+  HANDLE hPrinter,
+  HDC hdc,
+  INT iEsc,
+  ULONG cbIn,
+  PVOID pvIn,
+  ULONG cbOut,
+  PVOID pvOut
+);
+
+HRESULT DocumentEventW(
+  HANDLE hPrinter,
+  HDC hdc,
+  INT iEsc,
+  ULONG cbIn,
+  PVOID pvIn,
+  ULONG cbOut,
+  PVOID pvOut
+);
 
 typedef struct _DRIVER_INFO_8W {
-  DWORD cVersion;
-  LPWSTR pName;
-  LPWSTR pEnvironment;
-  LPWSTR pDriverPath;
-  LPWSTR pDataFile;
-  LPWSTR pConfigFile;
-  LPWSTR pHelpFile;
-  LPWSTR pDependentFiles;
-  LPWSTR pMonitorName;
-  LPWSTR pDefaultDataType;
-  LPWSTR pszzPreviousNames;
-  FILETIME ftDriverDate;
+  DWORD     cVersion;
+  LPWSTR    pName;
+  LPWSTR    pEnvironment;
+  LPWSTR    pDriverPath;
+  LPWSTR    pDataFile;
+  LPWSTR    pConfigFile;
+  LPWSTR    pHelpFile;
+  LPWSTR    pDependentFiles;
+  LPWSTR    pMonitorName;
+  LPWSTR    pDefaultDataType;
+  LPWSTR    pszzPreviousNames;
+  FILETIME  ftDriverDate;
   DWORDLONG dwlDriverVersion;
-  LPWSTR pszMfgName;
-  LPWSTR pszOEMUrl;
-  LPWSTR pszHardwareID;
-  LPWSTR pszProvider;
-  LPWSTR pszPrintProcessor;
-  LPWSTR pszVendorSetup;
-  LPWSTR pszzColorProfiles;
-  LPWSTR pszInfPath;
-  DWORD dwPrinterDriverAttributes;
-  LPWSTR pszzCoreDriverDependencies;
-  FILETIME ftMinInboxDriverVerDate;
+  LPWSTR    pszMfgName;
+  LPWSTR    pszOEMUrl;
+  LPWSTR    pszHardwareID;
+  LPWSTR    pszProvider;
+  LPWSTR    pszPrintProcessor;
+  LPWSTR    pszVendorSetup;
+  LPWSTR    pszzColorProfiles;
+  LPWSTR    pszInfPath;
+  DWORD     dwPrinterDriverAttributes;
+  LPWSTR    pszzCoreDriverDependencies;
+  FILETIME  ftMinInboxDriverVerDate;
   DWORDLONG dwlMinInboxDriverVerVersion;
 } DRIVER_INFO_8W, *PDRIVER_INFO_8W, *LPDRIVER_INFO_8W;
 
-// FLAGS for dwDriverAttributes
-#define DRIVER_KERNELMODE 0x00000001
-#define DRIVER_USERMODE   0x00000002
-
-// FLAGS for DeletePrinterDriverEx.
-#define DPD_DELETE_UNUSED_FILES     0x00000001
-#define DPD_DELETE_SPECIFIC_VERSION 0x00000002
-#define DPD_DELETE_ALL_FILES        0x00000004
-
-// FLAGS for AddPrinterDriverEx.
-#define APD_STRICT_UPGRADE      0x00000001
-#define APD_STRICT_DOWNGRADE    0x00000002
-#define APD_COPY_ALL_FILES      0x00000004
-#define APD_COPY_NEW_FILES      0x00000008
-#if(_WIN32_WINNT >= 0x0501)
-#define APD_COPY_FROM_DIRECTORY 0x00000010
-#endif
-
-typedef struct _MONITOR_INFO_1A {
-  LPSTR pName;
-} MONITOR_INFO_1A, *PMONITOR_INFO_1A, *LPMONITOR_INFO_1A;
-
-typedef struct _MONITOR_INFO_1W {
-  LPWSTR pName;
-} MONITOR_INFO_1W, *PMONITOR_INFO_1W, *LPMONITOR_INFO_1W;
-
-typedef struct _PORT_INFO_1A {
-  LPSTR pName;
-} PORT_INFO_1A, *PPORT_INFO_1A, *LPPORT_INFO_1A;
-
-typedef struct _PORT_INFO_1W {
-  LPWSTR pName;
-} PORT_INFO_1W, *PPORT_INFO_1W, *LPPORT_INFO_1W;
-
-typedef struct _MONITOR_INFO_2A {
-  LPSTR pName;
-  LPSTR pEnvironment;
-  LPSTR pDLLName;
-} MONITOR_INFO_2A, *PMONITOR_INFO_2A, *LPMONITOR_INFO_2A;
-
-typedef struct _MONITOR_INFO_2W {
-  LPWSTR pName;
-  LPWSTR pEnvironment;
-  LPWSTR pDLLName;
-} MONITOR_INFO_2W, *PMONITOR_INFO_2W, *LPMONITOR_INFO_2W;
-
-typedef struct _PORT_INFO_2A {
-  LPSTR pPortName;
-  LPSTR pMonitorName;
-  LPSTR pDescription;
-  DWORD fPortType;
-  DWORD Reserved;
-} PORT_INFO_2A, *PPORT_INFO_2A, *LPPORT_INFO_2A;
-
-typedef struct _PORT_INFO_2W {
-  LPWSTR pPortName;
-  LPWSTR pMonitorName;
-  LPWSTR pDescription;
-  DWORD fPortType;
-  DWORD Reserved;
-} PORT_INFO_2W, *PPORT_INFO_2W, *LPPORT_INFO_2W;
-
-typedef struct _PORT_INFO_3A {
-  DWORD dwStatus;
-  LPSTR pszStatus;
-  DWORD dwSeverity;
-} PORT_INFO_3A, *PPORT_INFO_3A, *LPPORT_INFO_3A;
-
-typedef struct _PORT_INFO_3W {
-  DWORD dwStatus;
-  LPWSTR pszStatus;
-  DWORD dwSeverity;
-} PORT_INFO_3W, *PPORT_INFO_3W, *LPPORT_INFO_3W;
-
-typedef struct _PRINTER_INFO_1A {
-  DWORD Flags;
-  LPSTR pDescription;
-  LPSTR pName;
-  LPSTR pComment;
-} PRINTER_INFO_1A, *PPRINTER_INFO_1A, *LPPRINTER_INFO_1A;
-
-typedef struct _PRINTER_INFO_1W {
-  DWORD Flags;
-  LPWSTR pDescription;
-  LPWSTR pName;
-  LPWSTR pComment;
-} PRINTER_INFO_1W, *PPRINTER_INFO_1W, *LPPRINTER_INFO_1W;
-
-typedef struct _PRINTER_INFO_2A {
-  LPSTR pServerName;
-  LPSTR pPrinterName;
-  LPSTR pShareName;
-  LPSTR pPortName;
-  LPSTR pDriverName;
-  LPSTR pComment;
-  LPSTR pLocation;
-  LPDEVMODEA pDevMode;
-  LPSTR pSepFile;
-  LPSTR pPrintProcessor;
-  LPSTR pDatatype;
-  LPSTR pParameters;
-  PSECURITY_DESCRIPTOR pSecurityDescriptor;
-  DWORD Attributes;
-  DWORD Priority;
-  DWORD DefaultPriority;
-  DWORD StartTime;
-  DWORD UntilTime;
-  DWORD Status;
-  DWORD cJobs;
-  DWORD AveragePPM;
-} PRINTER_INFO_2A, *PPRINTER_INFO_2A, *LPPRINTER_INFO_2A;
-
-typedef struct _PRINTER_INFO_2W {
-  LPWSTR pServerName;
-  LPWSTR pPrinterName;
-  LPWSTR pShareName;
-  LPWSTR pPortName;
-  LPWSTR pDriverName;
-  LPWSTR pComment;
-  LPWSTR pLocation;
-  LPDEVMODEW pDevMode;
-  LPWSTR pSepFile;
-  LPWSTR pPrintProcessor;
-  LPWSTR pDatatype;
-  LPWSTR pParameters;
-  PSECURITY_DESCRIPTOR pSecurityDescriptor;
-  DWORD Attributes;
-  DWORD Priority;
-  DWORD DefaultPriority;
-  DWORD StartTime;
-  DWORD UntilTime;
-  DWORD Status;
-  DWORD cJobs;
-  DWORD AveragePPM;
-} PRINTER_INFO_2W, *PPRINTER_INFO_2W, *LPPRINTER_INFO_2W;
-
-typedef struct _PRINTER_INFO_3 {
-  PSECURITY_DESCRIPTOR pSecurityDescriptor;
-} PRINTER_INFO_3, *PPRINTER_INFO_3, *LPPRINTER_INFO_3;
-
-typedef struct _PRINTER_INFO_4A {
-  LPSTR pPrinterName;
-  LPSTR pServerName;
-  DWORD Attributes;
-} PRINTER_INFO_4A, *PPRINTER_INFO_4A, *LPPRINTER_INFO_4A;
-
-typedef struct _PRINTER_INFO_4W {
-  LPWSTR pPrinterName;
-  LPWSTR pServerName;
-  DWORD Attributes;
-} PRINTER_INFO_4W, *PPRINTER_INFO_4W, *LPPRINTER_INFO_4W;
-
-typedef struct _PRINTER_INFO_5A {
-  LPSTR pPrinterName;
-  LPSTR pPortName;
-  DWORD Attributes;
-  DWORD DeviceNotSelectedTimeout;
-  DWORD TransmissionRetryTimeout;
-} PRINTER_INFO_5A, *PPRINTER_INFO_5A, *LPPRINTER_INFO_5A;
-
-typedef struct _PRINTER_INFO_5W {
-  LPWSTR pPrinterName;
-  LPWSTR pPortName;
-  DWORD Attributes;
-  DWORD DeviceNotSelectedTimeout;
-  DWORD TransmissionRetryTimeout;
-} PRINTER_INFO_5W, *PPRINTER_INFO_5W, *LPPRINTER_INFO_5W;
-
-typedef struct _PRINTER_INFO_6 {
-  DWORD dwStatus;
-} PRINTER_INFO_6, *PPRINTER_INFO_6, *LPPRINTER_INFO_6;
-
-typedef struct _PRINTER_INFO_7A {
-  LPSTR pszObjectGUID;
-  DWORD dwAction;
-} PRINTER_INFO_7A, *PPRINTER_INFO_7A, *LPPRINTER_INFO_7A;
-
-typedef struct _PRINTER_INFO_7W {
-  LPWSTR pszObjectGUID;
-  DWORD dwAction;
-} PRINTER_INFO_7W, *PPRINTER_INFO_7W, *LPPRINTER_INFO_7W;
-
-typedef struct _PRINTER_INFO_8A {
-  LPDEVMODEA pDevMode;
-} PRINTER_INFO_8A, *PPRINTER_INFO_8A, *LPPRINTER_INFO_8A;
-
-typedef struct _PRINTER_INFO_8W {
-  LPDEVMODEW pDevMode;
-} PRINTER_INFO_8W, *PPRINTER_INFO_8W, *LPPRINTER_INFO_8W;
-
-typedef struct _PRINTER_INFO_9A {
-  LPDEVMODEA pDevMode;
-} PRINTER_INFO_9A, *PPRINTER_INFO_9A, *LPPRINTER_INFO_9A;
-
-typedef struct _PRINTER_INFO_9W {
-  LPDEVMODEW pDevMode;
-} PRINTER_INFO_9W, *PPRINTER_INFO_9W, *LPPRINTER_INFO_9W;
-
-typedef struct _PRINTPROCESSOR_INFO_1A {
-  LPSTR pName;
-} PRINTPROCESSOR_INFO_1A, *PPRINTPROCESSOR_INFO_1A, *LPPRINTPROCESSOR_INFO_1A;
-
-typedef struct _PRINTPROCESSOR_INFO_1W {
-  LPWSTR pName;
-} PRINTPROCESSOR_INFO_1W, *PPRINTPROCESSOR_INFO_1W, *LPPRINTPROCESSOR_INFO_1W;
-
-typedef struct _PRINTER_NOTIFY_INFO_DATA {
-  WORD Type;
-  WORD Field;
-  DWORD Reserved;
-  DWORD Id;
-  union {
-    DWORD adwData[2];
-    struct {
-      DWORD cbBuf;
-      PVOID pBuf;
-    } Data;
-  } NotifyData;
-} PRINTER_NOTIFY_INFO_DATA, *PPRINTER_NOTIFY_INFO_DATA, *LPPRINTER_NOTIFY_INFO_DATA;
-
-typedef struct _PRINTER_NOTIFY_INFO {
-  DWORD Version;
-  DWORD Flags;
-  DWORD Count;
-  PRINTER_NOTIFY_INFO_DATA aData[1];
-} PRINTER_NOTIFY_INFO, *PPRINTER_NOTIFY_INFO, *LPPRINTER_NOTIFY_INFO;
-
-typedef struct _FORM_INFO_1A {
-  DWORD Flags;
-  LPSTR pName;
-  SIZEL Size;
-  RECTL ImageableArea;
-} FORM_INFO_1A, *PFORM_INFO_1A, *LPFORM_INFO_1A;
-
-typedef struct _FORM_INFO_1W {
-  DWORD Flags;
-  LPWSTR pName;
-  SIZEL Size;
-  RECTL ImageableArea;
-} FORM_INFO_1W, *PFORM_INFO_1W, *LPFORM_INFO_1W;
-
-#if (NTDDI_VERSION >= NTDDI_VISTA)
+typedef struct _DRIVER_INFO_8A {
+  DWORD     cVersion;
+  LPSTR    pName;
+  LPSTR    pEnvironment;
+  LPSTR    pDriverPath;
+  LPSTR    pDataFile;
+  LPSTR    pConfigFile;
+  LPSTR    pHelpFile;
+  LPSTR    pDependentFiles;
+  LPSTR    pMonitorName;
+  LPSTR    pDefaultDataType;
+  LPSTR    pszzPreviousNames;
+  FILETIME  ftDriverDate;
+  DWORDLONG dwlDriverVersion;
+  LPSTR    pszMfgName;
+  LPSTR    pszOEMUrl;
+  LPSTR    pszHardwareID;
+  LPSTR    pszProvider;
+  LPSTR    pszPrintProcessor;
+  LPSTR    pszVendorSetup;
+  LPSTR    pszzColorProfiles;
+  LPSTR    pszInfPath;
+  DWORD     dwPrinterDriverAttributes;
+  LPSTR    pszzCoreDriverDependencies;
+  FILETIME  ftMinInboxDriverVerDate;
+  DWORDLONG dwlMinInboxDriverVerVersion;
+} DRIVER_INFO_8A, *PDRIVER_INFO_8A, *LPDRIVER_INFO_8A;
 
 typedef struct _FORM_INFO_2A {
-  DWORD Flags;
-  LPCSTR pName;
-  SIZEL Size;
-  RECTL ImageableArea;
-  LPCSTR pKeyword;
-  DWORD StringType;
-  LPCSTR pMuiDll;
-  DWORD dwResourceId;
-  LPCSTR pDisplayName;
-  LANGID wLangId;
-} FORM_INFO_2A, *PFORM_INFO_2A, *LPFORM_INFO_2A;
+  DWORD   Flags;
+  LPSTR   pName;
+  SIZEL   Size;
+  RECTL   ImageableArea;
+  LPCSTR  pKeyword;
+  DWORD   StringType;
+  LPCSTR  pMuiDll;
+  DWORD   dwResourceId;
+  LPCSTR  pDisplayName;
+  LANGID  wLangId;
+} FORM_INFO_2A, *PFORM_INFO_2A;
 
 typedef struct _FORM_INFO_2W {
-  DWORD Flags;
-  LPCWSTR pName;
-  SIZEL Size;
-  RECTL ImageableArea;
-  LPCSTR pKeyword;
-  DWORD StringType;
+  DWORD   Flags;
+  LPWSTR  pName;
+  SIZEL   Size;
+  RECTL   ImageableArea;
+  LPCSTR  pKeyword;
+  DWORD   StringType;
   LPCWSTR pMuiDll;
-  DWORD dwResourceId;
+  DWORD   dwResourceId;
   LPCWSTR pDisplayName;
-  LANGID wLangId;
-} FORM_INFO_2W, *PFORM_INFO_2W, *LPFORM_INFO_2W;
-
-#endif /* (NTDDI_VERSION >= NTDDI_VISTA) */
-
-typedef struct _PRINTER_DEFAULTSA {
-  LPSTR pDatatype;
-  LPDEVMODE pDevMode;
-  ACCESS_MASK DesiredAccess;
-} PRINTER_DEFAULTSA, *PPRINTER_DEFAULTSA, *LPPRINTER_DEFAULTSA;
-
-typedef struct _PRINTER_DEFAULTSW {
-  LPWSTR pDatatype;
-  LPDEVMODE pDevMode;
-  ACCESS_MASK DesiredAccess;
-} PRINTER_DEFAULTSW, *PPRINTER_DEFAULTSW, *LPPRINTER_DEFAULTSW;
-
-typedef struct _PROVIDOR_INFO_1A {
-  LPSTR pName;
-  LPSTR pEnvironment;
-  LPSTR pDLLName;
-} PROVIDOR_INFO_1A, *PPROVIDOR_INFO_1A, *LPPROVIDOR_INFO_1A;
-
-typedef struct _PROVIDOR_INFO_1W {
-  LPWSTR pName;
-  LPWSTR pEnvironment;
-  LPWSTR pDLLName;
-} PROVIDOR_INFO_1W, *LPPROVIDOR_INFO_1W;
-
-typedef struct _PROVIDOR_INFO_2A {
-  LPSTR pOrder;
-} PROVIDOR_INFO_2A, *PPROVIDOR_INFO_2A, *LPPROVIDOR_INFO_2A;
-
-typedef struct _PROVIDOR_INFO_2W {
-  LPWSTR pOrder;
-} PROVIDOR_INFO_2W, *LPPROVIDOR_INFO_2W;
-
-typedef struct _BINARY_CONTAINER {
-  DWORD cbBuf;
-  LPBYTE pData;
-} BINARY_CONTAINER, *PBINARY_CONTAINER;
-
-typedef struct _BIDI_DATA {
-  DWORD dwBidiType;
-  union {
-    BOOL bData;
-    INT iData;
-    LPWSTR sData;
-    FLOAT fData;
-    BINARY_CONTAINER biData;
-  } u;
-} BIDI_DATA, *LPBIDI_DATA, *PBIDI_DATA;
-
-typedef struct _BIDI_REQUEST_DATA {
-  DWORD dwReqNumber;
-  LPWSTR pSchema;
-  BIDI_DATA data;
-} BIDI_REQUEST_DATA, *LPBIDI_REQUEST_DATA, *PBIDI_REQUEST_DATA;
-
-typedef struct _BIDI_REQUEST_CONTAINER {
-  DWORD Version;
-  DWORD Flags;
-  DWORD Count;
-  BIDI_REQUEST_DATA aData[1];
-} BIDI_REQUEST_CONTAINER, *LPBIDI_REQUEST_CONTAINER, *PBIDI_REQUEST_CONTAINER;
-
-typedef struct _BIDI_RESPONSE_DATA {
-  DWORD dwResult;
-  DWORD dwReqNumber;
-  LPWSTR pSchema;
-  BIDI_DATA data;
-} BIDI_RESPONSE_DATA, *LPBIDI_RESPONSE_DATA, *PBIDI_RESPONSE_DATA;
-
-typedef struct _BIDI_RESPONSE_CONTAINER {
-  DWORD Version;
-  DWORD Flags;
-  DWORD Count;
-  BIDI_RESPONSE_DATA aData[1];
-} BIDI_RESPONSE_CONTAINER, *LPBIDI_RESPONSE_CONTAINER, *PBIDI_RESPONSE_CONTAINER;
-
-#ifdef UNICODE
-
-typedef JOB_INFO_1W JOB_INFO_1, *PJOB_INFO_1, *LPJOB_INFO_1;
-typedef JOB_INFO_2W JOB_INFO_2, *PJOB_INFO_2, *LPJOB_INFO_2;
-typedef JOB_INFO_4W JOB_INFO_4, *PJOB_INFO_4, *LPJOB_INFO_4;
-typedef ADDJOB_INFO_1W ADDJOB_INFO_1, *PADDJOB_INFO_1, *LPADDJOB_INFO_1;
-typedef DATATYPES_INFO_1W DATATYPES_INFO_1, *PDATATYPES_INFO_1, *LPDATATYPES_INFO_1;
-typedef MONITOR_INFO_1W MONITOR_INFO_1, *PMONITOR_INFO_1, *LPMONITOR_INFO_1;
-typedef MONITOR_INFO_2W MONITOR_INFO_2, *PMONITOR_INFO_2, *LPMONITOR_INFO_2;
-typedef DOC_INFO_1W DOC_INFO_1, *PDOC_INFO_1, *LPDOC_INFO_1;
-typedef DOC_INFO_2W DOC_INFO_2, *PDOC_INFO_2, *LPDOC_INFO_2;
-typedef PORT_INFO_1W PORT_INFO_1, *PPORT_INFO_1, *LPPORT_INFO_1;
-typedef PORT_INFO_2W PORT_INFO_2, *PPORT_INFO_2, *LPPORT_INFO_2;
-typedef PORT_INFO_3W PORT_INFO_3, *PPORT_INFO_3, *LPPORT_INFO_3;
-typedef DRIVER_INFO_2W DRIVER_INFO_2, *PDRIVER_INFO_2, *LPDRIVER_INFO_2;
-typedef DRIVER_INFO_4W DRIVER_INFO_4, *PDRIVER_INFO_4, *LPDRIVER_INFO_4;
-typedef DRIVER_INFO_5W DRIVER_INFO_5, *PDRIVER_INFO_5, *LPDRIVER_INFO_5;
-typedef DRIVER_INFO_6W DRIVER_INFO_6, *PDRIVER_INFO_6, *LPDRIVER_INFO_6;
-typedef DRIVER_INFO_8W DRIVER_INFO_8, *PDRIVER_INFO_8, *LPDRIVER_INFO_8;
-typedef PRINTER_INFO_1W PRINTER_INFO_1, *PPRINTER_INFO_1, *LPPRINTER_INFO_1;
-typedef PRINTER_INFO_2W PRINTER_INFO_2, *PPRINTER_INFO_2, *LPPRINTER_INFO_2;
-typedef PRINTER_INFO_4W PRINTER_INFO_4, *PPRINTER_INFO_4, *LPPRINTER_INFO_4;
-typedef PRINTER_INFO_5W PRINTER_INFO_5, *PPRINTER_INFO_5, *LPPRINTER_INFO_5;
-typedef PRINTER_INFO_7W PRINTER_INFO_7, *PPRINTER_INFO_7, *LPPRINTER_INFO_7;
-typedef PRINTER_INFO_8W PRINTER_INFO_8, *PPRINTER_INFO_8, *LPPRINTER_INFO_8;
-typedef PRINTER_INFO_9W PRINTER_INFO_9, *PPRINTER_INFO_9, *LPPRINTER_INFO_9;
-typedef PRINTPROCESSOR_INFO_1W PRINTPROCESSOR_INFO_1, *PPRINTPROCESSOR_INFO_1, *LPPRINTPROCESSOR_INFO_1;
-typedef FORM_INFO_1W FORM_INFO_1, *PFORM_INFO_1, *LPFORM_INFO_1;
-#if (NTDDI_VERSION >= NTDDI_VISTA)
-typedef FORM_INFO_2W FORM_INFO_2, *PFORM_INFO_2, *LPFORM_INFO_2;
-#endif
-typedef PRINTER_DEFAULTSW PRINTER_DEFAULTS, *PPRINTER_DEFAULTS, *LPPRINTER_DEFAULTS;
-typedef PROVIDOR_INFO_1W PROVIDOR_INFO_1, *PPROVIDOR_INFO_1, *LPPROVIDOR_INFO_1;
-typedef PROVIDOR_INFO_2W PROVIDOR_INFO_2, *PPROVIDOR_INFO_2, *LPPROVIDOR_INFO_2;
-
-#else /* UNICODE */
-
-typedef JOB_INFO_1A JOB_INFO_1, *PJOB_INFO_1, *LPJOB_INFO_1;
-typedef JOB_INFO_2A JOB_INFO_2, *PJOB_INFO_2, *LPJOB_INFO_2;
-typedef JOB_INFO_4A JOB_INFO_4, *PJOB_INFO_4, *LPJOB_INFO_4;
-typedef ADDJOB_INFO_1A ADDJOB_INFO_1, *PADDJOB_INFO_1, *LPADDJOB_INFO_1;
-typedef DATATYPES_INFO_1A DATATYPES_INFO_1, *PDATATYPES_INFO_1, *LPDATATYPES_INFO_1;
-typedef MONITOR_INFO_1A MONITOR_INFO_1, *PMONITOR_INFO_1, *LPMONITOR_INFO_1;
-typedef MONITOR_INFO_2A MONITOR_INFO_2, *PMONITOR_INFO_2, *LPMONITOR_INFO_2;
-typedef DOC_INFO_1A DOC_INFO_1, *PDOC_INFO_1, *LPDOC_INFO_1;
-typedef DOC_INFO_2A DOC_INFO_2, *PDOC_INFO_2, *LPDOC_INFO_2;
-typedef PORT_INFO_1A PORT_INFO_1, *PPORT_INFO_1, *LPPORT_INFO_1;
-typedef PORT_INFO_2A PORT_INFO_2, *PPORT_INFO_2, *LPPORT_INFO_2;
-typedef PORT_INFO_3A PORT_INFO_3, *PPORT_INFO_3, *LPPORT_INFO_3;
-typedef DRIVER_INFO_2A DRIVER_INFO_2, *PDRIVER_INFO_2, *LPDRIVER_INFO_2;
-typedef DRIVER_INFO_4A DRIVER_INFO_4, *PDRIVER_INFO_4, *LPDRIVER_INFO_4;
-typedef DRIVER_INFO_5A DRIVER_INFO_5, *PDRIVER_INFO_5, *LPDRIVER_INFO_5;
-typedef DRIVER_INFO_6A DRIVER_INFO_6, *PDRIVER_INFO_6, *LPDRIVER_INFO_6;
-typedef DRIVER_INFO_8A DRIVER_INFO_8, *PDRIVER_INFO_8, *LPDRIVER_INFO_8;
-typedef PRINTER_INFO_1A PRINTER_INFO_1, *PPRINTER_INFO_1, *LPPRINTER_INFO_1;
-typedef PRINTER_INFO_2A PRINTER_INFO_2, *PPRINTER_INFO_2, *LPPRINTER_INFO_2;
-typedef PRINTER_INFO_4A PRINTER_INFO_4, *PPRINTER_INFO_4, *LPPRINTER_INFO_4;
-typedef PRINTER_INFO_5A PRINTER_INFO_5, *PPRINTER_INFO_5, *LPPRINTER_INFO_5;
-typedef PRINTER_INFO_7A PRINTER_INFO_7, *PPRINTER_INFO_7, *LPPRINTER_INFO_7;
-typedef PRINTER_INFO_8A PRINTER_INFO_8, *PPRINTER_INFO_8, *LPPRINTER_INFO_8;
-typedef PRINTER_INFO_9A PRINTER_INFO_9, *PPRINTER_INFO_9, *LPPRINTER_INFO_9;
-typedef PRINTPROCESSOR_INFO_1A PRINTPROCESSOR_INFO_1, *PPRINTPROCESSOR_INFO_1, *LPPRINTPROCESSOR_INFO_1;
-typedef FORM_INFO_1A FORM_INFO_1, *PFORM_INFO_1, *LPFORM_INFO_1;
-#if (NTDDI_VERSION >= NTDDI_VISTA)
-typedef FORM_INFO_2A FORM_INFO_2, *PFORM_INFO_2, *LPFORM_INFO_2;
-#endif
-typedef PRINTER_DEFAULTSA PRINTER_DEFAULTS, *PPRINTER_DEFAULTS, *LPPRINTER_DEFAULTS;
-typedef PROVIDOR_INFO_1A PROVIDOR_INFO_1, *PPROVIDOR_INFO_1, *LPPROVIDOR_INFO_1;
-typedef PROVIDOR_INFO_2A PROVIDOR_INFO_2, *PPROVIDOR_INFO_2, *LPPROVIDOR_INFO_2;
-
-#endif /* UNICODE */
-
-BOOL WINAPI AbortPrinter(_In_ HANDLE);
-
-BOOL
-WINAPI
-AddFormA(
-  _In_ HANDLE hPrinter,
-  _In_range_(1, 2) DWORD Level,
-  _When_(Level == 1, _In_reads_bytes_(sizeof(FORM_INFO_1A)))
-#if (NTDDI_VERSION >= NTDDI_VISTA)
-  _When_(Level == 2, _In_reads_bytes_(sizeof(FORM_INFO_2A)))
-#endif
-    LPBYTE pForm);
-
-BOOL
-WINAPI
-AddFormW(
-  _In_ HANDLE hPrinter,
-  _In_range_(1, 2) DWORD Level,
-  _When_(Level == 1, _In_reads_bytes_(sizeof(FORM_INFO_1W)))
-#if (NTDDI_VERSION >= NTDDI_VISTA)
-  _When_(Level == 2, _In_reads_bytes_(sizeof(FORM_INFO_2W)))
-#endif
-  LPBYTE pForm);
-
-BOOL
-WINAPI
-AddJobA(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pData,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-AddJobW(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pData,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-AddMonitorA(
-  _In_opt_ LPSTR pName,
-  _In_ DWORD Level,
-  _In_reads_bytes_opt_(sizeof(MONITOR_INFO_2)) LPBYTE pMonitors);
-
-BOOL
-WINAPI
-AddMonitorW(
-  _In_opt_ LPWSTR pName,
-  _In_ DWORD Level,
-  _In_reads_bytes_opt_(sizeof(MONITOR_INFO_2)) LPBYTE pMonitors);
-
-BOOL WINAPI AddPortA(_In_opt_ LPSTR, _In_ HWND, _In_ LPSTR);
-BOOL WINAPI AddPortW(_In_opt_ LPWSTR, _In_ HWND, _In_ LPWSTR);
-
-HANDLE
-WINAPI
-AddPrinterA(
-  _In_opt_ LPSTR pName,
-  _In_range_(1, 2) DWORD Level,
-  _When_(Level == 1, _In_reads_bytes_(sizeof(PRINTER_INFO_1)))
-  _When_(Level == 2, _In_reads_bytes_(sizeof(PRINTER_INFO_2)))
-  LPBYTE pPrinter);
-
-HANDLE
-WINAPI
-AddPrinterW(
-  _In_opt_ LPWSTR pName,
-  _In_range_(1, 2) DWORD Level,
-  _When_(Level == 1, _In_reads_bytes_(sizeof(PRINTER_INFO_1)))
-  _When_(Level == 2, _In_reads_bytes_(sizeof(PRINTER_INFO_2)))
-  LPBYTE pPrinter);
-
-BOOL WINAPI AddPrinterConnectionA(_In_ LPSTR);
-BOOL WINAPI AddPrinterConnectionW(_In_ LPWSTR);
-BOOL WINAPI AddPrinterDriverA(_In_opt_ LPSTR, _In_ DWORD, _In_ PBYTE);
-BOOL WINAPI AddPrinterDriverW(_In_opt_ LPWSTR, _In_ DWORD, _In_ PBYTE);
-
-BOOL
-WINAPI
-AddPrintProcessorA(
-  _In_opt_ LPSTR pName,
-  _In_opt_ LPSTR pEnvironment,
-  _In_ LPSTR pPathName,
-  _In_ LPSTR pPrintProcessorName);
-
-BOOL
-WINAPI
-AddPrintProcessorW(
-  _In_opt_ LPWSTR pName,
-  _In_opt_ LPWSTR pEnvironment,
-  _In_ LPWSTR pPathName,
-  _In_ LPWSTR pPrintProcessorName);
-
-BOOL
-WINAPI
-AddPrintProvidorA(
-  _In_opt_ LPSTR pName,
-  _In_range_(1, 2) DWORD Level,
-  _When_(Level == 1, _In_reads_bytes_(sizeof(PROVIDOR_INFO_1)))
-  _When_(Level == 2, _In_reads_bytes_(sizeof(PROVIDOR_INFO_2)))
-    LPBYTE pProvidorInfo);
-
-BOOL
-WINAPI
-AddPrintProvidorW(
-  _In_opt_ LPWSTR pName,
-  _In_range_(1, 2) DWORD Level,
-  _When_(Level == 1, _In_reads_bytes_(sizeof(PROVIDOR_INFO_1)))
-  _When_(Level == 2, _In_reads_bytes_(sizeof(PROVIDOR_INFO_2)))
-    LPBYTE pProvidorInfo);
-
-LONG
-WINAPI
-AdvancedDocumentPropertiesA(
-  _In_ HWND hWnd,
-  _In_ HANDLE hPrinter,
-  _In_ LPSTR pDeviceName,
-  _Inout_opt_ PDEVMODEA pDevModeOutput,
-  _In_opt_ PDEVMODEA pDevModeInput);
-
-LONG
-WINAPI
-AdvancedDocumentPropertiesW(
-  _In_ HWND hWnd,
-  _In_ HANDLE hPrinter,
-  _In_ LPWSTR pDeviceName,
-  _Inout_opt_ PDEVMODEW pDevModeOutput,
-  _In_opt_ PDEVMODEW pDevModeInput);
-
-BOOL WINAPI ClosePrinter(_In_ HANDLE);
-BOOL WINAPI ConfigurePortA(_In_opt_ LPSTR, _In_ HWND, _In_ LPSTR);
-BOOL WINAPI ConfigurePortW(_In_opt_ LPWSTR, _In_ HWND, _In_ LPWSTR);
-HANDLE WINAPI ConnectToPrinterDlg(_In_ HWND, _In_ DWORD);
-BOOL WINAPI DeleteFormA(_In_ HANDLE, _In_ LPSTR);
-BOOL WINAPI DeleteFormW(_In_ HANDLE, _In_ LPWSTR);
-BOOL WINAPI DeleteMonitorA(_In_opt_ LPSTR, _In_opt_ LPSTR, _In_ LPSTR);
-BOOL WINAPI DeleteMonitorW(_In_opt_ LPWSTR, _In_opt_ LPWSTR, _In_ LPWSTR);
-BOOL WINAPI DeletePortA(_In_opt_ LPSTR, _In_ HWND, _In_ LPSTR);
-BOOL WINAPI DeletePortW(_In_opt_ LPWSTR, _In_ HWND, _In_ LPWSTR);
-BOOL WINAPI DeletePrinter(_Inout_ HANDLE);
-BOOL WINAPI DeletePrinterConnectionA(_In_ LPSTR);
-BOOL WINAPI DeletePrinterConnectionW(_In_ LPWSTR);
-DWORD WINAPI DeletePrinterDataA(_In_ HANDLE, _In_ LPSTR);
-DWORD WINAPI DeletePrinterDataW(_In_ HANDLE, _In_ LPWSTR);
-
-BOOL
-WINAPI
-DeletePrinterDriverA(
-  _In_opt_ LPSTR pName,
-  _In_opt_ LPSTR pEnvironment,
-  _In_ LPSTR pDriverName);
-
-BOOL
-WINAPI
-DeletePrinterDriverW(
-  _In_opt_ LPWSTR pName,
-  _In_opt_ LPWSTR pEnvironment,
-  _In_ LPWSTR pDriverName);
-
-BOOL
-WINAPI
-DeletePrintProcessorA(
-  _In_opt_ LPSTR pName,
-  _In_opt_ LPSTR pEnvironment,
-  _In_ LPSTR pPrintProcessorName);
-
-BOOL
-WINAPI
-DeletePrintProcessorW(
-  _In_opt_ LPWSTR pName,
-  _In_opt_ LPWSTR pEnvironment,
-  _In_ LPWSTR pPrintProcessorName);
-
-BOOL
-WINAPI
-DeletePrintProvidorA(
-  _In_opt_ LPSTR pName,
-  _In_opt_ LPSTR pEnvironment,
-  _In_ LPSTR pPrintProvidorName);
-
-BOOL
-WINAPI
-DeletePrintProvidorW(
-  _In_opt_ LPWSTR pName,
-  _In_opt_ LPWSTR pEnvironment,
-  _In_ LPWSTR pPrintProvidorName);
-
-LONG
-WINAPI
-DocumentPropertiesA(
-  _In_opt_ HWND hWnd,
-  _In_ HANDLE hPrinter,
-  _In_ LPSTR pDeviceName,
-  _Out_opt_ PDEVMODEA pDevModeOutput,
-  _In_opt_ PDEVMODEA pDevModeInput,
-  _In_ DWORD fMode);
-
-LONG
-WINAPI
-DocumentPropertiesW(
-  _In_opt_ HWND hWnd,
-  _In_ HANDLE hPrinter,
-  _In_ LPWSTR pDeviceName,
-  _Out_opt_ PDEVMODEW pDevModeOutput,
-  _In_opt_ PDEVMODEW pDevModeInput,
-  _In_ DWORD fMode);
-
-BOOL WINAPI EndDocPrinter(_In_ HANDLE);
-BOOL WINAPI EndPagePrinter(_In_ HANDLE);
-
-BOOL
-WINAPI
-EnumFormsA(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pForm,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-BOOL
-WINAPI
-EnumFormsW(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pForm,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-BOOL
-WINAPI
-EnumJobsA(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD FirstJob,
-  _In_ DWORD NoJobs,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pJob,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-BOOL
-WINAPI
-EnumJobsW(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD FirstJob,
-  _In_ DWORD NoJobs,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pJob,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-BOOL
-WINAPI
-EnumMonitorsA(
-  _In_opt_ LPSTR pName,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pMonitor,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-BOOL
-WINAPI
-EnumMonitorsW(
-  _In_opt_ LPWSTR pName,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pMonitor,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-BOOL
-WINAPI
-EnumPortsA(
-  _In_opt_ LPSTR pName,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pPort,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-BOOL
-WINAPI
-EnumPortsW(
-  _In_opt_ LPWSTR pName,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pPort,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-DWORD
-WINAPI
-EnumPrinterDataA(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD dwIndex,
-  _Out_writes_bytes_(cbValueName) LPSTR pValueName,
-  _In_ DWORD cbValueName,
-  _Out_ LPDWORD pcbValueName,
-  _Out_opt_ LPDWORD pType,
-  _Out_writes_to_opt_(cbData, *pcbData) LPBYTE pData,
-  _In_ DWORD cbData,
-  _When_(pData != NULL, _Pre_notnull_) _Out_opt_ LPDWORD pcbData);
-
-DWORD
-WINAPI
-EnumPrinterDataW(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD dwIndex,
-  _Out_writes_bytes_(cbValueName) LPWSTR pValueName,
-  _In_ DWORD cbValueName,
-  _Out_ LPDWORD pcbValueName,
-  _Out_opt_ LPDWORD pType,
-  _Out_writes_to_opt_(cbData, *pcbData) LPBYTE pData,
-  _In_ DWORD cbData,
-  _When_(pData != NULL, _Pre_notnull_) _Out_opt_ LPDWORD pcbData);
-
-BOOL
-WINAPI
-EnumPrinterDriversA(
-  _In_opt_ LPSTR pName,
-  _In_opt_ LPSTR pEnvironment,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pDriverInfo,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-BOOL
-WINAPI
-EnumPrinterDriversW(
-  _In_opt_ LPWSTR pName,
-  _In_opt_ LPWSTR pEnvironment,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pDriverInfo,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-_Success_(return != 0)
-BOOL
-WINAPI
-EnumPrintersA(
-  _In_ DWORD Flags,
-  _In_opt_ LPSTR Name,
-  _In_ DWORD Level,
-  _Out_writes_bytes_to_opt_(cbBuf, *pcbNeeded) LPBYTE pPrinterEnum,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-_Success_(return != 0)
-BOOL
-WINAPI
-EnumPrintersW(
-  _In_ DWORD Flags,
-  _In_opt_ LPWSTR Name,
-  _In_ DWORD Level,
-  _Out_writes_bytes_to_opt_(cbBuf, *pcbNeeded) LPBYTE pPrinterEnum,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-_Success_(return != 0)
-BOOL
-WINAPI
-EnumPrintProcessorDatatypesA(
-  _In_opt_ LPSTR pName,
-  _In_ LPSTR pPrintProcessorName,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pDatatypes,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-_Success_(return != 0)
-BOOL
-WINAPI
-EnumPrintProcessorDatatypesW(
-  _In_opt_ LPWSTR pName,
-  _In_ LPWSTR pPrintProcessorName,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pDatatypes,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-BOOL
-WINAPI
-EnumPrintProcessorsA(
-  _In_opt_ LPSTR pName,
-  _In_opt_ LPSTR pEnvironment,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pPrintProcessorInfo,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-BOOL
-WINAPI
-EnumPrintProcessorsW(
-  _In_opt_ LPWSTR pName,
-  _In_opt_ LPWSTR pEnvironment,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pPrintProcessorInfo,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded,
-  _Out_ LPDWORD pcReturned);
-
-LONG
-WINAPI
-ExtDeviceMode(
-  _In_opt_ HWND hWnd,
-  _In_opt_ HANDLE hInst,
-  _Inout_opt_ LPDEVMODEA pDevModeOutput,
-  _In_opt_ LPSTR pDeviceName,
-  _In_opt_ LPSTR pPort,
-  _In_opt_ LPDEVMODEA pDevModeInput,
-  _In_opt_ LPSTR pProfile,
-  _In_ DWORD fMode);
-
-BOOL WINAPI FindClosePrinterChangeNotification(_In_ HANDLE);
-
-HANDLE
-WINAPI
-FindFirstPrinterChangeNotification(
-  _In_ HANDLE hPrinter,
-  DWORD fdwFilter,
-  DWORD fdwOptions,
-  _In_opt_ PVOID pPrinterNotifyOptions);
-
-BOOL
-WINAPI
-FindNextPrinterChangeNotification(
-  _In_ HANDLE hChange,
-  _Out_opt_ PDWORD pdwChange,
-  _In_opt_ LPVOID pvReserved,
-  _Out_opt_ LPVOID *ppPrinterNotifyInfo);
-
-BOOL WINAPI FreePrinterNotifyInfo(_In_ PPRINTER_NOTIFY_INFO);
-
-#if _WIN32_WINNT >= 0x0500
-
-BOOL
-WINAPI
-GetDefaultPrinterA(
-  _Out_writes_opt_(*pcchBuffer) LPSTR pszBuffer,
-  _Inout_ LPDWORD pcchBuffer);
-
-BOOL
-WINAPI
-GetDefaultPrinterW(
-  _Out_writes_opt_(*pcchBuffer) LPWSTR pszBuffer,
-  _Inout_ LPDWORD pcchBuffer);
-
-#endif
-
-BOOL
-WINAPI
-GetFormA(
-  _In_ HANDLE hPrinter,
-  _In_ LPSTR pFormName,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pForm,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetFormW(
-  _In_ HANDLE hPrinter,
-  _In_ LPWSTR pFormName,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pForm,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetJobA(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD JobId,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pJob,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetJobW(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD JobId,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pJob,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetPrinterA(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pPrinter,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetPrinterW(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pPrinter,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-DWORD
-WINAPI
-GetPrinterDataA(
-  _In_ HANDLE hPrinter,
-  _In_ LPSTR pValueName,
-  _Out_opt_ LPDWORD pType,
-  _Out_writes_bytes_opt_(nSize) LPBYTE pData,
-  _In_ DWORD nSize,
-  _Out_ LPDWORD pcbNeeded);
-
-DWORD
-WINAPI
-GetPrinterDataW(
-  _In_ HANDLE hPrinter,
-  _In_ LPWSTR pValueName,
-  _Out_opt_ LPDWORD pType,
-  _Out_writes_bytes_opt_(nSize) LPBYTE pData,
-  _In_ DWORD nSize,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetPrinterDriverA(
-  _In_ HANDLE  hPrinter,
-  _In_opt_ LPSTR pEnvironment,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pDriverInfo,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetPrinterDriverW(
-  _In_ HANDLE hPrinter,
-  _In_opt_ LPWSTR pEnvironment,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pDriverInfo,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetPrinterDriverDirectoryA(
-  _In_opt_ LPSTR pName,
-  _In_opt_ LPSTR pEnvironment,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pDriverDirectory,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetPrinterDriverDirectoryW(
-  _In_opt_ LPWSTR pName,
-  _In_opt_ LPWSTR pEnvironment,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pDriverDirectory,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetPrintProcessorDirectoryA(
-  _In_opt_ LPSTR pName,
-  _In_opt_ LPSTR pEnvironment,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pPrintProcessorInfo,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-BOOL
-WINAPI
-GetPrintProcessorDirectoryW(
-  _In_opt_ LPWSTR pName,
-  _In_opt_ LPWSTR pEnvironment,
-  _In_ DWORD Level,
-  _Out_writes_bytes_opt_(cbBuf) LPBYTE pPrintProcessorInfo,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcbNeeded);
-
-#if NTDDI_VERSION >= NTDDI_WINXPSP2
-BOOL WINAPI IsValidDevmodeA(_In_opt_ PDEVMODEA, size_t);
-BOOL WINAPI IsValidDevmodeW(_In_opt_ PDEVMODEW, size_t);
-#endif
-
-BOOL
-WINAPI
-OpenPrinterA(
-  _In_opt_ LPSTR pPrinterName,
-  _Out_ LPHANDLE phPrinter,
-  _In_opt_ LPPRINTER_DEFAULTSA pDefault);
-
-BOOL
-WINAPI
-OpenPrinterW(
-  _In_opt_ LPWSTR pPrinterName,
-  _Out_ LPHANDLE phPrinter,
-  _In_opt_ LPPRINTER_DEFAULTSW pDefault);
-
-DWORD
-WINAPI
-PrinterMessageBoxA(
-  _In_ HANDLE hPrinter,
-  DWORD Error,
-  _In_ HWND hWnd,
-  _In_ LPSTR pText,
-  _In_ LPSTR pCaption,
-  DWORD dwType);
-
-DWORD
-WINAPI
-PrinterMessageBoxW(
-  _In_ HANDLE hPrinter,
-  DWORD Error,
-  _In_ HWND hWnd,
-  _In_ LPWSTR pText,
-  _In_ LPWSTR pCaption,
-  DWORD dwType);
-
-BOOL WINAPI PrinterProperties(_In_ HWND, _In_ HANDLE);
-
-BOOL
-WINAPI
-ReadPrinter(
-  _In_ HANDLE hPrinter,
-  _Out_writes_bytes_(cbBuf) LPVOID pBuf,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pNoBytesRead);
-
-BOOL WINAPI ResetPrinterA(_In_ HANDLE, _In_opt_ LPPRINTER_DEFAULTSA);
-BOOL WINAPI ResetPrinterW(_In_ HANDLE, _In_opt_ LPPRINTER_DEFAULTSW);
-BOOL WINAPI ScheduleJob(_In_ HANDLE, _In_ DWORD);
-
-BOOL
-WINAPI
-SetFormA(
-  _In_ HANDLE hPrinter,
-  _In_ LPSTR pFormName,
-  _In_range_(1, 2) DWORD Level,
-  _When_(Level == 1, _In_reads_bytes_(sizeof(FORM_INFO_1A))) LPBYTE pForm);
-
-BOOL
-WINAPI
-SetFormW(
-  _In_ HANDLE hPrinter,
-  _In_ LPWSTR pFormName,
-  _In_range_(1, 2) DWORD Level,
-  _When_(Level == 1, _In_reads_bytes_(sizeof(FORM_INFO_1W))) LPBYTE pForm);
-
-BOOL
-WINAPI
-SetJobA(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD JobId,
-  _In_range_(0, 4) DWORD Level,
-  _When_(Level == 0, _Reserved_)
-  _When_(Level == 1, _In_reads_bytes_opt_(sizeof(JOB_INFO_1)))
-  _When_(Level == 2, _In_reads_bytes_opt_(sizeof(JOB_INFO_2)))
-  _When_(Level == 3, _In_reads_bytes_opt_(sizeof(JOB_INFO_3)))
-  _When_(Level == 4, _In_reads_bytes_opt_(sizeof(JOB_INFO_4)))
-    LPBYTE pJob,
-  _In_ DWORD Command);
-
-BOOL
-WINAPI
-SetJobW(
-  _In_ HANDLE hPrinter,
-  _In_ DWORD JobId,
-  _In_range_(0, 4) DWORD Level,
-  _When_(Level == 0, _Reserved_)
-  _When_(Level == 1, _In_reads_bytes_opt_(sizeof(JOB_INFO_1)))
-  _When_(Level == 2, _In_reads_bytes_opt_(sizeof(JOB_INFO_2)))
-  _When_(Level == 3, _In_reads_bytes_opt_(sizeof(JOB_INFO_3)))
-  _When_(Level == 4, _In_reads_bytes_opt_(sizeof(JOB_INFO_4)))
-    LPBYTE pJob,
-  _In_ DWORD Command);
-
-BOOL
-WINAPI
-SetPrinterA(
-  _In_ HANDLE hPrinter,
-  _In_range_(0, 9) DWORD Level,
-  _When_(Level == 0 && Command == PRINTER_CONTROL_SET_STATUS, _In_reads_bytes_(sizeof(DWORD)))
-  _When_(Level == 0 && Command != PRINTER_CONTROL_SET_STATUS, _In_opt_)
-  _When_(Level == 1, _In_reads_bytes_(sizeof(PRINTER_INFO_1)))
-  _When_(Level == 2, _In_reads_bytes_(sizeof(PRINTER_INFO_2)))
-  _When_(Level == 3, _In_reads_bytes_(sizeof(PRINTER_INFO_3)))
-  _When_(Level == 4, _In_reads_bytes_(sizeof(PRINTER_INFO_4)))
-  _When_(Level == 5, _In_reads_bytes_(sizeof(PRINTER_INFO_5)))
-  _When_(Level == 6, _In_reads_bytes_(sizeof(PRINTER_INFO_6)))
-  _When_(Level == 7, _In_reads_bytes_(sizeof(PRINTER_INFO_7)))
-  _When_(Level == 8, _In_reads_bytes_(sizeof(PRINTER_INFO_8)))
-  _When_(Level == 9, _In_reads_bytes_(sizeof(PRINTER_INFO_9)))
-    LPBYTE pPrinter,
-  _In_ DWORD Command);
-
-BOOL
-WINAPI
-SetPrinterW(
-  _In_ HANDLE hPrinter,
-  _In_range_(0, 9) DWORD Level,
-  _When_(Level == 0 && Command == PRINTER_CONTROL_SET_STATUS, _In_reads_bytes_(sizeof(DWORD)))
-  _When_(Level == 0 && Command != PRINTER_CONTROL_SET_STATUS, _In_opt_)
-  _When_(Level == 1, _In_reads_bytes_(sizeof(PRINTER_INFO_1)))
-  _When_(Level == 2, _In_reads_bytes_(sizeof(PRINTER_INFO_2)))
-  _When_(Level == 3, _In_reads_bytes_(sizeof(PRINTER_INFO_3)))
-  _When_(Level == 4, _In_reads_bytes_(sizeof(PRINTER_INFO_4)))
-  _When_(Level == 5, _In_reads_bytes_(sizeof(PRINTER_INFO_5)))
-  _When_(Level == 6, _In_reads_bytes_(sizeof(PRINTER_INFO_6)))
-  _When_(Level == 7, _In_reads_bytes_(sizeof(PRINTER_INFO_7)))
-  _When_(Level == 8, _In_reads_bytes_(sizeof(PRINTER_INFO_8)))
-  _When_(Level == 9, _In_reads_bytes_(sizeof(PRINTER_INFO_9)))
-    LPBYTE pPrinter,
-  _In_ DWORD Command);
-
-BOOL
-WINAPI
-SetPrinterDataA(
-  _In_ HANDLE hPrinter,
-  _In_ LPSTR pValueName,
-  _In_ DWORD Type,
-  _In_reads_bytes_(cbData) LPBYTE pData,
-  _In_ DWORD cbData);
-
-BOOL
-WINAPI
-SetPrinterDataW(
-  _In_ HANDLE hPrinter,
-  _In_ LPWSTR pValueName,
-  _In_ DWORD Type,
-  _In_reads_bytes_(cbData) LPBYTE pData,
-  _In_ DWORD cbData);
-
-#ifdef _WINE
-LPSTR WINAPI StartDocDlgA(HANDLE hPrinter, DOCINFOA *doc);
-LPWSTR WINAPI StartDocDlgW(HANDLE hPrinter, DOCINFOW *doc);
-#define StartDocDlg WINELIB_NAME_AW(StartDocDlg)
-#endif
+  LANGID  wLangId;
+} FORM_INFO_2W, *PFORM_INFO_2W;
+
+__MINGW_TYPEDEF_AW(DRIVER_INFO_8)
+__MINGW_TYPEDEF_AW(PDRIVER_INFO_8)
+__MINGW_TYPEDEF_AW(LPDRIVER_INFO_8)
+__MINGW_TYPEDEF_AW(FORM_INFO_2)
+__MINGW_TYPEDEF_AW(PFORM_INFO_2)
+
+typedef struct _PRINTPROCESSOR_CAPS_2 {
+  DWORD dwLevel;
+  DWORD dwNupOptions;
+  DWORD dwPageOrderFlags;
+  DWORD dwNumberOfCopies;
+  DWORD dwNupDirectionCaps;
+  DWORD dwNupBorderCaps;
+  DWORD dwBookletHandlingCaps;
+  DWORD dwDuplexHandlingCaps;
+  DWORD dwScalingCaps;
+} PRINTPROCESSOR_CAPS_2, *PPRINTPROCESSOR_CAPS_2;
+
+HRESULT ReportJobProcessingProgress(
+  HANDLE printerHandle,
+  ULONG jobId,
+  EPrintXPSJobOperation jobOperation,
+  EPrintXPSJobProgress jobProgress
+);
+
+typedef struct _CORE_PRINTER_DRIVERA {
+  GUID      CoreDriverGUID;
+  FILETIME  ftDriverDate;
+  DWORDLONG dwlDriverVersion;
+  CHAR      szPackageID[MAX_PATH];
+} CORE_PRINTER_DRIVERA, *PCORE_PRINTER_DRIVERA;
 
-DWORD
-WINAPI
-StartDocPrinterA(
-  _In_ HANDLE hPrinter,
-  _In_range_(1, 3) DWORD Level,
-  _When_(Level == 1, _In_reads_bytes_(sizeof(DOC_INFO_1))) LPBYTE pDocInfo);
-
-DWORD
-WINAPI
-StartDocPrinterW(
-  _In_ HANDLE hPrinter,
-  _In_range_(1, 3) DWORD Level,
-  _When_(Level == 1, _In_reads_bytes_(sizeof(DOC_INFO_1))) LPBYTE pDocInfo);
-
-BOOL WINAPI StartPagePrinter(_In_ HANDLE);
-DWORD WINAPI WaitForPrinterChange(_In_ HANDLE, _In_ DWORD);
-
-BOOL
-WINAPI
-WritePrinter(
-  _In_ HANDLE hPrinter,
-  _In_reads_bytes_(cbBuf) LPVOID pBuf,
-  _In_ DWORD cbBuf,
-  _Out_ LPDWORD pcWritten);
-
-BOOL
-WINAPI
-XcvDataW(
-  _In_ HANDLE hXcv,
-  _In_ PCWSTR pszDataName,
-  _In_reads_bytes_opt_(cbInputData) PBYTE pInputData,
-  DWORD cbInputData,
-  _Out_writes_bytes_opt_(cbOutputData) PBYTE pOutputData,
-  DWORD cbOutputData,
-  _Out_ PDWORD pcbOutputNeeded,
-  _Out_opt_ PDWORD pdwStatus);
-
-#ifdef UNICODE
-
-#define AddForm AddFormW
-#define AddJob AddJobW
-#define AddMonitor AddMonitorW
-#define AddPort AddPortW
-#define AddPrinter AddPrinterW
-#define AddPrinterConnection AddPrinterConnectionW
-#define AddPrinterDriver AddPrinterDriverW
-#define AddPrintProcessor AddPrintProcessorW
-#define AddPrintProvidor AddPrintProvidorW
-#define AdvancedDocumentProperties AdvancedDocumentPropertiesW
-#define ConfigurePort ConfigurePortW
-#define DeleteForm DeleteFormW
-#define DeleteMonitor DeleteMonitorW
-#define DeletePort DeletePortW
-#define DeletePrinterConnection DeletePrinterConnectionW
-#define DeletePrinterData DeletePrinterDataW
-#define DeletePrinterDriver DeletePrinterDriverW
-#define DeletePrintProcessor DeletePrinterProcessorW
-#define DeletePrintProvidor DeletePrinterProvidorW
-#define DocumentProperties DocumentPropertiesW
-#define EnumForms EnumFormsW
-#define EnumJobs EnumJobsW
-#define EnumMonitors EnumMonitorsW
-#define EnumPorts EnumPortsW
-#define EnumPrinterData EnumPrinterDataW
-#define EnumPrinterDrivers EnumPrinterDriversW
-#define EnumPrinters EnumPrintersW
-#define EnumPrintProcessorDatatypes EnumPrintProcessorDatatypesW
-#define EnumPrintProcessors EnumPrintProcessorsW
-#define GetDefaultPrinter GetDefaultPrinterW
-#define GetForm GetFormW
-#define GetJob GetJobW
-#define GetPrinter GetPrinterW
-#define GetPrinterData GetPrinterDataW
-#define GetPrinterDriver GetPrinterDriverW
-#define GetPrinterDriverDirectory GetPrinterDriverDirectoryW
-#define GetPrintProcessorDirectory GetPrintProcessorDirectoryW
-#define IsValidDevmode IsValidDevmodeW
-#define OpenPrinter OpenPrinterW
-#define PrinterMessageBox PrinterMessageBoxW
-#define ResetPrinter ResetPrinterW
-#define SetForm SetFormW
-#define SetJob SetJobW
-#define SetPrinter SetPrinterW
-#define SetPrinterData SetPrinterDataW
-#define StartDocPrinter StartDocPrinterW
-
-#else /* UNICODE */
-
-#define AddForm AddFormA
-#define AddJob AddJobA
-#define AddMonitor AddMonitorA
-#define AddPort AddPortA
-#define AddPrinter AddPrinterA
-#define AddPrinterConnection AddPrinterConnectionA
-#define AddPrinterDriver AddPrinterDriverA
-#define AddPrintProcessor AddPrintProcessorA
-#define AddPrintProvidor AddPrintProvidorA
-#define AdvancedDocumentProperties AdvancedDocumentPropertiesA
-#define ConfigurePort ConfigurePortA
-#define DeleteForm DeleteFormA
-#define DeleteMonitor DeleteMonitorA
-#define DeletePort DeletePortA
-#define DeletePrinterConnection DeletePrinterConnectionA
-#define DeletePrinterData DeletePrinterDataA
-#define DeletePrinterDriver DeletePrinterDriverA
-#define DeletePrintProcessor DeletePrinterProcessorA
-#define DeletePrintProvidor DeletePrinterProvidorA
-#define DocumentProperties DocumentPropertiesA
-#define EnumForms EnumFormsA
-#define EnumJobs EnumJobsA
-#define EnumMonitors EnumMonitorsA
-#define EnumPorts EnumPortsA
-#define EnumPrinterData EnumPrinterDataA
-#define EnumPrinterDrivers EnumPrinterDriversA
-#define EnumPrinters EnumPrintersA
-#define EnumPrintProcessorDatatypes EnumPrintProcessorDatatypesA
-#define EnumPrintProcessors EnumPrintProcessorsA
-#define GetDefaultPrinter GetDefaultPrinterA
-#define GetForm GetFormA
-#define GetJob GetJobA
-#define GetPrinter GetPrinterA
-#define GetPrinterData GetPrinterDataA
-#define GetPrinterDriver GetPrinterDriverA
-#define GetPrinterDriverDirectory GetPrinterDriverDirectoryA
-#define GetPrintProcessorDirectory GetPrintProcessorDirectoryA
-#define IsValidDevmode IsValidDevmodeA
-#define OpenPrinter OpenPrinterA
-#define PrinterMessageBox PrinterMessageBoxA
-#define ResetPrinter ResetPrinterA
-#define SetForm SetFormA
-#define SetJob SetJobA
-#define SetPrinter SetPrinterA
-#define SetPrinterData SetPrinterDataA
-#define StartDocPrinter StartDocPrinterA
-
-#endif /* UNICODE */
-
-#endif /* !RC_INVOKED */
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
+typedef struct _CORE_PRINTER_DRIVERW {
+  GUID      CoreDriverGUID;
+  FILETIME  ftDriverDate;
+  DWORDLONG dwlDriverVersion;
+  WCHAR     szPackageID[MAX_PATH];
+} CORE_PRINTER_DRIVERW, *PCORE_PRINTER_DRIVERW;
+
+typedef struct _PRINTER_OPTIONS {
+  UINT  cbSize;
+  DWORD dwFlags;
+} PRINTER_OPTIONS, *PPRINTER_OPTIONS;
+
+__MINGW_TYPEDEF_AW(CORE_PRINTER_DRIVER)
+__MINGW_TYPEDEF_AW(PCORE_PRINTER_DRIVER)
+#define GetCorePrinterDrivers __MINGW_NAME_AW(GetCorePrinterDrivers)
+#define GetPrinterDriver2 __MINGW_NAME_AW(GetPrinterDriver2)
+#define GetPrinterDriverPackagePath __MINGW_NAME_AW(GetPrinterDriverPackagePath)
+#define GetSpoolFileHandle __MINGW_NAME_AW(GetSpoolFileHandle)
+
+HRESULT WINAPI GetCorePrinterDriversA(
+  LPCSTR pszServer,
+  LPCSTR pszEnvironment,
+  LPCSTR pszzCoreDriverDependencies,
+  DWORD cCorePrinterDrivers,
+  PCORE_PRINTER_DRIVERA pCorePrinterDrivers
+);
+
+HRESULT WINAPI GetCorePrinterDriversW(
+  LPCWSTR pszServer,
+  LPCWSTR pszEnvironment,
+  LPCWSTR pszzCoreDriverDependencies,
+  DWORD cCorePrinterDrivers,
+  PCORE_PRINTER_DRIVERW pCorePrinterDrivers
+);
+
+/*Unsupported*/
+WINBOOL WINAPI GetPrinterDriver2A(
+  HWND hWnd,
+  HANDLE hPrinter,
+  LPSTR pEnvironment,
+  DWORD Level,
+  LPBYTE pDriverInfo,
+  DWORD cbBuf,
+  LPDWORD pcbNeeded
+);
+
+WINBOOL WINAPI GetPrinterDriver2W(
+  HWND hWnd,
+  HANDLE hPrinter,
+  LPWSTR pEnvironment,
+  DWORD Level,
+  LPBYTE pDriverInfo,
+  DWORD cbBuf,
+  LPDWORD pcbNeeded
+);
+
+HRESULT WINAPI GetPrinterDriverPackagePathA(
+  LPCSTR pszServer,
+  LPCSTR pszEnvironment,
+  LPCSTR pszLanguage,
+  LPCSTR pszPackageID,
+  LPSTR  pszDriverPackageCab,
+  DWORD  cchDriverPackageCab,
+  LPDWORD pcchRequiredSize
+);
+
+HRESULT WINAPI GetPrinterDriverPackagePathW(
+  LPCWSTR pszServer,
+  LPCWSTR pszEnvironment,
+  LPCWSTR pszLanguage,
+  LPCWSTR pszPackageID,
+  LPWSTR  pszDriverPackageCab,
+  DWORD   cchDriverPackageCab,
+  LPDWORD pcchRequiredSize
+);
+
+HANDLE WINAPI GetSpoolFileHandleA(
+  HANDLE hPrinter
+);
+
+HANDLE WINAPI GetSpoolFileHandleW(
+  HANDLE hPrinter
+);
+
+HANDLE WINAPI CommitSpoolData(
+  HANDLE hPrinter,
+  HANDLE hSpoolFile,
+  DWORD cbCommit
+);
+
+WINBOOL WINAPI CloseSpoolFileHandle(
+  HANDLE hPrinter,
+  HANDLE hSpoolFile
+);
+
+WINBOOL WINAPI OpenPrinter2A(
+  LPCSTR pPrinterName,
+  LPHANDLE phPrinter,
+  LPPRINTER_DEFAULTS pDefault,
+  PPRINTER_OPTIONS pOptions
+);
+
+WINBOOL WINAPI OpenPrinter2W(
+  LPCWSTR pPrinterName,
+  LPHANDLE phPrinter,
+  LPPRINTER_DEFAULTS pDefault,
+  PPRINTER_OPTIONS pOptions
+);
+
+#define OpenPrinter2 __MINGW_NAME_AW(OpenPrinter2)
+
+HRESULT WINAPI UploadPrinterDriverPackageA(
+  LPCSTR pszServer,
+  LPCSTR pszInfPath,
+  LPCSTR pszEnvironment,
+  DWORD dwFlags,
+  HWND hwnd,
+  LPSTR pszDestInfPath,
+  PULONG pcchDestInfPath
+);
+
+HRESULT WINAPI UploadPrinterDriverPackageW(
+  LPCWSTR pszServer,
+  LPCWSTR pszInfPath,
+  LPCWSTR pszEnvironment,
+  DWORD dwFlags,
+  HWND hwnd,
+  LPWSTR pszDestInfPath,
+  PULONG pcchDestInfPath
+);
+
+#define UploadPrinterDriverPackage __MINGW_NAME_AW(UploadPrinterDriverPackage)
+
+#endif /*(_WIN32_WINNT >= 0x0600)*/
 
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _WINSPOOL_ */
+#endif
diff --git a/reactos/sdk/include/reactos/idl/winspool.idl b/reactos/sdk/include/reactos/idl/winspool.idl
new file mode 100644 (file)
index 0000000..736b532
--- /dev/null
@@ -0,0 +1,1594 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     This file is in the public domain
+ * PURPOSE:     WIDL-compatible interface definition for the Spooler API
+ * AUTHORS:     Colin Finck <colin@reactos.org>
+ */
+
+#include <ms-dtyp.idl>
+
+/*
+ * Handle definitions
+ */
+typedef [context_handle] void* WINSPOOL_GDI_HANDLE;
+typedef [context_handle] void* WINSPOOL_PRINTER_HANDLE;
+typedef [handle, string] WCHAR* WINSPOOL_HANDLE;
+
+
+/*
+ * Type definitions and enumerations
+ */
+typedef WORD WINSPOOL_LANGID;
+
+typedef enum
+{
+    WINSPOOL_BIDI_NULL = 0,
+    WINSPOOL_BIDI_INT = 1,
+    WINSPOOL_BIDI_FLOAT = 2,
+    WINSPOOL_BIDI_BOOL = 3,
+    WINSPOOL_BIDI_STRING = 4,
+    WINSPOOL_BIDI_TEXT = 5,
+    WINSPOOL_BIDI_ENUM = 6,
+    WINSPOOL_BIDI_BLOB = 7
+}
+WINSPOOL_BIDI_TYPE;
+
+typedef enum
+{
+    WINSPOOL_TABLE_DWORD = 1,
+    WINSPOOL_TABLE_STRING = 2,
+    WINSPOOL_TABLE_DEVMODE = 3,
+    WINSPOOL_TABLE_TIME = 4,
+    WINSPOOL_TABLE_SECURITYDESCRIPTOR = 5
+}
+WINSPOOL_TABLE_TYPE;
+
+
+/*
+ * Various definitions
+ */
+typedef struct _WINSPOOL_RECTL
+{
+    LONG left;
+    LONG top;
+    LONG right;
+    LONG bottom;
+}
+WINSPOOL_RECTL;
+
+typedef struct _WINSPOOL_SIZEL
+{
+    LONG cx;
+    LONG cy;
+}
+WINSPOOL_SIZEL;
+
+
+/*
+ * INFO structures
+ */
+typedef struct _WINSPOOL_DOC_INFO_1 {
+    [string] WCHAR* pDocName;
+    [string] WCHAR* pOutputFile;
+    [string] WCHAR* pDatatype;
+}
+WINSPOOL_DOC_INFO_1;
+
+typedef struct _WINSPOOL_DRIVER_INFO_1
+{
+    [string] WCHAR* pName;
+}
+WINSPOOL_DRIVER_INFO_1;
+
+typedef struct _WINSPOOL_DRIVER_INFO_2
+{
+    DWORD cVersion;
+    [string] WCHAR* pName;
+    [string] WCHAR* pEnvironment;
+    [string] WCHAR* pDriverPath;
+    [string] WCHAR* pDataFile;
+    [string] WCHAR* pConfigFile;
+}
+WINSPOOL_DRIVER_INFO_2;
+
+typedef struct _WINSPOOL_DRIVER_INFO_3
+{
+    DWORD cVersion;
+    [string] WCHAR* pName;
+    [string] WCHAR* pEnvironment;
+    [string] WCHAR* pDriverPath;
+    [string] WCHAR* pDataFile;
+    [string] WCHAR* pConfigFile;
+    [string] WCHAR* pHelpFile;
+    [string] WCHAR* pMonitorName;
+    [string] WCHAR* pDefaultDataType;
+    DWORD cchDependentFiles;
+    [size_is(cchDependentFiles), unique] WCHAR* pDependentFiles;
+}
+WINSPOOL_DRIVER_INFO_3;
+
+typedef struct _WINSPOOL_DRIVER_INFO_4
+{
+    DWORD cVersion;
+    [string] WCHAR* pName;
+    [string] WCHAR* pEnvironment;
+    [string] WCHAR* pDriverPath;
+    [string] WCHAR* pDataFile;
+    [string] WCHAR* pConfigFile;
+    [string] WCHAR* pHelpFile;
+    [string] WCHAR* pMonitorName;
+    [string] WCHAR* pDefaultDataType;
+    DWORD cchDependentFiles;
+    [size_is(cchDependentFiles), unique] WCHAR* pDependentFiles;
+    DWORD cchPreviousNames;
+    [size_is(cchPreviousNames), unique] WCHAR* pszzPreviousNames;
+}
+WINSPOOL_DRIVER_INFO_4;
+
+typedef struct _WINSPOOL_DRIVER_INFO_6
+{
+    DWORD cVersion;
+    [string] WCHAR* pName;
+    [string] WCHAR* pEnvironment;
+    [string] WCHAR* pDriverPath;
+    [string] WCHAR* pDataFile;
+    [string] WCHAR* pConfigFile;
+    [string] WCHAR* pHelpFile;
+    [string] WCHAR* pMonitorName;
+    [string] WCHAR* pDefaultDataType;
+    DWORD cchDependentFiles;
+    [size_is(cchDependentFiles), unique] WCHAR* pDependentFiles;
+    DWORD cchPreviousNames;
+    [size_is(cchPreviousNames), unique] WCHAR* pszzPreviousNames;
+    FILETIME ftDriverDate;
+    DWORDLONG dwlDriverVersion;
+    [string] WCHAR* pMfgName;
+    [string] WCHAR* pOEMUrl;
+    [string] WCHAR* pHardwareID;
+    [string] WCHAR* pProvider;
+}
+WINSPOOL_DRIVER_INFO_6;
+
+typedef struct _WINSPOOL_DRIVER_INFO_8
+{
+    DWORD cVersion;
+    [string] WCHAR* pName;
+    [string] WCHAR* pEnvironment;
+    [string] WCHAR* pDriverPath;
+    [string] WCHAR* pDataFile;
+    [string] WCHAR* pConfigFile;
+    [string] WCHAR* pHelpFile;
+    [string] WCHAR* pMonitorName;
+    [string] WCHAR* pDefaultDataType;
+    DWORD cchDependentFiles;
+    [size_is(cchDependentFiles), unique] WCHAR* pDependentFiles;
+    DWORD cchPreviousNames;
+    [size_is(cchPreviousNames), unique] WCHAR* pszzPreviousNames;
+    FILETIME ftDriverDate;
+    DWORDLONG dwlDriverVersion;
+    [string] WCHAR* pMfgName;
+    [string] WCHAR* pOEMUrl;
+    [string] WCHAR* pHardwareID;
+    [string] WCHAR* pProvider;
+    [string] WCHAR* pPrintProcessor;
+    [string] WCHAR* pVendorSetup;
+    DWORD cchColorProfiles;
+    [size_is(cchColorProfiles), unique] WCHAR* pszzColorProfiles;
+    [string] WCHAR* pInfPath;
+    DWORD dwPrinterDriverAttributes;
+    DWORD cchCoreDependencies;
+    [size_is(cchCoreDependencies), unique] WCHAR* pszzCoreDriverDependencies;
+    FILETIME ftMinInboxDriverVerDate;
+    DWORDLONG dwlMinInboxDriverVerVersion;
+}
+WINSPOOL_DRIVER_INFO_8;
+
+typedef struct _WINSPOOL_FORM_INFO_1
+{
+    DWORD Flags;
+    [string] WCHAR* pName;
+    WINSPOOL_SIZEL Size;
+    WINSPOOL_RECTL ImageableArea;
+}
+WINSPOOL_FORM_INFO_1;
+
+typedef struct _WINSPOOL_FORM_INFO_2
+{
+    DWORD Flags;
+    [string, unique] const WCHAR* pName;
+    WINSPOOL_SIZEL Size;
+    WINSPOOL_RECTL ImageableArea;
+    [string, unique] const WCHAR* pKeyword;
+    DWORD StringType;
+    [string, unique] const WCHAR* pMuiDll;
+    DWORD dwResourceId;
+    [string, unique] const WCHAR* pDisplayName;
+    WINSPOOL_LANGID wLangID;
+}
+WINSPOOL_FORM_INFO_2;
+
+typedef struct _WINSPOOL_JOB_INFO_1
+{
+    DWORD JobId;
+    [string] WCHAR* pPrinterName;
+    [string] WCHAR* pMachineName;
+    [string] WCHAR* pUserName;
+    [string] WCHAR* pDocument;
+    [string] WCHAR* pDatatype;
+    [string] WCHAR* pStatus;
+    DWORD Status;
+    DWORD Priority;
+    DWORD Position;
+    DWORD TotalPages;
+    DWORD PagesPrinted;
+    SYSTEMTIME Submitted;
+}
+WINSPOOL_JOB_INFO_1;
+
+typedef struct _WINSPOOL_JOB_INFO_2
+{
+    DWORD JobId;
+    [string] WCHAR* pPrinterName;
+    [string] WCHAR* pMachineName;
+    [string] WCHAR* pUserName;
+    [string] WCHAR* pDocument;
+    [string] WCHAR* pNotifyName;
+    [string] WCHAR* pDatatype;
+    [string] WCHAR* pPrintProcessor;
+    [string] WCHAR* pParameters;
+    [string] WCHAR* pDriverName;
+    ULONG_PTR pDevMode;
+    [string] WCHAR* pStatus;
+    ULONG_PTR pSecurityDescriptor;
+    DWORD Status;
+    DWORD Priority;
+    DWORD Position;
+    DWORD StartTime;
+    DWORD UntilTime;
+    DWORD TotalPages;
+    DWORD Size;
+    SYSTEMTIME Submitted;
+    DWORD Time;
+    DWORD PagesPrinted;
+}
+WINSPOOL_JOB_INFO_2;
+
+typedef struct _WINSPOOL_JOB_INFO_3
+{
+    DWORD JobId;
+    DWORD NextJobId;
+    DWORD Reserved;
+}
+WINSPOOL_JOB_INFO_3;
+
+typedef struct _WINSPOOL_JOB_INFO_4
+{
+    DWORD JobId;
+    [string] WCHAR* pPrinterName;
+    [string] WCHAR* pMachineName;
+    [string] WCHAR* pUserName;
+    [string] WCHAR* pDocument;
+    [string] WCHAR* pNotifyName;
+    [string] WCHAR* pDatatype;
+    [string] WCHAR* pPrintProcessor;
+    [string] WCHAR* pParameters;
+    [string] WCHAR* pDriverName;
+    ULONG_PTR pDevMode;
+    [string] WCHAR* pStatus;
+    ULONG_PTR pSecurityDescriptor;
+    DWORD Status;
+    DWORD Priority;
+    DWORD Position;
+    DWORD StartTime;
+    DWORD UntilTime;
+    DWORD TotalPages;
+    DWORD Size;
+    SYSTEMTIME Submitted;
+    DWORD Time;
+    DWORD PagesPrinted;
+    LONG SizeHigh;
+}
+WINSPOOL_JOB_INFO_4;
+
+typedef struct _WINSPOOL_MONITOR_INFO_1
+{
+    [string] WCHAR* pName;
+}
+WINSPOOL_MONITOR_INFO_1;
+
+typedef struct _WINSPOOL_MONITOR_INFO_2
+{
+    [string] WCHAR* pName;
+    [string] WCHAR* pEnvironment;
+    [string] WCHAR* pDLLName;
+}
+WINSPOOL_MONITOR_INFO_2;
+
+typedef struct _WINSPOOL_PORT_INFO_1
+{
+    [string] WCHAR* pPortName;
+}
+WINSPOOL_PORT_INFO_1;
+
+typedef struct _WINSPOOL_PORT_INFO_2
+{
+    [string] WCHAR* pPortName;
+    [string] WCHAR* pMonitorName;
+    [string] WCHAR* pDescription;
+    DWORD fPortType;
+    DWORD Reserved;
+}
+WINSPOOL_PORT_INFO_2;
+
+typedef struct _WINSPOOL_PORT_INFO_3
+{
+    DWORD dwStatus;
+    [string] WCHAR* pszStatus;
+    DWORD dwSeverity;
+}
+WINSPOOL_PORT_INFO_3;
+
+typedef struct _WINSPOOL_PORT_INFO_FF
+{
+    [string] WCHAR* pPortName;
+    DWORD cbMonitorData;
+    BYTE* pMonitorData;
+}
+WINSPOOL_PORT_INFO_FF;
+
+typedef struct _WINSPOOL_PRINTER_INFO_STRESS {
+    [string] WCHAR* pPrinterName;
+    [string] WCHAR* pServerName;
+    DWORD cJobs;
+    DWORD cTotalJobs;
+    DWORD cTotalBytes;
+    SYSTEMTIME stUpTime;
+    DWORD MaxcRef;
+    DWORD cTotalPagesPrinted;
+    DWORD dwGetVersion;
+    DWORD fFreeBuild;
+    DWORD cSpooling;
+    DWORD cMaxSpooling;
+    DWORD cRef;
+    DWORD cErrorOutOfPaper;
+    DWORD cErrorNotReady;
+    DWORD cJobError;
+    DWORD dwNumberOfProcessors;
+    DWORD dwProcessorType;
+    DWORD dwHighPartTotalBytes;
+    DWORD cChangeID;
+    DWORD dwLastError;
+    DWORD Status;
+    DWORD cEnumerateNetworkPrinters;
+    DWORD cAddNetPrinters;
+    USHORT wProcessorArchitecture;
+    USHORT wProcessorLevel;
+    DWORD cRefIC;
+    DWORD dwReserved2;
+    DWORD dwReserved3;
+}
+WINSPOOL_PRINTER_INFO_STRESS;
+
+typedef struct _WINSPOOL_PRINTER_INFO_1
+{
+    DWORD Flags;
+    [string] WCHAR* pDescription;
+    [string] WCHAR* pName;
+    [string] WCHAR* pComment;
+}
+WINSPOOL_PRINTER_INFO_1;
+
+typedef struct _WINSPOOL_PRINTER_INFO_2
+{
+    [string] WCHAR* pServerName;
+    [string] WCHAR* pPrinterName;
+    [string] WCHAR* pShareName;
+    [string] WCHAR* pPortName;
+    [string] WCHAR* pDriverName;
+    [string] WCHAR* pComment;
+    [string] WCHAR* pLocation;
+    ULONG_PTR pDevMode;
+    [string] WCHAR* pSepFile;
+    [string] WCHAR* pPrintProcessor;
+    [string] WCHAR* pDatatype;
+    [string] WCHAR* pParameters;
+    ULONG_PTR pSecurityDescriptor;
+    DWORD Attributes;
+    DWORD Priority;
+    DWORD DefaultPriority;
+    DWORD StartTime;
+    DWORD UntilTime;
+    DWORD Status;
+    DWORD cJobs;
+    DWORD AveragePPM;
+}
+WINSPOOL_PRINTER_INFO_2;
+
+typedef struct _WINSPOOL_PRINTER_INFO_3
+{
+    ULONG_PTR pSecurityDescriptor;
+}
+WINSPOOL_PRINTER_INFO_3;
+
+typedef struct _WINSPOOL_PRINTER_INFO_4
+{
+    [string] WCHAR* pPrinterName;
+    [string] WCHAR* pServerName;
+    DWORD Attributes;
+}
+WINSPOOL_PRINTER_INFO_4;
+
+typedef struct _WINSPOOL_PRINTER_INFO_5
+{
+    [string] WCHAR* pPrinterName;
+    [string] WCHAR* pPortName;
+    DWORD Attributes;
+    DWORD DeviceNotSelectedTimeout;
+    DWORD TransmissionRetryTimeout;
+}
+WINSPOOL_PRINTER_INFO_5;
+
+typedef struct _WINSPOOL_PRINTER_INFO_6
+{
+    DWORD dwStatus;
+}
+WINSPOOL_PRINTER_INFO_6;
+
+typedef struct _WINSPOOL_PRINTER_INFO_7
+{
+    [string] WCHAR* pszObjectGUID;
+    DWORD dwAction;
+}
+WINSPOOL_PRINTER_INFO_7;
+
+typedef struct _WINSPOOL_PRINTER_INFO_8
+{
+    ULONG_PTR pDevMode;
+}
+WINSPOOL_PRINTER_INFO_8;
+
+typedef struct _WINSPOOL_PRINTER_INFO_9
+{
+    ULONG_PTR pDevMode;
+}
+WINSPOOL_PRINTER_INFO_9;
+
+typedef struct _WINSPOOL_PROVIDOR_INFO_1
+{
+    WCHAR* pName;
+    WCHAR* pEnvironment;
+    WCHAR* pDLLName;
+}
+WINSPOOL_PROVIDOR_INFO_1;
+
+typedef struct _WINSPOOL_PROVIDOR_INFO_2
+{
+    WCHAR* pOrder;
+}
+WINSPOOL_PROVIDOR_INFO_2;
+
+typedef struct _WINSPOOL_SPLCLIENT_INFO_1
+{
+    DWORD dwSize;
+    [string] WCHAR* pMachineName;
+    [string] WCHAR* pUserName;
+    DWORD dwBuildNum;
+    DWORD dwMajorVersion;
+    DWORD dwMinorVersion;
+    USHORT wProcessorArchitecture;
+}
+WINSPOOL_SPLCLIENT_INFO_1;
+
+typedef struct _WINSPOOL_SPLCLIENT_INFO_2
+{
+    LONG_PTR notUsed;
+}
+WINSPOOL_SPLCLIENT_INFO_2;
+
+typedef struct _WINSPOOL_SPLCLIENT_INFO_3
+{
+    UINT cbSize;
+    DWORD dwFlags;
+    DWORD dwSize;
+    [string] WCHAR* pMachineName;
+    [string] WCHAR* pUserName;
+    DWORD dwBuildNum;
+    DWORD dwMajorVersion;
+    DWORD dwMinorVersion;
+    USHORT wProcessorArchitecture;
+    UINT64 hSplPrinter;
+}
+WINSPOOL_SPLCLIENT_INFO_3;
+
+
+/*
+ * CONTAINER structures
+ */
+typedef struct _WINSPOOL_BINARY_CONTAINER
+{
+    DWORD cbBuf;
+    [size_is(cbBuf), unique] BYTE* pszString;
+}
+WINSPOOL_BINARY_CONTAINER;
+
+typedef struct _WINSPOOL_BIDI_DATA
+{
+    DWORD dwBidiType;
+
+    [switch_is(dwBidiType)] union
+    {
+        [case(WINSPOOL_BIDI_NULL, WINSPOOL_BIDI_BOOL)]
+            INT bData;
+        [case(WINSPOOL_BIDI_INT)]
+            LONG iData;
+        [case(WINSPOOL_BIDI_STRING, WINSPOOL_BIDI_TEXT, WINSPOOL_BIDI_ENUM)]
+            [string, unique] WCHAR* sData;
+        [case(WINSPOOL_BIDI_FLOAT)]
+            FLOAT fData;
+        [case(WINSPOOL_BIDI_BLOB)]
+            WINSPOOL_BINARY_CONTAINER biData;
+    }
+    u;
+}
+WINSPOOL_BIDI_DATA;
+
+typedef struct _WINSPOOL_BIDI_REQUEST_DATA
+{
+    DWORD dwReqNumber;
+    [string, unique] WCHAR* pSchema;
+    WINSPOOL_BIDI_DATA data;
+}
+WINSPOOL_BIDI_REQUEST_DATA;
+
+typedef struct _WINSPOOL_BIDI_RESPONSE_DATA
+{
+    DWORD dwResult;
+    DWORD dwReqNumber;
+    [string, unique] WCHAR* pSchema;
+    WINSPOOL_BIDI_DATA data;
+}
+WINSPOOL_BIDI_RESPONSE_DATA;
+
+typedef struct _WINSPOOL_BIDI_REQUEST_CONTAINER
+{
+    DWORD Version;
+    DWORD Flags;
+    DWORD Count;
+    [size_is(Count), unique] WINSPOOL_BIDI_REQUEST_DATA aData[];
+}
+WINSPOOL_BIDI_REQUEST_CONTAINER;
+
+typedef struct _WINSPOOL_BIDI_RESPONSE_CONTAINER
+{
+    DWORD Version;
+    DWORD Flags;
+    DWORD Count;
+    [size_is(Count), unique] WINSPOOL_BIDI_RESPONSE_DATA aData[];
+}
+WINSPOOL_BIDI_RESPONSE_CONTAINER;
+
+typedef struct _WINSPOOL_DOC_INFO_CONTAINER
+{
+    DWORD Level;
+
+    [switch_is(Level)] union
+    {
+        [case(1)]
+            WINSPOOL_DOC_INFO_1* pDocInfo1;
+    }
+    DocInfo;
+}
+WINSPOOL_DOC_INFO_CONTAINER;
+
+typedef struct _WINSPOOL_DEVMODE_CONTAINER
+{
+    DWORD cbBuf;
+    [size_is(cbBuf), unique] BYTE* pDevMode;
+}
+WINSPOOL_DEVMODE_CONTAINER;
+
+typedef struct _WINSPOOL_DRIVER_CONTAINER
+{
+    DWORD Level;
+
+    [switch_is(Level)] union
+    {
+        [case(1)]
+            WINSPOOL_DRIVER_INFO_1* pNotUsed;
+        [case(2)]
+            WINSPOOL_DRIVER_INFO_2* Level2;
+        [case(3)]
+            WINSPOOL_DRIVER_INFO_3* Level3;
+        [case(4)]
+            WINSPOOL_DRIVER_INFO_4* Level4;
+        [case(6)]
+            WINSPOOL_DRIVER_INFO_6* Level6;
+        [case(8)]
+            WINSPOOL_DRIVER_INFO_8* Level8;
+    }
+    DriverInfo;
+}
+WINSPOOL_DRIVER_CONTAINER;
+
+typedef struct _WINSPOOL_FORM_CONTAINER
+{
+    DWORD Level;
+
+    [switch_is(Level)] union
+    {
+        [case(1)]
+            WINSPOOL_FORM_INFO_1* pFormInfo1;
+        [case(2)]
+            WINSPOOL_FORM_INFO_2* pFormInfo2;
+    }
+    FormInfo;
+}
+WINSPOOL_FORM_CONTAINER;
+
+typedef struct _WINSPOOL_JOB_CONTAINER
+{
+    DWORD Level;
+
+    [switch_is(Level)] union
+    {
+        [case(1)]
+            WINSPOOL_JOB_INFO_1* Level1;
+        [case(2)]
+            WINSPOOL_JOB_INFO_2* Level2;
+        [case(3)]
+            WINSPOOL_JOB_INFO_3* Level3;
+        [case(4)]
+            WINSPOOL_JOB_INFO_4* Level4;
+    }
+    JobInfo;
+}
+WINSPOOL_JOB_CONTAINER;
+
+typedef struct _WINSPOOL_MONITOR_CONTAINER
+{
+    DWORD Level;
+
+    [switch_is(Level)] union
+    {
+        [case(1)]
+            WINSPOOL_MONITOR_INFO_1* pMonitorInfo1;
+        [case(2)]
+            WINSPOOL_MONITOR_INFO_2* pMonitorInfo2;
+    }
+    MonitorInfo;
+}
+WINSPOOL_MONITOR_CONTAINER;
+
+typedef struct _WINSPOOL_PORT_CONTAINER
+{
+    DWORD Level;
+
+    [switch_is(0x00FFFFFF & Level)]
+    union
+    {
+        [case(1)]
+            WINSPOOL_PORT_INFO_1* pPortInfo1;
+        [case(2)]
+            WINSPOOL_PORT_INFO_2* pPortInfo2;
+        [case(3)]
+            WINSPOOL_PORT_INFO_3* pPortInfo3;
+        [case(0x00FFFFFF)]
+            WINSPOOL_PORT_INFO_FF* pPortInfoFF;
+    }
+    PortInfo;
+}
+WINSPOOL_PORT_CONTAINER;
+
+typedef struct _WINSPOOL_PORT_VAR_CONTAINER
+{
+    DWORD cbMonitorData;
+    [size_is(cbMonitorData), unique] BYTE* pMonitorData;
+}
+WINSPOOL_PORT_VAR_CONTAINER;
+
+typedef struct _WINSPOOL_PRINTER_CONTAINER
+{
+    DWORD Level;
+
+    [switch_is(Level)] union
+    {
+        [case(0)]
+            WINSPOOL_PRINTER_INFO_STRESS* pPrinterInfoStress;
+        [case(1)]
+            WINSPOOL_PRINTER_INFO_1* pPrinterInfo1;
+        [case(2)]
+            WINSPOOL_PRINTER_INFO_2* pPrinterInfo2;
+        [case(3)]
+            WINSPOOL_PRINTER_INFO_3* pPrinterInfo3;
+        [case(4)]
+            WINSPOOL_PRINTER_INFO_4* pPrinterInfo4;
+        [case(5)]
+            WINSPOOL_PRINTER_INFO_5* pPrinterInfo5;
+        [case(6)]
+            WINSPOOL_PRINTER_INFO_6* pPrinterInfo6;
+        [case(7)]
+            WINSPOOL_PRINTER_INFO_7* pPrinterInfo7;
+        [case(8)]
+            WINSPOOL_PRINTER_INFO_8* pPrinterInfo8;
+        [case(9)]
+            WINSPOOL_PRINTER_INFO_9* pPrinterInfo9;
+    }
+    PrinterInfo;
+}
+WINSPOOL_PRINTER_CONTAINER;
+
+typedef struct _WINSPOOL_PROVIDOR_CONTAINER
+{
+    DWORD Level;
+
+    [switch_is(Level)] union
+    {
+        [case(1)]
+            WINSPOOL_PROVIDOR_INFO_1* pProvidorInfo1;
+        [case(2)]
+            WINSPOOL_PROVIDOR_INFO_2* pProvidorInfo2;
+    }
+    ProvidorInfo;
+}
+WINSPOOL_PROVIDOR_CONTAINER;
+
+typedef struct _WINSPOOL_SECURITY_CONTAINER
+{
+    DWORD cbBuf;
+    [size_is(cbBuf), unique] BYTE* pSecurity;
+}
+WINSPOOL_SECURITY_CONTAINER;
+
+typedef struct _WINSPOOL_SPLCLIENT_CONTAINER
+{
+    DWORD Level;
+
+    [switch_is(Level)] union
+    {
+        [case(1)]
+            WINSPOOL_SPLCLIENT_INFO_1* pClientInfo1;
+        [case(2)]
+            WINSPOOL_SPLCLIENT_INFO_2* pNotUsed1;
+        [case(3)]
+            WINSPOOL_SPLCLIENT_INFO_3* pNotUsed2;
+    }
+    ClientInfo;
+}
+WINSPOOL_SPLCLIENT_CONTAINER;
+
+typedef struct _WINSPOOL_STRING_CONTAINER
+{
+    DWORD cbBuf;
+    [size_is(cbBuf / 2), unique] WCHAR* pszString;
+}
+WINSPOOL_STRING_CONTAINER;
+
+typedef struct _WINSPOOL_SYSTEMTIME_CONTAINER {
+    DWORD cbBuf;
+    SYSTEMTIME* pSystemTime;
+}
+WINSPOOL_SYSTEMTIME_CONTAINER;
+
+
+/*
+ * Various definitions
+ */
+typedef struct _WINSPOOL_V2_NOTIFY_OPTIONS_TYPE
+{
+    USHORT Type;
+    USHORT Reserved0;
+    DWORD Reserved1;
+    DWORD Reserved2;
+    DWORD Count;
+    [size_is(Count), unique] USHORT* pFields;
+}
+WINSPOOL_V2_NOTIFY_OPTIONS_TYPE;
+
+typedef struct _WINSPOOL_V2_NOTIFY_OPTIONS
+{
+    DWORD Version;
+    DWORD Reserved;
+    DWORD Count;
+    [size_is(Count), unique] WINSPOOL_V2_NOTIFY_OPTIONS_TYPE* pTypes;
+}
+WINSPOOL_V2_NOTIFY_OPTIONS;
+
+typedef [switch_type(DWORD)] union _WINSPOOL_V2_NOTIFY_INFO_DATA_DATA
+{
+    [case(WINSPOOL_TABLE_STRING)]
+        WINSPOOL_STRING_CONTAINER String;
+    [case(WINSPOOL_TABLE_DWORD)]
+        DWORD dwData[2];
+    [case(WINSPOOL_TABLE_TIME)]
+        WINSPOOL_SYSTEMTIME_CONTAINER SystemTime;
+    [case(WINSPOOL_TABLE_DEVMODE)]
+        WINSPOOL_DEVMODE_CONTAINER DevMode;
+    [case(WINSPOOL_TABLE_SECURITYDESCRIPTOR)]
+        WINSPOOL_SECURITY_CONTAINER SecurityDescriptor;
+}
+WINSPOOL_V2_NOTIFY_INFO_DATA_DATA;
+
+typedef struct _WINSPOOL_V2_NOTIFY_INFO_DATA
+{
+    USHORT Type;
+    USHORT Field;
+    DWORD Reserved;
+    DWORD Id;
+    [switch_is(Reserved & 0xffff)] WINSPOOL_V2_NOTIFY_INFO_DATA_DATA Data;
+}
+WINSPOOL_V2_NOTIFY_INFO_DATA;
+
+typedef struct _WINSPOOL_V2_NOTIFY_INFO
+{
+    DWORD Version;
+    DWORD Flags;
+    DWORD Count;
+    [size_is(Count), unique] WINSPOOL_V2_NOTIFY_INFO_DATA aData[];
+}
+WINSPOOL_V2_NOTIFY_INFO;
+
+typedef [switch_type(DWORD)] union _WINSPOOL_V2_UREPLY_PRINTER
+{
+    [case(0)]
+        WINSPOOL_V2_NOTIFY_INFO* pInfo;
+}
+WINSPOOL_V2_UREPLY_PRINTER;
+
+
+/*
+ * The actual interface
+ */
+[
+    uuid(12345678-1234-ABCD-EF00-0123456789AB),
+    version(1.0),
+    endpoint("ncalrpc:[spoolss]", "ncacn_np:[\\pipe\\spoolss]"),
+    pointer_default(unique)
+]
+
+interface winspool {
+    /* Function 0x00 */
+    DWORD _RpcEnumPrinters(
+        [in] DWORD Flags,
+        [in, unique] WINSPOOL_HANDLE Name,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pPrinterEnum,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded,
+        [out] DWORD* pcReturned
+    );
+
+    /* Function 0x01 */
+    DWORD _RpcOpenPrinter(
+        [in, unique] WINSPOOL_HANDLE pPrinterName,
+        [out] WINSPOOL_PRINTER_HANDLE* phPrinter,
+        [in, string, unique] WCHAR* pDatatype,
+        [in] WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer,
+        [in] DWORD AccessRequired
+    );
+    
+    /* Function 0x02 */
+    DWORD _RpcSetJob(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD JobId,
+        [in, unique] WINSPOOL_JOB_CONTAINER* pJobContainer,
+        [in] DWORD Command
+    );
+
+    /* Function 0x03 */
+    DWORD _RpcGetJob(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD JobId,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pJob,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded
+    );
+
+    /* Function 0x04 */
+    DWORD _RpcEnumJobs(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD FirstJob,
+        [in] DWORD NoJobs,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pJob,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded,
+        [out] DWORD* pcReturned
+    );
+
+    /* Function 0x05 */
+    DWORD _RpcAddPrinter(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] WINSPOOL_PRINTER_CONTAINER* pPrinterContainer,
+        [in] WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer,
+        [in] WINSPOOL_SECURITY_CONTAINER* pSecurityContainer,
+        [out] WINSPOOL_PRINTER_HANDLE* pHandle
+    );
+
+    /* Function 0x06 */
+    DWORD _RpcDeletePrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter
+    );
+
+    /* Function 0x07 */
+    DWORD _RpcSetPrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] WINSPOOL_PRINTER_CONTAINER* pPrinterContainer,
+        [in] WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer,
+        [in] WINSPOOL_SECURITY_CONTAINER* pSecurityContainer,
+        [in] DWORD Command
+    );
+
+    /* Function 0x08 */
+    DWORD _RpcGetPrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pPrinter,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded
+    );
+
+    /* Function 0x09 */
+    DWORD _RpcAddPrinterDriver(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] WINSPOOL_DRIVER_CONTAINER* pDriverContainer
+    );
+
+    /* Function 0x0A */
+    DWORD _RpcEnumPrinterDrivers(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string, unique] WCHAR* pEnvironment,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pDrivers,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded,
+        [out] DWORD* pcReturned
+    );
+
+    /* Function 0x0B */
+    DWORD _RpcGetPrinterDriver(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string, unique] WCHAR* pEnvironment,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pDriver,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded
+    );
+
+    /* Function 0x0C */
+    DWORD _RpcGetPrinterDriverDirectory(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string, unique] WCHAR*  pEnvironment,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pDriverDirectory,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded
+    );
+
+    /* Function 0x0D */
+    DWORD _RpcDeletePrinterDriver(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string] WCHAR* pEnvironment,
+        [in, string] WCHAR* pDriverName
+    );
+
+    /* Function 0x0E */
+    DWORD _RpcAddPrintProcessor(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string] WCHAR* pEnvironment,
+        [in, string] WCHAR* pPathName,
+        [in, string] WCHAR* pPrintProcessorName
+    );
+
+    /* Function 0x0F */
+    DWORD _RpcEnumPrintProcessors(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string, unique] WCHAR* pEnvironment,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pPrintProcessorInfo,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded,
+        [out] DWORD* pcReturned
+    );
+
+    /* Function 0x10 */
+    DWORD _RpcGetPrintProcessorDirectory(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string, unique] WCHAR* pEnvironment,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pPrintProcessorDirectory,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded
+    );
+
+    /* Function 0x11 */
+    DWORD _RpcStartDocPrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] WINSPOOL_DOC_INFO_CONTAINER* pDocInfoContainer,
+        [out] DWORD* pJobId
+    );
+
+    /* Function 0x12 */
+    DWORD _RpcStartPagePrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter
+    );
+
+    /* Function 0x13 */
+    DWORD _RpcWritePrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, size_is(cbBuf)] BYTE* pBuf,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcWritten
+    );
+
+    /* Function 0x14 */
+    DWORD _RpcEndPagePrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter
+    );
+
+    /* Function 0x15 */
+    DWORD _RpcAbortPrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter
+    );
+
+    /* Function 0x16 */
+    DWORD _RpcReadPrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [out, size_is(cbBuf)] BYTE* pBuf,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcNoBytesRead
+    );
+
+    /* Function 0x17 */
+    DWORD _RpcEndDocPrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter
+    );
+
+    /* Function 0x18 */
+    DWORD _RpcAddJob(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pAddJob,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded
+    );
+
+    /* Function 0x19 */
+    DWORD _RpcScheduleJob(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD JobId
+    );
+
+    /* Function 0x1A */
+    DWORD _RpcGetPrinterData(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] WCHAR* pValueName,
+        [out] DWORD* pType,
+        [out, size_is(nSize)] BYTE* pData,
+        [in] DWORD nSize,
+        [out] DWORD* pcbNeeded
+    );
+
+    /* Function 0x1B */
+    DWORD _RpcSetPrinterData(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] WCHAR*  pValueName,
+        [in] DWORD Type,
+        [in, size_is(cbData)] BYTE* pData,
+        [in] DWORD cbData
+    );
+
+    /* Function 0x1C */
+    DWORD _RpcWaitForPrinterChange(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD Flags,
+        [out] DWORD* pFlags
+    );
+
+    /* Function 0x1D */
+    DWORD _RpcClosePrinter(
+        [in, out] WINSPOOL_PRINTER_HANDLE* phPrinter
+    );
+
+    /* Function 0x1E */
+    DWORD _RpcAddForm(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] WINSPOOL_FORM_CONTAINER* pFormInfoContainer
+    );
+
+    /* Function 0x1F */
+    DWORD _RpcDeleteForm(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] WCHAR* pFormName
+    );
+
+    /* Function 0x20 */
+    DWORD _RpcGetForm(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] WCHAR* pFormName,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pForm,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded
+    );
+
+    /* Function 0x21 */
+    DWORD _RpcSetForm(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] WCHAR* pFormName,
+        [in] WINSPOOL_FORM_CONTAINER* pFormInfoContainer
+    );
+
+    /* Function 0x22 */
+    DWORD _RpcEnumForms(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pForm,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded,
+        [out] DWORD* pcReturned
+    );
+
+    /* Function 0x23 */
+    DWORD _RpcEnumPorts(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pPort,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded,
+        [out] DWORD* pcReturned
+    );
+
+    /* Function 0x24 */
+    DWORD _RpcEnumMonitors(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pMonitor,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded,
+        [out] DWORD* pcReturned
+    );
+
+    /* Function 0x25 */
+    DWORD _RpcAddPort(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] ULONG_PTR hWnd,
+        [in] WCHAR* pMonitorName
+    );
+
+    /* Function 0x26 */
+    DWORD _RpcConfigurePort(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] ULONG_PTR hWnd,
+        [in] WCHAR* pPortName
+    );
+
+    /* Function 0x27 */
+    DWORD _RpcDeletePort(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] ULONG_PTR hWnd,
+        [in, string] WCHAR* pPortName
+    );
+
+    /* Function 0x28 */
+    DWORD _RpcCreatePrinterIC(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [out] WINSPOOL_GDI_HANDLE* pHandle,
+        [in] WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer
+    );
+
+    /* Function 0x29 */
+    DWORD _RpcPlayGdiScriptOnPrinterIC(
+        [in] WINSPOOL_GDI_HANDLE hPrinterIC,
+        [in, size_is(cIn)] BYTE* pIn,
+        [in] DWORD cIn,
+        [out, size_is(cOut)] BYTE* pOut,
+        [in] DWORD cOut,
+        [in] DWORD ul
+    );
+
+    /* Function 0x2A */
+    DWORD _RpcDeletePrinterIC(
+        [in, out] WINSPOOL_GDI_HANDLE* phPrinterIC
+    );
+
+    /* Function 0x2B */
+    DWORD _RpcAddPrinterConnection(
+        [in, unique] WINSPOOL_HANDLE pName
+    );
+
+    /* Function 0x2C */
+    DWORD _RpcDeletePrinterConnection(
+        [in, unique] WINSPOOL_HANDLE pName
+    );
+
+    /* Function 0x2D */
+    DWORD _RpcPrinterMessageBox(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD Error,
+        [in] ULONG_PTR hWnd,
+        [in, string] WCHAR* pText,
+        [in, string] WCHAR* pCaption,
+        [in] DWORD dwType
+    );
+
+    /* Function 0x2E */
+    DWORD _RpcAddMonitor(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] WINSPOOL_MONITOR_CONTAINER* pMonitorContainer
+    );
+
+    /* Function 0x2F */
+    DWORD _RpcDeleteMonitor(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string, unique] WCHAR* pEnvironment,
+        [in, string] WCHAR* pMonitorName
+    );
+
+    /* Function 0x30 */
+    DWORD _RpcDeletePrintProcessor(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string, unique] WCHAR* pEnvironment,
+        [in, string] WCHAR* pPrintProcessorName
+    );
+
+    /* Function 0x31 */
+    DWORD _RpcAddPrintProvidor(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] WINSPOOL_PROVIDOR_CONTAINER* pProvidorContainer
+    );
+
+    /* Function 0x32 */
+    DWORD _RpcDeletePrintProvidor(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string, unique] WCHAR* pEnvironment,
+        [in, string] WCHAR* pPrintProviderName
+    );
+
+    /* Function 0x33 */
+    DWORD _RpcEnumPrintProcessorDatatypes(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string, unique] WCHAR* pPrintProcessorName,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pDatatypes,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded,
+        [out] DWORD* pcReturned
+    );
+
+    /* Function 0x34 */
+    DWORD _RpcResetPrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string, unique] WCHAR* pDatatype,
+        [in] WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer
+    );
+
+    /* Function 0x35 */
+    DWORD _RpcGetPrinterDriver2(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string, unique] WCHAR* pEnvironment,
+        [in] DWORD Level,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pDriver,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcbNeeded,
+        [in] DWORD dwClientMajorVersion,
+        [in] DWORD dwClientMinorVersion,
+        [out] DWORD* pdwServerMaxVersion,
+        [out] DWORD* pdwServerMinVersion
+    );
+
+    /* Function 0x36 (TODO) */
+    DWORD _RpcClientFindFirstPrinterChangeNotification(
+        /* TODO */
+    );
+
+    /* Function 0x37 (TODO) */
+    DWORD _RpcFindNextPrinterChangeNotification(
+        /* TODO */
+    );
+
+    /* Function 0x38 (TODO) */
+    DWORD _RpcFindClosePrinterChangeNotification(
+        /* TODO */
+    );
+
+    /* Function 0x39 (TODO) */
+    DWORD _RpcRouterFindFirstPrinterChangeNotificationOld(
+        /* TODO */
+    );
+
+    /* Function 0x3A */
+    DWORD _RpcReplyOpenPrinter(
+        [in] WINSPOOL_HANDLE pMachine,
+        [out] WINSPOOL_PRINTER_HANDLE* phPrinterNotify,
+        [in] DWORD dwPrinterRemote,
+        [in] DWORD dwType,
+        [in, range(0, 512)] DWORD cbBuffer,
+        [in, unique, size_is(cbBuffer), disable_consistency_check] BYTE* pBuffer
+    );
+
+    /* Function 0x3B */
+    DWORD _RpcRouterReplyPrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hNotify,
+        [in] DWORD fdwFlags,
+        [in, range(0, 512)] DWORD cbBuffer,
+        [in, unique, size_is(cbBuffer), disable_consistency_check] BYTE* pBuffer
+    );
+
+    /* Function 0x3C */
+    DWORD _RpcReplyClosePrinter(
+        [in, out] WINSPOOL_PRINTER_HANDLE* phNotify
+    );
+
+    /* Function 0x3D */
+    DWORD _RpcAddPortEx(
+        [in] WINSPOOL_HANDLE pName,
+        [in] WINSPOOL_PORT_CONTAINER* pPortContainer,
+        [in] WINSPOOL_PORT_VAR_CONTAINER* pPortVarContainer,
+        [in, string] WCHAR* pMonitorName
+    );
+
+    /* Function 0x3E */
+    DWORD _RpcRemoteFindFirstPrinterChangeNotification(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD fdwFlags,
+        [in] DWORD fdwOptions,
+        [in, string, unique] WCHAR* pszLocalMachine,
+        [in] DWORD dwPrinterLocal,
+        [in, range(0, 512)] DWORD cbBuffer,
+        [in, out, unique, size_is(cbBuffer), disable_consistency_check] BYTE* pBuffer
+    );
+
+    /* Function 0x3F */
+    DWORD _RpcSpoolerInit();
+
+    /* Function 0x40 (TODO) */
+    DWORD _RpcResetPrinterEx(
+        /* TODO */
+    );
+
+    /* Function 0x41 */
+    DWORD _RpcRemoteFindFirstPrinterChangeNotificationEx(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD fdwFlags,
+        [in] DWORD fdwOptions,
+        [in, string, unique] WCHAR* pszLocalMachine,
+        [in] DWORD dwPrinterLocal,
+        [in, unique] WINSPOOL_V2_NOTIFY_OPTIONS* pOptions
+    );
+
+    /* Function 0x42 */
+    DWORD _RpcRouterReplyPrinterEx(
+        [in] WINSPOOL_PRINTER_HANDLE hNotify,
+        [in] DWORD dwColor,
+        [in] DWORD fdwFlags,
+        [out] DWORD* pdwResult,
+        [in] DWORD dwReplyType,
+        [in, switch_is(dwReplyType)] WINSPOOL_V2_UREPLY_PRINTER Reply
+    );
+
+    /* Function 0x43 */
+    DWORD _RpcRouterRefreshPrinterChangeNotification(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD dwColor,
+        [in, unique] WINSPOOL_V2_NOTIFY_OPTIONS* pOptions,
+        [out] WINSPOOL_V2_NOTIFY_INFO** ppInfo
+    );
+
+    /* Function 0x44 (TODO) */
+    DWORD _RpcSetAllocFailCount(
+        /* TODO */
+    );
+
+    /* Function 0x45 */
+    DWORD _RpcOpenPrinterEx(
+        [in, unique] WINSPOOL_HANDLE pPrinterName,
+        [out] WINSPOOL_PRINTER_HANDLE* pHandle,
+        [in, string, unique] WCHAR* pDatatype,
+        [in] WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer,
+        [in] DWORD AccessRequired,
+        [in] WINSPOOL_SPLCLIENT_CONTAINER* pClientInfo
+    );
+
+    /* Function 0x46 */
+    DWORD _RpcAddPrinterEx(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] WINSPOOL_PRINTER_CONTAINER* pPrinterContainer,
+        [in] WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer,
+        [in] WINSPOOL_SECURITY_CONTAINER* pSecurityContainer,
+        [in] WINSPOOL_SPLCLIENT_CONTAINER* pClientInfo,
+        [out] WINSPOOL_PRINTER_HANDLE* pHandle
+    );
+
+    /* Function 0x47 */
+    DWORD _RpcSetPort(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string, unique] WCHAR* pPortName,
+        [in] WINSPOOL_PORT_CONTAINER* pPortContainer
+    );
+
+    /* Function 0x48 */
+    DWORD _RpcEnumPrinterData(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in] DWORD dwIndex,
+        [out, size_is(cbValueName / sizeof(WCHAR))] WCHAR* pValueName,
+        [in]  DWORD cbValueName,
+        [out] DWORD* pcbValueName,
+        [out] DWORD* pType,
+        [out, size_is(cbData)] BYTE* pData,
+        [in]  DWORD cbData,
+        [out] DWORD* pcbData
+    );
+
+    /* Function 0x49 */
+    DWORD _RpcDeletePrinterData(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] WCHAR* pValueName
+    );
+
+    /* Function 0x4A (TODO) */
+    DWORD _RpcClusterSplOpen(
+        /* TODO */
+    );
+
+    /* Function 0x4B (TODO) */
+    DWORD _RpcClusterSplClose(
+        /* TODO */
+    );
+
+    /* Function 0x4C (TODO) */
+    DWORD _RpcClusterSplIsAlive(
+        /* TODO */
+    );
+
+    /* Function 0x4D */
+    DWORD _RpcSetPrinterDataEx(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] const WCHAR* pKeyName,
+        [in, string] const WCHAR* pValueName,
+        [in] DWORD Type,
+        [in, size_is(cbData)] BYTE* pData,
+        [in] DWORD cbData
+    );
+
+    /* Function 0x4E */
+    DWORD _RpcGetPrinterDataEx(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] const WCHAR* pKeyName,
+        [in, string] const WCHAR* pValueName,
+        [out] DWORD* pType,
+        [out, size_is(nSize)] BYTE* pData,
+        [in] DWORD nSize,
+        [out] DWORD* pcbNeeded
+    );
+
+    /* Function 0x4F */
+    DWORD _RpcEnumPrinterDataEx(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string]  const WCHAR* pKeyName,
+        [out, size_is(cbEnumValues)] BYTE* pEnumValues,
+        [in]  DWORD cbEnumValues,
+        [out] DWORD* pcbEnumValues,
+        [out] DWORD* pnEnumValues
+    );
+
+    /* Function 0x50 */
+    DWORD _RpcEnumPrinterKey(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] const WCHAR* pKeyName,
+        [out, size_is(cbSubkey / sizeof(WCHAR))] WCHAR* pSubkey,
+        [in]  DWORD cbSubkey,
+        [out] DWORD* pcbSubkey
+    );
+
+    /* Function 0x51 */
+    DWORD _RpcDeletePrinterDataEx(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] const WCHAR* pKeyName,
+        [in, string] const WCHAR* pValueName
+    );
+
+    /* Function 0x52 */
+    DWORD _RpcDeletePrinterKey(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string] const WCHAR* pKeyName
+    );
+
+    /* Function 0x53 (TODO) */
+    DWORD _RpcSeekPrinter(
+        /* TODO */
+    );
+
+    /* Function 0x54 */
+    DWORD _RpcDeletePrinterDriverEx(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in, string] WCHAR* pEnvironment,
+        [in, string] WCHAR* pDriverName,
+        [in] DWORD dwDeleteFlag,
+        [in] DWORD dwVersionNum
+    );
+
+    /* Function 0x55 */
+    DWORD _RpcAddPerMachineConnection(
+        [in, unique] WINSPOOL_HANDLE pServer,
+        [in, string] const WCHAR*  pPrinterName,
+        [in, string] const WCHAR*  pPrintServer,
+        [in, string] const WCHAR*  pProvider
+    );
+
+    /* Function 0x56 */
+    DWORD _RpcDeletePerMachineConnection(
+        [in, unique] WINSPOOL_HANDLE pServer,
+        [in, string] const WCHAR*  pPrinterName
+    );
+
+    /* Function 0x57 */
+    DWORD _RpcEnumPerMachineConnections(
+        [in, unique]  WINSPOOL_HANDLE pServer,
+        [in, out, unique, size_is(cbBuf), disable_consistency_check] BYTE* pPrinterEnum,
+        [in]  DWORD cbBuf,
+        [out] DWORD* pcbNeeded,
+        [out] DWORD* pcReturned
+    );
+
+    /* Function 0x58 */
+    DWORD _RpcXcvData(
+        [in] WINSPOOL_PRINTER_HANDLE hXcv,
+        [in, string]  const WCHAR* pszDataName,
+        [in, size_is(cbInputData)] BYTE* pInputData,
+        [in] DWORD cbInputData,
+        [out, size_is(cbOutputData)] BYTE* pOutputData,
+        [in] DWORD cbOutputData,
+        [out] DWORD* pcbOutputNeeded,
+        [in, out] DWORD* pdwStatus
+    );
+
+    /* Function 0x59 */
+    DWORD _RpcAddPrinterDriverEx(
+        [in, unique] WINSPOOL_HANDLE pName,
+        [in] WINSPOOL_DRIVER_CONTAINER* pDriverContainer,
+        [in] DWORD dwFileCopyFlags
+    );
+
+    /* Function 0x5A (TODO) */
+    DWORD _RpcSplOpenPrinter(
+        /* TODO */
+    );
+
+    /* Function 0x5B (TODO) */
+    DWORD _RpcGetSpoolFileInfo(
+        /* TODO */
+    );
+
+    /* Function 0x5C (TODO) */
+    DWORD _RpcCommitSpoolData(
+        /* TODO */
+    );
+
+    /* Function 0x5D (TODO) */
+    DWORD _RpcCloseSpoolFileHandle(
+        /* TODO */
+    );
+
+    /* Function 0x5E */
+    DWORD _RpcFlushPrinter(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, size_is(cbBuf)] BYTE* pBuf,
+        [in] DWORD cbBuf,
+        [out] DWORD* pcWritten,
+        [in] DWORD cSleep
+    );
+
+    /* Function 0x5F */
+    DWORD _RpcSendRecvBidiData(
+        [in] WINSPOOL_PRINTER_HANDLE hPrinter,
+        [in, string, unique] const WCHAR* pAction,
+        [in] WINSPOOL_BIDI_REQUEST_CONTAINER* pReqData,
+        [out] WINSPOOL_BIDI_RESPONSE_CONTAINER** ppRespData
+    );
+
+    /* Function 0x60 (TODO) */
+    DWORD _RpcAddDriverCatalog(
+        /* TODO */
+    );
+}
index 3c48a3a..700a332 100644 (file)
@@ -42,6 +42,7 @@ endif()
 
 add_subdirectory(rtl)
 add_subdirectory(scrnsave)
+add_subdirectory(skiplist)
 add_subdirectory(strmiids)
 add_subdirectory(smlib)
 add_subdirectory(tdilib)
diff --git a/reactos/sdk/lib/skiplist/CMakeLists.txt b/reactos/sdk/lib/skiplist/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4ceeec8
--- /dev/null
@@ -0,0 +1,11 @@
+
+# The library
+add_definitions(-DSKIPLIST_LEVELS=16)
+add_library(skiplist16 skiplist.c)
+add_dependencies(skiplist16 psdk)
+
+# The Test program
+add_executable(skiplist_test skiplist_test.c)
+target_link_libraries(skiplist_test skiplist16)
+set_module_type(skiplist_test win32cui)
+add_importlibs(skiplist_test msvcrt kernel32 ntdll)
diff --git a/reactos/sdk/lib/skiplist/skiplist.c b/reactos/sdk/lib/skiplist/skiplist.c
new file mode 100644 (file)
index 0000000..b1764df
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * PROJECT:     Skiplist implementation for the ReactOS Project
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     All implemented functions operating on the Skiplist
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <intrin.h>
+#include <windef.h>
+#include <winbase.h>
+#include "skiplist.h"
+
+/**
+ * @name _GetRandomLevel
+ *
+ * Returns a random level for the next element to be inserted.
+ * This level is geometrically distributed for p = 0.5, so perfectly suitable for an efficient Skiplist implementation.
+ *
+ * @return
+ * A value between 0 and SKIPLIST_LEVELS - 1.
+ */
+static __inline CHAR
+_GetRandomLevel()
+{
+    // Using a simple fixed seed and the Park-Miller Lehmer Minimal Standard Random Number Generator gives an acceptable distribution for our "random" levels.
+    static DWORD dwRandom = 1;
+
+    DWORD dwLevel = 0;
+    DWORD dwShifted;
+
+    // Generate 31 uniformly distributed pseudo-random bits using the Park-Miller Lehmer Minimal Standard Random Number Generator.
+    dwRandom = (DWORD)(((ULONGLONG)dwRandom * 48271UL) % 2147483647UL);
+
+    // Shift out (31 - SKIPLIST_LEVELS) bits to the right to have no more than SKIPLIST_LEVELS bits set.
+    dwShifted = dwRandom >> (31 - SKIPLIST_LEVELS);
+
+    // BitScanForward doesn't operate on a zero input value.
+    if (dwShifted)
+    {
+        // BitScanForward sets dwLevel to the zero-based position of the first set bit (from LSB to MSB).
+        // This makes dwLevel a geometrically distributed value between 0 and SKIPLIST_LEVELS - 1 for p = 0.5.
+        BitScanForward(&dwLevel, dwShifted);
+    }
+
+    // dwLevel can't have a value higher than 30 this way, so a CHAR is more than enough.
+    return (CHAR)dwLevel;
+}
+
+/**
+ * @name _InsertElementSkiplistWithInformation
+ *
+ * Determines a level for the new element and inserts it at the given position in the Skiplist.
+ * This function is internally used by the Skiplist insertion functions.
+ *
+ * @param Skiplist
+ * Pointer to the SKIPLIST structure to operate on.
+ *
+ * @param Element
+ * The element to insert.
+ *
+ * @param pUpdate
+ * Array containing the last nodes before our new node on each level.
+ *
+ * @param dwDistance
+ * Array containing the distance to the last node before our new node on each level.
+ *
+ * @return
+ * TRUE if the node was successfully inserted, FALSE if no memory could be allocated for it.
+ */
+static BOOL
+_InsertElementSkiplistWithInformation(PSKIPLIST Skiplist, PVOID Element, PSKIPLIST_NODE* pUpdate, DWORD* dwDistance)
+{
+    CHAR chNewLevel;
+    CHAR i;
+    PSKIPLIST_NODE pNode;
+
+    // Get the highest level, on which the node shall be inserted.
+    chNewLevel = _GetRandomLevel();
+
+    // Check if the new level is higher than the maximum level we currently have in the Skiplist.
+    if (chNewLevel > Skiplist->MaximumLevel)
+    {
+        // It is, so we also need to insert the new node right after the Head node on some levels.
+        // These are the levels higher than the current maximum level up to the new level.
+        // We also need to set the distance of these elements to the new node count to account for the calculations below.
+        for (i = Skiplist->MaximumLevel + 1; i <= chNewLevel; i++)
+        {
+            pUpdate[i] = &Skiplist->Head;
+            pUpdate[i]->Distance[i] = Skiplist->NodeCount + 1;
+        }
+
+        // The new level is the new maximum level of the entire Skiplist.
+        Skiplist->MaximumLevel = chNewLevel;
+    }
+
+    // Finally create our new Skiplist node.
+    pNode = Skiplist->AllocateRoutine(sizeof(SKIPLIST_NODE));
+    if (!pNode)
+        return FALSE;
+
+    pNode->Element = Element;
+
+    // For each used level, insert us between the saved node for this level and its current next node.
+    for (i = 0; i <= chNewLevel; i++)
+    {
+        pNode->Next[i] = pUpdate[i]->Next[i];
+        pUpdate[i]->Next[i] = pNode;
+
+        // We know the walked distance in this level: dwDistance[i]
+        // We also know the element index of the new node: dwDistance[0]
+        // The new node's distance is now the walked distance in this level plus the difference between the saved node's distance and the element index.
+        pNode->Distance[i] = dwDistance[i] + (pUpdate[i]->Distance[i] - dwDistance[0]);
+
+        // The saved node's distance is now the element index plus one (to account for the added node) minus the walked distance in this level.
+        pUpdate[i]->Distance[i] = dwDistance[0] + 1 - dwDistance[i];
+    }
+
+    // For all levels above the new node's level, we need to increment the distance, because we've just added our new node.
+    for (i = chNewLevel + 1; i <= Skiplist->MaximumLevel; i++)
+        ++pUpdate[i]->Distance[i];
+
+    // We've successfully added a node :)
+    ++Skiplist->NodeCount;
+    return TRUE;
+}
+
+/**
+ * @name DeleteElementSkiplist
+ *
+ * Deletes an element from the Skiplist. The efficiency of this operation is O(log N) on average.
+ *
+ * Instead of the result of a LookupElementSkiplist call, it's sufficient to provide a dummy element with just enough information for your CompareRoutine.
+ * A lookup for the element to be deleted needs to be performed in any case.
+ *
+ * @param Skiplist
+ * Pointer to the SKIPLIST structure to operate on.
+ *
+ * @param Element
+ * Information about the element to be deleted.
+ *
+ * @return
+ * Returns the deleted element or NULL if no such element was found.
+ * You can then free memory for the deleted element if necessary.
+ */
+PVOID
+DeleteElementSkiplist(PSKIPLIST Skiplist, PVOID Element)
+{
+    CHAR i;
+    PSKIPLIST_NODE pLastComparedNode = NULL;
+    PSKIPLIST_NODE pNode = &Skiplist->Head;
+    PSKIPLIST_NODE pUpdate[SKIPLIST_LEVELS];
+    PVOID pReturnValue;
+
+    // Find the node on every currently used level, after which the node to be deleted must follow.
+    // This can be done efficiently by starting from the maximum level and going down a level each time a position has been found.
+    for (i = Skiplist->MaximumLevel + 1; --i >= 0;)
+    {
+        while (pNode->Next[i] && pNode->Next[i] != pLastComparedNode && Skiplist->CompareRoutine(pNode->Next[i]->Element, Element) < 0)
+            pNode = pNode->Next[i];
+
+        // Reduce the number of comparisons by not comparing the same node on different levels twice.
+        pLastComparedNode = pNode->Next[i];
+        pUpdate[i] = pNode;
+    }
+
+    // Check if the node we're looking for has been found.
+    pNode = pNode->Next[0];
+    if (!pNode || Skiplist->CompareRoutine(pNode->Element, Element) != 0)
+    {
+        // It hasn't been found, so there's nothing to delete.
+        return NULL;
+    }
+
+    // Beginning at the lowest level, remove the node from each level of the list and merge distances.
+    // We can stop as soon as we found the first level that doesn't contain the node.
+    for (i = 0; i <= Skiplist->MaximumLevel && pUpdate[i]->Next[i] == pNode; i++)
+    {
+        pUpdate[i]->Distance[i] += pNode->Distance[i] - 1;
+        pUpdate[i]->Next[i] = pNode->Next[i];
+    }
+
+    // Now decrement the distance of the corresponding node in levels higher than the deleted node's level to account for the deleted node.
+    while (i <= Skiplist->MaximumLevel)
+    {
+        --pUpdate[i]->Distance[i];
+        i++;
+    }
+
+    // Return the deleted element (so the caller can free it if necessary) and free the memory for the node itself (allocated by us).
+    pReturnValue = pNode->Element;
+    Skiplist->FreeRoutine(pNode);
+
+    // Find all levels which now contain no more nodes and reduce the maximum level of the entire Skiplist accordingly.
+    while (Skiplist->MaximumLevel > 0 && !Skiplist->Head.Next[Skiplist->MaximumLevel])
+        --Skiplist->MaximumLevel;
+
+    // We've successfully deleted the node :)
+    --Skiplist->NodeCount;
+    return pReturnValue;
+}
+
+/**
+ * @name InitializeSkiplist
+ *
+ * Initializes a new Skiplist structure.
+ *
+ * @param Skiplist
+ * Pointer to the SKIPLIST structure to operate on.
+ *
+ * @param AllocateRoutine
+ * Pointer to a SKIPLIST_ALLOCATE_ROUTINE for allocating memory for new Skiplist nodes.
+ *
+ * @param CompareRoutine
+ * Pointer to a SKIPLIST_COMPARE_ROUTINE for comparing two elements of the Skiplist.
+ *
+ * @param FreeRoutine
+ * Pointer to a SKIPLIST_FREE_ROUTINE for freeing memory allocated with AllocateRoutine.
+ */
+void
+InitializeSkiplist(PSKIPLIST Skiplist, PSKIPLIST_ALLOCATE_ROUTINE AllocateRoutine, PSKIPLIST_COMPARE_ROUTINE CompareRoutine, PSKIPLIST_FREE_ROUTINE FreeRoutine)
+{
+    // Store the routines.
+    Skiplist->AllocateRoutine = AllocateRoutine;
+    Skiplist->CompareRoutine = CompareRoutine;
+    Skiplist->FreeRoutine = FreeRoutine;
+
+    // Initialize the members and pointers.
+    // The Distance array is only used when a node is non-NULL, so it doesn't need initialization.
+    Skiplist->MaximumLevel = 0;
+    Skiplist->NodeCount = 0;
+    ZeroMemory(Skiplist->Head.Next, sizeof(Skiplist->Head.Next));
+}
+
+/**
+ * @name InsertElementSkiplist
+ *
+ * Inserts a new element into the Skiplist. The efficiency of this operation is O(log N) on average.
+ * Uses CompareRoutine to find the right position for the insertion.
+ *
+ * @param Skiplist
+ * Pointer to the SKIPLIST structure to operate on.
+ *
+ * @param Element
+ * The element to insert.
+ *
+ * @return
+ * TRUE if the node was successfully inserted, FALSE if it already exists or no memory could be allocated for it.
+ */
+BOOL
+InsertElementSkiplist(PSKIPLIST Skiplist, PVOID Element)
+{
+    CHAR i;
+    DWORD dwDistance[SKIPLIST_LEVELS + 1] = { 0 };
+    PSKIPLIST_NODE pLastComparedNode = NULL;
+    PSKIPLIST_NODE pNode = &Skiplist->Head;
+    PSKIPLIST_NODE pUpdate[SKIPLIST_LEVELS];
+
+    // Find the node on every currently used level, after which the new node needs to be inserted.
+    // This can be done efficiently by starting from the maximum level and going down a level each time a position has been found.
+    for (i = Skiplist->MaximumLevel + 1; --i >= 0;)
+    {
+        // When entering this level, we begin at the distance of the last level we walked through.
+        dwDistance[i] = dwDistance[i + 1];
+
+        while (pNode->Next[i] && pNode->Next[i] != pLastComparedNode && Skiplist->CompareRoutine(pNode->Next[i]->Element, Element) < 0)
+        {
+            // Save our position in every level when walking through the nodes.
+            dwDistance[i] += pNode->Distance[i];
+
+            // Advance to the next node.
+            pNode = pNode->Next[i];
+        }
+
+        // Reduce the number of comparisons by not comparing the same node on different levels twice.
+        pLastComparedNode = pNode->Next[i];
+        pUpdate[i] = pNode;
+    }
+
+    // Check if the node already exists in the Skiplist.
+    pNode = pNode->Next[0];
+    if (pNode && Skiplist->CompareRoutine(pNode->Element, Element) == 0)
+    {
+        // All elements to be inserted mustn't exist in the list, so we see this as a failure.
+        return FALSE;
+    }
+
+    // The rest of the procedure is the same for both insertion functions.
+    return _InsertElementSkiplistWithInformation(Skiplist, Element, pUpdate, dwDistance);
+}
+
+/**
+ * @name InsertTailElementSkiplist
+ *
+ * Inserts a new element at the end of the Skiplist. The efficiency of this operation is O(log N) on average.
+ * In contrast to InsertElementSkiplist, this function is more efficient by not calling CompareRoutine at all and always inserting the element at the end.
+ * You're responsible for calling this function only when you can guarantee that InsertElementSkiplist would also insert the element at the end.
+ *
+ * @param Skiplist
+ * Pointer to the SKIPLIST structure to operate on.
+ *
+ * @param Element
+ * The element to insert.
+ *
+ * @return
+ * TRUE if the node was successfully inserted, FALSE if it already exists or no memory could be allocated for it.
+ */
+BOOL
+InsertTailElementSkiplist(PSKIPLIST Skiplist, PVOID Element)
+{
+    CHAR i;
+    DWORD dwDistance[SKIPLIST_LEVELS + 1] = { 0 };
+    PSKIPLIST_NODE pNode = &Skiplist->Head;
+    PSKIPLIST_NODE pUpdate[SKIPLIST_LEVELS];
+
+    // Find the last node on every currently used level, after which the new node needs to be inserted.
+    // This can be done efficiently by starting from the maximum level and going down a level each time a position has been found.
+    for (i = Skiplist->MaximumLevel + 1; --i >= 0;)
+    {
+        // When entering this level, we begin at the distance of the last level we walked through.
+        dwDistance[i] = dwDistance[i + 1];
+
+        while (pNode->Next[i])
+        {
+            // Save our position in every level when walking through the nodes.
+            dwDistance[i] += pNode->Distance[i];
+
+            // Advance to the next node.
+            pNode = pNode->Next[i];
+        }
+
+        pUpdate[i] = pNode;
+    }
+
+    // The rest of the procedure is the same for both insertion functions.
+    return _InsertElementSkiplistWithInformation(Skiplist, Element, pUpdate, dwDistance);
+}
+
+/**
+ * @name LookupElementSkiplist
+ *
+ * Looks up an element in the Skiplist. The efficiency of this operation is O(log N) on average.
+ *
+ * @param Skiplist
+ * Pointer to the SKIPLIST structure to operate on.
+ *
+ * @param Element
+ * Information about the element to look for.
+ *
+ * @param ElementIndex
+ * Pointer to a DWORD that will contain the zero-based index of the element in the Skiplist.
+ * If you're not interested in the index, you can set this parameter to NULL.
+ *
+ * @return
+ * Returns the found element or NULL if no such element was found.
+ */
+PVOID
+LookupElementSkiplist(PSKIPLIST Skiplist, PVOID Element, PDWORD ElementIndex)
+{
+    CHAR i;
+    DWORD dwIndex = 0;
+    PSKIPLIST_NODE pLastComparedNode = NULL;
+    PSKIPLIST_NODE pNode = &Skiplist->Head;
+
+    // Do the efficient lookup in Skiplists:
+    //    * Start from the maximum level.
+    //    * Walk through all nodes on this level that come before the node we're looking for.
+    //    * When we have reached such a node, go down a level and continue there.
+    //    * Repeat these steps till we're in level 0, right in front of the node we're looking for.
+    for (i = Skiplist->MaximumLevel + 1; --i >= 0;)
+    {
+        while (pNode->Next[i] && pNode->Next[i] != pLastComparedNode && Skiplist->CompareRoutine(pNode->Next[i]->Element, Element) < 0)
+        {
+            dwIndex += pNode->Distance[i];
+            pNode = pNode->Next[i];
+        }   
+
+        // Reduce the number of comparisons by not comparing the same node on different levels twice.
+        pLastComparedNode = pNode->Next[i];
+    }
+
+    // We must be right in front of the node we're looking for now, otherwise it doesn't exist in the Skiplist at all.
+    pNode = pNode->Next[0];
+    if (!pNode || Skiplist->CompareRoutine(pNode->Element, Element) != 0)
+    {
+        // It hasn't been found, so there's nothing to return.
+        return NULL;
+    }
+
+    // Return the index of the element if the caller is interested.
+    if (ElementIndex)
+        *ElementIndex = dwIndex;
+
+    // Return the stored element of the found node.
+    return pNode->Element;
+}
+
+/**
+ * @name LookupNodeByIndexSkiplist
+ *
+ * Looks up a node in the Skiplist at the given position. The efficiency of this operation is O(log N) on average.
+ *
+ * @param Skiplist
+ * Pointer to the SKIPLIST structure to operate on.
+ *
+ * @param ElementIndex
+ * Zero-based position of the node in the Skiplist.
+ *
+ * @return
+ * Returns the found node or NULL if the position is invalid.
+ */
+PSKIPLIST_NODE
+LookupNodeByIndexSkiplist(PSKIPLIST Skiplist, DWORD ElementIndex)
+{
+    CHAR i;
+    DWORD dwIndex = 0;
+    PSKIPLIST_NODE pNode = &Skiplist->Head;
+
+    // The only way the node can't be found is when the index is out of range.
+    if (ElementIndex >= Skiplist->NodeCount)
+        return NULL;
+
+    // Do the efficient lookup in Skiplists:
+    //    * Start from the maximum level.
+    //    * Walk through all nodes on this level that come before the node we're looking for.
+    //    * When we have reached such a node, go down a level and continue there.
+    //    * Repeat these steps till we're in level 0, right in front of the node we're looking for.
+    for (i = Skiplist->MaximumLevel + 1; --i >= 0;)
+    {
+        // We compare with <= instead of < here, because the added distances make up a 1-based index while ElementIndex is zero-based,
+        // so we have to jump one node further.
+        while (pNode->Next[i] && dwIndex + pNode->Distance[i] <= ElementIndex)
+        {
+            dwIndex += pNode->Distance[i];
+            pNode = pNode->Next[i];
+        }
+    }
+
+    // We are right in front of the node we're looking for now.
+    return pNode->Next[0];
+}
diff --git a/reactos/sdk/lib/skiplist/skiplist.h b/reactos/sdk/lib/skiplist/skiplist.h
new file mode 100644 (file)
index 0000000..1bdd118
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * PROJECT:     Skiplist implementation for the ReactOS Project
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Interfaces for the Skiplist
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#ifndef _REACTOS_SKIPLIST_H
+#define _REACTOS_SKIPLIST_H
+
+// Define SKIPLIST_LEVELS to a value between 1 and 31 before including this header.
+// This specifies the maximum number of levels you want your Skiplist to have.
+// A value of X is recommended for handling up to 2^X elements.
+#ifndef SKIPLIST_LEVELS
+#error Please define SKIPLIST_LEVELS to a value between 1 and 31.
+#endif
+
+C_ASSERT(SKIPLIST_LEVELS >= 1);
+C_ASSERT(SKIPLIST_LEVELS <= 31);
+
+// Function pointer definitions
+typedef PVOID (WINAPI *PSKIPLIST_ALLOCATE_ROUTINE)(DWORD);
+typedef int (WINAPI *PSKIPLIST_COMPARE_ROUTINE)(PVOID, PVOID);
+typedef void (WINAPI *PSKIPLIST_FREE_ROUTINE)(PVOID);
+
+// Structure definitions
+typedef struct _SKIPLIST_NODE
+{
+    DWORD Distance[SKIPLIST_LEVELS];
+    struct _SKIPLIST_NODE* Next[SKIPLIST_LEVELS];
+    PVOID Element;
+}
+SKIPLIST_NODE, *PSKIPLIST_NODE;
+
+typedef struct _SKIPLIST
+{
+    SKIPLIST_NODE Head;
+    CHAR MaximumLevel;
+    DWORD NodeCount;
+    PSKIPLIST_ALLOCATE_ROUTINE AllocateRoutine;
+    PSKIPLIST_COMPARE_ROUTINE CompareRoutine;
+    PSKIPLIST_FREE_ROUTINE FreeRoutine;
+}
+SKIPLIST, *PSKIPLIST;
+
+// Function prototypes
+void InitializeSkiplist(PSKIPLIST Skiplist, PSKIPLIST_ALLOCATE_ROUTINE AllocateRoutine, PSKIPLIST_COMPARE_ROUTINE CompareRoutine, PSKIPLIST_FREE_ROUTINE FreeRoutine);
+BOOL InsertElementSkiplist(PSKIPLIST Skiplist, PVOID Element);
+BOOL InsertTailElementSkiplist(PSKIPLIST Skiplist, PVOID Element);
+PVOID DeleteElementSkiplist(PSKIPLIST Skiplist, PVOID Element);
+PVOID LookupElementSkiplist(PSKIPLIST Skiplist, PVOID Element, PDWORD ElementIndex);
+PSKIPLIST_NODE LookupNodeByIndexSkiplist(PSKIPLIST Skiplist, DWORD ElementIndex);
+
+#endif
diff --git a/reactos/sdk/lib/skiplist/skiplist_test.c b/reactos/sdk/lib/skiplist/skiplist_test.c
new file mode 100644 (file)
index 0000000..9586562
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * PROJECT:     Skiplist implementation for the ReactOS Project
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     A simple program for testing the Skiplist implementation
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <windows.h>
+#include <stdio.h>
+#include "skiplist.h"
+
+void
+DumpSkiplist(PSKIPLIST Skiplist)
+{
+    CHAR i;
+    DWORD j;
+    PSKIPLIST_NODE pNode;
+
+    printf("======= DUMPING SKIPLIST =======\n");
+
+    for (i = Skiplist->MaximumLevel + 1; --i >= 0;)
+    {
+        pNode = &Skiplist->Head;
+        printf("H");
+
+        while (pNode->Next[i])
+        {
+            printf("-");
+
+            // By using the Distance array for painting the lines, we verify both the links and the distances for correctness.
+            for (j = 1; j < pNode->Distance[i]; j++)
+                printf("---");
+
+            printf("%02lu", (DWORD)pNode->Next[i]->Element);
+
+            pNode = pNode->Next[i];
+        }
+
+        printf("\n");
+    }
+
+    printf("================================\n\n");
+}
+
+PVOID WINAPI
+MyAlloc(DWORD Size)
+{
+    return HeapAlloc(GetProcessHeap(), 0, Size);
+}
+
+int WINAPI
+MyCompare(PVOID A, PVOID B)
+{
+    return (DWORD)A - (DWORD)B;
+}
+
+void WINAPI
+MyFree(PVOID Ptr)
+{
+    HeapFree(GetProcessHeap(), 0, Ptr);
+}
+
+int
+main()
+{
+    DWORD Element;
+    DWORD ElementIndex;
+    DWORD i;
+    SKIPLIST Skiplist;
+    PSKIPLIST_NODE pNode;
+
+    system("mode con cols=300");
+    InitializeSkiplist(&Skiplist, MyAlloc, MyCompare, MyFree);
+
+    // Insert some random elements with random numbers.
+    for (i = 0; i < 40; i++)
+        InsertElementSkiplist(&Skiplist, (PVOID)(rand() % 100));
+
+    // Delete all with index 0 to 29.
+    for (i = 0; i < 30; i++)
+        DeleteElementSkiplist(&Skiplist, (PVOID)i);
+
+    // Insert some more random elements.
+    for (i = 0; i < 40; i++)
+        InsertElementSkiplist(&Skiplist, (PVOID)(rand() % 100));
+
+    // Output the third element (with zero-based index 2).
+    pNode = LookupNodeByIndexSkiplist(&Skiplist, 2);
+    printf("Element = %lu for index 2\n", (DWORD)pNode->Element);
+
+    // Check if an element with number 44 is in the list and output its index.
+    Element = (DWORD)LookupElementSkiplist(&Skiplist, (PVOID)44, &ElementIndex);
+    printf("Element = %lu, ElementIndex = %lu\n\n", Element, ElementIndex);
+
+    DumpSkiplist(&Skiplist);
+
+    return 0;
+}
index 7618347..fec1a74 100644 (file)
@@ -16,6 +16,7 @@ if(USE_DIBLIB)
 endif()
 
 add_subdirectory(gdi/gdi32)
+add_subdirectory(printing)
 add_subdirectory(reactx)
 add_subdirectory(user/user32)
 add_subdirectory(user/winsrv)
diff --git a/reactos/win32ss/printing/CMakeLists.txt b/reactos/win32ss/printing/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e91164d
--- /dev/null
@@ -0,0 +1,7 @@
+include_directories(include)
+
+add_subdirectory(base)
+#add_subdirectory(drivers)
+add_subdirectory(monitors)
+add_subdirectory(processors)
+add_subdirectory(providers)
diff --git a/reactos/win32ss/printing/base/CMakeLists.txt b/reactos/win32ss/printing/base/CMakeLists.txt
new file mode 100644 (file)
index 0000000..25ae882
--- /dev/null
@@ -0,0 +1,4 @@
+#add_subdirectory(printui)
+add_subdirectory(spoolss)
+add_subdirectory(spoolsv)
+add_subdirectory(winspool)
diff --git a/reactos/win32ss/printing/base/spoolss/CMakeLists.txt b/reactos/win32ss/printing/base/spoolss/CMakeLists.txt
new file mode 100644 (file)
index 0000000..67a7e1f
--- /dev/null
@@ -0,0 +1,26 @@
+
+spec2def(spoolss.dll spoolss.spec ADD_IMPORTLIB)
+
+list(APPEND SOURCE
+    context.c
+    jobs.c
+    main.c
+    memory.c
+    monitors.c
+    ports.c
+    precomp.h
+    printers.c
+    printprocessors.c
+    tools.c)
+
+add_library(spoolss SHARED
+    ${SOURCE}
+    spoolss.rc
+    ${CMAKE_CURRENT_BINARY_DIR}/spoolss_stubs.c
+    ${CMAKE_CURRENT_BINARY_DIR}/spoolss.def)
+
+set_module_type(spoolss win32dll UNICODE)
+target_link_libraries(spoolss wine)
+add_importlibs(spoolss advapi32 msvcrt kernel32 ntdll)
+add_pch(spoolss precomp.h SOURCE)
+add_cd_file(TARGET spoolss DESTINATION reactos/system32 FOR all)
diff --git a/reactos/win32ss/printing/base/spoolss/context.c b/reactos/win32ss/printing/base/spoolss/context.c
new file mode 100644 (file)
index 0000000..649df49
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to switching between security contexts
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+/**
+ * @see RevertToPrinterSelf
+ */
+BOOL WINAPI
+ImpersonatePrinterClient(HANDLE hToken)
+{
+    DWORD cbReturned;
+    DWORD dwErrorCode;
+    TOKEN_TYPE Type;
+
+    // Sanity check
+    if (!hToken)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Get the type of the supplied token.
+    if (!GetTokenInformation(hToken, TokenType, &Type, sizeof(TOKEN_TYPE), &cbReturned))
+    {
+        dwErrorCode = GetLastError();
+        ERR("GetTokenInformation failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Check if this is an impersonation token and only set it as the thread token in this case.
+    // This is not always an impersonation token, see RevertToPrinterSelf.
+    if (Type == TokenImpersonation)
+    {
+        if (!SetThreadToken(NULL, hToken))
+        {
+            dwErrorCode = GetLastError();
+            ERR("SetThreadToken failed with error %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+    }
+
+Cleanup:
+    if (hToken)
+        CloseHandle(hToken);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+/**
+ * RevertToPrinterSelf reverts the security context from the current user's context back to the process context.
+ * As spoolss.dll is used by spoolsv.exe, this is usually the SYSTEM security context.
+ *
+ * Unlike the traditional ImpersonateClient and then RevertToSelf approach, we do it the other way round here,
+ * because spoolss.dll is delay-loaded by spoolsv.exe in the current user's context. Use RevertToPrinterSelf then to
+ * return to the SYSTEM context for specific tasks.
+ */
+HANDLE WINAPI
+RevertToPrinterSelf()
+{
+    DWORD dwErrorCode;
+    HANDLE hReturnValue = NULL;
+    HANDLE hToken = NULL;
+
+    // All spoolss code is usually called after impersonating the client. In this case, we can retrieve our current thread impersonation token using OpenThreadToken.
+    // But in rare occasions, spoolss code is also called from a higher-privileged thread that doesn't impersonate the client. Then we don't get an impersonation token.
+    // Anyway, we can't just return nothing in this case, because this is being treated as failure by the caller. So we return the token of the current process.
+    // This behaviour is verified with Windows!
+    if (OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hToken))
+    {
+        // Tell the thread to stop impersonating.
+        if (!SetThreadToken(NULL, NULL))
+        {
+            dwErrorCode = GetLastError();
+            ERR("SetThreadToken failed with error %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+    }
+    else if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
+    {
+        dwErrorCode = GetLastError();
+        ERR("OpenProcessToken failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // We were successful, return a token!
+    dwErrorCode = ERROR_SUCCESS;
+    hReturnValue = hToken;
+
+    // Don't let the cleanup routine close this.
+    hToken = NULL;
+
+Cleanup:
+    if (hToken)
+        CloseHandle(hToken);
+
+    SetLastError(dwErrorCode);
+    return hReturnValue;
+}
diff --git a/reactos/win32ss/printing/base/spoolss/jobs.c b/reactos/win32ss/printing/base/spoolss/jobs.c
new file mode 100644 (file)
index 0000000..d3e5eb7
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions for managing print jobs
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+BOOL WINAPI
+AddJobW(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpAddJob(pHandle->hPrinter, Level, pData, cbBuf, pcbNeeded);
+}
+
+BOOL WINAPI
+EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpEnumJobs(pHandle->hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned);
+}
+
+BOOL WINAPI
+GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpGetJob(pHandle->hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
+}
+
+BOOL WINAPI
+ScheduleJob(HANDLE hPrinter, DWORD dwJobID)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpScheduleJob(pHandle->hPrinter, dwJobID);
+}
+
+BOOL WINAPI
+SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpSetJob(pHandle->hPrinter, JobId, Level, pJobInfo, Command);
+}
diff --git a/reactos/win32ss/printing/base/spoolss/main.c b/reactos/win32ss/printing/base/spoolss/main.c
new file mode 100644 (file)
index 0000000..673186c
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Main functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+// Global Variables
+HANDLE hProcessHeap;
+LIST_ENTRY PrintProviderList;
+
+
+static DWORD
+_AddPrintProviderToList(PCWSTR pwszFileName)
+{
+    DWORD dwErrorCode;
+    HINSTANCE hinstPrintProvider;
+    PInitializePrintProvidor pfnInitializePrintProvidor;
+    PSPOOLSS_PRINT_PROVIDER pPrintProvider = NULL;
+
+    // Try to load it.
+    hinstPrintProvider = LoadLibraryW(pwszFileName);
+    if (!hinstPrintProvider)
+    {
+        dwErrorCode = GetLastError();
+        ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", pwszFileName, dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Get the initialization routine.
+    pfnInitializePrintProvidor = (PInitializePrintProvidor)GetProcAddress(hinstPrintProvider, "InitializePrintProvidor");
+    if (!pfnInitializePrintProvidor)
+    {
+        dwErrorCode = GetLastError();
+        ERR("GetProcAddress failed for \"%S\" with error %lu!\n", pwszFileName, dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Create a new SPOOLSS_PRINT_PROVIDER structure for it.
+    pPrintProvider = DllAllocSplMem(sizeof(SPOOLSS_PRINT_PROVIDER));
+    if (!pPrintProvider)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Call the Print Provider initialization function.
+    if (!pfnInitializePrintProvidor(&pPrintProvider->PrintProvider, sizeof(PRINTPROVIDOR), NULL))
+    {
+        dwErrorCode = GetLastError();
+        ERR("InitializePrintProvidor failed for \"%S\" with error %lu!\n", pwszFileName, dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Add this Print Provider to the list.
+    InsertTailList(&PrintProviderList, &pPrintProvider->Entry);
+
+    // Don't let the cleanup routine free this.
+    pPrintProvider = NULL;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (pPrintProvider)
+        DllFreeSplMem(pPrintProvider);
+
+    return dwErrorCode;
+}
+
+static BOOL
+_InitializePrintProviderList()
+{
+    DWORD cbFileName;
+    DWORD cchMaxSubKey;
+    DWORD cchPrintProviderName;
+    DWORD dwErrorCode;
+    DWORD dwSubKeys;
+    DWORD i;
+    HKEY hKey = NULL;
+    HKEY hSubKey = NULL;
+    PWSTR pwszPrintProviderName = NULL;
+    WCHAR wszFileName[MAX_PATH];
+
+    // Initialize an empty list for our Print Providers.
+    InitializeListHead(&PrintProviderList);
+
+    // First add the Local Spooler.
+    // This one must exist and must be the first one in the list.
+    dwErrorCode = _AddPrintProviderToList(L"localspl");
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("The Local Spooler could not be loaded!\n");
+        goto Cleanup;
+    }
+
+    // Now add additional Print Providers from the registry.
+    // First of all, open the key containing print providers.
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Print\\Providers", 0, KEY_READ, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Get the number of Print Providers and maximum sub key length.
+    dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Allocate a temporary buffer for the Print Provider names.
+    pwszPrintProviderName = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
+    if (!pwszPrintProviderName)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Loop through all available Print Providers.
+    for (i = 0; i < dwSubKeys; i++)
+    {
+        // Cleanup tasks from the previous run
+        if (hSubKey)
+        {
+            RegCloseKey(hSubKey);
+            hSubKey = NULL;
+        }
+
+        // Get the name of this Print Provider.
+        cchPrintProviderName = cchMaxSubKey + 1;
+        dwErrorCode = (DWORD)RegEnumKeyExW(hKey, i, pwszPrintProviderName, &cchPrintProviderName, NULL, NULL, NULL, NULL);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
+            continue;
+        }
+
+        // Open this Print Provider's registry key.
+        dwErrorCode = (DWORD)RegOpenKeyExW(hKey, pwszPrintProviderName, 0, KEY_READ, &hSubKey);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegOpenKeyExW failed for Print Provider \"%S\" with status %lu!\n", pwszPrintProviderName, dwErrorCode);
+            continue;
+        }
+
+        // Get the file name of the Print Provider.
+        cbFileName = MAX_PATH * sizeof(WCHAR);
+        dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Driver", NULL, NULL, (PBYTE)wszFileName, &cbFileName);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
+            continue;
+        }
+
+        // Load and add it to the list.
+        dwErrorCode = _AddPrintProviderToList(wszFileName);
+        if (dwErrorCode != ERROR_SUCCESS)
+            continue;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    // Inside the loop
+    if (hSubKey)
+        RegCloseKey(hSubKey);
+
+    // Outside the loop
+    if (pwszPrintProviderName)
+        DllFreeSplMem(pwszPrintProviderName);
+
+    if (hKey)
+        RegCloseKey(hKey);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    switch (fdwReason)
+    {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls(hinstDLL);
+                       hProcessHeap = GetProcessHeap();
+            break;
+    }
+
+    return TRUE;
+}
+
+BOOL WINAPI
+InitializeRouter(HANDLE SpoolerStatusHandle)
+{
+    return _InitializePrintProviderList();
+}
+
+BOOL WINAPI
+SplInitializeWinSpoolDrv(PVOID* pTable)
+{
+    HINSTANCE hWinspool;
+    int i;
+
+    hWinspool = LoadLibraryW(L"winspool.drv");
+    if (!hWinspool)
+    {
+        ERR("Could not load winspool.drv, last error is %lu!\n", GetLastError());
+        return FALSE;
+    }
+
+    // Get the function pointers which are meant to be returned by this function.
+    pTable[0] = GetProcAddress(hWinspool, "OpenPrinterW");
+    pTable[1] = GetProcAddress(hWinspool, "ClosePrinter");
+    pTable[2] = GetProcAddress(hWinspool, "SpoolerDevQueryPrintW");
+    pTable[3] = GetProcAddress(hWinspool, "SpoolerPrinterEvent");
+    pTable[4] = GetProcAddress(hWinspool, "DocumentPropertiesW");
+    pTable[5] = GetProcAddress(hWinspool, (LPSTR)212);
+    pTable[6] = GetProcAddress(hWinspool, (LPSTR)213);
+    pTable[7] = GetProcAddress(hWinspool, (LPSTR)214);
+    pTable[8] = GetProcAddress(hWinspool, (LPSTR)215);
+
+    // Verify that all calls succeeded.
+    for (i = 0; i < 9; i++)
+        if (!pTable[i])
+            return FALSE;
+
+    return TRUE;
+}
+
+BOOL WINAPI
+SplIsUpgrade()
+{
+       return FALSE;
+}
+
+BOOL WINAPI
+SpoolerInit()
+{
+    // Nothing to do here yet
+    SetLastError(ERROR_SUCCESS);
+    return TRUE;
+}
diff --git a/reactos/win32ss/printing/base/spoolss/memory.c b/reactos/win32ss/printing/base/spoolss/memory.c
new file mode 100644 (file)
index 0000000..96a7fd1
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions for allocating and freeing memory
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+
+/**
+ * @name AllocSplStr
+ *
+ * Allocates memory for a Unicode string and copies the input string into it.
+ * Equivalent of wcsdup, but the returned buffer is allocated from the spooler heap and must be freed with DllFreeSplStr.
+ *
+ * @param pwszInput
+ * The input string to copy
+ *
+ * @return
+ * Pointer to the copied string or NULL if no memory could be allocated.
+ */
+PWSTR WINAPI
+AllocSplStr(PCWSTR pwszInput)
+{
+    DWORD cbInput;
+    PWSTR pwszOutput;
+
+    // Sanity check
+    if (!pwszInput)
+        return NULL;
+
+    // Get the length of the input string.
+    cbInput = (wcslen(pwszInput) + 1) * sizeof(WCHAR);
+
+    // Allocate it. We don't use DllAllocSplMem here, because it unnecessarily zeroes the memory.
+    pwszOutput = HeapAlloc(hProcessHeap, 0, cbInput);
+    if (!pwszOutput)
+    {
+        ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+        return NULL;
+    }
+
+    // Copy the string and return it.
+    CopyMemory(pwszOutput, pwszInput, cbInput);
+    return pwszOutput;
+}
+
+/**
+ * @name DllAllocSplMem
+ *
+ * Allocate a block of zeroed memory.
+ * Windows allocates from a separate spooler heap here while we just use the process heap.
+ *
+ * @param dwBytes
+ * Number of bytes to allocate.
+ *
+ * @return
+ * A pointer to the allocated memory or NULL in case of an error.
+ * You have to free this memory using DllFreeSplMem.
+ */
+PVOID WINAPI
+DllAllocSplMem(DWORD dwBytes)
+{
+    return HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwBytes);
+}
+
+/**
+ * @name DllFreeSplMem
+ *
+ * Frees the memory allocated with DllAllocSplMem.
+ *
+ * @param pMem
+ * Pointer to the allocated memory.
+ *
+ * @return
+ * TRUE in case of success, FALSE otherwise.
+ */
+BOOL WINAPI
+DllFreeSplMem(PVOID pMem)
+{
+    return HeapFree(hProcessHeap, 0, pMem);
+}
+
+/**
+ * @name DllFreeSplStr
+ *
+ * Frees the string allocated with AllocSplStr.
+ *
+ * @param pwszString
+ * Pointer to the allocated string.
+ *
+ * @return
+ * TRUE in case of success, FALSE otherwise.
+ */
+BOOL WINAPI
+DllFreeSplStr(PWSTR pwszString)
+{
+    return HeapFree(hProcessHeap, 0, pwszString);
+}
+
+/**
+ * @name ReallocSplMem
+ *
+ * Allocates a new block of memory and copies the contents of the old block into the new one.
+ *
+ * @param pOldMem
+ * Pointer to the old block of memory.
+ * If this parameter is NULL, ReallocSplMem behaves exactly like DllAllocSplMem.
+ *
+ * @param cbOld
+ * Number of bytes to copy from the old block into the new one.
+ *
+ * @param cbNew
+ * Number of bytes to allocate for the new block.
+ *
+ * @return
+ * A pointer to the allocated new block or NULL in case of an error.
+ * You have to free this memory using DllFreeSplMem.
+ */
+PVOID WINAPI
+ReallocSplMem(PVOID pOldMem, DWORD cbOld, DWORD cbNew)
+{
+    PVOID pNewMem;
+
+    // Always allocate the new block of memory.
+    pNewMem = DllAllocSplMem(cbNew);
+    if (!pNewMem)
+    {
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        return NULL;
+    }
+
+    // Copy the old memory into the new block and free it.
+    if (pOldMem)
+    {
+        CopyMemory(pNewMem, pOldMem, min(cbOld, cbNew));
+        DllFreeSplMem(pOldMem);
+    }
+
+    return pNewMem;
+}
+
+/**
+ * @name ReallocSplStr
+ *
+ * Frees a string allocated by AllocSplStr and copies the given Unicode string into a newly allocated block of memory.
+ *
+ * @param ppwszString
+ * Pointer to the string pointer allocated by AllocSplStr.
+ * When the function returns, the variable receives the pointer to the copied string.
+ *
+ * @param pwszInput
+ * The Unicode string to copy into the new block of memory.
+ *
+ * @return
+ * Returns TRUE in any case.
+*/
+BOOL WINAPI
+ReallocSplStr(PWSTR* ppwszString, PCWSTR pwszInput)
+{
+    if (*ppwszString)
+        DllFreeSplStr(*ppwszString);
+
+    *ppwszString = AllocSplStr(pwszInput);
+    
+    return TRUE;
+}
diff --git a/reactos/win32ss/printing/base/spoolss/monitors.c b/reactos/win32ss/printing/base/spoolss/monitors.c
new file mode 100644 (file)
index 0000000..1a7a258
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Print Monitors
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+BOOL WINAPI
+EnumMonitorsW(PWSTR pName, DWORD Level, PBYTE pMonitors, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    BOOL bReturnValue;
+    DWORD cbCallBuffer;
+    DWORD cbNeeded;
+    DWORD dwReturned;
+    PBYTE pCallBuffer;
+    PSPOOLSS_PRINT_PROVIDER pPrintProvider;
+    PLIST_ENTRY pEntry;
+
+    // Sanity checks.
+    if ((cbBuf && !pMonitors) || !pcbNeeded || !pcReturned)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // Begin counting.
+    *pcbNeeded = 0;
+    *pcReturned = 0;
+
+    // At the beginning, we have the full buffer available.
+    cbCallBuffer = cbBuf;
+    pCallBuffer = pMonitors;
+
+    // Loop through all Print Provider.
+    for (pEntry = PrintProviderList.Flink; pEntry != &PrintProviderList; pEntry = pEntry->Flink)
+    {
+        pPrintProvider = CONTAINING_RECORD(pEntry, SPOOLSS_PRINT_PROVIDER, Entry);
+
+        // Check if this Print Provider provides an EnumMonitors function.
+        if (!pPrintProvider->PrintProvider.fpEnumMonitors)
+            continue;
+
+        // Call the EnumMonitors function of this Print Provider.
+        bReturnValue = pPrintProvider->PrintProvider.fpEnumMonitors(pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
+
+        // Add the returned counts to the total values.
+        *pcbNeeded += cbNeeded;
+        *pcReturned += dwReturned;
+
+        // Reduce the available buffer size for the next call without risking an underflow.
+        if (cbNeeded < cbCallBuffer)
+            cbCallBuffer -= cbNeeded;
+        else
+            cbCallBuffer = 0;
+
+        // Advance the buffer if the caller provided it.
+        if (pCallBuffer)
+            pCallBuffer += cbNeeded;
+
+        // Check if we shall not ask other Print Providers.
+        if (bReturnValue == ROUTER_STOP_ROUTING)
+            break;
+    }
+
+    return bReturnValue;
+}
diff --git a/reactos/win32ss/printing/base/spoolss/ports.c b/reactos/win32ss/printing/base/spoolss/ports.c
new file mode 100644 (file)
index 0000000..52b0b6c
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Ports of the Print Monitors
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+BOOL WINAPI
+EnumPortsW(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    BOOL bReturnValue;
+    DWORD cbCallBuffer;
+    DWORD cbNeeded;
+    DWORD dwReturned;
+    PBYTE pCallBuffer;
+    PSPOOLSS_PRINT_PROVIDER pPrintProvider;
+    PLIST_ENTRY pEntry;
+
+    // Sanity checks.
+    if ((cbBuf && !pPorts) || !pcbNeeded || !pcReturned)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // Begin counting.
+    *pcbNeeded = 0;
+    *pcReturned = 0;
+
+    // At the beginning, we have the full buffer available.
+    cbCallBuffer = cbBuf;
+    pCallBuffer = pPorts;
+
+    // Loop through all Print Provider.
+    for (pEntry = PrintProviderList.Flink; pEntry != &PrintProviderList; pEntry = pEntry->Flink)
+    {
+        pPrintProvider = CONTAINING_RECORD(pEntry, SPOOLSS_PRINT_PROVIDER, Entry);
+
+        // Call the EnumPorts function of this Print Provider.
+        bReturnValue = pPrintProvider->PrintProvider.fpEnumPorts(pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
+
+        // Add the returned counts to the total values.
+        *pcbNeeded += cbNeeded;
+        *pcReturned += dwReturned;
+
+        // Reduce the available buffer size for the next call without risking an underflow.
+        if (cbNeeded < cbCallBuffer)
+            cbCallBuffer -= cbNeeded;
+        else
+            cbCallBuffer = 0;
+
+        // Advance the buffer if the caller provided it.
+        if (pCallBuffer)
+            pCallBuffer += cbNeeded;
+
+        // Check if we shall not ask other Print Providers.
+        if (bReturnValue == ROUTER_STOP_ROUTING)
+            break;
+    }
+
+    return bReturnValue;
+}
diff --git a/reactos/win32ss/printing/base/spoolss/precomp.h b/reactos/win32ss/printing/base/spoolss/precomp.h
new file mode 100644 (file)
index 0000000..b23649c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Precompiled Header for all source files
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#ifndef _PRECOMP_H
+#define _PRECOMP_H
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winreg.h>
+#include <winspool.h>
+#include <winsplp.h>
+#include <ndk/rtlfuncs.h>
+
+#include <spoolss.h>
+
+#include <wine/debug.h>
+WINE_DEFAULT_DEBUG_CHANNEL(spoolss);
+
+// Function pointers
+typedef BOOL (WINAPI *PInitializePrintProvidor)(LPPRINTPROVIDOR, DWORD, LPWSTR);
+
+// Structures
+/**
+ * Describes a Print Provider.
+ */
+typedef struct _SPOOLSS_PRINT_PROVIDER
+{
+    LIST_ENTRY Entry;
+    PRINTPROVIDOR PrintProvider;
+}
+SPOOLSS_PRINT_PROVIDER, *PSPOOLSS_PRINT_PROVIDER;
+
+/*
+ * Describes a handle returned by OpenPrinterW.
+ * We can't just pass the handle returned by the Print Provider, because spoolss has to remember which Print Provider opened this handle.
+ */
+typedef struct _SPOOLSS_PRINTER_HANDLE
+{
+    PSPOOLSS_PRINT_PROVIDER pPrintProvider;         /** Pointer to the Print Provider that opened this printer. */
+    HANDLE hPrinter;                                /** The handle returned by fpOpenPrinter of the Print Provider and passed to subsequent Print Provider functions. */
+}
+SPOOLSS_PRINTER_HANDLE, *PSPOOLSS_PRINTER_HANDLE;
+
+// main.c
+extern HANDLE hProcessHeap;
+extern LIST_ENTRY PrintProviderList;
+
+#endif
diff --git a/reactos/win32ss/printing/base/spoolss/printers.c b/reactos/win32ss/printing/base/spoolss/printers.c
new file mode 100644 (file)
index 0000000..2bbf698
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printers and printing
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+
+BOOL WINAPI
+ClosePrinter(HANDLE hPrinter)
+{
+    BOOL bReturnValue;
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // FIXME: Call FindClosePrinterChangeNotification for all created change notifications (according to MSDN).
+
+    // Call CloseHandle of the Print Provider.
+    bReturnValue = pHandle->pPrintProvider->PrintProvider.fpClosePrinter(pHandle->hPrinter);
+
+    // Free our handle information.
+    DllFreeSplMem(pHandle);
+
+    return bReturnValue;
+}
+
+BOOL WINAPI
+EndDocPrinter(HANDLE hPrinter)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpEndDocPrinter(pHandle->hPrinter);
+}
+
+BOOL WINAPI
+EndPagePrinter(HANDLE hPrinter)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpEndPagePrinter(pHandle->hPrinter);
+}
+
+BOOL WINAPI
+EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    BOOL bReturnValue;
+    DWORD cbCallBuffer;
+    DWORD cbNeeded;
+    DWORD dwReturned;
+    PBYTE pCallBuffer;
+    PSPOOLSS_PRINT_PROVIDER pPrintProvider;
+    PLIST_ENTRY pEntry;
+
+    // Sanity checks.
+    if ((cbBuf && !pPrinterEnum) || !pcbNeeded || !pcReturned)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // Begin counting.
+    *pcbNeeded = 0;
+    *pcReturned = 0;
+
+    // At the beginning, we have the full buffer available.
+    cbCallBuffer = cbBuf;
+    pCallBuffer = pPrinterEnum;
+
+    // Loop through all Print Provider.
+    for (pEntry = PrintProviderList.Flink; pEntry != &PrintProviderList; pEntry = pEntry->Flink)
+    {
+        pPrintProvider = CONTAINING_RECORD(pEntry, SPOOLSS_PRINT_PROVIDER, Entry);
+
+        // Call the EnumPrinters function of this Print Provider.
+        bReturnValue = pPrintProvider->PrintProvider.fpEnumPrinters(Flags, Name, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
+
+        // Add the returned counts to the total values.
+        *pcbNeeded += cbNeeded;
+        *pcReturned += dwReturned;
+
+        // Reduce the available buffer size for the next call without risking an underflow.
+        if (cbNeeded < cbCallBuffer)
+            cbCallBuffer -= cbNeeded;
+        else
+            cbCallBuffer = 0;
+
+        // Advance the buffer if the caller provided it.
+        if (pCallBuffer)
+            pCallBuffer += cbNeeded;
+    }
+
+    return bReturnValue;
+}
+
+BOOL WINAPI
+GetPrinterDriverW(HANDLE hPrinter, PWSTR pEnvironment, DWORD Level, PBYTE pDriverInfo, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpGetPrinterDriver(pHandle->hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
+}
+
+BOOL WINAPI
+GetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpGetPrinter(pHandle->hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
+}
+
+BOOL WINAPI
+OpenPrinterW(PWSTR pPrinterName, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault)
+{
+    BOOL bReturnValue;
+    HANDLE hPrinter;
+    PLIST_ENTRY pEntry;
+    PSPOOLSS_PRINTER_HANDLE pHandle;
+    PSPOOLSS_PRINT_PROVIDER pPrintProvider;
+
+    // Sanity checks.
+    if (!pPrinterName || !phPrinter)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // Loop through all Print Providers to find one able to open this Printer.
+    for (pEntry = PrintProviderList.Flink; pEntry != &PrintProviderList; pEntry = pEntry->Flink)
+    {
+        pPrintProvider = CONTAINING_RECORD(pEntry, SPOOLSS_PRINT_PROVIDER, Entry);
+
+        bReturnValue = pPrintProvider->PrintProvider.fpOpenPrinter(pPrinterName, &hPrinter, pDefault);
+        if (bReturnValue == ROUTER_SUCCESS)
+        {
+            // This Print Provider has opened this Printer.
+            // Store this information and return a handle.
+            pHandle = DllAllocSplMem(sizeof(SPOOLSS_PRINTER_HANDLE));
+            if (!pHandle)
+            {
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+                return FALSE;
+            }
+
+            pHandle->pPrintProvider = pPrintProvider;
+            pHandle->hPrinter = hPrinter;
+            *phPrinter = (HANDLE)pHandle;
+
+            SetLastError(ERROR_SUCCESS);
+            return TRUE;
+        }
+        else if (bReturnValue == ROUTER_STOP_ROUTING)
+        {
+            ERR("A Print Provider returned ROUTER_STOP_ROUTING for Printer \"%S\"!\n", pPrinterName);
+            return FALSE;
+        }
+    }
+
+    // We found no Print Provider able to open this Printer.
+    return FALSE;
+}
+
+BOOL WINAPI
+ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
+}
+
+DWORD WINAPI
+StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpStartDocPrinter(pHandle->hPrinter, Level, pDocInfo);
+}
+
+BOOL WINAPI
+StartPagePrinter(HANDLE hPrinter)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpStartPagePrinter(pHandle->hPrinter);
+}
+
+BOOL WINAPI
+WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
+}
+
+BOOL WINAPI
+XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hXcv;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return pHandle->pPrintProvider->PrintProvider.fpXcvData(pHandle->hPrinter, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus);
+}
diff --git a/reactos/win32ss/printing/base/spoolss/printprocessors.c b/reactos/win32ss/printing/base/spoolss/printprocessors.c
new file mode 100644 (file)
index 0000000..643f72f
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Print Processors
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+BOOL WINAPI
+EnumPrintProcessorDatatypesW(PWSTR pName, PWSTR pPrintProcessorName, DWORD Level, PBYTE pDatatypes, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    // Always call this function on the Local Spooler.
+    PSPOOLSS_PRINT_PROVIDER pPrintProvider = CONTAINING_RECORD(&PrintProviderList.Flink, SPOOLSS_PRINT_PROVIDER, Entry);
+    return pPrintProvider->PrintProvider.fpEnumPrintProcessorDatatypes(pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned);
+}
+
+BOOL WINAPI
+EnumPrintProcessorsW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    // Always call this function on the Local Spooler.
+    PSPOOLSS_PRINT_PROVIDER pPrintProvider = CONTAINING_RECORD(&PrintProviderList.Flink, SPOOLSS_PRINT_PROVIDER, Entry);
+    return pPrintProvider->PrintProvider.fpEnumPrintProcessors(pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded, pcReturned);
+}
+
+BOOL WINAPI
+GetPrintProcessorDirectoryW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    // Always call this function on the Local Spooler.
+    PSPOOLSS_PRINT_PROVIDER pPrintProvider = CONTAINING_RECORD(&PrintProviderList.Flink, SPOOLSS_PRINT_PROVIDER, Entry);
+    return pPrintProvider->PrintProvider.fpGetPrintProcessorDirectory(pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded);
+}
diff --git a/reactos/win32ss/printing/base/spoolss/spoolss.rc b/reactos/win32ss/printing/base/spoolss/spoolss.rc
new file mode 100644 (file)
index 0000000..f14cd12
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION  "ReactOS Spooler Router"
+#define REACTOS_STR_INTERNAL_NAME     "spoolss"
+#define REACTOS_STR_ORIGINAL_FILENAME "spoolss.dll"
+#include <reactos/version.rc>
similarity index 60%
rename from reactos/dll/win32/spoolss/spoolss.spec
rename to reactos/win32ss/printing/base/spoolss/spoolss.spec
index 68188ba..49dbc0d 100644 (file)
@@ -1,23 +1,38 @@
 @ stub AbortPrinter
+@ stub AddDriverCatalog
 @ stub AddFormW
-@ stub AddJobW
-@ stdcall AddMonitorW(wstr long ptr)
+@ stdcall AddJobW(long long ptr long ptr)
+@ stub AddMonitorW
 @ stub AddPerMachineConnectionW
 @ stub AddPortExW
 @ stub AddPortW
-@ stub AddPrintProcessorW
-@ stub AddPrintProvidorW
 @ stub AddPrinterConnectionW
-@ stdcall AddPrinterDriverExW(wstr long ptr long)
+@ stub AddPrinterDriverExW
 @ stub AddPrinterDriverW
 @ stub AddPrinterExW
 @ stub AddPrinterW
-@ stdcall AllocSplStr(wstr)
+@ stub AddPrintProcessorW
+@ stub AddPrintProvidorW
+@ stub AdjustPointers
+@ stub AdjustPointersInStructuresArray
+@ stub AlignKMPtr
+@ stub AlignRpcPtr
+@ stdcall AllocSplStr(ptr)
+@ stub AllowRemoteCalls
 @ stub AppendPrinterNotifyInfoData
-@ stdcall BuildOtherNamesFromMachineName(ptr ptr)
+@ stub bGetDevModePerUser
+@ stub bSetDevModePerUser
+@ stub BuildOtherNamesFromMachineName
+@ stub CacheAddName
+@ stub CacheCreateAndAddNode
+@ stub CacheCreateAndAddNodeWithIPAddresses
+@ stub CacheDeleteNode
+@ stub CacheIsNameCluster
+@ stub CacheIsNameInNodeList
 @ stub CallDrvDevModeConversion
 @ stub CallRouterFindFirstPrinterChangeNotification
-@ stub ClosePrinter
+@ stub CheckLocalCall
+@ stdcall ClosePrinter(long)
 @ stub ClusterSplClose
 @ stub ClusterSplIsAlive
 @ stub ClusterSplOpen
 @ stub CreatePrinterIC
 @ stub DbgGetPointers
 @ stub DeleteFormW
-@ stdcall DeleteMonitorW(wstr wstr wstr)
+@ stub DeleteMonitorW
 @ stub DeletePerMachineConnectionW
 @ stub DeletePortW
-@ stub DeletePrintProcessorW
-@ stub DeletePrintProvidorW
 @ stub DeletePrinter
 @ stub DeletePrinterConnectionW
 @ stub DeletePrinterDataExW
 @ stub DeletePrinterDriverW
 @ stub DeletePrinterIC
 @ stub DeletePrinterKeyW
+@ stub DeletePrintProcessorW
+@ stub DeletePrintProvidorW
 @ stdcall DllAllocSplMem(long)
 @ stdcall DllFreeSplMem(ptr)
-@ stdcall DllFreeSplStr(wstr)
-@ stub EndDocPrinter
-@ stub EndPagePrinter
+@ stdcall DllFreeSplStr(ptr)
+@ stdcall EndDocPrinter(long)
+@ stdcall EndPagePrinter(long)
 @ stub EnumFormsW
-@ stub EnumJobsW
+@ stdcall EnumJobsW(long long long long ptr long ptr ptr)
 @ stdcall EnumMonitorsW(wstr long ptr long ptr ptr)
 @ stub EnumPerMachineConnectionsW
 @ stdcall EnumPortsW(wstr long ptr long ptr ptr)
-@ stub EnumPrintProcessorDatatypesW
-@ stub EnumPrintProcessorsW
 @ stub EnumPrinterDataExW
 @ stub EnumPrinterDataW
 @ stub EnumPrinterDriversW
 @ stub EnumPrinterKeyW
-@ stub EnumPrintersW
+@ stdcall EnumPrintersW(long wstr long ptr long ptr ptr)
+@ stdcall EnumPrintProcessorDatatypesW(wstr wstr long ptr long ptr ptr)
+@ stdcall EnumPrintProcessorsW(wstr wstr long ptr long ptr ptr)
 @ stub FindClosePrinterChangeNotification
 @ stub FlushPrinter
 @ stub FormatPrinterForRegistryKey
 @ stub GetClientUserHandle
 @ stub GetFormW
 @ stub GetJobAttributes
-@ stub GetJobW
+@ stdcall GetJobW(long long long ptr long ptr)
 @ stub GetNetworkId
-@ stub GetPrintProcessorDirectoryW
 @ stub GetPrinterDataExW
 @ stub GetPrinterDataW
-@ stdcall GetPrinterDriverDirectoryW(wstr wstr long ptr long ptr)
+@ stub GetPrinterDriverDirectoryW
 @ stub GetPrinterDriverExW
-@ stub GetPrinterDriverW
-@ stub GetPrinterW
+@ stdcall GetPrinterDriverW(long wstr long ptr long ptr)
+@ stdcall GetPrinterW(long long ptr long ptr)
+@ stdcall GetPrintProcessorDirectoryW(wstr wstr long ptr long ptr)
+@ stub GetServerPolicy
+@ stub GetShrinkedSize
 @ stdcall ImpersonatePrinterClient(long)
-@ stdcall InitializeRouter()
-@ stdcall IsLocalCall()
+@ stdcall InitializeRouter(long)
 @ stub IsNamedPipeRpcCall
 @ stub LoadDriver
 @ stub LoadDriverFiletoConvertDevmode
+@ stub LoadDriverWithVersion
+@ stub LogWmiTraceEvent
+@ stdcall MarshallDownStructure(ptr ptr long long)
+@ stub MarshallDownStructuresArray
+@ stub MarshallUpStructure
+@ stub MarshallUpStructuresArray
 @ stub MIDL_user_allocate1
 @ stub MIDL_user_free1
-@ stub MarshallDownStructure
-@ stub MarshallUpStructure
 @ stub OldGetPrinterDriverW
 @ stub OpenPrinterExW
 @ stub OpenPrinterPortW
-@ stub OpenPrinterW
-@ stub PackStrings
+@ stdcall OpenPrinterW(wstr ptr ptr)
+@ stdcall PackStrings(ptr ptr ptr ptr)
 @ stub PartialReplyPrinterChangeNotification
 @ stub PlayGdiScriptOnPrinterIC
 @ stub PrinterHandleRundown
 @ stub PrinterMessageBoxW
 @ stub ProvidorFindClosePrinterChangeNotification
 @ stub ProvidorFindFirstPrinterChangeNotification
-@ stub ReadPrinter
-@ stub ReallocSplMem
-@ stub ReallocSplStr
+@ stub pszDbgAllocMsgA
+@ stdcall ReadPrinter(long ptr long ptr)
+@ stdcall ReallocSplMem(ptr long long)
+@ stdcall ReallocSplStr(ptr ptr)
 @ stub RemoteFindFirstPrinterChangeNotification
 @ stub ReplyClosePrinter
 @ stub ReplyOpenPrinter
 @ stub ReplyPrinterChangeNotification
 @ stub ResetPrinterW
 @ stdcall RevertToPrinterSelf()
+@ stub RouterAllocBidiMem
+@ stub RouterAllocBidiResponseContainer
 @ stub RouterAllocPrinterNotifyInfo
 @ stub RouterFindFirstPrinterChangeNotification
 @ stub RouterFindNextPrinterChangeNotification
+@ stub RouterFreeBidiMem
 @ stub RouterFreePrinterNotifyInfo
 @ stub RouterRefreshPrinterChangeNotification
 @ stub RouterReplyPrinter
-@ stub ScheduleJob
+@ stdcall ScheduleJob(long long)
 @ stub SeekPrinter
+@ stub SendRecvBidiData
 @ stub SetAllocFailCount
 @ stub SetFormW
-@ stub SetJobW
+@ stdcall SetJobW(long long long ptr long)
 @ stub SetPortW
 @ stub SetPrinterDataExW
 @ stub SetPrinterDataW
 @ stub SplDriverUnloadComplete
 @ stub SplGetSpoolFileInfo
 @ stdcall SplInitializeWinSpoolDrv(ptr)
+@ stub SplIsSessionZero
 @ stdcall SplIsUpgrade()
+@ stub SplPowerEvent
 @ stub SplProcessPnPEvent
+@ stub SplPromptUIInUsersSession
 @ stub SplReadPrinter
 @ stub SplRegisterForDeviceEvents
-@ stub SplStartPhase2Init
+@ stub SplShutDownRouter
 @ stub SplUnregisterForDeviceEvents
 @ stub SpoolerFindClosePrinterChangeNotification
 @ stub SpoolerFindFirstPrinterChangeNotification
 @ stub SpoolerFindNextPrinterChangeNotification
 @ stub SpoolerFreePrinterNotifyInfo
-@ stdcall SpoolerHasInitialized()
+@ stub SpoolerHasInitialized
 @ stdcall SpoolerInit()
-@ stub StartDocPrinterW
-@ stub StartPagePrinter
+@ stdcall StartDocPrinterW(long long ptr)
+@ stdcall StartPagePrinter(long)
+@ stub UndoAlignKMPtr
+@ stub UndoAlignRpcPtr
 @ stub UnloadDriver
 @ stub UnloadDriverFile
 @ stub UpdateBufferSize
 @ stub UpdatePrinterRegAll
 @ stub UpdatePrinterRegUser
-@ stub WaitForPrinterChange
-@ stdcall WaitForSpoolerInitialization()
-@ stub WritePrinter
-@ stub XcvDataW
-@ stub bGetDevModePerUser
-@ stub bSetDevModePerUser
-@ stub pszDbgAllocMsgA
 @ stub vDbgLogError
+@ stub WaitForPrinterChange
+@ stub WaitForSpoolerInitialization
+@ stdcall WritePrinter(long ptr long ptr)
+@ stdcall XcvDataW(long wstr ptr long ptr long ptr ptr)
diff --git a/reactos/win32ss/printing/base/spoolss/tools.c b/reactos/win32ss/printing/base/spoolss/tools.c
new file mode 100644 (file)
index 0000000..5113600
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Miscellaneous tool functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+
+/**
+ * @name MarshallDownStructure
+ *
+ * Prepare a structure for marshalling/serialization by replacing absolute pointer addresses in its fields by relative offsets.
+ *
+ * @param pStructure
+ * Pointer to the structure to operate on.
+ *
+ * @param pParameters
+ * Array of MARSHALL_DOWN_INFO elements containing information about the fields of the structure as well as how to modify them.
+ * See the documentation on MARSHALL_DOWN_INFO for more information.
+ * You have to indicate the end of the array by setting the dwOffset field to MAXDWORD.
+ *
+ * @param cbStructureSize
+ * Apparently, this is the size in bytes of the structure given through pStructure under Windows.
+ * This parameter is unused in my implementation.
+ *
+ * @param bSomeBoolean
+ * Unknown boolean value
+ *
+ * @return
+ * TRUE if the structure was successfully adjusted, FALSE otherwise.
+ */
+BOOL WINAPI
+MarshallDownStructure(PVOID pStructure, PMARSHALL_DOWN_INFO pParameters, DWORD cbStructureSize, BOOL bSomeBoolean)
+{
+    // Sanity checks
+    if (!pStructure || !pParameters)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // Loop until we reach an element with offset set to MAXDWORD.
+    while (pParameters->dwOffset != MAXDWORD)
+    {
+        if (pParameters->bAdjustAddress)
+        {
+            // Apply the byte offset on pStructure. There must be a pointer at this position, whose address we're adjusting
+            // by subtracting the address of pStructure from it.
+            *((PULONG_PTR)((PBYTE)pStructure + pParameters->dwOffset)) -= (ULONG_PTR)pStructure;
+        }
+
+        // Advance to the next element description.
+        pParameters++;
+    }
+
+    return TRUE;
+}
+
+/**
+ * @name PackStrings
+ *
+ * Takes an array of Unicode strings and fills an output buffer with these strings at the end and pointers to each string at specific offsets.
+ * Useful helper for functions that copy an information structure including strings into a given buffer (like PRINTER_INFO_1).
+ *
+ * @param pSource
+ * The array of Unicode strings to copy. Needs to have at least as many elements as the DestOffsets array.
+ *
+ * @param pDest
+ * Pointer to the beginning of the output buffer.
+ * The caller is responsible for verifying that this buffer is large enough to hold all strings and pointers.
+ *
+ * @param DestOffsets
+ * Array of byte offsets in the output buffer. For each element of DestOffsets, the function will copy the address of the corresponding copied
+ * string of pSource to this location in the output buffer. If a string in pSource is NULL, the function will set the pointer address to NULL
+ * in the output buffer.
+ * Use macros like FIELD_OFFSET to calculate the offsets for this array.
+ * The last element of the array must have the value MAXDWORD to let the function detect the end of the array.
+ *
+ * @param pEnd
+ * Pointer to the end of the output buffer. That means the first element outside of the buffer given in pDest.
+ *
+ * @return
+ * Returns a pointer to the beginning of the strings in pDest.
+ * The strings are copied in reverse order, so this pointer will point to the last copied string of pSource.
+ */
+PBYTE WINAPI
+PackStrings(PCWSTR* pSource, PBYTE pDest, PDWORD DestOffsets, PBYTE pEnd)
+{
+    DWORD cbString;
+    ULONG_PTR StringAddress;
+
+    // Loop until we reach an element with offset set to MAXDWORD.
+    while (*DestOffsets != MAXDWORD)
+    {
+        StringAddress = 0;
+
+        if (*pSource)
+        {
+            // Determine the length of the source string.
+            cbString = (wcslen(*pSource) + 1) * sizeof(WCHAR);
+
+            // Copy it before the last string.
+            pEnd -= cbString;
+            StringAddress = (ULONG_PTR)pEnd;
+            CopyMemory(pEnd, *pSource, cbString);
+        }
+
+        // Copy the address of the copied string to the location given by the offset.
+        CopyMemory(&pDest[*DestOffsets], &StringAddress, sizeof(ULONG_PTR));
+
+        // Advance to the next source string and destination offset.
+        pSource++;
+        DestOffsets++;
+    }
+
+    // pEnd is now at the last string we copied. Return this value as a pointer to the beginning of all strings in the output buffer.
+    return pEnd;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/CMakeLists.txt b/reactos/win32ss/printing/base/spoolsv/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6467f3b
--- /dev/null
@@ -0,0 +1,30 @@
+
+include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/idl)
+add_rpc_files(server ${REACTOS_SOURCE_DIR}/sdk/include/reactos/idl/winspool.idl)
+
+list(APPEND SOURCE
+    forms.c
+    init.c
+    jobs.c
+    main.c
+    monitors.c
+    notifications.c
+    ports.c
+    precomp.h
+    printerdata.c
+    printerdrivers.c
+    printers.c
+    printprocessors.c
+    printproviders.c
+    rpcserver.c
+    rpcstubs.c
+    xcv.c
+    ${CMAKE_CURRENT_BINARY_DIR}/winspool_s.c)
+
+add_executable(spoolsv ${SOURCE} spoolsv.rc)
+set_module_type(spoolsv win32cui UNICODE)
+target_link_libraries(spoolsv wine)
+add_delay_importlibs(spoolsv spoolss)
+add_importlibs(spoolsv advapi32 rpcrt4 msvcrt kernel32 ntdll)
+add_pch(spoolsv precomp.h SOURCE)
+add_cd_file(TARGET spoolsv DESTINATION reactos/system32 FOR all)
diff --git a/reactos/win32ss/printing/base/spoolsv/forms.c b/reactos/win32ss/printing/base/spoolsv/forms.c
new file mode 100644 (file)
index 0000000..45950e7
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Forms
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD
+_RpcAddForm(WINSPOOL_PRINTER_HANDLE hPrinter, WINSPOOL_FORM_CONTAINER* pFormInfoContainer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeleteForm(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pFormName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcEnumForms(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD Level, BYTE* pForm, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcGetForm(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pFormName, DWORD Level, BYTE* pForm, DWORD cbBuf, DWORD* pcbNeeded)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcSetForm(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pFormName, WINSPOOL_FORM_CONTAINER* pFormInfoContainer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/init.c b/reactos/win32ss/printing/base/spoolsv/init.c
new file mode 100644 (file)
index 0000000..435b24e
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Various initialization functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD
+_RpcSpoolerInit()
+{
+    DWORD dwErrorCode;
+
+    // Call SpoolerInit in the security context of the client.
+    // This delay-loads spoolss.dll in the user context and all further calls to functions in spoolss.dll will be done in the user context as well.
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    SpoolerInit();
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/jobs.c b/reactos/win32ss/printing/base/spoolsv/jobs.c
new file mode 100644 (file)
index 0000000..1a2843a
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions for managing print jobs
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static void
+_MarshallDownAddJobInfo(PADDJOB_INFO_1W pAddJobInfo1)
+{
+    // Replace absolute pointer addresses in the output by relative offsets.
+    pAddJobInfo1->Path = (PWSTR)((ULONG_PTR)pAddJobInfo1->Path - (ULONG_PTR)pAddJobInfo1);
+}
+
+static void
+_MarshallDownJobInfo(PBYTE pJobInfo, DWORD Level)
+{
+    PJOB_INFO_1W pJobInfo1;
+    PJOB_INFO_2W pJobInfo2;
+
+    // Replace absolute pointer addresses in the output by relative offsets.
+    if (Level == 1)
+    {
+        pJobInfo1 = (PJOB_INFO_1W)pJobInfo;
+        pJobInfo1->pDatatype = (PWSTR)((ULONG_PTR)pJobInfo1->pDatatype - (ULONG_PTR)pJobInfo1);
+        pJobInfo1->pDocument = (PWSTR)((ULONG_PTR)pJobInfo1->pDocument - (ULONG_PTR)pJobInfo1);
+        pJobInfo1->pMachineName = (PWSTR)((ULONG_PTR)pJobInfo1->pMachineName - (ULONG_PTR)pJobInfo1);
+        pJobInfo1->pPrinterName = (PWSTR)((ULONG_PTR)pJobInfo1->pPrinterName - (ULONG_PTR)pJobInfo1);
+
+        if (pJobInfo1->pStatus)
+            pJobInfo1->pStatus = (PWSTR)((ULONG_PTR)pJobInfo1->pStatus - (ULONG_PTR)pJobInfo1);
+
+        pJobInfo1->pUserName = (PWSTR)((ULONG_PTR)pJobInfo1->pUserName - (ULONG_PTR)pJobInfo1);
+    }
+    else if (Level == 2)
+    {
+        pJobInfo2 = (PJOB_INFO_2W)pJobInfo;
+        pJobInfo2->pDatatype = (PWSTR)((ULONG_PTR)pJobInfo2->pDatatype - (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pDevMode = (PDEVMODEW)((ULONG_PTR)pJobInfo2->pDevMode - (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pDocument = (PWSTR)((ULONG_PTR)pJobInfo2->pDocument - (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pDriverName = (PWSTR)((ULONG_PTR)pJobInfo2->pDriverName - (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pMachineName = (PWSTR)((ULONG_PTR)pJobInfo2->pMachineName - (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pNotifyName = (PWSTR)((ULONG_PTR)pJobInfo2->pNotifyName - (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pPrinterName = (PWSTR)((ULONG_PTR)pJobInfo2->pPrinterName - (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pPrintProcessor = (PWSTR)((ULONG_PTR)pJobInfo2->pPrintProcessor - (ULONG_PTR)pJobInfo2);
+
+        if (pJobInfo2->pParameters)
+            pJobInfo2->pParameters = (PWSTR)((ULONG_PTR)pJobInfo2->pParameters - (ULONG_PTR)pJobInfo2);
+
+        if (pJobInfo2->pStatus)
+            pJobInfo2->pStatus = (PWSTR)((ULONG_PTR)pJobInfo2->pStatus - (ULONG_PTR)pJobInfo2);
+
+        pJobInfo2->pUserName = (PWSTR)((ULONG_PTR)pJobInfo2->pUserName - (ULONG_PTR)pJobInfo2);
+    }
+}
+
+DWORD
+_RpcAddJob(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD Level, BYTE* pAddJob, DWORD cbBuf, DWORD* pcbNeeded)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    AddJobW(hPrinter, Level, pAddJob, cbBuf, pcbNeeded);
+    dwErrorCode = GetLastError();
+
+    if (dwErrorCode == ERROR_SUCCESS)
+        _MarshallDownAddJobInfo((PADDJOB_INFO_1W)pAddJob);
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcEnumJobs(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, BYTE* pJob, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pJob;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    EnumJobsW(hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned);
+    dwErrorCode = GetLastError();
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace absolute pointer addresses in the output by relative offsets.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallDownJobInfo(p, Level);
+
+            if (Level == 1)
+                p += sizeof(JOB_INFO_1W);
+            else if (Level == 2)
+                p += sizeof(JOB_INFO_2W);
+        }
+    }
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcGetJob(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD JobId, DWORD Level, BYTE* pJob, DWORD cbBuf, DWORD* pcbNeeded)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    GetJobW(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
+    dwErrorCode = GetLastError();
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace absolute pointer addresses in the output by relative offsets.
+        _MarshallDownJobInfo(pJob, Level);
+    }
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcScheduleJob(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD JobId)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    ScheduleJob(hPrinter, JobId);
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcSetJob(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD JobId, WINSPOOL_JOB_CONTAINER* pJobContainer, DWORD Command)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    // pJobContainer->JobInfo is a union of pointers, so we can just convert any element to our BYTE pointer.
+    SetJobW(hPrinter, JobId, pJobContainer->Level, (PBYTE)pJobContainer->JobInfo.Level1, Command);
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/main.c b/reactos/win32ss/printing/base/spoolsv/main.c
new file mode 100644 (file)
index 0000000..8e25b36
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Main functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+SERVICE_STATUS_HANDLE hServiceStatus;
+SERVICE_STATUS ServiceStatus;
+WCHAR wszServiceName[] = L"Spooler";
+
+static void
+_UpdateServiceStatus(DWORD dwNewStatus, DWORD dwCheckPoint)
+{
+    ServiceStatus.dwCheckPoint = dwCheckPoint;
+    ServiceStatus.dwCurrentState = dwNewStatus;
+    SetServiceStatus(hServiceStatus, &ServiceStatus);
+}
+
+static DWORD WINAPI
+_ServiceControlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
+{
+    switch (dwControl)
+    {
+        case SERVICE_CONTROL_SHUTDOWN:
+        case SERVICE_CONTROL_STOP:
+            _UpdateServiceStatus(SERVICE_STOP_PENDING, 1);
+            RpcMgmtStopServerListening(NULL);
+            _UpdateServiceStatus(SERVICE_STOPPED, 0);
+            return NO_ERROR;
+
+        case SERVICE_CONTROL_INTERROGATE:
+            return NO_ERROR;
+
+        default:
+            return ERROR_CALL_NOT_IMPLEMENTED;
+    }
+}
+
+static VOID WINAPI
+_ServiceMain(DWORD dwArgc, LPWSTR* lpszArgv)
+{
+    HANDLE hThread;
+
+    UNREFERENCED_PARAMETER(dwArgc);
+    UNREFERENCED_PARAMETER(lpszArgv);
+
+    // Register our service for control
+    hServiceStatus = RegisterServiceCtrlHandlerExW(wszServiceName, _ServiceControlHandlerEx, NULL);
+
+    // Report initial SERVICE_START_PENDING status
+    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+    ServiceStatus.dwServiceSpecificExitCode = 0;
+    ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+    ServiceStatus.dwWaitHint = 4000;
+    ServiceStatus.dwWin32ExitCode = NO_ERROR;
+    _UpdateServiceStatus(SERVICE_START_PENDING, 0);
+
+    // Create a thread for serving RPC requests
+    hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)LrpcThreadProc, NULL, 0, NULL);
+    if (!hThread)
+    {
+        ERR("CreateThread failed with error %u!\n", GetLastError());
+        _UpdateServiceStatus(SERVICE_STOPPED, 0);
+        return;
+    }
+
+    // We don't need the thread handle. Keeping it open blocks the thread from terminating.
+    CloseHandle(hThread);
+
+    // Initialize the routing layer in spoolss.dll
+    if (!InitializeRouter(hServiceStatus))
+    {
+        ERR("InitializeRouter failed with error %lu!\n", GetLastError());
+        _UpdateServiceStatus(SERVICE_STOPPED, 0);
+        return;
+    }
+
+    // We're alive!
+    _UpdateServiceStatus(SERVICE_RUNNING, 0);
+}
+
+int
+wmain(int argc, WCHAR* argv[])
+{
+    SERVICE_TABLE_ENTRYW ServiceTable[] =
+    {
+        {wszServiceName, _ServiceMain},
+        {NULL, NULL}
+    };
+
+    UNREFERENCED_PARAMETER(argc);
+    UNREFERENCED_PARAMETER(argv);
+
+    StartServiceCtrlDispatcherW(ServiceTable);
+
+    return 0;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/monitors.c b/reactos/win32ss/printing/base/spoolsv/monitors.c
new file mode 100644 (file)
index 0000000..1f2bc72
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Print Monitors
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static void
+_MarshallDownMonitorInfo(PBYTE pMonitorInfo, DWORD Level)
+{
+    PMONITOR_INFO_2W pMonitorInfo2 = (PMONITOR_INFO_2W)pMonitorInfo;        // MONITOR_INFO_1W is a subset of MONITOR_INFO_2W
+
+    // Replace absolute pointer addresses in the output by relative offsets.
+    pMonitorInfo2->pName = (PWSTR)((ULONG_PTR)pMonitorInfo2->pName - (ULONG_PTR)pMonitorInfo2);
+
+    if (Level == 2)
+    {
+        pMonitorInfo2->pDLLName = (PWSTR)((ULONG_PTR)pMonitorInfo2->pDLLName - (ULONG_PTR)pMonitorInfo2);
+        pMonitorInfo2->pEnvironment = (PWSTR)((ULONG_PTR)pMonitorInfo2->pEnvironment - (ULONG_PTR)pMonitorInfo2);
+    }
+}
+
+DWORD
+_RpcAddMonitor(WINSPOOL_HANDLE pName, WINSPOOL_MONITOR_CONTAINER* pMonitorContainer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeleteMonitor(WINSPOOL_HANDLE pName, WCHAR* pEnvironment, WCHAR* pMonitorName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcEnumMonitors(WINSPOOL_HANDLE pName, DWORD Level, BYTE* pMonitor, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pMonitor;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    EnumMonitorsW(pName, Level, pMonitor, cbBuf, pcbNeeded, pcReturned);
+    dwErrorCode = GetLastError();
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace absolute pointer addresses in the output by relative offsets.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallDownMonitorInfo(p, Level);
+
+            if (Level == 1)
+                p += sizeof(MONITOR_INFO_1W);
+            else if (Level == 2)
+                p += sizeof(MONITOR_INFO_2W);
+        }
+    }
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/notifications.c b/reactos/win32ss/printing/base/spoolsv/notifications.c
new file mode 100644 (file)
index 0000000..8b99825
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printer Configuration Data
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD
+_RpcClientFindFirstPrinterChangeNotification()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcFindClosePrinterChangeNotification()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcFindNextPrinterChangeNotification()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcRemoteFindFirstPrinterChangeNotification(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD fdwFlags, DWORD fdwOptions, WCHAR* pszLocalMachine, DWORD dwPrinterLocal, DWORD cbBuffer, BYTE* pBuffer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcRemoteFindFirstPrinterChangeNotificationEx(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD fdwFlags, DWORD fdwOptions, WCHAR* pszLocalMachine, DWORD dwPrinterLocal, WINSPOOL_V2_NOTIFY_OPTIONS* pOptions)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcReplyClosePrinter(WINSPOOL_PRINTER_HANDLE* phNotify)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcReplyOpenPrinter(WINSPOOL_HANDLE pMachine, WINSPOOL_PRINTER_HANDLE* phPrinterNotify, DWORD dwPrinterRemote, DWORD dwType, DWORD cbBuffer, BYTE* pBuffer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcRouterFindFirstPrinterChangeNotificationOld()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcRouterRefreshPrinterChangeNotification(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD dwColor, WINSPOOL_V2_NOTIFY_OPTIONS* pOptions, WINSPOOL_V2_NOTIFY_INFO** ppInfo)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcRouterReplyPrinter(WINSPOOL_PRINTER_HANDLE hNotify, DWORD fdwFlags, DWORD cbBuffer, BYTE* pBuffer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcRouterReplyPrinterEx(WINSPOOL_PRINTER_HANDLE hNotify, DWORD dwColor, DWORD fdwFlags, DWORD* pdwResult, DWORD dwReplyType, WINSPOOL_V2_UREPLY_PRINTER Reply)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcWaitForPrinterChange(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD Flags, DWORD* pFlags)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/ports.c b/reactos/win32ss/printing/base/spoolsv/ports.c
new file mode 100644 (file)
index 0000000..cb3d993
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Ports
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static void
+_MarshallDownPortInfo(PBYTE pPortInfo, DWORD Level)
+{
+    PPORT_INFO_2W pPortInfo2 = (PPORT_INFO_2W)pPortInfo;         // PORT_INFO_1W is a subset of PORT_INFO_2W
+
+    // Replace absolute pointer addresses in the output by relative offsets.
+    pPortInfo2->pPortName = (PWSTR)((ULONG_PTR)pPortInfo2->pPortName - (ULONG_PTR)pPortInfo2);
+
+    if (Level == 2)
+    {
+        pPortInfo2->pDescription = (PWSTR)((ULONG_PTR)pPortInfo2->pDescription - (ULONG_PTR)pPortInfo2);
+        pPortInfo2->pMonitorName = (PWSTR)((ULONG_PTR)pPortInfo2->pMonitorName - (ULONG_PTR)pPortInfo2);
+    }
+}
+
+DWORD
+_RpcAddPort(WINSPOOL_HANDLE pName, ULONG_PTR hWnd, WCHAR* pMonitorName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcAddPortEx(WINSPOOL_HANDLE pName, WINSPOOL_PORT_CONTAINER* pPortContainer, WINSPOOL_PORT_VAR_CONTAINER* pPortVarContainer, WCHAR* pMonitorName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcConfigurePort(WINSPOOL_HANDLE pName, ULONG_PTR hWnd, WCHAR* pPortName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeletePort(WINSPOOL_HANDLE pName, ULONG_PTR hWnd, WCHAR* pPortName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcEnumPorts(WINSPOOL_HANDLE pName, DWORD Level, BYTE* pPort, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pPort;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    EnumPortsW(pName, Level, pPort, cbBuf, pcbNeeded, pcReturned);
+    dwErrorCode = GetLastError();
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace absolute pointer addresses in the output by relative offsets.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallDownPortInfo(p, Level);
+
+            if (Level == 1)
+                p += sizeof(PORT_INFO_1W);
+            else if (Level == 2)
+                p += sizeof(PORT_INFO_2W);
+        }
+    }
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcSetPort(WINSPOOL_HANDLE pName, WCHAR* pPortName, WINSPOOL_PORT_CONTAINER* pPortContainer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/precomp.h b/reactos/win32ss/printing/base/spoolsv/precomp.h
new file mode 100644 (file)
index 0000000..b882f98
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Precompiled Header for all source files
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#ifndef _PRECOMP_H
+#define _PRECOMP_H
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <winreg.h>
+#include <winsvc.h>
+#include <wingdi.h>
+#include <winspool.h>
+#include <winsplp.h>
+#include <winspool_s.h>
+
+#include <wine/debug.h>
+WINE_DEFAULT_DEBUG_CHANNEL(spoolsv);
+
+// rpcserver.c
+DWORD WINAPI LrpcThreadProc(LPVOID lpParameter);
+
+// Undocumented spoolss
+BOOL WINAPI InitializeRouter(HANDLE SpoolerStatusHandle);
+DWORD WINAPI SpoolerInit();
+
+#endif
diff --git a/reactos/win32ss/printing/base/spoolsv/printerdata.c b/reactos/win32ss/printing/base/spoolsv/printerdata.c
new file mode 100644 (file)
index 0000000..1ffb0af
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printer Configuration Data
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD
+_RpcDeletePrinterData(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pValueName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeletePrinterDataEx(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, const WCHAR* pValueName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeletePrinterKey(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcEnumPrinterData(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD dwIndex, WCHAR* pValueName, DWORD cbValueName, DWORD* pcbValueName, DWORD* pType, BYTE* pData, DWORD cbData, DWORD* pcbData)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcEnumPrinterKey(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, WCHAR* pSubkey, DWORD cbSubkey, DWORD* pcbSubkey)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcEnumPrinterDataEx(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, BYTE* pEnumValues, DWORD cbEnumValues, DWORD* pcbEnumValues, DWORD* pnEnumValues)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcGetPrinterData(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pValueName, DWORD* pType, BYTE* pData, DWORD nSize, DWORD* pcbNeeded)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcGetPrinterDataEx(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, const WCHAR* pValueName, DWORD* pType, BYTE* pData, DWORD nSize, DWORD* pcbNeeded)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcSetPrinterData(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pValueName, DWORD Type, BYTE* pData, DWORD cbData)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcSetPrinterDataEx(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, const WCHAR* pValueName, DWORD Type, BYTE* pData, DWORD cbData)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/printerdrivers.c b/reactos/win32ss/printing/base/spoolsv/printerdrivers.c
new file mode 100644 (file)
index 0000000..a56f0db
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printer Drivers
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD
+_RpcAddPrinterDriver(WINSPOOL_HANDLE pName, WINSPOOL_DRIVER_CONTAINER* pDriverContainer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcAddPrinterDriverEx(WINSPOOL_HANDLE pName, WINSPOOL_DRIVER_CONTAINER* pDriverContainer, DWORD dwFileCopyFlags)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeletePrinterDriver(WINSPOOL_HANDLE pName, WCHAR* pEnvironment, WCHAR* pDriverName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeletePrinterDriverEx(WINSPOOL_HANDLE pName, WCHAR* pEnvironment, WCHAR* pDriverName, DWORD dwDeleteFlag, DWORD dwVersionNum)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcEnumPrinterDrivers(WINSPOOL_HANDLE pName, WCHAR* pEnvironment, DWORD Level, BYTE* pDrivers, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcGetPrinterDriver(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pEnvironment, DWORD Level, BYTE* pDriver, DWORD cbBuf, DWORD* pcbNeeded)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcGetPrinterDriver2(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pEnvironment, DWORD Level, BYTE* pDriver, DWORD cbBuf, DWORD* pcbNeeded, DWORD dwClientMajorVersion, DWORD dwClientMinorVersion, DWORD* pdwServerMaxVersion, DWORD* pdwServerMinVersion)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcGetPrinterDriverDirectory(WINSPOOL_HANDLE pName, WCHAR* pEnvironment, DWORD Level, BYTE* pDriverDirectory, DWORD cbBuf, DWORD* pcbNeeded)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/printers.c b/reactos/win32ss/printing/base/spoolsv/printers.c
new file mode 100644 (file)
index 0000000..fb20f04
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printers and printing
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static void
+_MarshallDownPrinterInfo(PBYTE pPrinterInfo, DWORD Level)
+{
+    PPRINTER_INFO_1W pPrinterInfo1;
+    PPRINTER_INFO_2W pPrinterInfo2;
+
+    // Replace absolute pointer addresses in the output by relative offsets.
+    if (Level == 1)
+    {
+        pPrinterInfo1 = (PPRINTER_INFO_1W)pPrinterInfo;
+
+        pPrinterInfo1->pName = (PWSTR)((ULONG_PTR)pPrinterInfo1->pName - (ULONG_PTR)pPrinterInfo1);
+        pPrinterInfo1->pDescription = (PWSTR)((ULONG_PTR)pPrinterInfo1->pDescription - (ULONG_PTR)pPrinterInfo1);
+        pPrinterInfo1->pComment = (PWSTR)((ULONG_PTR)pPrinterInfo1->pComment - (ULONG_PTR)pPrinterInfo1);
+    }
+    else if (Level == 2)
+    {
+        pPrinterInfo2 = (PPRINTER_INFO_2W)pPrinterInfo;
+
+        pPrinterInfo2->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPrinterName - (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pShareName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pShareName - (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pPortName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPortName - (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pDriverName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pDriverName - (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pComment = (PWSTR)((ULONG_PTR)pPrinterInfo2->pComment - (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pLocation = (PWSTR)((ULONG_PTR)pPrinterInfo2->pLocation - (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pDevMode = (PDEVMODEW)((ULONG_PTR)pPrinterInfo2->pDevMode - (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pSepFile = (PWSTR)((ULONG_PTR)pPrinterInfo2->pSepFile - (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pPrintProcessor = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPrintProcessor - (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pDatatype = (PWSTR)((ULONG_PTR)pPrinterInfo2->pDatatype - (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pParameters = (PWSTR)((ULONG_PTR)pPrinterInfo2->pParameters - (ULONG_PTR)pPrinterInfo2);
+
+        if (pPrinterInfo2->pServerName)
+            pPrinterInfo2->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pServerName - (ULONG_PTR)pPrinterInfo2);
+
+        if (pPrinterInfo2->pSecurityDescriptor)
+            pPrinterInfo2->pSecurityDescriptor = (PWSTR)((ULONG_PTR)pPrinterInfo2->pSecurityDescriptor - (ULONG_PTR)pPrinterInfo2);
+    }
+}
+
+DWORD
+_RpcAbortPrinter(WINSPOOL_PRINTER_HANDLE hPrinter)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcAddPrinter(WINSPOOL_HANDLE pName, WINSPOOL_PRINTER_CONTAINER* pPrinterContainer, WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer, WINSPOOL_SECURITY_CONTAINER* pSecurityContainer, WINSPOOL_PRINTER_HANDLE* pHandle)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcAddPrinterEx(WINSPOOL_HANDLE pName, WINSPOOL_PRINTER_CONTAINER* pPrinterContainer, WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer, WINSPOOL_SECURITY_CONTAINER* pSecurityContainer, WINSPOOL_SPLCLIENT_CONTAINER* pClientInfo, WINSPOOL_PRINTER_HANDLE* pHandle)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcClosePrinter(WINSPOOL_PRINTER_HANDLE* phPrinter)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    if (ClosePrinter(*phPrinter))
+        *phPrinter = NULL;
+
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcDeletePrinter(WINSPOOL_PRINTER_HANDLE hPrinter)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcEndDocPrinter(WINSPOOL_PRINTER_HANDLE hPrinter)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    EndDocPrinter(hPrinter);
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcEndPagePrinter(WINSPOOL_PRINTER_HANDLE hPrinter)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    EndPagePrinter(hPrinter);
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcEnumPrinters(DWORD Flags, WINSPOOL_HANDLE Name, DWORD Level, BYTE* pPrinterEnum, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pPrinterEnum;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    EnumPrintersW(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+    dwErrorCode = GetLastError();
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace absolute pointer addresses in the output by relative offsets.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallDownPrinterInfo(p, Level);
+
+            if (Level == 1)
+                p += sizeof(PRINTER_INFO_1W);
+            else if (Level == 2)
+                p += sizeof(PRINTER_INFO_2W);
+        }
+    }
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcFlushPrinter(WINSPOOL_PRINTER_HANDLE hPrinter, BYTE* pBuf, DWORD cbBuf, DWORD* pcWritten, DWORD cSleep)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcGetPrinter(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD Level, BYTE* pPrinter, DWORD cbBuf, DWORD* pcbNeeded)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcOpenPrinter(WINSPOOL_HANDLE pPrinterName, WINSPOOL_PRINTER_HANDLE* phPrinter, WCHAR* pDatatype, WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer, DWORD AccessRequired)
+{
+    DWORD dwErrorCode;
+    PRINTER_DEFAULTSW Default;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    Default.DesiredAccess = AccessRequired;
+    Default.pDatatype = pDatatype;
+    Default.pDevMode = (PDEVMODEW)pDevModeContainer->pDevMode;
+
+    OpenPrinterW(pPrinterName, phPrinter, &Default);
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcOpenPrinterEx(WINSPOOL_HANDLE pPrinterName, WINSPOOL_PRINTER_HANDLE* pHandle, WCHAR* pDatatype, WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer, DWORD AccessRequired, WINSPOOL_SPLCLIENT_CONTAINER* pClientInfo)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcReadPrinter(WINSPOOL_PRINTER_HANDLE hPrinter, BYTE* pBuf, DWORD cbBuf, DWORD* pcNoBytesRead)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    ReadPrinter(hPrinter, pBuf, cbBuf, pcNoBytesRead);
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcResetPrinter(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pDatatype, WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcResetPrinterEx()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcSeekPrinter()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcSetPrinter(WINSPOOL_PRINTER_HANDLE hPrinter, WINSPOOL_PRINTER_CONTAINER* pPrinterContainer, WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer, WINSPOOL_SECURITY_CONTAINER* pSecurityContainer, DWORD Command)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcStartDocPrinter(WINSPOOL_PRINTER_HANDLE hPrinter, WINSPOOL_DOC_INFO_CONTAINER* pDocInfoContainer, DWORD* pJobId)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    *pJobId = StartDocPrinterW(hPrinter, pDocInfoContainer->Level, (PBYTE)pDocInfoContainer->DocInfo.pDocInfo1);
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcStartPagePrinter(WINSPOOL_PRINTER_HANDLE hPrinter)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    StartPagePrinter(hPrinter);
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcWritePrinter(WINSPOOL_PRINTER_HANDLE hPrinter, BYTE* pBuf, DWORD cbBuf, DWORD* pcWritten)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    WritePrinter(hPrinter, pBuf, cbBuf, pcWritten);
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/printprocessors.c b/reactos/win32ss/printing/base/spoolsv/printprocessors.c
new file mode 100644 (file)
index 0000000..ef224e5
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Print Processors
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static void
+_MarshallDownDatatypesInfo(PDATATYPES_INFO_1W pDatatypesInfo1)
+{
+    // Replace absolute pointer addresses in the output by relative offsets.
+    pDatatypesInfo1->pName = (PWSTR)((ULONG_PTR)pDatatypesInfo1->pName - (ULONG_PTR)pDatatypesInfo1);
+}
+
+static void
+_MarshallDownPrintProcessorInfo(PPRINTPROCESSOR_INFO_1W pPrintProcessorInfo1)
+{
+    // Replace absolute pointer addresses in the output by relative offsets.
+    pPrintProcessorInfo1->pName = (PWSTR)((ULONG_PTR)pPrintProcessorInfo1->pName - (ULONG_PTR)pPrintProcessorInfo1);
+}
+
+DWORD
+_RpcAddPrintProcessor(WINSPOOL_HANDLE pName, WCHAR* pEnvironment, WCHAR* pPathName, WCHAR* pPrintProcessorName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeletePrintProcessor(WINSPOOL_HANDLE pName, WCHAR* pEnvironment, WCHAR* pPrintProcessorName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcEnumPrintProcessorDatatypes(WINSPOOL_HANDLE pName, WCHAR* pPrintProcessorName, DWORD Level, BYTE* pDatatypes, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pDatatypes;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    EnumPrintProcessorDatatypesW(pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned);
+    dwErrorCode = GetLastError();
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace absolute pointer addresses in the output by relative offsets.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallDownDatatypesInfo((PDATATYPES_INFO_1W)p);
+            p += sizeof(DATATYPES_INFO_1W);
+        }
+    }
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcEnumPrintProcessors(WINSPOOL_HANDLE pName, WCHAR* pEnvironment, DWORD Level, BYTE* pPrintProcessorInfo, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pPrintProcessorInfo;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    EnumPrintProcessorsW(pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded, pcReturned);
+    dwErrorCode = GetLastError();
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace absolute pointer addresses in the output by relative offsets.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallDownPrintProcessorInfo((PPRINTPROCESSOR_INFO_1W)p);
+            p += sizeof(PRINTPROCESSOR_INFO_1W);
+        }
+    }
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
+
+DWORD
+_RpcGetPrintProcessorDirectory(WINSPOOL_HANDLE pName, WCHAR* pEnvironment, DWORD Level, BYTE* pPrintProcessorDirectory, DWORD cbBuf, DWORD* pcbNeeded)
+{
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    GetPrintProcessorDirectoryW(pName, pEnvironment, Level, pPrintProcessorDirectory, cbBuf, pcbNeeded);
+    dwErrorCode = GetLastError();
+
+    RpcRevertToSelf();
+    return dwErrorCode;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/printproviders.c b/reactos/win32ss/printing/base/spoolsv/printproviders.c
new file mode 100644 (file)
index 0000000..eafd50b
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Print Providers
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD
+_RpcAddPrintProvidor(WINSPOOL_HANDLE pName, WINSPOOL_PROVIDOR_CONTAINER* pProvidorContainer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeletePrintProvidor(WINSPOOL_HANDLE pName, WCHAR* pEnvironment, WCHAR* pPrintProviderName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/rpcserver.c b/reactos/win32ss/printing/base/spoolsv/rpcserver.c
new file mode 100644 (file)
index 0000000..d5f8c92
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     RPC Server Thread
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD WINAPI
+LrpcThreadProc(LPVOID lpParameter)
+{
+    RPC_STATUS Status;
+
+    Status = RpcServerUseProtseqEpW(L"ncalrpc", 20, L"spoolss", NULL);
+    if (Status != RPC_S_OK)
+    {
+        ERR("RpcServerUseProtseqEpW failed with status %ld!\n", Status);
+        return 0;
+    }
+
+    Status = RpcServerRegisterIf(winspool_v1_0_s_ifspec, NULL, NULL);
+    if (Status != RPC_S_OK)
+    {
+        ERR("RpcServerRegisterIf failed with status %ld!\n", Status);
+        return 0;
+    }
+
+    Status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0);
+    if (Status != RPC_S_OK)
+    {
+        ERR("RpcServerListen() failed with status %ld!\n", Status);
+    }
+
+    return 0;
+}
+
+void __RPC_FAR* __RPC_USER
+midl_user_allocate(SIZE_T len)
+{
+    return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
+}
+
+void __RPC_USER
+midl_user_free(void __RPC_FAR* ptr)
+{
+    HeapFree(GetProcessHeap(), 0, ptr);
+}
+
+void __RPC_USER
+WINSPOOL_GDI_HANDLE_rundown(WINSPOOL_GDI_HANDLE hGdiHandle)
+{
+}
+
+void __RPC_USER
+WINSPOOL_PRINTER_HANDLE_rundown(WINSPOOL_PRINTER_HANDLE hPrinter)
+{
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/rpcstubs.c b/reactos/win32ss/printing/base/spoolsv/rpcstubs.c
new file mode 100644 (file)
index 0000000..857e78f
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Unimplemented RPC calls
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD
+_RpcCreatePrinterIC(WINSPOOL_PRINTER_HANDLE hPrinter, WINSPOOL_GDI_HANDLE* pHandle, WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcPlayGdiScriptOnPrinterIC(WINSPOOL_GDI_HANDLE hPrinterIC, BYTE* pIn, DWORD cIn, BYTE* pOut, DWORD cOut, DWORD ul)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeletePrinterIC(WINSPOOL_GDI_HANDLE* phPrinterIC)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcAddPrinterConnection(WINSPOOL_HANDLE pName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeletePrinterConnection(WINSPOOL_HANDLE pName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcPrinterMessageBox(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD Error, ULONG_PTR hWnd, WCHAR* pText, WCHAR* pCaption, DWORD dwType)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcSetAllocFailCount()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcClusterSplOpen()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcClusterSplClose()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcClusterSplIsAlive()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcAddPerMachineConnection(WINSPOOL_HANDLE pServer, const WCHAR* pPrinterName, const WCHAR* pPrintServer, const WCHAR* pProvider)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcDeletePerMachineConnection(WINSPOOL_HANDLE pServer, const WCHAR* pPrinterName)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcEnumPerMachineConnections(WINSPOOL_HANDLE pServer, BYTE* pPrinterEnum, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcSplOpenPrinter()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcGetSpoolFileInfo()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcCommitSpoolData()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcCloseSpoolFileHandle()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcSendRecvBidiData(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pAction, WINSPOOL_BIDI_REQUEST_CONTAINER* pReqData, WINSPOOL_BIDI_RESPONSE_CONTAINER** ppRespData)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
+
+DWORD
+_RpcAddDriverCatalog()
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
diff --git a/reactos/win32ss/printing/base/spoolsv/spoolsv.rc b/reactos/win32ss/printing/base/spoolsv/spoolsv.rc
new file mode 100644 (file)
index 0000000..714f4da
--- /dev/null
@@ -0,0 +1,4 @@
+#define REACTOS_STR_FILE_DESCRIPTION  "ReactOS Print Spooler Service"
+#define REACTOS_STR_INTERNAL_NAME     "spoolsv"
+#define REACTOS_STR_ORIGINAL_FILENAME "spoolsv.exe"
+#include <reactos/version.rc>
diff --git a/reactos/win32ss/printing/base/spoolsv/xcv.c b/reactos/win32ss/printing/base/spoolsv/xcv.c
new file mode 100644 (file)
index 0000000..e02f1bd
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Service
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Xcv* functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD
+_RpcXcvData(WINSPOOL_PRINTER_HANDLE hXcv, const WCHAR* pszDataName, BYTE* pInputData, DWORD cbInputData, BYTE* pOutputData, DWORD cbOutputData, DWORD* pcbOutputNeeded, DWORD* pdwStatus)
+{
+    UNIMPLEMENTED;
+    return ERROR_INVALID_FUNCTION;
+}
diff --git a/reactos/win32ss/printing/base/winspool/CMakeLists.txt b/reactos/win32ss/printing/base/winspool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f9c4f92
--- /dev/null
@@ -0,0 +1,31 @@
+
+include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/idl)
+add_rpc_files(client ${REACTOS_SOURCE_DIR}/sdk/include/reactos/idl/winspool.idl)
+spec2def(winspool.drv winspool.spec ADD_IMPORTLIB)
+
+list(APPEND SOURCE
+    devmode.c
+    jobs.c
+    main.c
+    monitors.c
+    ports.c
+    precomp.h
+    printerdata.c
+    printerdrivers.c
+    printers.c
+    printprocessors.c
+    printproviders.c
+    ${CMAKE_CURRENT_BINARY_DIR}/winspool_c.c)
+
+add_library(winspool SHARED
+    ${SOURCE}
+    winspool.rc
+    ${CMAKE_CURRENT_BINARY_DIR}/winspool_stubs.c
+    ${CMAKE_CURRENT_BINARY_DIR}/winspool.def)
+
+set_target_properties(winspool PROPERTIES SUFFIX ".drv")
+set_module_type(winspool win32dll UNICODE)
+target_link_libraries(winspool wine ${PSEH_LIB})
+add_importlibs(winspool gdi32 rpcrt4 msvcrt kernel32 ntdll)
+add_pch(winspool precomp.h SOURCE)
+add_cd_file(TARGET winspool DESTINATION reactos/system32 FOR all)
diff --git a/reactos/win32ss/printing/base/winspool/devmode.c b/reactos/win32ss/printing/base/winspool/devmode.c
new file mode 100644 (file)
index 0000000..2349d5f
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions giving information about DEVMODE structures
+ * COPYRIGHT:   Copyright 2016 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+typedef struct _MINIMUM_SIZE_TABLE
+{
+    DWORD dwField;
+    WORD wSize;
+}
+MINIMUM_SIZE_TABLE, *PMINIMUM_SIZE_TABLE;
+
+/**
+ * Minimum required DEVMODEA structure size based on the used fields. Must be in descending order!
+ */
+static MINIMUM_SIZE_TABLE MinimumSizeA[] = {
+    { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEA, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPanningHeight) },
+    { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEA, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPanningWidth) },
+    { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEA, dmDitherType) + RTL_FIELD_SIZE(DEVMODEA, dmDitherType) },
+    { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEA, dmMediaType) + RTL_FIELD_SIZE(DEVMODEA, dmMediaType) },
+    { DM_ICMINTENT, FIELD_OFFSET(DEVMODEA, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEA, dmICMIntent) },
+    { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEA, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEA, dmICMMethod) },
+    { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEA, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFrequency) },
+    { DM_NUP, FIELD_OFFSET(DEVMODEA, dmNup) + RTL_FIELD_SIZE(DEVMODEA, dmNup) },
+    { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEA, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFlags) },
+    { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEA, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPelsHeight) },
+    { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEA, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPelsWidth) },
+    { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEA, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEA, dmBitsPerPel) },
+    { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEA, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEA, dmLogPixels) },
+    { DM_FORMNAME, FIELD_OFFSET(DEVMODEA, dmFormName) + RTL_FIELD_SIZE(DEVMODEA, dmFormName) },
+    { DM_COLLATE, FIELD_OFFSET(DEVMODEA, dmCollate) + RTL_FIELD_SIZE(DEVMODEA, dmCollate) },
+    { DM_TTOPTION, FIELD_OFFSET(DEVMODEA, dmTTOption) + RTL_FIELD_SIZE(DEVMODEA, dmTTOption) },
+    { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEA, dmYResolution) + RTL_FIELD_SIZE(DEVMODEA, dmYResolution) },
+    { DM_DUPLEX, FIELD_OFFSET(DEVMODEA, dmDuplex) + RTL_FIELD_SIZE(DEVMODEA, dmDuplex) },
+    { DM_COLOR, FIELD_OFFSET(DEVMODEA, dmColor) + RTL_FIELD_SIZE(DEVMODEA, dmColor) },
+    { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEA, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFixedOutput) },
+    { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEA, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayOrientation) },
+    { DM_POSITION, FIELD_OFFSET(DEVMODEA, dmPosition) + RTL_FIELD_SIZE(DEVMODEA, dmPosition) },
+    { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEA, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEA, dmPrintQuality) },
+    { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEA, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEA, dmDefaultSource) },
+    { DM_COPIES, FIELD_OFFSET(DEVMODEA, dmCopies) + RTL_FIELD_SIZE(DEVMODEA, dmCopies) },
+    { DM_SCALE, FIELD_OFFSET(DEVMODEA, dmScale) + RTL_FIELD_SIZE(DEVMODEA, dmScale) },
+    { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEA, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPaperWidth) },
+    { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEA, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEA, dmPaperLength) },
+    { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEA, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEA, dmPaperSize) },
+    { DM_ORIENTATION, FIELD_OFFSET(DEVMODEA, dmOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmOrientation) },
+    { 0, 0 }
+};
+
+/**
+ * Minimum required DEVMODEW structure size based on the used fields. Must be in descending order!
+ */
+static MINIMUM_SIZE_TABLE MinimumSizeW[] = {
+    { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEW, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPanningHeight) },
+    { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEW, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPanningWidth) },
+    { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEW, dmDitherType) + RTL_FIELD_SIZE(DEVMODEW, dmDitherType) },
+    { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEW, dmMediaType) + RTL_FIELD_SIZE(DEVMODEW, dmMediaType) },
+    { DM_ICMINTENT, FIELD_OFFSET(DEVMODEW, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEW, dmICMIntent) },
+    { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEW, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEW, dmICMMethod) },
+    { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEW, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFrequency) },
+    { DM_NUP, FIELD_OFFSET(DEVMODEW, dmNup) + RTL_FIELD_SIZE(DEVMODEW, dmNup) },
+    { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEW, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFlags) },
+    { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEW, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPelsHeight) },
+    { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEW, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPelsWidth) },
+    { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEW, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEW, dmBitsPerPel) },
+    { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEW, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEW, dmLogPixels) },
+    { DM_FORMNAME, FIELD_OFFSET(DEVMODEW, dmFormName) + RTL_FIELD_SIZE(DEVMODEW, dmFormName) },
+    { DM_COLLATE, FIELD_OFFSET(DEVMODEW, dmCollate) + RTL_FIELD_SIZE(DEVMODEW, dmCollate) },
+    { DM_TTOPTION, FIELD_OFFSET(DEVMODEW, dmTTOption) + RTL_FIELD_SIZE(DEVMODEW, dmTTOption) },
+    { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEW, dmYResolution) + RTL_FIELD_SIZE(DEVMODEW, dmYResolution) },
+    { DM_DUPLEX, FIELD_OFFSET(DEVMODEW, dmDuplex) + RTL_FIELD_SIZE(DEVMODEW, dmDuplex) },
+    { DM_COLOR, FIELD_OFFSET(DEVMODEW, dmColor) + RTL_FIELD_SIZE(DEVMODEW, dmColor) },
+    { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEW, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFixedOutput) },
+    { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEW, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayOrientation) },
+    { DM_POSITION, FIELD_OFFSET(DEVMODEW, dmPosition) + RTL_FIELD_SIZE(DEVMODEW, dmPosition) },
+    { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEW, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEW, dmPrintQuality) },
+    { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEW, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEW, dmDefaultSource) },
+    { DM_COPIES, FIELD_OFFSET(DEVMODEW, dmCopies) + RTL_FIELD_SIZE(DEVMODEW, dmCopies) },
+    { DM_SCALE, FIELD_OFFSET(DEVMODEW, dmScale) + RTL_FIELD_SIZE(DEVMODEW, dmScale) },
+    { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEW, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPaperWidth) },
+    { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEW, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEW, dmPaperLength) },
+    { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEW, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEW, dmPaperSize) },
+    { DM_ORIENTATION, FIELD_OFFSET(DEVMODEW, dmOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmOrientation) },
+    { 0, 0 }
+};
+
+/**
+ * Replace the last character by a null terminator if the given ANSI string is not null-terminated.
+ */
+static __inline void
+_FixStringA(PBYTE String, DWORD cbString)
+{
+    const PBYTE pLastCharacter = &String[cbString / sizeof(BYTE) - 1];
+    PBYTE p = String;
+
+    while (*p)
+    {
+        if (p == pLastCharacter)
+        {
+            *p = 0;
+            break;
+        }
+
+        p++;
+    }
+}
+
+/**
+ * Replace the last character by a null terminator if the given Unicode string is not null-terminated.
+ */
+static __inline void
+_FixStringW(PWSTR String, DWORD cbString)
+{
+    const PWSTR pLastCharacter = &String[cbString / sizeof(WCHAR) - 1];
+    PWSTR p = String;
+
+    while (*p)
+    {
+        if (p == pLastCharacter)
+        {
+            *p = 0;
+            break;
+        }
+
+        p++;
+    }
+}
+
+BOOL WINAPI
+IsValidDevmodeA(PDEVMODEA pDevmode, size_t DevmodeSize)
+{
+    PMINIMUM_SIZE_TABLE pTable = MinimumSizeA;
+    WORD wRequiredSize;
+
+    // Check if a Devmode was given at all.
+    if (!pDevmode)
+        goto Failure;
+
+    // Verify that DevmodeSize is large enough to hold the public and private members of the structure.
+    if (DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra)
+        goto Failure;
+
+    // If the structure has private members, the public structure must be 32-bit packed.
+    if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4)
+        goto Failure;
+
+    // Now determine the minimum possible dmSize based on the given fields in dmFields.
+    wRequiredSize = FIELD_OFFSET(DEVMODEA, dmFields) + RTL_FIELD_SIZE(DEVMODEA, dmFields);
+
+    while (pTable->dwField)
+    {
+        if (pDevmode->dmFields & pTable->dwField)
+        {
+            wRequiredSize = pTable->wSize;
+            break;
+        }
+
+        pTable++;
+    }
+
+    // Verify that the value in dmSize is big enough for the used fields.
+    if (pDevmode->dmSize < wRequiredSize)
+        goto Failure;
+
+    // Check if dmDeviceName and (if used) dmFormName are null-terminated.
+    // Fix this if they aren't.
+    _FixStringA(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName));
+    if (pDevmode->dmFields & DM_FORMNAME)
+        _FixStringA(pDevmode->dmFormName, sizeof(pDevmode->dmFormName));
+
+    // Return success without setting the error code.
+    return TRUE;
+
+Failure:
+    SetLastError(ERROR_INVALID_DATA);
+    return FALSE;
+}
+
+BOOL WINAPI
+IsValidDevmodeW(PDEVMODEW pDevmode, size_t DevmodeSize)
+{
+    PMINIMUM_SIZE_TABLE pTable = MinimumSizeW;
+    WORD wRequiredSize;
+
+    // Check if a Devmode was given at all.
+    if (!pDevmode)
+        goto Failure;
+
+    // Verify that DevmodeSize is large enough to hold the public and private members of the structure.
+    if (DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra)
+        goto Failure;
+
+    // If the structure has private members, the public structure must be 32-bit packed.
+    if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4)
+        goto Failure;
+
+    // Now determine the minimum possible dmSize based on the given fields in dmFields.
+    wRequiredSize = FIELD_OFFSET(DEVMODEW, dmFields) + RTL_FIELD_SIZE(DEVMODEW, dmFields);
+
+    while (pTable->dwField)
+    {
+        if (pDevmode->dmFields & pTable->dwField)
+        {
+            wRequiredSize = pTable->wSize;
+            break;
+        }
+
+        pTable++;
+    }
+
+    // Verify that the value in dmSize is big enough for the used fields.
+    if (pDevmode->dmSize < wRequiredSize)
+        goto Failure;
+
+    // Check if dmDeviceName and (if used) dmFormName are null-terminated.
+    // Fix this if they aren't.
+    _FixStringW(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName));
+    if (pDevmode->dmFields & DM_FORMNAME)
+        _FixStringW(pDevmode->dmFormName, sizeof(pDevmode->dmFormName));
+
+    // Return success without setting the error code.
+    return TRUE;
+
+Failure:
+    SetLastError(ERROR_INVALID_DATA);
+    return FALSE;
+}
diff --git a/reactos/win32ss/printing/base/winspool/jobs.c b/reactos/win32ss/printing/base/winspool/jobs.c
new file mode 100644 (file)
index 0000000..ab0069d
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions for managing print jobs
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static void
+_MarshallUpAddJobInfo(PADDJOB_INFO_1W pAddJobInfo1)
+{
+    // Replace relative offset addresses in the output by absolute pointers.
+    pAddJobInfo1->Path = (PWSTR)((ULONG_PTR)pAddJobInfo1->Path + (ULONG_PTR)pAddJobInfo1);
+}
+
+static void
+_MarshallUpJobInfo(PBYTE pJobInfo, DWORD Level)
+{
+    PJOB_INFO_1W pJobInfo1;
+    PJOB_INFO_2W pJobInfo2;
+
+    // Replace relative offset addresses in the output by absolute pointers.
+    if (Level == 1)
+    {
+        pJobInfo1 = (PJOB_INFO_1W)pJobInfo;
+        pJobInfo1->pDatatype = (PWSTR)((ULONG_PTR)pJobInfo1->pDatatype + (ULONG_PTR)pJobInfo1);
+        pJobInfo1->pDocument = (PWSTR)((ULONG_PTR)pJobInfo1->pDocument + (ULONG_PTR)pJobInfo1);
+        pJobInfo1->pMachineName = (PWSTR)((ULONG_PTR)pJobInfo1->pMachineName + (ULONG_PTR)pJobInfo1);
+        pJobInfo1->pPrinterName = (PWSTR)((ULONG_PTR)pJobInfo1->pPrinterName + (ULONG_PTR)pJobInfo1);
+
+        if (pJobInfo1->pStatus)
+            pJobInfo1->pStatus = (PWSTR)((ULONG_PTR)pJobInfo1->pStatus + (ULONG_PTR)pJobInfo1);
+
+        pJobInfo1->pUserName = (PWSTR)((ULONG_PTR)pJobInfo1->pUserName + (ULONG_PTR)pJobInfo1);
+    }
+    else if (Level == 2)
+    {
+        pJobInfo2 = (PJOB_INFO_2W)pJobInfo;
+        pJobInfo2->pDatatype = (PWSTR)((ULONG_PTR)pJobInfo2->pDatatype + (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pDevMode = (PDEVMODEW)((ULONG_PTR)pJobInfo2->pDevMode + (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pDocument = (PWSTR)((ULONG_PTR)pJobInfo2->pDocument + (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pDriverName = (PWSTR)((ULONG_PTR)pJobInfo2->pDriverName + (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pMachineName = (PWSTR)((ULONG_PTR)pJobInfo2->pMachineName + (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pNotifyName = (PWSTR)((ULONG_PTR)pJobInfo2->pNotifyName + (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pPrinterName = (PWSTR)((ULONG_PTR)pJobInfo2->pPrinterName + (ULONG_PTR)pJobInfo2);
+        pJobInfo2->pPrintProcessor = (PWSTR)((ULONG_PTR)pJobInfo2->pPrintProcessor + (ULONG_PTR)pJobInfo2);
+
+        if (pJobInfo2->pParameters)
+            pJobInfo2->pParameters = (PWSTR)((ULONG_PTR)pJobInfo2->pParameters + (ULONG_PTR)pJobInfo2);
+
+        if (pJobInfo2->pStatus)
+            pJobInfo2->pStatus = (PWSTR)((ULONG_PTR)pJobInfo2->pStatus + (ULONG_PTR)pJobInfo2);
+
+        pJobInfo2->pUserName = (PWSTR)((ULONG_PTR)pJobInfo2->pUserName + (ULONG_PTR)pJobInfo2);
+    }
+}
+
+BOOL WINAPI
+AddJobA(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+AddJobW(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcAddJob(pHandle->hPrinter, Level, pData, cbBuf, pcbNeeded);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcAddJob failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    if (dwErrorCode == ERROR_SUCCESS)
+        _MarshallUpAddJobInfo((PADDJOB_INFO_1W)pData);
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pJob;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcEnumJobs(pHandle->hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcEnumJobs failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace relative offset addresses in the output by absolute pointers.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallUpJobInfo(p, Level);
+
+            if (Level == 1)
+                p += sizeof(JOB_INFO_1W);
+            else if (Level == 2)
+                p += sizeof(JOB_INFO_2W);
+        }
+    }
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcGetJob(pHandle->hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcGetJob failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace relative offset addresses in the output by absolute pointers.
+        _MarshallUpJobInfo(pJob, Level);
+    }
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+ScheduleJob(HANDLE hPrinter, DWORD dwJobID)
+{
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, dwJobID);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
+{
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+    WINSPOOL_JOB_CONTAINER JobContainer;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // pJobContainer->JobInfo is a union of pointers, so we can just set any element to our BYTE pointer.
+    JobContainer.Level = Level;
+    JobContainer.JobInfo.Level1 = (WINSPOOL_JOB_INFO_1*)pJobInfo;
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcSetJob(pHandle->hPrinter, JobId, &JobContainer, Command);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcSetJob failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
diff --git a/reactos/win32ss/printing/base/winspool/main.c b/reactos/win32ss/printing/base/winspool/main.c
new file mode 100644 (file)
index 0000000..b6d3dff
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Main functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+// Global Variables
+HANDLE hProcessHeap;
+
+
+handle_t __RPC_USER
+WINSPOOL_HANDLE_bind(WINSPOOL_HANDLE wszName)
+{
+    handle_t hBinding;
+    PWSTR wszStringBinding;
+    RPC_STATUS Status;
+
+    // Get us a string binding handle from the supplied connection information
+    Status = RpcStringBindingComposeW(NULL, L"ncalrpc", NULL, L"spoolss", NULL, &wszStringBinding);
+    if (Status != RPC_S_OK)
+    {
+        ERR("RpcStringBindingComposeW failed with status %ld!\n", Status);
+        return NULL;
+    }
+
+    // Get a handle_t binding handle from the string binding handle
+    Status = RpcBindingFromStringBindingW(wszStringBinding, &hBinding);
+    if (Status != RPC_S_OK)
+    {
+        ERR("RpcBindingFromStringBindingW failed with status %ld!\n", Status);
+        return NULL;
+    }
+
+    // Free the string binding handle
+    Status = RpcStringFreeW(&wszStringBinding);
+    if (Status != RPC_S_OK)
+    {
+        ERR("RpcStringFreeW failed with status %ld!\n", Status);
+        return NULL;
+    }
+
+    return hBinding;
+}
+
+void __RPC_USER
+WINSPOOL_HANDLE_unbind(WINSPOOL_HANDLE wszName, handle_t hBinding)
+{
+    RPC_STATUS Status;
+
+    Status = RpcBindingFree(&hBinding);
+    if (Status != RPC_S_OK)
+    {
+        ERR("RpcBindingFree failed with status %ld!\n", Status);
+    }
+}
+
+void __RPC_FAR* __RPC_USER
+midl_user_allocate(SIZE_T len)
+{
+    return HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, len);
+}
+
+void __RPC_USER
+midl_user_free(void __RPC_FAR* ptr)
+{
+    HeapFree(hProcessHeap, 0, ptr);
+}
+
+BOOL WINAPI
+DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    switch (fdwReason)
+    {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls(hinstDLL);
+            hProcessHeap = GetProcessHeap();
+            break;
+    }
+
+    return TRUE;
+}
+
+BOOL WINAPI
+SpoolerInit()
+{
+    BOOL bReturnValue = FALSE;
+    DWORD dwErrorCode;
+
+    // Nothing to initialize here yet, but pass this call to the Spool Service as well.
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcSpoolerInit();
+        SetLastError(dwErrorCode);
+        bReturnValue = (dwErrorCode == ERROR_SUCCESS);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        ERR("_RpcSpoolerInit failed with exception code %lu!\n", RpcExceptionCode());
+    }
+    RpcEndExcept;
+
+    return bReturnValue;
+}
diff --git a/reactos/win32ss/printing/base/winspool/monitors.c b/reactos/win32ss/printing/base/winspool/monitors.c
new file mode 100644 (file)
index 0000000..c3ed069
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Print Monitors
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static void
+_MarshallUpMonitorInfo(PBYTE pMonitorInfo, DWORD Level)
+{
+    PMONITOR_INFO_2W pMonitorInfo2 = (PMONITOR_INFO_2W)pMonitorInfo;        // MONITOR_INFO_1W is a subset of MONITOR_INFO_2W
+
+    // Replace relative offset addresses in the output by absolute pointers.
+    pMonitorInfo2->pName = (PWSTR)((ULONG_PTR)pMonitorInfo2->pName + (ULONG_PTR)pMonitorInfo2);
+
+    if (Level == 2)
+    {
+        pMonitorInfo2->pDLLName = (PWSTR)((ULONG_PTR)pMonitorInfo2->pDLLName + (ULONG_PTR)pMonitorInfo2);
+        pMonitorInfo2->pEnvironment = (PWSTR)((ULONG_PTR)pMonitorInfo2->pEnvironment + (ULONG_PTR)pMonitorInfo2);
+    }
+}
+
+BOOL WINAPI
+AddMonitorW(PWSTR pName, DWORD Level, PBYTE pMonitors)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+DeleteMonitorW(PWSTR pName, PWSTR pEnvironment, PWSTR pMonitorName)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+EnumMonitorsW(PWSTR pName, DWORD Level, PBYTE pMonitors, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pMonitors;
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcEnumMonitors(pName, Level, pMonitors, cbBuf, pcbNeeded, pcReturned);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcEnumPorts failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace relative offset addresses in the output by absolute pointers.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallUpMonitorInfo(p, Level);
+
+            if (Level == 1)
+                p += sizeof(MONITOR_INFO_1W);
+            else if (Level == 2)
+                p += sizeof(MONITOR_INFO_2W);
+        }
+    }
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
diff --git a/reactos/win32ss/printing/base/winspool/ports.c b/reactos/win32ss/printing/base/winspool/ports.c
new file mode 100644 (file)
index 0000000..e893188
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Ports
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static void
+_MarshallUpPortInfo(PBYTE pPortInfo, DWORD Level)
+{
+    PPORT_INFO_2W pPortInfo2 = (PPORT_INFO_2W)pPortInfo;         // PORT_INFO_1W is a subset of PORT_INFO_2W
+
+    // Replace relative offset addresses in the output by absolute pointers.
+    pPortInfo2->pPortName = (PWSTR)((ULONG_PTR)pPortInfo2->pPortName + (ULONG_PTR)pPortInfo2);
+
+    if (Level == 2)
+    {
+        pPortInfo2->pDescription = (PWSTR)((ULONG_PTR)pPortInfo2->pDescription + (ULONG_PTR)pPortInfo2);
+        pPortInfo2->pMonitorName = (PWSTR)((ULONG_PTR)pPortInfo2->pMonitorName + (ULONG_PTR)pPortInfo2);
+    }
+}
+
+BOOL WINAPI
+AddPortW(PWSTR pName, HWND hWnd, PWSTR pMonitorName)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+ConfigurePortW(PWSTR pName, HWND hWnd, PWSTR pPortName)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+DeletePortW(PWSTR pName, HWND hWnd, PWSTR pPortName)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+EnumPortsW(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pPorts;
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcEnumPorts(pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcEnumPorts failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace relative offset addresses in the output by absolute pointers.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallUpPortInfo(p, Level);
+
+            if (Level == 1)
+                p += sizeof(PORT_INFO_1W);
+            else if (Level == 2)
+                p += sizeof(PORT_INFO_2W);
+        }
+    }
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
diff --git a/reactos/win32ss/printing/base/winspool/precomp.h b/reactos/win32ss/printing/base/winspool/precomp.h
new file mode 100644 (file)
index 0000000..df5588a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * PROJECT:     ReactOS Print Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Precompiled Header for all source files
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#ifndef _PRECOMP_H
+#define _PRECOMP_H
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winreg.h>
+#include <winspool.h>
+#include <winspool_c.h>
+
+#include <wine/debug.h>
+WINE_DEFAULT_DEBUG_CHANNEL(winspool);
+
+// Structures
+/*
+ * Describes a handle returned by OpenPrinterW.
+ */
+typedef struct _SPOOLER_HANDLE
+{
+    BOOL bStartedDoc : 1;
+    DWORD dwJobID;
+    HANDLE hPrinter;
+    HANDLE hSPLFile;
+}
+SPOOLER_HANDLE, *PSPOOLER_HANDLE;
+
+// main.c
+extern HANDLE hProcessHeap;
+
+#endif
diff --git a/reactos/win32ss/printing/base/winspool/printerdata.c b/reactos/win32ss/printing/base/winspool/printerdata.c
new file mode 100644 (file)
index 0000000..ec15ac6
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printer Configuration Data
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+LONG WINAPI
+AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, PWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+DWORD WINAPI
+GetPrinterDataW(HANDLE hPrinter, PWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+DWORD WINAPI
+SetPrinterDataW(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
diff --git a/reactos/win32ss/printing/base/winspool/printerdrivers.c b/reactos/win32ss/printing/base/winspool/printerdrivers.c
new file mode 100644 (file)
index 0000000..0b987f4
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printer Drivers
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+BOOL WINAPI
+AddPrinterDriverW(PWSTR pName, DWORD Level, PBYTE pDriverInfo)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+DeletePrinterDriverW(PWSTR pName, PWSTR pEnvironment, PWSTR pDriverName)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+EnumPrinterDriversW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pDriverInfo, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+GetPrinterDriverDirectoryW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pDriverDirectory, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
diff --git a/reactos/win32ss/printing/base/winspool/printers.c b/reactos/win32ss/printing/base/winspool/printers.c
new file mode 100644 (file)
index 0000000..c82132c
--- /dev/null
@@ -0,0 +1,799 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printers and printing
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static void
+_MarshallUpPrinterInfo(PBYTE pPrinterInfo, DWORD Level)
+{
+    PPRINTER_INFO_1W pPrinterInfo1;
+    PPRINTER_INFO_2W pPrinterInfo2;
+
+    // Replace relative offset addresses in the output by absolute pointers.
+    if (Level == 1)
+    {
+        pPrinterInfo1 = (PPRINTER_INFO_1W)pPrinterInfo;
+
+        pPrinterInfo1->pName = (PWSTR)((ULONG_PTR)pPrinterInfo1->pName + (ULONG_PTR)pPrinterInfo1);
+        pPrinterInfo1->pDescription = (PWSTR)((ULONG_PTR)pPrinterInfo1->pDescription + (ULONG_PTR)pPrinterInfo1);
+        pPrinterInfo1->pComment = (PWSTR)((ULONG_PTR)pPrinterInfo1->pComment + (ULONG_PTR)pPrinterInfo1);
+    }
+    else if (Level == 2)
+    {
+        pPrinterInfo2 = (PPRINTER_INFO_2W)pPrinterInfo;
+
+        pPrinterInfo2->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPrinterName + (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pShareName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pShareName + (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pPortName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPortName + (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pDriverName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pDriverName + (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pComment = (PWSTR)((ULONG_PTR)pPrinterInfo2->pComment + (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pLocation = (PWSTR)((ULONG_PTR)pPrinterInfo2->pLocation + (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pDevMode = (PDEVMODEW)((ULONG_PTR)pPrinterInfo2->pDevMode + (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pSepFile = (PWSTR)((ULONG_PTR)pPrinterInfo2->pSepFile + (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pPrintProcessor = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPrintProcessor + (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pDatatype = (PWSTR)((ULONG_PTR)pPrinterInfo2->pDatatype + (ULONG_PTR)pPrinterInfo2);
+        pPrinterInfo2->pParameters = (PWSTR)((ULONG_PTR)pPrinterInfo2->pParameters + (ULONG_PTR)pPrinterInfo2);
+
+        if (pPrinterInfo2->pServerName)
+            pPrinterInfo2->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pServerName + (ULONG_PTR)pPrinterInfo2);
+
+        if (pPrinterInfo2->pSecurityDescriptor)
+            pPrinterInfo2->pSecurityDescriptor = (PWSTR)((ULONG_PTR)pPrinterInfo2->pSecurityDescriptor + (ULONG_PTR)pPrinterInfo2);
+    }
+}
+
+static DWORD
+_StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB_INFO_1W pAddJobInfo1)
+{
+    DWORD cbNeeded;
+    DWORD dwErrorCode;
+    PJOB_INFO_1W pJobInfo1 = NULL;
+
+    // Create the spool file.
+    pHandle->hSPLFile = CreateFileW(pAddJobInfo1->Path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
+    if (pHandle->hSPLFile == INVALID_HANDLE_VALUE)
+    {
+        dwErrorCode = GetLastError();
+        ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1->Path, dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Get the size of the job information.
+    GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
+    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+    {
+        dwErrorCode = GetLastError();
+        ERR("GetJobW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Allocate enough memory for the returned job information.
+    pJobInfo1 = HeapAlloc(hProcessHeap, 0, cbNeeded);
+    if (!pJobInfo1)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Get the job information.
+    if (!GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
+    {
+        dwErrorCode = GetLastError();
+        ERR("GetJobW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Add our document information.
+    if (pDocInfo1->pDatatype)
+        pJobInfo1->pDatatype = pDocInfo1->pDatatype;
+
+    pJobInfo1->pDocument = pDocInfo1->pDocName;
+
+    // Set the new job information.
+    if (!SetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
+    {
+        dwErrorCode = GetLastError();
+        ERR("SetJobW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // We were successful!
+    pHandle->dwJobID = pAddJobInfo1->JobId;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (pJobInfo1)
+        HeapFree(hProcessHeap, 0, pJobInfo1);
+
+    return dwErrorCode;
+}
+
+static DWORD
+_StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
+{
+    DWORD dwErrorCode;
+    WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer;
+
+    DocInfoContainer.Level = 1;
+    DocInfoContainer.DocInfo.pDocInfo1 = (WINSPOOL_DOC_INFO_1*)pDocInfo1;
+
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcStartDocPrinter(pHandle->hPrinter, &DocInfoContainer, &pHandle->dwJobID);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    return dwErrorCode;
+}
+
+HANDLE WINAPI
+AddPrinterW(PWSTR pName, DWORD Level, PBYTE pPrinter)
+{
+    UNIMPLEMENTED;
+    return NULL;
+}
+
+BOOL WINAPI
+ClosePrinter(HANDLE hPrinter)
+{
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Do the RPC call.
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcClosePrinter(pHandle->hPrinter);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    // Close any open file handle.
+    if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
+        CloseHandle(pHandle->hSPLFile);
+
+    // Free the memory for the handle.
+    HeapFree(hProcessHeap, 0, pHandle);
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+
+}
+
+DWORD WINAPI
+DeviceCapabilitiesA(LPCSTR pDevice, LPCSTR pPort, WORD fwCapability, LPSTR pOutput, const DEVMODEA* pDevMode)
+{
+    return 0;
+}
+
+DWORD WINAPI
+DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort, WORD fwCapability, LPWSTR pOutput, const DEVMODEW* pDevMode)
+{
+    return 0;
+}
+
+LONG WINAPI
+DocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName, PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput, DWORD fMode)
+{
+    return 0;
+}
+
+LONG WINAPI
+DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput, DWORD fMode)
+{
+    return 0;
+}
+
+BOOL WINAPI
+EndDocPrinter(HANDLE hPrinter)
+{
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
+    {
+        // For spooled jobs, the document is finished by calling _RpcScheduleJob.
+        RpcTryExcept
+        {
+            dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, pHandle->dwJobID);
+        }
+        RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwErrorCode = RpcExceptionCode();
+            ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
+        }
+        RpcEndExcept;
+
+        // Close the spool file handle.
+        CloseHandle(pHandle->hSPLFile);
+    }
+    else
+    {
+        // In all other cases, just call _RpcEndDocPrinter.
+        RpcTryExcept
+        {
+            dwErrorCode = _RpcEndDocPrinter(pHandle->hPrinter);
+        }
+        RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwErrorCode = RpcExceptionCode();
+            ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode);
+        }
+        RpcEndExcept;
+    }
+
+    // A new document can now be started again.
+    pHandle->bStartedDoc = FALSE;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+EndPagePrinter(HANDLE hPrinter)
+{
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
+    {
+        // For spooled jobs, we don't need to do anything.
+        dwErrorCode = ERROR_SUCCESS;
+    }
+    else
+    {
+        // In all other cases, just call _RpcEndPagePrinter.
+        RpcTryExcept
+        {
+            dwErrorCode = _RpcEndPagePrinter(pHandle->hPrinter);
+        }
+        RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwErrorCode = RpcExceptionCode();
+            ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode);
+        }
+        RpcEndExcept;
+    }
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+EnumPrintersA(DWORD Flags, PSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    return FALSE;
+}
+
+BOOL WINAPI
+EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pPrinterEnum;
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace relative offset addresses in the output by absolute pointers.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallUpPrinterInfo(p, Level);
+
+            if (Level == 1)
+                p += sizeof(PRINTER_INFO_1W);
+            else if (Level == 2)
+                p += sizeof(PRINTER_INFO_2W);
+        }
+    }
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
+{
+    return FALSE;
+}
+
+BOOL WINAPI
+GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
+{
+    return FALSE;
+}
+
+BOOL WINAPI
+GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
+{
+    return FALSE;
+}
+
+BOOL WINAPI
+GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
+{
+    return FALSE;
+}
+
+BOOL WINAPI
+GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
+{
+    return FALSE;
+}
+
+BOOL WINAPI
+GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
+{
+    return FALSE;
+}
+
+BOOL WINAPI
+OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
+{
+    BOOL bReturnValue = FALSE;
+    DWORD cch;
+    PWSTR pwszPrinterName = NULL;
+    PRINTER_DEFAULTSW wDefault = { 0 };
+
+    if (pPrinterName)
+    {
+        // Convert pPrinterName to a Unicode string pwszPrinterName
+        cch = strlen(pPrinterName);
+
+        pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!pwszPrinterName)
+        {
+            ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
+    }
+
+    if (pDefault)
+    {
+        wDefault.DesiredAccess = pDefault->DesiredAccess;
+
+        if (pDefault->pDatatype)
+        {
+            // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
+            cch = strlen(pDefault->pDatatype);
+
+            wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+            if (!wDefault.pDatatype)
+            {
+                ERR("HeapAlloc failed for wDefault.pDatatype with last error %lu!\n", GetLastError());
+                goto Cleanup;
+            }
+
+            MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
+        }
+
+        if (pDefault->pDevMode)
+            wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
+    }
+
+    bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
+
+Cleanup:
+    if (wDefault.pDatatype)
+        HeapFree(hProcessHeap, 0, wDefault.pDatatype);
+
+    if (wDefault.pDevMode)
+        HeapFree(hProcessHeap, 0, wDefault.pDevMode);
+
+    if (pwszPrinterName)
+        HeapFree(hProcessHeap, 0, pwszPrinterName);
+
+    return bReturnValue;
+}
+
+BOOL WINAPI
+OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
+{
+    DWORD dwErrorCode;
+    HANDLE hPrinter;
+    PSPOOLER_HANDLE pHandle;
+    PWSTR pDatatype = NULL;
+    WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
+    ACCESS_MASK AccessRequired = 0;
+
+    // Prepare the additional parameters in the format required by _RpcOpenPrinter
+    if (pDefault)
+    {
+        pDatatype = pDefault->pDatatype;
+        DevModeContainer.cbBuf = sizeof(DEVMODEW);
+        DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
+        AccessRequired = pDefault->DesiredAccess;
+    }
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Create a new SPOOLER_HANDLE structure.
+        pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
+        if (!pHandle)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        pHandle->hPrinter = hPrinter;
+        pHandle->hSPLFile = INVALID_HANDLE_VALUE;
+
+        // Return it as phPrinter.
+        *phPrinter = (HANDLE)pHandle;
+    }
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
+{
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+DWORD WINAPI
+StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
+{
+    DOC_INFO_1W wDocInfo1 = { 0 };
+    DWORD cch;
+    DWORD dwErrorCode;
+    DWORD dwReturnValue = 0;
+    PDOC_INFO_1A pDocInfo1 = (PDOC_INFO_1A)pDocInfo;
+
+    // Only check the minimum required for accessing pDocInfo.
+    // Additional sanity checks are done in StartDocPrinterW.
+    if (!pDocInfo1)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    if (Level != 1)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    if (pDocInfo1->pDatatype)
+    {
+        // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
+        cch = strlen(pDocInfo1->pDatatype);
+
+        wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!wDocInfo1.pDatatype)
+        {
+            ERR("HeapAlloc failed for wDocInfo1.pDatatype with last error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDatatype, -1, wDocInfo1.pDatatype, cch + 1);
+    }
+
+    if (pDocInfo1->pDocName)
+    {
+        // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
+        cch = strlen(pDocInfo1->pDocName);
+
+        wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!wDocInfo1.pDocName)
+        {
+            ERR("HeapAlloc failed for wDocInfo1.pDocName with last error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDocName, -1, wDocInfo1.pDocName, cch + 1);
+    }
+
+    if (pDocInfo1->pOutputFile)
+    {
+        // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
+        cch = strlen(pDocInfo1->pOutputFile);
+
+        wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!wDocInfo1.pOutputFile)
+        {
+            ERR("HeapAlloc failed for wDocInfo1.pOutputFile with last error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pOutputFile, -1, wDocInfo1.pOutputFile, cch + 1);
+    }
+
+    dwReturnValue = StartDocPrinterW(hPrinter, Level, (PBYTE)&wDocInfo1);
+    dwErrorCode = GetLastError();
+
+Cleanup:
+    if (wDocInfo1.pDatatype)
+        HeapFree(hProcessHeap, 0, wDocInfo1.pDatatype);
+
+    if (wDocInfo1.pDocName)
+        HeapFree(hProcessHeap, 0, wDocInfo1.pDocName);
+
+    if (wDocInfo1.pOutputFile)
+        HeapFree(hProcessHeap, 0, wDocInfo1.pOutputFile);
+
+    SetLastError(dwErrorCode);
+    return dwReturnValue;
+}
+
+DWORD WINAPI
+StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
+{
+    DWORD cbAddJobInfo1;
+    DWORD cbNeeded;
+    DWORD dwErrorCode;
+    DWORD dwReturnValue = 0;
+    PADDJOB_INFO_1W pAddJobInfo1 = NULL;
+    PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    if (!pDocInfo1)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    if (Level != 1)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    if (pHandle->bStartedDoc)
+    {
+        dwErrorCode = ERROR_INVALID_PRINTER_STATE;
+        goto Cleanup;
+    }
+
+    // Check if we want to redirect output into a file.
+    if (pDocInfo1->pOutputFile)
+    {
+        // Do a StartDocPrinter RPC call in this case.
+        dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
+    }
+    else
+    {
+        // Allocate memory for the ADDJOB_INFO_1W structure and a path.
+        cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
+        pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
+        if (!pAddJobInfo1)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        // Try to add a new job.
+        // This only succeeds if the printer is set to do spooled printing.
+        if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
+        {
+            // Do spooled printing.
+            dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
+        }
+        else if (GetLastError() == ERROR_INVALID_ACCESS)
+        {
+            // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
+            // In this case, we do a StartDocPrinter RPC call.
+            dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
+        }
+        else
+        {
+            dwErrorCode = GetLastError();
+            ERR("AddJobW failed with error %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+    }
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        pHandle->bStartedDoc = TRUE;
+        dwReturnValue = pHandle->dwJobID;
+    }
+
+Cleanup:
+    if (pAddJobInfo1)
+        HeapFree(hProcessHeap, 0, pAddJobInfo1);
+
+    SetLastError(dwErrorCode);
+    return dwReturnValue;
+}
+
+BOOL WINAPI
+StartPagePrinter(HANDLE hPrinter)
+{
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
+{
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    if (!pHandle->bStartedDoc)
+    {
+        dwErrorCode = ERROR_SPL_NO_STARTDOC;
+        goto Cleanup;
+    }
+
+    if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
+    {
+        // Write to the spool file. This doesn't need an RPC request.
+        if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
+        {
+            dwErrorCode = GetLastError();
+            ERR("WriteFile failed with error %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+
+        dwErrorCode = ERROR_SUCCESS;
+    }
+    else
+    {
+        // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
+        // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
+
+        // Do the RPC call
+        RpcTryExcept
+        {
+            dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
+        }
+        RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwErrorCode = RpcExceptionCode();
+            ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
+        }
+        RpcEndExcept;
+    }
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
+{
+    return FALSE;
+}
diff --git a/reactos/win32ss/printing/base/winspool/printprocessors.c b/reactos/win32ss/printing/base/winspool/printprocessors.c
new file mode 100644 (file)
index 0000000..97a0759
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Print Processors
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static void
+_MarshallUpDatatypesInfo(PDATATYPES_INFO_1W pDatatypesInfo1)
+{
+    // Replace relative offset addresses in the output by absolute pointers.
+    pDatatypesInfo1->pName = (PWSTR)((ULONG_PTR)pDatatypesInfo1->pName + (ULONG_PTR)pDatatypesInfo1);
+}
+
+static void
+_MarshallUpPrintProcessorInfo(PPRINTPROCESSOR_INFO_1W pPrintProcessorInfo1)
+{
+    // Replace relative offset addresses in the output by absolute pointers.
+    pPrintProcessorInfo1->pName = (PWSTR)((ULONG_PTR)pPrintProcessorInfo1->pName + (ULONG_PTR)pPrintProcessorInfo1);
+}
+
+BOOL WINAPI
+AddPrintProcessorW(PWSTR pName, PWSTR pEnvironment, PWSTR pPathName, PWSTR pPrintProcessorName)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+DeletePrintProcessorW(PWSTR pName, PWSTR pEnvironment, PWSTR pPrintProcessorName)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+EnumPrintProcessorDatatypesA(PSTR pName, LPSTR pPrintProcessorName, DWORD Level, PBYTE pDatatypes, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+EnumPrintProcessorDatatypesW(PWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, PBYTE pDatatypes, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pDatatypes;
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcEnumPrintProcessorDatatypes(pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcEnumPrintProcessorDatatypes failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace relative offset addresses in the output by absolute pointers.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallUpDatatypesInfo((PDATATYPES_INFO_1W)p);
+            p += sizeof(DATATYPES_INFO_1W);
+        }
+    }
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+EnumPrintProcessorsW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE p = pPrintProcessorInfo;
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcEnumPrintProcessors(pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded, pcReturned);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcEnumPrintProcessors failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Replace relative offset addresses in the output by absolute pointers.
+        for (i = 0; i < *pcReturned; i++)
+        {
+            _MarshallUpPrintProcessorInfo((PPRINTPROCESSOR_INFO_1W)p);
+            p += sizeof(PRINTPROCESSOR_INFO_1W);
+        }
+    }
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+GetPrintProcessorDirectoryW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    BOOL bReturnValue = FALSE;
+    DWORD dwErrorCode;
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcGetPrintProcessorDirectory(pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded);
+        SetLastError(dwErrorCode);
+        bReturnValue = (dwErrorCode == ERROR_SUCCESS);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        ERR("_RpcGetPrintProcessorDirectory failed with exception code %lu!\n", RpcExceptionCode());
+    }
+    RpcEndExcept;
+
+    return bReturnValue;
+}
diff --git a/reactos/win32ss/printing/base/winspool/printproviders.c b/reactos/win32ss/printing/base/winspool/printproviders.c
new file mode 100644 (file)
index 0000000..f89310f
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * PROJECT:     ReactOS Spooler API
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Print Providers
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+BOOL WINAPI
+AddPrintProvidorW(PWSTR pName, DWORD Level, PBYTE pProviderInfo)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+DeletePrintProvidorW(PWSTR pName, PWSTR pEnvironment, PWSTR pPrintProviderName)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
similarity index 71%
rename from reactos/dll/win32/winspool/winspool.rc
rename to reactos/win32ss/printing/base/winspool/winspool.rc
index 139320b..c7b2a80 100644 (file)
@@ -1,5 +1,5 @@
 #define REACTOS_VERSION_DLL
-#define REACTOS_STR_FILE_DESCRIPTION  "Windows printer spooler"
+#define REACTOS_STR_FILE_DESCRIPTION  "ReactOS Spooler API"
 #define REACTOS_STR_INTERNAL_NAME     "winspool"
 #define REACTOS_STR_ORIGINAL_FILENAME "winspool.drv"
 #include <reactos/version.rc>
diff --git a/reactos/win32ss/printing/base/winspool/winspool.spec b/reactos/win32ss/printing/base/winspool/winspool.spec
new file mode 100644 (file)
index 0000000..5350f4f
--- /dev/null
@@ -0,0 +1,200 @@
+100 stub -noname EnumPrinterPropertySheets
+101 stub -noname ClusterSplOpen
+102 stub -noname ClusterSplClose
+103 stub -noname ClusterSplIsAlive
+104 stub PerfClose
+105 stub PerfCollect
+106 stub PerfOpen
+107 stub ADVANCEDSETUPDIALOG
+108 stub AbortPrinter
+109 stub AddFormA
+110 stub AddFormW
+111 stdcall AddJobA(long long ptr long ptr)
+112 stdcall AddJobW(long long ptr long ptr)
+113 stub AddMonitorA
+114 stdcall AddMonitorW(wstr long ptr)
+115 stub AddPortA
+116 stub AddPortExA
+117 stub AddPortExW
+118 stdcall AddPortW(wstr long wstr)
+119 stub AddPrintProcessorA
+120 stdcall AddPrintProcessorW(wstr wstr wstr wstr)
+121 stub AddPrintProvidorA
+122 stdcall AddPrintProvidorW(wstr long ptr)
+123 stub AddPrinterA
+124 stub AddPrinterConnectionA
+125 stub AddPrinterConnectionW
+126 stub AddPrinterDriverA
+127 stub AddPrinterDriverExA
+128 stub AddPrinterDriverExW
+129 stdcall AddPrinterDriverW(wstr long ptr)
+130 stdcall AddPrinterW(wstr long ptr)
+131 stub AdvancedDocumentPropertiesA
+132 stdcall AdvancedDocumentPropertiesW(long long wstr ptr ptr)
+133 stub AdvancedSetupDialog
+134 stdcall ClosePrinter(long)
+135 stub CloseSpoolFileHandle
+136 stub CommitSpoolData
+137 stub ConfigurePortA
+138 stdcall ConfigurePortW(wstr long wstr)
+139 stub ConnectToPrinterDlg
+140 stub ConvertAnsiDevModeToUnicodeDevmode
+141 stub ConvertUnicodeDevModeToAnsiDevmode
+142 stub CreatePrinterIC
+143 stub DEVICECAPABILITIES
+144 stub DEVICEMODE
+145 stub DeleteFormA
+146 stub DeleteFormW
+147 stub DeleteMonitorA
+148 stdcall DeleteMonitorW(wstr wstr wstr)
+149 stub DeletePortA
+150 stdcall DeletePortW(wstr long wstr)
+151 stub DeletePrintProcessorA
+152 stdcall DeletePrintProcessorW(wstr wstr wstr)
+153 stub DeletePrintProvidorA
+154 stdcall DeletePrintProvidorW(wstr wstr wstr)
+155 stub DeletePrinter
+156 stub DeletePrinterConnectionA
+157 stub DeletePrinterConnectionW
+158 stub DeletePrinterDataA
+159 stub DeletePrinterDataExA
+160 stub DeletePrinterDataExW
+161 stub DeletePrinterDataW
+162 stub DeletePrinterDriverA
+163 stub DeletePrinterDriverExA
+164 stub DeletePrinterDriverExW
+165 stdcall DeletePrinterDriverW(wstr wstr wstr)
+166 stub DeletePrinterIC
+167 stub DeletePrinterKeyA
+168 stub DeletePrinterKeyW
+169 stub DevQueryPrint
+170 stub DevQueryPrintEx
+171 stub DeviceCapabilities
+172 stdcall DeviceCapabilitiesA(str str long ptr ptr)
+173 stdcall DeviceCapabilitiesW(wstr wstr long ptr ptr)
+174 stub DeviceMode
+175 stub DevicePropertySheets
+176 stub DocumentEvent
+177 stdcall DocumentPropertiesA(long long ptr ptr ptr long)
+178 stdcall DocumentPropertiesW(long long ptr ptr ptr long)
+179 stub DocumentPropertySheets
+180 stub EXTDEVICEMODE
+181 stdcall EndDocPrinter(long)
+182 stdcall EndPagePrinter(long)
+183 stub EnumFormsA
+184 stub EnumFormsW
+185 stdcall EnumJobsA(long long long long ptr long ptr ptr)
+186 stdcall EnumJobsW(long long long long ptr long ptr ptr)
+187 stub EnumMonitorsA
+188 stdcall EnumMonitorsW(wstr long ptr long ptr ptr)
+189 stub EnumPortsA
+190 stdcall EnumPortsW(wstr long ptr long ptr ptr)
+191 stdcall EnumPrintProcessorDatatypesA(ptr ptr long ptr long ptr ptr)
+192 stdcall EnumPrintProcessorDatatypesW(ptr ptr long ptr long ptr ptr)
+193 stub EnumPrintProcessorsA
+194 stdcall EnumPrintProcessorsW(ptr ptr long ptr long ptr ptr)
+195 stub EnumPrinterDataA
+196 stub EnumPrinterDataExA
+197 stub EnumPrinterDataExW
+198 stub EnumPrinterDataW
+199 stub EnumPrinterDriversA
+200 stdcall EnumPrinterDriversW(wstr wstr long ptr long ptr ptr)
+201 stdcall GetDefaultPrinterA(ptr ptr)
+202 stub SetDefaultPrinterA
+203 stdcall GetDefaultPrinterW(ptr ptr)
+204 stub SetDefaultPrinterW
+205 stub -noname SplReadPrinter
+206 stub -noname AddPerMachineConnectionA
+207 stub -noname AddPerMachineConnectionW
+208 stub -noname DeletePerMachineConnectionA
+209 stub -noname DeletePerMachineConnectionW
+210 stub -noname EnumPerMachineConnectionsA
+211 stub -noname EnumPerMachineConnectionsW
+212 stub -noname LoadPrinterDriver
+213 stub -noname RefCntLoadDriver
+214 stub -noname RefCntUnloadDriver
+215 stub -noname ForceUnloadDriver
+216 stub -noname PublishPrinterA
+217 stub -noname PublishPrinterW
+218 stub -noname CallCommonPropertySheetUI
+219 stub -noname PrintUIQueueCreate
+220 stub -noname PrintUIPrinterPropPages
+221 stub -noname PrintUIDocumentDefaults
+222 stub -noname SendRecvBidiData
+223 stub -noname RouterFreeBidiResponseContainer
+224 stub -noname ExternalConnectToLd64In32Server
+225 stub EnumPrinterKeyA
+226 stub -noname PrintUIWebPnpEntry
+227 stub -noname PrintUIWebPnpPostEntry
+228 stub -noname PrintUICreateInstance
+229 stub -noname PrintUIDocumentPropertiesWrap
+230 stub -noname PrintUIPrinterSetup
+231 stub -noname PrintUIServerPropPages
+232 stub -noname AddDriverCatalog
+233 stub EnumPrinterKeyW
+234 stdcall EnumPrintersA(long ptr long ptr long ptr ptr)
+235 stdcall EnumPrintersW(long ptr long ptr long ptr ptr)
+236 stub ExtDeviceMode
+237 stub FindClosePrinterChangeNotification
+238 stub FindFirstPrinterChangeNotification
+239 stub FindNextPrinterChangeNotification
+240 stub FlushPrinter
+241 stub FreePrinterNotifyInfo
+242 stub GetFormA
+243 stub GetFormW
+244 stdcall GetJobA(long long long ptr long ptr)
+245 stdcall GetJobW(long long long ptr long ptr)
+246 stub GetPrintProcessorDirectoryA
+247 stdcall GetPrintProcessorDirectoryW(wstr wstr long ptr long ptr)
+248 stdcall GetPrinterA(long long ptr long ptr)
+249 stub GetPrinterDataA
+250 stub GetPrinterDataExA
+251 stub GetPrinterDataExW
+252 stdcall GetPrinterDataW(long wstr ptr ptr long ptr)
+253 stdcall GetPrinterDriverA(long str long ptr long ptr)
+254 stub GetPrinterDriverDirectoryA
+255 stdcall GetPrinterDriverDirectoryW(wstr wstr long ptr long ptr)
+256 stdcall GetPrinterDriverW(long wstr long ptr long ptr)
+257 stdcall GetPrinterW(long long ptr long ptr)
+258 stub GetSpoolFileHandle
+259 stdcall IsValidDevmodeA(ptr long)
+260 stdcall IsValidDevmodeW(ptr long)
+261 stdcall OpenPrinterA(str ptr ptr)
+262 stdcall OpenPrinterW(wstr ptr ptr)
+263 stub PlayGdiScriptOnPrinterIC
+264 stub PrinterMessageBoxA
+265 stub PrinterMessageBoxW
+266 stub PrinterProperties
+267 stub QueryColorProfile
+268 stub QueryRemoteFonts
+269 stub QuerySpoolMode
+270 stdcall ReadPrinter(long ptr long ptr)
+271 stub ResetPrinterA
+272 stdcall ResetPrinterW(long ptr)
+273 stdcall ScheduleJob(long long)
+274 stub SeekPrinter
+275 stub SetAllocFailCount
+276 stub SetFormA
+277 stub SetFormW
+278 stdcall SetJobA(long long long ptr long)
+279 stdcall SetJobW(long long long ptr long)
+280 stub SetPortA
+281 stub SetPortW
+282 stub SetPrinterA
+283 stub SetPrinterDataA
+284 stub SetPrinterDataExA
+285 stub SetPrinterDataExW
+286 stdcall SetPrinterDataW(long wstr long ptr long)
+287 stdcall SetPrinterW(long long ptr long)
+288 stub SplDriverUnloadComplete
+289 stub SpoolerDevQueryPrintW
+290 stdcall SpoolerInit()
+291 stub SpoolerPrinterEvent
+292 stub StartDocDlgA
+293 stub StartDocDlgW
+294 stdcall StartDocPrinterA(long long ptr)
+295 stdcall StartDocPrinterW(long long ptr)
+296 stdcall StartPagePrinter(long)
+297 stub WaitForPrinterChange
+298 stdcall WritePrinter(long ptr long ptr)
+299 stdcall XcvDataW(long wstr ptr long ptr long ptr ptr)
diff --git a/reactos/win32ss/printing/drivers/CMakeLists.txt b/reactos/win32ss/printing/drivers/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b8da0bb
--- /dev/null
@@ -0,0 +1,2 @@
+#add_subdirectory(tty)
+#add_subdirectory(unidrv)
diff --git a/reactos/win32ss/printing/include/spoolss.h b/reactos/win32ss/printing/include/spoolss.h
new file mode 100644 (file)
index 0000000..d1677ae
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * PROJECT:     ReactOS Printing Include files
+ * LICENSE:     GNU LGPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Undocumented APIs of the Spooler Router "spoolss.dll"
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#ifndef _REACTOS_SPOOLSS_H
+#define _REACTOS_SPOOLSS_H
+
+typedef struct _MARSHALL_DOWN_INFO
+{
+    DWORD dwOffset;             /** Byte offset of this element within the structure or MAXDWORD to indicate the end of the array */
+    DWORD cbSize;               /** Total size of this element in bytes under Windows. Unused here, I don't know what we need this number for. */
+    DWORD cbPerElementSize;     /** If this element is a structure itself, this field gives the size in bytes of each element of the structure.
+                                    Otherwise, this is the same as cbTotalSize. E.g. for SYSTEMTIME, cbSize would be 16 and cbPerElementSize would be 2.
+                                    Unused here, I don't know what we need this number for. */
+    BOOL bAdjustAddress;        /** TRUE if MarshallDownStructure shall adjust the address of this element, FALSE if it shall leave this element untouched. */
+}
+MARSHALL_DOWN_INFO, *PMARSHALL_DOWN_INFO;
+
+PWSTR WINAPI AllocSplStr(PCWSTR pwszInput);
+PVOID WINAPI DllAllocSplMem(DWORD dwBytes);
+BOOL WINAPI DllFreeSplMem(PVOID pMem);
+BOOL WINAPI DllFreeSplStr(PWSTR pwszString);
+BOOL WINAPI MarshallDownStructure(PVOID pStructure, PMARSHALL_DOWN_INFO pParameters, DWORD cbStructureSize, BOOL bSomeBoolean);
+PBYTE WINAPI PackStrings(PCWSTR* pSource, PBYTE pDest, PDWORD DestOffsets, PBYTE pEnd);
+PVOID WINAPI ReallocSplMem(PVOID pOldMem, DWORD cbOld, DWORD cbNew);
+BOOL WINAPI ReallocSplStr(PWSTR* ppwszString, PCWSTR pwszInput);
+BOOL WINAPI SplInitializeWinSpoolDrv(PVOID* pTable);
+
+#endif
diff --git a/reactos/win32ss/printing/monitors/CMakeLists.txt b/reactos/win32ss/printing/monitors/CMakeLists.txt
new file mode 100644 (file)
index 0000000..cc255ca
--- /dev/null
@@ -0,0 +1,4 @@
+add_subdirectory(localmon)
+#add_subdirectory(pjlmon)
+#add_subdirectory(tcpmon)
+#add_subdirectory(usbmon)
diff --git a/reactos/win32ss/printing/monitors/localmon/CMakeLists.txt b/reactos/win32ss/printing/monitors/localmon/CMakeLists.txt
new file mode 100644 (file)
index 0000000..dc83cc0
--- /dev/null
@@ -0,0 +1,22 @@
+
+add_subdirectory(ui)
+
+spec2def(localmon.dll localmon.spec ADD_IMPORTLIB)
+
+list(APPEND SOURCE
+    main.c
+    ports.c
+    precomp.h
+    tools.c
+    xcv.c)
+
+add_library(localmon SHARED
+    ${SOURCE}
+    localmon.rc
+    ${CMAKE_CURRENT_BINARY_DIR}/localmon.def)
+
+set_module_type(localmon win32dll UNICODE)
+target_link_libraries(localmon wine)
+add_importlibs(localmon advapi32 spoolss user32 msvcrt kernel32 ntdll)
+add_pch(localmon precomp.h SOURCE)
+add_cd_file(TARGET localmon DESTINATION reactos/system32 FOR all)
diff --git a/reactos/win32ss/printing/monitors/localmon/lang/de-DE.rc b/reactos/win32ss/printing/monitors/localmon/lang/de-DE.rc
new file mode 100644 (file)
index 0000000..9432798
--- /dev/null
@@ -0,0 +1,7 @@
+LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
+
+STRINGTABLE
+BEGIN
+    IDS_LOCAL_PORT "Lokaler Anschluss"
+    IDS_LOCAL_MONITOR "Lokaler Monitor"
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/lang/en-US.rc b/reactos/win32ss/printing/monitors/localmon/lang/en-US.rc
new file mode 100644 (file)
index 0000000..191450d
--- /dev/null
@@ -0,0 +1,7 @@
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+STRINGTABLE
+BEGIN
+    IDS_LOCAL_PORT "Local Port"
+    IDS_LOCAL_MONITOR "Local Monitor"
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/localmon.rc b/reactos/win32ss/printing/monitors/localmon/localmon.rc
new file mode 100644 (file)
index 0000000..72a3837
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * PROJECT:     ReactOS Local Port Monitor
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Resource file
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <windef.h>
+#include "resource.h"
+
+/* Version Information */
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION  "ReactOS Local Port Monitor"
+#define REACTOS_STR_INTERNAL_NAME     "localmon"
+#define REACTOS_STR_ORIGINAL_FILENAME "localmon.dll"
+#include <reactos/version.rc>
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+#ifdef LANGUAGE_DE_DE
+    #include "lang/de-DE.rc"
+#endif
+#ifdef LANGUAGE_EN_US
+    #include "lang/en-US.rc"
+#endif
diff --git a/reactos/win32ss/printing/monitors/localmon/localmon.spec b/reactos/win32ss/printing/monitors/localmon/localmon.spec
new file mode 100644 (file)
index 0000000..48db9c4
--- /dev/null
@@ -0,0 +1 @@
+@ stdcall InitializePrintMonitor2(ptr ptr)
diff --git a/reactos/win32ss/printing/monitors/localmon/main.c b/reactos/win32ss/printing/monitors/localmon/main.c
new file mode 100644 (file)
index 0000000..336a03a
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * PROJECT:     ReactOS Local Port Monitor
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Main functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+// Global Variables
+DWORD cbLocalMonitor;
+DWORD cbLocalPort;
+PCWSTR pwszLocalMonitor;
+PCWSTR pwszLocalPort;
+
+// Local Constants
+static MONITOR2 _MonitorFunctions = {
+    sizeof(MONITOR2),               // cbSize
+    LocalmonEnumPorts,              // pfnEnumPorts
+    LocalmonOpenPort,               // pfnOpenPort
+    NULL,                           // pfnOpenPortEx
+    LocalmonStartDocPort,           // pfnStartDocPort
+    LocalmonWritePort,              // pfnWritePort
+    LocalmonReadPort,               // pfnReadPort
+    LocalmonEndDocPort,             // pfnEndDocPort
+    LocalmonClosePort,              // pfnClosePort
+    NULL,                           // pfnAddPort
+    NULL,                           // pfnAddPortEx
+    NULL,                           // pfnConfigurePort
+    NULL,                           // pfnDeletePort
+    LocalmonGetPrinterDataFromPort, // pfnGetPrinterDataFromPort
+    LocalmonSetPortTimeOuts,        // pfnSetPortTimeOuts
+    LocalmonXcvOpenPort,            // pfnXcvOpenPort
+    LocalmonXcvDataPort,            // pfnXcvDataPort
+    LocalmonXcvClosePort,           // pfnXcvClosePort
+    LocalmonShutdown,               // pfnShutdown
+    NULL,                           // pfnSendRecvBidiDataFromPort
+};
+
+
+/**
+ * @name _IsNEPort
+ *
+ * Checks if the given port name is a virtual Ne port.
+ * A virtual Ne port may appear in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports and can have the formats
+ * Ne00:, Ne01:, Ne-02:, Ne456:
+ * This check is extra picky to not cause false positives (like file name ports starting with "Ne").
+ *
+ * @param pwszPortName
+ * The port name to check.
+ *
+ * @return
+ * TRUE if this is definitely a virtual Ne port, FALSE if not.
+ */
+static __inline BOOL
+_IsNEPort(PCWSTR pwszPortName)
+{
+    PCWSTR p = pwszPortName;
+
+    // First character needs to be 'N' (uppercase or lowercase)
+    if (*p != L'N' && *p != L'n')
+        return FALSE;
+
+    // Next character needs to be 'E' (uppercase or lowercase)
+    p++;
+    if (*p != L'E' && *p != L'e')
+        return FALSE;
+
+    // An optional hyphen may follow now.
+    p++;
+    if (*p == L'-')
+        p++;
+
+    // Now an arbitrary number of digits may follow.
+    while (*p >= L'0' && *p <= L'9')
+        p++;
+
+    // Finally, the virtual Ne port must be terminated by a colon.
+    if (*p != ':')
+        return FALSE;
+
+    // If this is the end of the string, we have a virtual Ne port.
+    p++;
+    return (*p == L'\0');
+}
+
+static void
+_LoadResources(HINSTANCE hinstDLL)
+{
+    LoadStringW(hinstDLL, IDS_LOCAL_MONITOR, (PWSTR)&pwszLocalMonitor, 0);
+    cbLocalMonitor = (wcslen(pwszLocalMonitor) + 1) * sizeof(WCHAR);
+
+    LoadStringW(hinstDLL, IDS_LOCAL_PORT, (PWSTR)&pwszLocalPort, 0);
+    cbLocalPort = (wcslen(pwszLocalPort) + 1) * sizeof(WCHAR);
+}
+
+BOOL WINAPI
+DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    switch (fdwReason)
+    {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls(hinstDLL);
+            _LoadResources(hinstDLL);
+            break;
+    }
+
+    return TRUE;
+}
+
+void WINAPI
+LocalmonShutdown(HANDLE hMonitor)
+{
+    PLOCALMON_HANDLE pLocalmon;
+    PLOCALMON_PORT pPort;
+    PLOCALMON_XCV pXcv;
+
+    TRACE("LocalmonShutdown(%p)\n", hMonitor);
+
+    pLocalmon = (PLOCALMON_HANDLE)hMonitor;
+
+    // Close all virtual file ports.
+    while (!IsListEmpty(&pLocalmon->FilePorts))
+    {
+        pPort = CONTAINING_RECORD(&pLocalmon->FilePorts.Flink, LOCALMON_PORT, Entry);
+        LocalmonClosePort((HANDLE)pPort);
+    }
+
+    // Do the same for the open Xcv ports.
+    while (!IsListEmpty(&pLocalmon->XcvHandles))
+    {
+        pXcv = CONTAINING_RECORD(&pLocalmon->XcvHandles.Flink, LOCALMON_XCV, Entry);
+        LocalmonXcvClosePort((HANDLE)pXcv);
+    }
+
+    // Now close all registry ports, remove them from the list and free their memory.
+    while (!IsListEmpty(&pLocalmon->RegistryPorts))
+    {
+        pPort = CONTAINING_RECORD(&pLocalmon->RegistryPorts.Flink, LOCALMON_PORT, Entry);
+        LocalmonClosePort((HANDLE)pPort);
+        RemoveEntryList(&pPort->Entry);
+        DllFreeSplMem(pPort);
+    }
+
+    // Finally clean the LOCALMON_HANDLE structure itself.
+    DeleteCriticalSection(&pLocalmon->Section);
+    DllFreeSplMem(pLocalmon);
+}
+
+PMONITOR2 WINAPI
+InitializePrintMonitor2(PMONITORINIT pMonitorInit, PHANDLE phMonitor)
+{
+    DWORD cchMaxPortName;
+    DWORD cchPortName;
+    DWORD dwErrorCode;
+    DWORD dwPortCount;
+    DWORD i;
+    HKEY hKey;
+    PMONITOR2 pReturnValue = NULL;
+    PLOCALMON_HANDLE pLocalmon;
+    PLOCALMON_PORT pPort = NULL;
+
+    TRACE("InitializePrintMonitor2(%p, %p)\n", pMonitorInit, phMonitor);
+
+    // Create a new LOCALMON_HANDLE structure.
+    pLocalmon = DllAllocSplMem(sizeof(LOCALMON_HANDLE));
+    InitializeCriticalSection(&pLocalmon->Section);
+    InitializeListHead(&pLocalmon->FilePorts);
+    InitializeListHead(&pLocalmon->RegistryPorts);
+    InitializeListHead(&pLocalmon->XcvHandles);
+
+    // The Local Spooler Port Monitor doesn't need to care about the given registry key and functions.
+    // Instead it uses a well-known registry key for getting its information about local ports. Open this one.
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", 0, KEY_READ, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Get the number of ports and the length of the largest port name.
+    dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwPortCount, &cchMaxPortName, NULL, NULL, NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Loop through all ports.
+    for (i = 0; i < dwPortCount; i++)
+    {
+        // Allocate memory for a new LOCALMON_PORT structure and its name.
+        pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + (cchMaxPortName + 1) * sizeof(WCHAR));
+        if (!pPort)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        pPort->pLocalmon = pLocalmon;
+        pPort->hFile = INVALID_HANDLE_VALUE;
+        pPort->pwszPortName = (PWSTR)((PBYTE)pPort + sizeof(LOCALMON_PORT));
+
+        // Get the port name.
+        cchPortName = cchMaxPortName + 1;
+        dwErrorCode = (DWORD)RegEnumValueW(hKey, i, pPort->pwszPortName, &cchPortName, NULL, NULL, NULL, NULL);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegEnumValueW failed with status %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+
+        // pwszPortName can be one of the following to be valid for this Port Monitor:
+        //    COMx:                        - Physical COM port
+        //    LPTx:                        - Physical LPT port (or redirected one using "net use LPT1 ...")
+        //    FILE:                        - Opens a prompt that asks for an output filename
+        //    C:\bla.txt                   - Redirection into the file "C:\bla.txt"
+        //    \\COMPUTERNAME\PrinterName   - Redirection to a shared network printer installed as a local port
+        //
+        // We can't detect valid and invalid ones by the name, so we can only exclude empty ports and the virtual "Ne00:", "Ne01:", ... ports.
+        // Skip the invalid ones here.
+        if (!cchPortName || _IsNEPort(pPort->pwszPortName))
+        {
+            DllFreeSplMem(pPort);
+            continue;
+        }
+
+        // Add it to the list.
+        InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry);
+
+        // Don't let the cleanup routine free this.
+        pPort = NULL;
+    }
+
+    // Return our handle and the Print Monitor functions.
+    *phMonitor = (HANDLE)pLocalmon;
+    pReturnValue = &_MonitorFunctions;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (pPort)
+        DllFreeSplMem(pPort);
+
+    SetLastError(dwErrorCode);
+    return pReturnValue;
+}
diff --git a/reactos/win32ss/printing/monitors/localmon/ports.c b/reactos/win32ss/printing/monitors/localmon/ports.c
new file mode 100644 (file)
index 0000000..a56b5fd
--- /dev/null
@@ -0,0 +1,1085 @@
+/*
+ * PROJECT:     ReactOS Local Port Monitor
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to ports
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+
+// Local Constants
+static const WCHAR wszNonspooledPrefix[] = L"NONSPOOLED_";
+static const DWORD cchNonspooledPrefix = _countof(wszNonspooledPrefix) - 1;
+
+
+/**
+ * @name _GetNonspooledPortName
+ *
+ * Prepends "NONSPOOLED_" to a port name without colon.
+ *
+ * @param pwszPortNameWithoutColon
+ * Result of a previous GetPortNameWithoutColon call.
+ *
+ * @param ppwszNonspooledPortName
+ * Pointer to a buffer that will contain the NONSPOOLED port name.
+ * You have to free this buffer using DllFreeSplMem.
+ *
+ * @return
+ * ERROR_SUCCESS if the NONSPOOLED port name was successfully copied into the buffer.
+ * ERROR_NOT_ENOUGH_MEMORY if memory allocation failed.
+ */
+static __inline DWORD
+_GetNonspooledPortName(PCWSTR pwszPortNameWithoutColon, PWSTR* ppwszNonspooledPortName)
+{
+    DWORD cchPortNameWithoutColon;
+
+    cchPortNameWithoutColon = wcslen(pwszPortNameWithoutColon);
+
+    *ppwszNonspooledPortName = DllAllocSplMem((cchNonspooledPrefix + cchPortNameWithoutColon + 1) * sizeof(WCHAR));
+    if (!*ppwszNonspooledPortName)
+    {
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
+
+    CopyMemory(*ppwszNonspooledPortName, wszNonspooledPrefix, cchNonspooledPrefix * sizeof(WCHAR));
+    CopyMemory(&(*ppwszNonspooledPortName)[cchNonspooledPrefix], pwszPortNameWithoutColon, (cchPortNameWithoutColon + 1) * sizeof(WCHAR));
+
+    return ERROR_SUCCESS;
+}
+
+/**
+ * @name _IsLegacyPort
+ *
+ * Checks if the given port name is a legacy port (COM or LPT).
+ * This check is extra picky to not cause false positives (like file name ports starting with "COM" or "LPT").
+ *
+ * @param pwszPortName
+ * The port name to check.
+ *
+ * @param pwszPortType
+ * L"COM" or L"LPT"
+ *
+ * @return
+ * TRUE if this is definitely the asked legacy port, FALSE if not.
+ */
+static __inline BOOL
+_IsLegacyPort(PCWSTR pwszPortName, PCWSTR pwszPortType)
+{
+    const DWORD cchPortType = 3;
+    PCWSTR p = pwszPortName;
+
+    // The port name must begin with pwszPortType.
+    if (_wcsnicmp(p, pwszPortType, cchPortType) != 0)
+        return FALSE;
+
+    p += cchPortType;
+
+    // Now an arbitrary number of digits may follow.
+    while (*p >= L'0' && *p <= L'9')
+        p++;
+
+    // Finally, the legacy port must be terminated by a colon.
+    if (*p != ':')
+        return FALSE;
+
+    // If this is the end of the string, we have a legacy port.
+    p++;
+    return (*p == L'\0');
+}
+
+/**
+ * @name _ClosePortHandles
+ *
+ * Closes a port of any type if it's open.
+ * Removes any saved mapping or existing definition of a NONSPOOLED device mapping.
+ *
+ * @param pPort
+ * The port you want to close.
+ */
+static void
+_ClosePortHandles(PLOCALMON_PORT pPort)
+{
+    PWSTR pwszNonspooledPortName;
+    PWSTR pwszPortNameWithoutColon;
+
+    // A port is already fully closed if the file handle is invalid.
+    if (pPort->hFile == INVALID_HANDLE_VALUE)
+        return;
+
+    // Close the file handle.
+    CloseHandle(pPort->hFile);
+    pPort->hFile = INVALID_HANDLE_VALUE;
+
+    // A NONSPOOLED port was only created if pwszMapping contains the current port mapping.
+    if (!pPort->pwszMapping)
+        return;
+
+    // Free the information about the current mapping.
+    DllFreeSplStr(pPort->pwszMapping);
+    pPort->pwszMapping = NULL;
+
+    // Finally get the required strings and remove the DOS device definition for the NONSPOOLED port.
+    if (GetPortNameWithoutColon(pPort->pwszPortName, &pwszPortNameWithoutColon) == ERROR_SUCCESS)
+    {
+        if (_GetNonspooledPortName(pwszPortNameWithoutColon, &pwszNonspooledPortName) == ERROR_SUCCESS)
+        {
+            DefineDosDeviceW(DDD_REMOVE_DEFINITION, pwszNonspooledPortName, NULL);
+            DllFreeSplMem(pwszNonspooledPortName);
+        }
+
+        DllFreeSplMem(pwszPortNameWithoutColon);
+    }
+}
+
+/**
+ * @name _CreateNonspooledPort
+ *
+ * Queries the system-wide device definition of the given port.
+ * If such a definition exists, it's a legacy port remapped to a named pipe by the spooler.
+ * In this case, the function creates and opens a NONSPOOLED device definition to the most recent mapping before the current one (usually the physical device).
+ *
+ * @param pPort
+ * Pointer to the LOCALMON_PORT structure of the desired port.
+ *
+ * @return
+ * TRUE if a NONSPOOLED port was successfully created, FALSE otherwise.
+ * A more specific error code can be obtained through GetLastError.
+ * In particular, if the return value is FALSE and GetLastError returns ERROR_SUCCESS, no NONSPOOLED port is needed for this port.
+ */
+static BOOL
+_CreateNonspooledPort(PLOCALMON_PORT pPort)
+{
+    const WCHAR wszLocalSlashes[] = L"\\\\.\\";
+    const DWORD cchLocalSlashes = _countof(wszLocalSlashes) - 1;
+
+    const WCHAR wszSpoolerNamedPipe[] = L"\\Device\\NamedPipe\\Spooler\\";
+    const DWORD cchSpoolerNamedPipe = _countof(wszSpoolerNamedPipe) - 1;
+
+    BOOL bReturnValue = FALSE;
+    DWORD cchPortNameWithoutColon;
+    DWORD dwErrorCode;
+    HANDLE hToken = NULL;
+    PWSTR p;
+    PWSTR pwszDeviceMappings = NULL;
+    PWSTR pwszNonspooledFileName = NULL;
+    PWSTR pwszNonspooledPortName = NULL;
+    PWSTR pwszPipeName = NULL;
+    PWSTR pwszPortNameWithoutColon = NULL;
+
+    // We need the port name without the trailing colon.
+    dwErrorCode = GetPortNameWithoutColon(pPort->pwszPortName, &pwszPortNameWithoutColon);
+    if (dwErrorCode == ERROR_INVALID_PARAMETER)
+    {
+        // This port has no trailing colon, so we also need no NONSPOOLED mapping for it.
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+    else if (dwErrorCode != ERROR_SUCCESS)
+    {
+        // Another unexpected failure.
+        goto Cleanup;
+    }
+
+    cchPortNameWithoutColon = wcslen(pwszPortNameWithoutColon);
+
+    // The spooler has usually remapped the legacy port to a named pipe of the format in wszSpoolerNamedPipe.
+    // Construct the device name of this pipe.
+    pwszPipeName = DllAllocSplMem((cchSpoolerNamedPipe + cchPortNameWithoutColon + 1) * sizeof(WCHAR));
+    if (!pwszPipeName)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    CopyMemory(pwszPipeName, wszSpoolerNamedPipe, cchSpoolerNamedPipe * sizeof(WCHAR));
+    CopyMemory(&pwszPipeName[cchSpoolerNamedPipe], pwszPortNameWithoutColon, (cchPortNameWithoutColon + 1) * sizeof(WCHAR));
+
+    // QueryDosDeviceW is one of the shitty APIs that gives no information about the required buffer size and wants you to know it by pure magic.
+    // Examples show that a value of MAX_PATH * sizeof(WCHAR) is usually taken here, so we have no other option either.
+    pwszDeviceMappings = DllAllocSplMem(MAX_PATH * sizeof(WCHAR));
+    if (!pwszDeviceMappings)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Switch to the SYSTEM context, because we're only interested in creating NONSPOOLED ports for system-wide ports.
+    // User-local ports (like _some_ redirected networked ones) aren't remapped by the spooler and can be opened directly.
+    hToken = RevertToPrinterSelf();
+    if (!hToken)
+    {
+        dwErrorCode = GetLastError();
+        ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // QueryDosDeviceW returns the current mapping and a list of prior mappings of this legacy port, which is managed as a DOS device in the system.
+    if (!QueryDosDeviceW(pwszPortNameWithoutColon, pwszDeviceMappings, MAX_PATH))
+    {
+        // No system-wide port exists, so we also need no NONSPOOLED mapping.
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+
+    // Check if this port has already been opened by _CreateNonspooledPort previously.
+    if (pPort->pwszMapping)
+    {
+        // In this case, we just need to do something if the mapping has changed.
+        // Therefore, check if the stored mapping equals the current mapping.
+        if (wcscmp(pPort->pwszMapping, pwszDeviceMappings) == 0)
+        {
+            // We don't need to do anything in this case.
+            dwErrorCode = ERROR_SUCCESS;
+            goto Cleanup;
+        }
+        else
+        {
+            // Close the open file handle and free the memory for pwszMapping before remapping.
+            CloseHandle(pPort->hFile);
+            pPort->hFile = INVALID_HANDLE_VALUE;
+
+            DllFreeSplStr(pPort->pwszMapping);
+            pPort->pwszMapping = NULL;
+        }
+    }
+
+    // The port is usually mapped to the named pipe and this is how we received our data for printing.
+    // What we now need for accessing the actual port is the most recent mapping different from the named pipe.
+    p = pwszDeviceMappings;
+
+    for (;;)
+    {
+        if (!*p)
+        {
+            // We reached the end of the list without finding a mapping.
+            ERR("Can't find a suitable mapping for the port \"%S\"!", pPort->pwszPortName);
+            goto Cleanup;
+        }
+
+        if (_wcsicmp(p, pwszPipeName) != 0)
+            break;
+
+        // Advance to the next mapping in the list.
+        p += wcslen(p) + 1;
+    }
+
+    // We now want to create a DOS device "NONSPOOLED_<PortName>" to this mapping, so that we're able to open it through CreateFileW.
+    dwErrorCode = _GetNonspooledPortName(pwszPortNameWithoutColon, &pwszNonspooledPortName);
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    // Delete a possibly existing NONSPOOLED device before creating the new one, so we don't stack up device definitions.
+    DefineDosDeviceW(DDD_REMOVE_DEFINITION, pwszNonspooledPortName, NULL);
+
+    if (!DefineDosDeviceW(DDD_RAW_TARGET_PATH, pwszNonspooledPortName, p))
+    {
+        dwErrorCode = GetLastError();
+        ERR("DefineDosDeviceW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // This is all we needed to do in SYSTEM context.
+    ImpersonatePrinterClient(hToken);
+    hToken = NULL;
+
+    // Construct the file name to our created device for CreateFileW.
+    pwszNonspooledFileName = DllAllocSplMem((cchLocalSlashes + cchNonspooledPrefix + cchPortNameWithoutColon + 1) * sizeof(WCHAR));
+    if (!pwszNonspooledFileName)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    CopyMemory(pwszNonspooledFileName, wszLocalSlashes, cchLocalSlashes * sizeof(WCHAR));
+    CopyMemory(&pwszNonspooledFileName[cchLocalSlashes], wszNonspooledPrefix, cchNonspooledPrefix * sizeof(WCHAR));
+    CopyMemory(&pwszNonspooledFileName[cchLocalSlashes + cchNonspooledPrefix], pwszPortNameWithoutColon, (cchPortNameWithoutColon + 1) * sizeof(WCHAR));
+
+    // Finally open it for reading and writing.
+    pPort->hFile = CreateFileW(pwszNonspooledFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
+    if (pPort->hFile == INVALID_HANDLE_VALUE)
+    {
+        dwErrorCode = GetLastError();
+        ERR("CreateFileW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Store the current mapping of the port, so that we can check if it has changed.
+    pPort->pwszMapping = AllocSplStr(pwszDeviceMappings);
+    if (!pPort->pwszMapping)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        goto Cleanup;
+    }
+
+    bReturnValue = TRUE;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (hToken)
+        ImpersonatePrinterClient(hToken);
+
+    if (pwszDeviceMappings)
+        DllFreeSplMem(pwszDeviceMappings);
+
+    if (pwszNonspooledFileName)
+        DllFreeSplMem(pwszNonspooledFileName);
+
+    if (pwszNonspooledPortName)
+        DllFreeSplMem(pwszNonspooledPortName);
+
+    if (pwszPipeName)
+        DllFreeSplMem(pwszPipeName);
+
+    if (pwszPortNameWithoutColon)
+        DllFreeSplMem(pwszPortNameWithoutColon);
+
+    SetLastError(dwErrorCode);
+    return bReturnValue;
+}
+
+static PLOCALMON_PORT
+_FindPort(PLOCALMON_HANDLE pLocalmon, PCWSTR pwszPortName)
+{
+    PLIST_ENTRY pEntry;
+    PLOCALMON_PORT pPort;
+
+    for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
+    {
+        pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
+
+        if (wcscmp(pPort->pwszPortName, pwszPortName) == 0)
+            return pPort;
+    }
+
+    return NULL;
+}
+
+static DWORD
+_LocalmonEnumPortsLevel1(PLOCALMON_HANDLE pLocalmon, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD cbPortName;
+    DWORD dwErrorCode;
+    DWORD dwPortCount = 0;
+    PBYTE pPortInfo;
+    PBYTE pPortString;
+    PLIST_ENTRY pEntry;
+    PLOCALMON_PORT pPort;
+    PORT_INFO_1W PortInfo1;
+
+    // Count the required buffer size and the number of datatypes.
+    for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
+    {
+        pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
+
+        cbPortName = (wcslen(pPort->pwszPortName) + 1) * sizeof(WCHAR);
+        *pcbNeeded += sizeof(PORT_INFO_1W) + cbPortName;
+        dwPortCount++;
+    }
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Put the strings right after the last PORT_INFO_1W structure.
+    pPortInfo = pPorts;
+    pPortString = pPorts + dwPortCount * sizeof(PORT_INFO_1W);
+
+    // Copy over all ports.
+    for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
+    {
+        pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
+
+        // Copy the port name.
+        PortInfo1.pName = (PWSTR)pPortString;
+        cbPortName = (wcslen(pPort->pwszPortName) + 1) * sizeof(WCHAR);
+        CopyMemory(pPortString, pPort->pwszPortName, cbPortName);
+        pPortString += cbPortName;
+
+        // Copy the structure and advance to the next one in the output buffer.
+        CopyMemory(pPortInfo, &PortInfo1, sizeof(PORT_INFO_1W));
+        pPortInfo += sizeof(PORT_INFO_1W);
+    }
+
+    *pcReturned = dwPortCount;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    return dwErrorCode;
+}
+
+static DWORD
+_LocalmonEnumPortsLevel2(PLOCALMON_HANDLE pLocalmon, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD cbPortName;
+    DWORD dwErrorCode;
+    DWORD dwPortCount = 0;
+    PBYTE pPortInfo;
+    PBYTE pPortString;
+    PLIST_ENTRY pEntry;
+    PLOCALMON_PORT pPort;
+    PORT_INFO_2W PortInfo2;
+
+    // Count the required buffer size and the number of datatypes.
+    for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
+    {
+        pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
+
+        cbPortName = (wcslen(pPort->pwszPortName) + 1) * sizeof(WCHAR);
+        *pcbNeeded += sizeof(PORT_INFO_2W) + cbPortName + cbLocalMonitor + cbLocalPort;
+        dwPortCount++;
+    }
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Put the strings right after the last PORT_INFO_2W structure.
+    pPortInfo = pPorts;
+    pPortString = pPorts + dwPortCount * sizeof(PORT_INFO_2W);
+
+    // Copy over all ports.
+    for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
+    {
+        pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
+
+        // All local ports are writable and readable.
+        PortInfo2.fPortType = PORT_TYPE_WRITE | PORT_TYPE_READ;
+        PortInfo2.Reserved = 0;
+
+        // Copy the port name.
+        PortInfo2.pPortName = (PWSTR)pPortString;
+        cbPortName = (wcslen(pPort->pwszPortName) + 1) * sizeof(WCHAR);
+        CopyMemory(pPortString, pPort->pwszPortName, cbPortName);
+        pPortString += cbPortName;
+
+        // Copy the monitor name.
+        PortInfo2.pMonitorName = (PWSTR)pPortString;
+        CopyMemory(pPortString, pwszLocalMonitor, cbLocalMonitor);
+        pPortString += cbLocalMonitor;
+
+        // Copy the description.
+        PortInfo2.pDescription = (PWSTR)pPortString;
+        CopyMemory(pPortString, pwszLocalPort, cbLocalPort);
+        pPortString += cbLocalPort;
+
+        // Copy the structure and advance to the next one in the output buffer.
+        CopyMemory(pPortInfo, &PortInfo2, sizeof(PORT_INFO_2W));
+        pPortInfo += sizeof(PORT_INFO_2W);
+    }
+
+    *pcReturned = dwPortCount;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    return dwErrorCode;
+}
+
+/**
+ * @name _SetTransmissionRetryTimeout
+ *
+ * Checks if the given port is a physical one and sets the transmission retry timeout in this case using the value from registry.
+ *
+ * @param pPort
+ * The port to operate on.
+ *
+ * @return
+ * TRUE if the given port is a physical one, FALSE otherwise.
+ */
+static BOOL
+_SetTransmissionRetryTimeout(PLOCALMON_PORT pPort)
+{
+    COMMTIMEOUTS CommTimeouts;
+
+    // Get the timeout from the port.
+    if (!GetCommTimeouts(pPort->hFile, &CommTimeouts))
+        return FALSE;
+
+    // Set the timeout using the value from registry.
+    CommTimeouts.WriteTotalTimeoutConstant = GetLPTTransmissionRetryTimeout() * 1000;
+    SetCommTimeouts(pPort->hFile, &CommTimeouts);
+
+    return TRUE;
+}
+
+BOOL WINAPI
+LocalmonClosePort(HANDLE hPort)
+{
+    PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
+
+    TRACE("LocalmonClosePort(%p)\n", hPort);
+
+    // Sanity checks
+    if (!pPort)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // Close the file handle, free memory for pwszMapping and delete any NONSPOOLED port.
+    _ClosePortHandles(pPort);
+
+    // Close any open printer handle.
+    if (pPort->hPrinter)
+    {
+        ClosePrinter(pPort->hPrinter);
+        pPort->hPrinter = NULL;
+    }
+
+    // Free virtual FILE: ports which were created in LocalmonOpenPort.
+    if (pPort->PortType == PortType_FILE)
+    {
+        EnterCriticalSection(&pPort->pLocalmon->Section);
+        RemoveEntryList(&pPort->Entry);
+        DllFreeSplMem(pPort);
+        LeaveCriticalSection(&pPort->pLocalmon->Section);
+    }
+
+    SetLastError(ERROR_SUCCESS);
+    return TRUE;
+}
+
+BOOL WINAPI
+LocalmonEndDocPort(HANDLE hPort)
+{
+    PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
+
+    TRACE("LocalmonEndDocPort(%p)\n", hPort);
+
+    // Sanity checks
+    if (!pPort)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // Ending a document requires starting it first :-P
+    if (pPort->bStartedDoc)
+    {
+        // Close all ports opened in StartDocPort.
+        // That is, all but physical LPT ports (opened in OpenPort).
+        if (pPort->PortType != PortType_PhysicalLPT)
+            _ClosePortHandles(pPort);
+
+        // Report our progress.
+        SetJobW(pPort->hPrinter, pPort->dwJobID, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
+
+        // We're done with the printer.
+        ClosePrinter(pPort->hPrinter);
+        pPort->hPrinter = NULL;
+
+        // A new document can now be started again.
+        pPort->bStartedDoc = FALSE;
+    }
+
+    SetLastError(ERROR_SUCCESS);
+    return TRUE;
+}
+
+BOOL WINAPI
+LocalmonEnumPorts(HANDLE hMonitor, PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD dwErrorCode;
+    PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
+
+    TRACE("LocalmonEnumPorts(%p, %S, %lu, %p, %lu, %p, %p)\n", hMonitor, pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
+
+    // Sanity checks
+    if (!pLocalmon || (cbBuf && !pPorts) || !pcbNeeded || !pcReturned)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    if (Level > 2)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    // Begin counting.
+    *pcbNeeded = 0;
+    *pcReturned = 0;
+
+    EnterCriticalSection(&pLocalmon->Section);
+
+    // The function behaves differently for each level.
+    if (Level == 1)
+        dwErrorCode = _LocalmonEnumPortsLevel1(pLocalmon, pPorts, cbBuf, pcbNeeded, pcReturned);
+    else if (Level == 2)
+        dwErrorCode = _LocalmonEnumPortsLevel2(pLocalmon, pPorts, cbBuf, pcbNeeded, pcReturned);
+
+    LeaveCriticalSection(&pLocalmon->Section);
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+/*
+ * @name LocalmonGetPrinterDataFromPort
+ *
+ * Performs a DeviceIoControl call for the given port.
+ *
+ * @param hPort
+ * The port to operate on.
+ *
+ * @param ControlID
+ * The dwIoControlCode passed to DeviceIoControl. Must not be zero!
+ *
+ * @param pValueName
+ * This parameter is ignored.
+ *
+ * @param lpInBuffer
+ * The lpInBuffer passed to DeviceIoControl.
+ *
+ * @param cbInBuffer
+ * The nInBufferSize passed to DeviceIoControl.
+ *
+ * @param lpOutBuffer
+ * The lpOutBuffer passed to DeviceIoControl.
+ *
+ * @param cbOutBuffer
+ * The nOutBufferSize passed to DeviceIoControl.
+ *
+ * @param lpcbReturned
+ * The lpBytesReturned passed to DeviceIoControl. Must not be zero!
+ *
+ * @return
+ * TRUE if the DeviceIoControl call was successful, FALSE otherwise.
+ * A more specific error code can be obtained through GetLastError.
+ */
+BOOL WINAPI
+LocalmonGetPrinterDataFromPort(HANDLE hPort, DWORD ControlID, PWSTR pValueName, PWSTR lpInBuffer, DWORD cbInBuffer, PWSTR lpOutBuffer, DWORD cbOutBuffer, PDWORD lpcbReturned)
+{
+    BOOL bOpenedPort = FALSE;
+    DWORD dwErrorCode;
+    PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
+
+    TRACE("LocalmonGetPrinterDataFromPort(%p, %lu, %p, %p, %lu, %p, %lu, %p)\n", hPort, ControlID, pValueName, lpInBuffer, cbInBuffer, lpOutBuffer, cbOutBuffer, lpcbReturned);
+
+    // Sanity checks
+    if (!pPort || !ControlID || !lpcbReturned)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // If this is a serial port, a temporary file handle may be opened.
+    if (pPort->PortType == PortType_PhysicalCOM)
+    {
+        if (_CreateNonspooledPort(pPort))
+        {
+            bOpenedPort = TRUE;
+        }
+        else if (GetLastError() != ERROR_SUCCESS)
+        {
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+    }
+    else if (pPort->hFile == INVALID_HANDLE_VALUE)
+    {
+        // All other port types need to be opened already.
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Pass the parameters to DeviceIoControl.
+    if (!DeviceIoControl(pPort->hFile, ControlID, lpInBuffer, cbInBuffer, lpOutBuffer, cbOutBuffer, lpcbReturned, NULL))
+    {
+        dwErrorCode = GetLastError();
+        ERR("DeviceIoControl failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (bOpenedPort)
+        _ClosePortHandles(pPort);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalmonOpenPort(HANDLE hMonitor, PWSTR pName, PHANDLE pHandle)
+{
+    DWORD dwErrorCode;
+    PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
+    PLOCALMON_PORT pPort;
+
+    TRACE("LocalmonOpenPort(%p, %S, %p)\n", hMonitor, pName, pHandle);
+
+    // Sanity checks
+    if (!pLocalmon || !pName || !pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Check if this is a FILE: port.
+    if (_wcsicmp(pName, L"FILE:") == 0)
+    {
+        // For FILE:, we create a virtual port for each request.
+        pPort = DllAllocSplMem(sizeof(LOCALMON_PORT));
+        pPort->pLocalmon = pLocalmon;
+        pPort->PortType = PortType_FILE;
+        pPort->hFile = INVALID_HANDLE_VALUE;
+
+        // Add it to the list of file ports.
+        EnterCriticalSection(&pLocalmon->Section);
+        InsertTailList(&pLocalmon->FilePorts, &pPort->Entry);
+    }
+    else
+    {
+        EnterCriticalSection(&pLocalmon->Section);
+
+        // Check if the port name is valid.
+        pPort = _FindPort(pLocalmon, pName);
+        if (!pPort)
+        {
+            LeaveCriticalSection(&pLocalmon->Section);
+            dwErrorCode = ERROR_UNKNOWN_PORT;
+            goto Cleanup;
+        }
+
+        // Even if this API is called OpenPort, port file handles aren't always opened here :-P
+        // Windows only does this for physical LPT ports here to enable bidirectional communication with the printer outside of jobs (using ReadPort and WritePort).
+        // The others are only opened per job in StartDocPort.
+        if (_IsLegacyPort(pName, L"LPT"))
+        {
+            // Try to create a NONSPOOLED port and open it.
+            if (_CreateNonspooledPort(pPort))
+            {
+                // Set the transmission retry timeout for the ReadPort and WritePort calls.
+                // This also checks if this port is a physical one.
+                if (_SetTransmissionRetryTimeout(pPort))
+                {
+                    // This is definitely a physical LPT port!
+                    pPort->PortType = PortType_PhysicalLPT;
+                }
+                else
+                {
+                    // This is no physical port, so don't keep its handle open.
+                    _ClosePortHandles(pPort);
+                }
+            }
+            else if (GetLastError() != ERROR_SUCCESS)
+            {
+                LeaveCriticalSection(&pLocalmon->Section);
+                dwErrorCode = GetLastError();
+                goto Cleanup;
+            }
+        }
+        else if (_IsLegacyPort(pName, L"COM"))
+        {
+            // COM ports can't be redirected over the network, so this is a physical one.
+            pPort->PortType = PortType_PhysicalCOM;
+        }
+    }
+
+    LeaveCriticalSection(&pLocalmon->Section);
+
+    // Return our fetched LOCALMON_PORT structure in the handle.
+    *pHandle = (PHANDLE)pPort;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+/*
+ * @name LocalmonSetPortTimeOuts
+ *
+ * Performs a SetCommTimeouts call for the given port.
+ *
+ * @param hPort
+ * The port to operate on.
+ *
+ * @param lpCTO
+ * Pointer to a COMMTIMEOUTS structure that is passed to SetCommTimeouts.
+ *
+ * @param Reserved
+ * Reserved parameter, must be 0.
+ *
+ * @return
+ * TRUE if the SetCommTimeouts call was successful, FALSE otherwise.
+ * A more specific error code can be obtained through GetLastError.
+ */
+BOOL WINAPI
+LocalmonSetPortTimeOuts(HANDLE hPort, LPCOMMTIMEOUTS lpCTO, DWORD Reserved)
+{
+    BOOL bOpenedPort = FALSE;
+    DWORD dwErrorCode;
+    PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
+
+    TRACE("LocalmonSetPortTimeOuts(%p, %p, %lu)\n", hPort, lpCTO, Reserved);
+
+    // Sanity checks
+    if (!pPort || !lpCTO)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // If this is a serial port, a temporary file handle may be opened.
+    if (pPort->PortType == PortType_PhysicalCOM)
+    {
+        if (_CreateNonspooledPort(pPort))
+        {
+            bOpenedPort = TRUE;
+        }
+        else if (GetLastError() != ERROR_SUCCESS)
+        {
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+    }
+    else if (pPort->hFile == INVALID_HANDLE_VALUE)
+    {
+        // All other port types need to be opened already.
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Finally pass the parameters to SetCommTimeouts.
+    if (!SetCommTimeouts(pPort->hFile, lpCTO))
+    {
+        dwErrorCode = GetLastError();
+        ERR("SetCommTimeouts failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (bOpenedPort)
+        _ClosePortHandles(pPort);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalmonReadPort(HANDLE hPort, PBYTE pBuffer, DWORD cbBuffer, PDWORD pcbRead)
+{
+    BOOL bOpenedPort = FALSE;
+    DWORD dwErrorCode;
+    PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
+
+    TRACE("LocalmonReadPort(%p, %p, %lu, %p)\n", hPort, pBuffer, cbBuffer, pcbRead);
+
+    // Sanity checks
+    if (!pPort || (cbBuffer && !pBuffer) || !pcbRead)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Reading is only supported for physical ports.
+    if (pPort->PortType != PortType_PhysicalCOM && pPort->PortType != PortType_PhysicalLPT)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // If this is a serial port, a temporary file handle may be opened.
+    if (pPort->PortType == PortType_PhysicalCOM)
+    {
+        if (_CreateNonspooledPort(pPort))
+        {
+            bOpenedPort = TRUE;
+        }
+        else if (GetLastError() != ERROR_SUCCESS)
+        {
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+    }
+
+    // Pass the parameters to ReadFile.
+    if (!ReadFile(pPort->hFile, pBuffer, cbBuffer, pcbRead, NULL))
+    {
+        dwErrorCode = GetLastError();
+        ERR("ReadFile failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+Cleanup:
+    if (bOpenedPort)
+        _ClosePortHandles(pPort);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalmonStartDocPort(HANDLE hPort, PWSTR pPrinterName, DWORD JobId, DWORD Level, PBYTE pDocInfo)
+{
+    DWORD dwErrorCode;
+    PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;        // DOC_INFO_1W is the least common denominator for both DOC_INFO levels.
+    PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
+
+    TRACE("LocalmonStartDocPort(%p, %S, %lu, %lu, %p)\n", hPort, pPrinterName, JobId, Level, pDocInfo);
+
+    // Sanity checks
+    if (!pPort || !pPrinterName || (pPort->PortType == PortType_FILE && (!pDocInfo1 || !pDocInfo1->pOutputFile || !*pDocInfo1->pOutputFile)))
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    if (Level > 2)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    // Calling StartDocPort multiple times isn't considered a failure, but we don't need to do anything then.
+    if (pPort->bStartedDoc)
+    {
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+
+    // Open a handle to the given printer for later reporting our progress using SetJobW.
+    if (!OpenPrinterW(pPrinterName, &pPort->hPrinter, NULL))
+    {
+        dwErrorCode = GetLastError();
+        ERR("OpenPrinterW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // We need our Job ID for SetJobW as well.
+    pPort->dwJobID = JobId;
+
+    // Check the port type.
+    if (pPort->PortType == PortType_PhysicalLPT)
+    {
+        // Update the NONSPOOLED mapping if the port mapping has changed since our OpenPort call.
+        if (!_CreateNonspooledPort(pPort) && GetLastError() != ERROR_SUCCESS)
+        {
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // Update the transmission retry timeout as well.
+        _SetTransmissionRetryTimeout(pPort);
+    }
+    else if(pPort->PortType == PortType_FILE)
+    {
+        // This is a FILE: port. Open the output file given in the Document Info.
+        pPort->hFile = CreateFileW(pDocInfo1->pOutputFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
+        if (pPort->hFile == INVALID_HANDLE_VALUE)
+        {
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+    }
+    else
+    {
+        // This can be:
+        //    - a physical COM port
+        //    - a non-physical LPT port (e.g. with "net use LPT1 ...")
+        //    - any other port (e.g. a file or a shared printer installed as a local port)
+        //
+        // For all these cases, we try to create a NONSPOOLED port per job.
+        // If _CreateNonspooledPort reports that no NONSPOOLED port is necessary, we can just open the port name.
+        if (!_CreateNonspooledPort(pPort))
+        {
+            if (GetLastError() == ERROR_SUCCESS)
+            {
+                pPort->hFile = CreateFileW(pPort->pwszPortName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
+                if (pPort->hFile == INVALID_HANDLE_VALUE)
+                {
+                    dwErrorCode = GetLastError();
+                    goto Cleanup;
+                }
+            }
+            else
+            {
+                dwErrorCode = GetLastError();
+                goto Cleanup;
+            }
+        }
+    }
+
+    // We were successful!
+    dwErrorCode = ERROR_SUCCESS;
+    pPort->bStartedDoc = TRUE;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalmonWritePort(HANDLE hPort, PBYTE pBuffer, DWORD cbBuf, PDWORD pcbWritten)
+{
+    BOOL bOpenedPort = FALSE;
+    DWORD dwErrorCode;
+    PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
+
+    TRACE("LocalmonWritePort(%p, %p, %lu, %p)\n", hPort, pBuffer, cbBuf, pcbWritten);
+
+    // Sanity checks
+    if (!pPort || (cbBuf && !pBuffer) || !pcbWritten)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // If this is a serial port, a temporary file handle may be opened.
+    if (pPort->PortType == PortType_PhysicalCOM)
+    {
+        if (_CreateNonspooledPort(pPort))
+        {
+            bOpenedPort = TRUE;
+        }
+        else if (GetLastError() != ERROR_SUCCESS)
+        {
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+    }
+    else if (pPort->hFile == INVALID_HANDLE_VALUE)
+    {
+        // All other port types need to be opened already.
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Pass the parameters to WriteFile.
+    if (!WriteFile(pPort->hFile, pBuffer, cbBuf, pcbWritten, NULL))
+    {
+        dwErrorCode = GetLastError();
+        ERR("WriteFile failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // If something was written down, we consider that a success, otherwise it's a timeout.
+    if (*pcbWritten)
+        dwErrorCode = ERROR_SUCCESS;
+    else
+        dwErrorCode = ERROR_TIMEOUT;
+
+Cleanup:
+    if (bOpenedPort)
+        _ClosePortHandles(pPort);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
diff --git a/reactos/win32ss/printing/monitors/localmon/precomp.h b/reactos/win32ss/printing/monitors/localmon/precomp.h
new file mode 100644 (file)
index 0000000..6e5388d
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * PROJECT:     ReactOS Local Port Monitor
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Precompiled Header for all source files
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#ifndef _PRECOMP_H
+#define _PRECOMP_H
+
+#define WIN32_NO_STATUS
+#include <stdlib.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winreg.h>
+#include <winspool.h>
+#include <winsplp.h>
+#include <winuser.h>
+#include <ndk/rtlfuncs.h>
+
+#include <spoolss.h>
+
+#include <wine/debug.h>
+WINE_DEFAULT_DEBUG_CHANNEL(localmon);
+
+#include "resource.h"
+
+// Structures
+/**
+ * Describes the monitor handle returned by InitializePrintMonitor2.
+ * Manages all available ports in this instance.
+ */
+typedef struct _LOCALMON_HANDLE
+{
+    CRITICAL_SECTION Section;       /** Critical Section for modifying or reading the ports. */
+    LIST_ENTRY FilePorts;           /** Ports created when a document is printed on FILE: and the user entered a file name. */
+    LIST_ENTRY RegistryPorts;       /** Valid ports loaded from the local registry. */
+    LIST_ENTRY XcvHandles;          /** Xcv handles created with LocalmonXcvOpenPort. */
+}
+LOCALMON_HANDLE, *PLOCALMON_HANDLE;
+
+/**
+ * Describes the port handle returned by LocalmonOpenPort.
+ * Manages a legacy port (COM/LPT) or virtual FILE: port for printing as well as its associated printer and job.
+ */
+typedef struct _LOCALMON_PORT
+{
+    LIST_ENTRY Entry;
+    enum {
+        PortType_Other = 0,         /** Any port that doesn't belong into the other categories (default). */
+        PortType_FILE,              /** A port created when a document is printed on FILE: and the user entered a file name. */
+        PortType_PhysicalCOM,       /** A physical serial port (COM) */
+        PortType_PhysicalLPT        /** A physical parallel port (LPT) */
+    }
+    PortType;
+    BOOL bStartedDoc;               /** Whether a document has been started with StartDocPort. */
+    DWORD dwJobID;                  /** ID of the printing job we are processing (for later reporting progress using SetJobW). */
+    HANDLE hFile;                   /** Handle to the opened port or INVALID_HANDLE_VALUE if it isn't currently opened. */
+    HANDLE hPrinter;                /** Handle to the printer for the job on this port (for using SetJobW). */
+    PLOCALMON_HANDLE pLocalmon;     /** Pointer to the parent LOCALMON_HANDLE structure. */
+    PWSTR pwszMapping;              /** The current mapping of the DOS Device corresponding to this port at the time _CreateNonspooledPort has been called. */
+    PWSTR pwszPortName;             /** The name of this port including the trailing colon. Empty for virtual file ports. */
+}
+LOCALMON_PORT, *PLOCALMON_PORT;
+
+/**
+ * Describes the Xcv handle returned by LocalmonXcvOpenPort.
+ * Manages the required data for the Xcv* calls.
+ */
+typedef struct _LOCALMON_XCV
+{
+    LIST_ENTRY Entry;
+    ACCESS_MASK GrantedAccess;
+    PLOCALMON_HANDLE pLocalmon;
+    PWSTR pwszObject;
+}
+LOCALMON_XCV, *PLOCALMON_XCV;
+
+// main.c
+extern DWORD cbLocalMonitor;
+extern DWORD cbLocalPort;
+extern PCWSTR pwszLocalMonitor;
+extern PCWSTR pwszLocalPort;
+void WINAPI LocalmonShutdown(HANDLE hMonitor);
+
+// ports.c
+BOOL WINAPI LocalmonClosePort(HANDLE hPort);
+BOOL WINAPI LocalmonEndDocPort(HANDLE hPort);
+BOOL WINAPI LocalmonEnumPorts(HANDLE hMonitor, PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned);
+BOOL WINAPI LocalmonGetPrinterDataFromPort(HANDLE hPort, DWORD ControlID, PWSTR pValueName, PWSTR lpInBuffer, DWORD cbInBuffer, PWSTR lpOutBuffer, DWORD cbOutBuffer, PDWORD lpcbReturned);
+BOOL WINAPI LocalmonOpenPort(HANDLE hMonitor, PWSTR pName, PHANDLE pHandle);
+BOOL WINAPI LocalmonReadPort(HANDLE hPort, PBYTE pBuffer, DWORD cbBuffer, PDWORD pcbRead);
+BOOL WINAPI LocalmonSetPortTimeOuts(HANDLE hPort, LPCOMMTIMEOUTS lpCTO, DWORD Reserved);
+BOOL WINAPI LocalmonStartDocPort(HANDLE hPort, PWSTR pPrinterName, DWORD JobId, DWORD Level, PBYTE pDocInfo);
+BOOL WINAPI LocalmonWritePort(HANDLE hPort, PBYTE pBuffer, DWORD cbBuf, PDWORD pcbWritten);
+
+// tools.c
+BOOL DoesPortExist(PCWSTR pwszPortName);
+DWORD GetLPTTransmissionRetryTimeout();
+DWORD GetPortNameWithoutColon(PCWSTR pwszPortName, PWSTR* ppwszPortNameWithoutColon);
+
+// xcv.c
+BOOL WINAPI LocalmonXcvClosePort(HANDLE hXcv);
+DWORD WINAPI LocalmonXcvDataPort(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded);
+BOOL WINAPI LocalmonXcvOpenPort(HANDLE hMonitor, PCWSTR pszObject, ACCESS_MASK GrantedAccess, PHANDLE phXcv);
+
+#endif
diff --git a/reactos/win32ss/printing/monitors/localmon/resource.h b/reactos/win32ss/printing/monitors/localmon/resource.h
new file mode 100644 (file)
index 0000000..850f1f7
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * PROJECT:     ReactOS Local Port Monitor
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Resource IDs
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#pragma once
+
+#define IDS_LOCAL_PORT           500
+#define IDS_LOCAL_MONITOR        507
diff --git a/reactos/win32ss/printing/monitors/localmon/tools.c b/reactos/win32ss/printing/monitors/localmon/tools.c
new file mode 100644 (file)
index 0000000..61e047c
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * PROJECT:     ReactOS Local Port Monitor
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Various support functions shared by multiple files
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+/**
+ * @name DoesPortExist
+ *
+ * Checks all Port Monitors installed on the local system to find out if a given port already exists.
+ *
+ * @param pwszPortName
+ * The port name to check.
+ *
+ * @return
+ * TRUE if a port with that name already exists on the local system.
+ * If the return value is FALSE, either the port doesn't exist or an error occurred.
+ * Use GetLastError in this case to check the error case.
+ */
+BOOL
+DoesPortExist(PCWSTR pwszPortName)
+{
+    BOOL bReturnValue = FALSE;
+    DWORD cbNeeded;
+    DWORD dwErrorCode;
+    DWORD dwReturned;
+    DWORD i;
+    PPORT_INFO_1W p;
+    PPORT_INFO_1W pPortInfo1 = NULL;
+
+    // Determine the required buffer size.
+    EnumPortsW(NULL, 1, NULL, 0, &cbNeeded, &dwReturned);
+    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+    {
+        dwErrorCode = GetLastError();
+        ERR("EnumPortsW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Allocate a buffer large enough.
+    pPortInfo1 = DllAllocSplMem(cbNeeded);
+    if (!pPortInfo1)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Now get the actual port information.
+    if (!EnumPortsW(NULL, 1, (PBYTE)pPortInfo1, cbNeeded, &cbNeeded, &dwReturned))
+    {
+        dwErrorCode = GetLastError();
+        ERR("EnumPortsW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // We were successful! Loop through all returned ports.
+    dwErrorCode = ERROR_SUCCESS;
+    p = pPortInfo1;
+
+    for (i = 0; i < dwReturned; i++)
+    {
+        // Check if this existing port matches our queried one.
+        if (wcsicmp(p->pName, pwszPortName) == 0)
+        {
+            bReturnValue = TRUE;
+            goto Cleanup;
+        }
+
+        p++;
+    }
+
+Cleanup:
+    if (pPortInfo1)
+        DllFreeSplMem(pPortInfo1);
+
+    SetLastError(dwErrorCode);
+    return bReturnValue;
+}
+
+DWORD
+GetLPTTransmissionRetryTimeout()
+{
+    DWORD cbBuffer;
+    DWORD dwReturnValue = 90;       // Use 90 seconds as default if we fail to read from registry.
+    HKEY hKey;
+    LSTATUS lStatus;
+
+    // Six digits is the most you can enter in Windows' LocalUI.dll.
+    // Larger values make it crash, so introduce a limit here.
+    WCHAR wszBuffer[6 + 1];
+
+    // Open the key where our value is stored.
+    lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, KEY_READ, &hKey);
+    if (lStatus != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %ld!\n", lStatus);
+        goto Cleanup;
+    }
+
+    // Query the value.
+    cbBuffer = sizeof(wszBuffer);
+    lStatus = RegQueryValueExW(hKey, L"TransmissionRetryTimeout", NULL, NULL, (PBYTE)wszBuffer, &cbBuffer);
+    if (lStatus != ERROR_SUCCESS)
+    {
+        ERR("RegQueryValueExW failed with status %ld!\n", lStatus);
+        goto Cleanup;
+    }
+
+    // Return it converted to a DWORD.
+    dwReturnValue = wcstoul(wszBuffer, NULL, 10);
+
+Cleanup:
+    if (hKey)
+        RegCloseKey(hKey);
+
+    return dwReturnValue;
+}
+
+/**
+ * @name GetPortNameWithoutColon
+ *
+ * Most of the time, we operate on port names with a trailing colon. But some functions require the name without the trailing colon.
+ * This function checks if the port has a trailing colon and if so, it returns the port name without the colon.
+ *
+ * @param pwszPortName
+ * The port name with colon
+ *
+ * @param ppwszPortNameWithoutColon
+ * Pointer to a PWSTR that will contain the port name without colon.
+ * You have to free this buffer using DllFreeSplMem.
+ *
+ * @return
+ * ERROR_SUCCESS if the port name without colon was successfully copied into the buffer.
+ * ERROR_INVALID_PARAMETER if this port name has no trailing colon.
+ * ERROR_NOT_ENOUGH_MEMORY if memory allocation failed.
+ */
+DWORD
+GetPortNameWithoutColon(PCWSTR pwszPortName, PWSTR* ppwszPortNameWithoutColon)
+{
+    DWORD cchPortNameWithoutColon;
+
+    // Compute the string length of pwszPortNameWithoutColon.
+    cchPortNameWithoutColon = wcslen(pwszPortName) - 1;
+
+    // Check if pwszPortName really has a colon as the last character.
+    if (pwszPortName[cchPortNameWithoutColon] != L':')
+        return ERROR_INVALID_PARAMETER;
+
+    // Allocate the output buffer.
+    *ppwszPortNameWithoutColon = DllAllocSplMem((cchPortNameWithoutColon + 1) * sizeof(WCHAR));
+    if (!*ppwszPortNameWithoutColon)
+    {
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
+
+    // Copy the port name without colon into the buffer.
+    // The buffer is already zero-initialized, so no additional null-termination is necessary.
+    CopyMemory(*ppwszPortNameWithoutColon, pwszPortName, cchPortNameWithoutColon * sizeof(WCHAR));
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/CMakeLists.txt b/reactos/win32ss/printing/monitors/localmon/ui/CMakeLists.txt
new file mode 100644 (file)
index 0000000..beed196
--- /dev/null
@@ -0,0 +1,14 @@
+
+add_definitions(-D__WINESRC__)
+include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine)
+spec2def(localui.dll localui.spec)
+
+list(APPEND SOURCE
+    localui.c
+    ${CMAKE_CURRENT_BINARY_DIR}/localui.def)
+
+add_library(localui SHARED ${SOURCE} localui.rc)
+set_module_type(localui win32dll)
+target_link_libraries(localui wine)
+add_importlibs(localui winspool user32 msvcrt kernel32 ntdll)
+add_cd_file(TARGET localui DESTINATION reactos/system32 FOR all)
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Da.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Da.rc
new file mode 100644 (file)
index 0000000..d508ac7
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Danish resources for localui
+ *
+ * Copyright 2008 Jens Albretsen <jens@albretsen.dk>
+ *
+ * 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
+ *
+ */
+
+LANGUAGE LANG_DANISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Lokal port"
+    IDS_INVALIDNAME "'%s' er ikke et gyldigt port navn"
+    IDS_PORTEXISTS "Porten %s findes allerede"
+    IDS_NOTHINGTOCONFIG "Denne port har ingen indstillinger"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Opret en lokal port"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Skriv navnet på den nye port:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Annuller", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Opsæt LPT port"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Timeout (sekunder)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Transmission retry:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Annuller", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_De.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_De.rc
new file mode 100644 (file)
index 0000000..12a292c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * German resources for localui
+ *
+ * Copyright 2007 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
+ *
+ */
+
+#pragma code_page(65001)
+
+LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Lokaler Anschluss"
+    IDS_INVALIDNAME "'%s' ist kein gültiger Anschlussname"
+    IDS_PORTEXISTS "Der Anschluss %s existiert bereits"
+    IDS_NOTHINGTOCONFIG "Dieser Anschluss besitzt keine zu konfigurierenden Optionen"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Lokalen Anschluss hinzufügen"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Lokaler Anschluss, der hinzugefügt werden soll:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Abbrechen", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "LPT-Anschluss konfigurieren"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Zeitüberschreitung (Sekunden)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "Ü&bertragung wiederholen:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Abbrechen", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_En.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_En.rc
new file mode 100644 (file)
index 0000000..bb7eec2
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * English resources for localui
+ *
+ * Copyright 2007 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
+ *
+ */
+
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Local Port"
+    IDS_INVALIDNAME "'%s' is not a valid port name"
+    IDS_PORTEXISTS "Port %s already exists"
+    IDS_NOTHINGTOCONFIG "This port has no options to configure"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Add a Local Port"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Enter the port name to add:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Cancel", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Configure LPT Port"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Timeout (seconds)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Transmission Retry:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Cancel", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Es.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Es.rc
new file mode 100644 (file)
index 0000000..336c9ab
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Spanish resources for localui
+ *
+ * Copyright 2010 José Rostagno
+ *
+ * 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
+ *
+ */
+
+#pragma code_page(65001)
+
+LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Puerto local"
+    IDS_INVALIDNAME "'%s' no es un nombre de puerto válido"
+    IDS_PORTEXISTS "El puerto %s ya existe"
+    IDS_NOTHINGTOCONFIG "Este puerto no tiene opciones para configurar"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Agregar un puerto local"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Ingrese el nombre del puerto a agregar:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "Aceptar", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Cancelar", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Configurar puerto LPT"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Tiempo de espera (segundos)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Reintentar transmisión:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "Aceptar", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Cancelar", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Fr.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Fr.rc
new file mode 100644 (file)
index 0000000..20c0672
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * French resources for localui
+ *
+ * Copyright 2007 Jonathan Ernst
+ *
+ * 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
+ *
+ */
+
+#pragma code_page(65001)
+
+LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Port local"
+    IDS_INVALIDNAME "« %s » n'est pas un nom de port valide"
+    IDS_PORTEXISTS "Le port %s existe déjà"
+    IDS_NOTHINGTOCONFIG "Ce port n'a pas d'options de configuration"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Ajouter un port local"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Saisisser le nom du port à ajouter :", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Annuler", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Configurer un port LPT"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Délai (en secondes)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Essais de retransmission :", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Annuler", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_He.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_He.rc
new file mode 100644 (file)
index 0000000..d806609
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Hebrew resources for localui
+ *
+ * Copyright 2007 Detlef Riekenberg
+ *
+ * Translated by Baruch Rutman
+ *
+ * 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
+ *
+ */
+
+LANGUAGE LANG_HEBREW, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "יציאה מקומית"
+    IDS_INVALIDNAME "'%s' אינו שם יציאה חוקי"
+    IDS_PORTEXISTS "יציאה %s כבר קיימת"
+    IDS_NOTHINGTOCONFIG "ליציאה הזו אין אפשרויות להגדיר"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "הוסף יציאה מקומית"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "הזן את שם היציאה כדי להוסיפה:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "אישור", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "ביטול", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "הגדרת יציאת LPT"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "פסק זמן (שניות)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "ניסיון שידור חוזר:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "אישור", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "ביטול", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Hu.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Hu.rc
new file mode 100644 (file)
index 0000000..95fa1db
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Hungarian resources for localui
+ *
+ * Copyright 2010 Andras Kovacs
+ *
+ * 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
+ *
+ */
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+LANGUAGE LANG_HUNGARIAN, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Helyi port"
+    IDS_INVALIDNAME "A(z) '%s' nem egy érvényes portnév"
+    IDS_PORTEXISTS "A port: %s már létezik"
+    IDS_NOTHINGTOCONFIG "Ennek a portnak nincsenek beállítható tulajdonságai"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Helyi port hozzáadása"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "Adja meg a &hozzáadni kívánt port nevét:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Mégse", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "LPT port beállítása"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Időtúllépés (másodperc)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "Á&tviteli újrapróbálkozás:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Mégse", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_It.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_It.rc
new file mode 100644 (file)
index 0000000..f580a19
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Italian resources for localui
+ *
+ * Copyright 2007 Detlef Riekenberg
+ * Copyright 2010 Luca Bennati
+ *
+ * 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
+ *
+ */
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Porta locale"
+    IDS_INVALIDNAME "'%s' non è un nome di porta valido"
+    IDS_PORTEXISTS "La porta %s già esiste"
+    IDS_NOTHINGTOCONFIG "Questa porta non ha opzioni da configurare"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Aggiungi una porta locale"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Inserisci il nome della porta da aggiungere:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Annulla", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Configura la porta LPT"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Timeout (secondi)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Tentativi di trasmissione:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Annulla", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ja.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ja.rc
new file mode 100644 (file)
index 0000000..525b821
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Japanese resources for localui
+ *
+ * Copyright 2007 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
+ *
+ */
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "ローカル ポート"
+    IDS_INVALIDNAME "'%s' はポート名として正しくありません"
+    IDS_PORTEXISTS "ポート %s はすでに存在します"
+    IDS_NOTHINGTOCONFIG "このポートには設定項目がありません"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "ローカル ポートの追加"
+FONT 9, "MS UI Gothic"
+BEGIN
+    LTEXT "追加するポートの名前(&E):", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "キャンセル", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "プリンタ ポートの設定"
+FONT 9, "MS UI Gothic"
+BEGIN
+    GROUPBOX "タイムアウト (秒)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "再送回数(&T):", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "キャンセル", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ko.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ko.rc
new file mode 100644 (file)
index 0000000..51be816
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Korean resources for localui
+ *
+ * Copyright 2007 Detlef Riekenberg
+ * Copyright 2007 YunSong Hwang(황윤성)(hys545@dreamwiz.com)
+ *
+ * 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
+ *
+ */
+
+LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "지역 포트"
+    IDS_INVALIDNAME "'%s'는 올바른 포트 이름이 아닙니다"
+    IDS_PORTEXISTS "포트 %s는 이미 존재합니다"
+    IDS_NOTHINGTOCONFIG "이 포트는 설정할 옵션이 없습니다"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "지역 포트 더하기"
+FONT 9, "MS Shell Dlg"
+BEGIN
+    LTEXT "더할 포트 이름 입력(&E):", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "확인", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "취소", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "LPT 포트 설정"
+FONT 9, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "시간초과(초)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "재 전송 횟수(&T):", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "확인", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "취소", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Lt.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Lt.rc
new file mode 100644 (file)
index 0000000..4e88948
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Lithuanian resources for localui
+ *
+ * Copyright 2009 Aurimas Fišeras <aurimas@gmail.com>
+ *
+ * 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
+ *
+ */
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+LANGUAGE LANG_LITHUANIAN, SUBLANG_NEUTRAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Vietinis prievadas"
+    IDS_INVALIDNAME "„%s“ yra netinkamas prievado vardas"
+    IDS_PORTEXISTS "Prievadas %s jau egzistuoja"
+    IDS_NOTHINGTOCONFIG "Šis prievadas neturi parinkčių konfigūravimui"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Pridėti vietinį prievadą"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Įveskite pridedamo prievado vardą:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "Gerai", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Atsisakyti", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Konfigūruoti LPT prievadą"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Laukimo laikas (sekundėmis)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Perdavimo pakartojimas:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "Gerai", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Atsisakyti", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Nl.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Nl.rc
new file mode 100644 (file)
index 0000000..6bb1948
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Dutch resources for localui
+ *
+ * Copyright 2008 Frans Kool
+ *
+ * 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
+ *
+ */
+
+LANGUAGE LANG_DUTCH, SUBLANG_NEUTRAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Lokale Poort"
+    IDS_INVALIDNAME "'%s' is geen valide poort naam"
+    IDS_PORTEXISTS "Poort %s bestaat reeds"
+    IDS_NOTHINGTOCONFIG "Deze poort heeft geen opties om in te stellen"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Voeg een Lokale Poort toe"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Voer de toe te voegen poort naam in:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Annuleren", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Configureer LPT Poort"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Timeout (seconden)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Transmissie Herstart:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Annuleren", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_No.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_No.rc
new file mode 100644 (file)
index 0000000..a5f90ec
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Norwegian Bokmål resources for localui
+ *
+ * Copyright 2007 Alexander N. Sørnes <alex@thehandofagony.com>
+ *
+ * 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
+ *
+ */
+
+LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Lokal port"
+    IDS_INVALIDNAME "«%s» er ikke et gyldig portnavn"
+    IDS_PORTEXISTS "Porten %s finnes allerede"
+    IDS_NOTHINGTOCONFIG "Denne porten har ingen innstillinger"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Legg til en lokal port"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "Skriv inn navn&et på den nye porten:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Avbryt", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Oppsett av LPT-port"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Tidsavbrudd (sekunder)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "Prøv å sende på nyt&t:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Avbryt", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Pl.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Pl.rc
new file mode 100644 (file)
index 0000000..58ce7e8
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Polish resources for localui
+ *
+ * Copyright 2007 Detlef Riekenberg
+ * Copyright 2007 Mikolaj Zalewski
+ *
+ * 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
+ *
+ */
+
+LANGUAGE LANG_POLISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Port lokalny"
+    IDS_INVALIDNAME "'%s' nie jest poprawną nazwą portu"
+    IDS_PORTEXISTS "Port %s już istnieje"
+    IDS_NOTHINGTOCONFIG "Ten port nie ma opcji do skonfigurowania"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Dodaj port lokalny"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Nazwa nowego portu:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "&OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "&Anuluj", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Konfiguracja portu LPT"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Czasy oczekiwania (sekundy)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Ponowienie transmisji:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "&OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "&Anuluj", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Pt.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Pt.rc
new file mode 100644 (file)
index 0000000..cff6a3f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Portuguese resources for localui
+ *
+ * Copyright 2008 Ricardo Filipe
+ *
+ * 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
+ *
+ */
+
+LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Porta Local"
+    IDS_INVALIDNAME "'%s' não é um nome de porta válido"
+    IDS_PORTEXISTS "Porta %s já existe"
+    IDS_NOTHINGTOCONFIG "Esta porta não possui opções de configuração"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Adicionar uma porta local"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Introduza o nome da porta a adicionar:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Cancelar", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Configurar porta LPT"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Tempo expirado (segundos)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Recomeço da transmissão:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Cancelar", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ro.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ro.rc
new file mode 100644 (file)
index 0000000..1d3687b
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2007 Detlef Riekenberg
+ * Copyright 2008 Michael Stefaniuc
+ *           2011 Fulea Ștefan
+ *
+ * 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
+ *
+ */
+
+#pragma code_page(65001)
+
+LANGUAGE LANG_ROMANIAN, SUBLANG_NEUTRAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Port local"
+    IDS_INVALIDNAME "„%s” nu este un nume valid de port"
+    IDS_PORTEXISTS "Portul %s existsă deja"
+    IDS_NOTHINGTOCONFIG "Acest port nu are opțiuni de configurat"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Adaugare port local"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "N&umele portului adăugat:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "Con&firmă", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "A&nulează", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Configurare port LPT"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Temporizare (secunde)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Reîncearcă transmisia:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "Con&firmă", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "A&nulează", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ru.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Ru.rc
new file mode 100644 (file)
index 0000000..623ae6f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Russian resources for localui
+ *
+ * Copyright 2008 Vitaliy Margolen
+ *
+ * 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
+ *
+ */
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Локальный порт"
+    IDS_INVALIDNAME "Неправильное название порта '%s'"
+    IDS_PORTEXISTS "Порт '%s' уже существует"
+    IDS_NOTHINGTOCONFIG "Этот порт не имеет настроек"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Добавить локальный порт"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "Введите &название локального порта:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Отменить", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Установки параллельного порта"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Тайм-аут (секунд)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Попыток пересылки:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Отмена", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Si.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Si.rc
new file mode 100644 (file)
index 0000000..c08313c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Slovenian resources for localui
+ *
+ * Copyright 2008 Rok Mandeljc
+ *
+ * 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
+ *
+ */
+
+#pragma code_page(65001)
+
+LANGUAGE LANG_SLOVENIAN, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Lokalna vrata"
+    IDS_INVALIDNAME "'%s' ni veljavno ime vrat"
+    IDS_PORTEXISTS "Vrata z imenom %s že obstajajo"
+    IDS_NOTHINGTOCONFIG "Ta vrata nimajo možnosti nastavitve"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Dodaj lokalna vrata"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Ime vrat:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "V redu", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Prekliči", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Nastavitev LPT vrat"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Časovna omejitev (seconds)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Ponoven poskus prenosa:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "V redu", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Prekliči", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Sq.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Sq.rc
new file mode 100644 (file)
index 0000000..562c6d1
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * English resources for localui
+ *
+ * Copyright 2007 Detlef Riekenberg
+ * Translation: Ardit Dani (Albanian Translation Resource File)
+ *
+ * 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
+ *
+ */
+
+LANGUAGE LANG_ALBANIAN, SUBLANG_NEUTRAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Porti Vendor"
+    IDS_INVALIDNAME "'%s' nuk është emer e vlefshem porti"
+    IDS_PORTEXISTS "Porti %s ekziston"
+    IDS_NOTHINGTOCONFIG "Ky port ska opsione për të konfiguruar"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Shto një Port Vendor"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "Fut emrin e Portit për të shtuar:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Anulo", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Konfiguro Portin LPT"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Timeout (sekonda)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Transmission Riprove:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Anulo", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Sv.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Sv.rc
new file mode 100644 (file)
index 0000000..614bd62
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Swedish resources for localui
+ *
+ * Copyright 2007 Daniel Nylander
+ *
+ * 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
+ *
+ */
+
+LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Lokal port"
+    IDS_INVALIDNAME "'%s' är inte ett giltigt portnamn"
+    IDS_PORTEXISTS "Porten %s finns redan"
+    IDS_NOTHINGTOCONFIG "Denna port har inga alternativ att konfigurera"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Lägg till en lokal port"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Ange portnamnet att lägga till:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Avbryt", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Konfigurera LPT-port"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Tidsgräns (sekunder)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "Öve&rföringsförsök:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Avbryt", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Tr.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Tr.rc
new file mode 100644 (file)
index 0000000..9056a05
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Turkish resources for localui
+ *
+ * Copyright: 2014 Erdem Ersoy (eersoy93) (erdemersoy@live.com)
+ *
+ * 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
+ *
+ */
+
+LANGUAGE LANG_TURKISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Yerli Giriş"
+    IDS_INVALIDNAME """%s"", geçerli bir giriş adı değil."
+    IDS_PORTEXISTS "%s girişi önceden var."
+    IDS_NOTHINGTOCONFIG "Bu girişin yapılandırmak için seçeneği yok."
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Bir Yerli Giriş Ekle"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "&Eklemek için giriş adını giriniz:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "Tamam", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "İptal", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "LPT Girişini Yapılandır"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Süre Aşımı (Sâniye)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&İletimi Yeniden Dene:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "Tamam", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "İptal", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Uk.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Uk.rc
new file mode 100644 (file)
index 0000000..d428148
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Ukrainian resources for localui
+ *
+ * Copyright 2010 Igor Paliychuk
+ *
+ * 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
+ *
+ */
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "Локальний порт"
+    IDS_INVALIDNAME "'%s' не дійсна назва порту"
+    IDS_PORTEXISTS "Порт '%s' вже існує"
+    IDS_NOTHINGTOCONFIG "Цей порт не має налаштувань"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Додати локальний порт"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT "Введіть &назву локального порта:", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "OK", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Скасувати", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "Налаштування LPT порта"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "Тайм-аут (секунд)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "&Спроб пересилання:", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "OK", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "Скасувати", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Zh.rc b/reactos/win32ss/printing/monitors/localmon/ui/lang/ui_Zh.rc
new file mode 100644 (file)
index 0000000..6b81fb9
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * localui (Simplified and Traditional Chinese Resources)
+ *
+ * Copyright 2008 Hongbo Ni <hongbo.at.njstar.com>
+ *
+ * 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
+ *
+ */
+
+/* Chinese text is encoded in UTF-8 */
+#pragma code_page(65001)
+
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "本地端口"
+    IDS_INVALIDNAME "'%s' 不是有效的端口名称"
+    IDS_PORTEXISTS "端口 %s 已经存在"
+    IDS_NOTHINGTOCONFIG "这个端口没有可设置选项"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "添加本地端口"
+FONT 9, "MS Shell Dlg"
+BEGIN
+    LTEXT "添加本地端口名称(&E):", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "确定", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "取消", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "设置打印端口"
+FONT 9, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "超时(秒)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "重试通讯(&T):", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "确定", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "取消", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
+
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL
+
+STRINGTABLE
+{
+    IDS_LOCALPORT "本地端口"
+    IDS_INVALIDNAME "'%s' 不是有效的端口名稱"
+    IDS_PORTEXISTS "端口 %s 已經存在"
+    IDS_NOTHINGTOCONFIG "這個端口沒有可設定選項"
+}
+
+ADDPORT_DIALOG DIALOGEX 6, 18, 245, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "添加本地端口"
+FONT 9, "MS Shell Dlg"
+BEGIN
+    LTEXT "添加本地端口名稱(&E):", -1, 7, 13, 194, 13, WS_VISIBLE
+    EDITTEXT ADDPORT_EDIT, 6, 28, 174, 12, WS_VISIBLE | ES_AUTOHSCROLL
+    DEFPUSHBUTTON "確定", IDOK, 188, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "取消", IDCANCEL, 188, 27, 50, 14, WS_VISIBLE
+END
+
+
+LPTCONFIG_DIALOG DIALOGEX 6, 18, 220, 47
+STYLE DS_SHELLFONT | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFOREGROUND | WS_POPUPWINDOW | WS_VISIBLE | WS_CAPTION
+CAPTION "設定列印端口"
+FONT 9, "MS Shell Dlg"
+BEGIN
+    GROUPBOX "超時(秒)", LPTCONFIG_GROUP, 6, 6, 150, 35, BS_GROUPBOX
+    LTEXT "重試通訊(&T):", -1, 14, 22, 90, 13, WS_VISIBLE
+    EDITTEXT LPTCONFIG_EDIT, 112, 20, 32, 13, WS_VISIBLE | ES_NUMBER
+    DEFPUSHBUTTON "確定", IDOK, 164, 10, 50, 14, WS_VISIBLE
+    PUSHBUTTON "取消", IDCANCEL, 164, 27, 50, 14, WS_VISIBLE
+END
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/localui.c b/reactos/win32ss/printing/monitors/localmon/ui/localui.c
new file mode 100644 (file)
index 0000000..d58e507
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * Implementation of the Local Printmonitor User Interface
+ *
+ * Copyright 2007 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
+ */
+
+#define WIN32_NO_STATUS
+
+#include <stdarg.h>
+
+#define NONAMELESSUNION
+
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winreg.h>
+#include <winuser.h>
+
+#include <winspool.h>
+#include <ddk/winsplp.h>
+
+#include <wine/debug.h>
+#include <wine/unicode.h>
+#include "localui.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(localui);
+
+/*****************************************************/
+
+static HINSTANCE LOCALUI_hInstance;
+
+static const WCHAR cmd_AddPortW[] = {'A','d','d','P','o','r','t',0};
+static const WCHAR cmd_ConfigureLPTPortCommandOKW[] = {'C','o','n','f','i','g','u','r','e',
+                                    'L','P','T','P','o','r','t',
+                                    'C','o','m','m','a','n','d','O','K',0};
+static const WCHAR cmd_DeletePortW[] = {'D','e','l','e','t','e','P','o','r','t',0};
+static const WCHAR cmd_GetDefaultCommConfigW[] = {'G','e','t',
+                                    'D','e','f','a','u','l','t',
+                                    'C','o','m','m','C','o','n','f','i','g',0};
+static const WCHAR cmd_GetTransmissionRetryTimeoutW[] = {'G','e','t',
+                                    'T','r','a','n','s','m','i','s','s','i','o','n',
+                                    'R','e','t','r','y','T','i','m','e','o','u','t',0};
+static const WCHAR cmd_PortIsValidW[] = {'P','o','r','t','I','s','V','a','l','i','d',0};
+static const WCHAR cmd_SetDefaultCommConfigW[] = {'S','e','t',
+                                    'D','e','f','a','u','l','t',
+                                    'C','o','m','m','C','o','n','f','i','g',0};
+
+static const WCHAR fmt_uW[]  = {'%','u',0};
+static const WCHAR portname_LPT[]  = {'L','P','T',0};
+static const WCHAR portname_COM[]  = {'C','O','M',0};
+static const WCHAR portname_FILE[] = {'F','I','L','E',':',0};
+static const WCHAR portname_CUPS[] = {'C','U','P','S',':',0};
+static const WCHAR portname_LPR[]  = {'L','P','R',':',0};
+
+static const WCHAR XcvMonitorW[] = {',','X','c','v','M','o','n','i','t','o','r',' ',0};
+static const WCHAR XcvPortW[] = {',','X','c','v','P','o','r','t',' ',0};
+
+/*****************************************************/
+
+typedef struct tag_addportui_t {
+    LPWSTR  portname;
+    HANDLE  hXcv;
+} addportui_t;
+
+typedef struct tag_lptconfig_t {
+    HANDLE  hXcv;
+    DWORD   value;
+} lptconfig_t;
+
+
+static INT_PTR CALLBACK dlgproc_lptconfig(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+
+/*****************************************************
+ *   strdupWW [internal]
+ */
+
+static LPWSTR strdupWW(LPCWSTR pPrefix, LPCWSTR pSuffix)
+{
+    LPWSTR  ptr;
+    DWORD   len;
+
+    len = lstrlenW(pPrefix) + (pSuffix ? lstrlenW(pSuffix) : 0) + 1;
+    ptr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    if (ptr) {
+        lstrcpyW(ptr, pPrefix);
+        if (pSuffix) lstrcatW(ptr, pSuffix);
+    }
+    return ptr;
+}
+
+/*****************************************************
+ *   dlg_configure_com [internal]
+ *
+ */
+
+static BOOL dlg_configure_com(HANDLE hXcv, HWND hWnd, PCWSTR pPortName)
+{
+    COMMCONFIG cfg;
+    LPWSTR shortname;
+    DWORD status;
+    DWORD dummy;
+    DWORD len;
+    BOOL  res;
+
+    /* strip the colon (pPortName is never empty here) */
+    len = lstrlenW(pPortName);
+    shortname = HeapAlloc(GetProcessHeap(), 0, len  * sizeof(WCHAR));
+    if (shortname) {
+        memcpy(shortname, pPortName, (len -1) * sizeof(WCHAR));
+        shortname[len-1] = '\0';
+
+        /* get current settings */
+        len = FIELD_OFFSET(COMMCONFIG, wcProviderData[1]);
+        status = ERROR_SUCCESS;
+        res = XcvDataW( hXcv, cmd_GetDefaultCommConfigW,
+                        (PBYTE) shortname,
+                        (lstrlenW(shortname) +1) * sizeof(WCHAR),
+                        (PBYTE) &cfg, len, &len, &status);
+
+        if (res && (status == ERROR_SUCCESS)) {
+            /* display the Dialog */
+            res = CommConfigDialogW(pPortName, hWnd, &cfg);
+            if (res) {
+                status = ERROR_SUCCESS;
+                /* set new settings */
+                res = XcvDataW(hXcv, cmd_SetDefaultCommConfigW,
+                               (PBYTE) &cfg, len,
+                               (PBYTE) &dummy, 0, &len, &status);
+            }
+        }
+        HeapFree(GetProcessHeap(), 0, shortname);
+        return res;
+    }
+    return FALSE;
+}
+
+
+/*****************************************************
+ *   dlg_configure_lpt [internal]
+ *
+ */
+
+static BOOL dlg_configure_lpt(HANDLE hXcv, HWND hWnd)
+{
+    lptconfig_t data;
+    BOOL  res;
+
+
+    data.hXcv = hXcv;
+
+    res = DialogBoxParamW(LOCALUI_hInstance, MAKEINTRESOURCEW(LPTCONFIG_DIALOG), hWnd,
+                               dlgproc_lptconfig, (LPARAM) &data);
+
+    TRACE("got %u with %u\n", res, GetLastError());
+
+    if (!res) SetLastError(ERROR_CANCELLED);
+    return res;
+}
+
+/******************************************************************
+ *  dlg_port_already_exists [internal]
+ */
+
+static void dlg_port_already_exists(HWND hWnd, LPCWSTR portname)
+{
+    WCHAR res_PortW[IDS_LOCALPORT_MAXLEN];
+    WCHAR res_PortExistsW[IDS_PORTEXISTS_MAXLEN];
+    LPWSTR  message;
+    DWORD   len;
+
+    res_PortW[0] = '\0';
+    res_PortExistsW[0] = '\0';
+    LoadStringW(LOCALUI_hInstance, IDS_LOCALPORT, res_PortW, IDS_LOCALPORT_MAXLEN);
+    LoadStringW(LOCALUI_hInstance, IDS_PORTEXISTS, res_PortExistsW, IDS_PORTEXISTS_MAXLEN);
+
+    len = lstrlenW(portname) + IDS_PORTEXISTS_MAXLEN + 1;
+    message = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    if (message) {
+        message[0] = '\0';
+        snprintfW(message, len, res_PortExistsW, portname);
+        MessageBoxW(hWnd, message, res_PortW, MB_OK | MB_ICONERROR);
+        HeapFree(GetProcessHeap(), 0, message);
+    }
+}
+
+/******************************************************************
+ *  dlg_invalid_portname [internal]
+ */
+
+static void dlg_invalid_portname(HWND hWnd, LPCWSTR portname)
+{
+    WCHAR res_PortW[IDS_LOCALPORT_MAXLEN];
+    WCHAR res_InvalidNameW[IDS_INVALIDNAME_MAXLEN];
+    LPWSTR  message;
+    DWORD   len;
+
+    res_PortW[0] = '\0';
+    res_InvalidNameW[0] = '\0';
+    LoadStringW(LOCALUI_hInstance, IDS_LOCALPORT, res_PortW, IDS_LOCALPORT_MAXLEN);
+    LoadStringW(LOCALUI_hInstance, IDS_INVALIDNAME, res_InvalidNameW, IDS_INVALIDNAME_MAXLEN);
+
+    len = lstrlenW(portname) + IDS_INVALIDNAME_MAXLEN;
+    message = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    if (message) {
+        message[0] = '\0';
+        snprintfW(message, len, res_InvalidNameW, portname);
+        MessageBoxW(hWnd, message, res_PortW, MB_OK | MB_ICONERROR);
+        HeapFree(GetProcessHeap(), 0, message);
+    }
+}
+
+/******************************************************************
+ * display the Dialog "Nothing to configure"
+ *
+ */
+
+static void dlg_nothingtoconfig(HWND hWnd)
+{
+    WCHAR res_PortW[IDS_LOCALPORT_MAXLEN];
+    WCHAR res_nothingW[IDS_NOTHINGTOCONFIG_MAXLEN];
+
+    res_PortW[0] = '\0';
+    res_nothingW[0] = '\0';
+    LoadStringW(LOCALUI_hInstance, IDS_LOCALPORT, res_PortW, IDS_LOCALPORT_MAXLEN);
+    LoadStringW(LOCALUI_hInstance, IDS_NOTHINGTOCONFIG, res_nothingW, IDS_NOTHINGTOCONFIG_MAXLEN);
+
+    MessageBoxW(hWnd, res_nothingW, res_PortW, MB_OK | MB_ICONINFORMATION);
+}
+
+/******************************************************************
+ *  dlg_win32error [internal]
+ */
+
+static void dlg_win32error(HWND hWnd, DWORD lasterror)
+{
+    WCHAR res_PortW[IDS_LOCALPORT_MAXLEN];
+    LPWSTR  message = NULL;
+    DWORD   res;
+
+    res_PortW[0] = '\0';
+    LoadStringW(LOCALUI_hInstance, IDS_LOCALPORT, res_PortW, IDS_LOCALPORT_MAXLEN);
+
+
+    res = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                        NULL, lasterror, 0, (LPWSTR) &message, 0, NULL);
+
+    if (res > 0) {
+        MessageBoxW(hWnd, message, res_PortW, MB_OK | MB_ICONERROR);
+        LocalFree(message);
+    }
+}
+
+/*****************************************************************************
+ *
+ */
+
+static INT_PTR CALLBACK dlgproc_addport(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    addportui_t * data;
+    DWORD   status;
+    DWORD   dummy;
+    DWORD   len;
+    DWORD   res;
+
+    switch(msg)
+    {
+    case WM_INITDIALOG:
+        SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
+        return TRUE;
+
+    case WM_COMMAND:
+        if (wparam == MAKEWPARAM(IDOK, BN_CLICKED))
+        {
+            data = (addportui_t *) GetWindowLongPtrW(hwnd, DWLP_USER);
+            /* length in WCHAR, without the '\0' */
+            len = SendDlgItemMessageW(hwnd, ADDPORT_EDIT, WM_GETTEXTLENGTH, 0, 0);
+            data->portname = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
+
+            if (!data->portname) {
+                EndDialog(hwnd, FALSE);
+                return TRUE;
+            }
+            /* length is in WCHAR, including the '\0' */
+            GetDlgItemTextW(hwnd, ADDPORT_EDIT, data->portname, len + 1);
+            status = ERROR_SUCCESS;
+            res = XcvDataW( data->hXcv, cmd_PortIsValidW, (PBYTE) data->portname,
+                            (lstrlenW(data->portname) + 1) * sizeof(WCHAR),
+                            (PBYTE) &dummy, 0, &len, &status);
+
+            TRACE("got %u with status %u\n", res, status);
+            if (res && (status == ERROR_SUCCESS)) {
+                /* The caller must free data->portname */
+                EndDialog(hwnd, TRUE);
+                return TRUE;
+            }
+
+            if (res && (status == ERROR_INVALID_NAME)) {
+                dlg_invalid_portname(hwnd, data->portname);
+                HeapFree(GetProcessHeap(), 0, data->portname);
+                data->portname = NULL;
+                return TRUE;
+            }
+
+            dlg_win32error(hwnd, status);
+            HeapFree(GetProcessHeap(), 0, data->portname);
+            data->portname = NULL;
+            return TRUE;
+        }
+
+        if (wparam == MAKEWPARAM(IDCANCEL, BN_CLICKED))
+        {
+            EndDialog(hwnd, FALSE);
+            return TRUE;
+        }
+        return FALSE;
+    }
+    return FALSE;
+}
+
+/*****************************************************************************
+ *   dlgproc_lptconfig  [internal]
+ *
+ * Our message-proc is simple, as the range-check is done only during the
+ * command "OK" and the dialog is set to the start-value at "out of range".
+ *
+ * Native localui.dll does the check during keyboard-input and set the dialog
+ * to the previous value.
+ *
+ */
+
+static INT_PTR CALLBACK dlgproc_lptconfig(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    lptconfig_t * data;
+    WCHAR   bufferW[16];
+    DWORD   status;
+    DWORD   dummy;
+    DWORD   len;
+    DWORD   res;
+
+
+    switch(msg)
+    {
+    case WM_INITDIALOG:
+        SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
+        data = (lptconfig_t *) lparam;
+
+        /* Get current setting */
+        data->value = 45;
+        status = ERROR_SUCCESS;
+        res = XcvDataW( data->hXcv, cmd_GetTransmissionRetryTimeoutW,
+                        (PBYTE) &dummy, 0,
+                        (PBYTE) &data->value, sizeof(data->value), &len, &status);
+
+        TRACE("got %u with status %u\n", res, status);
+
+        /* Set current setting as the initial value in the Dialog */
+        SetDlgItemInt(hwnd, LPTCONFIG_EDIT, data->value, FALSE);
+        return TRUE;
+
+    case WM_COMMAND:
+        if (wparam == MAKEWPARAM(IDOK, BN_CLICKED))
+        {
+            data = (lptconfig_t *) GetWindowLongPtrW(hwnd, DWLP_USER);
+
+            status = FALSE;
+            res = GetDlgItemInt(hwnd, LPTCONFIG_EDIT, (BOOL *) &status, FALSE);
+            /* length is in WCHAR, including the '\0' */
+            GetDlgItemTextW(hwnd, LPTCONFIG_EDIT, bufferW, sizeof(bufferW) / sizeof(bufferW[0]));
+            TRACE("got %s and %u (translated: %u)\n", debugstr_w(bufferW), res, status);
+
+            /* native localui.dll use the same limits */
+            if ((res > 0) && (res < 1000000) && status) {
+                sprintfW(bufferW, fmt_uW, res);
+                res = XcvDataW( data->hXcv, cmd_ConfigureLPTPortCommandOKW,
+                        (PBYTE) bufferW,
+                        (lstrlenW(bufferW) +1) * sizeof(WCHAR),
+                        (PBYTE) &dummy, 0, &len, &status);
+
+                TRACE("got %u with status %u\n", res, status);
+                EndDialog(hwnd, TRUE);
+                return TRUE;
+            }
+
+            /* Set initial value and rerun the Dialog */
+            SetDlgItemInt(hwnd, LPTCONFIG_EDIT, data->value, FALSE);
+            return TRUE;
+        }
+
+        if (wparam == MAKEWPARAM(IDCANCEL, BN_CLICKED))
+        {
+            EndDialog(hwnd, FALSE);
+            return TRUE;
+        }
+        return FALSE;
+    }
+    return FALSE;
+}
+
+
+/*****************************************************
+ * get_type_from_name (internal)
+ *
+ */
+
+static DWORD get_type_from_name(LPCWSTR name)
+{
+    HANDLE  hfile;
+
+    if (!strncmpiW(name, portname_LPT, sizeof(portname_LPT) / sizeof(WCHAR) -1))
+        return PORT_IS_LPT;
+
+    if (!strncmpiW(name, portname_COM, sizeof(portname_COM) / sizeof(WCHAR) -1))
+        return PORT_IS_COM;
+
+    if (!strcmpiW(name, portname_FILE))
+        return PORT_IS_FILE;
+
+    if (name[0] == '/')
+        return PORT_IS_UNIXNAME;
+
+    if (name[0] == '|')
+        return PORT_IS_PIPE;
+
+    if (!strncmpW(name, portname_CUPS, sizeof(portname_CUPS) / sizeof(WCHAR) -1))
+        return PORT_IS_CUPS;
+
+    if (!strncmpW(name, portname_LPR, sizeof(portname_LPR) / sizeof(WCHAR) -1))
+        return PORT_IS_LPR;
+
+    /* Must be a file or a directory. Does the file exist ? */
+    hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    TRACE("%p for OPEN_EXISTING on %s\n", hfile, debugstr_w(name));
+    if (hfile == INVALID_HANDLE_VALUE) {
+        /* Can we create the file? */
+        hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
+        TRACE("%p for OPEN_ALWAYS\n", hfile);
+    }
+    if (hfile != INVALID_HANDLE_VALUE) {
+        CloseHandle(hfile);
+        return PORT_IS_FILENAME;
+    }
+    /* We can't use the name. use GetLastError() for the reason */
+    return PORT_IS_UNKNOWN;
+}
+
+/*****************************************************
+ *   open_monitor_by_name [internal]
+ *
+ */
+static BOOL open_monitor_by_name(LPCWSTR pPrefix, LPCWSTR pPort, HANDLE * phandle)
+{
+    PRINTER_DEFAULTSW pd;
+    LPWSTR  fullname;
+    BOOL    res;
+
+    * phandle = 0;
+    TRACE("(%s,%s)\n", debugstr_w(pPrefix),debugstr_w(pPort) );
+
+    fullname = strdupWW(pPrefix, pPort);
+    pd.pDatatype = NULL;
+    pd.pDevMode  = NULL;
+    pd.DesiredAccess = SERVER_ACCESS_ADMINISTER;
+
+    res = OpenPrinterW(fullname, phandle, &pd);
+    HeapFree(GetProcessHeap(), 0, fullname);
+    return res;
+}
+
+/*****************************************************
+ *   localui_AddPortUI [exported through MONITORUI]
+ *
+ * Display a Dialog to add a local Port
+ *
+ * PARAMS
+ *  pName       [I] Servername or NULL (local Computer)
+ *  hWnd        [I] Handle to parent Window for the Dialog-Box or NULL
+ *  pMonitorName[I] Name of the Monitor, that should be used to add a Port or NULL
+ *  ppPortName  [O] PTR to PTR of a buffer, that receive the Name of the new Port or NULL
+ *
+ * RETURNS
+ *  Success: TRUE
+ *  Failure: FALSE
+ *
+ * NOTES
+ * The caller must free the buffer (returned in ppPortName) with GlobalFree().
+ * Native localui.dll failed with ERROR_INVALID_PARAMETER, when the user tried
+ * to add a Port, that start with "COM" or "LPT".
+ *
+ */
+static BOOL WINAPI localui_AddPortUI(PCWSTR pName, HWND hWnd, PCWSTR pMonitorName, PWSTR *ppPortName)
+{
+    addportui_t data;
+    HANDLE  hXcv;
+    DWORD   needed;
+    DWORD   dummy;
+    DWORD   status;
+    DWORD   res = FALSE;
+
+    TRACE(  "(%s, %p, %s, %p) (*ppPortName: %p)\n", debugstr_w(pName), hWnd,
+            debugstr_w(pMonitorName), ppPortName, ppPortName ? *ppPortName : NULL);
+
+    if (open_monitor_by_name(XcvMonitorW, pMonitorName, &hXcv)) {
+
+        ZeroMemory(&data, sizeof(addportui_t));
+        data.hXcv = hXcv;
+        res = DialogBoxParamW(LOCALUI_hInstance, MAKEINTRESOURCEW(ADDPORT_DIALOG), hWnd,
+                               dlgproc_addport, (LPARAM) &data);
+
+        TRACE("got %u with %u for %s\n", res, GetLastError(), debugstr_w(data.portname));
+
+        if (ppPortName) *ppPortName = NULL;
+
+        if (res) {
+            res = XcvDataW(hXcv, cmd_AddPortW, (PBYTE) data.portname,
+                            (lstrlenW(data.portname)+1) * sizeof(WCHAR),
+                            (PBYTE) &dummy, 0, &needed, &status);
+
+            TRACE("got %u with status %u\n", res, status);
+            if (res && (status == ERROR_SUCCESS) && ppPortName) {
+                /* Native localui uses GlobalAlloc also.
+                   The caller must GlobalFree the buffer */
+                *ppPortName = GlobalAlloc(GPTR, (lstrlenW(data.portname)+1) * sizeof(WCHAR));
+                if (*ppPortName) lstrcpyW(*ppPortName, data.portname);
+            }
+
+            if (res && (status == ERROR_ALREADY_EXISTS)) {
+                dlg_port_already_exists(hWnd, data.portname);
+                /* Native localui also return "TRUE" from AddPortUI in this case */
+            }
+
+            HeapFree(GetProcessHeap(), 0, data.portname);
+        }
+        else
+        {
+            SetLastError(ERROR_CANCELLED);
+        }
+        ClosePrinter(hXcv);
+    }
+
+    TRACE("=> %u with %u\n", res, GetLastError());
+    return res;
+}
+
+
+/*****************************************************
+ *   localui_ConfigurePortUI [exported through MONITORUI]
+ *
+ * Display the Configuration-Dialog for a specific Port
+ *
+ * PARAMS
+ *  pName     [I] Servername or NULL (local Computer)
+ *  hWnd      [I] Handle to parent Window for the Dialog-Box or NULL
+ *  pPortName [I] Name of the Port, that should be configured
+ *
+ * RETURNS
+ *  Success: TRUE
+ *  Failure: FALSE
+ *
+ */
+static BOOL WINAPI localui_ConfigurePortUI(PCWSTR pName, HWND hWnd, PCWSTR pPortName)
+{
+    HANDLE  hXcv;
+    DWORD   res;
+
+    TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
+    if (open_monitor_by_name(XcvPortW, pPortName, &hXcv)) {
+
+        res = get_type_from_name(pPortName);
+        switch(res)
+        {
+
+        case PORT_IS_COM:
+            res = dlg_configure_com(hXcv, hWnd, pPortName);
+            break;
+
+        case PORT_IS_LPT:
+            res = dlg_configure_lpt(hXcv, hWnd);
+            break;
+
+        default:
+            dlg_nothingtoconfig(hWnd);
+            SetLastError(ERROR_CANCELLED);
+            res = FALSE;
+        }
+
+        ClosePrinter(hXcv);
+        return res;
+    }
+    return FALSE;
+
+}
+
+/*****************************************************
+ *   localui_DeletePortUI [exported through MONITORUI]
+ *
+ * Delete a specific Port
+ *
+ * PARAMS
+ *  pName     [I] Servername or NULL (local Computer)
+ *  hWnd      [I] Handle to parent Window
+ *  pPortName [I] Name of the Port, that should be deleted
+ *
+ * RETURNS
+ *  Success: TRUE
+ *  Failure: FALSE
+ *
+ * NOTES
+ *  Native localui does not allow to delete a COM / LPT - Port (ERROR_NOT_SUPPORTED)
+ *
+ */
+static BOOL WINAPI localui_DeletePortUI(PCWSTR pName, HWND hWnd, PCWSTR pPortName)
+{
+    HANDLE  hXcv;
+    DWORD   dummy;
+    DWORD   needed;
+    DWORD   status;
+
+    TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
+
+    if ((!pPortName) || (!pPortName[0])) {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    if (open_monitor_by_name(XcvPortW, pPortName, &hXcv)) {
+        /* native localui tests here for LPT / COM - Ports and failed with
+           ERROR_NOT_SUPPORTED. */
+        if (XcvDataW(hXcv, cmd_DeletePortW, (LPBYTE) pPortName,
+            (lstrlenW(pPortName)+1) * sizeof(WCHAR), (LPBYTE) &dummy, 0, &needed, &status)) {
+
+            ClosePrinter(hXcv);
+            if (status != ERROR_SUCCESS) SetLastError(status);
+            return (status == ERROR_SUCCESS);
+        }
+        ClosePrinter(hXcv);
+        return FALSE;
+    }
+    SetLastError(ERROR_UNKNOWN_PORT);
+    return FALSE;
+}
+
+/*****************************************************
+ *      InitializePrintMonitorUI  (LOCALUI.@)
+ *
+ * Initialize the User-Interface for the Local Ports
+ *
+ * RETURNS
+ *  Success: Pointer to a MONITORUI Structure
+ *  Failure: NULL
+ *
+ */
+
+PMONITORUI WINAPI InitializePrintMonitorUI(void)
+{
+    static MONITORUI mymonitorui =
+    {
+        sizeof(MONITORUI),
+        localui_AddPortUI,
+        localui_ConfigurePortUI,
+        localui_DeletePortUI
+    };
+
+    TRACE("=> %p\n", &mymonitorui);
+    return &mymonitorui;
+}
+
+/*****************************************************
+ *      DllMain
+ */
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    TRACE("(%p, %d, %p)\n",hinstDLL, fdwReason, lpvReserved);
+
+    switch(fdwReason)
+    {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls( hinstDLL );
+            LOCALUI_hInstance = hinstDLL;
+            break;
+    }
+    return TRUE;
+}
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/localui.h b/reactos/win32ss/printing/monitors/localmon/ui/localui.h
new file mode 100644 (file)
index 0000000..7b59a26
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * internal include file of the Local Printmonitor User Interface
+ *
+ * Copyright 2007 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
+ */
+
+#ifndef __WINE_LOCALUI__
+#define __WINE_LOCALUI__
+
+//#include <windef.h>
+//#include <winuser.h>
+
+/* ## Resource-ID ## */
+#define ADDPORT_DIALOG  100
+#define ADDPORT_EDIT    101
+
+#define LPTCONFIG_DIALOG 200
+#define LPTCONFIG_GROUP  201
+#define LPTCONFIG_EDIT   202
+
+#define IDS_LOCALPORT       300
+#define IDS_INVALIDNAME     301
+#define IDS_PORTEXISTS      302
+#define IDS_NOTHINGTOCONFIG 303
+
+/* ## Reserved memorysize for the strings (in WCHAR) ## */
+#define IDS_LOCALPORT_MAXLEN 32
+#define IDS_INVALIDNAME_MAXLEN 48
+#define IDS_PORTEXISTS_MAXLEN  48
+#define IDS_NOTHINGTOCONFIG_MAXLEN 80
+
+/* ## Type of Ports ## */
+/* windows types */
+#define PORT_IS_UNKNOWN  0
+#define PORT_IS_LPT      1
+#define PORT_IS_COM      2
+#define PORT_IS_FILE     3
+#define PORT_IS_FILENAME 4
+
+/* wine extensions */
+#define PORT_IS_WINE     5
+#define PORT_IS_UNIXNAME 5
+#define PORT_IS_PIPE     6
+#define PORT_IS_CUPS     7
+#define PORT_IS_LPR      8
+
+
+#endif /* __WINE_LOCALUI__ */
@@ -1,7 +1,7 @@
 /*
- * Top level resource file for localspl
+ * Top level resource file for localui
  *
- * Copyright 2006 Detlef Riekenberg
+ * Copyright 2007 Detlef Riekenberg
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  *
  */
 
-#include "resource.h"
+#include <windef.h>
+#include <winuser.h>
 
-#define WINE_FILENAME_STR "localspl.dll"
-#define WINE_FILEDESCRIPTION_STR "Wine Printer spooler component"
+#include "localui.h"
+
+#define WINE_FILENAME_STR "localui.dll"
+#define WINE_FILEDESCRIPTION_STR "User Interface for the Local Monitor"
 
 /* Same Version as WinXP_sp2 */
 #define WINE_FILEVERSION      5,1,2600,2180
 #define WINE_PRODUCTVERSION      5,1,2600,2180
 #define WINE_PRODUCTVERSION_STR "5.1.2600.2180"
 
-#include <wine/wine_common_ver.rc>
+#include "wine/wine_common_ver.rc"
+
+#include <reactos/manifest_dll.rc>
 
 /* UTF-8 */
 #pragma code_page(65001)
 
 #ifdef LANGUAGE_DA_DK
-    #include "lang/spl_Da.rc"
+    #include "lang/ui_Da.rc"
 #endif
 #ifdef LANGUAGE_DE_DE
-    #include "lang/spl_De.rc"
+    #include "lang/ui_De.rc"
 #endif
 #ifdef LANGUAGE_EN_US
-    #include "lang/spl_En.rc"
+    #include "lang/ui_En.rc"
 #endif
 #ifdef LANGUAGE_ES_ES
-    #include "lang/spl_Es.rc"
+    #include "lang/ui_Es.rc"
 #endif
 #ifdef LANGUAGE_FR_FR
-    #include "lang/spl_Fr.rc"
+    #include "lang/ui_Fr.rc"
 #endif
 #ifdef LANGUAGE_HE_IL
-    #include "lang/spl_He.rc"
+    #include "lang/ui_He.rc"
 #endif
 #ifdef LANGUAGE_HU_HU
-    #include "lang/spl_Hu.rc"
+    #include "lang/ui_Hu.rc"
 #endif
 #ifdef LANGUAGE_IT_IT
-    #include "lang/spl_It.rc"
+    #include "lang/ui_It.rc"
 #endif
 #ifdef LANGUAGE_JA_JP
-    #include "lang/spl_Ja.rc"
+    #include "lang/ui_Ja.rc"
 #endif
 #ifdef LANGUAGE_KO_KR
-    #include "lang/spl_Ko.rc"
+    #include "lang/ui_Ko.rc"
 #endif
 #ifdef LANGUAGE_LT_LT
-    #include "lang/spl_Lt.rc"
+    #include "lang/ui_Lt.rc"
 #endif
 #ifdef LANGUAGE_NL_NL
-    #include "lang/spl_Nl.rc"
+    #include "lang/ui_Nl.rc"
 #endif
 #ifdef LANGUAGE_NO_NO
-    #include "lang/spl_No.rc"
+    #include "lang/ui_No.rc"
 #endif
 #ifdef LANGUAGE_PL_PL
-    #include "lang/spl_Pl.rc"
+    #include "lang/ui_Pl.rc"
 #endif
 #ifdef LANGUAGE_PT_PT
-    #include "lang/spl_Pt.rc"
+    #include "lang/ui_Pt.rc"
 #endif
 #ifdef LANGUAGE_RO_RO
-    #include "lang/spl_Ro.rc"
+    #include "lang/ui_Ro.rc"
 #endif
 #ifdef LANGUAGE_RU_RU
-    #include "lang/spl_Ru.rc"
+    #include "lang/ui_Ru.rc"
 #endif
 #ifdef LANGUAGE_SL_SI
-    #include "lang/spl_Si.rc"
+    #include "lang/ui_Si.rc"
 #endif
 #ifdef LANGUAGE_SQ_AL
-    #include "lang/spl_Sq.rc"
+    #include "lang/ui_Sq.rc"
 #endif
 #ifdef LANGUAGE_SV_SE
-    #include "lang/spl_Sv.rc"
+    #include "lang/ui_Sv.rc"
 #endif
 #ifdef LANGUAGE_TR_TR
-    #include "lang/spl_Tr.rc"
+    #include "lang/ui_Tr.rc"
 #endif
 #ifdef LANGUAGE_UK_UA
-    #include "lang/spl_Uk.rc"
+    #include "lang/ui_Uk.rc"
 #endif
 #ifdef LANGUAGE_ZH_CN
-    #include "lang/spl_Zh.rc"
+    #include "lang/ui_Zh.rc"
 #endif
diff --git a/reactos/win32ss/printing/monitors/localmon/ui/localui.spec b/reactos/win32ss/printing/monitors/localmon/ui/localui.spec
new file mode 100644 (file)
index 0000000..dafed22
--- /dev/null
@@ -0,0 +1 @@
+@ stdcall InitializePrintMonitorUI()
diff --git a/reactos/win32ss/printing/monitors/localmon/xcv.c b/reactos/win32ss/printing/monitors/localmon/xcv.c
new file mode 100644 (file)
index 0000000..c436278
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * PROJECT:     ReactOS Local Port Monitor
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Implementation of Xcv* and support functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static DWORD
+_HandleAddPort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
+{
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/**
+ * @name _HandleConfigureLPTPortCommandOK
+ *
+ * Writes the value for "TransmissionRetryTimeout" to the registry. Checks for granted SERVER_ACCESS_ADMINISTER access.
+ * Actually the opposite of _HandleGetTransmissionRetryTimeout, but name kept for compatibility.
+ *
+ * @param pXcv
+ * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port.
+ *
+ * @param pInputData
+ * Pointer to a Unicode string containing the value to be written to the registry.
+ *
+ * @param pcbOutputNeeded
+ * Pointer to a DWORD that will be zeroed on return.
+ *
+ * @return
+ * An error code indicating success or failure.
+ */
+static DWORD
+_HandleConfigureLPTPortCommandOK(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
+{
+    DWORD cbBuffer;
+    DWORD dwErrorCode;
+    HKEY hKey = NULL;
+    HKEY hToken = NULL;
+
+    // Sanity checks
+    if (!pXcv || !pInputData || !pcbOutputNeeded)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    *pcbOutputNeeded = 0;
+
+    // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
+    if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
+    {
+        dwErrorCode = ERROR_ACCESS_DENIED;
+        goto Cleanup;
+    }
+
+    // Switch to the SYSTEM context for modifying the registry.
+    hToken = RevertToPrinterSelf();
+    if (!hToken)
+    {
+        dwErrorCode = GetLastError();
+        ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Open the key where our value is stored.
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, KEY_SET_VALUE, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // We don't use cbInputData here, because the buffer pInputData could be bigger than the data it contains.
+    cbBuffer = (wcslen((PWSTR)pInputData) + 1) * sizeof(WCHAR);
+
+    // Write the value to the registry.
+    dwErrorCode = (DWORD)RegSetValueExW(hKey, L"TransmissionRetryTimeout", 0, REG_SZ, pInputData, cbBuffer);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+Cleanup:
+    if (hKey)
+        RegCloseKey(hKey);
+
+    if (hToken)
+        ImpersonatePrinterClient(hToken);
+
+    return dwErrorCode;
+}
+
+static DWORD
+_HandleDeletePort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
+{
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/**
+ * @name _HandleGetDefaultCommConfig
+ *
+ * Gets the default configuration of a legacy port.
+ * The opposite function is _HandleSetDefaultCommConfig.
+ *
+ * @param pInputData
+ * The port name (without colon!) whose default configuration you want to get.
+ *
+ * @param pOutputData
+ * Pointer to a COMMCONFIG structure that will receive the configuration information.
+ *
+ * @param cbOutputData
+ * Size of the variable pointed to by pOutputData.
+ *
+ * @param pcbOutputNeeded
+ * Pointer to a DWORD that contains the required size for pOutputData on return.
+ *
+ * @return
+ * An error code indicating success or failure.
+ */
+static DWORD
+_HandleGetDefaultCommConfig(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
+{
+    // Sanity checks
+    if (!pInputData || !pcbOutputNeeded)
+        return ERROR_INVALID_PARAMETER;
+
+    *pcbOutputNeeded = sizeof(COMMCONFIG);
+
+    // Check if the supplied buffer is large enough.
+    if (cbOutputData < *pcbOutputNeeded)
+        return ERROR_INSUFFICIENT_BUFFER;
+
+    // Finally get the port configuration.
+    if (!GetDefaultCommConfigW((PCWSTR)pInputData, (LPCOMMCONFIG)pOutputData, pcbOutputNeeded))
+        return GetLastError();
+
+    return ERROR_SUCCESS;
+}
+
+/**
+ * @name _HandleGetTransmissionRetryTimeout
+ *
+ * Reads the value for "TransmissionRetryTimeout" from the registry and converts it to a DWORD.
+ * The opposite function is _HandleConfigureLPTPortCommandOK.
+ *
+ * @param pOutputData
+ * Pointer to a DWORD that will receive the timeout value.
+ *
+ * @param cbOutputData
+ * Size of the variable pointed to by pOutputData.
+ *
+ * @param pcbOutputNeeded
+ * Pointer to a DWORD that contains the required size for pOutputData on return.
+ *
+ * @return
+ * An error code indicating success or failure.
+ */
+static DWORD
+_HandleGetTransmissionRetryTimeout(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
+{
+    DWORD dwTimeout;
+
+    // Sanity checks
+    if (!pOutputData || !pcbOutputNeeded)
+        return ERROR_INVALID_PARAMETER;
+
+    *pcbOutputNeeded = sizeof(DWORD);
+
+    // Check if the supplied buffer is large enough.
+    if (cbOutputData < *pcbOutputNeeded)
+        return ERROR_INSUFFICIENT_BUFFER;
+
+    // Retrieve and copy the number.
+    dwTimeout = GetLPTTransmissionRetryTimeout();
+    CopyMemory(pOutputData, &dwTimeout, sizeof(DWORD));
+    return ERROR_SUCCESS;
+}
+
+/**
+ * @name _HandleMonitorUI
+ *
+ * Returns the filename of the associated UI DLL for this Port Monitor.
+ *
+ * @param pOutputData
+ * Pointer to a Unicode string that will receive the DLL filename.
+ *
+ * @param cbOutputData
+ * Size of the variable pointed to by pOutputData.
+ *
+ * @param pcbOutputNeeded
+ * Pointer to a DWORD that contains the required size for pOutputData on return.
+ *
+ * @return
+ * An error code indicating success or failure.
+ */
+static DWORD
+_HandleMonitorUI(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
+{
+    const WCHAR wszMonitorUI[] = L"LocalUI.dll";
+
+    // Sanity checks
+    if (!pOutputData || !pcbOutputNeeded)
+        return ERROR_INVALID_PARAMETER;
+
+    *pcbOutputNeeded = sizeof(wszMonitorUI);
+
+    // Check if the supplied buffer is large enough.
+    if (cbOutputData < *pcbOutputNeeded)
+        return ERROR_INSUFFICIENT_BUFFER;
+
+    // Copy the string.
+    CopyMemory(pOutputData, wszMonitorUI, sizeof(wszMonitorUI));
+    return ERROR_SUCCESS;
+}
+
+/**
+ * @name _HandlePortExists
+ *
+ * Checks all Port Monitors installed on the local system to find out if a given port already exists.
+ *
+ * @param pInputData
+ * Pointer to a Unicode string specifying the port name to check.
+ *
+ * @param pOutputData
+ * Pointer to a BOOL that receives the result of the check.
+ *
+ * @param cbOutputData
+ * Size of the variable pointed to by pOutputData.
+ *
+ * @param pcbOutputNeeded
+ * Pointer to a DWORD that contains the required size for pOutputData on return.
+ *
+ * @return
+ * An error code indicating success or failure.
+ */
+static DWORD
+_HandlePortExists(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
+{
+    // Sanity checks
+    if (!pInputData || !pOutputData || !pcbOutputNeeded)
+        return ERROR_INVALID_PARAMETER;
+
+    *pcbOutputNeeded = sizeof(BOOL);
+
+    // Check if the supplied buffer is large enough.
+    if (cbOutputData < *pcbOutputNeeded)
+        return ERROR_INSUFFICIENT_BUFFER;
+
+    // Return the check result and error code.
+    *(PBOOL)pOutputData = DoesPortExist((PCWSTR)pInputData);
+    return GetLastError();
+}
+
+static DWORD
+_HandlePortIsValid(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
+{
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/**
+ * @name _HandleSetDefaultCommConfig
+ *
+ * Sets the default configuration of a legacy port. Checks for granted SERVER_ACCESS_ADMINISTER access.
+ * You have to supply the port name (with colon!) in XcvOpenPort.
+ * The opposite function is _HandleGetDefaultCommConfig.
+ *
+ * @param pXcv
+ * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port.
+ *
+ * @param pInputData
+ * Pointer to the COMMCONFIG structure that shall be passed to SetDefaultCommConfigW.
+ *
+ * @param pcbOutputNeeded
+ * Pointer to a DWORD that will be zeroed on return.
+ *
+ * @return
+ * An error code indicating success or failure.
+ */
+static DWORD
+_HandleSetDefaultCommConfig(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
+{
+    DWORD dwErrorCode;
+    HANDLE hToken = NULL;
+    LPCOMMCONFIG pCommConfig;
+    PWSTR pwszPortNameWithoutColon = NULL;
+
+    // Sanity checks
+    // pwszObject needs to be at least 2 characters long to be a port name with a trailing colon.
+    if (!pXcv || !pXcv->pwszObject || !pXcv->pwszObject[0] || !pXcv->pwszObject[1] || !pInputData || !pcbOutputNeeded)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    *pcbOutputNeeded = 0;
+
+    // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
+    if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
+    {
+        dwErrorCode = ERROR_ACCESS_DENIED;
+        goto Cleanup;
+    }
+
+    // SetDefaultCommConfigW needs the port name without colon.
+    dwErrorCode = GetPortNameWithoutColon(pXcv->pwszObject, &pwszPortNameWithoutColon);
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    // Switch to the SYSTEM context for setting the port configuration.
+    hToken = RevertToPrinterSelf();
+    if (!hToken)
+    {
+        dwErrorCode = GetLastError();
+        ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Finally pass the parameters to SetDefaultCommConfigW.
+    pCommConfig = (LPCOMMCONFIG)pInputData;
+    if (!SetDefaultCommConfigW(pwszPortNameWithoutColon, pCommConfig, pCommConfig->dwSize))
+    {
+        dwErrorCode = GetLastError();
+        ERR("SetDefaultCommConfigW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (hToken)
+        ImpersonatePrinterClient(hToken);
+
+    if (pwszPortNameWithoutColon)
+        DllFreeSplMem(pwszPortNameWithoutColon);
+
+    return dwErrorCode;
+}
+
+BOOL WINAPI
+LocalmonXcvClosePort(HANDLE hXcv)
+{
+    PLOCALMON_XCV pXcv = (PLOCALMON_XCV)hXcv;
+
+    TRACE("LocalmonXcvClosePort(%p)\n", hXcv);
+
+    // Sanity checks
+    if (!pXcv)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // Remove it from the list and free the memory.
+    RemoveEntryList(&pXcv->Entry);
+    DllFreeSplMem(pXcv);
+
+    SetLastError(ERROR_SUCCESS);
+    return TRUE;
+}
+
+DWORD WINAPI
+LocalmonXcvDataPort(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
+{
+    TRACE("LocalmonXcvDataPort(%p, %S, %p, %lu, %p, %lu, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
+
+    // Sanity checks
+    if (!pszDataName)
+        return ERROR_INVALID_PARAMETER;
+
+    // Call the appropriate handler for the requested data name.
+    if (wcscmp(pszDataName, L"AddPort") == 0)
+        return _HandleAddPort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
+
+    if (wcscmp(pszDataName, L"ConfigureLPTPortCommandOK") == 0)
+        return _HandleConfigureLPTPortCommandOK((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
+
+    if (wcscmp(pszDataName, L"DeletePort") == 0)
+        return _HandleDeletePort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
+
+    if (wcscmp(pszDataName, L"GetDefaultCommConfig") == 0)
+        return _HandleGetDefaultCommConfig(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
+
+    if (wcscmp(pszDataName, L"GetTransmissionRetryTimeout") == 0)
+        return _HandleGetTransmissionRetryTimeout(pOutputData, cbOutputData, pcbOutputNeeded);
+
+    if (wcscmp(pszDataName, L"MonitorUI") == 0)
+        return _HandleMonitorUI(pOutputData, cbOutputData, pcbOutputNeeded);
+
+    if (wcscmp(pszDataName, L"PortExists") == 0)
+        return _HandlePortExists(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
+
+    if (wcscmp(pszDataName, L"PortIsValid") == 0)
+        return _HandlePortIsValid(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
+
+    if (wcscmp(pszDataName, L"SetDefaultCommConfig") == 0)
+        return _HandleSetDefaultCommConfig((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
+
+    return ERROR_INVALID_PARAMETER;
+}
+
+BOOL WINAPI
+LocalmonXcvOpenPort(HANDLE hMonitor, PCWSTR pwszObject, ACCESS_MASK GrantedAccess, PHANDLE phXcv)
+{
+    DWORD cbObject = 0;
+    DWORD dwErrorCode;
+    PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
+    PLOCALMON_XCV pXcv;
+
+    TRACE("LocalmonXcvOpenPort(%p, %S, %lu, %p)\n", hMonitor, pwszObject, GrantedAccess, phXcv);
+
+    // Sanity checks
+    if (!pLocalmon || !phXcv)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    if (pwszObject)
+        cbObject = (wcslen(pwszObject) + 1) * sizeof(WCHAR);
+
+    // Create a new LOCALMON_XCV structure and fill the relevant fields.
+    pXcv = DllAllocSplMem(sizeof(LOCALMON_XCV) + cbObject);
+    if (!pXcv)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    pXcv->pLocalmon = pLocalmon;
+    pXcv->GrantedAccess = GrantedAccess;
+
+    if (cbObject)
+    {
+        pXcv->pwszObject = (PWSTR)((PBYTE)pXcv + sizeof(LOCALMON_XCV));
+        CopyMemory(pXcv->pwszObject, pwszObject, cbObject);
+    }
+
+    InsertTailList(&pLocalmon->XcvHandles, &pXcv->Entry);
+
+    // Return it as the Xcv handle.
+    *phXcv = (HANDLE)pXcv;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
diff --git a/reactos/win32ss/printing/monitors/tcpmon/CMakeLists.txt b/reactos/win32ss/printing/monitors/tcpmon/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3051d97
--- /dev/null
@@ -0,0 +1 @@
+#add_subdirectory(ui)
diff --git a/reactos/win32ss/printing/processors/CMakeLists.txt b/reactos/win32ss/printing/processors/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8d6a9cc
--- /dev/null
@@ -0,0 +1 @@
+add_subdirectory(winprint)
diff --git a/reactos/win32ss/printing/processors/winprint/CMakeLists.txt b/reactos/win32ss/printing/processors/winprint/CMakeLists.txt
new file mode 100644 (file)
index 0000000..713a5a9
--- /dev/null
@@ -0,0 +1,18 @@
+
+spec2def(winprint.dll winprint.spec ADD_IMPORTLIB)
+
+list(APPEND SOURCE
+    main.c
+    raw.c
+    precomp.h)
+
+add_library(winprint SHARED
+    ${SOURCE}
+    winprint.rc
+    ${CMAKE_CURRENT_BINARY_DIR}/winprint.def)
+
+set_module_type(winprint win32dll UNICODE)
+target_link_libraries(winprint wine)
+add_importlibs(winprint spoolss msvcrt kernel32 ntdll)
+add_pch(winprint precomp.h SOURCE)
+add_cd_file(TARGET winprint DESTINATION reactos/system32/spool/prtprocs/w32x86 FOR all)
diff --git a/reactos/win32ss/printing/processors/winprint/main.c b/reactos/win32ss/printing/processors/winprint/main.c
new file mode 100644 (file)
index 0000000..1385aab
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * PROJECT:     ReactOS Standard Print Processor
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Main functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+// Local Constants
+static PCWSTR _pwszDatatypes[] = {
+    L"RAW",
+    0
+};
+
+
+/**
+ * @name ClosePrintProcessor
+ *
+ * Closes a Print Processor Handle that has previously been opened through OpenPrintProcessor.
+ *
+ * @param hPrintProcessor
+ * The return value of a previous successful OpenPrintProcessor call.
+ *
+ * @return
+ * TRUE if the Print Processor Handle was successfully closed, FALSE otherwise.
+ * A more specific error code can be obtained through GetLastError.
+ */
+BOOL WINAPI
+ClosePrintProcessor(HANDLE hPrintProcessor)
+{
+    DWORD dwErrorCode;
+    PWINPRINT_HANDLE pHandle;
+
+    TRACE("ClosePrintProcessor(%p)\n", hPrintProcessor);
+
+    // Sanity checks
+    if (!hPrintProcessor)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pHandle = (PWINPRINT_HANDLE)hPrintProcessor;
+
+    // Free all structure fields for which memory has been allocated.
+    if (pHandle->pwszDatatype)
+        DllFreeSplStr(pHandle->pwszDatatype);
+
+    if (pHandle->pwszDocumentName)
+        DllFreeSplStr(pHandle->pwszDocumentName);
+
+    if (pHandle->pwszOutputFile)
+        DllFreeSplStr(pHandle->pwszOutputFile);
+
+    if (pHandle->pwszPrinterPort)
+        DllFreeSplStr(pHandle->pwszPrinterPort);
+
+    // Finally free the WINSPOOL_HANDLE structure itself.
+    DllFreeSplMem(pHandle);
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+ControlPrintProcessor(HANDLE hPrintProcessor, DWORD Command)
+{
+    TRACE("ControlPrintProcessor(%p, %lu)\n", hPrintProcessor, Command);
+
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+/**
+ * @name EnumPrintProcessorDatatypesW
+ *
+ * Obtains an array of all datatypes supported by this Print Processor.
+ *
+ * @param pName
+ * Server Name. Ignored here, because every caller of EnumPrintProcessorDatatypesW is interested in this Print Processor's information.
+ *
+ * @param pPrintProcessorName
+ * Print Processor Name. Ignored here, because every caller of EnumPrintProcessorDatatypesW is interested in this Print Processor's information.
+ *
+ * @param Level
+ * The level of the structure supplied through pDatatypes. This must be 1.
+ *
+ * @param pDatatypes
+ * Pointer to the buffer that receives an array of DATATYPES_INFO_1W structures.
+ * Can be NULL if you just want to know the required size of the buffer.
+ *
+ * @param cbBuf
+ * Size of the buffer you supplied for pDatatypes, in bytes.
+ *
+ * @param pcbNeeded
+ * Pointer to a variable that receives the required size of the buffer for pDatatypes, in bytes.
+ * This parameter mustn't be NULL!
+ *
+ * @param pcReturned
+ * Pointer to a variable that receives the number of elements of the DATATYPES_INFO_1W array.
+ * This parameter mustn't be NULL!
+ *
+ * @return
+ * TRUE if we successfully copied the array into pDatatypes, FALSE otherwise.
+ * A more specific error code can be obtained through GetLastError.
+ */
+BOOL WINAPI
+EnumPrintProcessorDatatypesW(PWSTR pName, PWSTR pPrintProcessorName, DWORD Level, PBYTE pDatatypes, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD cbDatatype;
+    DWORD dwDatatypeCount = 0;
+    DWORD dwErrorCode;
+    DWORD dwOffsets[_countof(_pwszDatatypes)];
+    PCWSTR* pCurrentDatatype;
+    PDWORD pCurrentOffset = dwOffsets;
+
+    TRACE("EnumPrintProcessorDatatypesW(%S, %S, %lu, %p, %lu, %p, %p)\n", pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned);
+
+    // Sanity checks
+    if (Level != 1 || !pcbNeeded || !pcReturned)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Count the required buffer size and the number of datatypes.
+    *pcbNeeded = 0;
+    *pcReturned = 0;
+
+    for (pCurrentDatatype = _pwszDatatypes; *pCurrentDatatype; pCurrentDatatype++)
+    {
+        cbDatatype = (wcslen(*pCurrentDatatype) + 1) * sizeof(WCHAR);
+        *pcbNeeded += sizeof(DATATYPES_INFO_1W) + cbDatatype;
+
+        // Also calculate the offset in the output buffer of the pointer to this datatype string.
+        *pCurrentOffset = dwDatatypeCount * sizeof(DATATYPES_INFO_1W) + FIELD_OFFSET(DATATYPES_INFO_1W, pName);
+
+        dwDatatypeCount++;
+        pCurrentOffset++;
+    }
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Check if a buffer was supplied at all.
+    if (!pDatatypes)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Copy over all datatypes.
+    *pCurrentOffset = MAXDWORD;
+    PackStrings(_pwszDatatypes, pDatatypes, dwOffsets, &pDatatypes[*pcbNeeded]);
+
+    *pcReturned = dwDatatypeCount;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+
+DWORD WINAPI
+GetPrintProcessorCapabilities(PWSTR pValueName, DWORD dwAttributes, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
+{
+    TRACE("GetPrintProcessorCapabilities(%S, %lu, %p, %lu, %p)\n", pValueName, dwAttributes, pData, nSize, pcbNeeded);
+
+    UNIMPLEMENTED;
+    return 0;
+}
+
+/**
+ * @name OpenPrintProcessor
+ *
+ * Prepares this Print Processor for processing a document.
+ *
+ * @param pPrinterName
+ * String in the format "\\COMPUTERNAME\Port:, Port" that is passed to OpenPrinterW for writing to the Print Monitor on the specified port.
+ *
+ * @param pPrintProcessorOpenData
+ * Pointer to a PRINTPROCESSOROPENDATA structure containing details about the print job to be processed.
+ *
+ * @return
+ * A Print Processor handle on success or NULL in case of a failure. This handle has to be passed to PrintDocumentOnPrintProcessor to do the actual processing.
+ * A more specific error code can be obtained through GetLastError.
+ */
+HANDLE WINAPI
+OpenPrintProcessor(PWSTR pPrinterName, PPRINTPROCESSOROPENDATA pPrintProcessorOpenData)
+{
+    DWORD dwErrorCode;
+    HANDLE hReturnValue = NULL;
+    PWINPRINT_HANDLE pHandle = NULL;
+
+    TRACE("OpenPrintProcessor(%S, %p)\n", pPrinterName, pPrintProcessorOpenData);
+
+    // Sanity checks
+    // This time a datatype needs to be given. We can't fall back to a default here.
+    if (!pPrintProcessorOpenData || !pPrintProcessorOpenData->pDatatype || !*pPrintProcessorOpenData->pDatatype)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Create a new WINPRINT_HANDLE structure and fill the relevant fields.
+    pHandle = DllAllocSplMem(sizeof(WINPRINT_HANDLE));
+
+    // Check what datatype was given.
+    if (wcsicmp(pPrintProcessorOpenData->pDatatype, L"RAW") == 0)
+    {
+        pHandle->Datatype = RAW;
+    }
+    else
+    {
+        dwErrorCode = ERROR_INVALID_DATATYPE;
+        goto Cleanup;
+    }
+
+    // Fill the relevant fields.
+    pHandle->dwJobID = pPrintProcessorOpenData->JobId;
+    pHandle->pwszDatatype = AllocSplStr(pPrintProcessorOpenData->pDatatype);
+    pHandle->pwszDocumentName = AllocSplStr(pPrintProcessorOpenData->pDocumentName);
+    pHandle->pwszOutputFile = AllocSplStr(pPrintProcessorOpenData->pOutputFile);
+    pHandle->pwszPrinterPort = AllocSplStr(pPrinterName);
+
+    // We were successful! Return the handle and don't let the cleanup routine free it.
+    dwErrorCode = ERROR_SUCCESS;
+    hReturnValue = pHandle;
+    pHandle = NULL;
+
+Cleanup:
+    if (pHandle)
+        DllFreeSplMem(pHandle);
+
+    SetLastError(dwErrorCode);
+    return hReturnValue;
+}
+
+/**
+ * @name PrintDocumentOnPrintProcessor
+ *
+ * Prints a document on this Print Processor after a handle for the document has been opened through OpenPrintProcessor.
+ *
+ * @param hPrintProcessor
+ * The return value of a previous successful OpenPrintProcessor call.
+ *
+ * @param pDocumentName
+ * String in the format "Printer, Job N" describing the spooled job that is to be processed.
+ *
+ * @return
+ * TRUE if the document was successfully processed by this Print Processor, FALSE otherwise.
+ * A more specific error code can be obtained through GetLastError.
+ */
+BOOL WINAPI
+PrintDocumentOnPrintProcessor(HANDLE hPrintProcessor, PWSTR pDocumentName)
+{
+    DWORD dwErrorCode;
+    PWINPRINT_HANDLE pHandle;
+
+    TRACE("PrintDocumentOnPrintProcessor(%p, %S)\n", hPrintProcessor, pDocumentName);
+
+    // Sanity checks
+    if (!hPrintProcessor)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pHandle = (PWINPRINT_HANDLE)hPrintProcessor;
+
+    // Call the corresponding Print function for the datatype.
+    if (pHandle->Datatype == RAW)
+        dwErrorCode = PrintRawJob(pHandle, pDocumentName);
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
diff --git a/reactos/win32ss/printing/processors/winprint/precomp.h b/reactos/win32ss/printing/processors/winprint/precomp.h
new file mode 100644 (file)
index 0000000..f4f57e0
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * PROJECT:     ReactOS Standard Print Processor
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Precompiled Header for all source files
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#ifndef _PRECOMP_H
+#define _PRECOMP_H
+
+#define WIN32_NO_STATUS
+#include <stdlib.h>
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winreg.h>
+#include <winspool.h>
+#include <winsplp.h>
+
+#include <spoolss.h>
+
+#include <wine/debug.h>
+WINE_DEFAULT_DEBUG_CHANNEL(winprint);
+
+// Structures
+typedef struct _WINPRINT_HANDLE
+{
+    enum { RAW } Datatype;
+    DWORD dwJobID;
+    PWSTR pwszDatatype;
+    PWSTR pwszDocumentName;
+    PWSTR pwszOutputFile;
+    PWSTR pwszPrinterPort;
+}
+WINPRINT_HANDLE, *PWINPRINT_HANDLE;
+
+// raw.c
+DWORD PrintRawJob(PWINPRINT_HANDLE pHandle, PWSTR pwszPrinterAndJob);
+
+#endif
diff --git a/reactos/win32ss/printing/processors/winprint/raw.c b/reactos/win32ss/printing/processors/winprint/raw.c
new file mode 100644 (file)
index 0000000..3be598b
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * PROJECT:     ReactOS Standard Print Processor
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Printing a job with RAW datatype
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+/**
+ * @name PrintRawJob
+ *
+ * @param pHandle
+ * Pointer to a WINPRINT_HANDLE structure containing information about this job.
+ *
+ * @param pwszPrinterAndJob
+ * String in the format "Printer, Job N" that is passed to OpenPrinterW to read from the spooled print job.
+ *
+ * @return
+ * An error code indicating success or failure.
+ */
+DWORD
+PrintRawJob(PWINPRINT_HANDLE pHandle, PWSTR pwszPrinterAndJob)
+{
+    // Use a read buffer of 256 KB size like Windows does.
+    const DWORD cbReadBuffer = 262144;
+
+    BOOL bStartedDoc = FALSE;
+    DOC_INFO_1W DocInfo1;
+    DWORD cbRead;
+    DWORD cbWritten;
+    DWORD dwErrorCode;
+    HANDLE hPrintJob;
+    HANDLE hPrintMonitor;
+    PBYTE pBuffer = NULL;
+
+    // Open the spooled job to read from it.
+    if (!OpenPrinterW(pwszPrinterAndJob, &hPrintJob, NULL))
+    {
+        dwErrorCode = GetLastError();
+        ERR("OpenPrinterW failed for \"%S\" with error %lu!\n", pwszPrinterAndJob, GetLastError());
+        goto Cleanup;
+    }
+
+    // Open a Print Monitor handle to write to it.
+    if (!OpenPrinterW(pHandle->pwszPrinterPort, &hPrintMonitor, NULL))
+    {
+        dwErrorCode = GetLastError();
+        ERR("OpenPrinterW failed for \"%S\" with error %lu!\n", pHandle->pwszPrinterPort, GetLastError());
+        goto Cleanup;
+    }
+
+    // Fill the Document Information.
+    DocInfo1.pDatatype = pHandle->pwszDatatype;
+    DocInfo1.pDocName = pHandle->pwszDocumentName;
+    DocInfo1.pOutputFile = pHandle->pwszOutputFile;
+
+    // Tell the Print Monitor that we're starting a new document.
+    if (!StartDocPrinterW(hPrintMonitor, 1, (PBYTE)&DocInfo1))
+    {
+        dwErrorCode = GetLastError();
+        ERR("StartDocPrinterW failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    bStartedDoc = TRUE;
+
+    // Allocate a read buffer on the heap. This would easily exceed the stack size.
+    pBuffer = DllAllocSplMem(cbReadBuffer);
+    if (!pBuffer)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Loop as long as data is available.
+    while (ReadPrinter(hPrintJob, pBuffer, cbReadBuffer, &cbRead) && cbRead)
+    {
+        // Write it to the Print Monitor.
+        WritePrinter(hPrintMonitor, pBuffer, cbRead, &cbWritten);
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (pBuffer)
+        DllFreeSplMem(pBuffer);
+    
+    if (bStartedDoc)
+        EndDocPrinter(hPrintMonitor);
+
+    if (hPrintMonitor)
+        ClosePrinter(hPrintMonitor);
+
+    if (hPrintJob)
+        ClosePrinter(hPrintJob);
+
+    return dwErrorCode;
+}
diff --git a/reactos/win32ss/printing/processors/winprint/winprint.rc b/reactos/win32ss/printing/processors/winprint/winprint.rc
new file mode 100644 (file)
index 0000000..522d139
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION  "ReactOS Standard Print Processor"
+#define REACTOS_STR_INTERNAL_NAME     "winprint"
+#define REACTOS_STR_ORIGINAL_FILENAME "winprint.dll"
+#include <reactos/version.rc>
diff --git a/reactos/win32ss/printing/processors/winprint/winprint.spec b/reactos/win32ss/printing/processors/winprint/winprint.spec
new file mode 100644 (file)
index 0000000..36ed976
--- /dev/null
@@ -0,0 +1,6 @@
+@ stdcall ClosePrintProcessor(long)
+@ stdcall ControlPrintProcessor(long long)
+@ stdcall EnumPrintProcessorDatatypesW(ptr ptr long ptr long ptr ptr)
+@ stdcall GetPrintProcessorCapabilities(wstr long ptr long ptr)
+@ stdcall OpenPrintProcessor(wstr ptr)
+@ stdcall PrintDocumentOnPrintProcessor(long wstr)
diff --git a/reactos/win32ss/printing/providers/CMakeLists.txt b/reactos/win32ss/printing/providers/CMakeLists.txt
new file mode 100644 (file)
index 0000000..30b37ad
--- /dev/null
@@ -0,0 +1,3 @@
+#add_subdirectory(inetpp)
+add_subdirectory(localspl)
+#add_subdirectory(win32spl)
diff --git a/reactos/win32ss/printing/providers/localspl/CMakeLists.txt b/reactos/win32ss/printing/providers/localspl/CMakeLists.txt
new file mode 100644 (file)
index 0000000..365f84b
--- /dev/null
@@ -0,0 +1,27 @@
+
+spec2def(localspl.dll localspl.spec ADD_IMPORTLIB)
+
+include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/skiplist)
+
+list(APPEND SOURCE
+    jobs.c
+    main.c
+    monitors.c
+    ports.c
+    precomp.h
+    printers.c
+    printingthread.c
+    printprocessors.c
+    tools.c)
+
+add_library(localspl SHARED
+    ${SOURCE}
+    localspl.rc
+    ${CMAKE_CURRENT_BINARY_DIR}/localspl_stubs.c
+    ${CMAKE_CURRENT_BINARY_DIR}/localspl.def)
+
+set_module_type(localspl win32dll UNICODE)
+target_link_libraries(localspl skiplist16 wine)
+add_importlibs(localspl advapi32 rpcrt4 spoolss msvcrt kernel32 ntdll)
+add_pch(localspl precomp.h SOURCE)
+add_cd_file(TARGET localspl DESTINATION reactos/system32 FOR all)
diff --git a/reactos/win32ss/printing/providers/localspl/jobs.c b/reactos/win32ss/printing/providers/localspl/jobs.c
new file mode 100644 (file)
index 0000000..2479b77
--- /dev/null
@@ -0,0 +1,1480 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions for managing print jobs
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+// Global Variables
+SKIPLIST GlobalJobList;
+
+// Local Variables
+static DWORD _dwLastJobID;
+
+
+/**
+ * @name _EqualStrings
+ *
+ * Returns whether two strings are equal.
+ * Unlike wcscmp, this function also works with NULL strings.
+ *
+ * @param pwszA
+ * First string to compare.
+ *
+ * @param pwszB
+ * Second string to compare.
+ *
+ * @return
+ * TRUE if the strings are equal, FALSE if they differ.
+ */
+static __inline BOOL
+_EqualStrings(PCWSTR pwszA, PCWSTR pwszB)
+{
+    if (!pwszA && !pwszB)
+        return TRUE;
+
+    if (pwszA && !pwszB)
+        return FALSE;
+
+    if (!pwszA && pwszB)
+        return FALSE;
+
+    return (wcscmp(pwszA, pwszB) == 0);
+}
+
+static BOOL
+_GetNextJobID(PDWORD dwJobID)
+{
+    ++_dwLastJobID;
+
+    while (LookupElementSkiplist(&GlobalJobList, &_dwLastJobID, NULL))
+    {
+        // This ID is already taken. Try the next one.
+        ++_dwLastJobID;
+    }
+
+    if (!IS_VALID_JOB_ID(_dwLastJobID))
+    {
+        ERR("Job ID %lu isn't valid!\n", _dwLastJobID);
+        return FALSE;
+    }
+
+    *dwJobID = _dwLastJobID;
+    return TRUE;
+}
+
+/**
+ * @name _GlobalJobListCompareRoutine
+ *
+ * SKIPLIST_COMPARE_ROUTINE for the Global Job List.
+ * We need the Global Job List to check whether a Job ID is already in use. Consequently, this list is sorted by ID.
+ */
+static int WINAPI
+_GlobalJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
+{
+    PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct;
+    PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct;
+
+    return A->dwJobID - B->dwJobID;
+}
+
+/**
+ * @name _PrinterJobListCompareRoutine
+ *
+ * SKIPLIST_COMPARE_ROUTINE for the each Printer's Job List.
+ * Jobs in this list are sorted in the desired order of processing.
+ */
+static int WINAPI
+_PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
+{
+    PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct;
+    PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct;
+    int iComparison;
+    FILETIME ftSubmittedA;
+    FILETIME ftSubmittedB;
+    ULARGE_INTEGER uliSubmittedA;
+    ULARGE_INTEGER uliSubmittedB;
+    ULONGLONG ullResult;
+
+    // First compare the priorities to determine the order.
+    // The job with a higher priority shall come first.
+    iComparison = A->dwPriority - B->dwPriority;
+    if (iComparison != 0)
+        return iComparison;
+
+    // Both have the same priority, so go by creation time.
+    // Comparison is done using the MSDN-recommended way for comparing SYSTEMTIMEs.
+    if (!SystemTimeToFileTime(&A->stSubmitted, &ftSubmittedA))
+    {
+        ERR("SystemTimeToFileTime failed for A with error %lu!\n", GetLastError());
+        return 0;
+    }
+
+    if (!SystemTimeToFileTime(&B->stSubmitted, &ftSubmittedB))
+    {
+        ERR("SystemTimeToFileTime failed for B with error %lu!\n", GetLastError());
+        return 0;
+    }
+
+    uliSubmittedA.LowPart = ftSubmittedA.dwLowDateTime;
+    uliSubmittedA.HighPart = ftSubmittedA.dwHighDateTime;
+    uliSubmittedB.LowPart = ftSubmittedB.dwLowDateTime;
+    uliSubmittedB.HighPart = ftSubmittedB.dwHighDateTime;
+    ullResult = uliSubmittedA.QuadPart - uliSubmittedB.QuadPart;
+
+    if (ullResult < 0)
+        return -1;
+    else if (ullResult > 0)
+        return 1;
+
+    return 0;
+}
+
+DWORD
+GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput)
+{
+    const WCHAR wszPrintersPath[] = L"\\PRINTERS\\";
+    const DWORD cchPrintersPath = _countof(wszPrintersPath) - 1;
+    const DWORD cchSpoolerFile = sizeof("?????.") - 1;
+    const DWORD cchExtension = sizeof("SPL") - 1;                   // pwszExtension may be L"SPL" or L"SHD", same length for both!
+
+    if (pwszOutput)
+    {
+        CopyMemory(pwszOutput, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
+        CopyMemory(&pwszOutput[cchSpoolDirectory], wszPrintersPath, cchPrintersPath * sizeof(WCHAR));
+        swprintf(&pwszOutput[cchSpoolDirectory + cchPrintersPath], L"%05lu.", dwJobID);
+        CopyMemory(&pwszOutput[cchSpoolDirectory + cchPrintersPath + cchSpoolerFile], pwszExtension, (cchExtension + 1) * sizeof(WCHAR));
+    }
+
+    return (cchSpoolDirectory + cchPrintersPath + cchSpoolerFile + cchExtension + 1) * sizeof(WCHAR);
+}
+
+BOOL
+InitializeGlobalJobList()
+{
+    const WCHAR wszPath[] = L"\\PRINTERS\\?????.SHD";
+    const DWORD cchPath = _countof(wszPath) - 1;
+
+    DWORD dwErrorCode;
+    DWORD dwJobID;
+    HANDLE hFind;
+    PLOCAL_JOB pJob = NULL;
+    PWSTR p;
+    WCHAR wszFullPath[MAX_PATH];
+    WIN32_FIND_DATAW FindData;
+
+    // This one is incremented in _GetNextJobID.
+    _dwLastJobID = 0;
+
+    // Initialize an empty list for all jobs of all local printers.
+    // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
+    InitializeSkiplist(&GlobalJobList, DllAllocSplMem, _GlobalJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
+
+    // Construct the full path search pattern.
+    CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
+    CopyMemory(&wszFullPath[cchSpoolDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR));
+
+    // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
+    hFind = FindFirstFileW(wszFullPath, &FindData);
+    if (hFind == INVALID_HANDLE_VALUE)
+    {
+        // No unfinished jobs found.
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+
+    do
+    {
+        // Skip possible subdirectories.
+        if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+            continue;
+
+        // Extract the Job ID and verify the file name format at the same time.
+        // This includes all valid names (like "00005.SHD") and excludes invalid ones (like "10ABC.SHD").
+        dwJobID = wcstoul(FindData.cFileName, &p, 10);
+        if (!IS_VALID_JOB_ID(dwJobID))
+            continue;
+
+        if (wcsicmp(p, L".SHD") != 0)
+            continue;
+
+        // This shadow file has a valid name. Construct the full path and try to load it.
+        GetJobFilePath(L"SHD", dwJobID, wszFullPath);
+        pJob = ReadJobShadowFile(wszFullPath);
+        if (!pJob)
+            continue;
+
+        // Add it to the Global Job List.
+        if (!InsertElementSkiplist(&GlobalJobList, pJob))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
+            goto Cleanup;
+        }
+
+        // Add it to the Printer's Job List.
+        if (!InsertElementSkiplist(&pJob->pPrinter->JobList, pJob))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
+            goto Cleanup;
+        }
+    }
+    while (FindNextFileW(hFind, &FindData));
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    // Outside the loop
+    if (hFind)
+        FindClose(hFind);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+void
+InitializePrinterJobList(PLOCAL_PRINTER pPrinter)
+{
+    // Initialize an empty list for this printer's jobs.
+    // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
+    InitializeSkiplist(&pPrinter->JobList, DllAllocSplMem, _PrinterJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
+}
+
+DWORD WINAPI
+CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle)
+{
+    const WCHAR wszDoubleBackslash[] = L"\\";
+    const DWORD cchDoubleBackslash = _countof(wszDoubleBackslash) - 1;
+
+    DWORD cchMachineName;
+    DWORD cchUserName;
+    DWORD dwErrorCode;
+    PLOCAL_JOB pJob;
+    RPC_BINDING_HANDLE hServerBinding = NULL;
+    RPC_WSTR pwszBinding = NULL;
+    RPC_WSTR pwszMachineName = NULL;
+
+    // Create a new job.
+    pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
+    if (!pJob)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Reserve an ID for this job.
+    if (!_GetNextJobID(&pJob->dwJobID))
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        goto Cleanup;
+    }
+
+    // Copy over defaults to the LOCAL_JOB structure.
+    pJob->pPrinter = pPrinterHandle->pPrinter;
+    pJob->pPrintProcessor = pPrinterHandle->pPrinter->pPrintProcessor;
+    pJob->dwPriority = DEF_PRIORITY;
+    pJob->dwStatus = JOB_STATUS_SPOOLING;
+    pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
+    pJob->pwszDocumentName = AllocSplStr(wszDefaultDocumentName);
+    pJob->pDevMode = DuplicateDevMode(pPrinterHandle->pDevMode);
+    GetSystemTime(&pJob->stSubmitted);
+
+    // Get the user name for the Job.
+    cchUserName = UNLEN + 1;
+    pJob->pwszUserName = DllAllocSplMem(cchUserName * sizeof(WCHAR));
+    if (!GetUserNameW(pJob->pwszUserName, &cchUserName))
+    {
+        dwErrorCode = GetLastError();
+        ERR("GetUserNameW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // FIXME: For now, pwszNotifyName equals pwszUserName.
+    pJob->pwszNotifyName = AllocSplStr(pJob->pwszUserName);
+
+    // Get the name of the machine that submitted the Job over RPC.
+    dwErrorCode = RpcBindingServerFromClient(NULL, &hServerBinding);
+    if (dwErrorCode != RPC_S_OK)
+    {
+        ERR("RpcBindingServerFromClient failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    dwErrorCode = RpcBindingToStringBindingW(hServerBinding, &pwszBinding);
+    if (dwErrorCode != RPC_S_OK)
+    {
+        ERR("RpcBindingToStringBindingW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    dwErrorCode = RpcStringBindingParseW(pwszBinding, NULL, NULL, &pwszMachineName, NULL, NULL);
+    if (dwErrorCode != RPC_S_OK)
+    {
+        ERR("RpcStringBindingParseW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    cchMachineName = wcslen(pwszMachineName);
+    pJob->pwszMachineName = DllAllocSplMem((cchMachineName + cchDoubleBackslash + 1) * sizeof(WCHAR));
+    CopyMemory(pJob->pwszMachineName, wszDoubleBackslash, cchDoubleBackslash * sizeof(WCHAR));
+    CopyMemory(&pJob->pwszMachineName[cchDoubleBackslash], pwszMachineName, (cchMachineName + 1) * sizeof(WCHAR));
+
+    // Add the job to the Global Job List.
+    if (!InsertElementSkiplist(&GlobalJobList, pJob))
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
+        goto Cleanup;
+    }
+
+    // Add the job at the end of the Printer's Job List.
+    // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
+    if (!InsertTailElementSkiplist(&pJob->pPrinter->JobList, pJob))
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
+        goto Cleanup;
+    }
+
+    // We were successful!
+    pPrinterHandle->bStartedDoc = TRUE;
+    pPrinterHandle->pJob = pJob;
+    dwErrorCode = ERROR_SUCCESS;
+
+    // Don't let the cleanup routine free this.
+    pJob = NULL;
+
+Cleanup:
+    if (pJob)
+        DllFreeSplMem(pJob);
+
+    if (pwszMachineName)
+        RpcStringFreeW(&pwszMachineName);
+
+    if (pwszBinding)
+        RpcStringFreeW(&pwszBinding);
+
+    if (hServerBinding)
+        RpcBindingFree(&hServerBinding);
+
+    return dwErrorCode;
+}
+
+BOOL WINAPI
+LocalAddJob(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    ADDJOB_INFO_1W AddJobInfo1;
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Check if this is a printer handle.
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // This handle must not have started a job yet!
+    if (pPrinterHandle->pJob)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Check if this is the right structure level.
+    if (Level != 1)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    // Check if the printer is set to do direct printing.
+    // The Job List isn't used in this case.
+    if (pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT)
+    {
+        dwErrorCode = ERROR_INVALID_ACCESS;
+        goto Cleanup;
+    }
+
+    // Check if the supplied buffer is large enough.
+    *pcbNeeded = sizeof(ADDJOB_INFO_1W) + GetJobFilePath(L"SPL", 0, NULL);
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // All requirements are met - create a new job.
+    dwErrorCode = CreateJob(pPrinterHandle);
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    // Mark that this job was started with AddJob (so that it can be scheduled for printing with ScheduleJob).
+    pPrinterHandle->pJob->bAddedJob = TRUE;
+
+    // Return a proper ADDJOB_INFO_1W structure.
+    AddJobInfo1.JobId = pPrinterHandle->pJob->dwJobID;
+    AddJobInfo1.Path = (PWSTR)(pData + sizeof(ADDJOB_INFO_1W));
+
+    CopyMemory(pData, &AddJobInfo1, sizeof(ADDJOB_INFO_1W));
+    GetJobFilePath(L"SPL", AddJobInfo1.JobId, AddJobInfo1.Path);
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+
+static DWORD
+_LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PBYTE* ppStart, PBYTE* ppEnd, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
+    DWORD cbDocumentName = 0;  
+    DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
+    DWORD cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+    DWORD cbStatus = 0;
+    DWORD cbUserName = 0;
+    DWORD dwErrorCode;
+    JOB_INFO_1W JobInfo1 = { 0 };
+
+    // Calculate the lengths of the optional values.
+    if (pJob->pwszDocumentName)
+        cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
+
+    if (pJob->pwszStatus)
+        cbStatus = (wcslen(pJob->pwszStatus) + 1) * sizeof(WCHAR);
+
+    if (pJob->pwszUserName)
+        cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
+
+    // Check if the supplied buffer is large enough.
+    *pcbNeeded += sizeof(JOB_INFO_1W) + cbDatatype + cbDocumentName + cbMachineName + cbPrinterName + cbStatus + cbUserName;
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Put the strings at the end of the buffer.
+    *ppEnd -= cbDatatype;
+    JobInfo1.pDatatype = (PWSTR)*ppEnd;
+    CopyMemory(*ppEnd, pJob->pwszDatatype, cbDatatype);
+
+    *ppEnd -= cbMachineName;
+    JobInfo1.pMachineName = (PWSTR)*ppEnd;
+    CopyMemory(*ppEnd, pJob->pwszMachineName, cbMachineName);
+
+    *ppEnd -= cbPrinterName;
+    JobInfo1.pPrinterName = (PWSTR)*ppEnd;
+    CopyMemory(*ppEnd, pJob->pPrinter->pwszPrinterName, cbPrinterName);
+
+    // Copy the optional values.
+    if (cbDocumentName)
+    {
+        *ppEnd -= cbDocumentName;
+        JobInfo1.pDocument = (PWSTR)*ppEnd;
+        CopyMemory(*ppEnd, pJob->pwszDocumentName, cbDocumentName);
+    }
+
+    if (cbStatus)
+    {
+        *ppEnd -= cbStatus;
+        JobInfo1.pStatus = (PWSTR)*ppEnd;
+        CopyMemory(*ppEnd, pJob->pwszStatus, cbStatus);
+    }
+
+    if (cbUserName)
+    {
+        *ppEnd -= cbUserName;
+        JobInfo1.pUserName = (PWSTR)*ppEnd;
+        CopyMemory(*ppEnd, pJob->pwszUserName, cbUserName);
+    }
+
+    // Fill the rest of the structure.
+    JobInfo1.JobId = pJob->dwJobID;
+    JobInfo1.Priority = pJob->dwPriority;
+    JobInfo1.Status = pJob->dwStatus;
+    JobInfo1.TotalPages = pJob->dwTotalPages;
+    CopyMemory(&JobInfo1.Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
+
+    // Finally copy the structure to the output pointer.
+    CopyMemory(*ppStart, &JobInfo1, sizeof(JOB_INFO_1W));
+    *ppStart += sizeof(JOB_INFO_1W);
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    return dwErrorCode;
+}
+
+static DWORD
+_LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PBYTE* ppStart, PBYTE* ppEnd, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
+    DWORD cbDevMode = pJob->pDevMode->dmSize + pJob->pDevMode->dmDriverExtra;
+    DWORD cbDocumentName = 0;
+    DWORD cbDriverName = (wcslen(pJob->pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
+    DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
+    DWORD cbNotifyName = 0;
+    DWORD cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+    DWORD cbPrintProcessor = (wcslen(pJob->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
+    DWORD cbPrintProcessorParameters = 0;
+    DWORD cbStatus = 0;
+    DWORD cbUserName = 0;
+    DWORD dwErrorCode;
+    FILETIME ftNow;
+    FILETIME ftSubmitted;
+    JOB_INFO_2W JobInfo2 = { 0 };
+    ULARGE_INTEGER uliNow;
+    ULARGE_INTEGER uliSubmitted;
+
+    // Calculate the lengths of the optional values.
+    if (pJob->pwszDocumentName)
+        cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
+
+    if (pJob->pwszNotifyName)
+        cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
+
+    if (pJob->pwszPrintProcessorParameters)
+        cbPrintProcessorParameters = (wcslen(pJob->pwszPrintProcessorParameters) + 1) * sizeof(WCHAR);
+
+    if (pJob->pwszStatus)
+        cbStatus = (wcslen(pJob->pwszStatus) + 1) * sizeof(WCHAR);
+
+    if (pJob->pwszUserName)
+        cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
+
+    // Check if the supplied buffer is large enough.
+    *pcbNeeded += sizeof(JOB_INFO_2W) + cbDatatype + cbDevMode + cbDocumentName + cbDriverName + cbMachineName + cbNotifyName + cbPrinterName + cbPrintProcessor + cbPrintProcessorParameters + cbStatus + cbUserName;
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Put the strings at the end of the buffer.
+    *ppEnd -= cbDatatype;
+    JobInfo2.pDatatype = (PWSTR)*ppEnd;
+    CopyMemory(*ppEnd, pJob->pwszDatatype, cbDatatype);
+
+    *ppEnd -= cbDevMode;
+    JobInfo2.pDevMode = (PDEVMODEW)*ppEnd;
+    CopyMemory(*ppEnd, pJob->pDevMode, cbDevMode);
+
+    *ppEnd -= cbDriverName;
+    JobInfo2.pDriverName = (PWSTR)*ppEnd;
+    CopyMemory(*ppEnd, pJob->pPrinter->pwszPrinterDriver, cbDriverName);
+
+    *ppEnd -= cbMachineName;
+    JobInfo2.pMachineName = (PWSTR)*ppEnd;
+    CopyMemory(*ppEnd, pJob->pwszMachineName, cbMachineName);
+
+    *ppEnd -= cbPrinterName;
+    JobInfo2.pPrinterName = (PWSTR)*ppEnd;
+    CopyMemory(*ppEnd, pJob->pPrinter->pwszPrinterName, cbPrinterName);
+
+    *ppEnd -= cbPrintProcessor;
+    JobInfo2.pPrintProcessor = (PWSTR)*ppEnd;
+    CopyMemory(*ppEnd, pJob->pPrintProcessor->pwszName, cbPrintProcessor);
+
+    // Copy the optional values.
+    if (cbDocumentName)
+    {
+        *ppEnd -= cbDocumentName;
+        JobInfo2.pDocument = (PWSTR)*ppEnd;
+        CopyMemory(*ppEnd, pJob->pwszDocumentName, cbDocumentName);
+    }
+
+    if (cbNotifyName)
+    {
+        *ppEnd -= cbNotifyName;
+        JobInfo2.pNotifyName = (PWSTR)*ppEnd;
+        CopyMemory(*ppEnd, pJob->pwszNotifyName, cbNotifyName);
+    }
+
+    if (cbPrintProcessorParameters)
+    {
+        *ppEnd -= cbPrintProcessorParameters;
+        JobInfo2.pParameters = (PWSTR)*ppEnd;
+        CopyMemory(*ppEnd, pJob->pwszPrintProcessorParameters, cbPrintProcessorParameters);
+    }
+
+    if (cbStatus)
+    {
+        *ppEnd -= cbStatus;
+        JobInfo2.pStatus = (PWSTR)*ppEnd;
+        CopyMemory(*ppEnd, pJob->pwszStatus, cbStatus);
+    }
+
+    if (cbUserName)
+    {
+        *ppEnd -= cbUserName;
+        JobInfo2.pUserName = (PWSTR)*ppEnd;
+        CopyMemory(*ppEnd, pJob->pwszUserName, cbUserName);
+    }
+
+    // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
+    if (!SystemTimeToFileTime(&pJob->stSubmitted, &ftSubmitted))
+    {
+        ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
+        return FALSE;
+    }
+
+    GetSystemTimeAsFileTime(&ftNow);
+    uliSubmitted.LowPart = ftSubmitted.dwLowDateTime;
+    uliSubmitted.HighPart = ftSubmitted.dwHighDateTime;
+    uliNow.LowPart = ftNow.dwLowDateTime;
+    uliNow.HighPart = ftNow.dwHighDateTime;
+    JobInfo2.Time = (DWORD)((uliNow.QuadPart - uliSubmitted.QuadPart) / 10000);
+
+    // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
+    // Retrieve this through the element index of the job in the Printer's Job List.
+    if (!LookupElementSkiplist(&pJob->pPrinter->JobList, pJob, &JobInfo2.Position))
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("pJob could not be located in the Printer's Job List!\n");
+        goto Cleanup;
+    }
+
+    // Make the index 1-based.
+    ++JobInfo2.Position;
+
+    // Fill the rest of the structure.
+    JobInfo2.JobId = pJob->dwJobID;
+    JobInfo2.PagesPrinted = pJob->dwPagesPrinted;
+    JobInfo2.Priority = pJob->dwPriority;
+    JobInfo2.StartTime = pJob->dwStartTime;
+    JobInfo2.Status = pJob->dwStatus;
+    JobInfo2.TotalPages = pJob->dwTotalPages;
+    JobInfo2.UntilTime = pJob->dwUntilTime;
+    CopyMemory(&JobInfo2.Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
+
+    // Finally copy the structure to the output pointer.
+    CopyMemory(*ppStart, &JobInfo2, sizeof(JOB_INFO_2W));
+    *ppStart += sizeof(JOB_INFO_2W);
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    return dwErrorCode;
+}
+
+BOOL WINAPI
+LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded)
+{
+    DWORD dwErrorCode;
+    PBYTE pEnd = &pStart[cbBuf];
+    PLOCAL_HANDLE pHandle;
+    PLOCAL_JOB pJob;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Check if this is a printer handle.
+    pHandle = (PLOCAL_HANDLE)hPrinter;
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // Get the desired job.
+    pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
+    if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Begin counting.
+    *pcbNeeded = 0;
+
+    // The function behaves differently for each level.
+    if (Level == 1)
+        dwErrorCode = _LocalGetJobLevel1(pPrinterHandle, pJob, &pStart, &pEnd, cbBuf, pcbNeeded);
+    else if (Level == 2)
+        dwErrorCode = _LocalGetJobLevel2(pPrinterHandle, pJob, &pStart, &pEnd, cbBuf, pcbNeeded);
+    else
+        dwErrorCode = ERROR_INVALID_LEVEL;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+static DWORD
+_LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PJOB_INFO_1W pJobInfo)
+{
+    DWORD dwErrorCode;
+
+    // First check the validity of the input before changing anything.
+    if (!FindDatatype(pJob->pPrintProcessor, pJobInfo->pDatatype))
+    {
+        dwErrorCode = ERROR_INVALID_DATATYPE;
+        goto Cleanup;
+    }
+
+    // Check if the datatype has changed.
+    if (!_EqualStrings(pJob->pwszDatatype, pJobInfo->pDatatype))
+    {
+        // Use the new value.
+        if (!ReallocSplStr(&pJob->pwszDatatype, pJobInfo->pDatatype))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Check if the document name has changed. An empty string is permitted here!
+    if (!_EqualStrings(pJob->pwszDocumentName, pJobInfo->pDocument))
+    {
+        // Use the new value.
+        if (!ReallocSplStr(&pJob->pwszDocumentName, pJobInfo->pDocument))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Check if the status message has changed. An empty string is permitted here!
+    if (!_EqualStrings(pJob->pwszStatus, pJobInfo->pStatus))
+    {
+        // Use the new value.
+        if (!ReallocSplStr(&pJob->pwszStatus, pJobInfo->pStatus))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Check if the user name has changed. An empty string is permitted here!
+    if (!_EqualStrings(pJob->pwszUserName, pJobInfo->pUserName) != 0)
+    {
+        // The new user name doesn't need to exist, so no additional verification is required.
+
+        // Use the new value.
+        if (!ReallocSplStr(&pJob->pwszUserName, pJobInfo->pUserName))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Check if the priority has changed.
+    if (pJob->dwPriority != pJobInfo->Priority && IS_VALID_PRIORITY(pJobInfo->Priority))
+    {
+        // Set the new priority.
+        pJob->dwPriority = pJobInfo->Priority;
+
+        // Remove and reinsert the job in the Printer's Job List.
+        // The Compare function will be used to find the right position now considering the new priority.
+        DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
+        InsertElementSkiplist(&pJob->pPrinter->JobList, pJob);
+    }
+
+    // Check if the status flags have changed.
+    if (pJob->dwStatus != pJobInfo->Status)
+    {
+        // Only add status flags that make sense.
+        if (pJobInfo->Status & JOB_STATUS_PAUSED)
+            pJob->dwStatus |= JOB_STATUS_PAUSED;
+
+        if (pJobInfo->Status & JOB_STATUS_ERROR)
+            pJob->dwStatus |= JOB_STATUS_ERROR;
+
+        if (pJobInfo->Status & JOB_STATUS_OFFLINE)
+            pJob->dwStatus |= JOB_STATUS_OFFLINE;
+
+        if (pJobInfo->Status & JOB_STATUS_PAPEROUT)
+            pJob->dwStatus |= JOB_STATUS_PAPEROUT;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    return dwErrorCode;
+}
+
+static DWORD
+_LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PJOB_INFO_2W pJobInfo)
+{
+    DWORD dwErrorCode;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+
+    // First check the validity of the input before changing anything.
+    pPrintProcessor = FindPrintProcessor(pJobInfo->pPrintProcessor);
+    if (!pPrintProcessor)
+    {
+        dwErrorCode = ERROR_UNKNOWN_PRINTPROCESSOR;
+        goto Cleanup;
+    }
+
+    if (!FindDatatype(pPrintProcessor, pJobInfo->pDatatype))
+    {
+        dwErrorCode = ERROR_INVALID_DATATYPE;
+        goto Cleanup;
+    }
+
+    // Check if the datatype has changed.
+    if (!_EqualStrings(pJob->pwszDatatype, pJobInfo->pDatatype))
+    {
+        // Use the new value.
+        if (!ReallocSplStr(&pJob->pwszDatatype, pJobInfo->pDatatype))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Check if the document name has changed. An empty string is permitted here!
+    if (!_EqualStrings(pJob->pwszDocumentName, pJobInfo->pDocument))
+    {
+        // Use the new value.
+        if (!ReallocSplStr(&pJob->pwszDocumentName, pJobInfo->pDocument))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Check if the notify name has changed. An empty string is permitted here!
+    if (!_EqualStrings(pJob->pwszNotifyName, pJobInfo->pNotifyName))
+    {
+        // The new notify name doesn't need to exist, so no additional verification is required.
+
+        // Use the new value.
+        if (!ReallocSplStr(&pJob->pwszNotifyName, pJobInfo->pNotifyName))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Check if the Print Processor Parameters have changed. An empty string is permitted here!
+    if (!_EqualStrings(pJob->pwszPrintProcessorParameters, pJobInfo->pParameters))
+    {
+        // Use the new value.
+        if (!ReallocSplStr(&pJob->pwszPrintProcessorParameters, pJobInfo->pParameters))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Check if the Status Message has changed. An empty string is permitted here!
+    if (!_EqualStrings(pJob->pwszStatus, pJobInfo->pStatus))
+    {
+        // Use the new value.
+        if (!ReallocSplStr(&pJob->pwszStatus, pJobInfo->pStatus))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Check if the user name has changed. An empty string is permitted here!
+    if (!_EqualStrings(pJob->pwszUserName, pJobInfo->pUserName))
+    {
+        // The new user name doesn't need to exist, so no additional verification is required.
+
+        // Use the new value.
+        if (!ReallocSplStr(&pJob->pwszUserName, pJobInfo->pUserName))
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Check if the priority has changed.
+    if (pJob->dwPriority != pJobInfo->Priority && IS_VALID_PRIORITY(pJobInfo->Priority))
+    {
+        // Set the new priority.
+        pJob->dwPriority = pJobInfo->Priority;
+
+        // Remove and reinsert the job in the Printer's Job List.
+        // The Compare function will be used to find the right position now considering the new priority.
+        DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
+        InsertElementSkiplist(&pJob->pPrinter->JobList, pJob);
+    }
+
+    // Check if the status flags have changed.
+    if (pJob->dwStatus != pJobInfo->Status)
+    {
+        // Only add status flags that make sense.
+        if (pJobInfo->Status & JOB_STATUS_PAUSED)
+            pJob->dwStatus |= JOB_STATUS_PAUSED;
+
+        if (pJobInfo->Status & JOB_STATUS_ERROR)
+            pJob->dwStatus |= JOB_STATUS_ERROR;
+
+        if (pJobInfo->Status & JOB_STATUS_OFFLINE)
+            pJob->dwStatus |= JOB_STATUS_OFFLINE;
+
+        if (pJobInfo->Status & JOB_STATUS_PAPEROUT)
+            pJob->dwStatus |= JOB_STATUS_PAPEROUT;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    return dwErrorCode;
+}
+
+BOOL WINAPI
+LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
+{
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle;
+    PLOCAL_JOB pJob;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+    WCHAR wszFullPath[MAX_PATH];
+
+    // Check if this is a printer handle.
+    pHandle = (PLOCAL_HANDLE)hPrinter;
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // Get the desired job.
+    pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
+    if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Setting job information is handled differently for each level.
+    if (Level)
+    {
+        if (Level == 1)
+            dwErrorCode = _LocalSetJobLevel1(pPrinterHandle, pJob, (PJOB_INFO_1W)pJobInfo);
+        else if (Level == 2)
+            dwErrorCode = _LocalSetJobLevel2(pPrinterHandle, pJob, (PJOB_INFO_2W)pJobInfo);
+        else
+            dwErrorCode = ERROR_INVALID_LEVEL;
+    }
+
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    // If we do spooled printing, the job information is written down into a shadow file.
+    if (!(pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT))
+    {
+        // Write the job data into the shadow file.
+        GetJobFilePath(L"SHD", JobId, wszFullPath);
+        WriteJobShadowFile(wszFullPath, pJob);
+    }
+
+    // Perform an additional command if desired.
+    if (Command)
+    {
+        if (Command == JOB_CONTROL_SENT_TO_PRINTER)
+        {
+            // This indicates the end of the Print Job.
+
+            // Cancel the Job at the Print Processor.
+            if (pJob->hPrintProcessor)
+                pJob->pPrintProcessor->pfnControlPrintProcessor(pJob->hPrintProcessor, JOB_CONTROL_CANCEL);
+
+            FreeJob(pJob);
+
+            // TODO: All open handles associated with the job need to be invalidated.
+            // This certainly needs handle tracking...
+        }
+        else
+        {
+            ERR("Unimplemented SetJob Command: %lu!\n", Command);
+        }
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalEnumJobs(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
+{
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE pEnd = &pStart[cbBuf];
+    PLOCAL_HANDLE pHandle;
+    PLOCAL_JOB pJob;
+    PSKIPLIST_NODE pFirstJobNode;
+    PSKIPLIST_NODE pNode;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Check if this is a printer handle.
+    pHandle = (PLOCAL_HANDLE)hPrinter;
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // Check the level.
+    if (Level > 2)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    // Begin counting.
+    *pcbNeeded = 0;
+    *pcReturned = 0;
+
+    // Lookup the node of the first job requested by the caller in the Printer's Job List.
+    pFirstJobNode = LookupNodeByIndexSkiplist(&pPrinterHandle->pPrinter->JobList, FirstJob);
+
+    // Count the required buffer size and the number of jobs.
+    i = 0;
+    pNode = pFirstJobNode;
+
+    while (i < NoJobs && pNode)
+    {
+        pJob = (PLOCAL_JOB)pNode->Element;
+
+        // The function behaves differently for each level.
+        if (Level == 1)
+            _LocalGetJobLevel1(pPrinterHandle, pJob, NULL, NULL, 0, pcbNeeded);
+        else if (Level == 2)
+            _LocalGetJobLevel2(pPrinterHandle, pJob, NULL, NULL, 0, pcbNeeded);
+
+        // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
+        i++;
+        pNode = pNode->Next[0];
+    }
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Begin counting again and also empty the given buffer.
+    *pcbNeeded = 0;
+    ZeroMemory(pStart, cbBuf);
+
+    // Now call the same functions again to copy the actual data for each job into the buffer.
+    i = 0;
+    pNode = pFirstJobNode;
+
+    while (i < NoJobs && pNode)
+    {
+        pJob = (PLOCAL_JOB)pNode->Element;
+
+        // The function behaves differently for each level.
+        if (Level == 1)
+            dwErrorCode = _LocalGetJobLevel1(pPrinterHandle, pJob, &pStart, &pEnd, cbBuf, pcbNeeded);
+        else if (Level == 2)
+            dwErrorCode = _LocalGetJobLevel2(pPrinterHandle, pJob, &pStart, &pEnd, cbBuf, pcbNeeded);
+
+        if (dwErrorCode != ERROR_SUCCESS)
+            goto Cleanup;
+
+        // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
+        i++;
+        pNode = pNode->Next[0];
+    }
+
+    *pcReturned = i;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
+{
+    DWORD dwAttributes;
+    DWORD dwErrorCode;
+    HANDLE hThread;
+    PLOCAL_JOB pJob;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+    WCHAR wszFullPath[MAX_PATH];
+
+    // Check if this is a printer handle.
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // Check if the Job ID is valid.
+    pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
+    if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Check if this Job was started with AddJob.
+    if (!pJob->bAddedJob)
+    {
+        dwErrorCode = ERROR_SPL_NO_ADDJOB;
+        goto Cleanup;
+    }
+
+    // Construct the full path to the spool file.
+    GetJobFilePath(L"SPL", dwJobID, wszFullPath);
+
+    // Check if it exists.
+    dwAttributes = GetFileAttributesW(wszFullPath);
+    if (dwAttributes == INVALID_FILE_ATTRIBUTES || dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
+    {
+        dwErrorCode = ERROR_SPOOL_FILE_NOT_FOUND;
+        goto Cleanup;
+    }
+
+    // Spooling is finished at this point.
+    pJob->dwStatus &= ~JOB_STATUS_SPOOLING;
+
+    // Write the job data into the shadow file.
+    wcscpy(wcsrchr(wszFullPath, L'.'), L".SHD");
+    WriteJobShadowFile(wszFullPath, pJob);
+
+    // Create the thread for performing the printing process.
+    hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PrintingThreadProc, pJob, 0, NULL);
+    if (!hThread)
+    {
+        dwErrorCode = GetLastError();
+        ERR("CreateThread failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // We don't need the thread handle. Keeping it open blocks the thread from terminating.
+    CloseHandle(hThread);
+
+    // ScheduleJob has done its job. The rest happens inside the thread.
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+PLOCAL_JOB
+ReadJobShadowFile(PCWSTR pwszFilePath)
+{
+    DWORD cbFileSize;
+    DWORD cbRead;
+    HANDLE hFile = INVALID_HANDLE_VALUE;
+    PLOCAL_JOB pJob;
+    PLOCAL_JOB pReturnValue = NULL;
+    PLOCAL_PRINTER pPrinter;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+    PSHD_HEADER pShadowFile = NULL;
+    PWSTR pwszPrinterName;
+    PWSTR pwszPrintProcessor;
+
+    // Try to open the file.
+    hFile = CreateFileW(pwszFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+    {
+        ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
+        goto Cleanup;
+    }
+
+    // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
+    cbFileSize = GetFileSize(hFile, NULL);
+    pShadowFile = DllAllocSplMem(cbFileSize);
+    if (!pShadowFile)
+    {
+        ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
+        goto Cleanup;
+    }
+
+    // Read the entire file.
+    if (!ReadFile(hFile, pShadowFile, cbFileSize, &cbRead, NULL))
+    {
+        ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
+        goto Cleanup;
+    }
+
+    // Check signature and header size.
+    if (pShadowFile->dwSignature != SHD_WIN2003_SIGNATURE || pShadowFile->cbHeader != sizeof(SHD_HEADER))
+    {
+        ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath);
+        goto Cleanup;
+    }
+
+    // Retrieve the associated printer from the list.
+    pwszPrinterName = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrinterName);
+    pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
+    if (!pPrinter)
+    {
+        ERR("Shadow file \"%S\" references a non-existing printer \"%S\"!\n", pwszFilePath, pwszPrinterName);
+        goto Cleanup;
+    }
+
+    // Retrieve the associated Print Processor from the list.
+    pwszPrintProcessor = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrintProcessor);
+    pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
+    if (!pPrintProcessor)
+    {
+        ERR("Shadow file \"%S\" references a non-existing Print Processor \"%S\"!\n", pwszFilePath, pwszPrintProcessor);
+        goto Cleanup;
+    }
+
+    // Create a new job structure and copy over the relevant fields.
+    pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
+    if (!pJob)
+    {
+        ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
+        goto Cleanup;
+    }
+
+    pJob->dwJobID = pShadowFile->dwJobID;
+    pJob->dwPriority = pShadowFile->dwPriority;
+    pJob->dwStartTime = pShadowFile->dwStartTime;
+    pJob->dwTotalPages = pShadowFile->dwTotalPages;
+    pJob->dwUntilTime = pShadowFile->dwUntilTime;
+    pJob->pPrinter = pPrinter;
+    pJob->pPrintProcessor = pPrintProcessor;
+    pJob->pDevMode = DuplicateDevMode((PDEVMODEW)((ULONG_PTR)pShadowFile + pShadowFile->offDevMode));
+    pJob->pwszDatatype = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDatatype));
+    pJob->pwszMachineName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offMachineName));
+    CopyMemory(&pJob->stSubmitted, &pShadowFile->stSubmitted, sizeof(SYSTEMTIME));
+
+    // Copy the optional values.
+    if (pShadowFile->offDocumentName)
+        pJob->pwszDocumentName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDocumentName));
+
+    if (pShadowFile->offNotifyName)
+        pJob->pwszNotifyName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offNotifyName));
+
+    if (pShadowFile->offPrintProcessorParameters)
+        pJob->pwszPrintProcessorParameters = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrintProcessorParameters));
+
+    if (pShadowFile->offUserName)
+        pJob->pwszUserName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offUserName));
+
+    // Jobs read from shadow files were always added using AddJob.
+    pJob->bAddedJob = TRUE;
+
+    pReturnValue = pJob;
+
+Cleanup:
+    if (pShadowFile)
+        DllFreeSplMem(pShadowFile);
+
+    if (hFile != INVALID_HANDLE_VALUE)
+        CloseHandle(hFile);
+
+    return pReturnValue;
+}
+
+BOOL
+WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob)
+{
+    BOOL bReturnValue = FALSE;
+    DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
+    DWORD cbDevMode = pJob->pDevMode->dmSize + pJob->pDevMode->dmDriverExtra;
+    DWORD cbDocumentName = 0;
+    DWORD cbFileSize;
+    DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
+    DWORD cbNotifyName = 0;
+    DWORD cbPrinterDriver = (wcslen(pJob->pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
+    DWORD cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+    DWORD cbPrintProcessor = (wcslen(pJob->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
+    DWORD cbPrintProcessorParameters = 0;
+    DWORD cbUserName = 0;
+    DWORD cbWritten;
+    DWORD dwCurrentOffset;
+    HANDLE hSHDFile = INVALID_HANDLE_VALUE;
+    HANDLE hSPLFile = INVALID_HANDLE_VALUE;
+    PSHD_HEADER pShadowFile = NULL;
+
+    // Try to open the SHD file.
+    hSHDFile = CreateFileW(pwszFilePath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
+    if (hSHDFile == INVALID_HANDLE_VALUE)
+    {
+        ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
+        goto Cleanup;
+    }
+
+    // Calculate the lengths of the optional values and the total size of the shadow file.
+    if (pJob->pwszDocumentName)
+        cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
+
+    if (pJob->pwszNotifyName)
+        cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
+
+    if (pJob->pwszPrintProcessorParameters)
+        cbPrintProcessorParameters = (wcslen(pJob->pwszPrintProcessorParameters) + 1) * sizeof(WCHAR);
+
+    if (pJob->pwszUserName)
+        cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
+
+    cbFileSize = sizeof(SHD_HEADER) + cbDatatype + cbDocumentName + cbDevMode + cbMachineName + cbNotifyName + cbPrinterDriver + cbPrinterName + cbPrintProcessor + cbPrintProcessorParameters + cbUserName;
+
+    // Allocate memory for it.
+    pShadowFile = DllAllocSplMem(cbFileSize);
+    if (!pShadowFile)
+    {
+        ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
+        goto Cleanup;
+    }
+
+    // Fill out the shadow file header information.
+    pShadowFile->dwSignature = SHD_WIN2003_SIGNATURE;
+    pShadowFile->cbHeader = sizeof(SHD_HEADER);
+
+    // Copy the values.
+    pShadowFile->dwJobID = pJob->dwJobID;
+    pShadowFile->dwPriority = pJob->dwPriority;
+    pShadowFile->dwStartTime = pJob->dwStartTime;
+    pShadowFile->dwTotalPages = pJob->dwTotalPages;
+    pShadowFile->dwUntilTime = pJob->dwUntilTime;
+    CopyMemory(&pShadowFile->stSubmitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
+
+    // Determine the file size of the .SPL file
+    wcscpy(wcsrchr(pwszFilePath, L'.'), L".SPL");
+    hSPLFile = CreateFileW(pwszFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+    if (hSPLFile != INVALID_HANDLE_VALUE)
+        pShadowFile->dwSPLSize = GetFileSize(hSPLFile, NULL);
+
+    // Add the extra values that are stored as offsets in the shadow file.
+    // The first value begins right after the shadow file header.
+    dwCurrentOffset = sizeof(SHD_HEADER);
+
+    CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDatatype, cbDatatype);
+    pShadowFile->offDatatype = dwCurrentOffset;
+    dwCurrentOffset += cbDatatype;
+
+    CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pDevMode, cbDevMode);
+    pShadowFile->offDevMode = dwCurrentOffset;
+    dwCurrentOffset += cbDevMode;
+
+    // offDriverName is only written, but automatically determined through offPrinterName when reading.
+    CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrinter->pwszPrinterDriver, cbPrinterDriver);
+    pShadowFile->offDriverName = dwCurrentOffset;
+    dwCurrentOffset += cbPrinterDriver;
+
+    CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszMachineName, cbMachineName);
+    pShadowFile->offMachineName = dwCurrentOffset;
+    dwCurrentOffset += cbMachineName;
+
+    CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrinter->pwszPrinterName, cbPrinterName);
+    pShadowFile->offPrinterName = dwCurrentOffset;
+    dwCurrentOffset += cbPrinterName;
+
+    CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrintProcessor->pwszName, cbPrintProcessor);
+    pShadowFile->offPrintProcessor = dwCurrentOffset;
+    dwCurrentOffset += cbPrintProcessor;
+
+    // Copy the optional values.
+    if (cbDocumentName)
+    {
+        CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDocumentName, cbDocumentName);
+        pShadowFile->offDocumentName = dwCurrentOffset;
+        dwCurrentOffset += cbDocumentName;
+    }
+
+    if (cbNotifyName)
+    {
+        CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszNotifyName, cbNotifyName);
+        pShadowFile->offNotifyName = dwCurrentOffset;
+        dwCurrentOffset += cbNotifyName;
+    }
+
+    if (cbPrintProcessorParameters)
+    {
+        CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszPrintProcessorParameters, cbPrintProcessorParameters);
+        pShadowFile->offPrintProcessorParameters = dwCurrentOffset;
+        dwCurrentOffset += cbPrintProcessorParameters;
+    }
+
+    if (cbUserName)
+    {
+        CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszUserName, cbUserName);
+        pShadowFile->offUserName = dwCurrentOffset;
+        dwCurrentOffset += cbUserName;
+    }
+
+    // Write the file.
+    if (!WriteFile(hSHDFile, pShadowFile, cbFileSize, &cbWritten, NULL))
+    {
+        ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
+        goto Cleanup;
+    }
+
+    bReturnValue = TRUE;
+
+Cleanup:
+    if (pShadowFile)
+        DllFreeSplMem(pShadowFile);
+
+    if (hSHDFile != INVALID_HANDLE_VALUE)
+        CloseHandle(hSHDFile);
+
+    if (hSPLFile != INVALID_HANDLE_VALUE)
+        CloseHandle(hSPLFile);
+
+    return bReturnValue;
+}
+
+void
+FreeJob(PLOCAL_JOB pJob)
+{
+    PWSTR pwszSHDFile;
+    
+    // Remove the Job from both Job Lists.
+    DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
+    DeleteElementSkiplist(&GlobalJobList, pJob);
+
+    // Try to delete the corresponding .SHD file.
+    pwszSHDFile = DllAllocSplMem(GetJobFilePath(L"SHD", 0, NULL));
+    if (pwszSHDFile && GetJobFilePath(L"SHD", pJob->dwJobID, pwszSHDFile))
+        DeleteFileW(pwszSHDFile);
+
+    // Free memory for the mandatory fields.
+    DllFreeSplMem(pJob->pDevMode);
+    DllFreeSplStr(pJob->pwszDatatype);
+    DllFreeSplStr(pJob->pwszDocumentName);
+    DllFreeSplStr(pJob->pwszMachineName);
+    DllFreeSplStr(pJob->pwszNotifyName);
+    DllFreeSplStr(pJob->pwszUserName);
+
+    // Free memory for the optional fields if they are present.
+    if (pJob->pwszOutputFile)
+        DllFreeSplStr(pJob->pwszOutputFile);
+    
+    if (pJob->pwszPrintProcessorParameters)
+        DllFreeSplStr(pJob->pwszPrintProcessorParameters);
+
+    if (pJob->pwszStatus)
+        DllFreeSplStr(pJob->pwszStatus);
+
+    // Finally free the job structure itself.
+    DllFreeSplMem(pJob);
+}
diff --git a/reactos/win32ss/printing/providers/localspl/localspl.rc b/reactos/win32ss/printing/providers/localspl/localspl.rc
new file mode 100644 (file)
index 0000000..71b7ec5
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION  "ReactOS Local Spooler"
+#define REACTOS_STR_INTERNAL_NAME     "localspl"
+#define REACTOS_STR_ORIGINAL_FILENAME "localspl.dll"
+#include <reactos/version.rc>
diff --git a/reactos/win32ss/printing/providers/localspl/localspl.spec b/reactos/win32ss/printing/providers/localspl/localspl.spec
new file mode 100644 (file)
index 0000000..12e6e29
--- /dev/null
@@ -0,0 +1 @@
+@ stdcall InitializePrintProvidor(ptr long ptr)
diff --git a/reactos/win32ss/printing/providers/localspl/main.c b/reactos/win32ss/printing/providers/localspl/main.c
new file mode 100644 (file)
index 0000000..98b4d17
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Main functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+// Global Variables
+WCHAR wszSpoolDirectory[MAX_PATH];
+DWORD cchSpoolDirectory;
+
+// Global Constants
+const WCHAR wszCurrentEnvironment[] =
+#if defined(_X86_)
+    L"Windows NT x86";
+#elif defined(_AMD64_)
+    L"Windows x64";
+#elif defined(_ARM_)
+    L"Windows ARM";
+#else
+    #error Unsupported architecture
+#endif
+
+const DWORD cbCurrentEnvironment = sizeof(wszCurrentEnvironment);
+
+const WCHAR wszDefaultDocumentName[] = L"Local Downlevel Document";
+
+const WCHAR* wszPrintProviderInfo[3] = {
+    L"Windows NT Local Print Providor",     // Name
+    L"Windows NT Local Printers",           // Description
+    L"Locally connected Printers"           // Comment
+};
+
+// Local Constants
+static const PRINTPROVIDOR _PrintProviderFunctions = {
+    LocalOpenPrinter,                           // fpOpenPrinter
+    LocalSetJob,                                // fpSetJob
+    LocalGetJob,                                // fpGetJob
+    LocalEnumJobs,                              // fpEnumJobs
+    NULL,                                       // fpAddPrinter
+    NULL,                                       // fpDeletePrinter
+    NULL,                                       // fpSetPrinter
+    NULL,                                       // fpGetPrinter
+    LocalEnumPrinters,                          // fpEnumPrinters
+    NULL,                                       // fpAddPrinterDriver
+    NULL,                                       // fpEnumPrinterDrivers
+    NULL,                                       // fpGetPrinterDriver
+    NULL,                                       // fpGetPrinterDriverDirectory
+    NULL,                                       // fpDeletePrinterDriver
+    NULL,                                       // fpAddPrintProcessor
+    LocalEnumPrintProcessors,                   // fpEnumPrintProcessors
+    LocalGetPrintProcessorDirectory,            // fpGetPrintProcessorDirectory
+    NULL,                                       // fpDeletePrintProcessor
+    LocalEnumPrintProcessorDatatypes,           // fpEnumPrintProcessorDatatypes
+    LocalStartDocPrinter,                       // fpStartDocPrinter
+    LocalStartPagePrinter,                      // fpStartPagePrinter
+    LocalWritePrinter,                          // fpWritePrinter
+    LocalEndPagePrinter,                        // fpEndPagePrinter
+    NULL,                                       // fpAbortPrinter
+    LocalReadPrinter,                           // fpReadPrinter
+    LocalEndDocPrinter,                         // fpEndDocPrinter
+    LocalAddJob,                                // fpAddJob
+    LocalScheduleJob,                           // fpScheduleJob
+    NULL,                                       // fpGetPrinterData
+    NULL,                                       // fpSetPrinterData
+    NULL,                                       // fpWaitForPrinterChange
+    LocalClosePrinter,                          // fpClosePrinter
+    NULL,                                       // fpAddForm
+    NULL,                                       // fpDeleteForm
+    NULL,                                       // fpGetForm
+    NULL,                                       // fpSetForm
+    NULL,                                       // fpEnumForms
+    LocalEnumMonitors,                          // fpEnumMonitors
+    LocalEnumPorts,                             // fpEnumPorts
+    NULL,                                       // fpAddPort
+    NULL,                                       // fpConfigurePort
+    NULL,                                       // fpDeletePort
+    NULL,                                       // fpCreatePrinterIC
+    NULL,                                       // fpPlayGdiScriptOnPrinterIC
+    NULL,                                       // fpDeletePrinterIC
+    NULL,                                       // fpAddPrinterConnection
+    NULL,                                       // fpDeletePrinterConnection
+    NULL,                                       // fpPrinterMessageBox
+    NULL,                                       // fpAddMonitor
+    NULL,                                       // fpDeleteMonitor
+    NULL,                                       // fpResetPrinter
+    NULL,                                       // fpGetPrinterDriverEx
+    NULL,                                       // fpFindFirstPrinterChangeNotification
+    NULL,                                       // fpFindClosePrinterChangeNotification
+    NULL,                                       // fpAddPortEx
+    NULL,                                       // fpShutDown
+    NULL,                                       // fpRefreshPrinterChangeNotification
+    NULL,                                       // fpOpenPrinterEx
+    NULL,                                       // fpAddPrinterEx
+    NULL,                                       // fpSetPort
+    NULL,                                       // fpEnumPrinterData
+    NULL,                                       // fpDeletePrinterData
+    NULL,                                       // fpClusterSplOpen
+    NULL,                                       // fpClusterSplClose
+    NULL,                                       // fpClusterSplIsAlive
+    NULL,                                       // fpSetPrinterDataEx
+    NULL,                                       // fpGetPrinterDataEx
+    NULL,                                       // fpEnumPrinterDataEx
+    NULL,                                       // fpEnumPrinterKey
+    NULL,                                       // fpDeletePrinterDataEx
+    NULL,                                       // fpDeletePrinterKey
+    NULL,                                       // fpSeekPrinter
+    NULL,                                       // fpDeletePrinterDriverEx
+    NULL,                                       // fpAddPerMachineConnection
+    NULL,                                       // fpDeletePerMachineConnection
+    NULL,                                       // fpEnumPerMachineConnections
+    NULL,                                       // fpXcvData
+    NULL,                                       // fpAddPrinterDriverEx
+    NULL,                                       // fpSplReadPrinter
+    NULL,                                       // fpDriverUnloadComplete
+    NULL,                                       // fpGetSpoolFileInfo
+    NULL,                                       // fpCommitSpoolData
+    NULL,                                       // fpCloseSpoolFileHandle
+    NULL,                                       // fpFlushPrinter
+    NULL,                                       // fpSendRecvBidiData
+    NULL,                                       // fpAddDriverCatalog
+};
+
+static void
+_GetSpoolDirectory()
+{
+    const WCHAR wszSpoolPath[] = L"\\spool";
+    const DWORD cchSpoolPath = _countof(wszSpoolPath) - 1;
+
+    // Get the system directory and append the "spool" subdirectory.
+    // Forget about length checks here. If this doesn't fit into MAX_PATH, our OS has more serious problems...
+    cchSpoolDirectory = GetSystemDirectoryW(wszSpoolDirectory, MAX_PATH);
+    CopyMemory(&wszSpoolDirectory[cchSpoolDirectory], wszSpoolPath, (cchSpoolPath + 1) * sizeof(WCHAR));
+    cchSpoolDirectory += cchSpoolPath;
+}
+
+BOOL WINAPI
+DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    switch (fdwReason)
+    {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls(hinstDLL);
+            _GetSpoolDirectory();
+
+            return InitializePrintMonitorList() &&
+                   InitializePortList() &&
+                   InitializePrintProcessorList() &&
+                   InitializePrinterList() &&
+                   InitializeGlobalJobList();
+
+        default:
+            return TRUE;
+    }
+}
+
+BOOL WINAPI
+InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor, DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
+{
+    CopyMemory(pPrintProvidor, &_PrintProviderFunctions, min(cbPrintProvidor, sizeof(PRINTPROVIDOR)));
+
+    SetLastError(ERROR_SUCCESS);
+    return TRUE;
+}
diff --git a/reactos/win32ss/printing/providers/localspl/monitors.c b/reactos/win32ss/printing/providers/localspl/monitors.c
new file mode 100644 (file)
index 0000000..c3ce99d
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Print Monitors
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+// Global Variables
+LIST_ENTRY PrintMonitorList;
+
+
+PLOCAL_PRINT_MONITOR
+FindPrintMonitor(PCWSTR pwszName)
+{
+    PLIST_ENTRY pEntry;
+    PLOCAL_PRINT_MONITOR pPrintMonitor;
+
+    if (!pwszName)
+        return NULL;
+
+    for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
+    {
+        pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
+
+        if (_wcsicmp(pPrintMonitor->pwszName, pwszName) == 0)
+            return pPrintMonitor;
+    }
+
+    return NULL;
+}
+
+BOOL
+InitializePrintMonitorList()
+{
+    const WCHAR wszMonitorsPath[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors";
+    const DWORD cchMonitorsPath = _countof(wszMonitorsPath) - 1;
+
+    DWORD cchMaxSubKey;
+    DWORD cchPrintMonitorName;
+    DWORD dwErrorCode;
+    DWORD dwSubKeys;
+    DWORD i;
+    HINSTANCE hinstPrintMonitor = NULL;
+    HKEY hKey = NULL;
+    HKEY hSubKey = NULL;
+    MONITORINIT MonitorInit;
+    PInitializePrintMonitor pfnInitializePrintMonitor;
+    PInitializePrintMonitor2 pfnInitializePrintMonitor2;
+    PLOCAL_PRINT_MONITOR pPrintMonitor = NULL;
+    PWSTR pwszRegistryPath = NULL;
+
+    // Initialize an empty list for our Print Monitors.
+    InitializeListHead(&PrintMonitorList);
+
+    // Open the key containing Print Monitors.
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszMonitorsPath, 0, KEY_READ, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Get the number of Print Providers and maximum sub key length.
+    dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Loop through all available Print Providers.
+    for (i = 0; i < dwSubKeys; i++)
+    {
+        // Cleanup tasks from the previous run
+        if (hSubKey)
+        {
+            RegCloseKey(hSubKey);
+            hSubKey = NULL;
+        }
+
+        if (pwszRegistryPath)
+        {
+            DllFreeSplMem(pwszRegistryPath);
+            pwszRegistryPath = NULL;
+        }
+
+        if (pPrintMonitor)
+        {
+            if (pPrintMonitor->pwszFileName)
+                DllFreeSplMem(pPrintMonitor->pwszFileName);
+
+            if (pPrintMonitor->pwszName)
+                DllFreeSplMem(pPrintMonitor->pwszName);
+
+            DllFreeSplMem(pPrintMonitor);
+            pPrintMonitor = NULL;
+        }
+
+        // Create a new LOCAL_PRINT_MONITOR structure for it.
+        pPrintMonitor = DllAllocSplMem(sizeof(LOCAL_PRINT_MONITOR));
+        if (!pPrintMonitor)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        // Allocate memory for the Print Monitor Name.
+        pPrintMonitor->pwszName = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
+        if (!pPrintMonitor->pwszName)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        // Get the name of this Print Monitor.
+        cchPrintMonitorName = cchMaxSubKey + 1;
+        dwErrorCode = (DWORD)RegEnumKeyExW(hKey, i, pPrintMonitor->pwszName, &cchPrintMonitorName, NULL, NULL, NULL, NULL);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
+            continue;
+        }
+
+        // Open this Print Monitor's registry key.
+        dwErrorCode = (DWORD)RegOpenKeyExW(hKey, pPrintMonitor->pwszName, 0, KEY_READ, &hSubKey);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegOpenKeyExW failed for Print Provider \"%S\" with status %lu!\n", pPrintMonitor->pwszName, dwErrorCode);
+            continue;
+        }
+
+        // Get the file name of the Print Monitor.
+        pPrintMonitor->pwszFileName = AllocAndRegQueryWSZ(hSubKey, L"Driver");
+        if (!pPrintMonitor->pwszFileName)
+            continue;
+
+        // Try to load it.
+        hinstPrintMonitor = LoadLibraryW(pPrintMonitor->pwszFileName);
+        if (!hinstPrintMonitor)
+        {
+            ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
+            continue;
+        }
+
+        // Try to find a Level 2 initialization routine first.
+        pfnInitializePrintMonitor2 = (PInitializePrintMonitor2)GetProcAddress(hinstPrintMonitor, "InitializePrintMonitor2");
+        if (pfnInitializePrintMonitor2)
+        {
+            // Prepare a MONITORINIT structure.
+            MonitorInit.cbSize = sizeof(MONITORINIT);
+            MonitorInit.bLocal = TRUE;
+
+            // TODO: Fill the other fields.
+
+            // Call the Level 2 initialization routine.
+            pPrintMonitor->pMonitor = (PMONITOR2)pfnInitializePrintMonitor2(&MonitorInit, &pPrintMonitor->hMonitor);
+            if (!pPrintMonitor->pMonitor)
+            {
+                ERR("InitializePrintMonitor2 failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
+                continue;
+            }
+
+            pPrintMonitor->bIsLevel2 = TRUE;
+        }
+        else
+        {
+            // Try to find a Level 1 initialization routine then.
+            pfnInitializePrintMonitor = (PInitializePrintMonitor)GetProcAddress(hinstPrintMonitor, "InitializePrintMonitor");
+            if (pfnInitializePrintMonitor)
+            {
+                // Construct the registry path.
+                pwszRegistryPath = DllAllocSplMem((cchMonitorsPath + 1 + cchPrintMonitorName + 1) * sizeof(WCHAR));
+                if (!pwszRegistryPath)
+                {
+                    dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+                    ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+                    goto Cleanup;
+                }
+
+                CopyMemory(pwszRegistryPath, wszMonitorsPath, cchMonitorsPath * sizeof(WCHAR));
+                pwszRegistryPath[cchMonitorsPath] = L'\\';
+                CopyMemory(&pwszRegistryPath[cchMonitorsPath + 1], pPrintMonitor->pwszName, (cchPrintMonitorName + 1) * sizeof(WCHAR));
+
+                // Call the Level 1 initialization routine.
+                pPrintMonitor->pMonitor = (LPMONITOREX)pfnInitializePrintMonitor(pwszRegistryPath);
+                if (!pPrintMonitor->pMonitor)
+                {
+                    ERR("InitializePrintMonitor failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
+                    continue;
+                }
+            }
+            else
+            {
+                ERR("No initialization routine found for \"%S\"!\n", pPrintMonitor->pwszFileName);
+                continue;
+            }
+        }
+
+        // Add this Print Monitor to the list.
+        InsertTailList(&PrintMonitorList, &pPrintMonitor->Entry);
+
+        // Don't let the cleanup routine free this.
+        pPrintMonitor = NULL;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    // Inside the loop
+    if (hSubKey)
+        RegCloseKey(hSubKey);
+
+    if (pwszRegistryPath)
+        DllFreeSplMem(pwszRegistryPath);
+
+    if (pPrintMonitor)
+    {
+        if (pPrintMonitor->pwszFileName)
+            DllFreeSplMem(pPrintMonitor->pwszFileName);
+
+        if (pPrintMonitor->pwszName)
+            DllFreeSplMem(pPrintMonitor->pwszName);
+
+        DllFreeSplMem(pPrintMonitor);
+    }
+
+    // Outside the loop
+    if (hKey)
+        RegCloseKey(hKey);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalEnumMonitors(PWSTR pName, DWORD Level, PBYTE pMonitors, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    DWORD cbFileName;
+    DWORD cbMonitorName;
+    DWORD dwErrorCode;
+    PBYTE pStart;
+    PBYTE pEnd;
+    PLIST_ENTRY pEntry;
+    PLOCAL_PRINT_MONITOR pPrintMonitor;
+    MONITOR_INFO_2W MonitorInfo2;               // MONITOR_INFO_1W is a subset of MONITOR_INFO_2W, so we can handle both in one function here.
+
+    // Sanity checks.
+    if (Level > 2)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    if ((cbBuf && !pMonitors) || !pcbNeeded || !pcReturned)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Begin counting.
+    *pcbNeeded = 0;
+    *pcReturned = 0;
+
+    // Count the required buffer size and the number of monitors.
+    for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
+    {
+        pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
+
+        cbMonitorName = (wcslen(pPrintMonitor->pwszName) + 1) * sizeof(WCHAR);
+        cbFileName = (wcslen(pPrintMonitor->pwszFileName) + 1) * sizeof(WCHAR);
+
+        if (Level == 1)
+            *pcbNeeded += sizeof(MONITOR_INFO_1W) + cbMonitorName;
+        else
+            *pcbNeeded += sizeof(MONITOR_INFO_2W) + cbMonitorName + cbCurrentEnvironment + cbFileName;
+    }
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Put the strings at the end of the buffer.
+    pStart = pMonitors;
+    pEnd = &pMonitors[cbBuf];
+
+    for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
+    {
+        pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
+
+        // Copy the monitor name.
+        cbMonitorName = (wcslen(pPrintMonitor->pwszName) + 1) * sizeof(WCHAR);
+        pEnd -= cbMonitorName;
+        MonitorInfo2.pName = (PWSTR)pEnd;
+        CopyMemory(pEnd, pPrintMonitor->pwszName, cbMonitorName);
+
+        if (Level == 1)
+        {
+            // Finally copy the structure.
+            CopyMemory(pStart, &MonitorInfo2, sizeof(MONITOR_INFO_1W));
+            pStart += sizeof(MONITOR_INFO_1W);
+        }
+        else
+        {
+            // Copy the environment.
+            pEnd -= cbCurrentEnvironment;
+            MonitorInfo2.pEnvironment = (PWSTR)pEnd;
+            CopyMemory(pEnd, wszCurrentEnvironment, cbCurrentEnvironment);
+
+            // Copy the file name.
+            cbFileName = (wcslen(pPrintMonitor->pwszFileName) + 1) * sizeof(WCHAR);
+            pEnd -= cbFileName;
+            MonitorInfo2.pDLLName = (PWSTR)pEnd;
+            CopyMemory(pEnd, pPrintMonitor->pwszFileName, cbFileName);
+
+            // Finally copy the structure.
+            CopyMemory(pStart, &MonitorInfo2, sizeof(MONITOR_INFO_2W));
+            pStart += sizeof(MONITOR_INFO_2W);
+        }
+
+        (*pcReturned)++;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
diff --git a/reactos/win32ss/printing/providers/localspl/ports.c b/reactos/win32ss/printing/providers/localspl/ports.c
new file mode 100644 (file)
index 0000000..1685a8a
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Ports of the Print Monitors
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+// Local Variables
+static LIST_ENTRY _PortList;
+
+
+PLOCAL_PORT
+FindPort(PCWSTR pwszName)
+{
+    PLIST_ENTRY pEntry;
+    PLOCAL_PORT pPort;
+
+    if (!pwszName)
+        return NULL;
+
+    for (pEntry = _PortList.Flink; pEntry != &_PortList; pEntry = pEntry->Flink)
+    {
+        pPort = CONTAINING_RECORD(pEntry, LOCAL_PORT, Entry);
+
+        if (_wcsicmp(pPort->pwszName, pwszName) == 0)
+            return pPort;
+    }
+
+    return NULL;
+}
+
+BOOL
+InitializePortList()
+{
+    BOOL bReturnValue;
+    DWORD cbNeeded;
+    DWORD cbPortName;
+    DWORD dwErrorCode;
+    DWORD dwReturned;
+    DWORD i;
+    PLOCAL_PORT pPort;
+    PLOCAL_PRINT_MONITOR pPrintMonitor;
+    PLIST_ENTRY pEntry;
+    PPORT_INFO_1W p;
+    PPORT_INFO_1W pPortInfo1 = NULL;
+
+    // Initialize an empty list for our Ports.
+    InitializeListHead(&_PortList);
+
+    // Loop through all Print Monitors.
+    for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
+    {
+        // Cleanup from the previous run.
+        if (pPortInfo1)
+        {
+            DllFreeSplMem(pPortInfo1);
+            pPortInfo1 = NULL;
+        }
+
+        pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
+
+        // Determine the required buffer size for EnumPorts.
+        if (pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnEnumPorts(pPrintMonitor->hMonitor, NULL, 1, NULL, 0, &cbNeeded, &dwReturned);
+        else
+            bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnEnumPorts(NULL, 1, NULL, 0, &cbNeeded, &dwReturned);
+
+        // Check the returned error code.
+        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+        {
+            ERR("Print Monitor \"%S\" failed with error %lu on EnumPorts!\n", pPrintMonitor->pwszName, GetLastError());
+            continue;
+        }
+
+        // Allocate a buffer large enough.
+        pPortInfo1 = DllAllocSplMem(cbNeeded);
+        if (!pPortInfo1)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        // Get the ports handled by this monitor.
+        if (pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnEnumPorts(pPrintMonitor->hMonitor, NULL, 1, (PBYTE)pPortInfo1, cbNeeded, &cbNeeded, &dwReturned);
+        else
+            bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnEnumPorts(NULL, 1, (PBYTE)pPortInfo1, cbNeeded, &cbNeeded, &dwReturned);
+
+        // Check the return value.
+        if (!bReturnValue)
+        {
+            ERR("Print Monitor \"%S\" failed with error %lu on EnumPorts!\n", pPrintMonitor->pwszName, GetLastError());
+            continue;
+        }
+
+        // Loop through all returned ports.
+        p = pPortInfo1;
+
+        for (i = 0; i < dwReturned; i++)
+        {
+            cbPortName = (wcslen(p->pName) + 1) * sizeof(WCHAR);
+
+            // Create a new LOCAL_PORT structure for it.
+            pPort = DllAllocSplMem(sizeof(LOCAL_PORT) + cbPortName);
+            if (!pPort)
+            {
+                dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+                ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+                goto Cleanup;
+            }
+
+            pPort->pPrintMonitor = pPrintMonitor;
+            pPort->pwszName = (PWSTR)((PBYTE)pPort + sizeof(LOCAL_PORT));
+            CopyMemory(pPort->pwszName, p->pName, cbPortName);
+
+            // Insert it into the list and advance to the next port.
+            InsertTailList(&_PortList, &pPort->Entry);
+            p++;
+        }
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    // Inside the loop
+    if (pPortInfo1)
+        DllFreeSplMem(pPortInfo1);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalEnumPorts(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    BOOL bReturnValue;
+    DWORD cbCallBuffer;
+    DWORD cbNeeded;
+    DWORD dwReturned;
+    PBYTE pCallBuffer;
+    PLOCAL_PRINT_MONITOR pPrintMonitor;
+    PLIST_ENTRY pEntry;
+
+    // Sanity checks.
+    if ((cbBuf && !pPorts) || !pcbNeeded || !pcReturned)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // Begin counting.
+    *pcbNeeded = 0;
+    *pcReturned = 0;
+
+    // At the beginning, we have the full buffer available.
+    cbCallBuffer = cbBuf;
+    pCallBuffer = pPorts;
+
+    // Loop through all Print Monitors.
+    for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
+    {
+        pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
+
+        // Call the EnumPorts function of this Print Monitor.
+        if (pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnEnumPorts(pPrintMonitor->hMonitor, pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
+        else
+            bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnEnumPorts(pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
+
+        // Add the returned counts to the total values.
+        *pcbNeeded += cbNeeded;
+        *pcReturned += dwReturned;
+
+        // Reduce the available buffer size for the next call without risking an underflow.
+        if (cbNeeded < cbCallBuffer)
+            cbCallBuffer -= cbNeeded;
+        else
+            cbCallBuffer = 0;
+
+        // Advance the buffer if the caller provided it.
+        if (pCallBuffer)
+            pCallBuffer += cbNeeded;
+    }
+
+    return bReturnValue;
+}
diff --git a/reactos/win32ss/printing/providers/localspl/precomp.h b/reactos/win32ss/printing/providers/localspl/precomp.h
new file mode 100644 (file)
index 0000000..c872853
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Precompiled Header for all source files
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#ifndef _PRECOMP_H
+#define _PRECOMP_H
+
+#define WIN32_NO_STATUS
+#include <limits.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#include <lmcons.h>
+#include <rpc.h>
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winreg.h>
+#include <winspool.h>
+#include <winsplp.h>
+#include <ndk/rtlfuncs.h>
+
+#define SKIPLIST_LEVELS 16
+#include <skiplist.h>
+#include <spoolss.h>
+
+#include <wine/debug.h>
+WINE_DEFAULT_DEBUG_CHANNEL(localspl);
+
+// Macros
+#define IS_VALID_JOB_ID(ID)     (ID >= 1 && ID <= 99999)
+#define IS_VALID_PRIORITY(P)    (P >= MIN_PRIORITY && P <= MAX_PRIORITY)
+
+// Constants
+#define MAX_PRINTER_NAME        220
+#define SHD_WIN2003_SIGNATURE   0x4968
+
+// Function pointers
+typedef BOOL (WINAPI *PClosePrintProcessor)(HANDLE);
+typedef BOOL (WINAPI *PControlPrintProcessor)(HANDLE, DWORD);
+typedef BOOL (WINAPI *PEnumPrintProcessorDatatypesW)(LPWSTR, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD);
+typedef DWORD (WINAPI *PGetPrintProcessorCapabilities)(LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD);
+typedef HANDLE (WINAPI *POpenPrintProcessor)(LPWSTR, PPRINTPROCESSOROPENDATA);
+typedef BOOL (WINAPI *PPrintDocumentOnPrintProcessor)(HANDLE, LPWSTR);
+typedef LPMONITOREX(WINAPI *PInitializePrintMonitor)(PWSTR);
+typedef LPMONITOR2(WINAPI *PInitializePrintMonitor2)(PMONITORINIT, PHANDLE);
+
+// Forward declarations
+typedef struct _LOCAL_HANDLE            LOCAL_HANDLE, *PLOCAL_HANDLE;
+typedef struct _LOCAL_JOB               LOCAL_JOB, *PLOCAL_JOB;
+typedef struct _LOCAL_PORT              LOCAL_PORT, *PLOCAL_PORT;
+typedef struct _LOCAL_PORT_HANDLE       LOCAL_PORT_HANDLE, *PLOCAL_PORT_HANDLE;
+typedef struct _LOCAL_PRINT_MONITOR     LOCAL_PRINT_MONITOR, *PLOCAL_PRINT_MONITOR;
+typedef struct _LOCAL_PRINT_PROCESSOR   LOCAL_PRINT_PROCESSOR, *PLOCAL_PRINT_PROCESSOR;
+typedef struct _LOCAL_PRINTER           LOCAL_PRINTER, *PLOCAL_PRINTER;
+typedef struct _LOCAL_PRINTER_HANDLE    LOCAL_PRINTER_HANDLE, *PLOCAL_PRINTER_HANDLE;
+typedef struct _LOCAL_XCV_HANDLE        LOCAL_XCV_HANDLE, *PLOCAL_XCV_HANDLE;
+typedef struct _SHD_HEADER              SHD_HEADER, *PSHD_HEADER;
+
+// Structures
+/**
+ * Describes a Print Monitor.
+ */
+struct _LOCAL_PRINT_MONITOR
+{
+    LIST_ENTRY Entry;
+    PWSTR pwszName;                             /** Name of the Print Monitor as read from the registry. */
+    PWSTR pwszFileName;                         /** DLL File Name of the Print Monitor. */
+    BOOL bIsLevel2;                             /** Whether this Print Monitor supplies an InitializePrintMonitor2 API (preferred) instead of InitializePrintMonitor. */
+    PVOID pMonitor;                             /** For bIsLevel2 == TRUE:  LPMONITOR2 pointer returned by InitializePrintMonitor2.
+                                                    For bIsLevel2 == FALSE: LPMONITOREX pointer returned by InitializePrintMonitor. */
+    HANDLE hMonitor;                            /** Only used when bIsLevel2 == TRUE: Handle returned by InitializePrintMonitor2. */
+};
+
+/**
+ * Describes a Port handled by a Print Monitor.
+ */
+struct _LOCAL_PORT
+{
+    LIST_ENTRY Entry;
+    PWSTR pwszName;                             /** The name of the port (including the trailing colon). */
+    PLOCAL_PRINT_MONITOR pPrintMonitor;         /** The Print Monitor handling this port. */
+    PLOCAL_JOB pNextJobToProcess;               /** The Print Job that will be processed by the next created Port handle. */
+};
+
+/**
+ * Describes a Print Processor.
+ */
+struct _LOCAL_PRINT_PROCESSOR
+{
+    LIST_ENTRY Entry;
+    PWSTR pwszName;
+    PDATATYPES_INFO_1W pDatatypesInfo1;
+    DWORD dwDatatypeCount;
+    PClosePrintProcessor pfnClosePrintProcessor;
+    PControlPrintProcessor pfnControlPrintProcessor;
+    PEnumPrintProcessorDatatypesW pfnEnumPrintProcessorDatatypesW;
+    PGetPrintProcessorCapabilities pfnGetPrintProcessorCapabilities;
+    POpenPrintProcessor pfnOpenPrintProcessor;
+    PPrintDocumentOnPrintProcessor pfnPrintDocumentOnPrintProcessor;
+};
+
+/**
+ * Describes a printer and manages its print jobs.
+ * Created once for every printer at startup.
+ */
+struct _LOCAL_PRINTER
+{
+    // This sort key must be the first element for LookupElementSkiplist to work!
+    PWSTR pwszPrinterName;
+
+    DWORD dwAttributes;
+    DWORD dwStatus;
+    PWSTR pwszPrinterDriver;
+    PWSTR pwszDescription;
+    PWSTR pwszDefaultDatatype;
+    PDEVMODEW pDefaultDevMode;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+    PLOCAL_PORT pPort;
+    SKIPLIST JobList;
+};
+
+/**
+ * Describes an entire print job associated to a specific printer through the Printer member.
+ * Created with every valid call to LocalStartDocPrinter.
+ */
+struct _LOCAL_JOB
+{
+    // This sort key must be the first element for LookupElementSkiplist to work!
+    DWORD dwJobID;                              /** Internal and external ID of this Job */
+
+    BOOL bAddedJob : 1;                         /** Whether AddJob has already been called on this Job. */
+    HANDLE hPrintProcessor;                     /** Handle returned by OpenPrintProcessor while the Job is printing. */
+    PLOCAL_PRINTER pPrinter;                    /** Associated Printer to this Job */
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;     /** Associated Print Processor to this Job */
+    DWORD dwPriority;                           /** Priority of this Job from MIN_PRIORITY to MAX_PRIORITY, default being DEF_PRIORITY */
+    SYSTEMTIME stSubmitted;                     /** Time of the submission of this Job */
+    PWSTR pwszUserName;                         /** Optional; User that submitted the Job */
+    PWSTR pwszNotifyName;                       /** Optional; User that shall be notified about the status of the Job */
+    PWSTR pwszDocumentName;                     /** Optional; Name of the Document that is printed */
+    PWSTR pwszDatatype;                         /** Datatype of the Document */
+    PWSTR pwszOutputFile;                       /** Output File to spool the Job to */
+    PWSTR pwszPrintProcessorParameters;         /** Optional; Parameters for the chosen Print Processor */
+    PWSTR pwszStatus;                           /** Optional; a Status Message for the Job */
+    DWORD dwTotalPages;                         /** Total pages of the Document */
+    DWORD dwPagesPrinted;                       /** Number of pages that have already been printed */
+    DWORD dwStartTime;                          /** Earliest time in minutes since 12:00 AM UTC when this document can be printed */
+    DWORD dwUntilTime;                          /** Latest time in minutes since 12:00 AM UTC when this document can be printed */
+    DWORD dwStatus;                             /** JOB_STATUS_* flags of the Job */
+    PWSTR pwszMachineName;                      /** Name of the machine that submitted the Job (prepended with two backslashes) */
+    PDEVMODEW pDevMode;                         /** Associated Device Mode to this Job */
+};
+
+/**
+ * Specific handle returned by LocalOpenPrinter for every valid call that opens a Printer or Print Job.
+ */
+struct _LOCAL_PRINTER_HANDLE
+{
+    BOOL bStartedDoc : 1;                       /** Whether StartDocPrinter has already been called. */
+    HANDLE hSPLFile;                            /** Handle to an opened SPL file for Printer Job handles. */
+    PLOCAL_PRINTER pPrinter;                    /** Printer associated with this handle. */
+    PLOCAL_JOB pJob;                            /** Print Job associated with this handle. This can be the specified Job if this is a Print Job handle or the started job through LocalStartDocPrinter. */
+    PWSTR pwszDatatype;                         /** Datatype used for newly started jobs. */
+    PDEVMODEW pDevMode;                         /** DevMode used for newly started jobs. */
+};
+
+/**
+ * Specific handle returned by LocalOpenPrinter for every valid call that opens a Port.
+ */
+struct _LOCAL_PORT_HANDLE
+{
+    HANDLE hPort;                               /** Handle returned by pfnOpenPort. */
+    PLOCAL_PORT pPort;                          /** Port associated with this handle. */
+};
+
+/**
+ * Specific handle returned by LocalOpenPrinter for every valid call that opens an XcvMonitor or XcvPort.
+ */
+struct _LOCAL_XCV_HANDLE
+{
+    HANDLE hXcv;                                /** Handle returned by pfnXcvOpenPort. */
+    PLOCAL_PRINT_MONITOR pPrintMonitor;         /** Print Monitor associated with this handle. */
+};
+
+/**
+ * Describes a handle returned by LocalOpenPrinter.
+ * Suitable for all things that can be opened through LocalOpenPrinter.
+ */
+struct _LOCAL_HANDLE
+{
+    enum {
+        HandleType_Port,                        /** pSpecificHandle is a PLOCAL_PORT_HANDLE. */
+        HandleType_Printer,                     /** pSpecificHandle is a PLOCAL_PRINTER_HANDLE. */
+        HandleType_Xcv                          /** pSpecificHandle is a PLOCAL_XCV_HANDLE. */
+    }
+    HandleType;
+    PVOID pSpecificHandle;
+};
+
+/**
+ * Describes the header of a print job serialized into a shadow file (.SHD)
+ * Documented in http://www.undocprint.org/formats/winspool/shd
+ * Compatible with Windows Server 2003
+ */
+struct _SHD_HEADER
+{
+    DWORD dwSignature;
+    DWORD cbHeader;
+    WORD wStatus;
+    WORD wUnknown1;
+    DWORD dwJobID;
+    DWORD dwPriority;
+    DWORD offUserName;
+    DWORD offNotifyName;
+    DWORD offDocumentName;
+    DWORD offPort;
+    DWORD offPrinterName;
+    DWORD offDriverName;
+    DWORD offDevMode;
+    DWORD offPrintProcessor;
+    DWORD offDatatype;
+    DWORD offPrintProcessorParameters;
+    SYSTEMTIME stSubmitted;
+    DWORD dwStartTime;
+    DWORD dwUntilTime;
+    DWORD dwUnknown6;
+    DWORD dwTotalPages;
+    DWORD cbSecurityDescriptor;
+    DWORD offSecurityDescriptor;
+    DWORD dwUnknown3;
+    DWORD dwUnknown4;
+    DWORD dwUnknown5;
+    DWORD offMachineName;
+    DWORD dwSPLSize;
+};
+
+// jobs.c
+extern SKIPLIST GlobalJobList;
+DWORD WINAPI CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle);
+void FreeJob(PLOCAL_JOB pJob);
+DWORD GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput);
+BOOL InitializeGlobalJobList();
+void InitializePrinterJobList(PLOCAL_PRINTER pPrinter);
+BOOL WINAPI LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded);
+BOOL WINAPI LocalEnumJobs(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
+BOOL WINAPI LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded);
+BOOL WINAPI LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID);
+BOOL WINAPI LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command);
+PLOCAL_JOB ReadJobShadowFile(PCWSTR pwszFilePath);
+BOOL WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob);
+
+// main.c
+extern const WCHAR wszCurrentEnvironment[];
+extern const DWORD cbCurrentEnvironment;
+extern const WCHAR wszDefaultDocumentName[];
+extern const WCHAR* wszPrintProviderInfo[3];
+extern WCHAR wszSpoolDirectory[MAX_PATH];
+extern DWORD cchSpoolDirectory;
+
+// monitors.c
+extern LIST_ENTRY PrintMonitorList;
+PLOCAL_PRINT_MONITOR FindPrintMonitor(PCWSTR pwszName);
+BOOL InitializePrintMonitorList();
+BOOL WINAPI LocalEnumMonitors(PWSTR pName, DWORD Level, PBYTE pMonitors, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned);
+
+// ports.c
+PLOCAL_PORT FindPort(PCWSTR pwszName);
+BOOL InitializePortList();
+BOOL WINAPI LocalEnumPorts(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned);
+
+// printers.c
+extern SKIPLIST PrinterList;
+BOOL InitializePrinterList();
+BOOL WINAPI LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
+BOOL WINAPI LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault);
+BOOL WINAPI LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead);
+DWORD WINAPI LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo);
+BOOL WINAPI LocalStartPagePrinter(HANDLE hPrinter);
+BOOL WINAPI LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten);
+BOOL WINAPI LocalEndPagePrinter(HANDLE hPrinter);
+BOOL WINAPI LocalEndDocPrinter(HANDLE hPrinter);
+BOOL WINAPI LocalClosePrinter(HANDLE hPrinter);
+
+// printingthread.c
+DWORD WINAPI PrintingThreadProc(PLOCAL_JOB pJob);
+
+// printprocessors.c
+BOOL FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype);
+PLOCAL_PRINT_PROCESSOR FindPrintProcessor(PCWSTR pwszName);
+BOOL InitializePrintProcessorList();
+BOOL WINAPI LocalEnumPrintProcessorDatatypes(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
+BOOL WINAPI LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
+BOOL WINAPI LocalGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded);
+
+// tools.c
+PWSTR AllocAndRegQueryWSZ(HKEY hKey, PCWSTR pwszValueName);
+PDEVMODEW DuplicateDevMode(PDEVMODEW pInput);
+
+#endif
diff --git a/reactos/win32ss/printing/providers/localspl/printers.c b/reactos/win32ss/printing/providers/localspl/printers.c
new file mode 100644 (file)
index 0000000..f7f9410
--- /dev/null
@@ -0,0 +1,1303 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printers and printing
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+// Global Variables
+SKIPLIST PrinterList;
+
+
+/**
+ * @name _PrinterListCompareRoutine
+ *
+ * SKIPLIST_COMPARE_ROUTINE for the Printer List.
+ * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers.
+ */
+static int WINAPI
+_PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
+{
+    PLOCAL_PRINTER A = (PLOCAL_PRINTER)FirstStruct;
+    PLOCAL_PRINTER B = (PLOCAL_PRINTER)SecondStruct;
+
+    return wcsicmp(A->pwszPrinterName, B->pwszPrinterName);
+}
+
+/**
+ * @name InitializePrinterList
+ *
+ * Initializes a list of locally available Printers.
+ * The list is searchable by name and returns information about the printers, including their job queues.
+ * During this process, the job queues are also initialized.
+ */
+BOOL
+InitializePrinterList()
+{
+    const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
+
+    DWORD cbData;
+    DWORD cchPrinterName;
+    DWORD dwErrorCode;
+    DWORD dwSubKeys;
+    DWORD i;
+    HKEY hKey = NULL;
+    HKEY hSubKey = NULL;
+    PLOCAL_PORT pPort;
+    PLOCAL_PRINTER pPrinter = NULL;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+    PWSTR pwszPort = NULL;
+    PWSTR pwszPrintProcessor = NULL;
+    WCHAR wszPrinterName[MAX_PRINTER_NAME + 1];
+
+    // Initialize an empty list for our printers.
+    InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
+
+    // Open our printers registry key. Each subkey is a local printer there.
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Get the number of subkeys.
+    dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Loop through all available local printers.
+    for (i = 0; i < dwSubKeys; i++)
+    {
+        // Cleanup tasks from the previous run
+        if (hSubKey)
+        {
+            RegCloseKey(hSubKey);
+            hSubKey = NULL;
+        }
+
+        if (pPrinter)
+        {
+            if (pPrinter->pDefaultDevMode)
+                DllFreeSplMem(pPrinter->pDefaultDevMode);
+
+            if (pPrinter->pwszDefaultDatatype)
+                DllFreeSplStr(pPrinter->pwszDefaultDatatype);
+
+            if (pPrinter->pwszDescription)
+                DllFreeSplStr(pPrinter->pwszDescription);
+
+            if (pPrinter->pwszPrinterDriver)
+                DllFreeSplStr(pPrinter->pwszPrinterDriver);
+
+            if (pPrinter->pwszPrinterName)
+                DllFreeSplStr(pPrinter->pwszPrinterName);
+
+            DllFreeSplMem(pPrinter);
+            pPrinter = NULL;
+        }
+
+        if (pwszPrintProcessor)
+        {
+            DllFreeSplStr(pwszPrintProcessor);
+            pwszPrintProcessor = NULL;
+        }
+
+        // Get the name of this printer.
+        cchPrinterName = _countof(wszPrinterName);
+        dwErrorCode = (DWORD)RegEnumKeyExW(hKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL);
+        if (dwErrorCode == ERROR_MORE_DATA)
+        {
+            // This printer name exceeds the maximum length and is invalid.
+            continue;
+        }
+        else if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
+            continue;
+        }
+
+        // Open this Printer's registry key.
+        dwErrorCode = (DWORD)RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName, dwErrorCode);
+            continue;
+        }
+
+        // Get the Print Processor.
+        pwszPrintProcessor = AllocAndRegQueryWSZ(hSubKey, L"Print Processor");
+        if (!pwszPrintProcessor)
+            continue;
+
+        // Try to find it in the Print Processor List.
+        pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
+        if (!pPrintProcessor)
+        {
+            ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName);
+            continue;
+        }
+
+        // Get the Port.
+        pwszPort = AllocAndRegQueryWSZ(hSubKey, L"Port");
+        if (!pwszPort)
+            continue;
+
+        // Try to find it in the Port List.
+        pPort = FindPort(pwszPort);
+        if (!pPort)
+        {
+            ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort, wszPrinterName);
+            continue;
+        }
+
+        // Create a new LOCAL_PRINTER structure for it.
+        pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER));
+        if (!pPrinter)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName);
+        pPrinter->pPrintProcessor = pPrintProcessor;
+        pPrinter->pPort = pPort;
+        InitializePrinterJobList(pPrinter);
+
+        // Get the printer driver.
+        pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
+        if (!pPrinter->pwszPrinterDriver)
+            continue;
+
+        // Get the description.
+        pPrinter->pwszDescription = AllocAndRegQueryWSZ(hSubKey, L"Description");
+        if (!pPrinter->pwszDescription)
+            continue;
+
+        // Get the default datatype.
+        pPrinter->pwszDefaultDatatype = AllocAndRegQueryWSZ(hSubKey, L"Datatype");
+        if (!pPrinter->pwszDefaultDatatype)
+            continue;
+
+        // Verify that it's valid.
+        if (!FindDatatype(pPrintProcessor, pPrinter->pwszDefaultDatatype))
+        {
+            ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName);
+            continue;
+        }
+
+        // Determine the size of the DevMode.
+        dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, NULL, &cbData);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
+            continue;
+        }
+
+        // Allocate enough memory for the DevMode.
+        pPrinter->pDefaultDevMode = DllAllocSplMem(cbData);
+        if (!pPrinter->pDefaultDevMode)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        // Get the default DevMode.
+        dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)pPrinter->pDefaultDevMode, &cbData);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
+            continue;
+        }
+
+        // Get the Attributes.
+        cbData = sizeof(DWORD);
+        dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Attributes", NULL, NULL, (PBYTE)&pPrinter->dwAttributes, &cbData);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
+            continue;
+        }
+
+        // Get the Status.
+        cbData = sizeof(DWORD);
+        dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Status", NULL, NULL, (PBYTE)&pPrinter->dwStatus, &cbData);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
+            continue;
+        }
+
+        // Add this printer to the printer list.
+        if (!InsertElementSkiplist(&PrinterList, pPrinter))
+        {
+            ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter->pwszPrinterName);
+            goto Cleanup;
+        }
+
+        // Don't let the cleanup routines free this.
+        pPrinter = NULL;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    // Inside the loop
+    if (hSubKey)
+        RegCloseKey(hSubKey);
+
+    if (pPrinter)
+    {
+        if (pPrinter->pDefaultDevMode)
+            DllFreeSplMem(pPrinter->pDefaultDevMode);
+
+        if (pPrinter->pwszDefaultDatatype)
+            DllFreeSplStr(pPrinter->pwszDefaultDatatype);
+
+        if (pPrinter->pwszDescription)
+            DllFreeSplStr(pPrinter->pwszDescription);
+
+        if (pPrinter->pwszPrinterDriver)
+            DllFreeSplStr(pPrinter->pwszPrinterDriver);
+
+        if (pPrinter->pwszPrinterName)
+            DllFreeSplStr(pPrinter->pwszPrinterName);
+
+        DllFreeSplMem(pPrinter);
+    }
+
+    if (pwszPrintProcessor)
+        DllFreeSplStr(pwszPrintProcessor);
+
+    // Outside the loop
+    if (hKey)
+        RegCloseKey(hKey);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+
+DWORD
+_LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
+{
+    const WCHAR wszComma[] = L",";
+
+    DWORD cbName;
+    DWORD cbComment;
+    DWORD cbDescription;
+    DWORD cchComputerName = 0;
+    DWORD dwErrorCode;
+    DWORD i;
+    PBYTE pPrinterInfo;
+    PBYTE pPrinterString;
+    PSKIPLIST_NODE pNode;
+    PLOCAL_PRINTER pPrinter;
+    PRINTER_INFO_1W PrinterInfo1;
+    WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1];
+
+    DWORD dwOffsets[] = {
+        FIELD_OFFSET(PRINTER_INFO_1W, pName),
+        FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
+        FIELD_OFFSET(PRINTER_INFO_1W, pComment),
+        MAXDWORD
+    };
+
+    if (Flags & PRINTER_ENUM_NAME)
+    {
+        if (Name)
+        {
+            // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
+            // Only process what's directed at us and dismiss every other request with ERROR_INVALID_NAME.
+            if (Name[0] == L'\\' && Name[1] == L'\\')
+            {
+                // Prepend slashes to the computer name.
+                wszComputerName[0] = L'\\';
+                wszComputerName[1] = L'\\';
+
+                // Get the local computer name for comparison.
+                cchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
+                if (!GetComputerNameW(&wszComputerName[2], &cchComputerName))
+                {
+                    dwErrorCode = GetLastError();
+                    ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
+                    goto Cleanup;
+                }
+
+                // Add the leading slashes to the total length.
+                cchComputerName += 2;
+
+                // Now compare this with the local computer name and reject if it doesn't match.
+                if (wcsicmp(&Name[2], &wszComputerName[2]) != 0)
+                {
+                    dwErrorCode = ERROR_INVALID_NAME;
+                    goto Cleanup;
+                }
+
+                // Add a trailing backslash to wszComputerName, which will later be prepended in front of the printer names.
+                wszComputerName[cchComputerName++] = L'\\';
+                wszComputerName[cchComputerName] = 0;
+            }
+            else if (wcsicmp(Name, wszPrintProviderInfo[0]) != 0)
+            {
+                // The user supplied a name that cannot be processed by the local print provider.
+                dwErrorCode = ERROR_INVALID_NAME;
+                goto Cleanup;
+            }
+        }
+        else
+        {
+            // The caller wants information about this Print Provider.
+            // spoolss packs this into an array of information about all Print Providers.
+            *pcbNeeded = sizeof(PRINTER_INFO_1W);
+
+            for (i = 0; i < 3; i++)
+                *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
+
+            // Check if the supplied buffer is large enough.
+            if (cbBuf < *pcbNeeded)
+            {
+                dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+                goto Cleanup;
+            }
+
+            // Copy over the print processor information.
+            ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
+            PackStrings(wszPrintProviderInfo, pPrinterEnum, dwOffsets, &pPrinterEnum[*pcbNeeded]);
+            *pcReturned = 1;
+            dwErrorCode = ERROR_SUCCESS;
+            goto Cleanup;
+        }
+    }
+
+    // Count the required buffer size and the number of printers.
+    i = 0;
+
+    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
+    {
+        pPrinter = (PLOCAL_PRINTER)pNode->Element;
+
+        // This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
+        // But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
+        // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
+        cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+        cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
+        cbDescription = cchComputerName * sizeof(WCHAR) + cbName + cbComment + sizeof(WCHAR);
+
+        *pcbNeeded += sizeof(PRINTER_INFO_1W) + cchComputerName * sizeof(WCHAR) + cbName + cbComment + cbDescription;
+        i++;
+    }
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Put the strings right after the last PRINTER_INFO_1W structure.
+    // Due to all the required string processing, we can't just use PackStrings here :(
+    pPrinterInfo = pPrinterEnum;
+    pPrinterString = pPrinterEnum + i * sizeof(PRINTER_INFO_1W);
+
+    // Copy over the printer information.
+    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
+    {
+        pPrinter = (PLOCAL_PRINTER)pNode->Element;
+
+        // FIXME: As for now, the Flags member returns no information.
+        PrinterInfo1.Flags = 0;
+
+        // Copy the printer name.
+        PrinterInfo1.pName = (PWSTR)pPrinterString;
+        CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
+        pPrinterString += cchComputerName * sizeof(WCHAR);
+        cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+        CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName);
+        pPrinterString += cbName;
+
+        // Copy the printer comment (equals the "Description" registry value).
+        PrinterInfo1.pComment = (PWSTR)pPrinterString;
+        cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
+        CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment);
+        pPrinterString += cbComment;
+
+        // Copy the description, which for PRINTER_INFO_1W has the form "Name,Comment,"
+        PrinterInfo1.pDescription = (PWSTR)pPrinterString;
+        CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
+        pPrinterString += cchComputerName * sizeof(WCHAR);
+        CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName - sizeof(WCHAR));
+        pPrinterString += cbName - sizeof(WCHAR);
+        CopyMemory(pPrinterString, wszComma, sizeof(WCHAR));
+        pPrinterString += sizeof(WCHAR);
+        CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment - sizeof(WCHAR));
+        pPrinterString += cbComment - sizeof(WCHAR);
+        CopyMemory(pPrinterString, wszComma, sizeof(wszComma));
+        pPrinterString += sizeof(wszComma);
+                
+        // Finally copy the structure and advance to the next one in the output buffer.
+        CopyMemory(pPrinterInfo, &PrinterInfo1, sizeof(PRINTER_INFO_1W));
+        pPrinterInfo += sizeof(PRINTER_INFO_1W);
+    }
+
+    *pcReturned = i;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    return dwErrorCode;
+}
+
+BOOL WINAPI
+LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
+{
+    DWORD dwErrorCode;
+
+    // Do no sanity checks here. This is verified by localspl_apitest!
+
+    // Begin counting.
+    *pcbNeeded = 0;
+    *pcReturned = 0;
+
+    // Think positive :)
+    // Treat it as success if the caller queried no information and we don't need to return any.
+    dwErrorCode = ERROR_SUCCESS;
+
+    if (Flags & PRINTER_ENUM_LOCAL)
+    {
+        // The function behaves quite differently for each level.
+        if (Level == 1)
+        {
+            dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+        }
+        else
+        {
+            // TODO: Handle other levels.
+            // The caller supplied an invalid level.
+            dwErrorCode = ERROR_INVALID_LEVEL;
+            goto Cleanup;
+        }
+    }
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
+{
+    BOOL bReturnValue;
+    DWORD cchComputerName;
+    DWORD cchFirstParameter;
+    DWORD dwErrorCode;
+    DWORD dwJobID;
+    HANDLE hExternalHandle;
+    PWSTR p = lpPrinterName;
+    PWSTR pwszFirstParameter = NULL;
+    PWSTR pwszSecondParameter = NULL;
+    PLOCAL_JOB pJob;
+    PLOCAL_HANDLE pHandle = NULL;
+    PLOCAL_PORT pPort;
+    PLOCAL_PORT_HANDLE pPortHandle = NULL;
+    PLOCAL_PRINT_MONITOR pPrintMonitor;
+    PLOCAL_PRINTER pPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
+    PLOCAL_XCV_HANDLE pXcvHandle = NULL;
+    WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+    WCHAR wszFullPath[MAX_PATH];
+
+    // TODO: lpPrinterName == NULL is supported and means access to the local printer server.
+    // Not sure yet if that is passed down to localspl.dll or processed in advance.
+
+    // Sanity checks
+    if (!lpPrinterName || !phPrinter)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    *phPrinter = NULL;
+
+    // Skip any server name in the first parameter.
+    // Does lpPrinterName begin with two backslashes to indicate a server name?
+    if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
+    {
+        // Skip these two backslashes.
+        lpPrinterName += 2;
+
+        // Look for the closing backslash.
+        p = wcschr(lpPrinterName, L'\\');
+        if (!p)
+        {
+            // We didn't get a proper server name.
+            dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+            goto Cleanup;
+        }
+
+        // Get the local computer name for comparison.
+        cchComputerName = _countof(wszComputerName);
+        if (!GetComputerNameW(wszComputerName, &cchComputerName))
+        {
+            dwErrorCode = GetLastError();
+            ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+
+        // Now compare this string excerpt with the local computer name.
+        // The input parameter may not be writable, so we can't null-terminate the input string at this point.
+        // This print provider only supports local printers, so both strings have to match.
+        if (p - lpPrinterName != cchComputerName || _wcsnicmp(lpPrinterName, wszComputerName, cchComputerName) != 0)
+        {
+            dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+            goto Cleanup;
+        }
+
+        // We have checked the server name and don't need it anymore.
+        lpPrinterName = p + 1;
+    }
+
+    // Look for a comma. If it exists, it indicates the end of the first parameter.
+    pwszSecondParameter = wcschr(lpPrinterName, L',');
+    if (pwszSecondParameter)
+        cchFirstParameter = pwszSecondParameter - p;
+    else
+        cchFirstParameter = wcslen(lpPrinterName);
+
+    // We must have at least one parameter.
+    if (!cchFirstParameter && !pwszSecondParameter)
+    {
+        dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+        goto Cleanup;
+    }
+
+    // Do we have a first parameter?
+    if (cchFirstParameter)
+    {
+        // Yes, extract it.
+        // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
+        pwszFirstParameter = DllAllocSplMem((cchFirstParameter + 1) * sizeof(WCHAR));
+        CopyMemory(pwszFirstParameter, lpPrinterName, cchFirstParameter * sizeof(WCHAR));
+    }
+
+    // Do we have a second parameter?
+    if (pwszSecondParameter)
+    {
+        // Yes, skip the comma at the beginning.
+        ++pwszSecondParameter;
+
+        // Skip whitespace as well.
+        while (*pwszSecondParameter == L' ')
+            ++pwszSecondParameter;
+    }
+
+    // Create a new handle.
+    pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Now we can finally check the type of handle actually requested.
+    if (pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Port", 4) == 0)
+    {
+        // The caller wants a port handle and provided a string like:
+        //    "LPT1:, Port"
+        //    "\\COMPUTERNAME\LPT1:, Port"
+        
+        // Look for this port in our Print Monitor Port list.
+        pPort = FindPort(pwszFirstParameter);
+        if (!pPort)
+        {
+            // The supplied port is unknown to all our Print Monitors.
+            dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+            goto Cleanup;
+        }
+
+        pPrintMonitor = pPort->pPrintMonitor;
+
+        // Call the monitor's OpenPort function.
+        if (pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszFirstParameter, &hExternalHandle);
+        else
+            bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszFirstParameter, &hExternalHandle);
+
+        if (!bReturnValue)
+        {
+            // The OpenPort function failed. Return its last error.
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // Create a new LOCAL_PORT_HANDLE.
+        pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE));
+        if (!pPortHandle)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        pPortHandle->hPort = hExternalHandle;
+        pPortHandle->pPort = pPort;
+
+        // Return the Port handle through our general handle.
+        pHandle->HandleType = HandleType_Port;
+        pHandle->pSpecificHandle = pPortHandle;
+    }
+    else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0)
+    {
+        // The caller wants an Xcv handle and provided a string like:
+        //    ", XcvMonitor Local Port"
+        //    "\\COMPUTERNAME\, XcvMonitor Local Port"
+        //    ", XcvPort LPT1:"
+        //    "\\COMPUTERNAME\, XcvPort LPT1:"
+
+        // Skip the "Xcv" string.
+        pwszSecondParameter += 3;
+
+        // Is XcvMonitor or XcvPort requested?
+        if (wcsncmp(pwszSecondParameter, L"Monitor ", 8) == 0)
+        {
+            // Skip the "Monitor " string.
+            pwszSecondParameter += 8;
+
+            // Look for this monitor in our Print Monitor list.
+            pPrintMonitor = FindPrintMonitor(pwszSecondParameter);
+            if (!pPrintMonitor)
+            {
+                // The caller supplied a non-existing Monitor name.
+                dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+                goto Cleanup;
+            }
+        }
+        else if (wcsncmp(pwszSecondParameter, L"Port ", 5) == 0)
+        {
+            // Skip the "Port " string.
+            pwszSecondParameter += 5;
+
+            // Look for this port in our Print Monitor Port list.
+            pPort = FindPort(pwszFirstParameter);
+            if (!pPort)
+            {
+                // The supplied port is unknown to all our Print Monitors.
+                dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+                goto Cleanup;
+            }
+
+            pPrintMonitor = pPort->pPrintMonitor;
+        }
+        else
+        {
+            dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+            goto Cleanup;
+        }
+
+        // Call the monitor's XcvOpenPort function.
+        if (pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle);
+        else
+            bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle);
+
+        if (!bReturnValue)
+        {
+            // The XcvOpenPort function failed. Return its last error.
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // Create a new LOCAL_XCV_HANDLE.
+        pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE));
+        if (!pXcvHandle)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        pXcvHandle->hXcv = hExternalHandle;
+        pXcvHandle->pPrintMonitor = pPrintMonitor;
+
+        // Return the Xcv handle through our general handle.
+        pHandle->HandleType = HandleType_Xcv;
+        pHandle->pSpecificHandle = pXcvHandle;
+    }
+    else
+    {
+        // The caller wants a Printer or Printer Job handle and provided a string like:
+        //    "HP DeskJet"
+        //    "\\COMPUTERNAME\HP DeskJet"
+        //    "HP DeskJet, Job 5"
+        //    "\\COMPUTERNAME\HP DeskJet, Job 5"
+
+        // Retrieve the printer from the list.
+        pPrinter = LookupElementSkiplist(&PrinterList, &pwszFirstParameter, NULL);
+        if (!pPrinter)
+        {
+            // The printer does not exist.
+            dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+            goto Cleanup;
+        }
+
+        // Create a new LOCAL_PRINTER_HANDLE.
+        pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
+        if (!pPrinterHandle)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE;
+        pPrinterHandle->pPrinter = pPrinter;
+
+        // Check if a datatype was given.
+        if (pDefault && pDefault->pDatatype)
+        {
+            // Use the datatype if it's valid.
+            if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
+            {
+                dwErrorCode = ERROR_INVALID_DATATYPE;
+                goto Cleanup;
+            }
+
+            pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
+        }
+        else
+        {
+            // Use the default datatype.
+            pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
+        }
+
+        // Check if a DevMode was given, otherwise use the default.
+        if (pDefault && pDefault->pDevMode)
+            pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode);
+        else
+            pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
+
+        // Check if the caller wants a handle to an existing Print Job.
+        if (pwszSecondParameter)
+        {
+            // The "Job " string has to follow now.
+            if (wcsncmp(pwszSecondParameter, L"Job ", 4) != 0)
+            {
+                dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+                goto Cleanup;
+            }
+
+            // Skip the "Job " string. 
+            pwszSecondParameter += 4;
+
+            // Skip even more whitespace.
+            while (*pwszSecondParameter == ' ')
+                ++pwszSecondParameter;
+
+            // Finally extract the desired Job ID.
+            dwJobID = wcstoul(pwszSecondParameter, NULL, 10);
+            if (!IS_VALID_JOB_ID(dwJobID))
+            {
+                // The user supplied an invalid Job ID.
+                dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+                goto Cleanup;
+            }
+
+            // Look for this job in the Global Job List.
+            pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
+            if (!pJob || pJob->pPrinter != pPrinter)
+            {
+                // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
+                dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+                goto Cleanup;
+            }
+
+            // Try to open its SPL file.
+            GetJobFilePath(L"SPL", dwJobID, wszFullPath);
+            pPrinterHandle->hSPLFile = CreateFileW(wszFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+            if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
+            {
+                dwErrorCode = GetLastError();
+                ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode, wszFullPath);
+                goto Cleanup;
+            }
+
+            // Associate the job to our Printer Handle, but don't set bStartedDoc.
+            // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
+            pPrinterHandle->pJob = pJob;
+        }
+
+        // Return the Printer handle through our general handle.
+        pHandle->HandleType = HandleType_Printer;
+        pHandle->pSpecificHandle = pPrinterHandle;
+    }
+
+    // We were successful! Return the handle.
+    *phPrinter = (HANDLE)pHandle;
+    dwErrorCode = ERROR_SUCCESS;
+
+    // Don't let the cleanup routines free this.
+    pHandle = NULL;
+    pPrinterHandle = NULL;
+
+Cleanup:
+    if (pHandle)
+        DllFreeSplMem(pHandle);
+
+    if (pPrinterHandle)
+    {
+        if (pPrinterHandle->pwszDatatype)
+            DllFreeSplStr(pPrinterHandle->pwszDatatype);
+
+        if (pPrinterHandle->pDevMode)
+            DllFreeSplMem(pPrinterHandle->pDevMode);
+
+        DllFreeSplMem(pPrinterHandle);
+    }
+
+    if (pwszFirstParameter)
+        DllFreeSplMem(pwszFirstParameter);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
+{
+    BOOL bReturnValue;
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Port handles are an entirely different thing.
+    if (pHandle->HandleType == HandleType_Port)
+    {
+        pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
+
+        // Call the monitor's ReadPort function.
+        if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
+        else
+            bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
+
+        if (!bReturnValue)
+        {
+            // The ReadPort function failed. Return its last error.
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // We were successful!
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+
+    // The remaining function deals with Printer handles only.
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // ReadPrinter needs an opened SPL file to work.
+    // This only works if a Printer Job Handle was requested in OpenPrinter.
+    if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Pass the parameters to ReadFile.
+    if (!ReadFile(pPrinterHandle->hSPLFile, pBuf, cbBuf, pNoBytesRead, NULL))
+    {
+        dwErrorCode = GetLastError();
+        ERR("ReadFile failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+DWORD WINAPI
+LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
+{
+    BOOL bReturnValue;
+    DWORD dwErrorCode;
+    DWORD dwReturnValue = 0;
+    PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
+    PLOCAL_JOB pJob;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Port handles are an entirely different thing.
+    if (pHandle->HandleType == HandleType_Port)
+    {
+        pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
+
+        // This call should come from a Print Processor and the job this port is going to print was assigned to us before opening the Print Processor.
+        // Claim it exclusively for this port handle.
+        pJob = pPortHandle->pPort->pNextJobToProcess;
+        pPortHandle->pPort->pNextJobToProcess = NULL;
+        ASSERT(pJob);
+
+        // Call the monitor's StartDocPort function.
+        if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
+        else
+            bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
+
+        if (!bReturnValue)
+        {
+            // The StartDocPort function failed. Return its last error.
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // We were successful!
+        dwErrorCode = ERROR_SUCCESS;
+        dwReturnValue = pJob->dwJobID;
+        goto Cleanup;
+    }
+
+    // The remaining function deals with Printer handles only.
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    if (!pDocInfo1)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
+    if (pPrinterHandle->pJob)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Check the validity of the datatype if we got one.
+    if (pDocInfo1->pDatatype && !FindDatatype(pPrinterHandle->pJob->pPrintProcessor, pDocInfo1->pDatatype))
+    {
+        dwErrorCode = ERROR_INVALID_DATATYPE;
+        goto Cleanup;
+    }
+
+    // Check if this is the right document information level.
+    if (Level != 1)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    // All requirements are met - create a new job.
+    dwErrorCode = CreateJob(pPrinterHandle);
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    // Use any given datatype.
+    if (pDocInfo1->pDatatype && !ReallocSplStr(&pPrinterHandle->pJob->pwszDatatype, pDocInfo1->pDatatype))
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Use any given document name.
+    if (pDocInfo1->pDocName && !ReallocSplStr(&pPrinterHandle->pJob->pwszDocumentName, pDocInfo1->pDocName))
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // We were successful!
+    dwErrorCode = ERROR_SUCCESS;
+    dwReturnValue = pPrinterHandle->pJob->dwJobID;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return dwReturnValue;
+}
+
+BOOL WINAPI
+LocalStartPagePrinter(HANDLE hPrinter)
+{
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Sanity checks.
+    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // We require StartDocPrinter or AddJob to be called first.
+    if (!pPrinterHandle->bStartedDoc)
+    {
+        dwErrorCode = ERROR_SPL_NO_STARTDOC;
+        goto Cleanup;
+    }
+
+    // Increase the page count.
+    ++pPrinterHandle->pJob->dwTotalPages;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalWritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
+{
+    BOOL bReturnValue;
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Port handles are an entirely different thing.
+    if (pHandle->HandleType == HandleType_Port)
+    {
+        pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
+
+        // Call the monitor's WritePort function.
+        if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
+        else
+            bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
+
+        if (!bReturnValue)
+        {
+            // The WritePort function failed. Return its last error.
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // We were successful!
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+
+    // The remaining function deals with Printer handles only.
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // We require StartDocPrinter or AddJob to be called first.
+    if (!pPrinterHandle->bStartedDoc)
+    {
+        dwErrorCode = ERROR_SPL_NO_STARTDOC;
+        goto Cleanup;
+    }
+
+    // TODO: This function is only called when doing non-spooled printing.
+    // This needs to be investigated further. We can't just use pPrinterHandle->hSPLFile here, because that's currently reserved for Printer Job handles (see LocalReadPrinter).
+#if 0
+    // Pass the parameters to WriteFile.
+    if (!WriteFile(SOME_SPOOL_FILE_HANDLE, pBuf, cbBuf, pcWritten, NULL))
+    {
+        dwErrorCode = GetLastError();
+        ERR("WriteFile failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+#endif
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalEndPagePrinter(HANDLE hPrinter)
+{
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // This function doesn't do anything else for now.
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalEndDocPrinter(HANDLE hPrinter)
+{
+    BOOL bReturnValue;
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Port handles are an entirely different thing.
+    if (pHandle->HandleType == HandleType_Port)
+    {
+        pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
+
+        // Call the monitor's EndDocPort function.
+        if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
+            bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnEndDocPort(pPortHandle->hPort);
+        else
+            bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnEndDocPort(pPortHandle->hPort);
+
+        if (!bReturnValue)
+        {
+            // The EndDocPort function failed. Return its last error.
+            dwErrorCode = GetLastError();
+            goto Cleanup;
+        }
+
+        // We were successful!
+        dwErrorCode = ERROR_SUCCESS;
+        goto Cleanup;
+    }
+
+    // The remaining function deals with Printer handles only.
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // We require StartDocPrinter or AddJob to be called first.
+    if (!pPrinterHandle->bStartedDoc)
+    {
+        dwErrorCode = ERROR_SPL_NO_STARTDOC;
+        goto Cleanup;
+    }
+
+    // TODO: Something like ScheduleJob
+
+    // Finish the job.
+    pPrinterHandle->bStartedDoc = FALSE;
+    pPrinterHandle->pJob = NULL;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalClosePrinter(HANDLE hPrinter)
+{
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+    PLOCAL_XCV_HANDLE pXcvHandle;
+
+    if (!pHandle)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    if (pHandle->HandleType == HandleType_Port)
+    {
+        pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
+
+        // Call the monitor's ClosePort function.
+        if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
+            ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnClosePort(pPortHandle->hPort);
+        else
+            ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnClosePort(pPortHandle->hPort);
+    }
+    else if (pHandle->HandleType == HandleType_Printer)
+    {
+        pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+        // Terminate any started job.
+        if (pPrinterHandle->pJob)
+            FreeJob(pPrinterHandle->pJob);
+
+        // Free memory for the fields.
+        DllFreeSplMem(pPrinterHandle->pDevMode);
+        DllFreeSplStr(pPrinterHandle->pwszDatatype);
+    }
+    else if (pHandle->HandleType == HandleType_Xcv)
+    {
+        pXcvHandle = (PLOCAL_XCV_HANDLE)pHandle->pSpecificHandle;
+
+        // Call the monitor's XcvClosePort function.
+        if (pXcvHandle->pPrintMonitor->bIsLevel2)
+            ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv);
+        else
+            ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv);
+    }
+
+    // Free memory for the handle and the specific handle.
+    DllFreeSplMem(pHandle->pSpecificHandle);
+    DllFreeSplMem(pHandle);
+
+    return TRUE;
+}
diff --git a/reactos/win32ss/printing/providers/localspl/printingthread.c b/reactos/win32ss/printing/providers/localspl/printingthread.c
new file mode 100644 (file)
index 0000000..d858267
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Implementation of the Thread that actually performs the printing process
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD WINAPI
+PrintingThreadProc(PLOCAL_JOB pJob)
+{
+    const DWORD cchMaxJobIdDigits = 5;              // Job ID is limited to 5 decimal digits, see IS_VALID_JOB_ID
+    const WCHAR wszJobAppendix[] = L", Job ";
+    const DWORD cchJobAppendix = _countof(wszJobAppendix) - 1;
+    const WCHAR wszPortAppendix[] = L", Port";
+
+    DWORD cchPortName;
+    DWORD cchPrinterName;
+    DWORD dwErrorCode;
+    HANDLE hPrintProcessor = NULL;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor = pJob->pPrintProcessor;
+    PRINTPROCESSOROPENDATA OpenData;
+    PWSTR pwszPrinterAndJob = NULL;
+    PWSTR pwszPrinterPort = NULL;
+    PWSTR pwszSPLFile = NULL;
+
+    // Prepare the pPrinterName parameter.
+    // This is the string for LocalOpenPrinter to open a port (e.g. "LPT1:, Port").
+    cchPortName = wcslen(pJob->pPrinter->pPort->pwszName);
+    pwszPrinterPort = DllAllocSplMem(cchPortName * sizeof(WCHAR) + sizeof(wszPortAppendix));
+    if (!pwszPrinterPort)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    CopyMemory(pwszPrinterPort, pJob->pPrinter->pPort->pwszName, cchPortName * sizeof(WCHAR));
+    CopyMemory(&pwszPrinterPort[cchPortName], wszPortAppendix, sizeof(wszPortAppendix));
+
+    // Prepare the pPrintProcessorOpenData parameter.
+    OpenData.JobId = pJob->dwJobID;
+    OpenData.pDatatype = pJob->pwszDatatype;
+    OpenData.pDevMode = pJob->pDevMode;
+    OpenData.pDocumentName = pJob->pwszDocumentName;
+    OpenData.pOutputFile = NULL;
+    OpenData.pParameters = pJob->pwszPrintProcessorParameters;
+    OpenData.pPrinterName = pJob->pPrinter->pwszPrinterName;
+
+    // Associate our job to the port. The next port handle created through LocalOpenPrinter will pick this up.
+    // LocalStartDocPrinter needs this information to call StartDocPort of the Print Monitor, but as the parameters
+    // for LocalOpenPrinter and LocalStartDocPrinter are fixed, we can only pass over the information this way.
+    pJob->pPrinter->pPort->pNextJobToProcess = pJob;
+
+    // Open a handle to the Print Processor.
+    hPrintProcessor = pPrintProcessor->pfnOpenPrintProcessor(pwszPrinterPort, &OpenData);
+    if (!hPrintProcessor)
+    {
+        dwErrorCode = GetLastError();
+        ERR("OpenPrintProcessor failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Let other functions use the Print Processor as well while it's opened.
+    pJob->hPrintProcessor = hPrintProcessor;
+
+    // Prepare the pDocumentName parameter.
+    cchPrinterName = wcslen(OpenData.pPrinterName);
+    pwszPrinterAndJob = DllAllocSplMem((cchPrinterName + cchJobAppendix + cchMaxJobIdDigits + 1) * sizeof(WCHAR));
+    if (!pwszPrinterAndJob)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    CopyMemory(pwszPrinterAndJob, OpenData.pPrinterName, cchPrinterName * sizeof(WCHAR));
+    CopyMemory(&pwszPrinterAndJob[cchPrinterName], wszJobAppendix, cchJobAppendix * sizeof(WCHAR));
+    _ultow(OpenData.JobId, &pwszPrinterAndJob[cchPrinterName + cchJobAppendix], 10);
+
+    // Printing starts here.
+    pJob->dwStatus |= JOB_STATUS_PRINTING;
+
+    // Print the document.
+    // Note that pJob is freed after this function, so we may not access it anymore.
+    if (!pPrintProcessor->pfnPrintDocumentOnPrintProcessor(hPrintProcessor, pwszPrinterAndJob))
+    {
+        dwErrorCode = GetLastError();
+        ERR("PrintDocumentOnPrintProcessor failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Close the Print Processor.
+    pPrintProcessor->pfnClosePrintProcessor(hPrintProcessor);
+    hPrintProcessor = NULL;
+
+    // Delete the spool file.
+    pwszSPLFile = DllAllocSplMem(GetJobFilePath(L"SPL", 0, NULL));
+    if (!pwszSPLFile)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    GetJobFilePath(L"SPL", OpenData.JobId, pwszSPLFile);
+    DeleteFileW(pwszSPLFile);
+
+    // We were successful!
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (hPrintProcessor)
+        pPrintProcessor->pfnClosePrintProcessor(hPrintProcessor);
+
+    if (pwszPrinterPort)
+        DllFreeSplMem(pwszPrinterPort);
+
+    if (pwszPrinterAndJob)
+        DllFreeSplMem(pwszPrinterAndJob);
+
+    if (pwszSPLFile)
+        DllFreeSplMem(pwszSPLFile);
+
+    return dwErrorCode;
+}
diff --git a/reactos/win32ss/printing/providers/localspl/printprocessors.c b/reactos/win32ss/printing/providers/localspl/printprocessors.c
new file mode 100644 (file)
index 0000000..bda9e53
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Print Processors
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+
+// Local Variables
+static LIST_ENTRY _PrintProcessorList;
+
+/**
+ * @name _OpenEnvironment
+ *
+ * Checks a supplied pEnvironment variable for validity and opens its registry key.
+ *
+ * @param pEnvironment
+ * The pEnvironment variable to check. Can be NULL to use the current environment.
+ *
+ * @param hKey
+ * On success, this variable will contain a HKEY to the opened registry key of the environment.
+ * You can use it for further tasks and have to close it with RegCloseKey.
+ *
+ * @return
+ * A Windows Error Code indicating success or failure.
+ */
+static DWORD
+_OpenEnvironment(PCWSTR pEnvironment, PHKEY hKey)
+{
+    const WCHAR wszEnvironmentsKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\";
+    const DWORD cchEnvironmentsKey = _countof(wszEnvironmentsKey) - 1;
+
+    DWORD cchEnvironment;
+    DWORD dwErrorCode;
+    PWSTR pwszEnvironmentKey = NULL;
+
+    // Use the current environment if none was supplied.
+    if (!pEnvironment)
+        pEnvironment = wszCurrentEnvironment;
+
+    // Construct the registry key of the demanded environment.
+    cchEnvironment = wcslen(pEnvironment);
+    pwszEnvironmentKey = DllAllocSplMem((cchEnvironmentsKey + cchEnvironment + 1) * sizeof(WCHAR));
+    if (!pwszEnvironmentKey)
+    {
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    CopyMemory(pwszEnvironmentKey, wszEnvironmentsKey, cchEnvironmentsKey * sizeof(WCHAR));
+    CopyMemory(&pwszEnvironmentKey[cchEnvironmentsKey], pEnvironment, (cchEnvironment + 1) * sizeof(WCHAR));
+
+    // Open the registry key.
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, pwszEnvironmentKey, 0, KEY_READ, hKey);
+    if (dwErrorCode == ERROR_FILE_NOT_FOUND)
+    {
+        dwErrorCode = ERROR_INVALID_ENVIRONMENT;
+        goto Cleanup;
+    }
+    else if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+Cleanup:
+    if (pwszEnvironmentKey)
+        DllFreeSplMem(pwszEnvironmentKey);
+
+    return dwErrorCode;
+}
+
+BOOL
+FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype)
+{
+    DWORD i;
+    PDATATYPES_INFO_1W pCurrentDatatype = pPrintProcessor->pDatatypesInfo1;
+
+    if (!pwszDatatype)
+        return FALSE;
+
+    for (i = 0; i < pPrintProcessor->dwDatatypeCount; i++)
+    {
+        if (wcsicmp(pCurrentDatatype->pName, pwszDatatype) == 0)
+            return TRUE;
+
+        ++pCurrentDatatype;
+    }
+
+    return FALSE;
+}
+
+PLOCAL_PRINT_PROCESSOR
+FindPrintProcessor(PCWSTR pwszName)
+{
+    PLIST_ENTRY pEntry;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+
+    if (!pwszName)
+        return NULL;
+
+    for (pEntry = _PrintProcessorList.Flink; pEntry != &_PrintProcessorList; pEntry = pEntry->Flink)
+    {
+        pPrintProcessor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_PROCESSOR, Entry);
+
+        if (wcsicmp(pPrintProcessor->pwszName, pwszName) == 0)
+            return pPrintProcessor;
+    }
+
+    return NULL;
+}
+
+/**
+ * @name InitializePrintProcessorList
+ *
+ * Initializes a singly linked list of locally available Print Processors.
+ */
+BOOL
+InitializePrintProcessorList()
+{
+    DWORD cbDatatypes;
+    DWORD cbFileName;
+    DWORD cchPrintProcessorPath;
+    DWORD cchMaxSubKey;
+    DWORD cchPrintProcessorName;
+    DWORD dwErrorCode;
+    DWORD dwSubKeys;
+    DWORD i;
+    HINSTANCE hinstPrintProcessor;
+    HKEY hKey = NULL;
+    HKEY hSubKey = NULL;
+    HKEY hSubSubKey = NULL;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor = NULL;
+    WCHAR wszFileName[MAX_PATH];
+    WCHAR wszPrintProcessorPath[MAX_PATH];
+
+    // Initialize an empty list for our Print Processors.
+    InitializeListHead(&_PrintProcessorList);
+    
+    // Prepare the path to the Print Processor directory.
+    if (!LocalGetPrintProcessorDirectory(NULL, NULL, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath))
+    {
+        dwErrorCode = GetLastError();
+        goto Cleanup;
+    }
+
+    // LocalGetPrintProcessorDirectory returns the number of copied bytes. Convert this into a number of characters without the terminating null-character.
+    cchPrintProcessorPath /= sizeof(WCHAR);
+    --cchPrintProcessorPath;
+
+    // Append a trailing backslash.
+    wszPrintProcessorPath[cchPrintProcessorPath] = L'\\';
+    ++cchPrintProcessorPath;
+
+    // Open the environment registry key.
+    dwErrorCode = _OpenEnvironment(NULL, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Open the "Print Processors" subkey.
+    dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Get the number of Print Processors and maximum sub key length.
+    dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwSubKeys, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Loop through all available local Print Processors.
+    for (i = 0; i < dwSubKeys; i++)
+    {
+        // Cleanup tasks from the previous run
+        if (hSubSubKey)
+        {
+            RegCloseKey(hSubSubKey);
+            hSubSubKey = NULL;
+        }
+
+        if (pPrintProcessor)
+        {
+            if (pPrintProcessor->pwszName)
+                DllFreeSplStr(pPrintProcessor->pwszName);
+
+            if (pPrintProcessor->pDatatypesInfo1)
+                DllFreeSplMem(pPrintProcessor->pDatatypesInfo1);
+
+            DllFreeSplMem(pPrintProcessor);
+            pPrintProcessor = NULL;
+        }
+
+        // Create a new LOCAL_PRINT_PROCESSOR structure for it.
+        pPrintProcessor = DllAllocSplMem(sizeof(LOCAL_PRINT_PROCESSOR));
+        if (!pPrintProcessor)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        // Allocate memory for the Print Monitor Name.
+        pPrintProcessor->pwszName = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
+        if (!pPrintProcessor->pwszName)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        // Get the name of this Print Processor.
+        cchPrintProcessorName = cchMaxSubKey + 1;
+        dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, pPrintProcessor->pwszName, &cchPrintProcessorName, NULL, NULL, NULL, NULL);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegEnumKeyExW failed with status %ld!\n", dwErrorCode);
+            continue;
+        }
+
+        // Open this Print Processor's registry key.
+        dwErrorCode = (DWORD)RegOpenKeyExW(hSubKey, pPrintProcessor->pwszName, 0, KEY_READ, &hSubSubKey);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegOpenKeyExW failed for Print Processor \"%S\" with status %lu!\n", pPrintProcessor->pwszName, dwErrorCode);
+            continue;
+        }
+
+        // Get the file name of the Print Processor.
+        cbFileName = sizeof(wszFileName);
+        dwErrorCode = (DWORD)RegQueryValueExW(hSubSubKey, L"Driver", NULL, NULL, (PBYTE)wszFileName, &cbFileName);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegQueryValueExW failed for Print Processor \"%S\" with status %lu!\n", pPrintProcessor->pwszName, dwErrorCode);
+            continue;
+        }
+
+        // Verify that our buffer is large enough.
+        if (cchPrintProcessorPath + cbFileName / sizeof(WCHAR) > MAX_PATH)
+        {
+            ERR("Print Processor directory \"%S\" for Print Processor \"%S\" is too long!\n", wszFileName, pPrintProcessor->pwszName);
+            continue;
+        }
+
+        // Construct the full path to the Print Processor.
+        CopyMemory(&wszPrintProcessorPath[cchPrintProcessorPath], wszFileName, cbFileName);
+
+        // Try to load it.
+        hinstPrintProcessor = LoadLibraryW(wszPrintProcessorPath);
+        if (!hinstPrintProcessor)
+        {
+            ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", wszPrintProcessorPath, GetLastError());
+            continue;
+        }
+
+        // Get and verify all its function pointers.
+        pPrintProcessor->pfnClosePrintProcessor = (PClosePrintProcessor)GetProcAddress(hinstPrintProcessor, "ClosePrintProcessor");
+        if (!pPrintProcessor->pfnClosePrintProcessor)
+        {
+            ERR("Print Processor \"%S\" exports no ClosePrintProcessor!\n", wszPrintProcessorPath);
+            continue;
+        }
+
+        pPrintProcessor->pfnControlPrintProcessor = (PControlPrintProcessor)GetProcAddress(hinstPrintProcessor, "ControlPrintProcessor");
+        if (!pPrintProcessor->pfnControlPrintProcessor)
+        {
+            ERR("Print Processor \"%S\" exports no ControlPrintProcessor!\n", wszPrintProcessorPath);
+            continue;
+        }
+
+        pPrintProcessor->pfnEnumPrintProcessorDatatypesW = (PEnumPrintProcessorDatatypesW)GetProcAddress(hinstPrintProcessor, "EnumPrintProcessorDatatypesW");
+        if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW)
+        {
+            ERR("Print Processor \"%S\" exports no EnumPrintProcessorDatatypesW!\n", wszPrintProcessorPath);
+            continue;
+        }
+
+        pPrintProcessor->pfnGetPrintProcessorCapabilities = (PGetPrintProcessorCapabilities)GetProcAddress(hinstPrintProcessor, "GetPrintProcessorCapabilities");
+        if (!pPrintProcessor->pfnGetPrintProcessorCapabilities)
+        {
+            ERR("Print Processor \"%S\" exports no GetPrintProcessorCapabilities!\n", wszPrintProcessorPath);
+            continue;
+        }
+
+        pPrintProcessor->pfnOpenPrintProcessor = (POpenPrintProcessor)GetProcAddress(hinstPrintProcessor, "OpenPrintProcessor");
+        if (!pPrintProcessor->pfnOpenPrintProcessor)
+        {
+            ERR("Print Processor \"%S\" exports no OpenPrintProcessor!\n", wszPrintProcessorPath);
+            continue;
+        }
+
+        pPrintProcessor->pfnPrintDocumentOnPrintProcessor = (PPrintDocumentOnPrintProcessor)GetProcAddress(hinstPrintProcessor, "PrintDocumentOnPrintProcessor");
+        if (!pPrintProcessor->pfnPrintDocumentOnPrintProcessor)
+        {
+            ERR("Print Processor \"%S\" exports no PrintDocumentOnPrintProcessor!\n", wszPrintProcessorPath);
+            continue;
+        }
+
+        // Get all supported datatypes.
+        pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbDatatypes, &pPrintProcessor->dwDatatypeCount);
+        pPrintProcessor->pDatatypesInfo1 = DllAllocSplMem(cbDatatypes);
+        if (!pPrintProcessor->pDatatypesInfo1)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, (PBYTE)pPrintProcessor->pDatatypesInfo1, cbDatatypes, &cbDatatypes, &pPrintProcessor->dwDatatypeCount))
+        {
+            ERR("EnumPrintProcessorDatatypesW failed for Print Processor \"%S\" with error %lu!\n", wszPrintProcessorPath, GetLastError());
+            continue;
+        }
+
+        // Add the Print Processor to the list.
+        InsertTailList(&_PrintProcessorList, &pPrintProcessor->Entry);
+
+        // Don't let the cleanup routines free this.
+        pPrintProcessor = NULL;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    // Inside the loop
+    if (hSubSubKey)
+        RegCloseKey(hSubSubKey);
+
+    if (pPrintProcessor)
+    {
+        if (pPrintProcessor->pwszName)
+            DllFreeSplStr(pPrintProcessor->pwszName);
+
+        if (pPrintProcessor->pDatatypesInfo1)
+            DllFreeSplMem(pPrintProcessor->pDatatypesInfo1);
+
+        DllFreeSplMem(pPrintProcessor);
+    }
+
+    // Outside the loop
+    if (hSubKey)
+        RegCloseKey(hSubKey);
+
+    if (hKey)
+        RegCloseKey(hKey);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+/**
+ * @name LocalEnumPrintProcessorDatatypes
+ *
+ * Obtains an array of all datatypes supported by a particular Print Processor.
+ * Print Provider function for EnumPrintProcessorDatatypesA/EnumPrintProcessorDatatypesW.
+ *
+ * @param pName
+ * Server Name. Ignored here, because every caller of LocalEnumPrintProcessorDatatypes is interested in the local directory.
+ *
+ * @param pPrintProcessorName
+ * The (case-insensitive) name of the Print Processor to query.
+ *
+ * @param Level
+ * The level of the structure supplied through pDatatypes. This must be 1.
+ *
+ * @param pDatatypes
+ * Pointer to the buffer that receives an array of DATATYPES_INFO_1W structures.
+ * Can be NULL if you just want to know the required size of the buffer.
+ *
+ * @param cbBuf
+ * Size of the buffer you supplied for pDatatypes, in bytes.
+ *
+ * @param pcbNeeded
+ * Pointer to a variable that receives the required size of the buffer for pDatatypes, in bytes.
+ * This parameter mustn't be NULL!
+ *
+ * @param pcReturned
+ * Pointer to a variable that receives the number of elements of the DATATYPES_INFO_1W array.
+ * This parameter mustn't be NULL!
+ *
+ * @return
+ * TRUE if we successfully copied the array into pDatatypes, FALSE otherwise.
+ * A more specific error code can be obtained through GetLastError.
+ */
+BOOL WINAPI
+LocalEnumPrintProcessorDatatypes(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
+{
+    DWORD dwErrorCode;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+
+    // Sanity checks
+    if (Level != 1)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    // Try to find the Print Processor.
+    pPrintProcessor = FindPrintProcessor(pPrintProcessorName);
+    if (!pPrintProcessor)
+    {
+        dwErrorCode = ERROR_UNKNOWN_PRINTPROCESSOR;
+        goto Cleanup;
+    }
+
+    // Call its EnumPrintProcessorDatatypesW function.
+    if (pPrintProcessor->pfnEnumPrintProcessorDatatypesW(pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned))
+        dwErrorCode = ERROR_SUCCESS;
+    else
+        dwErrorCode = GetLastError();
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+/**
+ * @name LocalEnumPrintProcessors
+ *
+ * Obtains an array of all available Print Processors on this computer.
+ * Print Provider function for EnumPrintProcessorsA/EnumPrintProcessorsW.
+ *
+ * @param pName
+ * Server Name. Ignored here, because every caller of LocalEnumPrintProcessors is interested in the local directory.
+ *
+ * @param pEnvironment
+ * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86").
+ * Alternatively, NULL to output the Print Processor directory of the current environment.
+ *
+ * @param Level
+ * The level of the structure supplied through pPrintProcessorInfo. This must be 1.
+ *
+ * @param pPrintProcessorInfo
+ * Pointer to the buffer that receives an array of PRINTPROCESSOR_INFO_1W structures.
+ * Can be NULL if you just want to know the required size of the buffer.
+ *
+ * @param cbBuf
+ * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
+ *
+ * @param pcbNeeded
+ * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
+ * This parameter mustn't be NULL!
+ *
+ * @param pcReturned
+ * Pointer to a variable that receives the number of elements of the PRINTPROCESSOR_INFO_1W array.
+ * This parameter mustn't be NULL!
+ *
+ * @return
+ * TRUE if we successfully copied the array into pPrintProcessorInfo, FALSE otherwise.
+ * A more specific error code can be obtained through GetLastError.
+ */
+BOOL WINAPI
+LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
+{
+    DWORD cchMaxSubKey;
+    DWORD cchPrintProcessor;
+    DWORD dwErrorCode;
+    DWORD dwPrintProcessorCount;
+    DWORD i;
+    HKEY hKey = NULL;
+    HKEY hSubKey = NULL;
+    PBYTE pCurrentOutputPrintProcessor;
+    PBYTE pCurrentOutputPrintProcessorInfo;
+    PRINTPROCESSOR_INFO_1W PrintProcessorInfo1;
+    PWSTR pwszTemp = NULL;
+
+    // Sanity checks
+    if (Level != 1)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    if (!pcbNeeded || !pcReturned)
+    {
+        // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Verify pEnvironment and open its registry key.
+    // We use the registry and not the PrintProcessorList here, because the caller may request information about a different environment.
+    dwErrorCode = _OpenEnvironment(pEnvironment, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Open the "Print Processors" subkey.
+    dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Get the number of Print Processors and maximum sub key length.
+    dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwPrintProcessorCount, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Allocate a temporary buffer to let RegEnumKeyExW succeed.
+    pwszTemp = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
+    if (!pwszTemp)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Determine the required size of the output buffer.
+    *pcbNeeded = 0;
+
+    for (i = 0; i < dwPrintProcessorCount; i++)
+    {
+        // RegEnumKeyExW sucks! Unlike similar API functions, it only returns the actual numbers of characters copied when you supply a buffer large enough.
+        // So use pwszTemp with its size cchMaxSubKey for this.
+        cchPrintProcessor = cchMaxSubKey + 1;
+        dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, pwszTemp, &cchPrintProcessor, NULL, NULL, NULL, NULL);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+
+        *pcbNeeded += sizeof(PRINTPROCESSOR_INFO_1W) + (cchPrintProcessor + 1) * sizeof(WCHAR);
+    }
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Put the Print Processor strings right after the last PRINTPROCESSOR_INFO_1W structure.
+    pCurrentOutputPrintProcessorInfo = pPrintProcessorInfo;
+    pCurrentOutputPrintProcessor = pPrintProcessorInfo + dwPrintProcessorCount * sizeof(PRINTPROCESSOR_INFO_1W);
+
+    // Copy over all Print Processors.
+    for (i = 0; i < dwPrintProcessorCount; i++)
+    {
+        // This isn't really correct, but doesn't cause any harm, because we've extensively checked the size of the supplied buffer above.
+        cchPrintProcessor = cchMaxSubKey + 1;
+
+        // Copy the Print Processor name.
+        dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, (PWSTR)pCurrentOutputPrintProcessor, &cchPrintProcessor, NULL, NULL, NULL, NULL);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+
+        // Fill and copy the PRINTPROCESSOR_INFO_1W structure belonging to this Print Processor.
+        PrintProcessorInfo1.pName = (PWSTR)pCurrentOutputPrintProcessor;
+        CopyMemory(pCurrentOutputPrintProcessorInfo, &PrintProcessorInfo1, sizeof(PRINTPROCESSOR_INFO_1W));
+
+        // Advance to the next PRINTPROCESSOR_INFO_1W location and string location in the output buffer.
+        pCurrentOutputPrintProcessor += (cchPrintProcessor + 1) * sizeof(WCHAR);
+        pCurrentOutputPrintProcessorInfo += sizeof(PRINTPROCESSOR_INFO_1W);
+    }
+
+    // We've finished successfully!
+    *pcReturned = dwPrintProcessorCount;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (pwszTemp)
+        DllFreeSplMem(pwszTemp);
+
+    if (hSubKey)
+        RegCloseKey(hSubKey);
+
+    if (hKey)
+        RegCloseKey(hKey);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+/**
+ * @name LocalGetPrintProcessorDirectory
+ *
+ * Obtains the path to the local Print Processor directory.
+ * Print Provider function for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW.
+ *
+ * @param pName
+ * Server Name. Ignored here, because every caller of LocalGetPrintProcessorDirectory is interested in the local directory.
+ *
+ * @param pEnvironment
+ * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86").
+ * Alternatively, NULL to output the Print Processor directory of the current environment.
+ *
+ * @param Level
+ * The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1.
+ *
+ * @param pPrintProcessorInfo
+ * Pointer to the buffer that receives the full path to the Print Processor directory.
+ * Can be NULL if you just want to know the required size of the buffer.
+ *
+ * @param cbBuf
+ * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
+ *
+ * @param pcbNeeded
+ * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
+ * This parameter mustn't be NULL!
+ *
+ * @return
+ * TRUE if we successfully copied the directory into pPrintProcessorInfo, FALSE otherwise.
+ * A more specific error code can be obtained through GetLastError.
+ */
+BOOL WINAPI
+LocalGetPrintProcessorDirectory(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    const WCHAR wszPath[] = L"\\PRTPROCS\\";
+    const DWORD cchPath = _countof(wszPath) - 1;
+
+    DWORD cbDirectoryName;
+    DWORD dwErrorCode;
+    HKEY hKey = NULL;
+    PWSTR pwszDirectory = (PWSTR)pPrintProcessorInfo;
+
+    // Sanity checks
+    if (Level != 1)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    if (!pcbNeeded)
+    {
+        // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Verify pEnvironment and open its registry key.
+    dwErrorCode = _OpenEnvironment(pEnvironment, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Determine the size of the required buffer.
+    dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, NULL, &cbDirectoryName);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    *pcbNeeded = (cchSpoolDirectory + cchPath) * sizeof(WCHAR) + cbDirectoryName;
+
+    // Is the supplied buffer large enough?
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Copy the path to the "prtprocs" directory into pPrintProcessorInfo
+    CopyMemory(pwszDirectory, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
+    CopyMemory(&pwszDirectory[cchSpoolDirectory], wszPath, cchPath * sizeof(WCHAR));
+
+    // Get the directory name from the registry.
+    dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, (PBYTE)&pwszDirectory[cchSpoolDirectory + cchPath], &cbDirectoryName);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // We've finished successfully!
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (hKey)
+        RegCloseKey(hKey);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
diff --git a/reactos/win32ss/printing/providers/localspl/tools.c b/reactos/win32ss/printing/providers/localspl/tools.c
new file mode 100644 (file)
index 0000000..06f3c1b
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Various tools
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+/**
+ * @name AllocAndRegQueryWSZ
+ *
+ * Queries a REG_SZ value in the registry, allocates memory for it and returns a buffer containing the value.
+ * You have to free this buffer using DllFreeSplMem.
+ *
+ * @param hKey
+ * HKEY variable of the key opened with RegOpenKeyExW.
+ *
+ * @param pwszValueName
+ * Name of the REG_SZ value to query.
+ *
+ * @return
+ * Pointer to the buffer containing the value or NULL in case of failure.
+ */
+PWSTR
+AllocAndRegQueryWSZ(HKEY hKey, PCWSTR pwszValueName)
+{
+    DWORD cbNeeded;
+    LONG lStatus;
+    PWSTR pwszValue;
+
+    // Determine the size of the required buffer.
+    lStatus = RegQueryValueExW(hKey, pwszValueName, NULL, NULL, NULL, &cbNeeded);
+    if (lStatus != ERROR_SUCCESS)
+    {
+        ERR("RegQueryValueExW failed with status %ld!\n", lStatus);
+        return NULL;
+    }
+
+    // Allocate it.
+    pwszValue = DllAllocSplMem(cbNeeded);
+    if (!pwszValue)
+    {
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        return NULL;
+    }
+
+    // Now get the actual value.
+    lStatus = RegQueryValueExW(hKey, pwszValueName, NULL, NULL, (PBYTE)pwszValue, &cbNeeded);
+    if (lStatus != ERROR_SUCCESS)
+    {
+        ERR("RegQueryValueExW failed with status %ld!\n", lStatus);
+        DllFreeSplMem(pwszValue);
+        return NULL;
+    }
+
+    return pwszValue;
+}
+
+PDEVMODEW
+DuplicateDevMode(PDEVMODEW pInput)
+{
+    PDEVMODEW pOutput;
+
+    // Allocate a buffer for this DevMode.
+    pOutput = DllAllocSplMem(pInput->dmSize + pInput->dmDriverExtra);
+    if (!pOutput)
+    {
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        return NULL;
+    }
+
+    // Copy it.
+    CopyMemory(pOutput, pInput, pInput->dmSize + pInput->dmDriverExtra);
+
+    return pOutput;
+}
index ba83151..cd87071 100644 (file)
@@ -5,4 +5,5 @@ add_subdirectory(ntfsinfo)
 add_subdirectory(tee)
 add_subdirectory(touch)
 add_subdirectory(uptime)
+add_subdirectory(winspool_print)
 add_subdirectory(y)
diff --git a/rosapps/applications/cmdutils/winspool_print/CMakeLists.txt b/rosapps/applications/cmdutils/winspool_print/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0b21264
--- /dev/null
@@ -0,0 +1,4 @@
+add_executable(winspool_print main.c)
+set_module_type(winspool_print win32cui)
+add_importlibs(winspool_print winspool msvcrt kernel32)
+add_cd_file(TARGET winspool_print DESTINATION reactos/system32 FOR all)
diff --git a/rosapps/applications/cmdutils/winspool_print/main.c b/rosapps/applications/cmdutils/winspool_print/main.c
new file mode 100644 (file)
index 0000000..84173ef
--- /dev/null
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <windows.h>
+
+int main()
+{
+    int ReturnValue = 1;
+    DWORD dwRead;
+    DWORD dwWritten;
+    HANDLE hFile = INVALID_HANDLE_VALUE;
+    HANDLE hPrinter = NULL;
+    DOC_INFO_1W docInfo;
+    BYTE Buffer[20000];
+
+    hFile = CreateFileW(L"testfile", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+    {
+        printf("CreateFileW failed, last error is %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    if (!ReadFile(hFile, Buffer, sizeof(Buffer), &dwRead, NULL))
+    {
+        printf("ReadFile failed, last error is %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    if (!OpenPrinterW(L"Dummy Printer On LPT1", &hPrinter, NULL))
+    {
+        printf("OpenPrinterW failed, last error is %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    ZeroMemory(&docInfo, sizeof(docInfo));
+    docInfo.pDocName = L"winspool_print";
+
+    if (!StartDocPrinterW(hPrinter, 1, (LPBYTE)&docInfo))
+    {
+        printf("StartDocPrinterW failed, last error is %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    if (!StartPagePrinter(hPrinter))
+    {
+        printf("StartPagePrinter failed, last error is %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    if (!WritePrinter(hPrinter, Buffer, dwRead, &dwWritten))
+    {
+        printf("WritePrinter failed, last error is %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    if (!EndPagePrinter(hPrinter))
+    {
+        printf("EndPagePrinter failed, last error is %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    if (!EndDocPrinter(hPrinter))
+    {
+        printf("EndDocPrinter failed, last error is %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    ReturnValue = 0;
+
+Cleanup:
+    if (hFile != INVALID_HANDLE_VALUE)
+        CloseHandle(hFile);
+
+    if (hPrinter)
+        ClosePrinter(hPrinter);
+
+    return ReturnValue;
+}
index 3a9bdfa..8947340 100644 (file)
@@ -15,6 +15,7 @@ add_subdirectory(iphlpapi)
 if(NOT ARCH STREQUAL "amd64")
     add_subdirectory(kernel32)
 endif()
+add_subdirectory(localspl)
 add_subdirectory(msgina)
 add_subdirectory(msvcrt)
 add_subdirectory(ntdll)
@@ -24,6 +25,7 @@ add_subdirectory(powrprof)
 add_subdirectory(sdk)
 add_subdirectory(setupapi)
 add_subdirectory(shell32)
+add_subdirectory(spoolss)
 add_subdirectory(psapi)
 add_subdirectory(user32)
 add_subdirectory(user32_dynamic)
@@ -33,4 +35,6 @@ if(NOT ARCH STREQUAL "amd64" AND NOT CMAKE_BUILD_TYPE STREQUAL "Release")
 endif()
 add_subdirectory(winhttp)
 add_subdirectory(wininet)
+add_subdirectory(winprint)
+add_subdirectory(winspool)
 add_subdirectory(ws2_32)
diff --git a/rostests/apitests/localspl/CMakeLists.txt b/rostests/apitests/localspl/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e6c26f6
--- /dev/null
@@ -0,0 +1,13 @@
+
+add_subdirectory(dll)
+
+list(APPEND SOURCE
+    service.c
+    tests.c
+    testlist.c)
+
+add_executable(localspl_apitest ${SOURCE})
+target_link_libraries(localspl_apitest wine ${PSEH_LIB})
+set_module_type(localspl_apitest win32cui)
+add_importlibs(localspl_apitest advapi32 winspool msvcrt kernel32 ntdll)
+add_cd_file(TARGET localspl_apitest DESTINATION reactos/bin FOR all)
diff --git a/rostests/apitests/localspl/dll/CMakeLists.txt b/rostests/apitests/localspl/dll/CMakeLists.txt
new file mode 100644 (file)
index 0000000..76e6a43
--- /dev/null
@@ -0,0 +1,13 @@
+
+include_directories(${REACTOS_SOURCE_DIR}/win32ss/printing/include)
+
+list(APPEND SOURCE
+    fpEnumPrinters.c
+    main.c)
+
+add_library(localspl_apitest.dll SHARED ${SOURCE})
+target_link_libraries(localspl_apitest.dll wine ${PSEH_LIB})
+set_module_type(localspl_apitest.dll win32dll)
+add_importlibs(localspl_apitest.dll spoolss msvcrt kernel32 ntdll)
+set_target_properties(localspl_apitest.dll PROPERTIES SUFFIX "")
+add_cd_file(TARGET localspl_apitest.dll DESTINATION reactos/bin FOR all)
diff --git a/rostests/apitests/localspl/dll/fpEnumPrinters.c b/rostests/apitests/localspl/dll/fpEnumPrinters.c
new file mode 100644 (file)
index 0000000..82cb5e7
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * PROJECT:     ReactOS Local Spooler API Tests Injected DLL
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for fpEnumPrinters
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winreg.h>
+#include <winspool.h>
+#include <winsplp.h>
+
+#include "../localspl_apitest.h"
+#include <spoolss.h>
+
+extern BOOL GetLocalsplFuncs(LPPRINTPROVIDOR pp);
+
+START_TEST(fpEnumPrinters)
+{
+    DWORD cbNeeded;
+    DWORD cbTemp;
+    DWORD dwReturned;
+    DWORD i;
+    PRINTPROVIDOR pp;
+    PPRINTER_INFO_1W pPrinterInfo1;
+    PVOID pMem;
+
+    if (!GetLocalsplFuncs(&pp))
+        return;
+
+    // Verify that localspl only returns information about a single print provider (namely itself).
+    cbNeeded = 0xDEADBEEF;
+    dwReturned = 0xDEADBEEF;
+    SetLastError(0xDEADBEEF);
+    ok(!pp.fpEnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbNeeded, &dwReturned), "fpEnumPrinters returns TRUE\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "fpEnumPrinters returns error %lu!\n", GetLastError());
+    ok(cbNeeded > 0, "cbNeeded is 0!\n");
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    SetLastError(0xDEADBEEF);
+    pPrinterInfo1 = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
+    ok(pp.fpEnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, (PBYTE)pPrinterInfo1, cbNeeded, &cbNeeded, &dwReturned), "fpEnumPrinters returns FALSE\n");
+    ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu!\n", GetLastError());
+    ok(cbNeeded > 0, "cbNeeded is 0!\n");
+    ok(dwReturned == 1, "dwReturned is %lu!\n", dwReturned);
+
+    // Verify the actual strings returned.
+    ok(wcscmp(pPrinterInfo1->pName, L"Windows NT Local Print Providor") == 0, "pPrinterInfo1->pName is \"%S\"!\n", pPrinterInfo1->pName);
+    ok(wcscmp(pPrinterInfo1->pDescription, L"Windows NT Local Printers") == 0, "pPrinterInfo1->pDescription is \"%S\"!\n", pPrinterInfo1->pDescription);
+    ok(wcscmp(pPrinterInfo1->pComment, L"Locally connected Printers") == 0, "pPrinterInfo1->pComment is \"%S\"!\n", pPrinterInfo1->pComment);
+
+    // Level 7 is the highest supported for localspl under Windows Server 2003.
+    // Higher levels need to fail, but they don't set an error code, just cbNeeded to 0.
+    cbNeeded = 0xDEADBEEF;
+    dwReturned = 0xDEADBEEF;
+    SetLastError(0xDEADBEEF);
+    ok(!pp.fpEnumPrinters(PRINTER_ENUM_LOCAL, NULL, 8, NULL, 0, &cbNeeded, &dwReturned), "fpEnumPrinters returns TRUE!\n");
+    ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu!\n", GetLastError());
+    ok(cbNeeded == 0, "cbNeeded is %lu!\n", cbNeeded);
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // Verify that all valid levels work.
+    // In contrast to EnumPrintersW, which only accepts levels 0, 1, 2, 4 and 5, localspl returns information for level 0 to 7.
+    for (i = 0; i <= 7; i++)
+    {
+        // Try with no valid arguments at all.
+        // This scenario is usually caugt by RPC, so it just raises an exception here.
+        _SEH2_TRY
+        {
+            dwReturned = 0;
+            pp.fpEnumPrinters(PRINTER_ENUM_LOCAL, NULL, i, NULL, 0, NULL, NULL);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwReturned = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
+
+        ok(dwReturned == EXCEPTION_ACCESS_VIOLATION, "dwReturned is %lu for Level %lu!\n", dwReturned, i);
+
+        // Now get the required buffer size.
+        cbNeeded = 0xDEADBEEF;
+        dwReturned = 0xDEADBEEF;
+        SetLastError(0xDEADBEEF);
+        ok(!pp.fpEnumPrinters(PRINTER_ENUM_LOCAL, NULL, i, NULL, 0, &cbNeeded, &dwReturned), "fpEnumPrinters returns TRUE for Level %lu!\n", i);
+        ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "fpEnumPrinters returns error %lu for Level %lu!\n", GetLastError(), i);
+        ok(cbNeeded > 0, "cbNeeded is 0 for Level %lu!\n", i);
+        ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, i);
+
+        // This test corrupts something inside spoolsv so that it's only runnable once without restarting spoolsv. Therefore it's disabled.
+#if 0
+        // Now provide the demanded size, but no buffer. This also mustn't touch cbNeeded.
+        // This scenario is also caught by RPC and we just have an exception here.
+        _SEH2_TRY
+        {
+            dwReturned = 0;
+            pp.fpEnumPrinters(PRINTER_ENUM_LOCAL, NULL, i, NULL, cbNeeded, &cbTemp, &dwReturned);
+        }
+            _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwReturned = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
+
+        ok(dwReturned == EXCEPTION_ACCESS_VIOLATION, "dwReturned is %lu for Level %lu!\n", dwReturned, i);
+        ok(cbNeeded == cbTemp, "cbNeeded is %lu, cbTemp is %lu for Level %lu!\n", cbNeeded, cbTemp, i);
+#endif
+
+        // Finally use the function as intended and aim for success!
+        pMem = DllAllocSplMem(cbNeeded);
+        SetLastError(0xDEADBEEF);
+        ok(pp.fpEnumPrinters(PRINTER_ENUM_LOCAL, NULL, i, pMem, cbNeeded, &cbTemp, &dwReturned), "fpEnumPrinters returns FALSE for Level %lu!\n", i);
+
+        // This is crazy. For level 3, fpEnumPrinters always returns ERROR_INSUFFICIENT_BUFFER even if we supply a buffer large enough.
+        if (i == 3)
+            ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "fpEnumPrinters returns error %lu for Level %lu!\n", GetLastError(), i);
+        else
+            ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu for Level %lu!\n", GetLastError(), i);
+
+        DllFreeSplMem(pMem);
+    }
+
+    // fpEnumPrinters has to succeed independent of the level (valid or not) if we query no information.
+    for (i = 0; i < 10; i++)
+    {
+        SetLastError(0xDEADBEEF);
+        ok(pp.fpEnumPrinters(0, NULL, i, NULL, 0, &cbNeeded, &dwReturned), "fpEnumPrinters returns FALSE for Level %lu!\n", i);
+        ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu for Level %lu!\n", GetLastError(), i);
+        ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, i);
+        ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, i);
+    }
+}
diff --git a/rostests/apitests/localspl/dll/main.c b/rostests/apitests/localspl/dll/main.c
new file mode 100644 (file)
index 0000000..fc63db4
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * PROJECT:     ReactOS Local Spooler API Tests Injected DLL
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Main functions
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#define __ROS_LONG64__
+
+#define STANDALONE
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <io.h>
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winreg.h>
+#include <winspool.h>
+#include <winsplp.h>
+
+#include "../localspl_apitest.h"
+
+//#define NDEBUG
+#include <debug.h>
+
+// Test list
+extern void func_fpEnumPrinters(void);
+
+const struct test winetest_testlist[] =
+{
+    { "fpEnumPrinters", func_fpEnumPrinters },
+
+    { 0, 0 }
+};
+
+BOOL
+GetLocalsplFuncs(LPPRINTPROVIDOR pp)
+{
+    HMODULE hLocalspl;
+    PInitializePrintProvidor pfnInitializePrintProvidor;
+
+    // Get us a handle to the loaded localspl.dll.
+    hLocalspl = GetModuleHandleW(L"localspl");
+    if (!hLocalspl)
+    {
+        skip("GetModuleHandleW failed with error %u!\n", GetLastError());
+        return FALSE;
+    }
+
+    // Get a pointer to its InitializePrintProvidor function.
+    pfnInitializePrintProvidor = (PInitializePrintProvidor)GetProcAddress(hLocalspl, "InitializePrintProvidor");
+    if (!pfnInitializePrintProvidor)
+    {
+        skip("GetProcAddress failed with error %u!\n", GetLastError());
+        return FALSE;
+    }
+
+    // Get localspl's function pointers.
+    if (!pfnInitializePrintProvidor(pp, sizeof(PRINTPROVIDOR), NULL))
+    {
+        skip("pfnInitializePrintProvidor failed with error %u!\n", GetLastError());
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+// Running the tests from the injected DLL and redirecting their output to the pipe.
+BOOL WINAPI
+DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    char szTestName[150];
+    DWORD cbRead;
+    FILE* fpStdout;
+    HANDLE hCommandPipe;
+    int iOldStdout;
+
+    // We only want to run our test once when the DLL is injected to the process.
+    if (fdwReason != DLL_PROCESS_ATTACH)
+        return TRUE;
+
+    // Read the test to run from the command pipe.
+    hCommandPipe = CreateFileW(COMMAND_PIPE_NAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+    if (hCommandPipe == INVALID_HANDLE_VALUE)
+    {
+        DPRINT("DLL: CreateFileW failed for the command pipe with error %lu!\n", GetLastError());
+        return FALSE;
+    }
+
+    if (!ReadFile(hCommandPipe, szTestName, sizeof(szTestName), &cbRead, NULL))
+    {
+        DPRINT("DLL: ReadFile failed for the command pipe with error %lu!\n", GetLastError());
+        return FALSE;
+    }
+
+    CloseHandle(hCommandPipe);
+
+    // Check if the test name is valid.
+    if (!find_test(szTestName))
+    {
+        DPRINT("DLL: Got invalid test name \"%s\"!\n", szTestName);
+        return FALSE;
+    }
+
+    // Backup our current stdout and set it to the output pipe.
+    iOldStdout = _dup(_fileno(stdout));
+    fpStdout = _wfreopen(OUTPUT_PIPE_NAME, L"w", stdout);
+    setbuf(stdout, NULL);
+
+    // Run the test.
+    run_test(szTestName);
+
+    // Restore stdout to the previous value.
+    fclose(fpStdout);
+    _dup2(iOldStdout, _fileno(stdout));
+
+    // Return FALSE so that our DLL is immediately unloaded.
+    return FALSE;
+}
diff --git a/rostests/apitests/localspl/localspl_apitest.h b/rostests/apitests/localspl/localspl_apitest.h
new file mode 100644 (file)
index 0000000..402a941
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * PROJECT:     ReactOS Local Spooler API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Shared definitions for the test program and the test DLL
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#ifndef _LOCALSPL_APITEST_H
+#define _LOCALSPL_APITEST_H
+
+#define COMMAND_PIPE_NAME       L"\\\\.\\pipe\\localspl_apitest_command_pipe"
+#define OUTPUT_PIPE_NAME        L"\\\\.\\pipe\\localspl_apitest_output_pipe"
+#define SERVICE_NAME            L"localspl_apitest_service"
+
+typedef BOOL (WINAPI *PInitializePrintProvidor)(LPPRINTPROVIDOR, DWORD, LPWSTR);
+
+#endif
\ No newline at end of file
diff --git a/rostests/apitests/localspl/service.c b/rostests/apitests/localspl/service.c
new file mode 100644 (file)
index 0000000..384b075
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * PROJECT:     ReactOS Local Spooler API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions needed to run our code as a service. This is needed to run in SYSTEM security context.
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winreg.h>
+#include <winsvc.h>
+#include <winspool.h>
+#include <winsplp.h>
+#include <tlhelp32.h>
+
+#include "localspl_apitest.h"
+
+//#define NDEBUG
+#include <debug.h>
+
+
+static void
+_DoDLLInjection()
+{
+    DWORD cbDLLPath;
+    HANDLE hProcess;
+    HANDLE hSnapshot;
+    HANDLE hThread;
+    PROCESSENTRY32W pe;
+    PVOID pLoadLibraryAddress;
+    PVOID pLoadLibraryArgument;
+    PWSTR p;
+    WCHAR wszFilePath[MAX_PATH];
+
+    // Get the full path to our EXE file.
+    if (!GetModuleFileNameW(NULL, wszFilePath, _countof(wszFilePath)))
+    {
+        DPRINT("GetModuleFileNameW failed with error %lu!\n", GetLastError());
+        return;
+    }
+
+    // Replace the extension.
+    p = wcsrchr(wszFilePath, L'.');
+    if (!p)
+    {
+        DPRINT("File path has no file extension: %S\n", wszFilePath);
+        return;
+    }
+
+    wcscpy(p, L".dll");
+    cbDLLPath = (wcslen(wszFilePath) + 1) * sizeof(WCHAR);
+
+    // Create a snapshot of the currently running processes.
+    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+    if (hSnapshot == INVALID_HANDLE_VALUE)
+    {
+        DPRINT("CreateToolhelp32Snapshot failed with error %lu!\n", GetLastError());
+        return;
+    }
+
+    // Enumerate through all running processes.
+    pe.dwSize = sizeof(pe);
+    if (!Process32FirstW(hSnapshot, &pe))
+    {
+        DPRINT("Process32FirstW failed with error %lu!\n", GetLastError());
+        return;
+    }
+
+    do
+    {
+        // Check if this is the spooler server process.
+        if (wcsicmp(pe.szExeFile, L"spoolsv.exe") != 0)
+            continue;
+
+        // Open a handle to the process.
+        hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
+        if (!hProcess)
+        {
+            DPRINT("OpenProcess failed with error %lu!\n", GetLastError());
+            return;
+        }
+
+        // Get the address of LoadLibraryW.
+        pLoadLibraryAddress = (PVOID)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW");
+        if (!pLoadLibraryAddress)
+        {
+            DPRINT("GetProcAddress failed with error %lu!\n", GetLastError());
+            return;
+        }
+
+        // Allocate memory for the DLL path in the spooler process.
+        pLoadLibraryArgument = VirtualAllocEx(hProcess, NULL, cbDLLPath, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+        if (!pLoadLibraryArgument)
+        {
+            DPRINT("VirtualAllocEx failed with error %lu!\n", GetLastError());
+            return;
+        }
+
+        // Write the DLL path to the process memory.
+        if (!WriteProcessMemory(hProcess, pLoadLibraryArgument, wszFilePath, cbDLLPath, NULL))
+        {
+            DPRINT("WriteProcessMemory failed with error %lu!\n", GetLastError());
+            return;
+        }
+
+        // Create a new thread in the spooler process that calls LoadLibraryW as the start routine with our DLL as the argument.
+        // This effectively injects our DLL into the spooler process and we can inspect localspl.dll there just like the spooler.
+        hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibraryAddress, pLoadLibraryArgument, 0, NULL);
+        if (!hThread)
+        {
+            DPRINT("CreateRemoteThread failed with error %lu!\n", GetLastError());
+            return;
+        }
+
+        CloseHandle(hThread);
+        break;
+    }
+    while (Process32NextW(hSnapshot, &pe));
+}
+
+static DWORD WINAPI
+_ServiceControlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
+{
+    return NO_ERROR;
+}
+
+static void WINAPI
+_ServiceMain(DWORD dwArgc, LPWSTR* lpszArgv)
+{
+    SERVICE_STATUS_HANDLE hServiceStatus;
+    SERVICE_STATUS ServiceStatus;
+
+    UNREFERENCED_PARAMETER(dwArgc);
+    UNREFERENCED_PARAMETER(lpszArgv);
+
+    // Register our service for control.
+    hServiceStatus = RegisterServiceCtrlHandlerExW(SERVICE_NAME, _ServiceControlHandlerEx, NULL);
+
+    // Report SERVICE_RUNNING status.
+    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+    ServiceStatus.dwServiceSpecificExitCode = 0;
+    ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+    ServiceStatus.dwWaitHint = 4000;
+    ServiceStatus.dwWin32ExitCode = NO_ERROR;
+    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
+    SetServiceStatus(hServiceStatus, &ServiceStatus);
+
+    // Do our funky crazy stuff.
+    _DoDLLInjection();
+
+    // Our work is done.
+    ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+    SetServiceStatus(hServiceStatus, &ServiceStatus);
+}
+
+START_TEST(service)
+{
+    int argc;
+    char** argv;
+
+    SERVICE_TABLE_ENTRYW ServiceTable[] =
+    {
+        { SERVICE_NAME, _ServiceMain },
+        { NULL, NULL }
+    };
+
+    // This is no real test, but an easy way to integrate the service handler routines into the API-Test executable.
+    // Therefore, bail out if someone tries to run "service" as a usual test.
+    argc = winetest_get_mainargs(&argv);
+    if (argc != 3)
+        return;
+
+    // If we have exactly 3 arguments, we're run as a service, so initialize the corresponding service handler functions.
+    StartServiceCtrlDispatcherW(ServiceTable);
+
+    // Prevent the testing framework from outputting a "0 tests executed" line here.
+    ExitProcess(0);
+}
diff --git a/rostests/apitests/localspl/testlist.c b/rostests/apitests/localspl/testlist.c
new file mode 100644 (file)
index 0000000..7102484
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * PROJECT:     ReactOS Local Spooler API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Test list
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#define __ROS_LONG64__
+
+#define STANDALONE
+#include <apitest.h>
+
+extern void func_fpEnumPrinters(void);
+extern void func_service(void);
+
+const struct test winetest_testlist[] =
+{
+    { "fpEnumPrinters", func_fpEnumPrinters },
+    { "service", func_service },
+
+    { 0, 0 }
+};
diff --git a/rostests/apitests/localspl/tests.c b/rostests/apitests/localspl/tests.c
new file mode 100644 (file)
index 0000000..9dd9324
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * PROJECT:     ReactOS Local Spooler API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Test list
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+/*
+ * The original localspl.dll from Windows Server 2003 is not easily testable.
+ * It relies on a proper initialization inside spoolsv.exe, so we can't just load it in an API-Test as usual.
+ * See https://www.reactos.org/pipermail/ros-dev/2015-June/017395.html for more information.
+ * 
+ * To make testing possible anyway, this program basically does four things:
+ *     - Injecting our testing code into spoolsv.exe.
+ *     - Registering and running us as a service in the SYSTEM security context like spoolsv.exe, so that injection is possible at all.
+ *     - Sending the test name and receiving the console output over named pipes.
+ *     - Redirecting the received console output to stdout again, so it looks and feels like a standard API-Test.
+ *
+ * To simplify debugging of the injected code, it is entirely separated into a DLL file localspl_apitest.dll.
+ * What we actually inject is a LoadLibraryW call, so that the DLL is loaded gracefully without any hacks.
+ * Therefore, you can just attach your debugger to the spoolsv.exe process and set breakpoints on the localspl_apitest.dll code.
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <stdio.h>
+#include <stdlib.h>
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winreg.h>
+#include <winsvc.h>
+#include <winspool.h>
+#include <winsplp.h>
+
+#include "localspl_apitest.h"
+
+
+static void
+_RunRemoteTest(const char* szTestName)
+{
+    BOOL bSuccessful = FALSE;
+    char szBuffer[1024];
+    DWORD cbRead;
+    DWORD cbWritten;
+    HANDLE hCommandPipe = INVALID_HANDLE_VALUE;
+    HANDLE hFind = NULL;
+    HANDLE hOutputPipe = INVALID_HANDLE_VALUE;
+    PWSTR p;
+    SC_HANDLE hSC = NULL;
+    SC_HANDLE hService = NULL;
+    SERVICE_STATUS ServiceStatus;
+    WCHAR wszFilePath[MAX_PATH + 20];
+    WIN32_FIND_DATAW fd;
+
+    // Do a dummy EnumPrintersW call.
+    // This guarantees that the Spooler Service has actually loaded localspl.dll, which is a requirement for our injected DLL to work properly.
+    EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbRead, &cbWritten);
+
+    // Get the full path to our EXE file.
+    if (!GetModuleFileNameW(NULL, wszFilePath, MAX_PATH))
+    {
+        skip("GetModuleFileNameW failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Replace the extension.
+    p = wcsrchr(wszFilePath, L'.');
+    if (!p)
+    {
+        skip("File path has no file extension: %S\n", wszFilePath);
+        goto Cleanup;
+    }
+
+    wcscpy(p, L".dll");
+
+    // Check if the corresponding DLL file exists.
+    hFind = FindFirstFileW(wszFilePath, &fd);
+    if (!hFind)
+    {
+        skip("My DLL file \"%S\" does not exist!\n", wszFilePath);
+        goto Cleanup;
+    }
+
+    // Change the extension back to .exe and add the parameters.
+    wcscpy(p, L".exe service dummy");
+
+    // Open a handle to the service manager.
+    hSC = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+    if (!hSC)
+    {
+        skip("OpenSCManagerW failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Ensure that the spooler service is running.
+    hService = OpenServiceW(hSC, L"spooler", SERVICE_QUERY_STATUS);
+    if (!hService)
+    {
+        skip("OpenServiceW failed for the spooler service with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    if (!QueryServiceStatus(hService, &ServiceStatus))
+    {
+        skip("QueryServiceStatus failed for the spooler service with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    if (ServiceStatus.dwCurrentState != SERVICE_RUNNING)
+    {
+        skip("Spooler Service is not running!\n");
+        goto Cleanup;
+    }
+
+    CloseServiceHandle(hService);
+
+    // Try to open the service if we've created it in a previous run.
+    hService = OpenServiceW(hSC, SERVICE_NAME, SERVICE_ALL_ACCESS);
+    if (!hService)
+    {
+        if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
+        {
+            // Create the service.
+            hService = CreateServiceW(hSC, SERVICE_NAME, NULL, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, wszFilePath, NULL, NULL, NULL, NULL, NULL);
+            if (!hService)
+            {
+                skip("CreateServiceW failed with error %lu!\n", GetLastError());
+                goto Cleanup;
+            }
+        }
+        else
+        {
+            skip("OpenServiceW failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    // Create pipes for the communication with the injected DLL.
+    hCommandPipe = CreateNamedPipeW(COMMAND_PIPE_NAME, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 1024, 1024, 10000, NULL);
+    if (hCommandPipe == INVALID_HANDLE_VALUE)
+    {
+        skip("CreateNamedPipeW failed for the command pipe with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    hOutputPipe = CreateNamedPipeW(OUTPUT_PIPE_NAME, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 10000, NULL);
+    if (hOutputPipe == INVALID_HANDLE_VALUE)
+    {
+        skip("CreateNamedPipeW failed for the output pipe with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Start the service with "service" and a dummy parameter (to distinguish it from a call by rosautotest to localspl_apitest:service)
+    if (!StartServiceW(hService, 0, NULL))
+    {
+        skip("StartServiceW failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Wait till it has injected the DLL and the DLL expects its test name.
+    if (!ConnectNamedPipe(hCommandPipe, NULL) && GetLastError() != ERROR_PIPE_CONNECTED)
+    {
+        skip("ConnectNamedPipe failed for the command pipe with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Send the test name.
+    if (!WriteFile(hCommandPipe, szTestName, strlen(szTestName) + sizeof(char), &cbWritten, NULL))
+    {
+        skip("WriteFile failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Now wait for the DLL to connect to the output pipe.
+    if (!ConnectNamedPipe(hOutputPipe, NULL))
+    {
+        skip("ConnectNamedPipe failed for the output pipe with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Get all testing messages from the pipe and output them on stdout.
+    while (ReadFile(hOutputPipe, szBuffer, sizeof(szBuffer), &cbRead, NULL) && cbRead)
+        fwrite(szBuffer, sizeof(char), cbRead, stdout);
+
+    bSuccessful = TRUE;
+
+Cleanup:
+    if (hCommandPipe)
+        CloseHandle(hCommandPipe);
+
+    if (hOutputPipe)
+        CloseHandle(hOutputPipe);
+
+    if (hFind)
+        FindClose(hFind);
+
+    if (hService)
+        CloseServiceHandle(hService);
+
+    if (hSC)
+        CloseServiceHandle(hSC);
+
+    // If we successfully received test output through the named pipe, we have also output a summary line already.
+    // Prevent the testing framework from outputting another "0 tests executed" line in this case.
+    if (bSuccessful)
+        ExitProcess(0);
+}
+
+START_TEST(fpEnumPrinters)
+{
+    _RunRemoteTest("fpEnumPrinters");
+}
diff --git a/rostests/apitests/spoolss/CMakeLists.txt b/rostests/apitests/spoolss/CMakeLists.txt
new file mode 100644 (file)
index 0000000..35e37f7
--- /dev/null
@@ -0,0 +1,14 @@
+
+include_directories(${REACTOS_SOURCE_DIR}/win32ss/printing/include)
+
+list(APPEND SOURCE
+    PackStrings.c
+    ReallocSplStr.c
+    SplInitializeWinSpoolDrv.c
+    testlist.c)
+
+add_executable(spoolss_apitest ${SOURCE})
+target_link_libraries(spoolss_apitest wine ${PSEH_LIB})
+set_module_type(spoolss_apitest win32cui)
+add_importlibs(spoolss_apitest spoolss msvcrt kernel32 ntdll)
+add_cd_file(TARGET spoolss_apitest DESTINATION reactos/bin FOR all)
diff --git a/rostests/apitests/spoolss/PackStrings.c b/rostests/apitests/spoolss/PackStrings.c
new file mode 100644 (file)
index 0000000..8f7c1a7
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * PROJECT:     ReactOS Spooler Router API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for PackStrings
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <spoolss.h>
+
+typedef struct _EXAMPLE_STRUCT
+{
+    PWSTR String1;
+    PWSTR String2;
+}
+EXAMPLE_STRUCT, *PEXAMPLE_STRUCT;
+
+START_TEST(PackStrings)
+{
+    PCWSTR Source1[] = { L"Test", L"String" };
+    PCWSTR Source2[] = { L"Test", NULL };
+
+    BYTE Buffer[50];
+    PBYTE pEnd;
+    PEXAMPLE_STRUCT pStruct = (PEXAMPLE_STRUCT)Buffer;
+    DWORD Offsets[] = {
+        FIELD_OFFSET(EXAMPLE_STRUCT, String1),
+        FIELD_OFFSET(EXAMPLE_STRUCT, String2),
+        MAXDWORD
+    };
+    
+    // Try a usual case with two strings. Verify that they are copied in reverse order.
+    pEnd = PackStrings(Source1, Buffer, Offsets, &Buffer[sizeof(Buffer)]);
+    ok(wcscmp(pStruct->String1, Source1[0]) == 0, "String1 and Source1[0] don't match!\n");
+    ok(wcscmp(pStruct->String2, Source1[1]) == 0, "String2 and Source1[1] don't match!\n");
+    ok(wcscmp((PWSTR)pEnd, Source1[1]) == 0, "pEnd and Source1[1] don't match!\n");
+
+    // Now verify that the corresponding pointer is set to NULL if a string is NULL.
+    pEnd = PackStrings(Source2, Buffer, Offsets, &Buffer[sizeof(Buffer)]);
+    ok(wcscmp(pStruct->String1, Source2[0]) == 0, "String1 and Source2[0] don't match!\n");
+    ok(!pStruct->String2, "String2 is %p!\n", pStruct->String2);
+    ok(wcscmp((PWSTR)pEnd, Source2[0]) == 0, "pEnd and Source2[0] don't match!\n");
+}
diff --git a/rostests/apitests/spoolss/ReallocSplStr.c b/rostests/apitests/spoolss/ReallocSplStr.c
new file mode 100644 (file)
index 0000000..d6827ff
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * PROJECT:     ReactOS Spooler Router API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for ReallocSplStr
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <spoolss.h>
+
+START_TEST(ReallocSplStr)
+{
+    const WCHAR wszTestString1[] = L"Test";
+    const WCHAR wszTestString2[] = L"New";
+
+    DWORD dwResult;
+    PWSTR pwszBackup;
+    PWSTR pwszTest;
+
+    // Verify that ReallocSplStr raises an exception if all parameters are NULL.
+    _SEH2_TRY
+    {
+        dwResult = 0;
+        ReallocSplStr(NULL, NULL);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwResult = _SEH2_GetExceptionCode();
+    }
+    _SEH2_END;
+
+    ok(dwResult == EXCEPTION_ACCESS_VIOLATION, "dwResult is %lx!\n", dwResult);
+
+    // Allocate a string for testing.
+    pwszTest = AllocSplStr(wszTestString1);
+    if (!pwszTest)
+    {
+        skip("AllocSplStr failed with error %lu!\n", GetLastError());
+        return;
+    }
+
+    // Verify that ReallocSplStr frees the old string even if pwszInput is NULL.
+    ok(ReallocSplStr(&pwszTest, NULL), "ReallocSplStr is FALSE!\n");
+    ok(pwszTest == NULL, "pwszTest is %p\n", pwszTest);
+
+    // Now verify that ReallocSplStr copies the new string into a new block and frees the old one.
+    pwszBackup = pwszTest;
+    ok(ReallocSplStr(&pwszTest, wszTestString2), "ReallocSplStr is FALSE!\n");
+    ok(wcscmp(pwszTest, wszTestString2) == 0, "New string was not copied into pwszTest!\n");
+
+    _SEH2_TRY
+    {
+        dwResult = (DWORD)wcscmp(pwszBackup, wszTestString1);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwResult = _SEH2_GetExceptionCode();
+    }
+    _SEH2_END;
+
+    ok(dwResult == EXCEPTION_ACCESS_VIOLATION, "dwResult is %lx!\n", dwResult);
+}
diff --git a/rostests/apitests/spoolss/SplInitializeWinSpoolDrv.c b/rostests/apitests/spoolss/SplInitializeWinSpoolDrv.c
new file mode 100644 (file)
index 0000000..c94ade8
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * PROJECT:     ReactOS Spooler Router API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for SplInitializeWinSpoolDrv
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <spoolss.h>
+
+START_TEST(SplInitializeWinSpoolDrv)
+{
+    HINSTANCE hWinspool;
+    PVOID Table[9];
+
+    hWinspool = LoadLibraryW(L"winspool.drv");
+    if (!hWinspool)
+    {
+        skip("Could not load winspool.drv, last error is %lu!\n", GetLastError());
+        return;
+    }
+
+    ok(SplInitializeWinSpoolDrv(Table), "SplInitializeWinSpoolDrv returns FALSE!\n");
+    ok(Table[0] == GetProcAddress(hWinspool, "OpenPrinterW"), "Table[0] is %p\n", Table[0]);
+    ok(Table[1] == GetProcAddress(hWinspool, "ClosePrinter"), "Table[1] is %p\n", Table[1]);
+    ok(Table[2] == GetProcAddress(hWinspool, "SpoolerDevQueryPrintW"), "Table[2] is %p\n", Table[2]);
+    ok(Table[3] == GetProcAddress(hWinspool, "SpoolerPrinterEvent"), "Table[3] is %p\n", Table[3]);
+    ok(Table[4] == GetProcAddress(hWinspool, "DocumentPropertiesW"), "Table[4] is %p\n", Table[4]);
+    ok(Table[5] == GetProcAddress(hWinspool, (LPSTR)212), "Table[5] is %p\n", Table[5]);
+    ok(Table[6] == GetProcAddress(hWinspool, (LPSTR)213), "Table[6] is %p\n", Table[6]);
+    ok(Table[7] == GetProcAddress(hWinspool, (LPSTR)214), "Table[7] is %p\n", Table[7]);
+    ok(Table[8] == GetProcAddress(hWinspool, (LPSTR)215), "Table[8] is %p\n", Table[8]);
+}
diff --git a/rostests/apitests/spoolss/testlist.c b/rostests/apitests/spoolss/testlist.c
new file mode 100644 (file)
index 0000000..82f57b5
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * PROJECT:     ReactOS Print Spooler Router API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Test list
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#define __ROS_LONG64__
+
+#define STANDALONE
+#include <apitest.h>
+
+extern void func_PackStrings(void);
+extern void func_ReallocSplStr(void);
+extern void func_SplInitializeWinSpoolDrv(void);
+
+const struct test winetest_testlist[] =
+{
+    { "PackStrings", func_PackStrings },
+    { "ReallocSplStr", func_ReallocSplStr },
+    { "SplInitializeWinSpoolDrv", func_SplInitializeWinSpoolDrv },
+
+    { 0, 0 }
+};
diff --git a/rostests/apitests/winprint/CMakeLists.txt b/rostests/apitests/winprint/CMakeLists.txt
new file mode 100644 (file)
index 0000000..68fa7d9
--- /dev/null
@@ -0,0 +1,10 @@
+
+list(APPEND SOURCE
+    EnumPrintProcessorDatatypesW.c
+    testlist.c)
+
+add_executable(winprint_apitest ${SOURCE})
+target_link_libraries(winprint_apitest wine ${PSEH_LIB})
+set_module_type(winprint_apitest win32cui)
+add_importlibs(winprint_apitest winprint msvcrt kernel32 ntdll)
+add_cd_file(TARGET winprint_apitest DESTINATION reactos/bin FOR all)
diff --git a/rostests/apitests/winprint/EnumPrintProcessorDatatypesW.c b/rostests/apitests/winprint/EnumPrintProcessorDatatypesW.c
new file mode 100644 (file)
index 0000000..551bfb9
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * PROJECT:     ReactOS Standard Print Processor API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for EnumPrintProcessorDatatypesW
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+START_TEST(EnumPrintProcessorDatatypesW)
+{
+    DWORD cbNeeded;
+    DWORD cbTemp;
+    DWORD dwReturned;
+    PDATATYPES_INFO_1W pDatatypesInfo1;
+
+    // Try with an invalid level. The error needs to be set by winspool, but not by the Print Processor.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 0, NULL, 0, NULL, NULL), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+
+    // Now try with valid level, but no pcbNeeded and no pcReturned. The error needs to be set by RPC.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, NULL, NULL), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+
+    // Now try with pcbNeeded and pcReturned, but give no Print Processor. Show that winprint actually ignores the given Print Processor.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+    ok(cbNeeded > 0, "cbNeeded is 0!\n");
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // Same error has to occur when looking for an invalid Print Processor.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, L"invalid", 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+    ok(cbNeeded > 0, "cbNeeded is 0!\n");
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // Now get the required buffer size by supplying all information. This needs to fail with ERROR_INSUFFICIENT_BUFFER.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, L"winprint", 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+    ok(cbNeeded > 0, "cbNeeded is 0!\n");
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // Same error has to occur with a size to small.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, L"winprint", 1, NULL, 1, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu!\n", GetLastError());
+    ok(cbNeeded > 0, "cbNeeded is 0!\n");
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // Now provide the demanded size, but no buffer. Show that winprint returns a different error than the same function in winspool.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, L"winprint", 1, NULL, cbNeeded, &cbTemp, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+    ok(cbTemp == cbNeeded, "cbTemp is %lu!\n", cbTemp);
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // This also has to fail the same way when no Print Processor was given at all.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, cbNeeded, &cbTemp, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+    ok(cbTemp == cbNeeded, "cbTemp is %lu!\n", cbTemp);
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // Finally use the function as intended and aim for success! Show that winprint doesn't modify the error code at all.
+    pDatatypesInfo1 = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
+    SetLastError(0xDEADBEEF);
+    ok(EnumPrintProcessorDatatypesW(NULL, L"winprint", 1, (PBYTE)pDatatypesInfo1, cbNeeded, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns FALSE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+    HeapFree(GetProcessHeap(), 0, pDatatypesInfo1);
+}
diff --git a/rostests/apitests/winprint/testlist.c b/rostests/apitests/winprint/testlist.c
new file mode 100644 (file)
index 0000000..1ad91ab
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * PROJECT:     ReactOS Standard Print Processor API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Test list
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+/*
+ * These tests are developed and tested against the Windows Server 2003 counterpart of winprint.
+ * While ReactOS implements the Standard Print Processor in a separate module winprint.dll,
+ * Windows Server 2003 puts it into the Local Print Spooler localspl.dll.
+ *
+ * To test against Windows, simply run these tests under Windows Server 2003, but copy its
+ * localspl.dll to winprint.dll in advance.
+ *
+ * winspool.drv also provides functions that go into winprint.dll, but as these tests show,
+ * they behave slightly different in terms of error codes due to the involved RPC and routing.
+ */
+
+#define __ROS_LONG64__
+
+#define STANDALONE
+#include <apitest.h>
+
+extern void func_EnumPrintProcessorDatatypesW(void);
+
+const struct test winetest_testlist[] =
+{
+    { "EnumPrintProcessorDatatypesW", func_EnumPrintProcessorDatatypesW },
+
+    { 0, 0 }
+};
diff --git a/rostests/apitests/winspool/CMakeLists.txt b/rostests/apitests/winspool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c1c7e6a
--- /dev/null
@@ -0,0 +1,16 @@
+
+list(APPEND SOURCE
+    ClosePrinter.c
+    EnumPrinters.c
+    EnumPrintProcessorDatatypes.c
+    GetPrintProcessorDirectory.c
+    IsValidDevmode.c
+    OpenPrinter.c
+    StartDocPrinter.c
+    testlist.c)
+
+add_executable(winspool_apitest ${SOURCE})
+target_link_libraries(winspool_apitest wine ${PSEH_LIB})
+set_module_type(winspool_apitest win32cui)
+add_importlibs(winspool_apitest winspool msvcrt kernel32 ntdll)
+add_cd_file(TARGET winspool_apitest DESTINATION reactos/bin FOR all)
diff --git a/rostests/apitests/winspool/ClosePrinter.c b/rostests/apitests/winspool/ClosePrinter.c
new file mode 100644 (file)
index 0000000..86865b4
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * PROJECT:     ReactOS Print Spooler DLL API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for ClosePrinter
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+START_TEST(ClosePrinter)
+{
+    SetLastError(0xDEADBEEF);
+    ok(!ClosePrinter(NULL), "ClosePrinter returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "ClosePrinter returns error %lu!\n", GetLastError());
+}
diff --git a/rostests/apitests/winspool/EnumPrintProcessorDatatypes.c b/rostests/apitests/winspool/EnumPrintProcessorDatatypes.c
new file mode 100644 (file)
index 0000000..4259ea5
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * PROJECT:     ReactOS Print Spooler DLL API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for EnumPrintProcessorDatatypesA/EnumPrintProcessorDatatypesW
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+START_TEST(EnumPrintProcessorDatatypes)
+{
+    DWORD cbNeeded;
+    DWORD cbTemp;
+    DWORD dwReturned;
+    PDATATYPES_INFO_1W pDatatypesInfo1;
+
+    // Try with an invalid level, this needs to be caught first.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 0, NULL, 0, NULL, NULL), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_LEVEL, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+
+    // Now try with valid level, but no pcbNeeded and no pcReturned.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, NULL, NULL), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == RPC_X_NULL_REF_POINTER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+
+    // Now try with pcbNeeded and pcReturned, but give no Print Processor.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_UNKNOWN_PRINTPROCESSOR, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+
+    // Same error has to occur when looking for an invalid Print Processor.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, L"invalid", 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_UNKNOWN_PRINTPROCESSOR, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+
+    // Now get the required buffer size by supplying all information. This needs to fail with ERROR_INSUFFICIENT_BUFFER.
+    // This also verifies that the function is really doing a case-insensitive lookup.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, L"wInPrInT", 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+    ok(cbNeeded > 0, "cbNeeded is 0!\n");
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // Same error has to occur with a size to small.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, L"wInPrInT", 1, NULL, 1, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu!\n", GetLastError());
+    ok(cbNeeded > 0, "cbNeeded is 0!\n");
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // Now provide the demanded size, but no buffer.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, L"wInPrInT", 1, NULL, cbNeeded, &cbTemp, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+    ok(cbTemp == 0, "cbTemp is %lu!\n", cbTemp);
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // This also has to fail the same way when no Print Processor was given at all.
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, cbNeeded, &cbTemp, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+    ok(cbTemp == 0, "cbTemp is %lu!\n", cbTemp);
+    ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
+
+    // Finally use the function as intended and aim for success!
+    pDatatypesInfo1 = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
+    SetLastError(0xDEADBEEF);
+    ok(EnumPrintProcessorDatatypesW(NULL, L"wInPrInT", 1, (PBYTE)pDatatypesInfo1, cbNeeded, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns FALSE!\n");
+    ok(GetLastError() == ERROR_SUCCESS, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError());
+    HeapFree(GetProcessHeap(), 0, pDatatypesInfo1);
+}
diff --git a/rostests/apitests/winspool/EnumPrinters.c b/rostests/apitests/winspool/EnumPrinters.c
new file mode 100644 (file)
index 0000000..f199f60
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * PROJECT:     ReactOS Print Spooler DLL API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for EnumPrintersA/EnumPrintersW
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+START_TEST(EnumPrinters)
+{
+    DWORD cbNeeded;
+    DWORD cbTemp;
+    DWORD dwReturned;
+    PVOID pMem;
+    DWORD i;
+    DWORD dwValidLevels[] = { 0, 1, 2, 4, 5 };
+
+    // Level 5 is the highest supported under Windows Server 2003. Higher levels need to fail and leave the variables untouched!
+    cbNeeded = 0xDEADBEEF;
+    dwReturned = 0xDEADBEEF;
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintersW(0, NULL, 6, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_LEVEL, "EnumPrintersW returns error %lu!\n", GetLastError());
+    ok(cbNeeded == 0xDEADBEEF, "cbNeeded is %lu!\n", cbNeeded);
+    ok(dwReturned == 0xDEADBEEF, "dwReturned is %lu!\n", dwReturned);
+
+    // Same goes for level 3.
+    cbNeeded = 0xDEADBEEF;
+    dwReturned = 0xDEADBEEF;
+    SetLastError(0xDEADBEEF);
+    ok(!EnumPrintersW(0, NULL, 3, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_LEVEL, "EnumPrintersW returns error %lu!\n", GetLastError());
+    ok(cbNeeded == 0xDEADBEEF, "cbNeeded is %lu!\n", cbNeeded);
+    ok(dwReturned == 0xDEADBEEF, "dwReturned is %lu!\n", dwReturned);
+
+    // Try for all valid levels. Level 0 is valid here and returns the PRINTER_INFO_STRESS structure (documented in MS-RPRN).
+    for (i = 0; i < sizeof(dwValidLevels) / sizeof(DWORD); i++)
+    {
+        // Try with no valid arguments at all.
+        SetLastError(0xDEADBEEF);
+        ok(!EnumPrintersW(0, NULL, dwValidLevels[i], NULL, 0, NULL, NULL), "EnumPrintersW returns TRUE for Level %lu!\n", dwValidLevels[i]);
+        ok(GetLastError() == RPC_X_NULL_REF_POINTER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]);
+
+        // It has to succeed if we supply the required pointers and query no information.
+        SetLastError(0xDEADBEEF);
+        ok(EnumPrintersW(0, NULL, dwValidLevels[i], NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", dwValidLevels[i]);
+        ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]);
+        ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, dwValidLevels[i]);
+        ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]);
+
+        // This constant is from Windows 9x/ME times and mustn't work anymore.
+        SetLastError(0xDEADBEEF);
+        ok(EnumPrintersW(PRINTER_ENUM_DEFAULT, NULL, dwValidLevels[i], NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", dwValidLevels[i]);
+        ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]);
+        ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, dwValidLevels[i]);
+        ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]);
+
+        // Now things get interesting. Let's query the buffer size for information about the local printers.
+        SetLastError(0xDEADBEEF);
+        ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, dwValidLevels[i], NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", dwValidLevels[i]);
+        ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]);
+        ok(cbNeeded > 0, "cbNeeded is 0 for Level %lu!\n", dwValidLevels[i]);
+        ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]);
+
+        // Same error has to occur with a size to small.
+        SetLastError(0xDEADBEEF);
+        ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, dwValidLevels[i], NULL, 1, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", dwValidLevels[i]);
+        ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]);
+        ok(cbNeeded > 0, "cbNeeded is 0 for Level %lu!\n", dwValidLevels[i]);
+        ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]);
+
+        // Now provide the demanded size, but no buffer.
+        SetLastError(0xDEADBEEF);
+        ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, dwValidLevels[i], NULL, cbNeeded, &cbTemp, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", dwValidLevels[i]);
+        ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]);
+        ok(cbTemp == 0, "cbTemp is %lu for Level %lu!\n", cbTemp, dwValidLevels[i]);
+        ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]);
+
+        // Finally use the function as intended and aim for success!
+        pMem = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
+        SetLastError(0xDEADBEEF);
+        ok(EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, dwValidLevels[i], pMem, cbNeeded, &cbTemp, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", dwValidLevels[i]);
+        ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]);
+        HeapFree(GetProcessHeap(), 0, pMem);
+    }
+}
diff --git a/rostests/apitests/winspool/GetPrintProcessorDirectory.c b/rostests/apitests/winspool/GetPrintProcessorDirectory.c
new file mode 100644 (file)
index 0000000..04d76d2
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * PROJECT:     ReactOS Print Spooler DLL API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+START_TEST(GetPrintProcessorDirectory)
+{
+    DWORD cbNeeded;
+    DWORD cbTemp;
+    PWSTR pwszBuffer;
+
+    // Try with an invalid level, this needs to be caught first.
+    SetLastError(0xDEADBEEF);
+    ok(!GetPrintProcessorDirectoryW(NULL, NULL, 0, NULL, 0, NULL), "GetPrintProcessorDirectoryW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_LEVEL, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError());
+
+    // Now try with valid level, but no pcbNeeded.
+    SetLastError(0xDEADBEEF);
+    ok(!GetPrintProcessorDirectoryW(NULL, NULL, 1, NULL, 0, NULL), "GetPrintProcessorDirectoryW returns TRUE!\n");
+    ok(GetLastError() == RPC_X_NULL_REF_POINTER, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError());
+
+    // Try with an invalid environment as well.
+    SetLastError(0xDEADBEEF);
+    ok(!GetPrintProcessorDirectoryW(NULL, L"invalid", 1, NULL, 0, &cbNeeded), "GetPrintProcessorDirectoryW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_ENVIRONMENT, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError());
+    ok(cbNeeded == 0, "cbNeeded is %lu!\n", cbNeeded);
+
+    // Now get the required buffer size by supplying pcbNeeded. This needs to fail with ERROR_INSUFFICIENT_BUFFER.
+    SetLastError(0xDEADBEEF);
+    ok(!GetPrintProcessorDirectoryW(NULL, NULL, 1, NULL, 0, &cbNeeded), "GetPrintProcessorDirectoryW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError());
+    ok(cbNeeded > 0, "cbNeeded is 0!\n");
+
+    // Now provide the demanded size, but no buffer.
+    SetLastError(0xDEADBEEF);
+    ok(!GetPrintProcessorDirectoryW(NULL, NULL, 1, NULL, cbNeeded, &cbTemp), "GetPrintProcessorDirectoryW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError());
+    ok(cbTemp == 0, "cbNeeded is %lu!\n", cbNeeded);
+
+    // Same error has to occur with a size too small.
+    SetLastError(0xDEADBEEF);
+    ok(!GetPrintProcessorDirectoryW(NULL, NULL, 1, NULL, 1, &cbTemp), "GetPrintProcessorDirectoryW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError());
+    ok(cbTemp == 0, "cbNeeded is %lu!\n", cbNeeded);
+
+    // Finally use the function as intended and aim for success!
+    pwszBuffer = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
+    SetLastError(0xDEADBEEF);
+    ok(GetPrintProcessorDirectoryW(NULL, NULL, 1, (PBYTE)pwszBuffer, cbNeeded, &cbNeeded), "GetPrintProcessorDirectoryW returns FALSE!\n");
+    ok(GetLastError() == ERROR_SUCCESS, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError());
+    HeapFree(GetProcessHeap(), 0, pwszBuffer);
+}
diff --git a/rostests/apitests/winspool/IsValidDevmode.c b/rostests/apitests/winspool/IsValidDevmode.c
new file mode 100644 (file)
index 0000000..1e02d4b
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * PROJECT:     ReactOS Print Spooler DLL API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for IsValidDevmodeA/IsValidDevmodeW
+ * COPYRIGHT:   Copyright 2016 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+#include <wchar.h>
+
+typedef struct _MINIMUM_SIZE_TABLE
+{
+    DWORD dwField;
+    WORD wSize;
+}
+MINIMUM_SIZE_TABLE, *PMINIMUM_SIZE_TABLE;
+
+static MINIMUM_SIZE_TABLE MinimumSizeA[] = {
+    { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEA, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPanningHeight) },
+    { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEA, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPanningWidth) },
+    { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEA, dmDitherType) + RTL_FIELD_SIZE(DEVMODEA, dmDitherType) },
+    { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEA, dmMediaType) + RTL_FIELD_SIZE(DEVMODEA, dmMediaType) },
+    { DM_ICMINTENT, FIELD_OFFSET(DEVMODEA, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEA, dmICMIntent) },
+    { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEA, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEA, dmICMMethod) },
+    { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEA, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFrequency) },
+    { DM_NUP, FIELD_OFFSET(DEVMODEA, dmNup) + RTL_FIELD_SIZE(DEVMODEA, dmNup) },
+    { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEA, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFlags) },
+    { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEA, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPelsHeight) },
+    { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEA, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPelsWidth) },
+    { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEA, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEA, dmBitsPerPel) },
+    { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEA, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEA, dmLogPixels) },
+    { DM_FORMNAME, FIELD_OFFSET(DEVMODEA, dmFormName) + RTL_FIELD_SIZE(DEVMODEA, dmFormName) },
+    { DM_COLLATE, FIELD_OFFSET(DEVMODEA, dmCollate) + RTL_FIELD_SIZE(DEVMODEA, dmCollate) },
+    { DM_TTOPTION, FIELD_OFFSET(DEVMODEA, dmTTOption) + RTL_FIELD_SIZE(DEVMODEA, dmTTOption) },
+    { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEA, dmYResolution) + RTL_FIELD_SIZE(DEVMODEA, dmYResolution) },
+    { DM_DUPLEX, FIELD_OFFSET(DEVMODEA, dmDuplex) + RTL_FIELD_SIZE(DEVMODEA, dmDuplex) },
+    { DM_COLOR, FIELD_OFFSET(DEVMODEA, dmColor) + RTL_FIELD_SIZE(DEVMODEA, dmColor) },
+    { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEA, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFixedOutput) },
+    { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEA, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayOrientation) },
+    { DM_POSITION, FIELD_OFFSET(DEVMODEA, dmPosition) + RTL_FIELD_SIZE(DEVMODEA, dmPosition) },
+    { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEA, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEA, dmPrintQuality) },
+    { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEA, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEA, dmDefaultSource) },
+    { DM_COPIES, FIELD_OFFSET(DEVMODEA, dmCopies) + RTL_FIELD_SIZE(DEVMODEA, dmCopies) },
+    { DM_SCALE, FIELD_OFFSET(DEVMODEA, dmScale) + RTL_FIELD_SIZE(DEVMODEA, dmScale) },
+    { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEA, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPaperWidth) },
+    { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEA, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEA, dmPaperLength) },
+    { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEA, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEA, dmPaperSize) },
+    { DM_ORIENTATION, FIELD_OFFSET(DEVMODEA, dmOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmOrientation) },
+    { 0, FIELD_OFFSET(DEVMODEA, dmFields) + RTL_FIELD_SIZE(DEVMODEA, dmFields) }
+};
+
+static MINIMUM_SIZE_TABLE MinimumSizeW[] = {
+    { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEW, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPanningHeight) },
+    { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEW, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPanningWidth) },
+    { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEW, dmDitherType) + RTL_FIELD_SIZE(DEVMODEW, dmDitherType) },
+    { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEW, dmMediaType) + RTL_FIELD_SIZE(DEVMODEW, dmMediaType) },
+    { DM_ICMINTENT, FIELD_OFFSET(DEVMODEW, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEW, dmICMIntent) },
+    { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEW, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEW, dmICMMethod) },
+    { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEW, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFrequency) },
+    { DM_NUP, FIELD_OFFSET(DEVMODEW, dmNup) + RTL_FIELD_SIZE(DEVMODEW, dmNup) },
+    { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEW, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFlags) },
+    { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEW, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPelsHeight) },
+    { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEW, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPelsWidth) },
+    { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEW, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEW, dmBitsPerPel) },
+    { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEW, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEW, dmLogPixels) },
+    { DM_FORMNAME, FIELD_OFFSET(DEVMODEW, dmFormName) + RTL_FIELD_SIZE(DEVMODEW, dmFormName) },
+    { DM_COLLATE, FIELD_OFFSET(DEVMODEW, dmCollate) + RTL_FIELD_SIZE(DEVMODEW, dmCollate) },
+    { DM_TTOPTION, FIELD_OFFSET(DEVMODEW, dmTTOption) + RTL_FIELD_SIZE(DEVMODEW, dmTTOption) },
+    { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEW, dmYResolution) + RTL_FIELD_SIZE(DEVMODEW, dmYResolution) },
+    { DM_DUPLEX, FIELD_OFFSET(DEVMODEW, dmDuplex) + RTL_FIELD_SIZE(DEVMODEW, dmDuplex) },
+    { DM_COLOR, FIELD_OFFSET(DEVMODEW, dmColor) + RTL_FIELD_SIZE(DEVMODEW, dmColor) },
+    { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEW, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFixedOutput) },
+    { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEW, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayOrientation) },
+    { DM_POSITION, FIELD_OFFSET(DEVMODEW, dmPosition) + RTL_FIELD_SIZE(DEVMODEW, dmPosition) },
+    { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEW, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEW, dmPrintQuality) },
+    { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEW, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEW, dmDefaultSource) },
+    { DM_COPIES, FIELD_OFFSET(DEVMODEW, dmCopies) + RTL_FIELD_SIZE(DEVMODEW, dmCopies) },
+    { DM_SCALE, FIELD_OFFSET(DEVMODEW, dmScale) + RTL_FIELD_SIZE(DEVMODEW, dmScale) },
+    { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEW, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPaperWidth) },
+    { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEW, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEW, dmPaperLength) },
+    { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEW, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEW, dmPaperSize) },
+    { DM_ORIENTATION, FIELD_OFFSET(DEVMODEW, dmOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmOrientation) },
+    { 0, FIELD_OFFSET(DEVMODEW, dmFields) + RTL_FIELD_SIZE(DEVMODEW, dmFields) }
+};
+
+
+
+START_TEST(IsValidDevmodeA)
+{
+    DEVMODEA DevMode;
+    PMINIMUM_SIZE_TABLE pTable = MinimumSizeA;
+
+    // Give no Devmode at all, this has to fail without crashing.
+    SetLastError(0xDEADBEEF);
+    ok(!IsValidDevmodeA(NULL, sizeof(DEVMODEA)), "IsValidDevmodeA returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_DATA, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+
+    // Give a zeroed Devmode, this has to fail, because dmSize isn't set.
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+    SetLastError(0xDEADBEEF);
+    ok(!IsValidDevmodeA(&DevMode, sizeof(DEVMODEA)), "IsValidDevmodeA returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_DATA, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+
+    // Now set only the dmSize member, IsValidDevmodeA should return TRUE now. The Last Error isn't touched again.
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+    DevMode.dmSize = sizeof(DEVMODEA);
+    SetLastError(0xDEADBEEF);
+    ok(IsValidDevmodeA(&DevMode, sizeof(DEVMODEA)), "IsValidDevmodeA returns FALSE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+
+    // IsValidDevmodeA should also succeed if the DevMode appears to be larger.
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+    DevMode.dmSize = sizeof(DEVMODEA) + 1;
+    SetLastError(0xDEADBEEF);
+    ok(IsValidDevmodeA(&DevMode, sizeof(DEVMODEA) + 1), "IsValidDevmodeA returns FALSE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+
+    // The DevmodeSize parameter may be larger than dmSize, but not the other way round!
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+    DevMode.dmSize = sizeof(DEVMODEA);
+    SetLastError(0xDEADBEEF);
+    ok(IsValidDevmodeA(&DevMode, sizeof(DEVMODEA) + 1), "IsValidDevmodeA returns FALSE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+    DevMode.dmSize = sizeof(DEVMODEA) + 1;
+    SetLastError(0xDEADBEEF);
+    ok(!IsValidDevmodeA(&DevMode, sizeof(DEVMODEA)), "IsValidDevmodeA returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_DATA, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+
+    // dmDriverExtra is also taken into account.
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+    DevMode.dmSize = sizeof(DEVMODEA);
+    DevMode.dmDriverExtra = 1;
+    SetLastError(0xDEADBEEF);
+    ok(!IsValidDevmodeA(&DevMode, sizeof(DEVMODEA)), "IsValidDevmodeA returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_DATA, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+
+    SetLastError(0xDEADBEEF);
+    ok(IsValidDevmodeA(&DevMode, sizeof(DEVMODEA) + 1), "IsValidDevmodeA returns FALSE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+
+    // dmSize must be a multiple of 4 if any dmDriverExtra is given.
+    for (DevMode.dmDriverExtra = 1; DevMode.dmDriverExtra <= 4; DevMode.dmDriverExtra++)
+    {
+        for (DevMode.dmSize = sizeof(DEVMODEA); DevMode.dmSize < sizeof(DEVMODEA) + 4; DevMode.dmSize++)
+        {
+            BOOL bExpected = (DevMode.dmSize % 4 == 0);
+            DWORD dwExpectedError = (bExpected ? 0xDEADBEEF : ERROR_INVALID_DATA);
+
+            SetLastError(0xDEADBEEF);
+            ok(IsValidDevmodeA(&DevMode, DevMode.dmSize + DevMode.dmDriverExtra) == bExpected, "IsValidDevmodeA returns %d!\n", !bExpected);
+            ok(GetLastError() == dwExpectedError, "IsValidDevmodeA returns error %lu!\n", GetLastError());
+        }
+    }
+
+    // IsValidDevmodeA also fixes missing null-terminations if the corresponding fields are used.
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+    DevMode.dmSize = sizeof(DEVMODEA);
+    memset(DevMode.dmDeviceName, 'A', 32);
+    memset(DevMode.dmFormName, 'A', 32);
+    ok(IsValidDevmodeA(&DevMode, DevMode.dmSize), "IsValidDevmodeA returns FALSE!\n");
+    ok(DevMode.dmDeviceName[31] == '\0', "Expected dmDeviceName to be '\\0', but is '%c'\n", DevMode.dmDeviceName[31]);
+    ok(DevMode.dmFormName[31] == 'A', "Expected dmFormName to be 'A', but is '%c'\n", DevMode.dmDeviceName[31]);
+
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+    DevMode.dmSize = sizeof(DEVMODEA);
+    DevMode.dmFields = DM_FORMNAME;
+    memset(DevMode.dmDeviceName, 'A', 32);
+    memset(DevMode.dmFormName, 'A', 32);
+    ok(IsValidDevmodeA(&DevMode, DevMode.dmSize), "IsValidDevmodeA returns FALSE!\n");
+    ok(DevMode.dmDeviceName[31] == '\0', "Expected dmDeviceName to be '\\0', but is '%c'\n", DevMode.dmDeviceName[31]);
+    ok(DevMode.dmFormName[31] == '\0', "Expected dmFormName to be '\\0', but is '%c'\n", DevMode.dmDeviceName[31]);
+
+    // Depending on which fields are given in dmFields, the minimum value for dmSize is different.
+    ZeroMemory(&DevMode, sizeof(DEVMODEA));
+
+    do
+    {
+        DevMode.dmFields = pTable->dwField;
+        DevMode.dmSize = pTable->wSize;
+
+        // This size should succeed!
+        ok(IsValidDevmodeA(&DevMode, DevMode.dmSize), "IsValidDevmodeA returns FALSE!\n");
+
+        // One byte less should not succeed!
+        DevMode.dmSize--;
+        ok(!IsValidDevmodeA(&DevMode, DevMode.dmSize), "IsValidDevmodeA returns TRUE!\n");
+
+        pTable++;
+    }
+    while (pTable->dwField);
+}
+
+START_TEST(IsValidDevmodeW)
+{
+    DEVMODEW DevMode;
+    PMINIMUM_SIZE_TABLE pTable = MinimumSizeW;
+
+    // Give no Devmode at all, this has to fail without crashing.
+    SetLastError(0xDEADBEEF);
+    ok(!IsValidDevmodeW(NULL, sizeof(DEVMODEW)), "IsValidDevmodeW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_DATA, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+
+    // Give a zeroed Devmode, this has to fail, because dmSize isn't set.
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+    SetLastError(0xDEADBEEF);
+    ok(!IsValidDevmodeW(&DevMode, sizeof(DEVMODEW)), "IsValidDevmodeW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_DATA, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+
+    // Now set only the dmSize member, IsValidDevmodeW should return TRUE now. The Last Error isn't touched again.
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+    DevMode.dmSize = sizeof(DEVMODEW);
+    SetLastError(0xDEADBEEF);
+    ok(IsValidDevmodeW(&DevMode, sizeof(DEVMODEW)), "IsValidDevmodeW returns FALSE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+
+    // IsValidDevmodeW should also succeed if the DevMode appears to be larger.
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+    DevMode.dmSize = sizeof(DEVMODEW) + 1;
+    SetLastError(0xDEADBEEF);
+    ok(IsValidDevmodeW(&DevMode, sizeof(DEVMODEW) + 1), "IsValidDevmodeW returns FALSE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+    
+    // The DevmodeSize parameter may be larger than dmSize, but not the other way round!
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+    DevMode.dmSize = sizeof(DEVMODEW);
+    SetLastError(0xDEADBEEF);
+    ok(IsValidDevmodeW(&DevMode, sizeof(DEVMODEW) + 1), "IsValidDevmodeW returns FALSE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+    DevMode.dmSize = sizeof(DEVMODEW) + 1;
+    SetLastError(0xDEADBEEF);
+    ok(!IsValidDevmodeW(&DevMode, sizeof(DEVMODEW)), "IsValidDevmodeW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_DATA, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+
+    // dmDriverExtra is also taken into account.
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+    DevMode.dmSize = sizeof(DEVMODEW);
+    DevMode.dmDriverExtra = 1;
+    SetLastError(0xDEADBEEF);
+    ok(!IsValidDevmodeW(&DevMode, sizeof(DEVMODEW)), "IsValidDevmodeW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_DATA, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+
+    SetLastError(0xDEADBEEF);
+    ok(IsValidDevmodeW(&DevMode, sizeof(DEVMODEW) + 1), "IsValidDevmodeW returns FALSE!\n");
+    ok(GetLastError() == 0xDEADBEEF, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+
+    // dmSize must be a multiple of 4 if any dmDriverExtra is given.
+    for (DevMode.dmDriverExtra = 1; DevMode.dmDriverExtra <= 4; DevMode.dmDriverExtra++)
+    {
+        for (DevMode.dmSize = sizeof(DEVMODEW); DevMode.dmSize < sizeof(DEVMODEW) + 4; DevMode.dmSize++)
+        {
+            BOOL bExpected = (DevMode.dmSize % 4 == 0);
+            DWORD dwExpectedError = (bExpected ? 0xDEADBEEF : ERROR_INVALID_DATA);
+
+            SetLastError(0xDEADBEEF);
+            ok(IsValidDevmodeW(&DevMode, DevMode.dmSize + DevMode.dmDriverExtra) == bExpected, "IsValidDevmodeW returns %d!\n", !bExpected);
+            ok(GetLastError() == dwExpectedError, "IsValidDevmodeW returns error %lu!\n", GetLastError());
+        }
+    }
+
+    // IsValidDevmodeW also fixes missing null-terminations if the corresponding fields are used.
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+    DevMode.dmSize = sizeof(DEVMODEW);
+    wmemset(DevMode.dmDeviceName, L'A', 32);
+    wmemset(DevMode.dmFormName, L'A', 32);
+    ok(IsValidDevmodeW(&DevMode, DevMode.dmSize), "IsValidDevmodeW returns FALSE!\n");
+    ok(DevMode.dmDeviceName[31] == L'\0', "Expected dmDeviceName to be '\\0', but is '%C'\n", DevMode.dmDeviceName[31]);
+    ok(DevMode.dmFormName[31] == L'A', "Expected dmFormName to be 'A', but is '%C'\n", DevMode.dmDeviceName[31]);
+
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+    DevMode.dmSize = sizeof(DEVMODEW);
+    DevMode.dmFields = DM_FORMNAME;
+    wmemset(DevMode.dmDeviceName, L'A', 32);
+    wmemset(DevMode.dmFormName, L'A', 32);
+    ok(IsValidDevmodeW(&DevMode, DevMode.dmSize), "IsValidDevmodeW returns FALSE!\n");
+    ok(DevMode.dmDeviceName[31] == L'\0', "Expected dmDeviceName to be '\\0', but is '%C'\n", DevMode.dmDeviceName[31]);
+    ok(DevMode.dmFormName[31] == L'\0', "Expected dmFormName to be '\\0', but is '%C'\n", DevMode.dmDeviceName[31]);
+
+    // Depending on which fields are given in dmFields, the minimum value for dmSize is different.
+    ZeroMemory(&DevMode, sizeof(DEVMODEW));
+
+    do
+    {
+        DevMode.dmFields = pTable->dwField;
+        DevMode.dmSize = pTable->wSize;
+
+        // This size should succeed!
+        ok(IsValidDevmodeW(&DevMode, DevMode.dmSize), "IsValidDevmodeW returns FALSE!\n");
+
+        // One byte less should not succeed!
+        DevMode.dmSize--;
+        ok(!IsValidDevmodeW(&DevMode, DevMode.dmSize), "IsValidDevmodeW returns TRUE!\n");
+
+        pTable++;
+    }
+    while (pTable->dwField);
+}
diff --git a/rostests/apitests/winspool/OpenPrinter.c b/rostests/apitests/winspool/OpenPrinter.c
new file mode 100644 (file)
index 0000000..dfc5dde
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * PROJECT:     ReactOS Print Spooler DLL API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for OpenPrinterA/OpenPrinterW
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+START_TEST(OpenPrinter)
+{
+    HANDLE hPrinter;
+
+    // Give no handle at all, this has to fail
+    SetLastError(0xDEADBEEF);
+    ok(!OpenPrinterW(NULL, NULL, NULL), "OpenPrinterW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "OpenPrinterW returns error %lu!\n", GetLastError());
+
+    // Open a handle to the local print server
+    SetLastError(0xDEADBEEF);
+    ok(OpenPrinterW(NULL, &hPrinter, NULL), "OpenPrinterW returns FALSE!\n");
+    ok(GetLastError() == ERROR_SUCCESS, "OpenPrinterW returns error %lu!\n", GetLastError());
+    ClosePrinter(hPrinter);
+}
diff --git a/rostests/apitests/winspool/StartDocPrinter.c b/rostests/apitests/winspool/StartDocPrinter.c
new file mode 100644 (file)
index 0000000..b4b5c43
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * PROJECT:     ReactOS Print Spooler DLL API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for StartDocPrinterA/StartDocPrinterW
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+START_TEST(StartDocPrinter)
+{
+    DWORD dwResult;
+    DOCINFOW docInfo = { 0 };
+
+    SetLastError(0xDEADBEEF);
+    dwResult = StartDocPrinterW(NULL, 0, NULL);
+    ok(dwResult == 0, "StartDocPrinterW returns %lu!\n", dwResult);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "StartDocPrinter returns error %lu!\n", GetLastError());
+
+    SetLastError(0xDEADBEEF);
+    dwResult = StartDocPrinterW(NULL, 1, NULL);
+    ok(dwResult == 0, "StartDocPrinterW returns %lu!\n", dwResult);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "StartDocPrinter returns error %lu!\n", GetLastError());
+
+    SetLastError(0xDEADBEEF);
+    dwResult = StartDocPrinterW(NULL, 0, (LPBYTE)&docInfo);
+    ok(dwResult == 0, "StartDocPrinterW returns %lu!\n", dwResult);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "StartDocPrinter returns error %lu!\n", GetLastError());
+
+    SetLastError(0xDEADBEEF);
+    dwResult = StartDocPrinterW(NULL, 1, (LPBYTE)&docInfo);
+    ok(dwResult == 0, "StartDocPrinterW returns %lu!\n", dwResult);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "StartDocPrinter returns error %lu!\n", GetLastError());
+
+    /// ERROR_INVALID_LEVEL with correct handle but invalid Level
+}
diff --git a/rostests/apitests/winspool/testlist.c b/rostests/apitests/winspool/testlist.c
new file mode 100644 (file)
index 0000000..189eb7c
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * PROJECT:     ReactOS Print Spooler DLL API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Test list
+ * COPYRIGHT:   Copyright 2015-2016 Colin Finck <colin@reactos.org>
+ */
+
+#define __ROS_LONG64__
+
+#define STANDALONE
+#include <apitest.h>
+
+extern void func_ClosePrinter(void);
+extern void func_EnumPrinters(void);
+extern void func_EnumPrintProcessorDatatypes(void);
+extern void func_GetPrintProcessorDirectory(void);
+extern void func_IsValidDevmodeA(void);
+extern void func_IsValidDevmodeW(void);
+extern void func_OpenPrinter(void);
+extern void func_StartDocPrinter(void);
+
+const struct test winetest_testlist[] =
+{
+    { "ClosePrinter", func_ClosePrinter },
+    { "EnumPrinters", func_EnumPrinters },
+    { "EnumPrintProcessorDatatypes", func_EnumPrintProcessorDatatypes },
+    { "GetPrintProcessorDirectory", func_GetPrintProcessorDirectory },
+    { "IsValidDevmodeA", func_IsValidDevmodeA },
+    { "IsValidDevmodeW", func_IsValidDevmodeW },
+    { "OpenPrinter", func_OpenPrinter },
+    { "StartDocPrinter", func_StartDocPrinter },
+
+    { 0, 0 }
+};
index 2d938cb..6f7b9ad 100644 (file)
@@ -112,7 +112,7 @@ add_subdirectory(windowscodecsext)
 add_subdirectory(winhttp)
 add_subdirectory(wininet)
 add_subdirectory(winmm)
-add_subdirectory(winspool)
+#add_subdirectory(winspool)
 add_subdirectory(wintrust)
 add_subdirectory(wlanapi)
 add_subdirectory(wldap32)