[LOCALSPL] [SPOOLSV] [WINSPOOL]
authorColin Finck <colin@reactos.org>
Sun, 30 Apr 2017 15:12:53 +0000 (15:12 +0000)
committerColin Finck <colin@reactos.org>
Sun, 30 Apr 2017 15:12:53 +0000 (15:12 +0000)
- Refactor the code returning PRINTER_INFO_* yet another time to support both EnumPrinters and GetPrinter calls.
- Implement support for PRINTER_INFO_1 through PRINTER_INFO_9 as well as the mostly unknown PRINTER_INFO_STRESS (level 0) structure and return as much information as we can.
- Implement GetPrinterW / LocalGetPrinter.

The Printers Shell folder in Explorer now shows our "Dummy Printer on LPT1" and we pass all 291 winspool:EnumPrinters API tests :)

svn path=/trunk/; revision=74433

reactos/win32ss/printing/base/spoolsv/printers.c
reactos/win32ss/printing/base/winspool/precomp.h
reactos/win32ss/printing/base/winspool/printers.c
reactos/win32ss/printing/include/spoolss.h
reactos/win32ss/printing/providers/localspl/main.c
reactos/win32ss/printing/providers/localspl/precomp.h
reactos/win32ss/printing/providers/localspl/printers.c

index 1468faa..75872d2 100644 (file)
@@ -8,23 +8,33 @@
 #include "precomp.h"
 
 static void
-_MarshallDownPrinterInfo(PBYTE pPrinterInfo, DWORD Level)
+_MarshallDownPrinterInfo(PBYTE* ppPrinterInfo, DWORD Level)
 {
-    PPRINTER_INFO_1W pPrinterInfo1;
-    PPRINTER_INFO_2W pPrinterInfo2;
-
     // Replace absolute pointer addresses in the output by relative offsets.
-    if (Level == 1)
+    if (Level == 0)
     {
-        pPrinterInfo1 = (PPRINTER_INFO_1W)pPrinterInfo;
+        PPRINTER_INFO_STRESS pPrinterInfo0 = (PPRINTER_INFO_STRESS)(*ppPrinterInfo);
+
+        pPrinterInfo0->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo0->pPrinterName - (ULONG_PTR)pPrinterInfo0);
+
+        if (pPrinterInfo0->pServerName)
+            pPrinterInfo0->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo0->pServerName - (ULONG_PTR)pPrinterInfo0);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_STRESS);
+    }
+    else if (Level == 1)
+    {
+        PPRINTER_INFO_1W pPrinterInfo1 = (PPRINTER_INFO_1W)(*ppPrinterInfo);
 
         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);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_1W);
     }
     else if (Level == 2)
     {
-        pPrinterInfo2 = (PPRINTER_INFO_2W)pPrinterInfo;
+        PPRINTER_INFO_2W pPrinterInfo2 = (PPRINTER_INFO_2W)(*ppPrinterInfo);
 
         pPrinterInfo2->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPrinterName - (ULONG_PTR)pPrinterInfo2);
         pPrinterInfo2->pShareName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pShareName - (ULONG_PTR)pPrinterInfo2);
@@ -42,7 +52,66 @@ _MarshallDownPrinterInfo(PBYTE pPrinterInfo, DWORD Level)
             pPrinterInfo2->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pServerName - (ULONG_PTR)pPrinterInfo2);
 
         if (pPrinterInfo2->pSecurityDescriptor)
-            pPrinterInfo2->pSecurityDescriptor = (PWSTR)((ULONG_PTR)pPrinterInfo2->pSecurityDescriptor - (ULONG_PTR)pPrinterInfo2);
+            pPrinterInfo2->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)((ULONG_PTR)pPrinterInfo2->pSecurityDescriptor - (ULONG_PTR)pPrinterInfo2);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_2W);
+    }
+    else if (Level == 3)
+    {
+        PPRINTER_INFO_3 pPrinterInfo3 = (PPRINTER_INFO_3)(*ppPrinterInfo);
+
+        pPrinterInfo3->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)((ULONG_PTR)pPrinterInfo3->pSecurityDescriptor - (ULONG_PTR)pPrinterInfo3);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_3);
+    }
+    else if (Level == 4)
+    {
+        PPRINTER_INFO_4W pPrinterInfo4 = (PPRINTER_INFO_4W)(*ppPrinterInfo);
+
+        pPrinterInfo4->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo4->pPrinterName - (ULONG_PTR)pPrinterInfo4);
+
+        if (pPrinterInfo4->pServerName)
+            pPrinterInfo4->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo4->pServerName - (ULONG_PTR)pPrinterInfo4);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_4W);
+    }
+    else if (Level == 5)
+    {
+        PPRINTER_INFO_5W pPrinterInfo5 = (PPRINTER_INFO_5W)(*ppPrinterInfo);
+
+        pPrinterInfo5->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo5->pPrinterName - (ULONG_PTR)pPrinterInfo5);
+        pPrinterInfo5->pPortName = (PWSTR)((ULONG_PTR)pPrinterInfo5->pPortName - (ULONG_PTR)pPrinterInfo5);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_5W);
+    }
+    else if (Level == 6)
+    {
+        *ppPrinterInfo += sizeof(PRINTER_INFO_6);
+    }
+    else if (Level == 7)
+    {
+        PPRINTER_INFO_7W pPrinterInfo7 = (PPRINTER_INFO_7W)(*ppPrinterInfo);
+
+        if (pPrinterInfo7->pszObjectGUID)
+            pPrinterInfo7->pszObjectGUID = (PWSTR)((ULONG_PTR)pPrinterInfo7->pszObjectGUID - (ULONG_PTR)pPrinterInfo7);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_7W);
+    }
+    else if (Level == 8)
+    {
+        PPRINTER_INFO_8W pPrinterInfo8 = (PPRINTER_INFO_8W)(*ppPrinterInfo);
+
+        pPrinterInfo8->pDevMode = (PDEVMODEW)((ULONG_PTR)pPrinterInfo8->pDevMode - (ULONG_PTR)pPrinterInfo8);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_8W);
+    }
+    else if (Level == 9)
+    {
+        PPRINTER_INFO_9W pPrinterInfo9 = (PPRINTER_INFO_9W)(*ppPrinterInfo);
+
+        pPrinterInfo9->pDevMode = (PDEVMODEW)((ULONG_PTR)pPrinterInfo9->pDevMode - (ULONG_PTR)pPrinterInfo9);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_9W);
     }
 }
 
@@ -155,16 +224,8 @@ _RpcEnumPrinters(DWORD Flags, WINSPOOL_HANDLE Name, DWORD Level, BYTE* pPrinterE
         DWORD i;
         PBYTE p = pPrinterEnumAligned;
 
-        // 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);
-        }
+            _MarshallDownPrinterInfo(&p, Level);
     }
 
     RpcRevertToSelf();
@@ -183,8 +244,30 @@ _RpcFlushPrinter(WINSPOOL_PRINTER_HANDLE hPrinter, BYTE* pBuf, DWORD cbBuf, DWOR
 DWORD
 _RpcGetPrinter(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD Level, BYTE* pPrinter, DWORD cbBuf, DWORD* pcbNeeded)
 {
-    UNIMPLEMENTED;
-    return ERROR_INVALID_FUNCTION;
+    DWORD dwErrorCode;
+    PBYTE pPrinterAligned;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    pPrinterAligned = AlignRpcPtr(pPrinter, &cbBuf);
+    GetPrinterW(hPrinter, Level, pPrinterAligned, cbBuf, pcbNeeded);
+    dwErrorCode = GetLastError();
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        PBYTE p = pPrinterAligned;
+        _MarshallDownPrinterInfo(&p, Level);
+    }
+
+    RpcRevertToSelf();
+    UndoAlignRpcPtr(pPrinter, pPrinterAligned, cbBuf, pcbNeeded);
+
+    return dwErrorCode;
 }
 
 DWORD
index df5588a..f5fc05e 100644 (file)
@@ -2,7 +2,7 @@
  * 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>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #ifndef _PRECOMP_H
@@ -16,6 +16,8 @@
 #include <winspool.h>
 #include <winspool_c.h>
 
+#include <spoolss.h>
+
 #include <wine/debug.h>
 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
 
index ffa965f..a34a43f 100644 (file)
@@ -8,23 +8,33 @@
 #include "precomp.h"
 
 static void
-_MarshallUpPrinterInfo(PBYTE pPrinterInfo, DWORD Level)
+_MarshallUpPrinterInfo(PBYTE* ppPrinterInfo, DWORD Level)
 {
-    PPRINTER_INFO_1W pPrinterInfo1;
-    PPRINTER_INFO_2W pPrinterInfo2;
+    // Replace relative offset addresses in the output by absolute pointers and advance to the next structure.
+    if (Level == 0)
+    {
+        PPRINTER_INFO_STRESS pPrinterInfo0 = (PPRINTER_INFO_STRESS)(*ppPrinterInfo);
+
+        pPrinterInfo0->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo0->pPrinterName + (ULONG_PTR)pPrinterInfo0);
 
-    // Replace relative offset addresses in the output by absolute pointers.
-    if (Level == 1)
+        if (pPrinterInfo0->pServerName)
+            pPrinterInfo0->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo0->pServerName + (ULONG_PTR)pPrinterInfo0);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_STRESS);
+    }
+    else if (Level == 1)
     {
-        pPrinterInfo1 = (PPRINTER_INFO_1W)pPrinterInfo;
+        PPRINTER_INFO_1W pPrinterInfo1 = (PPRINTER_INFO_1W)(*ppPrinterInfo);
 
         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);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_1W);
     }
     else if (Level == 2)
     {
-        pPrinterInfo2 = (PPRINTER_INFO_2W)pPrinterInfo;
+        PPRINTER_INFO_2W pPrinterInfo2 = (PPRINTER_INFO_2W)(*ppPrinterInfo);
 
         pPrinterInfo2->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPrinterName + (ULONG_PTR)pPrinterInfo2);
         pPrinterInfo2->pShareName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pShareName + (ULONG_PTR)pPrinterInfo2);
@@ -42,7 +52,66 @@ _MarshallUpPrinterInfo(PBYTE pPrinterInfo, DWORD Level)
             pPrinterInfo2->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pServerName + (ULONG_PTR)pPrinterInfo2);
 
         if (pPrinterInfo2->pSecurityDescriptor)
-            pPrinterInfo2->pSecurityDescriptor = (PWSTR)((ULONG_PTR)pPrinterInfo2->pSecurityDescriptor + (ULONG_PTR)pPrinterInfo2);
+            pPrinterInfo2->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)((ULONG_PTR)pPrinterInfo2->pSecurityDescriptor + (ULONG_PTR)pPrinterInfo2);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_2W);
+    }
+    else if (Level == 3)
+    {
+        PPRINTER_INFO_3 pPrinterInfo3 = (PPRINTER_INFO_3)(*ppPrinterInfo);
+
+        pPrinterInfo3->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)((ULONG_PTR)pPrinterInfo3->pSecurityDescriptor + (ULONG_PTR)pPrinterInfo3);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_3);
+    }
+    else if (Level == 4)
+    {
+        PPRINTER_INFO_4W pPrinterInfo4 = (PPRINTER_INFO_4W)(*ppPrinterInfo);
+
+        pPrinterInfo4->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo4->pPrinterName + (ULONG_PTR)pPrinterInfo4);
+
+        if (pPrinterInfo4->pServerName)
+            pPrinterInfo4->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo4->pServerName + (ULONG_PTR)pPrinterInfo4);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_4W);
+    }
+    else if (Level == 5)
+    {
+        PPRINTER_INFO_5W pPrinterInfo5 = (PPRINTER_INFO_5W)(*ppPrinterInfo);
+
+        pPrinterInfo5->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo5->pPrinterName + (ULONG_PTR)pPrinterInfo5);
+        pPrinterInfo5->pPortName = (PWSTR)((ULONG_PTR)pPrinterInfo5->pPortName + (ULONG_PTR)pPrinterInfo5);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_5W);
+    }
+    else if (Level == 6)
+    {
+        *ppPrinterInfo += sizeof(PRINTER_INFO_6);
+    }
+    else if (Level == 7)
+    {
+        PPRINTER_INFO_7W pPrinterInfo7 = (PPRINTER_INFO_7W)(*ppPrinterInfo);
+
+        if (pPrinterInfo7->pszObjectGUID)
+            pPrinterInfo7->pszObjectGUID = (PWSTR)((ULONG_PTR)pPrinterInfo7->pszObjectGUID + (ULONG_PTR)pPrinterInfo7);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_7W);
+    }
+    else if (Level == 8)
+    {
+        PPRINTER_INFO_8W pPrinterInfo8 = (PPRINTER_INFO_8W)(*ppPrinterInfo);
+
+        pPrinterInfo8->pDevMode = (PDEVMODEW)((ULONG_PTR)pPrinterInfo8->pDevMode + (ULONG_PTR)pPrinterInfo8);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_8W);
+    }
+    else if (Level == 9)
+    {
+        PPRINTER_INFO_9W pPrinterInfo9 = (PPRINTER_INFO_9W)(*ppPrinterInfo);
+
+        pPrinterInfo9->pDevMode = (PDEVMODEW)((ULONG_PTR)pPrinterInfo9->pDevMode + (ULONG_PTR)pPrinterInfo9);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_9W);
     }
 }
 
@@ -306,8 +375,6 @@ BOOL WINAPI
 EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
 {
     DWORD dwErrorCode;
-    DWORD i;
-    PBYTE p = pPrinterEnum;
 
     // Dismiss invalid levels already at this point.
     if (Level == 3 || Level > 5)
@@ -333,16 +400,11 @@ EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cb
 
     if (dwErrorCode == ERROR_SUCCESS)
     {
-        // Replace relative offset addresses in the output by absolute pointers.
-        for (i = 0; i < *pcReturned; i++)
-        {
-            _MarshallUpPrinterInfo(p, Level);
+        DWORD i;
+        PBYTE p = pPrinterEnum;
 
-            if (Level == 1)
-                p += sizeof(PRINTER_INFO_1W);
-            else if (Level == 2)
-                p += sizeof(PRINTER_INFO_2W);
-        }
+        for (i = 0; i < *pcReturned; i++)
+            _MarshallUpPrinterInfo(&p, Level);
     }
 
 Cleanup:
@@ -383,7 +445,39 @@ GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDri
 BOOL WINAPI
 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
 {
-    return FALSE;
+    DWORD dwErrorCode;
+
+    // Dismiss invalid levels already at this point.
+    if (Level > 9)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    if (cbBuf && pPrinter)
+        ZeroMemory(pPrinter, cbBuf);
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcGetPrinter(hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        PBYTE p = pPrinter;
+        _MarshallUpPrinterInfo(&p, Level);
+    }
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
index bbbbee9..d877baa 100644 (file)
@@ -19,6 +19,41 @@ typedef struct _MARSHALL_DOWN_INFO
 }
 MARSHALL_DOWN_INFO, *PMARSHALL_DOWN_INFO;
 
+/** From MS-RPRN, 2.2.1.10.1 */
+typedef struct _PRINTER_INFO_STRESS
+{
+    PWSTR pPrinterName;
+    PWSTR 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;
+}
+PRINTER_INFO_STRESS, *PPRINTER_INFO_STRESS;
+
 PVOID WINAPI AlignRpcPtr(PVOID pBuffer, PDWORD pcbBuffer);
 PWSTR WINAPI AllocSplStr(PCWSTR pwszInput);
 PVOID WINAPI DllAllocSplMem(DWORD dwBytes);
index c9c424a..4b2e75a 100644 (file)
@@ -31,7 +31,7 @@ static const PRINTPROVIDOR _PrintProviderFunctions = {
     NULL,                                       // fpAddPrinter
     NULL,                                       // fpDeletePrinter
     NULL,                                       // fpSetPrinter
-    NULL,                                       // fpGetPrinter
+    LocalGetPrinter,                            // fpGetPrinter
     LocalEnumPrinters,                          // fpEnumPrinters
     NULL,                                       // fpAddPrinterDriver
     NULL,                                       // fpEnumPrinterDrivers
index 699ba02..b57fd65 100644 (file)
@@ -2,7 +2,7 @@
  * 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>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #ifndef _PRECOMP_H
@@ -277,6 +277,7 @@ BOOL WINAPI LocalEnumPorts(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf,
 extern SKIPLIST PrinterList;
 BOOL InitializePrinterList();
 BOOL WINAPI LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
+BOOL WINAPI LocalGetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded);
 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);
index 339c138..435c612 100644 (file)
 // Global Variables
 SKIPLIST PrinterList;
 
+// Forward Declarations
+static void _LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_STRESS* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_4W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_8W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_9W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+
 // Local Constants
+typedef void (*PLocalGetPrinterLevelFunc)(PLOCAL_PRINTER, PVOID, PBYTE*, PDWORD, DWORD, PWSTR);
+
+static const PLocalGetPrinterLevelFunc pfnGetPrinterLevels[] = {
+    (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel0,
+    (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel1,
+    (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel2,
+    (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel3,
+    (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel4,
+    (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel5,
+    (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel6,
+    (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel7,
+    (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel8,
+    (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel9
+};
+
+static DWORD dwPrinterInfo0Offsets[] = {
+    FIELD_OFFSET(PRINTER_INFO_STRESS, pPrinterName),
+    MAXDWORD
+};
+
 static DWORD dwPrinterInfo1Offsets[] = {
     FIELD_OFFSET(PRINTER_INFO_1W, pName),
     FIELD_OFFSET(PRINTER_INFO_1W, pComment),
@@ -18,6 +50,31 @@ static DWORD dwPrinterInfo1Offsets[] = {
     MAXDWORD
 };
 
+static DWORD dwPrinterInfo2Offsets[] = {
+    FIELD_OFFSET(PRINTER_INFO_2W, pPrinterName),
+    FIELD_OFFSET(PRINTER_INFO_2W, pShareName),
+    FIELD_OFFSET(PRINTER_INFO_2W, pPortName),
+    FIELD_OFFSET(PRINTER_INFO_2W, pDriverName),
+    FIELD_OFFSET(PRINTER_INFO_2W, pComment),
+    FIELD_OFFSET(PRINTER_INFO_2W, pLocation),
+    FIELD_OFFSET(PRINTER_INFO_2W, pSepFile),
+    FIELD_OFFSET(PRINTER_INFO_2W, pPrintProcessor),
+    FIELD_OFFSET(PRINTER_INFO_2W, pDatatype),
+    FIELD_OFFSET(PRINTER_INFO_2W, pParameters),
+    MAXDWORD
+};
+
+static DWORD dwPrinterInfo4Offsets[] = {
+    FIELD_OFFSET(PRINTER_INFO_4W, pPrinterName),
+    MAXDWORD
+};
+
+static DWORD dwPrinterInfo5Offsets[] = {
+    FIELD_OFFSET(PRINTER_INFO_5W, pPrinterName),
+    FIELD_OFFSET(PRINTER_INFO_5W, pPortName),
+    MAXDWORD
+};
+
 /**
  * @name _PrinterListCompareRoutine
  *
@@ -437,137 +494,378 @@ _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbN
     return ERROR_SUCCESS;
 }
 
-static DWORD
-_LocalEnumPrintersLevel0(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
+static void
+_LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_STRESS* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
 {
-    return ERROR_INVALID_LEVEL;
+    size_t cbName;
+    PWSTR p;
+    PWSTR pwszStrings[1];
+    SYSTEM_INFO SystemInfo;
+
+    // Calculate the string lengths.
+    cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+
+    if (!ppPrinterInfo)
+    {
+        *pcbNeeded += sizeof(PRINTER_INFO_STRESS) + cbName;
+        return;
+    }
+
+    // Set the general fields.
+    ZeroMemory(*ppPrinterInfo, sizeof(PRINTER_INFO_STRESS));
+    (*ppPrinterInfo)->cJobs = pPrinter->JobList.NodeCount;
+    (*ppPrinterInfo)->dwGetVersion = GetVersion();
+    (*ppPrinterInfo)->Status = pPrinter->dwStatus;
+
+#if !defined(DBG)
+    (*ppPrinterInfo)->fFreeBuild = 1;
+#endif
+
+    GetSystemInfo(&SystemInfo);
+    (*ppPrinterInfo)->dwNumberOfProcessors = SystemInfo.dwNumberOfProcessors;
+    (*ppPrinterInfo)->dwProcessorType = SystemInfo.dwProcessorType;
+    (*ppPrinterInfo)->wProcessorArchitecture = SystemInfo.wProcessorArchitecture;
+    (*ppPrinterInfo)->wProcessorLevel = SystemInfo.wProcessorLevel;
+
+    // Copy the Printer Name.
+    pwszStrings[0] = DllAllocSplMem(cbName);
+    p = pwszStrings[0];
+    StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
+    StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
+
+    // Finally copy the structure and advance to the next one in the output buffer.
+    *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo0Offsets, *ppPrinterInfoEnd);
+    (*ppPrinterInfo)++;
+
+    // Free the memory for temporary strings.
+    DllFreeSplMem(pwszStrings[0]);
 }
 
-static DWORD
-_LocalEnumPrintersLevel1(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
+static void
+_LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
 {
     const WCHAR wszComma[] = L",";
 
     size_t cbName;
     size_t cbComment;
     size_t cbDescription;
-    DWORD dwErrorCode;
-    DWORD i;
-    PBYTE pPrinterInfo;
-    PBYTE pPrinterStrings;
-    PSKIPLIST_NODE pNode;
-    PLOCAL_PRINTER pPrinter;
     PWSTR p;
     PWSTR pwszStrings[3];
 
-    if (Flags & PRINTER_ENUM_NAME && !Name)
+    // Calculate the string lengths.
+    // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
+    // 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 = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+    cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
+    cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
+
+    if (!ppPrinterInfo)
     {
-        // The caller wants information about this Print Provider.
-        // spoolss packs this into an array of information about all Print Providers.
-        dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
-        goto Cleanup;
+        *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
+        return;
     }
 
-    // Count the required buffer size and the number of printers.
-    i = 0;
+    // Indicate that this is a Printer.
+    (*ppPrinterInfo)->Flags = PRINTER_ENUM_ICON8;
+
+    // Copy the Printer Name.
+    pwszStrings[0] = DllAllocSplMem(cbName);
+    p = pwszStrings[0];
+    StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
+    StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
+
+    // Copy the Printer comment (equals the "Description" registry value).
+    pwszStrings[1] = pPrinter->pwszDescription;
+
+    // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
+    pwszStrings[2] = DllAllocSplMem(cbDescription);
+    p = pwszStrings[2];
+    StringCbCopyExW(p, cbDescription, wszComputerName, &p, &cbDescription, 0);
+    StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterName, &p, &cbDescription, 0);
+    StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
+    StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterDriver, &p, &cbDescription, 0);
+    StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
+    StringCbCopyExW(p, cbDescription, pPrinter->pwszLocation, &p, &cbDescription, 0);
+
+    // Finally copy the structure and advance to the next one in the output buffer.
+    *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo1Offsets, *ppPrinterInfoEnd);
+    (*ppPrinterInfo)++;
+
+    // Free the memory for temporary strings.
+    DllFreeSplMem(pwszStrings[0]);
+    DllFreeSplMem(pwszStrings[2]);
+}
 
-    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
-    {
-        pPrinter = (PLOCAL_PRINTER)pNode->Element;
+static void
+_LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    WCHAR wszEmpty[] = L"";
 
-        // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
-        if (Flags & PRINTER_ENUM_SHARED)
-            continue;
+    size_t cbDevMode;
+    size_t cbPrinterName;
+    size_t cbShareName;
+    size_t cbPortName;
+    size_t cbDriverName;
+    size_t cbComment;
+    size_t cbLocation;
+    size_t cbSepFile;
+    size_t cbPrintProcessor;
+    size_t cbDatatype;
+    size_t cbParameters;
+    PWSTR p;
+    PWSTR pwszStrings[10];
 
-        // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
-        // 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 = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
-        cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
-        cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
+    // Calculate the string lengths.
+    cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
+    cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
 
-        *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
-        i++;
+    if (!ppPrinterInfo)
+    {
+        // Attention: pComment equals the "Description" registry value.
+        cbShareName = sizeof(wszEmpty);
+        cbPortName = (wcslen(pPrinter->pPort->pwszName) + 1) * sizeof(WCHAR);
+        cbDriverName = (wcslen(pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
+        cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
+        cbLocation = (wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
+        cbSepFile = sizeof(wszEmpty);
+        cbPrintProcessor = (wcslen(pPrinter->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
+        cbDatatype = (wcslen(pPrinter->pwszDefaultDatatype) + 1) * sizeof(WCHAR);
+        cbParameters = sizeof(wszEmpty);
+
+        *pcbNeeded += sizeof(PRINTER_INFO_2W) + cbDevMode + cbPrinterName + cbShareName + cbPortName + cbDriverName + cbComment + cbLocation + cbSepFile + cbPrintProcessor + cbDatatype + cbParameters;
+        return;
     }
 
-    // Check if the supplied buffer is large enough.
-    if (cbBuf < *pcbNeeded)
+    // Set the general fields.
+    ZeroMemory(*ppPrinterInfo, sizeof(PRINTER_INFO_2W));
+    (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
+    (*ppPrinterInfo)->cJobs = pPrinter->JobList.NodeCount;
+    (*ppPrinterInfo)->Status = pPrinter->dwStatus;
+
+    // Set the pDevMode field (and copy the DevMode).
+    *ppPrinterInfoEnd -= cbDevMode;
+    CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
+    (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
+
+    // Set the pPrinterName field.
+    pwszStrings[0] = DllAllocSplMem(cbPrinterName);
+    p = pwszStrings[0];
+    StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
+    StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
+
+    // Set the pShareName field.
+    pwszStrings[1] = wszEmpty;
+
+    // Set the pPortName field.
+    pwszStrings[2] = pPrinter->pPort->pwszName;
+
+    // Set the pDriverName field.
+    pwszStrings[3] = pPrinter->pwszPrinterDriver;
+
+    // Set the pComment field ((equals the "Description" registry value).
+    pwszStrings[4] = pPrinter->pwszDescription;
+
+    // Set the pLocation field.
+    pwszStrings[5] = pPrinter->pwszLocation;
+
+    // Set the pSepFile field.
+    pwszStrings[6] = wszEmpty;
+
+    // Set the pPrintProcessor field.
+    pwszStrings[7] = pPrinter->pPrintProcessor->pwszName;
+
+    // Set the pDatatype field.
+    pwszStrings[8] = pPrinter->pwszDefaultDatatype;
+
+    // Set the pParameters field.
+    pwszStrings[9] = wszEmpty;
+
+    // Finally copy the structure and advance to the next one in the output buffer.
+    *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo2Offsets, *ppPrinterInfoEnd);
+    (*ppPrinterInfo)++;
+
+    // Free the memory for temporary strings.
+    DllFreeSplMem(pwszStrings[0]);
+}
+
+static void
+_LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    SECURITY_DESCRIPTOR SecurityDescriptor = { 0 };
+
+    if (!ppPrinterInfo)
     {
-        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
-        goto Cleanup;
+        *pcbNeeded += sizeof(PRINTER_INFO_3) + sizeof(SECURITY_DESCRIPTOR);
+        return;
     }
 
-    // Initialize the variables for filling the output buffer using PackStrings.
-    pPrinterInfo = pPrinterEnum;
-    pPrinterStrings = &pPrinterEnum[*pcbNeeded];
+    FIXME("Return a valid security descriptor for PRINTER_INFO_3\n");
 
-    // Copy over the Printer information.
-    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
+    // Set the pSecurityDescriptor field (and copy the Security Descriptor).
+    *ppPrinterInfoEnd -= sizeof(SECURITY_DESCRIPTOR);
+    CopyMemory(*ppPrinterInfoEnd, &SecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));
+    (*ppPrinterInfo)->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)(*ppPrinterInfoEnd);
+
+    // Advance to the next structure.
+    (*ppPrinterInfo)++;
+}
+
+static void
+_LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_4W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    size_t cbPrinterName;
+    PWSTR p;
+    PWSTR pwszStrings[1];
+
+    // Calculate the string lengths.
+    cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+
+    if (!ppPrinterInfo)
     {
-        pPrinter = (PLOCAL_PRINTER)pNode->Element;
+        *pcbNeeded += sizeof(PRINTER_INFO_4W) + cbPrinterName;
+        return;
+    }
 
-        // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
-        if (Flags & PRINTER_ENUM_SHARED)
-            continue;
+    // Set the general fields.
+    (*ppPrinterInfo)->pServerName = NULL;
+    (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
 
-        // Indicate that this is a Printer.
-        ((PPRINTER_INFO_1W)pPrinterInfo)->Flags = PRINTER_ENUM_ICON8;
+    // Set the pPrinterName field.
+    pwszStrings[0] = DllAllocSplMem(cbPrinterName);
+    p = pwszStrings[0];
+    StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
+    StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
 
-        // Calculate the string lengths.
-        cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
-        cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
-        cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
+    // Finally copy the structure and advance to the next one in the output buffer.
+    *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo4Offsets, *ppPrinterInfoEnd);
+    (*ppPrinterInfo)++;
 
-        // Copy the Printer Name.
-        pwszStrings[0] = DllAllocSplMem(cbName);
-        p = pwszStrings[0];
-        StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
-        StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
+    // Free the memory for temporary strings.
+    DllFreeSplMem(pwszStrings[0]);
+}
 
-        // Copy the Printer comment (equals the "Description" registry value).
-        pwszStrings[1] = pPrinter->pwszDescription;
+static void
+_LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    size_t cbPrinterName;
+    size_t cbPortName;
+    PWSTR p;
+    PWSTR pwszStrings[1];
 
-        // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
-        pwszStrings[2] = DllAllocSplMem(cbDescription);
-        p = pwszStrings[2];
-        StringCbCopyExW(p, cbDescription, wszComputerName, &p, &cbDescription, 0);
-        StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterName, &p, &cbDescription, 0);
-        StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
-        StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterDriver, &p, &cbDescription, 0);
-        StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
-        StringCbCopyExW(p, cbDescription, pPrinter->pwszLocation, &p, &cbDescription, 0);
+    // Calculate the string lengths.
+    cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
 
-        // Finally copy the structure and advance to the next one in the output buffer.
-        pPrinterStrings = PackStrings(pwszStrings, pPrinterInfo, dwPrinterInfo1Offsets, pPrinterStrings);
-        pPrinterInfo += sizeof(PRINTER_INFO_1W);
+    if (!ppPrinterInfo)
+    {
+        cbPortName = (wcslen(pPrinter->pPort->pwszName) + 1) * sizeof(WCHAR);
 
-        // Free the memory for temporary strings.
-        DllFreeSplMem(pwszStrings[0]);
-        DllFreeSplMem(pwszStrings[2]);
+        *pcbNeeded += sizeof(PRINTER_INFO_5W) + cbPrinterName + cbPortName;
+        return;
     }
 
-    *pcReturned = i;
-    dwErrorCode = ERROR_SUCCESS;
+    // Set the general fields.
+    (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
+    (*ppPrinterInfo)->DeviceNotSelectedTimeout = 0;
+    (*ppPrinterInfo)->TransmissionRetryTimeout = 0;
 
-Cleanup:
-    return dwErrorCode;
+    // Set the pPrinterName field.
+    pwszStrings[0] = DllAllocSplMem(cbPrinterName);
+    p = pwszStrings[0];
+    StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
+    StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
+
+    // Set the pPortName field.
+    pwszStrings[1] = pPrinter->pPort->pwszName;
+
+    // Finally copy the structure and advance to the next one in the output buffer.
+    *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo5Offsets, *ppPrinterInfoEnd);
+    (*ppPrinterInfo)++;
+
+    // Free the memory for temporary strings.
+    DllFreeSplMem(pwszStrings[0]);
 }
 
-static DWORD
-_LocalEnumPrintersLevel2(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
+static void
+_LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
 {
-    return ERROR_INVALID_LEVEL;
+    if (!ppPrinterInfo)
+    {
+        *pcbNeeded += sizeof(PRINTER_INFO_6);
+        return;
+    }
+
+    // Set the general fields.
+    (*ppPrinterInfo)->dwStatus = pPrinter->dwStatus;
+
+    // Advance to the next structure.
+    (*ppPrinterInfo)++;
 }
 
-static DWORD
-_LocalEnumPrintersLevel4(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
+static void
+_LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
 {
-    return ERROR_INVALID_LEVEL;
+    if (!ppPrinterInfo)
+    {
+        *pcbNeeded += sizeof(PRINTER_INFO_7W);
+        return;
+    }
+
+    FIXME("No Directory Support, returning DSPRINT_UNPUBLISH for PRINTER_INFO_7 all the time!\n");
+
+    // Set the general fields.
+    (*ppPrinterInfo)->dwAction = DSPRINT_UNPUBLISH;
+    (*ppPrinterInfo)->pszObjectGUID = NULL;
+
+    // Advance to the next structure.
+    (*ppPrinterInfo)++;
 }
 
-static DWORD
-_LocalEnumPrintersLevel5(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
+static void
+_LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_8W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    DWORD cbDevMode;
+
+    // Calculate the string lengths.
+    cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
+
+    if (!ppPrinterInfo)
+    {
+        *pcbNeeded += sizeof(PRINTER_INFO_8W) + cbDevMode;
+        return;
+    }
+
+    // Set the pDevMode field (and copy the DevMode).
+    *ppPrinterInfoEnd -= cbDevMode;
+    CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
+    (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
+
+    // Advance to the next structure.
+    (*ppPrinterInfo)++;
+}
+
+static void
+_LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_9W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
 {
-    return ERROR_INVALID_LEVEL;
+    DWORD cbDevMode;
+
+    // Calculate the string lengths.
+    cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
+
+    if (!ppPrinterInfo)
+    {
+        *pcbNeeded += sizeof(PRINTER_INFO_9W) + cbDevMode;
+        return;
+    }
+
+    FIXME("Per-user settings are not yet implemented, returning the global DevMode for PRINTER_INFO_9!\n");
+
+    // Set the pDevMode field (and copy the DevMode).
+    *ppPrinterInfoEnd -= cbDevMode;
+    CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
+    (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
+
+    // Advance to the next structure.
+    (*ppPrinterInfo)++;
 }
 
 BOOL WINAPI
@@ -575,7 +873,11 @@ LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DW
 {
     DWORD cchComputerName = 0;
     DWORD dwErrorCode;
+    DWORD i;
+    PBYTE pPrinterInfoEnd;
+    PSKIPLIST_NODE pNode;
     WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
+    PLOCAL_PRINTER pPrinter;
 
     ASSERT(pcbNeeded);
     ASSERT(pcReturned);
@@ -601,38 +903,112 @@ LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DW
         goto Cleanup;
     }
 
+    if (Level == 3 || Level > 5)
+    {
+        // The caller supplied an invalid level for EnumPrinters.
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    if (Level == 1 && Flags & PRINTER_ENUM_NAME && !Name)
+    {
+        // The caller wants information about this Print Provider.
+        // spoolss packs this into an array of information about all Print Providers.
+        dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+        goto Cleanup;
+    }
+
     // Check the supplied Name parameter (if any).
     // This may return a Computer Name string we later prepend to the output.
     dwErrorCode = _LocalEnumPrintersCheckName(Flags, Name, wszComputerName, &cchComputerName);
     if (dwErrorCode != ERROR_SUCCESS)
         goto Cleanup;
 
-    if (Level == 0)
+    // Count the required buffer size and the number of printers.
+    i = 0;
+    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
     {
-        dwErrorCode = _LocalEnumPrintersLevel0(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
+        pPrinter = (PLOCAL_PRINTER)pNode->Element;
+
+        // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
+        if (Flags & PRINTER_ENUM_SHARED)
+        {
+            FIXME("Printer Sharing is not supported yet, returning no printers!\n");
+            continue;
+        }
+
+        pfnGetPrinterLevels[Level](pPrinter, NULL, NULL, pcbNeeded, cchComputerName, wszComputerName);
+        i++;
     }
-    else if (Level == 1)
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
     {
-        dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
     }
-    else if (Level == 2)
+
+    // Copy over the Printer information.
+    pPrinterInfoEnd = &pPrinterEnum[*pcbNeeded];
+
+    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
     {
-        dwErrorCode = _LocalEnumPrintersLevel2(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
+        pPrinter = (PLOCAL_PRINTER)pNode->Element;
+
+        // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
+        if (Flags & PRINTER_ENUM_SHARED)
+            continue;
+
+        pfnGetPrinterLevels[Level](pPrinter, &pPrinterEnum, &pPrinterInfoEnd, NULL, cchComputerName, wszComputerName);
     }
-    else if (Level == 4)
+
+    *pcReturned = i;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalGetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
+{
+    DWORD dwErrorCode;
+    PBYTE pPrinterEnd;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Check if this is a printer handle.
+    if (pHandle->HandleType != HandleType_Printer)
     {
-        dwErrorCode = _LocalEnumPrintersLevel4(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
     }
-    else if (Level == 5)
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    if (Level > 9)
     {
-        dwErrorCode = _LocalEnumPrintersLevel5(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
+        // The caller supplied an invalid level for GetPrinter.
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
     }
-    else
+
+    // Count the required buffer size.
+    pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, NULL, NULL, pcbNeeded, 0, NULL);
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
     {
-        // The caller supplied an invalid level.
-        dwErrorCode = ERROR_INVALID_LEVEL;
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
     }
 
+    // Copy over the Printer information.
+    pPrinterEnd = &pPrinter[*pcbNeeded];
+    pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, &pPrinter, &pPrinterEnd, NULL, 0, NULL);
+    dwErrorCode = ERROR_SUCCESS;
+
 Cleanup:
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);