[PRINTING]
[reactos.git] / reactos / win32ss / printing / providers / localspl / printers.c
index b923b24..4bbcf6e 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:     Functions related to Printers and printing
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
 // 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),
+    FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
+    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
+};
+
+/** These values serve no purpose anymore, but are still used in PRINTER_INFO_5 and
+    HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts */
+static const DWORD dwDeviceNotSelectedTimeout = 15000;
+static const DWORD dwTransmissionRetryTimeout = 45000;
+
 
 /**
  * @name _PrinterListCompareRoutine
@@ -33,20 +103,22 @@ _PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
  * 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.
  */
-void
+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;
-    LONG lStatus;
+    PLOCAL_PORT pPort;
     PLOCAL_PRINTER pPrinter = NULL;
     PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+    PWSTR pwszPort = NULL;
     PWSTR pwszPrintProcessor = NULL;
     WCHAR wszPrinterName[MAX_PRINTER_NAME + 1];
 
@@ -54,18 +126,18 @@ InitializePrinterList()
     InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
 
     // Open our printers registry key. Each subkey is a local printer there.
-    lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey);
-    if (lStatus != ERROR_SUCCESS)
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
     {
-        ERR("RegOpenKeyExW failed with status %ld!\n", lStatus);
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
         goto Cleanup;
     }
 
     // Get the number of subkeys.
-    lStatus = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
-    if (lStatus != ERROR_SUCCESS)
+    dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
     {
-        ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus);
+        ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
         goto Cleanup;
     }
 
@@ -81,6 +153,9 @@ InitializePrinterList()
 
         if (pPrinter)
         {
+            if (pPrinter->pDefaultDevMode)
+                DllFreeSplMem(pPrinter->pDefaultDevMode);
+
             if (pPrinter->pwszDefaultDatatype)
                 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
 
@@ -105,23 +180,23 @@ InitializePrinterList()
 
         // Get the name of this printer.
         cchPrinterName = _countof(wszPrinterName);
-        lStatus = RegEnumKeyExW(hKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL);
-        if (lStatus == ERROR_MORE_DATA)
+        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 (lStatus != ERROR_SUCCESS)
+        else if (dwErrorCode != ERROR_SUCCESS)
         {
-            ERR("RegEnumKeyExW failed for iteration %lu with status %ld!\n", i, lStatus);
+            ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
             continue;
         }
 
         // Open this Printer's registry key.
-        lStatus = RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey);
-        if (lStatus != ERROR_SUCCESS)
+        dwErrorCode = (DWORD)RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey);
+        if (dwErrorCode != ERROR_SUCCESS)
         {
-            ERR("RegOpenKeyExW failed for Printer \"%S\" with status %ld!\n", wszPrinterName, lStatus);
+            ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName, dwErrorCode);
             continue;
         }
 
@@ -138,18 +213,38 @@ InitializePrinterList()
             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 location.
+        pPrinter->pwszLocation = AllocAndRegQueryWSZ(hSubKey, L"Location");
+        if (!pPrinter->pwszLocation)
+            continue;
+
         // Get the printer driver.
         pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
         if (!pPrinter->pwszPrinterDriver)
@@ -172,30 +267,46 @@ InitializePrinterList()
             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.
-        cbData = sizeof(DEVMODEW);
-        lStatus = RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)&pPrinter->DefaultDevMode, &cbData);
-        if (lStatus != ERROR_SUCCESS || cbData != sizeof(DEVMODEW))
+        dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)pPrinter->pDefaultDevMode, &cbData);
+        if (dwErrorCode != ERROR_SUCCESS)
         {
-            ERR("Couldn't query a valid DevMode for Printer \"%S\", status is %ld, cbData is %lu!\n", wszPrinterName, lStatus, cbData);
+            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);
-        lStatus = RegQueryValueExW(hSubKey, L"Attributes", NULL, NULL, (PBYTE)&pPrinter->dwAttributes, &cbData);
-        if (lStatus != ERROR_SUCCESS)
+        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 %ld!\n", wszPrinterName, lStatus);
+            ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
             continue;
         }
 
         // Get the Status.
         cbData = sizeof(DWORD);
-        lStatus = RegQueryValueExW(hSubKey, L"Status", NULL, NULL, (PBYTE)&pPrinter->dwStatus, &cbData);
-        if (lStatus != ERROR_SUCCESS)
+        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 %ld!\n", wszPrinterName, lStatus);
+            ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
             continue;
         }
 
@@ -210,6 +321,8 @@ InitializePrinterList()
         pPrinter = NULL;
     }
 
+    dwErrorCode = ERROR_SUCCESS;
+
 Cleanup:
     // Inside the loop
     if (hSubKey)
@@ -217,6 +330,9 @@ Cleanup:
 
     if (pPrinter)
     {
+        if (pPrinter->pDefaultDevMode)
+            DllFreeSplMem(pPrinter->pDefaultDevMode);
+
         if (pPrinter->pwszDefaultDatatype)
             DllFreeSplStr(pPrinter->pwszDefaultDatatype);
 
@@ -238,549 +354,1230 @@ Cleanup:
     // 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)
+/**
+ * @name _LocalEnumPrintersCheckName
+ *
+ * Checks the Name parameter supplied to a call to EnumPrinters.
+ *
+ * @param Flags
+ * Flags parameter of EnumPrinters.
+ *
+ * @param Name
+ * Name parameter of EnumPrinters to check.
+ *
+ * @param pwszComputerName
+ * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
+ * On return, it may contain a computer name to prepend in EnumPrinters depending on the case.
+ *
+ * @param pcchComputerName
+ * If a string to prepend is returned, this pointer receives its length in characters.
+ *
+ * @return
+ * ERROR_SUCCESS if processing in EnumPrinters can be continued.
+ * ERROR_INVALID_NAME if the Name parameter is invalid for the given flags and this Print Provider.
+ * Any other error code if GetComputerNameW fails. Error codes indicating failure should then be returned by EnumPrinters.
+ */
+static DWORD
+_LocalEnumPrintersCheckName(DWORD Flags, PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName)
 {
-    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];
+    PCWSTR pName;
+    PCWSTR pComputerName;
 
-    DWORD dwOffsets[] = {
-        FIELD_OFFSET(PRINTER_INFO_1W, pName),
-        FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
-        FIELD_OFFSET(PRINTER_INFO_1W, pComment),
-        MAXDWORD
-    };
+    // If there is no Name parameter to check, we can just continue in EnumPrinters.
+    if (!Name)
+        return ERROR_SUCCESS;
 
-    if (Flags & PRINTER_ENUM_NAME)
+    // Check if Name does not begin with two backslashes (required for specifying Computer Names).
+    if (Name[0] != L'\\' || Name[1] != L'\\')
     {
-        if (Name)
+        if (Flags & PRINTER_ENUM_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;
-                }
+            // If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the
+            // Print Provider Name or the local Computer Name.
 
-                // 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;
-                }
+            // Compare with the Print Provider Name.
+            if (wcsicmp(Name, wszPrintProviderInfo[0]) == 0)
+                return ERROR_SUCCESS;
 
-                // 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;
-            }
+            // Dismiss anything else.
+            return ERROR_INVALID_NAME;
         }
         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);
+            // If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name.
+            return ERROR_SUCCESS;
+        }
+    }
+
+    // Prepend the backslashes to the output computer name.
+    pwszComputerName[0] = L'\\';
+    pwszComputerName[1] = L'\\';
 
-            for (i = 0; i < 3; i++)
-                *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
+    // Get the local computer name for comparison.
+    *pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
+    if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName))
+    {
+        ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
+        return GetLastError();
+    }
 
-            *pcReturned = 1;
+    // Add the leading slashes to the total length.
+    *pcchComputerName += 2;
 
-            // Check if the supplied buffer is large enough.
-            if (cbBuf < *pcbNeeded)
+    // Compare both names.
+    pComputerName = &pwszComputerName[2];
+    pName = &Name[2];
+    for (;;)
+    {
+        // Are we at the end of the local Computer Name string?
+        if (!*pComputerName)
+        {
+            // Are we also at the end of the supplied Name parameter?
+            // A terminating NUL character and a backslash are both treated as the end, but they are treated differently.
+            if (!*pName)
             {
-                dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
-                goto Cleanup;
+                // If both names match and Name ends with a NUL character, the computer name will be prepended in EnumPrinters.
+                // Add a trailing backslash for that.
+                pwszComputerName[(*pcchComputerName)++] = L'\\';
+                pwszComputerName[*pcchComputerName] = 0;
+                return ERROR_SUCCESS;
+            }
+            else if (*pName == L'\\')
+            {
+                if (Flags & PRINTER_ENUM_NAME)
+                {
+                    // If PRINTER_ENUM_NAME is specified and a Name parameter is given, it must be exactly the local
+                    // Computer Name with two backslashes prepended. Anything else (like "\\COMPUTERNAME\") is dismissed.
+                    return ERROR_INVALID_NAME;
+                }
+                else
+                {
+                    // If PRINTER_ENUM_NAME is not specified and a Name parameter is given, it may also end with a backslash.
+                    // Only the Computer Name between the backslashes is checked then.
+                    // This is largely undocumented, but verified by tests (see winspool_apitest).
+                    // In this case, no computer name is prepended in EnumPrinters though.
+                    *pwszComputerName = 0;
+                    *pcchComputerName = 0;
+                    return ERROR_SUCCESS;
+                }
             }
-
-            // Copy over the print processor information.
-            ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
-            PackStrings(wszPrintProviderInfo, pPrinterEnum, dwOffsets, &pPrinterEnum[*pcbNeeded]);
-            dwErrorCode = ERROR_SUCCESS;
-            goto Cleanup;
         }
+
+        // Compare both Computer Names case-insensitively and reject with ERROR_INVALID_NAME if they don't match.
+        if (towlower(*pName) != towlower(*pComputerName))
+            return ERROR_INVALID_NAME;
+
+        pName++;
+        pComputerName++;
     }
+}
 
-    // Count the required buffer size and the number of printers.
-    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
-    {
-        pPrinter = (PLOCAL_PRINTER)pNode->Element;
+static DWORD
+_DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+{
+    int i;
 
-        // 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);
+    // Count the needed bytes for Print Provider information.
+    *pcbNeeded = sizeof(PRINTER_INFO_1W);
 
-        *pcbNeeded += sizeof(PRINTER_INFO_1W) + cchComputerName * sizeof(WCHAR) + cbName + cbComment + cbDescription;
-        *pcReturned++;
-    }
+    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;
-    }
+        return ERROR_INSUFFICIENT_BUFFER;
 
-    // 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 + *pcReturned * sizeof(PRINTER_INFO_1W);
+    // Copy over the Print Provider information.
+    ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
+    PackStrings(wszPrintProviderInfo, pPrinterEnum, dwPrinterInfo1Offsets, &pPrinterEnum[*pcbNeeded]);
+    *pcReturned = 1;
 
-    // Copy over the printer information.
-    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
-    {
-        pPrinter = (PLOCAL_PRINTER)pNode->Element;
+    return ERROR_SUCCESS;
+}
 
-        // FIXME: As for now, the Flags member returns no information.
-        PrinterInfo1.Flags = 0;
+static void
+_LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_STRESS* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    size_t cbName;
+    PWSTR p;
+    PWSTR pwszStrings[1];
+    SYSTEM_INFO SystemInfo;
 
-        // 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;
+    // Calculate the string lengths.
+    cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
 
-        // 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);
+    if (!ppPrinterInfo)
+    {
+        *pcbNeeded += sizeof(PRINTER_INFO_STRESS) + cbName;
+        return;
     }
 
-    dwErrorCode = ERROR_SUCCESS;
-
-Cleanup:
-    return dwErrorCode;
+    // 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]);
 }
 
-BOOL WINAPI
-LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
+static void
+_LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
 {
-    DWORD dwErrorCode;
-
-    // Do no sanity checks here. This is verified by localspl_apitest!
+    const WCHAR wszComma[] = L",";
 
-    // Begin counting.
-    *pcbNeeded = 0;
-    *pcReturned = 0;
+    size_t cbName;
+    size_t cbComment;
+    size_t cbDescription;
+    PWSTR p;
+    PWSTR pwszStrings[3];
 
-    // Think positive :)
-    // Treat it as success if the caller queried no information and we don't need to return any.
-    dwErrorCode = ERROR_SUCCESS;
+    // 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 (Flags & PRINTER_ENUM_LOCAL)
+    if (!ppPrinterInfo)
     {
-        // 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;
-        }
+        *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
+        return;
     }
 
-Cleanup:
-    SetLastError(dwErrorCode);
-    return (dwErrorCode == ERROR_SUCCESS);
+    // 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]);
 }
 
-BOOL WINAPI
-LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
+static void
+_LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
 {
-    DWORD cchComputerName;
-    DWORD cchPrinterName;
-    DWORD dwErrorCode;
-    DWORD dwJobID;
-    PWSTR p = lpPrinterName;
-    PWSTR pwszPrinterName = NULL;
-    PLOCAL_JOB pJob;
-    PLOCAL_HANDLE pHandle;
-    PLOCAL_PRINTER pPrinter;
-    PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
-    WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
-
-    // Sanity checks
-    if (!lpPrinterName || !phPrinter)
+    WCHAR wszEmpty[] = L"";
+
+    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];
+
+    // Calculate the string lengths.
+    cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
+    cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+
+    if (!ppPrinterInfo)
     {
-        dwErrorCode = ERROR_INVALID_PARAMETER;
-        goto Cleanup;
+        // 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;
     }
 
-    // Does lpPrinterName begin with two backslashes to indicate a server name?
-    if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
-    {
-        // Skip these two backslashes.
-        lpPrinterName += 2;
+    // Set the general fields.
+    ZeroMemory(*ppPrinterInfo, sizeof(PRINTER_INFO_2W));
+    (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
+    (*ppPrinterInfo)->cJobs = pPrinter->JobList.NodeCount;
+    (*ppPrinterInfo)->Status = pPrinter->dwStatus;
 
-        // 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;
-        }
+    // Set the pDevMode field (and copy the DevMode).
+    *ppPrinterInfoEnd -= cbDevMode;
+    CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
+    (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
 
-        // Null-terminate the string here to enable comparison.
-        *p = 0;
+    // 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);
 
-        // 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;
-        }
+    // Set the pShareName field.
+    pwszStrings[1] = wszEmpty;
 
-        // Now compare this with the local computer name and reject if it doesn't match, because this print provider only supports local printers.
-        if (wcsicmp(lpPrinterName, wszComputerName) != 0)
-        {
-            dwErrorCode = ERROR_INVALID_PRINTER_NAME;
-            goto Cleanup;
-        }
+    // Set the pPortName field.
+    pwszStrings[2] = pPrinter->pPort->pwszName;
 
-        // We have checked the server name and don't need it anymore.
-        lpPrinterName = p + 1;
-    }
+    // Set the pDriverName field.
+    pwszStrings[3] = pPrinter->pwszPrinterDriver;
 
-    // Look for a comma. If it exists, it indicates the end of the printer name.
-    p = wcschr(lpPrinterName, L',');
-    if (p)
-        cchPrinterName = p - lpPrinterName;
-    else
-        cchPrinterName = wcslen(lpPrinterName);
+    // Set the pComment field ((equals the "Description" registry value).
+    pwszStrings[4] = pPrinter->pwszDescription;
 
-    // No printer name and no comma? This is invalid!
-    if (!cchPrinterName && !p)
-    {
-        dwErrorCode = ERROR_INVALID_PRINTER_NAME;
-        goto Cleanup;
-    }
+    // Set the pLocation field.
+    pwszStrings[5] = pPrinter->pwszLocation;
 
-    // Do we have a printer name?
-    if (cchPrinterName)
-    {
-        // Yes, extract it.
-        pwszPrinterName = DllAllocSplMem((cchPrinterName + 1) * sizeof(WCHAR));
-        CopyMemory(pwszPrinterName, lpPrinterName, cchPrinterName * sizeof(WCHAR));
-        pwszPrinterName[cchPrinterName] = 0;
+    // Set the pSepFile field.
+    pwszStrings[6] = wszEmpty;
 
-        // Retrieve the associated printer from the list.
-        pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
-        if (!pPrinter)
-        {
-            // The printer does not exist.
-            dwErrorCode = ERROR_INVALID_PRINTER_NAME;
-            goto Cleanup;
-        }
+    // Set the pPrintProcessor field.
+    pwszStrings[7] = pPrinter->pPrintProcessor->pwszName;
 
-        // Create a new printer handle.
-        pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
-        pPrinterHandle->Printer = pPrinter;
+    // Set the pDatatype field.
+    pwszStrings[8] = pPrinter->pwszDefaultDatatype;
 
-        // 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;
-            }
+    // Set the pParameters field.
+    pwszStrings[9] = wszEmpty;
 
-            pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
-        }
-        else
-        {
-            // Use the default datatype.
-            pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
-        }
+    // Finally copy the structure and advance to the next one in the output buffer.
+    *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo2Offsets, *ppPrinterInfoEnd);
+    (*ppPrinterInfo)++;
 
-        // Check if a DevMode was given, otherwise use the default.
-        if (pDefault && pDefault->pDevMode)
-            CopyMemory(&pPrinterHandle->DevMode, pDefault->pDevMode, sizeof(DEVMODEW));
-        else
-            CopyMemory(&pPrinterHandle->DevMode, &pPrinter->DefaultDevMode, sizeof(DEVMODEW));
+    // Free the memory for temporary strings.
+    DllFreeSplMem(pwszStrings[0]);
+}
 
-        // Did we have a comma? Then the user may want a handle to an existing job instead of creating a new job.
-        if (p)
-        {
-            ++p;
-            
-            // Skip whitespace.
-            do
-            {
-                ++p;
-            }
-            while (*p == ' ');
+static void
+_LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    SECURITY_DESCRIPTOR SecurityDescriptor = { 0 };
 
-            // The "Job " string has to follow now.
-            if (wcscmp(p, L"Job ") != 0)
-            {
-                dwErrorCode = ERROR_INVALID_PRINTER_NAME;
-                goto Cleanup;
-            }
+    if (!ppPrinterInfo)
+    {
+        *pcbNeeded += sizeof(PRINTER_INFO_3) + sizeof(SECURITY_DESCRIPTOR);
+        return;
+    }
 
-            // Skip the "Job " string. 
-            p += sizeof("Job ") - 1;
+    FIXME("Return a valid security descriptor for PRINTER_INFO_3\n");
 
-            // Skip even more whitespace.
-            while (*p == ' ')
-                ++p;
+    // Set the pSecurityDescriptor field (and copy the Security Descriptor).
+    *ppPrinterInfoEnd -= sizeof(SECURITY_DESCRIPTOR);
+    CopyMemory(*ppPrinterInfoEnd, &SecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));
+    (*ppPrinterInfo)->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)(*ppPrinterInfoEnd);
 
-            // Finally extract the desired Job ID.
-            dwJobID = wcstoul(p, NULL, 10);
-            if (!IS_VALID_JOB_ID(dwJobID))
-            {
-                // The user supplied an invalid Job ID.
-                dwErrorCode = ERROR_INVALID_PRINTER_NAME;
-                goto Cleanup;
-            }
+    // Advance to the next structure.
+    (*ppPrinterInfo)++;
+}
 
-            // Look for this job in the Global Job List.
-            pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
-            if (!pJob || pJob->Printer != 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;
-            }
+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];
 
-            pPrinterHandle->StartedJob = pJob;
-        }
+    // Calculate the string lengths.
+    cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
 
-        // Create a new handle that references a printer.
-        pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
-        pHandle->HandleType = Printer;
-        pHandle->SpecificHandle = pPrinterHandle;
-    }
-    else
+    if (!ppPrinterInfo)
     {
-        // No printer name, but we have a comma!
-        // This may be a request to a XcvMonitor or XcvPort handle.
-        ++p;
+        *pcbNeeded += sizeof(PRINTER_INFO_4W) + cbPrinterName;
+        return;
+    }
 
-        // Skip whitespace.
-        do
-        {
-            ++p;
-        }
-        while (*p == ' ');
+    // Set the general fields.
+    (*ppPrinterInfo)->pServerName = NULL;
+    (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
 
-        // Check if this is a request to a XcvMonitor.
-        if (wcscmp(p, L"XcvMonitor ") == 0)
-        {
-            // Skip the "XcvMonitor " string. 
-            p += sizeof("XcvMonitor ") - 1;
+    // 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);
 
-            ///////////// TODO /////////////////////
-            pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
-            pHandle->HandleType = Monitor;
-            //pHandle->SpecificHandle = pMonitorHandle;
-        }
-        else if (wcscmp(p, L"XcvPort ") == 0)
-        {
-            // Skip the "XcvPort " string. 
-            p += sizeof("XcvPort ") - 1;
+    // Finally copy the structure and advance to the next one in the output buffer.
+    *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo4Offsets, *ppPrinterInfoEnd);
+    (*ppPrinterInfo)++;
 
-            //////////// TODO //////////////////////
-            pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
-            pHandle->HandleType = Port;
-            //pHandle->SpecificHandle = pPortHandle;
-        }
-        else
-        {
-            dwErrorCode = ERROR_INVALID_PRINTER_NAME;
-            goto Cleanup;
-        }
-    }
+    // Free the memory for temporary strings.
+    DllFreeSplMem(pwszStrings[0]);
+}
 
-    *phPrinter = (HANDLE)pHandle;
-    dwErrorCode = ERROR_SUCCESS;
+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];
 
-    // Don't let the cleanup routines free this.
-    pPrinterHandle = NULL;
-    pwszPrinterName = NULL;
+    // Calculate the string lengths.
+    cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
 
-Cleanup:
-    if (pPrinterHandle)
+    if (!ppPrinterInfo)
     {
-        if (pPrinterHandle->pwszDatatype)
-            DllFreeSplStr(pPrinterHandle->pwszDatatype);
+        cbPortName = (wcslen(pPrinter->pPort->pwszName) + 1) * sizeof(WCHAR);
 
-        DllFreeSplMem(pPrinterHandle);
+        *pcbNeeded += sizeof(PRINTER_INFO_5W) + cbPrinterName + cbPortName;
+        return;
     }
 
-    if (pwszPrinterName)
-        DllFreeSplMem(pwszPrinterName);
+    // Set the general fields.
+    (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
+    (*ppPrinterInfo)->DeviceNotSelectedTimeout = dwDeviceNotSelectedTimeout;
+    (*ppPrinterInfo)->TransmissionRetryTimeout = dwTransmissionRetryTimeout;
 
-    SetLastError(dwErrorCode);
-    return (dwErrorCode == ERROR_SUCCESS);
+    // 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]);
 }
 
-DWORD WINAPI
-LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
+static void
+_LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
 {
-    DWORD dwErrorCode;
-    DWORD dwReturnValue = 0;
-    PDOC_INFO_1W pDocumentInfo1;
-    PLOCAL_HANDLE pHandle;
-    PLOCAL_JOB pJob;
-    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+    if (!ppPrinterInfo)
+    {
+        *pcbNeeded += sizeof(PRINTER_INFO_6);
+        return;
+    }
+
+    // Set the general fields.
+    (*ppPrinterInfo)->dwStatus = pPrinter->dwStatus;
+
+    // Advance to the next structure.
+    (*ppPrinterInfo)++;
+}
+
+static void
+_LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+    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 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)
+{
+    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
+LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
+{
+    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);
+
+    // Begin counting.
+    *pcbNeeded = 0;
+    *pcReturned = 0;
+
+    if (Flags & PRINTER_ENUM_CONNECTIONS || Flags & PRINTER_ENUM_REMOTE || Flags & PRINTER_ENUM_NETWORK)
+    {
+        // If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME.
+        // This is the internal way for a Print Provider to signal that it doesn't handle this request.
+        dwErrorCode = ERROR_INVALID_NAME;
+        goto Cleanup;
+    }
+
+    if (!(Flags & PRINTER_ENUM_LOCAL || Flags & PRINTER_ENUM_NAME))
+    {
+        // The Local Print Provider is the right destination for the request, but without any of these flags,
+        // there is no information that can be returned.
+        // So just signal a successful request.
+        dwErrorCode = ERROR_SUCCESS;
+        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;
+
+    // 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;
+
+        // 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++;
+    }
+
+    // Check if the supplied buffer is large enough.
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Copy over the Printer information.
+    pPrinterInfoEnd = &pPrinterEnum[*pcbNeeded];
+
+    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
+    {
+        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);
+    }
+
+    *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 = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    if (Level > 9)
+    {
+        // The caller supplied an invalid level for GetPrinter.
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    // 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)
+    {
+        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);
+}
+
+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);
+        }
 
-    // Sanity checks
-    if (!pDocInfo)
+        // 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)
     {
-        dwErrorCode = ERROR_INVALID_PARAMETER;
-        goto Cleanup;
+        if (pPrinterHandle->pwszDatatype)
+            DllFreeSplStr(pPrinterHandle->pwszDatatype);
+
+        if (pPrinterHandle->pDevMode)
+            DllFreeSplMem(pPrinterHandle->pDevMode);
+
+        DllFreeSplMem(pPrinterHandle);
     }
 
-    if (!hPrinter)
+    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;
     }
 
-    // Check if this is a printer handle.
-    pHandle = (PLOCAL_HANDLE)hPrinter;
-    if (pHandle->HandleType != Printer)
+    // 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->SpecificHandle;
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
 
-    // Check if this is the right document information level.
-    if (Level != 1)
+    // 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_LEVEL;
+        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;
     }
 
-    pDocumentInfo1 = (PDOC_INFO_1W)pDocInfo;
+    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;
 
-    // Create a new job.
-    pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
-    pJob->Printer = pPrinterHandle->Printer;
-    pJob->dwPriority = DEF_PRIORITY;
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
 
-    // Check if a datatype was given.
-    if (pDocumentInfo1->pDatatype)
+    // Port handles are an entirely different thing.
+    if (pHandle->HandleType == HandleType_Port)
     {
-        // Use the datatype if it's valid.
-        if (!FindDatatype(pJob->Printer->pPrintProcessor, pDocumentInfo1->pDatatype))
+        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)
         {
-            dwErrorCode = ERROR_INVALID_DATATYPE;
+            // The StartDocPort function failed. Return its last error.
+            dwErrorCode = GetLastError();
             goto Cleanup;
         }
 
-        pJob->pwszDatatype = AllocSplStr(pDocumentInfo1->pDatatype);
+        // We were successful!
+        dwErrorCode = ERROR_SUCCESS;
+        dwReturnValue = pJob->dwJobID;
+        goto Cleanup;
     }
-    else
+
+    // The remaining function deals with Printer handles only.
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    if (!pDocInfo1)
     {
-        // Use the printer handle datatype.
-        pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
     }
 
-    // Copy over printer defaults.
-    CopyMemory(&pJob->DevMode, &pPrinterHandle->DevMode, sizeof(DEVMODEW));
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
 
-    // Copy over supplied information.
-    if (pDocumentInfo1->pDocName)
-        pJob->pwszDocumentName = AllocSplStr(pDocumentInfo1->pDocName);
+    // 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;
+    }
 
-    if (pDocumentInfo1->pOutputFile)
-        pJob->pwszOutputFile = AllocSplStr(pDocumentInfo1->pOutputFile);
+    // 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;
+    }
 
-    // Get an ID for the new job.
-    if (!GetNextJobID(&pJob->dwJobID))
+    // Check if this is the right document information level.
+    if (Level != 1)
     {
-        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        dwErrorCode = ERROR_INVALID_LEVEL;
         goto Cleanup;
     }
 
-    // Add the job to the Global Job List.
-    if (!InsertElementSkiplist(&GlobalJobList, pJob))
+    // 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("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
+        ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
         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->Printer->JobList, pJob))
+    // Use any given document name.
+    if (pDocInfo1->pDocName && !ReallocSplStr(&pPrinterHandle->pJob->pwszDocumentName, pDocInfo1->pDocName))
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
+        ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
         goto Cleanup;
     }
 
-    pPrinterHandle->StartedJob = pJob;
+    // We were successful!
     dwErrorCode = ERROR_SUCCESS;
-    dwReturnValue = pJob->dwJobID;
+    dwReturnValue = pPrinterHandle->pJob->dwJobID;
 
 Cleanup:
     SetLastError(dwErrorCode);
@@ -790,48 +1587,246 @@ Cleanup:
 BOOL WINAPI
 LocalStartPagePrinter(HANDLE hPrinter)
 {
-    ///////////// TODO /////////////////////
-    return FALSE;
+    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, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
+LocalWritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
 {
-    ///////////// TODO /////////////////////
-    return FALSE;
+    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)
 {
-    ///////////// TODO /////////////////////
-    return FALSE;
+    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)
 {
-    ///////////// TODO /////////////////////
-    return FALSE;
+    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 pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PORT_HANDLE pPortHandle;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+    PLOCAL_XCV_HANDLE pXcvHandle;
 
-    if (!hPrinter)
+    if (!pHandle)
     {
         SetLastError(ERROR_INVALID_HANDLE);
         return FALSE;
     }
 
-    pHandle = (PLOCAL_HANDLE)hPrinter;
+    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;
 
-    ///////////// TODO /////////////////////
-    /// Check the handle type, do thoroughful checks on all data fields and clean them.
-    ////////////////////////////////////////
+        // 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;