[PRINTING]
authorColin Finck <colin@reactos.org>
Mon, 19 Jun 2017 14:18:19 +0000 (14:18 +0000)
committerColin Finck <colin@reactos.org>
Mon, 19 Jun 2017 14:18:19 +0000 (14:18 +0000)
- Implement GetPrinterDataA, GetPrinterDataExA, GetPrinterDataExW, GetPrinterDataW, SetPrinterDataA, SetPrinterDataExA, SetPrinterDataExW, SetPrinterDataW.
  They support all features for Print Server and Printer Handles (minus security checks!)
  I've also added tests for them.
- Store Printer data in SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers instead of SYSTEM\CurrentControlSet\Control\Print\Printers and create a registry symlink from the former path to the new one just like Windows does.
  According to https://social.technet.microsoft.com/Forums/windowsserver/en-US/a683ab54-c43c-4ebe-af8f-1f7a65af2a51, this is needed when having >900 printers to work around a size limit of the SYSTEM registry hive. And if Windows has both locations, we need both for compatibility anyway.
- Add several settings which are queried by the new Printer Data APIs when working with Print Server Handles.
- Store the job directory in the Windows-compatible "DefaultSpoolDirectory" setting and make use of it.
- Revert the ASSERTs in LocalEnumPrinters again to let us verify the NULL pointer exceptions in localspl_apitest (thanks Serge! CORE-13433)
- Translate ERROR_INVALID_NAME to ERROR_INVALID_PRINTER_NAME in all cases in OpenPrinterW (thanks Victor! CORE-13412)
- Make EnumMonitorsW and EnumPortsW in spoolss more robust against failing Print Monitors.
- Remove the wrong !phPrinter check in OpenPrinterW to make Print Server Handles work for real.
- Fix error handling when memory allocation fails: HeapAlloc doesn't set last error, so it's just wrong to query or return it.

One more item done from https://reactos.org/wiki/Printing !
This is all still a big Work-in-Progress, with many subtle bugs deep down in ReactOS, for which I need to open additional tickets. But I didn't want to make this commit even bigger..

svn path=/trunk/; revision=75125

29 files changed:
reactos/boot/bootdata/hivesft.inf
reactos/boot/bootdata/hivesys.inf
reactos/win32ss/printing/base/spoolss/CMakeLists.txt
reactos/win32ss/printing/base/spoolss/main.c
reactos/win32ss/printing/base/spoolss/memory.c
reactos/win32ss/printing/base/spoolss/monitors.c
reactos/win32ss/printing/base/spoolss/ports.c
reactos/win32ss/printing/base/spoolss/printerdata.c [new file with mode: 0644]
reactos/win32ss/printing/base/spoolss/printers.c
reactos/win32ss/printing/base/spoolss/spoolss.spec
reactos/win32ss/printing/base/spoolsv/printerdata.c
reactos/win32ss/printing/base/winspool/printerdata.c
reactos/win32ss/printing/base/winspool/printers.c
reactos/win32ss/printing/base/winspool/printprocessors.c
reactos/win32ss/printing/base/winspool/winspool.spec
reactos/win32ss/printing/providers/localspl/CMakeLists.txt
reactos/win32ss/printing/providers/localspl/jobs.c
reactos/win32ss/printing/providers/localspl/main.c
reactos/win32ss/printing/providers/localspl/monitors.c
reactos/win32ss/printing/providers/localspl/ports.c
reactos/win32ss/printing/providers/localspl/precomp.h
reactos/win32ss/printing/providers/localspl/printerdata.c [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/printers.c
reactos/win32ss/printing/providers/localspl/printingthread.c
reactos/win32ss/printing/providers/localspl/printprocessors.c
reactos/win32ss/printing/providers/localspl/tools.c
rostests/apitests/winspool/CMakeLists.txt
rostests/apitests/winspool/GetPrinterData.c [new file with mode: 0644]
rostests/apitests/winspool/testlist.c

index 2076f21..217725d 100644 (file)
@@ -432,6 +432,23 @@ HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports","LPT1:",2,""
 HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports","LPT2:",2,""
 HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports","LPT3:",2,""
 
+; Printing
+HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers",,0x00000012
+HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Attributes",0x00010001,0
+HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Datatype",,"RAW"
+HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Description",,""
+HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Default DevMode",1,\
+00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
+00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,dc,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
+00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
+00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
+00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
+HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Location",,"At Home"
+HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Port",,"LPT1:"
+HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Print Processor",,"winprint"
+HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Printer Driver",,"Dummy Printer Driver"
+HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Status",0x00010001,0
+
 ; Image File Execution Options (NtGlobalFlag with FLG_SHOW_LDR_SNAPS set for loadlib.exe) 
 HKLM,"Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\loadlib.exe","GlobalFlag",0x00000000,"0x02000000"
 ;HKLM,"Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\loaddll.exe","GlobalFlag",0x00010001,0x00000002
index 6a26d6e..da5279c 100644 (file)
@@ -1277,9 +1277,12 @@ HKLM,"SYSTEM\CurrentControlSet\Control\PnP\Pci\CardList","Ali",0x00030003,\
 
 
 ; Printing
+HKLM,"SYSTEM\CurrentControlSet\Control\Print","BeepEnabled",0x00010001,0
 HKLM,"SYSTEM\CurrentControlSet\Control\Print","MajorVersion",0x00010001,2
 HKLM,"SYSTEM\CurrentControlSet\Control\Print","MinorVersion",0x00010001,0
+HKLM,"SYSTEM\CurrentControlSet\Control\Print","PortThreadPriority",0x00010001,0
 HKLM,"SYSTEM\CurrentControlSet\Control\Print","PriorityClass",0x00010001,0
+HKLM,"SYSTEM\CurrentControlSet\Control\Print","SchedulerThreadPriority",0x00010001,0
 
 HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments",,0x00000010
 HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86","Directory",,"W32X86"
@@ -1289,22 +1292,6 @@ HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86\Print P
 HKLM,"SYSTEM\CurrentControlSet\Control\Print\Monitors",,0x00000010
 HKLM,"SYSTEM\CurrentControlSet\Control\Print\Monitors\Local Port","Driver",,"localmon.dll"
 
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers",,0x00000012
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Attributes",0x00010001,0
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Datatype",,"RAW"
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Description",,""
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Default DevMode",1,\
-00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
-00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,dc,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
-00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
-00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
-00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Location",,"At Home"
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Port",,"LPT1:"
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Print Processor",,"winprint"
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Printer Driver",,"Dummy Printer Driver"
-HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Status",0x00010001,0
-
 HKLM,"SYSTEM\CurrentControlSet\Control\Print\Providers",,0x00000010
 
 
@@ -2131,7 +2118,7 @@ HKLM,"SYSTEM\Setup","SystemPartition",0x00000000,"\Device\Harddisk0\Partition1"
 HKLM,"SYSTEM\Setup","SystemSetupInProgress",0x00010001,0x00000001
 
 ; Debug channels
-;HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\Environment","DEBUGCHANNEL",0x00020000,"+ole,+rpc"
+HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\Environment","DEBUGCHANNEL",0x00020000,"+localspl"
 
 ; Winsrv configuration
 HKLM,"SYSTEM\CurrentControlSet\Control\PriorityControl",,0x00000012
index 67a7e1f..f7ef3f2 100644 (file)
@@ -9,6 +9,7 @@ list(APPEND SOURCE
     monitors.c
     ports.c
     precomp.h
+    printerdata.c
     printers.c
     printprocessors.c
     tools.c)
index 673186c..d6ac29c 100644 (file)
@@ -43,7 +43,7 @@ _AddPrintProviderToList(PCWSTR pwszFileName)
     if (!pPrintProvider)
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed!\n");
         goto Cleanup;
     }
 
@@ -117,7 +117,7 @@ _InitializePrintProviderList()
     if (!pwszPrintProviderName)
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed!\n");
         goto Cleanup;
     }
 
index 02f6ad1..66e111f 100644 (file)
@@ -69,7 +69,7 @@ AllocSplStr(PCWSTR pwszInput)
     pwszOutput = HeapAlloc(hProcessHeap, 0, cbInput);
     if (!pwszOutput)
     {
-        ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+        ERR("HeapAlloc failed!\n");
         return NULL;
     }
 
@@ -159,7 +159,7 @@ ReallocSplMem(PVOID pOldMem, DWORD cbOld, DWORD cbNew)
     pNewMem = DllAllocSplMem(cbNew);
     if (!pNewMem)
     {
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed!\n");
         return NULL;
     }
 
index 1a7a258..e9fbb82 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Spooler Router
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions related to Print Monitors
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -43,6 +43,8 @@ EnumMonitorsW(PWSTR pName, DWORD Level, PBYTE pMonitors, DWORD cbBuf, PDWORD pcb
             continue;
 
         // Call the EnumMonitors function of this Print Provider.
+        cbNeeded = 0;
+        dwReturned = 0;
         bReturnValue = pPrintProvider->PrintProvider.fpEnumMonitors(pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
 
         // Add the returned counts to the total values.
index 52b0b6c..c5df5df 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Spooler Router
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions related to Ports of the Print Monitors
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -39,6 +39,8 @@ EnumPortsW(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded
         pPrintProvider = CONTAINING_RECORD(pEntry, SPOOLSS_PRINT_PROVIDER, Entry);
 
         // Call the EnumPorts function of this Print Provider.
+        cbNeeded = 0;
+        dwReturned = 0;
         bReturnValue = pPrintProvider->PrintProvider.fpEnumPorts(pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
 
         // Add the returned counts to the total values.
diff --git a/reactos/win32ss/printing/base/spoolss/printerdata.c b/reactos/win32ss/printing/base/spoolss/printerdata.c
new file mode 100644 (file)
index 0000000..0b7665d
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * PROJECT:     ReactOS Spooler Router
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printer Configuration Data
+ * COPYRIGHT:   Copyright 2017 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD WINAPI
+GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity check.
+    if (!pHandle)
+    {
+        // Yes, Windows checks for the handle here and sets the last error to ERROR_INVALID_HANDLE,
+        // but returns FALSE and not the error code.
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    // Call GetPrinterDataEx of the Print Provider.
+    return pHandle->pPrintProvider->PrintProvider.fpGetPrinterDataEx(pHandle->hPrinter, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
+}
+
+DWORD WINAPI
+GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
+{
+    // The ReactOS Printing Stack forwards all GetPrinterData calls to GetPrinterDataEx as soon as possible.
+    // This function may only be called if spoolss.dll is used together with Windows Printing Stack components.
+    WARN("This function should never be called!\n");
+    return GetPrinterDataExW(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded);
+}
+
+DWORD WINAPI
+SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData)
+{
+    PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter;
+
+    // Sanity check.
+    if (!pHandle)
+    {
+        // Yes, Windows checks for the handle here and sets the last error to ERROR_INVALID_HANDLE,
+        // but returns FALSE and not the error code.
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    // Call SetPrinterDataEx of the Print Provider.
+    return pHandle->pPrintProvider->PrintProvider.fpSetPrinterDataEx(pHandle->hPrinter, pKeyName, pValueName, Type, pData, cbData);
+}
+
+DWORD WINAPI
+SetPrinterDataW(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
+{
+    // The ReactOS Printing Stack forwards all SetPrinterData calls to SetPrinterDataEx as soon as possible.
+    // This function may only be called if spoolss.dll is used together with Windows Printing Stack components.
+    WARN("This function should never be called!\n");
+    return SetPrinterDataExW(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData);
+}
index ecd64d0..99d74d2 100644 (file)
@@ -93,6 +93,8 @@ EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cb
         pPrintProvider = CONTAINING_RECORD(pEntry, SPOOLSS_PRINT_PROVIDER, Entry);
 
         // Call the EnumPrinters function of this Print Provider.
+        cbNeeded = 0;
+        dwReturned = 0;
         pPrintProvider->PrintProvider.fpEnumPrinters(Flags, Name, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
 
         // Add the returned counts to the total values.
@@ -159,13 +161,6 @@ OpenPrinterW(PWSTR pPrinterName, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault)
     PSPOOLSS_PRINTER_HANDLE pHandle;
     PSPOOLSS_PRINT_PROVIDER pPrintProvider;
 
-    // Sanity checks.
-    if (!pPrinterName || !phPrinter)
-    {
-        dwErrorCode = ERROR_INVALID_PARAMETER;
-        goto Cleanup;
-    }
-
     // Loop through all Print Providers to find one able to open this Printer.
     for (pEntry = PrintProviderList.Flink; pEntry != &PrintProviderList; pEntry = pEntry->Flink)
     {
@@ -179,8 +174,8 @@ OpenPrinterW(PWSTR pPrinterName, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault)
             pHandle = DllAllocSplMem(sizeof(SPOOLSS_PRINTER_HANDLE));
             if (!pHandle)
             {
-                ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
                 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+                ERR("DllAllocSplMem failed!\n");
                 goto Cleanup;
             }
 
@@ -199,11 +194,11 @@ OpenPrinterW(PWSTR pPrinterName, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault)
         }
     }
 
+Cleanup:
     // ERROR_INVALID_NAME by the Print Provider is translated to ERROR_INVALID_PRINTER_NAME here, but not in other APIs as far as I know.
     if (dwErrorCode == ERROR_INVALID_NAME)
         dwErrorCode = ERROR_INVALID_PRINTER_NAME;
 
-Cleanup:
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);
 }
index 5c0892c..4696300 100644 (file)
@@ -80,8 +80,8 @@
 @ stub GetJobAttributes
 @ stdcall GetJobW(long long long ptr long ptr)
 @ stub GetNetworkId
-@ stub GetPrinterDataExW
-@ stub GetPrinterDataW
+@ stdcall GetPrinterDataExW(long wstr wstr ptr ptr long ptr)
+@ stdcall GetPrinterDataW(long wstr ptr ptr long ptr)
 @ stub GetPrinterDriverDirectoryW
 @ stub GetPrinterDriverExW
 @ stdcall GetPrinterDriverW(long wstr long ptr long ptr)
 @ stub SetFormW
 @ stdcall SetJobW(long long long ptr long)
 @ stub SetPortW
-@ stub SetPrinterDataExW
-@ stub SetPrinterDataW
+@ stdcall SetPrinterDataExW(long wstr wstr long ptr long)
+@ stdcall SetPrinterDataW(long wstr long ptr long)
 @ stub SetPrinterW
 @ stub SplCloseSpoolFileHandle
 @ stub SplCommitSpoolData
index 1ffb0af..a193720 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Print Spooler Service
  * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions related to Printer Configuration Data
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -52,27 +52,49 @@ _RpcEnumPrinterDataEx(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, B
 DWORD
 _RpcGetPrinterData(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pValueName, DWORD* pType, BYTE* pData, DWORD nSize, DWORD* pcbNeeded)
 {
-    UNIMPLEMENTED;
-    return ERROR_INVALID_FUNCTION;
+    return _RpcGetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded);
 }
 
 DWORD
 _RpcGetPrinterDataEx(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, const WCHAR* pValueName, DWORD* pType, BYTE* pData, DWORD nSize, DWORD* pcbNeeded)
 {
-    UNIMPLEMENTED;
-    return ERROR_INVALID_FUNCTION;
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    dwErrorCode = GetPrinterDataExW(hPrinter, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
+
+    RpcRevertToSelf();
+
+    return dwErrorCode;
 }
 
 DWORD
 _RpcSetPrinterData(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pValueName, DWORD Type, BYTE* pData, DWORD cbData)
 {
-    UNIMPLEMENTED;
-    return ERROR_INVALID_FUNCTION;
+    return _RpcSetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData);
 }
 
 DWORD
 _RpcSetPrinterDataEx(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, const WCHAR* pValueName, DWORD Type, BYTE* pData, DWORD cbData)
 {
-    UNIMPLEMENTED;
-    return ERROR_INVALID_FUNCTION;
+    DWORD dwErrorCode;
+
+    dwErrorCode = RpcImpersonateClient(NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode);
+        return dwErrorCode;
+    }
+
+    dwErrorCode = SetPrinterDataExW(hPrinter, pKeyName, pValueName, Type, pData, cbData);
+
+    RpcRevertToSelf();
+
+    return dwErrorCode;
 }
index ec15ac6..9080642 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Spooler API
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions related to Printer Configuration Data
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -15,15 +15,327 @@ AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, PWSTR pDeviceName, PDEVM
 }
 
 DWORD WINAPI
-GetPrinterDataW(HANDLE hPrinter, PWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
+GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
 {
-    UNIMPLEMENTED;
-    return FALSE;
+    return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded);
+}
+
+DWORD WINAPI
+GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName, LPCSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
+{
+    DWORD cbUnicodeData;
+    DWORD cch;
+    DWORD dwReturnValue;
+    DWORD dwType;
+    POSVERSIONINFOEXA pInfoA;
+    POSVERSIONINFOEXW pInfoW;
+    PVOID pUnicodeData = NULL;
+    PWSTR pwszKeyName = NULL;
+    PWSTR pwszValueName = NULL;
+
+    if (pKeyName)
+    {
+        // Convert pKeyName to a Unicode string pwszKeyName
+        cch = strlen(pKeyName);
+
+        pwszKeyName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!pwszKeyName)
+        {
+            dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed!\n");
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pKeyName, -1, pwszKeyName, cch + 1);
+    }
+
+    if (pValueName)
+    {
+        // Convert pValueName to a Unicode string pwszValueName
+        cch = strlen(pValueName);
+
+        pwszValueName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!pwszValueName)
+        {
+            dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed!\n");
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pValueName, -1, pwszValueName, cch + 1);
+    }
+
+    // We need the data type information, even if no pData was passed.
+    if (!pType)
+        pType = &dwType;
+
+    // Call GetPrinterDataExW for the first time.
+    // If we're lucky, the supplied buffer is already large enough and we don't need to do the expensive RPC call a second time.
+    dwReturnValue = GetPrinterDataExW(hPrinter, pwszKeyName, pwszValueName, pType, pData, nSize, pcbNeeded);
+
+    // If a critical error occurred, just return it. We cannot do anything else in this case.
+    if (dwReturnValue != ERROR_SUCCESS && dwReturnValue != ERROR_MORE_DATA)
+        goto Cleanup;
+
+    // Save the needed buffer size for the Unicode data. We may alter *pcbNeeded for an ANSI buffer size.
+    cbUnicodeData = *pcbNeeded;
+
+    if (*pType == REG_SZ || *pType == REG_MULTI_SZ || *pType == REG_EXPAND_SZ)
+    {
+        // This is a string that needs to be converted from Unicode to ANSI.
+        // Output the required buffer size for the ANSI string.
+        *pcbNeeded /= sizeof(WCHAR);
+    }
+    else if (*pType == REG_NONE)
+    {
+        if (cbUnicodeData == sizeof(OSVERSIONINFOW) && wcsicmp(pwszValueName, SPLREG_OS_VERSION) == 0)
+        {
+            // This is a Unicode OSVERSIONINFOW structure that needs to be converted to an ANSI OSVERSIONINFOA.
+            *pcbNeeded = sizeof(OSVERSIONINFOA);
+        }
+        else if (cbUnicodeData == sizeof(OSVERSIONINFOEXW) && wcsicmp(pwszValueName, SPLREG_OS_VERSIONEX) == 0)
+        {
+            // This is a Unicode OSVERSIONINFOEXW structure that needs to be converted to an ANSI OSVERSIONINFOEXA.
+            *pcbNeeded = sizeof(OSVERSIONINFOEXA);
+        }
+        else
+        {
+            // Other REG_NONE value, nothing to do.
+            goto Cleanup;
+        }
+    }
+
+    // Check if the supplied buffer is large enough for the ANSI data.
+    if (nSize < *pcbNeeded)
+    {
+        dwReturnValue = ERROR_MORE_DATA;
+        goto Cleanup;
+    }
+
+    // Allocate a temporary buffer for the Unicode data.
+    pUnicodeData = HeapAlloc(hProcessHeap, 0, cbUnicodeData);
+    if (!pUnicodeData)
+    {
+        dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("HeapAlloc failed!\n");
+        goto Cleanup;
+    }
+
+    if (dwReturnValue == ERROR_SUCCESS)
+    {
+        // ERROR_SUCCESS: The buffer is large enough for the ANSI and the Unicode string,
+        // so the Unicode string has been copied into pData. Copy it to pUnicodeData.
+        CopyMemory(pUnicodeData, pData, cbUnicodeData);
+    }
+    else
+    {
+        // ERROR_MORE_DATA: The buffer is large enough for the ANSI string, but not for the Unicode string.
+        // We have to call GetPrinterDataExW again with the temporary buffer.
+        dwReturnValue = GetPrinterDataExW(hPrinter, pwszKeyName, pwszValueName, NULL, (PBYTE)pUnicodeData, cbUnicodeData, &cbUnicodeData);
+        if (dwReturnValue != ERROR_SUCCESS)
+            goto Cleanup;
+    }
+
+    if (*pType == REG_SZ || *pType == REG_MULTI_SZ || *pType == REG_EXPAND_SZ)
+    {
+        // Convert the Unicode string to ANSI.
+        WideCharToMultiByte(CP_ACP, 0, (PWSTR)pUnicodeData, -1, (PSTR)pData, *pcbNeeded, NULL, NULL);
+    }
+    else
+    {
+        // This is a REG_NONE with either OSVERSIONINFOW or OSVERSIONINFOEXW.
+        // Copy the fields and convert the Unicode CSD Version string to ANSI.
+        pInfoW = (POSVERSIONINFOEXW)pUnicodeData;
+        pInfoA = (POSVERSIONINFOEXA)pData;
+        pInfoA->dwMajorVersion = pInfoW->dwMajorVersion;
+        pInfoA->dwMinorVersion = pInfoW->dwMinorVersion;
+        pInfoA->dwBuildNumber = pInfoW->dwBuildNumber;
+        pInfoA->dwPlatformId = pInfoW->dwPlatformId;
+        WideCharToMultiByte(CP_ACP, 0, pInfoW->szCSDVersion, -1, pInfoA->szCSDVersion, sizeof(pInfoA->szCSDVersion), NULL, NULL);
+
+        if (cbUnicodeData == sizeof(OSVERSIONINFOW))
+        {
+            pInfoA->dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+        }
+        else
+        {
+            pInfoA->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA);
+            pInfoA->wServicePackMajor = pInfoW->wServicePackMajor;
+            pInfoA->wServicePackMinor = pInfoW->wServicePackMinor;
+            pInfoA->wSuiteMask = pInfoW->wSuiteMask;
+            pInfoA->wProductType = pInfoW->wProductType;
+            pInfoA->wReserved = pInfoW->wReserved;
+        }
+    }
+
+Cleanup:
+    if (pwszKeyName)
+        HeapFree(hProcessHeap, 0, pwszKeyName);
+
+    if (pwszValueName)
+        HeapFree(hProcessHeap, 0, pwszValueName);
+
+    if (pUnicodeData)
+        HeapFree(hProcessHeap, 0, pUnicodeData);
+
+    return dwReturnValue;
+}
+
+DWORD WINAPI
+GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
+{
+    const WCHAR wszEmptyString[] = L"";
+
+    BYTE DummyData;
+    DWORD dwErrorCode;
+    DWORD dwType = REG_NONE;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks
+    if (!pHandle)
+        return ERROR_INVALID_HANDLE;
+
+    // Yes, instead of declaring these pointers unique in the IDL file (and perfectly accepting NULL pointers this way),
+    // Windows does it differently for GetPrinterDataExW and points them to empty variables.
+    if (!pKeyName)
+        pKeyName = wszEmptyString;
+
+    if (!pType)
+        pType = &dwType;
+
+    if (!pData && !nSize)
+        pData = &DummyData;
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcGetPrinterDataEx(pHandle->hPrinter, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+    }
+    RpcEndExcept;
+
+    return dwErrorCode;
+}
+
+DWORD WINAPI
+GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
+{
+    return GetPrinterDataExW(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded);
+}
+
+DWORD WINAPI
+SetPrinterDataA(HANDLE hPrinter, PSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
+{
+    return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type, pData, cbData);
+}
+
+DWORD WINAPI
+SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName, LPCSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData)
+{
+    DWORD cch;
+    DWORD dwReturnValue;
+    PWSTR pwszKeyName = NULL;
+    PWSTR pwszValueName = NULL;
+    PWSTR pUnicodeData = NULL;
+
+    if (pKeyName)
+    {
+        // Convert pKeyName to a Unicode string pwszKeyName
+        cch = strlen(pKeyName);
+
+        pwszKeyName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!pwszKeyName)
+        {
+            dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed!\n");
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pKeyName, -1, pwszKeyName, cch + 1);
+    }
+
+    if (pValueName)
+    {
+        // Convert pValueName to a Unicode string pwszValueName
+        cch = strlen(pValueName);
+
+        pwszValueName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!pwszValueName)
+        {
+            dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed!\n");
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pValueName, -1, pwszValueName, cch + 1);
+    }
+
+    if (Type == REG_SZ || Type == REG_MULTI_SZ || Type == REG_EXPAND_SZ)
+    {
+        // Convert pData to a Unicode string pUnicodeData.
+        pUnicodeData = HeapAlloc(hProcessHeap, 0, cbData * sizeof(WCHAR));
+        if (!pUnicodeData)
+        {
+            dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed!\n");
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, (PCSTR)pData, -1, pUnicodeData, cbData);
+
+        pData = (PBYTE)pUnicodeData;
+        cbData *= sizeof(WCHAR);
+    }
+
+    dwReturnValue = SetPrinterDataExW(hPrinter, pwszKeyName, pwszValueName, Type, pData, cbData);
+
+Cleanup:
+    if (pwszKeyName)
+        HeapFree(hProcessHeap, 0, pwszKeyName);
+
+    if (pwszValueName)
+        HeapFree(hProcessHeap, 0, pwszValueName);
+
+    if (pUnicodeData)
+        HeapFree(hProcessHeap, 0, pUnicodeData);
+
+    return dwReturnValue;
+}
+
+DWORD WINAPI
+SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData)
+{
+    const WCHAR wszEmptyString[] = L"";
+
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks
+    if (!pHandle)
+        return ERROR_INVALID_HANDLE;
+
+    if (!pKeyName)
+        pKeyName = wszEmptyString;
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcSetPrinterDataEx(pHandle->hPrinter, pKeyName, pValueName, Type, pData, cbData);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+    }
+    RpcEndExcept;
+
+    return dwErrorCode;
 }
 
 DWORD WINAPI
 SetPrinterDataW(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
 {
-    UNIMPLEMENTED;
-    return FALSE;
+    return SetPrinterDataExW(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData);
 }
index 5b95cbc..7ab062b 100644 (file)
@@ -152,7 +152,7 @@ _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB
     if (!pJobInfo1)
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+        ERR("HeapAlloc failed!\n");
         goto Cleanup;
     }
 
@@ -254,7 +254,6 @@ ClosePrinter(HANDLE hPrinter)
 Cleanup:
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);
-
 }
 
 DWORD WINAPI
@@ -438,8 +437,8 @@ GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
         pwszBuffer = HeapAlloc(hProcessHeap, 0, *pcchBuffer * sizeof(WCHAR));
         if (!pwszBuffer)
         {
-            dwErrorCode = GetLastError();
-            ERR("HeapAlloc failed with error %lu!\n", dwErrorCode);
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
     }
@@ -499,8 +498,8 @@ GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
     pwszDevice = HeapAlloc(hProcessHeap, 0, cbNeeded);
     if (!pwszDevice)
     {
-        dwErrorCode = GetLastError();
-        ERR("HeapAlloc failed with error %lu!\n", dwErrorCode);
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("HeapAlloc failed!\n");
         goto Cleanup;
     }
 
@@ -621,7 +620,8 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul
         pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
         if (!pwszPrinterName)
         {
-            ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
 
@@ -640,7 +640,8 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul
             wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
             if (!wDefault.pDatatype)
             {
-                ERR("HeapAlloc failed for wDefault.pDatatype with last error %lu!\n", GetLastError());
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                ERR("HeapAlloc failed!\n");
                 goto Cleanup;
             }
 
@@ -676,6 +677,13 @@ OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefau
     WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
     ACCESS_MASK AccessRequired = 0;
 
+    // Sanity check
+    if (!phPrinter)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
     // Prepare the additional parameters in the format required by _RpcOpenPrinter
     if (pDefault)
     {
@@ -704,7 +712,7 @@ OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefau
         if (!pHandle)
         {
             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
 
@@ -772,7 +780,8 @@ SetDefaultPrinterA(LPCSTR pszPrinter)
         pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
         if (!pwszPrinter)
         {
-            ERR("HeapAlloc failed for pwszPrinter with last error %lu!\n", GetLastError());
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
 
@@ -854,8 +863,8 @@ SetDefaultPrinterW(LPCWSTR pszPrinter)
     pwszDeviceValueData = HeapAlloc(hProcessHeap, 0, cbDeviceValueData);
     if (!pwszDeviceValueData)
     {
-        dwErrorCode = GetLastError();
-        ERR("HeapAlloc failed with error %lu\n", dwErrorCode);
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("HeapAlloc failed!\n");
         goto Cleanup;
     }
 
@@ -936,7 +945,8 @@ StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
         wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
         if (!wDocInfo1.pDatatype)
         {
-            ERR("HeapAlloc failed for wDocInfo1.pDatatype with last error %lu!\n", GetLastError());
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
 
@@ -951,7 +961,8 @@ StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
         wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
         if (!wDocInfo1.pDocName)
         {
-            ERR("HeapAlloc failed for wDocInfo1.pDocName with last error %lu!\n", GetLastError());
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
 
@@ -966,7 +977,8 @@ StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
         wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
         if (!wDocInfo1.pOutputFile)
         {
-            ERR("HeapAlloc failed for wDocInfo1.pOutputFile with last error %lu!\n", GetLastError());
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
 
@@ -1040,7 +1052,7 @@ StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
         if (!pAddJobInfo1)
         {
             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
 
index ba71970..bac7d9e 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Spooler API
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions related to Print Processors
- * COPYRIGHT:   Copyright 2015-2016 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -129,7 +129,8 @@ GetPrintProcessorDirectoryA(PSTR pName, PSTR pEnvironment, DWORD Level, PBYTE pP
         pwszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
         if (!pwszName)
         {
-            ERR("HeapAlloc failed for pwszName with last error %lu!\n", GetLastError());
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
 
@@ -144,7 +145,8 @@ GetPrintProcessorDirectoryA(PSTR pName, PSTR pEnvironment, DWORD Level, PBYTE pP
         pwszEnvironment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
         if (!pwszEnvironment)
         {
-            ERR("HeapAlloc failed for pwszEnvironment with last error %lu!\n", GetLastError());
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
 
@@ -159,7 +161,8 @@ GetPrintProcessorDirectoryA(PSTR pName, PSTR pEnvironment, DWORD Level, PBYTE pP
         pwszPrintProcessorInfo = HeapAlloc(hProcessHeap, 0, cbBuf);
         if (!pwszPrintProcessorInfo)
         {
-            ERR("HeapAlloc failed for pwszPrintProcessorInfo with last error %lu!\n", GetLastError());
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            ERR("HeapAlloc failed!\n");
             goto Cleanup;
         }
     }
index 21140cd..1083ebc 100644 (file)
 246 stdcall GetPrintProcessorDirectoryA(str str long ptr long ptr)
 247 stdcall GetPrintProcessorDirectoryW(wstr wstr long ptr long ptr)
 248 stdcall GetPrinterA(long long ptr long ptr)
-249 stub GetPrinterDataA
-250 stub GetPrinterDataExA
-251 stub GetPrinterDataExW
+249 stdcall GetPrinterDataA(long str ptr ptr long ptr)
+250 stdcall GetPrinterDataExA(long str str ptr ptr long ptr)
+251 stdcall GetPrinterDataExW(long wstr wstr ptr ptr long ptr)
 252 stdcall GetPrinterDataW(long wstr ptr ptr long ptr)
 253 stdcall GetPrinterDriverA(long str long ptr long ptr)
 254 stdcall -stub GetPrinterDriverDirectoryA(str str long ptr long ptr)
 280 stub SetPortA
 281 stub SetPortW
 282 stub SetPrinterA
-283 stub SetPrinterDataA
-284 stub SetPrinterDataExA
-285 stub SetPrinterDataExW
+283 stdcall SetPrinterDataA(long str long ptr long)
+284 stdcall SetPrinterDataExA(long str str long ptr long)
+285 stdcall SetPrinterDataExW(long wstr wstr long ptr long)
 286 stdcall SetPrinterDataW(long wstr long ptr long)
 287 stdcall SetPrinterW(long long ptr long)
 288 stub SplDriverUnloadComplete
index 365f84b..fb6e988 100644 (file)
@@ -9,6 +9,7 @@ list(APPEND SOURCE
     monitors.c
     ports.c
     precomp.h
+    printerdata.c
     printers.c
     printingthread.c
     printprocessors.c
@@ -22,6 +23,6 @@ add_library(localspl SHARED
 
 set_module_type(localspl win32dll UNICODE)
 target_link_libraries(localspl skiplist16 wine)
-add_importlibs(localspl advapi32 rpcrt4 spoolss msvcrt kernel32 ntdll)
+add_importlibs(localspl advapi32 netapi32 rpcrt4 secur32 spoolss msvcrt kernel32 ntdll)
 add_pch(localspl precomp.h SOURCE)
 add_cd_file(TARGET localspl DESTINATION reactos/system32 FOR all)
index 2479b77..69b5982 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 for managing print jobs
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -135,26 +135,20 @@ _PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
 DWORD
 GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput)
 {
-    const WCHAR wszPrintersPath[] = L"\\PRINTERS\\";
-    const DWORD cchPrintersPath = _countof(wszPrintersPath) - 1;
-    const DWORD cchSpoolerFile = sizeof("?????.") - 1;
-    const DWORD cchExtension = sizeof("SPL") - 1;                   // pwszExtension may be L"SPL" or L"SHD", same length for both!
-
     if (pwszOutput)
     {
-        CopyMemory(pwszOutput, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
-        CopyMemory(&pwszOutput[cchSpoolDirectory], wszPrintersPath, cchPrintersPath * sizeof(WCHAR));
-        swprintf(&pwszOutput[cchSpoolDirectory + cchPrintersPath], L"%05lu.", dwJobID);
-        CopyMemory(&pwszOutput[cchSpoolDirectory + cchPrintersPath + cchSpoolerFile], pwszExtension, (cchExtension + 1) * sizeof(WCHAR));
+        CopyMemory(pwszOutput, wszJobDirectory, cchJobDirectory * sizeof(WCHAR));
+        swprintf(&pwszOutput[cchJobDirectory], L"\\%05lu.%s", dwJobID, pwszExtension);
     }
 
-    return (cchSpoolDirectory + cchPrintersPath + cchSpoolerFile + cchExtension + 1) * sizeof(WCHAR);
+    // pwszExtension may be L"SPL" or L"SHD", same length for both!
+    return (cchJobDirectory + sizeof("\\?????.SPL")) * sizeof(WCHAR);
 }
 
 BOOL
 InitializeGlobalJobList()
 {
-    const WCHAR wszPath[] = L"\\PRINTERS\\?????.SHD";
+    const WCHAR wszPath[] = L"\\?????.SHD";
     const DWORD cchPath = _countof(wszPath) - 1;
 
     DWORD dwErrorCode;
@@ -173,8 +167,8 @@ InitializeGlobalJobList()
     InitializeSkiplist(&GlobalJobList, DllAllocSplMem, _GlobalJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
 
     // Construct the full path search pattern.
-    CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
-    CopyMemory(&wszFullPath[cchSpoolDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR));
+    CopyMemory(wszFullPath, wszJobDirectory, cchJobDirectory * sizeof(WCHAR));
+    CopyMemory(&wszFullPath[cchJobDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR));
 
     // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
     hFind = FindFirstFileW(wszFullPath, &FindData);
@@ -262,7 +256,7 @@ CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle)
     if (!pJob)
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed!\n");
         goto Cleanup;
     }
 
@@ -1209,7 +1203,7 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
     pShadowFile = DllAllocSplMem(cbFileSize);
     if (!pShadowFile)
     {
-        ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
+        ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath);
         goto Cleanup;
     }
 
@@ -1249,7 +1243,7 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
     pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
     if (!pJob)
     {
-        ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
+        ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath);
         goto Cleanup;
     }
 
@@ -1341,7 +1335,7 @@ WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob)
     pShadowFile = DllAllocSplMem(cbFileSize);
     if (!pShadowFile)
     {
-        ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
+        ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath);
         goto Cleanup;
     }
 
index 4b2e75a..44b7eea 100644 (file)
@@ -8,12 +8,20 @@
 #include "precomp.h"
 
 // Global Variables
+HKEY hPrintKey = NULL;
+HKEY hPrintersKey = NULL;
+WCHAR wszJobDirectory[MAX_PATH];
+DWORD cchJobDirectory;
 WCHAR wszSpoolDirectory[MAX_PATH];
 DWORD cchSpoolDirectory;
 
 // Global Constants
 #include <prtprocenv.h>
 
+/** This is what the Spooler of Windows Server 2003 returns (for example using GetPrinterDataExW, SPLREG_MAJOR_VERSION/SPLREG_MINOR_VERSION) */
+const DWORD dwSpoolerMajorVersion = 3;
+const DWORD dwSpoolerMinorVersion = 0;
+
 const WCHAR wszDefaultDocumentName[] = L"Local Downlevel Document";
 
 PWSTR wszPrintProviderInfo[3] = {
@@ -52,8 +60,8 @@ static const PRINTPROVIDOR _PrintProviderFunctions = {
     LocalEndDocPrinter,                         // fpEndDocPrinter
     LocalAddJob,                                // fpAddJob
     LocalScheduleJob,                           // fpScheduleJob
-    NULL,                                       // fpGetPrinterData
-    NULL,                                       // fpSetPrinterData
+    LocalGetPrinterData,                        // fpGetPrinterData
+    LocalSetPrinterData,                        // fpSetPrinterData
     NULL,                                       // fpWaitForPrinterChange
     LocalClosePrinter,                          // fpClosePrinter
     NULL,                                       // fpAddForm
@@ -89,8 +97,8 @@ static const PRINTPROVIDOR _PrintProviderFunctions = {
     NULL,                                       // fpClusterSplOpen
     NULL,                                       // fpClusterSplClose
     NULL,                                       // fpClusterSplIsAlive
-    NULL,                                       // fpSetPrinterDataEx
-    NULL,                                       // fpGetPrinterDataEx
+    LocalSetPrinterDataEx,                      // fpSetPrinterDataEx
+    LocalGetPrinterDataEx,                      // fpGetPrinterDataEx
     NULL,                                       // fpEnumPrinterDataEx
     NULL,                                       // fpEnumPrinterKey
     NULL,                                       // fpDeletePrinterDataEx
@@ -112,17 +120,112 @@ static const PRINTPROVIDOR _PrintProviderFunctions = {
     NULL,                                       // fpAddDriverCatalog
 };
 
-static void
-_GetSpoolDirectory()
+static BOOL
+_InitializeLocalSpooler(void)
 {
+    const WCHAR wszPrintersPath[] = L"\\PRINTERS";
+    const DWORD cchPrintersPath = _countof(wszPrintersPath) - 1;
     const WCHAR wszSpoolPath[] = L"\\spool";
     const DWORD cchSpoolPath = _countof(wszSpoolPath) - 1;
+    const WCHAR wszSymbolicLinkValue[] = L"REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers";
+    const DWORD cbSymbolicLinkValue = sizeof(wszSymbolicLinkValue) - sizeof(WCHAR);
+
+    BOOL bReturnValue = FALSE;
+    DWORD cbData;
+    DWORD dwErrorCode;
+    HKEY hKey;
+
+    // On startup, always create a volatile symbolic link in the registry if it doesn't exist yet.
+    //   "SYSTEM\CurrentControlSet\Control\Print\Printers" -> "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers"
+    //
+    // According to https://social.technet.microsoft.com/Forums/windowsserver/en-US/a683ab54-c43c-4ebe-af8f-1f7a65af2a51
+    // this is needed when having >900 printers to work around a size limit of the SYSTEM registry hive.
+    dwErrorCode = (DWORD)RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers", 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, KEY_CREATE_LINK | KEY_SET_VALUE, NULL, &hKey, NULL);
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Note that wszSymbolicLink has to be stored WITHOUT the terminating null character for the symbolic link to work!
+        // See cbSymbolicLinkValue above.
+        dwErrorCode = (DWORD)RegSetValueExW(hKey, L"SymbolicLinkValue", 0, REG_LINK, (PBYTE)wszSymbolicLinkValue, cbSymbolicLinkValue);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegSetValueExW failed for the Printers symlink with error %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+    }
+    else if (dwErrorCode != ERROR_ALREADY_EXISTS)
+    {
+        ERR("RegCreateKeyExW failed for the Printers symlink with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
 
-    // Get the system directory and append the "spool" subdirectory.
+    // Open some registry keys and leave them open. We need them multiple times throughout the Local Spooler.
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Print", 0, KEY_ALL_ACCESS, &hPrintKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed for \"Print\" with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers", 0, KEY_ALL_ACCESS, &hPrintersKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed for \"Printers\" with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Construct the path to "%SystemRoot%\system32\spool".
     // Forget about length checks here. If this doesn't fit into MAX_PATH, our OS has more serious problems...
     cchSpoolDirectory = GetSystemDirectoryW(wszSpoolDirectory, MAX_PATH);
     CopyMemory(&wszSpoolDirectory[cchSpoolDirectory], wszSpoolPath, (cchSpoolPath + 1) * sizeof(WCHAR));
     cchSpoolDirectory += cchSpoolPath;
+
+    // Query the job directory.
+    cbData = sizeof(wszJobDirectory);
+    dwErrorCode = (DWORD)RegQueryValueExW(hPrintersKey, SPLREG_DEFAULT_SPOOL_DIRECTORY, NULL, NULL, (PBYTE)wszJobDirectory, &cbData);
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        cchJobDirectory = cbData / sizeof(WCHAR) - 1;
+    }
+    else if (dwErrorCode == ERROR_FILE_NOT_FOUND)
+    {
+        // Use the default "%SystemRoot%\system32\spool\PRINTERS".
+        CopyMemory(wszJobDirectory, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
+        CopyMemory(&wszJobDirectory[cchSpoolDirectory], wszPrintersPath, (cchPrintersPath + 1) * sizeof(WCHAR));
+        cchJobDirectory = cchSpoolDirectory + cchPrintersPath;
+
+        // Save this for next time.
+        RegSetValueExW(hPrintersKey, SPLREG_DEFAULT_SPOOL_DIRECTORY, 0, REG_SZ, (PBYTE)wszJobDirectory, (cchJobDirectory + 1) * sizeof(WCHAR));
+    }
+    else
+    {
+        ERR("RegQueryValueExW failed for \"%S\" with error %lu!\n", SPLREG_DEFAULT_SPOOL_DIRECTORY, dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Initialize all lists.
+    if (!InitializePrintMonitorList())
+        goto Cleanup;
+
+    if (!InitializePortList())
+        goto Cleanup;
+
+    if (!InitializePrintProcessorList())
+        goto Cleanup;
+
+    if (!InitializePrinterList())
+        goto Cleanup;
+
+    if (!InitializeGlobalJobList())
+        goto Cleanup;
+
+    // Local Spooler Initialization finished successfully!
+    bReturnValue = TRUE;
+
+Cleanup:
+    if (hKey)
+        RegCloseKey(hKey);
+
+    return bReturnValue;
 }
 
 BOOL WINAPI
@@ -132,13 +235,7 @@ DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
     {
         case DLL_PROCESS_ATTACH:
             DisableThreadLibraryCalls(hinstDLL);
-            _GetSpoolDirectory();
-
-            return InitializePrintMonitorList() &&
-                   InitializePortList() &&
-                   InitializePrintProcessorList() &&
-                   InitializePrinterList() &&
-                   InitializeGlobalJobList();
+            return _InitializeLocalSpooler();
 
         default:
             return TRUE;
index c3ce99d..d9222a0 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 Print Monitors
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -103,7 +103,7 @@ InitializePrintMonitorList()
         if (!pPrintMonitor)
         {
             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            ERR("DllAllocSplMem failed!\n");
             goto Cleanup;
         }
 
@@ -112,7 +112,7 @@ InitializePrintMonitorList()
         if (!pPrintMonitor->pwszName)
         {
             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            ERR("DllAllocSplMem failed!\n");
             goto Cleanup;
         }
 
@@ -177,7 +177,7 @@ InitializePrintMonitorList()
                 if (!pwszRegistryPath)
                 {
                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-                    ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+                    ERR("DllAllocSplMem failed!\n");
                     goto Cleanup;
                 }
 
index 1685a8a..bb51d9c 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 Ports of the Print Monitors
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -79,7 +79,7 @@ InitializePortList()
         if (!pPortInfo1)
         {
             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            ERR("DllAllocSplMem failed!\n");
             goto Cleanup;
         }
 
@@ -108,7 +108,7 @@ InitializePortList()
             if (!pPort)
             {
                 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-                ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+                ERR("DllAllocSplMem failed!\n");
                 goto Cleanup;
             }
 
@@ -165,6 +165,9 @@ LocalEnumPorts(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNe
         pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
 
         // Call the EnumPorts function of this Print Monitor.
+        cbNeeded = 0;
+        dwReturned = 0;
+
         if (pPrintMonitor->bIsLevel2)
             bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnEnumPorts(pPrintMonitor->hMonitor, pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned);
         else
index 8419d5a..5241e43 100644 (file)
@@ -22,6 +22,8 @@
 #include <winreg.h>
 #include <winspool.h>
 #include <winsplp.h>
+#include <dsrole.h>
+#include <secext.h>
 #include <ndk/rtlfuncs.h>
 
 #define SKIPLIST_LEVELS 16
@@ -195,6 +197,7 @@ struct _LOCAL_HANDLE
     enum {
         HandleType_Port,                        /** pSpecificHandle is a PLOCAL_PORT_HANDLE. */
         HandleType_Printer,                     /** pSpecificHandle is a PLOCAL_PRINTER_HANDLE. */
+        HandleType_PrintServer,                 /** pSpecificHandle is NULL (no additional information needed for a handle to the Print Server) */
         HandleType_Xcv                          /** pSpecificHandle is a PLOCAL_XCV_HANDLE. */
     }
     HandleType;
@@ -256,8 +259,14 @@ BOOL WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob);
 // main.c
 extern const WCHAR wszCurrentEnvironment[];
 extern const DWORD cbCurrentEnvironment;
+extern const DWORD dwSpoolerMajorVersion;
+extern const DWORD dwSpoolerMinorVersion;
 extern const WCHAR wszDefaultDocumentName[];
+extern HKEY hPrintKey;
+extern HKEY hPrintersKey;
 extern PWSTR wszPrintProviderInfo[3];
+extern WCHAR wszJobDirectory[MAX_PATH];
+extern DWORD cchJobDirectory;
 extern WCHAR wszSpoolDirectory[MAX_PATH];
 extern DWORD cchSpoolDirectory;
 
@@ -272,6 +281,12 @@ PLOCAL_PORT FindPort(PCWSTR pwszName);
 BOOL InitializePortList();
 BOOL WINAPI LocalEnumPorts(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned);
 
+// printerdata.c
+DWORD WINAPI LocalGetPrinterData(HANDLE hPrinter, PWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded);
+DWORD WINAPI LocalGetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded);
+DWORD WINAPI LocalSetPrinterData(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData);
+DWORD WINAPI LocalSetPrinterDataEx(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData);
+
 // printers.c
 extern SKIPLIST PrinterList;
 BOOL InitializePrinterList();
diff --git a/reactos/win32ss/printing/providers/localspl/printerdata.c b/reactos/win32ss/printing/providers/localspl/printerdata.c
new file mode 100644 (file)
index 0000000..8075095
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Functions related to Printer Configuration Data
+ * COPYRIGHT:   Copyright 2017 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD WINAPI
+LocalGetPrinterData(HANDLE hPrinter, PWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
+{
+    // The ReactOS Printing Stack forwards all GetPrinterData calls to GetPrinterDataEx as soon as possible.
+    // This function may only be called if localspl.dll is used together with Windows Printing Stack components.
+    WARN("This function should never be called!\n");
+    return LocalGetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded);
+}
+
+static DWORD
+_MakePrinterSubKey(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PWSTR* ppwszSubKey)
+{
+    const WCHAR wszBackslash[] = L"\\";
+
+    size_t cbSubKey;
+    PWSTR p;
+
+    // Sanity check
+    if (!pKeyName)
+        return ERROR_INVALID_PARAMETER;
+
+    // Allocate a buffer for the subkey "PrinterName\KeyName".
+    cbSubKey = (wcslen(pPrinterHandle->pPrinter->pwszPrinterName) + 1 + wcslen(pKeyName) + 1) * sizeof(WCHAR);
+    *ppwszSubKey = DllAllocSplMem(cbSubKey);
+    if (!*ppwszSubKey)
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    // Concatenate the subkey.
+    p = *ppwszSubKey;
+    StringCbCopyExW(p, cbSubKey, pPrinterHandle->pPrinter->pwszPrinterName, &p, &cbSubKey, 0);
+    StringCbCopyExW(p, cbSubKey, wszBackslash, &p, &cbSubKey, 0);
+    StringCbCopyExW(p, cbSubKey, pKeyName, &p, &cbSubKey, 0);
+
+    return ERROR_SUCCESS;
+}
+
+static DWORD
+_LocalGetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
+{
+    DWORD dwErrorCode;
+    HKEY hKey = NULL;
+    PWSTR pwszSubKey = NULL;
+
+    dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    // Open the subkey.
+    dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_READ, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Query the desired value.
+    *pcbNeeded = nSize;
+    dwErrorCode = (DWORD)RegQueryValueExW(hKey, pValueName, NULL, pType, pData, pcbNeeded);
+
+Cleanup:
+    if (hKey)
+        RegCloseKey(hKey);
+
+    if (pwszSubKey)
+        DllFreeSplMem(pwszSubKey);
+
+    return dwErrorCode;
+}
+
+static DWORD
+_LocalGetPrintServerHandleData(PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
+{
+    DWORD dwErrorCode;
+
+    if (wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 ||
+        wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 ||
+        wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 ||
+        wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 ||
+        wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0)
+    {
+        *pcbNeeded = nSize;
+        return (DWORD)RegQueryValueExW(hPrintersKey, pValueName, NULL, pType, pData, pcbNeeded);
+    }
+    else if (wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY_DEFAULT) == 0 ||
+        wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY_DEFAULT) == 0)
+    {
+        // Store a DWORD value as REG_NONE.
+        *pType = REG_NONE;
+        *pcbNeeded = sizeof(DWORD);
+        if (nSize < *pcbNeeded)
+            return ERROR_MORE_DATA;
+
+        // Apparently, these values don't serve a purpose anymore.
+        *((PDWORD)pData) = 0;
+        return ERROR_SUCCESS;
+    }
+    else if (wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 ||
+        wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 ||
+        wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 ||
+        wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 ||
+        wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 ||
+        wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0)
+    {
+        HKEY hKey;
+
+        dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_READ, &hKey);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode);
+            return dwErrorCode;
+        }
+
+        *pcbNeeded = nSize;
+        dwErrorCode = (DWORD)RegQueryValueExW(hKey, pValueName, NULL, pType, pData, pcbNeeded);
+        RegCloseKey(hKey);
+        return dwErrorCode;
+    }
+    else if (wcsicmp(pValueName, SPLREG_MAJOR_VERSION) == 0)
+    {
+        // Store a DWORD value as REG_NONE.
+        *pType = REG_NONE;
+        *pcbNeeded = sizeof(DWORD);
+        if (nSize < *pcbNeeded)
+            return ERROR_MORE_DATA;
+
+        // Apparently, these values don't serve a purpose anymore.
+        *((PDWORD)pData) = dwSpoolerMajorVersion;
+        return ERROR_SUCCESS;
+    }
+    else if (wcsicmp(pValueName, SPLREG_MINOR_VERSION) == 0)
+    {
+        // Store a DWORD value as REG_NONE.
+        *pType = REG_NONE;
+        *pcbNeeded = sizeof(DWORD);
+        if (nSize < *pcbNeeded)
+            return ERROR_MORE_DATA;
+
+        // Apparently, these values don't serve a purpose anymore.
+        *((PDWORD)pData) = dwSpoolerMinorVersion;
+        return ERROR_SUCCESS;
+    }
+    else if (wcsicmp(pValueName, SPLREG_ARCHITECTURE) == 0)
+    {
+        // Store a string as REG_NONE with the length of the environment name string.
+        *pType = REG_NONE;
+        *pcbNeeded = cbCurrentEnvironment;
+        if (nSize < *pcbNeeded)
+            return ERROR_MORE_DATA;
+
+        // Copy the environment name as the output value for SPLREG_ARCHITECTURE.
+        CopyMemory(pData, wszCurrentEnvironment, cbCurrentEnvironment);
+        return ERROR_SUCCESS;
+    }
+    else if (wcsicmp(pValueName, SPLREG_OS_VERSION) == 0)
+    {
+        POSVERSIONINFOW pInfo = (POSVERSIONINFOW)pData;
+
+        // Store the OSVERSIONINFOW structure as REG_NONE.
+        *pType = REG_NONE;
+        *pcbNeeded = sizeof(OSVERSIONINFOW);
+        if (nSize < *pcbNeeded)
+            return ERROR_MORE_DATA;
+
+        // Return OS version information.
+        pInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
+        GetVersionExW(pInfo);
+        return ERROR_SUCCESS;
+    }
+    else if (wcsicmp(pValueName, SPLREG_OS_VERSIONEX) == 0)
+    {
+        POSVERSIONINFOEXW pInfo = (POSVERSIONINFOEXW)pData;
+
+        // Store the OSVERSIONINFOEXW structure as REG_NONE.
+        *pType = REG_NONE;
+        *pcbNeeded = sizeof(OSVERSIONINFOEXW);
+        if (nSize < *pcbNeeded)
+            return ERROR_MORE_DATA;
+
+        // Return extended OS version information.
+        pInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+        GetVersionExW((POSVERSIONINFOW)pInfo);
+        return ERROR_SUCCESS;
+    }
+    else if (wcsicmp(pValueName, SPLREG_DS_PRESENT) == 0)
+    {
+        PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pInfo;
+
+        // We want to store a REG_DWORD value.
+        *pType = REG_DWORD;
+        *pcbNeeded = sizeof(DWORD);
+        if (nSize < *pcbNeeded)
+            return ERROR_MORE_DATA;
+
+        // Get information about the domain membership of this computer.
+        dwErrorCode = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE*)&pInfo);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("DsRoleGetPrimaryDomainInformation failed with error %lu!\n", GetLastError());
+            return dwErrorCode;
+        }
+
+        // Return whether this computer is a workstation or server inside a domain.
+        *((PDWORD)pData) = (pInfo->MachineRole == DsRole_RoleMemberWorkstation || pInfo->MachineRole == DsRole_RoleMemberServer);
+        DsRoleFreeMemory(pInfo);
+        return ERROR_SUCCESS;
+    }
+    else if (wcsicmp(pValueName, SPLREG_DS_PRESENT_FOR_USER) == 0)
+    {
+        DWORD cch;
+        PWSTR pwszUserSam;
+        PWSTR p;
+        WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+
+        // We want to store a REG_DWORD value.
+        *pType = REG_DWORD;
+        *pcbNeeded = sizeof(DWORD);
+        if (nSize < *pcbNeeded)
+            return ERROR_MORE_DATA;
+
+        // Get the local Computer Name.
+        cch = MAX_COMPUTERNAME_LENGTH + 1;
+        if (!GetComputerNameW(wszComputerName, &cch))
+        {
+            ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
+            return GetLastError();
+        }
+
+        // Get the User Name in the SAM format.
+        // This could either be:
+        //     COMPUTERNAME\User
+        //     DOMAINNAME\User
+        cch = 0;
+        GetUserNameExW(NameSamCompatible, NULL, &cch);
+        dwErrorCode = GetLastError();
+        if (dwErrorCode != ERROR_MORE_DATA)
+        {
+            ERR("GetUserNameExW failed with error %lu!\n", dwErrorCode);
+            return dwErrorCode;
+        }
+
+        pwszUserSam = DllAllocSplMem(cch * sizeof(WCHAR));
+        if (!pwszUserSam)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("DllAllocSplMem failed!\n");
+            return dwErrorCode;
+        }
+
+        if (!GetUserNameExW(NameSamCompatible, pwszUserSam, &cch))
+        {
+            dwErrorCode = GetLastError();
+            ERR("GetUserNameExW failed with error %lu!\n", dwErrorCode);
+            DllFreeSplMem(pwszUserSam);
+            return dwErrorCode;
+        }
+
+        // Terminate the SAM-formatted User Name at the backslash.
+        p = wcschr(pwszUserSam, L'\\');
+        *p = 0;
+
+        // Compare it with the Computer Name.
+        // If they differ, this User is part of a domain.
+        *((PDWORD)pData) = (wcscmp(pwszUserSam, wszComputerName) != 0);
+        DllFreeSplMem(pwszUserSam);
+        return ERROR_SUCCESS;
+    }
+    else if (wcsicmp(pValueName, SPLREG_REMOTE_FAX) == 0)
+    {
+        // Store a DWORD value as REG_NONE.
+        *pType = REG_NONE;
+        *pcbNeeded = sizeof(DWORD);
+        if (nSize < *pcbNeeded)
+            return ERROR_MORE_DATA;
+
+        // TODO: We don't support any fax service yet, but let's return the same value as Windows Server 2003 here.
+        *((PDWORD)pData) = 1;
+        return ERROR_SUCCESS;
+    }
+    else if (wcsicmp(pValueName, SPLREG_DNS_MACHINE_NAME) == 0)
+    {
+        DWORD cchDnsName = 0;
+
+        // Get the length of the fully-qualified computer DNS name.
+        GetComputerNameExW(ComputerNameDnsFullyQualified, NULL, &cchDnsName);
+        dwErrorCode = GetLastError();
+        if (dwErrorCode != ERROR_MORE_DATA)
+        {
+            ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode);
+            return dwErrorCode;
+        }
+
+        // Check if our supplied buffer is large enough.
+        *pType = REG_SZ;
+        *pcbNeeded = cchDnsName * sizeof(WCHAR);
+        if (nSize < *pcbNeeded)
+            return ERROR_MORE_DATA;
+
+        // Get the actual DNS name.
+        if (!GetComputerNameExW(ComputerNameDnsFullyQualified, (PWSTR)pData, &cchDnsName))
+        {
+            dwErrorCode = GetLastError();
+            ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode);
+            return dwErrorCode;
+        }
+
+        // Lowercase the output just like Windows does.
+        _wcslwr((PWSTR)pData);
+        return ERROR_SUCCESS;
+    }
+    else
+    {
+        // For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
+        // That also includes SPLREG_WEBSHAREMGMT, which is supported in Windows Server 2003 according to the documentation,
+        // but is actually not!
+        return ERROR_INVALID_PARAMETER;
+    }
+}
+
+DWORD WINAPI
+LocalGetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
+{
+    DWORD dwErrorCode;
+    DWORD dwTemp;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+
+    // Even if GetPrinterDataExW in winspool ensures that the RPC function is never called without a valid pointer for pType,
+    // it's officially optional. Windows' fpGetPrinterDataEx also works with NULL for pType!
+    // Ensure here that it is always set to simplify the code later.
+    if (!pType)
+        pType = &dwTemp;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+    }
+    else if (!pcbNeeded)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+    }
+    else if (pHandle->HandleType == HandleType_Printer)
+    {
+        dwErrorCode = _LocalGetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
+    }
+    else if (pHandle->HandleType == HandleType_PrintServer)
+    {
+        dwErrorCode = _LocalGetPrintServerHandleData(pValueName, pType, pData, nSize, pcbNeeded);
+    }
+    else
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+    }
+
+    SetLastError(dwErrorCode);
+    return dwErrorCode;
+}
+
+DWORD WINAPI
+LocalSetPrinterData(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
+{
+    // The ReactOS Printing Stack forwards all SetPrinterData calls to SetPrinterDataEx as soon as possible.
+    // This function may only be called if localspl.dll is used together with Windows Printing Stack components.
+    WARN("This function should never be called!\n");
+    return LocalSetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData);
+}
+
+static DWORD
+_LocalSetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
+{
+    DWORD dwErrorCode;
+    HKEY hKey = NULL;
+    PWSTR pwszSubKey = NULL;
+
+    dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    // Open the subkey.
+    dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_SET_VALUE, &hKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Set the value.
+    dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData);
+
+Cleanup:
+    if (hKey)
+        RegCloseKey(hKey);
+
+    if (pwszSubKey)
+        DllFreeSplMem(pwszSubKey);
+
+    return dwErrorCode;
+}
+
+static DWORD
+_LocalSetPrintServerHandleData(PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
+{
+    DWORD dwErrorCode;
+
+    if (wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 ||
+        wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 ||
+        wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 ||
+        wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 ||
+        wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0)
+    {
+        return (DWORD)RegSetValueExW(hPrintersKey, pValueName, 0, Type, pData, cbData);
+    }
+    else if (wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 ||
+        wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 ||
+        wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 ||
+        wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 ||
+        wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 ||
+        wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0 ||
+        wcsicmp(pValueName, L"NoRemotePrinterDrivers") == 0)
+    {
+        HKEY hKey;
+
+        dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_SET_VALUE, &hKey);
+        if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode);
+            return dwErrorCode;
+        }
+
+        dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData);
+        RegCloseKey(hKey);
+        return dwErrorCode;
+    }
+    else if (wcsicmp(pValueName, SPLREG_WEBSHAREMGMT) == 0)
+    {
+        WARN("Attempting to set WebShareMgmt, which is based on IIS and therefore not supported. Returning fake success!\n");
+        return ERROR_SUCCESS;
+    }
+    else
+    {
+        // For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
+        return ERROR_INVALID_PARAMETER;
+    }
+}
+
+DWORD WINAPI
+LocalSetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
+{
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+    }
+    else if (pHandle->HandleType == HandleType_Printer)
+    {
+        dwErrorCode = _LocalSetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, Type, pData, cbData);
+    }
+    else if (pHandle->HandleType == HandleType_PrintServer)
+    {
+        dwErrorCode = _LocalSetPrintServerHandleData(pValueName, Type, pData, cbData);
+    }
+    else
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+    }
+
+    SetLastError(dwErrorCode);
+    return dwErrorCode;
+}
index 413c460..83af51b 100644 (file)
@@ -106,14 +106,11 @@ _PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
 BOOL
 InitializePrinterList()
 {
-    const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
-
     DWORD cbData;
     DWORD cchPrinterName;
     DWORD dwErrorCode;
     DWORD dwSubKeys;
     DWORD i;
-    HKEY hKey = NULL;
     HKEY hSubKey = NULL;
     PLOCAL_PORT pPort;
     PLOCAL_PRINTER pPrinter = NULL;
@@ -125,16 +122,8 @@ InitializePrinterList()
     // Initialize an empty list for our printers.
     InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
 
-    // Open our printers registry key. Each subkey is a local printer there.
-    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey);
-    if (dwErrorCode != ERROR_SUCCESS)
-    {
-        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
-        goto Cleanup;
-    }
-
-    // Get the number of subkeys.
-    dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    // Get the number of subkeys of the printers registry key. Each subkey is a local printer there.
+    dwErrorCode = (DWORD)RegQueryInfoKeyW(hPrintersKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
     if (dwErrorCode != ERROR_SUCCESS)
     {
         ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
@@ -180,7 +169,7 @@ InitializePrinterList()
 
         // Get the name of this printer.
         cchPrinterName = _countof(wszPrinterName);
-        dwErrorCode = (DWORD)RegEnumKeyExW(hKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL);
+        dwErrorCode = (DWORD)RegEnumKeyExW(hPrintersKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL);
         if (dwErrorCode == ERROR_MORE_DATA)
         {
             // This printer name exceeds the maximum length and is invalid.
@@ -193,7 +182,7 @@ InitializePrinterList()
         }
 
         // Open this Printer's registry key.
-        dwErrorCode = (DWORD)RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey);
+        dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, wszPrinterName, 0, KEY_READ, &hSubKey);
         if (dwErrorCode != ERROR_SUCCESS)
         {
             ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName, dwErrorCode);
@@ -231,7 +220,7 @@ InitializePrinterList()
         if (!pPrinter)
         {
             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            ERR("DllAllocSplMem failed!\n");
             goto Cleanup;
         }
 
@@ -280,7 +269,7 @@ InitializePrinterList()
         if (!pPrinter->pDefaultDevMode)
         {
             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            ERR("DllAllocSplMem failed!\n");
             goto Cleanup;
         }
 
@@ -351,10 +340,6 @@ Cleanup:
     if (pwszPrintProcessor)
         DllFreeSplStr(pwszPrintProcessor);
 
-    // Outside the loop
-    if (hKey)
-        RegCloseKey(hKey);
-
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);
 }
@@ -885,8 +870,8 @@ LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DW
     WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
     PLOCAL_PRINTER pPrinter;
 
-    ASSERT(pcbNeeded);
-    ASSERT(pcReturned);
+    // Do no sanity checks or assertions for pcbNeeded and pcReturned here.
+    // This is verified and required by localspl_apitest!
 
     // Begin counting.
     *pcbNeeded = 0;
@@ -984,6 +969,13 @@ LocalGetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDW
     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
     PLOCAL_PRINTER_HANDLE pPrinterHandle;
 
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
     // Check if this is a printer handle.
     if (pHandle->HandleType != HandleType_Printer)
     {
@@ -1020,41 +1012,358 @@ Cleanup:
     return (dwErrorCode == ERROR_SUCCESS);
 }
 
-BOOL WINAPI
-LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
+static DWORD
+_LocalOpenPortHandle(PWSTR pwszPortName, PHANDLE phPrinter)
 {
     BOOL bReturnValue;
-    DWORD cchComputerName;
-    DWORD cchFirstParameter;
     DWORD dwErrorCode;
-    DWORD dwJobID;
-    HANDLE hExternalHandle;
-    PWSTR p = lpPrinterName;
-    PWSTR pwszFirstParameter = NULL;
-    PWSTR pwszSecondParameter = NULL;
-    PLOCAL_JOB pJob;
+    HANDLE hPort;
     PLOCAL_HANDLE pHandle = NULL;
     PLOCAL_PORT pPort;
     PLOCAL_PORT_HANDLE pPortHandle = NULL;
     PLOCAL_PRINT_MONITOR pPrintMonitor;
+
+    // Look for this port in our Print Monitor Port list.
+    pPort = FindPort(pwszPortName);
+    if (!pPort)
+    {
+        // The supplied port is unknown to all our Print Monitors.
+        dwErrorCode = ERROR_INVALID_NAME;
+        goto Failure;
+    }
+
+    pPrintMonitor = pPort->pPrintMonitor;
+
+    // Call the monitor's OpenPort function.
+    if (pPrintMonitor->bIsLevel2)
+        bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszPortName, &hPort);
+    else
+        bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszPortName, &hPort);
+
+    if (!bReturnValue)
+    {
+        // The OpenPort function failed. Return its last error.
+        dwErrorCode = GetLastError();
+        goto Failure;
+    }
+
+    // Create a new generic handle.
+    pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed!\n");
+        goto Failure;
+    }
+
+    // Create a new LOCAL_PORT_HANDLE.
+    pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE));
+    if (!pPortHandle)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed!\n");
+        goto Failure;
+    }
+
+    pPortHandle->hPort = hPort;
+    pPortHandle->pPort = pPort;
+
+    // Make the generic handle a Port handle.
+    pHandle->HandleType = HandleType_Port;
+    pHandle->pSpecificHandle = pPortHandle;
+
+    // Return it.
+    *phPrinter = (HANDLE)pHandle;
+    return ERROR_SUCCESS;
+
+Failure:
+    if (pHandle)
+        DllFreeSplMem(pHandle);
+
+    if (pPortHandle)
+        DllFreeSplMem(pPortHandle);
+
+    return dwErrorCode;
+}
+
+static DWORD
+_LocalOpenPrinterHandle(PWSTR pwszPrinterName, PWSTR pwszJobParameter, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault)
+{
+    DWORD dwErrorCode;
+    DWORD dwJobID;
+    PLOCAL_HANDLE pHandle = NULL;
+    PLOCAL_JOB pJob;
     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.
+    // Retrieve the printer from the list.
+    pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
+    if (!pPrinter)
+    {
+        // The printer does not exist.
+        dwErrorCode = ERROR_INVALID_NAME;
+        goto Failure;
+    }
 
-    // Sanity checks
-    if (!lpPrinterName || !phPrinter)
+    // Create a new LOCAL_PRINTER_HANDLE.
+    pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
+    if (!pPrinterHandle)
     {
-        dwErrorCode = ERROR_INVALID_PARAMETER;
-        goto Cleanup;
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed!\n");
+        goto Failure;
+    }
+
+    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 Failure;
+        }
+
+        pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
+    }
+    else
+    {
+        // Use the default datatype.
+        pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
+    }
+
+    // Check if a DevMode was given, otherwise use the default.
+    if (pDefault && pDefault->pDevMode)
+        pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode);
+    else
+        pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
+
+    // Check if the caller wants a handle to an existing Print Job.
+    if (pwszJobParameter)
+    {
+        // The "Job " string has to follow now.
+        if (wcsncmp(pwszJobParameter, L"Job ", 4) != 0)
+        {
+            dwErrorCode = ERROR_INVALID_NAME;
+            goto Failure;
+        }
+
+        // Skip the "Job " string. 
+        pwszJobParameter += 4;
+
+        // Skip even more whitespace.
+        while (*pwszJobParameter == ' ')
+            ++pwszJobParameter;
+
+        // Finally extract the desired Job ID.
+        dwJobID = wcstoul(pwszJobParameter, NULL, 10);
+        if (!IS_VALID_JOB_ID(dwJobID))
+        {
+            // The user supplied an invalid Job ID.
+            dwErrorCode = ERROR_INVALID_NAME;
+            goto Failure;
+        }
+
+        // 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 Failure;
+        }
+
+        // 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 Failure;
+        }
+
+        // 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;
+    }
+
+    // Make the generic handle a Port handle.
+    pHandle->HandleType = HandleType_Printer;
+    pHandle->pSpecificHandle = pPrinterHandle;
+
+    // Return it.
+    *phPrinter = (HANDLE)pHandle;
+    return ERROR_SUCCESS;
+
+Failure:
+    if (pHandle)
+        DllFreeSplMem(pHandle);
+
+    if (pPrinterHandle)
+    {
+        if (pPrinterHandle->pwszDatatype)
+            DllFreeSplStr(pPrinterHandle->pwszDatatype);
+
+        if (pPrinterHandle->pDevMode)
+            DllFreeSplMem(pPrinterHandle->pDevMode);
+
+        DllFreeSplMem(pPrinterHandle);
+    }
+
+    return dwErrorCode;
+}
+
+static DWORD
+_LocalOpenPrintServerHandle(PHANDLE phPrinter)
+{
+    PLOCAL_HANDLE pHandle;
+
+    // Create a new generic handle.
+    pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
+    if (!pHandle)
+    {
+        ERR("DllAllocSplMem failed!\n");
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
+
+    // Make the generic handle a Print Server handle.
+    pHandle->HandleType = HandleType_PrintServer;
+    pHandle->pSpecificHandle = NULL;
+
+    // Return it.
+    *phPrinter = (HANDLE)pHandle;
+    return ERROR_SUCCESS;
+}
+
+static DWORD
+_LocalOpenXcvHandle(PWSTR pwszParameter, PHANDLE phPrinter)
+{
+    BOOL bReturnValue;
+    DWORD dwErrorCode;
+    HANDLE hXcv;
+    PLOCAL_HANDLE pHandle = NULL;
+    PLOCAL_PORT pPort;
+    PLOCAL_PRINT_MONITOR pPrintMonitor;
+    PLOCAL_XCV_HANDLE pXcvHandle = NULL;
+
+    // Skip the "Xcv" string.
+    pwszParameter += 3;
+
+    // Is XcvMonitor or XcvPort requested?
+    if (wcsncmp(pwszParameter, L"Monitor ", 8) == 0)
+    {
+        // Skip the "Monitor " string.
+        pwszParameter += 8;
+
+        // Look for this monitor in our Print Monitor list.
+        pPrintMonitor = FindPrintMonitor(pwszParameter);
+        if (!pPrintMonitor)
+        {
+            // The caller supplied a non-existing Monitor name.
+            dwErrorCode = ERROR_INVALID_NAME;
+            goto Failure;
+        }
+    }
+    else if (wcsncmp(pwszParameter, L"Port ", 5) == 0)
+    {
+        // Skip the "Port " string.
+        pwszParameter += 5;
+
+        // Look for this port in our Print Monitor Port list.
+        pPort = FindPort(pwszParameter);
+        if (!pPort)
+        {
+            // The supplied port is unknown to all our Print Monitors.
+            dwErrorCode = ERROR_INVALID_NAME;
+            goto Failure;
+        }
+
+        pPrintMonitor = pPort->pPrintMonitor;
+    }
+    else
+    {
+        dwErrorCode = ERROR_INVALID_NAME;
+        goto Failure;
+    }
+
+    // Call the monitor's XcvOpenPort function.
+    if (pPrintMonitor->bIsLevel2)
+        bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszParameter, SERVER_EXECUTE, &hXcv);
+    else
+        bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszParameter, SERVER_EXECUTE, &hXcv);
+
+    if (!bReturnValue)
+    {
+        // The XcvOpenPort function failed. Return its last error.
+        dwErrorCode = GetLastError();
+        goto Failure;
     }
 
+    // Create a new generic handle.
+    pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed!\n");
+        goto Failure;
+    }
+
+    // Create a new LOCAL_XCV_HANDLE.
+    pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE));
+    if (!pXcvHandle)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed!\n");
+        goto Failure;
+    }
+
+    pXcvHandle->hXcv = hXcv;
+    pXcvHandle->pPrintMonitor = pPrintMonitor;
+
+    // Make the generic handle a Xcv handle.
+    pHandle->HandleType = HandleType_Xcv;
+    pHandle->pSpecificHandle = pXcvHandle;
+
+    // Return it.
+    *phPrinter = (HANDLE)pHandle;
+    return ERROR_SUCCESS;
+
+Failure:
+    if (pHandle)
+        DllFreeSplMem(pHandle);
+
+    if (pXcvHandle)
+        DllFreeSplMem(pXcvHandle);
+
+    return dwErrorCode;
+}
+
+BOOL WINAPI
+LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
+{
+    DWORD cchComputerName;
+    DWORD cchFirstParameter;
+    DWORD dwErrorCode;
+    PWSTR p = lpPrinterName;
+    PWSTR pwszFirstParameter = NULL;
+    PWSTR pwszSecondParameter;
+    WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+
+    ASSERT(phPrinter);
     *phPrinter = NULL;
 
+    if (!lpPrinterName)
+    {
+        // The caller wants a Print Server handle and provided a NULL string.
+        dwErrorCode = _LocalOpenPrintServerHandle(phPrinter);
+        goto Cleanup;
+    }
+
     // 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'\\')
@@ -1062,14 +1371,10 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
         // 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;
-        }
+        // Look for the terminating null character or closing backslash.
+        p = lpPrinterName;
+        while (*p != L'\0' && *p != L'\\')
+            p++;
 
         // Get the local computer name for comparison.
         cchComputerName = _countof(wszComputerName);
@@ -1085,7 +1390,16 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
         // 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;
+            dwErrorCode = ERROR_INVALID_NAME;
+            goto Cleanup;
+        }
+
+        // If lpPrinterName is only "\\COMPUTERNAME" with nothing more, the caller wants a handle to the local Print Server.
+        if (!*p)
+        {
+            // The caller wants a Print Server handle and provided a string like:
+            //    "\\COMPUTERNAME"
+            dwErrorCode = _LocalOpenPrintServerHandle(phPrinter);
             goto Cleanup;
         }
 
@@ -1103,7 +1417,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
     // We must have at least one parameter.
     if (!cchFirstParameter && !pwszSecondParameter)
     {
-        dwErrorCode = ERROR_INVALID_PRINTER_NAME;
+        dwErrorCode = ERROR_INVALID_NAME;
         goto Cleanup;
     }
 
@@ -1127,61 +1441,13 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
             ++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;
+        dwErrorCode = _LocalOpenPortHandle(pwszFirstParameter, phPrinter);
     }
     else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0)
     {
@@ -1190,75 +1456,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
         //    "\\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;
+        dwErrorCode = _LocalOpenXcvHandle(pwszSecondParameter, phPrinter);
     }
     else
     {
@@ -1267,130 +1465,10 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
         //    "\\COMPUTERNAME\HP DeskJet"
         //    "HP DeskJet, Job 5"
         //    "\\COMPUTERNAME\HP DeskJet, Job 5"
-
-        // Retrieve the printer from the list.
-        pPrinter = LookupElementSkiplist(&PrinterList, &pwszFirstParameter, NULL);
-        if (!pPrinter)
-        {
-            // The printer does not exist.
-            dwErrorCode = ERROR_INVALID_PRINTER_NAME;
-            goto Cleanup;
-        }
-
-        // Create a new LOCAL_PRINTER_HANDLE.
-        pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
-        if (!pPrinterHandle)
-        {
-            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
-            goto Cleanup;
-        }
-
-        pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE;
-        pPrinterHandle->pPrinter = pPrinter;
-
-        // Check if a datatype was given.
-        if (pDefault && pDefault->pDatatype)
-        {
-            // Use the datatype if it's valid.
-            if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
-            {
-                dwErrorCode = ERROR_INVALID_DATATYPE;
-                goto Cleanup;
-            }
-
-            pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
-        }
-        else
-        {
-            // Use the default datatype.
-            pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
-        }
-
-        // Check if a DevMode was given, otherwise use the default.
-        if (pDefault && pDefault->pDevMode)
-            pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode);
-        else
-            pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
-
-        // Check if the caller wants a handle to an existing Print Job.
-        if (pwszSecondParameter)
-        {
-            // The "Job " string has to follow now.
-            if (wcsncmp(pwszSecondParameter, L"Job ", 4) != 0)
-            {
-                dwErrorCode = ERROR_INVALID_PRINTER_NAME;
-                goto Cleanup;
-            }
-
-            // Skip the "Job " string. 
-            pwszSecondParameter += 4;
-
-            // Skip even more whitespace.
-            while (*pwszSecondParameter == ' ')
-                ++pwszSecondParameter;
-
-            // Finally extract the desired Job ID.
-            dwJobID = wcstoul(pwszSecondParameter, NULL, 10);
-            if (!IS_VALID_JOB_ID(dwJobID))
-            {
-                // The user supplied an invalid Job ID.
-                dwErrorCode = ERROR_INVALID_PRINTER_NAME;
-                goto Cleanup;
-            }
-
-            // Look for this job in the Global Job List.
-            pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
-            if (!pJob || pJob->pPrinter != pPrinter)
-            {
-                // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
-                dwErrorCode = ERROR_INVALID_PRINTER_NAME;
-                goto Cleanup;
-            }
-
-            // Try to open its SPL file.
-            GetJobFilePath(L"SPL", dwJobID, wszFullPath);
-            pPrinterHandle->hSPLFile = CreateFileW(wszFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
-            if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
-            {
-                dwErrorCode = GetLastError();
-                ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode, wszFullPath);
-                goto Cleanup;
-            }
-
-            // Associate the job to our Printer Handle, but don't set bStartedDoc.
-            // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
-            pPrinterHandle->pJob = pJob;
-        }
-
-        // Return the Printer handle through our general handle.
-        pHandle->HandleType = HandleType_Printer;
-        pHandle->pSpecificHandle = pPrinterHandle;
+        dwErrorCode = _LocalOpenPrinterHandle(pwszFirstParameter, pwszSecondParameter, phPrinter, pDefault);
     }
 
-    // We were successful! Return the handle.
-    *phPrinter = (HANDLE)pHandle;
-    dwErrorCode = ERROR_SUCCESS;
-
-    // Don't let the cleanup routines free this.
-    pHandle = NULL;
-    pPrinterHandle = NULL;
-
 Cleanup:
-    if (pHandle)
-        DllFreeSplMem(pHandle);
-
-    if (pPrinterHandle)
-    {
-        if (pPrinterHandle->pwszDatatype)
-            DllFreeSplStr(pPrinterHandle->pwszDatatype);
-
-        if (pPrinterHandle->pDevMode)
-            DllFreeSplMem(pPrinterHandle->pDevMode);
-
-        DllFreeSplMem(pPrinterHandle);
-    }
-
     if (pwszFirstParameter)
         DllFreeSplMem(pwszFirstParameter);
 
@@ -1778,13 +1856,42 @@ Cleanup:
     return (dwErrorCode == ERROR_SUCCESS);
 }
 
+static void
+_LocalClosePortHandle(PLOCAL_PORT_HANDLE pPortHandle)
+{
+    // 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);
+}
+
+static void
+_LocalClosePrinterHandle(PLOCAL_PRINTER_HANDLE pPrinterHandle)
+{
+    // Terminate any started job.
+    if (pPrinterHandle->pJob)
+        FreeJob(pPrinterHandle->pJob);
+
+    // Free memory for the fields.
+    DllFreeSplMem(pPrinterHandle->pDevMode);
+    DllFreeSplStr(pPrinterHandle->pwszDatatype);
+}
+
+static void
+_LocalCloseXcvHandle(PLOCAL_XCV_HANDLE pXcvHandle)
+{
+    // 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);
+}
+
 BOOL WINAPI
 LocalClosePrinter(HANDLE hPrinter)
 {
     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
-    PLOCAL_PORT_HANDLE pPortHandle;
-    PLOCAL_PRINTER_HANDLE pPrinterHandle;
-    PLOCAL_XCV_HANDLE pXcvHandle;
 
     if (!pHandle)
     {
@@ -1794,39 +1901,25 @@ LocalClosePrinter(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);
+        _LocalClosePortHandle(pHandle->pSpecificHandle);
     }
     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);
+        _LocalClosePrinterHandle(pHandle->pSpecificHandle);
+    }
+    else if (pHandle->HandleType == HandleType_PrintServer)
+    {
+        // Nothing to do.
     }
     else if (pHandle->HandleType == HandleType_Xcv)
     {
-        pXcvHandle = (PLOCAL_XCV_HANDLE)pHandle->pSpecificHandle;
-
-        // Call the monitor's XcvClosePort function.
-        if (pXcvHandle->pPrintMonitor->bIsLevel2)
-            ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv);
-        else
-            ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv);
+        _LocalCloseXcvHandle(pHandle->pSpecificHandle);
     }
 
-    // Free memory for the handle and the specific handle.
-    DllFreeSplMem(pHandle->pSpecificHandle);
+    // Free memory for the handle and the specific handle (if any).
+    if (pHandle->pSpecificHandle)
+        DllFreeSplMem(pHandle->pSpecificHandle);
+
     DllFreeSplMem(pHandle);
 
     return TRUE;
index d858267..9197c4a 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:     Implementation of the Thread that actually performs the printing process
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -32,7 +32,7 @@ PrintingThreadProc(PLOCAL_JOB pJob)
     if (!pwszPrinterPort)
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed!\n");
         goto Cleanup;
     }
 
@@ -71,7 +71,7 @@ PrintingThreadProc(PLOCAL_JOB pJob)
     if (!pwszPrinterAndJob)
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed!\n");
         goto Cleanup;
     }
 
@@ -100,7 +100,7 @@ PrintingThreadProc(PLOCAL_JOB pJob)
     if (!pwszSPLFile)
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed!\n");
         goto Cleanup;
     }
 
index 1b994c6..1b948da 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 Print Processors
- * COPYRIGHT:   Copyright 2015-2016 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -48,7 +48,8 @@ _OpenEnvironment(PCWSTR pEnvironment, PHKEY hKey)
     pwszEnvironmentKey = DllAllocSplMem((cchEnvironmentsKey + cchEnvironment + 1) * sizeof(WCHAR));
     if (!pwszEnvironmentKey)
     {
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed!\n");
         goto Cleanup;
     }
 
@@ -205,7 +206,7 @@ InitializePrintProcessorList()
         if (!pPrintProcessor)
         {
             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            ERR("DllAllocSplMem failed!\n");
             goto Cleanup;
         }
 
@@ -214,7 +215,7 @@ InitializePrintProcessorList()
         if (!pPrintProcessor->pwszName)
         {
             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            ERR("DllAllocSplMem failed!\n");
             goto Cleanup;
         }
 
@@ -311,7 +312,7 @@ InitializePrintProcessorList()
         if (!pPrintProcessor->pDatatypesInfo1)
         {
             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-            ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+            ERR("DllAllocSplMem failed!\n");
             goto Cleanup;
         }
 
@@ -514,7 +515,7 @@ LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE
     if (!pwszTemp)
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed!\n");
         goto Cleanup;
     }
 
index 06f3c1b..a69317c 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:     Various tools
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -41,7 +41,7 @@ AllocAndRegQueryWSZ(HKEY hKey, PCWSTR pwszValueName)
     pwszValue = DllAllocSplMem(cbNeeded);
     if (!pwszValue)
     {
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed!\n");
         return NULL;
     }
 
@@ -66,7 +66,7 @@ DuplicateDevMode(PDEVMODEW pInput)
     pOutput = DllAllocSplMem(pInput->dmSize + pInput->dmDriverExtra);
     if (!pOutput)
     {
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed!\n");
         return NULL;
     }
 
index 7f39341..803d3d0 100644 (file)
@@ -4,6 +4,7 @@ list(APPEND SOURCE
     EnumPrinters.c
     EnumPrintProcessorDatatypes.c
     GetDefaultPrinter.c
+    GetPrinterData.c
     GetPrintProcessorDirectory.c
     IsValidDevmode.c
     OpenPrinter.c
diff --git a/rostests/apitests/winspool/GetPrinterData.c b/rostests/apitests/winspool/GetPrinterData.c
new file mode 100644 (file)
index 0000000..b866555
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * PROJECT:     ReactOS Print Spooler DLL API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for GetPrinterData(Ex)A/GetPrinterData(Ex)W/SetPrinterData(Ex)A/SetPrinterData(Ex)W
+ * COPYRIGHT:   Copyright 2017 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+#include <winreg.h>
+
+/* From printing/include/spoolss.h */
+#define MAX_PRINTER_NAME        220
+
+typedef struct _SPLREG_VALUE
+{
+    PSTR pszName;
+    PWSTR pwszName;
+    DWORD dwType;
+    DWORD cbNeededA;
+    BOOL bSettable;
+}
+SPLREG_VALUE, *PSPLREG_VALUE;
+
+SPLREG_VALUE SplRegValues[] = {
+#if 0
+    { "DefaultSpoolDirectory", L"DefaultSpoolDirectory", REG_SZ, 0xFFFFFFFF, TRUE },
+    { "PortThreadPriorityDefault", L"PortThreadPriorityDefault", REG_NONE, 4, FALSE },
+    { "PortThreadPriority", L"PortThreadPriority", REG_DWORD, 4, TRUE },
+    { "SchedulerThreadPriorityDefault", L"SchedulerThreadPriorityDefault", REG_NONE, 4, FALSE },
+    { "SchedulerThreadPriority", L"SchedulerThreadPriority", REG_DWORD, 4, TRUE },
+    { "BeepEnabled", L"BeepEnabled", REG_DWORD, 4, TRUE },
+
+    /* These fail in Win8, probably removed since NT6:
+
+    { "NetPopup", L"NetPopup", REG_DWORD, 4, TRUE },
+    { "RetryPopup", L"RetryPopup", REG_DWORD, 4, TRUE },
+    { "NetPopupToComputer", L"NetPopupToComputer", REG_DWORD, 4, TRUE },
+
+    */
+
+    { "EventLog", L"EventLog", REG_DWORD, 4, TRUE },
+    { "MajorVersion", L"MajorVersion", REG_NONE, 4, FALSE },
+    { "MinorVersion", L"MinorVersion", REG_NONE, 4, FALSE },
+    { "Architecture", L"Architecture", REG_NONE, 0xFFFFFFFF, FALSE },
+    { "OSVersion", L"OSVersion", REG_NONE, sizeof(OSVERSIONINFOA), FALSE },
+    { "OSVersionEx", L"OSVersionEx", REG_NONE, sizeof(OSVERSIONINFOEXA), FALSE },
+    { "DsPresent", L"DsPresent", REG_DWORD, 4, FALSE },
+    { "DsPresentForUser", L"DsPresentForUser", REG_DWORD, 4, FALSE },
+#endif
+    { "RemoteFax", L"RemoteFax", REG_NONE, 4, FALSE },
+    { "RestartJobOnPoolError", L"RestartJobOnPoolError", REG_DWORD, 4, TRUE },
+    { "RestartJobOnPoolEnabled", L"RestartJobOnPoolEnabled", REG_DWORD, 4, TRUE },
+    { "DNSMachineName", L"DNSMachineName", REG_SZ, 0xFFFFFFFF, FALSE },
+    { "AllowUserManageForms", L"AllowUserManageForms", REG_DWORD, 4, TRUE },
+    { NULL, NULL, 0, 0, FALSE }
+};
+
+START_TEST(GetPrinterData)
+{
+    DWORD cbNeeded;
+    DWORD cchDefaultPrinter;
+    DWORD dwReturnCode;
+    DWORD dwType;
+    HANDLE hPrinter;
+    PBYTE pDataA;
+    PBYTE pDataW;
+    PSPLREG_VALUE p;
+    WCHAR wszDefaultPrinter[MAX_PRINTER_NAME + 1];
+
+    // Don't supply any parameters, this has to fail with ERROR_INVALID_HANDLE!
+    dwReturnCode = GetPrinterDataExW(NULL, NULL, NULL, NULL, NULL, 0, NULL);
+    ok(dwReturnCode == ERROR_INVALID_HANDLE, "GetPrinterDataExW returns error %lu!\n", dwReturnCode);
+
+    // Open a handle to the local print server.
+    if (!OpenPrinterW(NULL, &hPrinter, NULL))
+    {
+        skip("Could not retrieve a handle to the local print server!\n");
+        return;
+    }
+
+    // Now try with valid handle, but leave remaining parameters NULL.
+    dwReturnCode = GetPrinterDataExW(hPrinter, NULL, NULL, NULL, NULL, 0, NULL);
+    ok(dwReturnCode == RPC_X_NULL_REF_POINTER, "GetPrinterDataExW returns error %lu!\n", dwReturnCode);
+
+    // Try all valid Print Server data values.
+    for (p = SplRegValues; p->pszName; p++)
+    {
+        // Try the ANSI version of the function.
+        dwType = 0xDEADBEEF;
+        dwReturnCode = GetPrinterDataExA(hPrinter, NULL, p->pszName, &dwType, NULL, 0, &cbNeeded);
+        ok(dwReturnCode == ERROR_MORE_DATA || dwReturnCode == ERROR_FILE_NOT_FOUND, "GetPrinterDataExA returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
+        if (dwReturnCode != ERROR_MORE_DATA)
+            continue;
+
+        ok(dwType == p->dwType, "dwType is %lu for \"%s\"!\n", dwType, p->pszName);
+
+        if (p->cbNeededA < 0xFFFFFFFF)
+            ok(cbNeeded == p->cbNeededA, "cbNeeded is %lu for \"%s\", but expected %lu!\n", cbNeeded, p->pszName, p->cbNeededA);
+        else
+            ok(cbNeeded > 0, "cbNeeded is 0 for \"%s\"!\n", p->pszName);
+
+        pDataA = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
+        dwReturnCode = GetPrinterDataExA(hPrinter, NULL, p->pszName, NULL, pDataA, cbNeeded, &cbNeeded);
+        ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExA returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
+
+        // Try the Unicode version of the function too.
+        dwType = 0xDEADBEEF;
+        dwReturnCode = GetPrinterDataExW(hPrinter, NULL, p->pwszName, &dwType, NULL, 0, &cbNeeded);
+        ok(dwReturnCode == ERROR_MORE_DATA, "GetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
+        ok(dwType == p->dwType, "dwType is %lu for \"%s\"!\n", dwType, p->pszName);
+
+        pDataW = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
+        dwReturnCode = GetPrinterDataExW(hPrinter, NULL, p->pwszName, NULL, pDataW, cbNeeded, &cbNeeded);
+        ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
+
+        // Verify that OSVERSIONINFO structures are correctly returned.
+        if (strcmp(p->pszName, "OSVersion") == 0)
+        {
+            POSVERSIONINFOA pOSVersionInfoA = (POSVERSIONINFOA)pDataA;
+            POSVERSIONINFOW pOSVersionInfoW = (POSVERSIONINFOW)pDataW;
+            ok(pOSVersionInfoA->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoA->dwOSVersionInfoSize);
+            ok(pOSVersionInfoW->dwOSVersionInfoSize == sizeof(OSVERSIONINFOW), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoW->dwOSVersionInfoSize);
+        }
+        else if (strcmp(p->pszName, "OSVersionEx") == 0)
+        {
+            POSVERSIONINFOEXA pOSVersionInfoA = (POSVERSIONINFOEXA)pDataA;
+            POSVERSIONINFOEXW pOSVersionInfoW = (POSVERSIONINFOEXW)pDataW;
+            ok(pOSVersionInfoA->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoA->dwOSVersionInfoSize);
+            ok(pOSVersionInfoW->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXW), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoW->dwOSVersionInfoSize);
+        }
+
+        // Shortly test SetPrinterDataExW by setting the same data we just retrieved.
+        if (p->bSettable)
+        {
+            dwReturnCode = SetPrinterDataExW(hPrinter, NULL, p->pwszName, dwType, pDataW, cbNeeded);
+            ok(dwReturnCode == ERROR_SUCCESS, "SetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
+        }
+
+        HeapFree(GetProcessHeap(), 0, pDataA);
+        HeapFree(GetProcessHeap(), 0, pDataW);
+    }
+
+    // Try an invalid one.
+    dwReturnCode = GetPrinterDataExW(hPrinter, NULL, L"Invalid", NULL, NULL, 0, &cbNeeded);
+    ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
+
+    ClosePrinter(hPrinter);
+
+    // Open a handle to the default printer.
+    cchDefaultPrinter = _countof(wszDefaultPrinter);
+    ok(GetDefaultPrinterW(wszDefaultPrinter, &cchDefaultPrinter), "GetDefaultPrinterW returns FALSE and requires %lu characters!\n", cchDefaultPrinter);
+    if (!OpenPrinterW(wszDefaultPrinter, &hPrinter, NULL))
+    {
+        skip("Could not retrieve a handle to the default printer!\n");
+        return;
+    }
+
+    // Using NULL or L"" for pKeyName on a Printer handle yields ERROR_INVALID_PARAMETER.
+    dwReturnCode = GetPrinterDataExW(hPrinter, NULL, L"Name", NULL, NULL, 0, &cbNeeded);
+    ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
+    dwReturnCode = GetPrinterDataExW(hPrinter, L"", L"Name", NULL, NULL, 0, &cbNeeded);
+    ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
+
+    // Using L"\\" allows us to examine the contents of the main printer key anyway.
+    dwReturnCode = GetPrinterDataExW(hPrinter, L"\\", L"Name", &dwType, NULL, 0, &cbNeeded);
+    ok(dwReturnCode == ERROR_MORE_DATA, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
+    ok(dwType == REG_SZ, "dwType is %lu!\n", dwType);
+    ok(cbNeeded > 0, "cbNeeded is 0!\n");
+
+    pDataW = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
+    dwReturnCode = GetPrinterDataExW(hPrinter, L"\\", L"Name", NULL, pDataW, cbNeeded, &cbNeeded);
+    ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
+
+    // The following test fails if the default printer is a remote printer.
+    ok(wcscmp((PWSTR)pDataW, wszDefaultPrinter) == 0, "pDataW is \"%S\", default printer is \"%S\"!\n", (PWSTR)pDataW, wszDefaultPrinter);
+
+    // SetPrinterDataExW should return ERROR_ACCESS_DENIED when attempting to set the Name.
+    dwReturnCode = SetPrinterDataExW(hPrinter, L"\\", L"Name", REG_SZ, pDataW, cbNeeded);
+    ok(dwReturnCode == ERROR_ACCESS_DENIED, "SetPrinterDataExW returns %lu!\n", dwReturnCode);
+
+    HeapFree(GetProcessHeap(), 0, pDataW);
+}
index 74a70fb..1ab8275 100644 (file)
@@ -14,6 +14,7 @@ extern void func_ClosePrinter(void);
 extern void func_EnumPrinters(void);
 extern void func_EnumPrintProcessorDatatypes(void);
 extern void func_GetDefaultPrinter(void);
+extern void func_GetPrinterData(void);
 extern void func_GetPrintProcessorDirectoryA(void);
 extern void func_GetPrintProcessorDirectoryW(void);
 extern void func_IsValidDevmodeA(void);
@@ -27,6 +28,7 @@ const struct test winetest_testlist[] =
     { "EnumPrinters", func_EnumPrinters },
     { "EnumPrintProcessorDatatypes", func_EnumPrintProcessorDatatypes },
     { "GetDefaultPrinter", func_GetDefaultPrinter },
+    { "GetPrinterData", func_GetPrinterData },
     { "GetPrintProcessorDirectoryA", func_GetPrintProcessorDirectoryA },
     { "GetPrintProcessorDirectoryW", func_GetPrintProcessorDirectoryW },
     { "IsValidDevmodeA", func_IsValidDevmodeA },