[PRINTING]
authorColin Finck <colin@reactos.org>
Tue, 9 May 2017 15:44:42 +0000 (15:44 +0000)
committerColin Finck <colin@reactos.org>
Tue, 9 May 2017 15:44:42 +0000 (15:44 +0000)
- Implement GetDefaultPrinterA/W and SetDefaultPrinterA/W in winspool.drv. Also add tests for these functions.
- Set our "Dummy Printer on LPT1" as the default printer in the user registry.
- Return meaningful values for DeviceNotSelectedTimeout and TransmissionRetryTimeout in PRINTER_INFO_5 in localspl.

The Print dialog now preselects "Dummy Printer on LPT1" in all applications.
One more task done from the list at https://reactos.org/wiki/Printing :)

svn path=/trunk/; revision=74513

reactos/boot/bootdata/hivedef.inf
reactos/win32ss/printing/base/winspool/CMakeLists.txt
reactos/win32ss/printing/base/winspool/printers.c
reactos/win32ss/printing/base/winspool/winspool.spec
reactos/win32ss/printing/include/spoolss.h
reactos/win32ss/printing/providers/localspl/precomp.h
reactos/win32ss/printing/providers/localspl/printers.c
rostests/apitests/winspool/CMakeLists.txt
rostests/apitests/winspool/GetDefaultPrinter.c [new file with mode: 0644]
rostests/apitests/winspool/testlist.c

index 8532d85..1524322 100644 (file)
@@ -1837,13 +1837,13 @@ HKCU,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run","kbswitch.exe",2,"kbswitch.
 
 HKCU,"SOFTWARE\Microsoft\Windows NT",,0x00000012
 HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion",,0x00000012
-HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Devices",,0x00000012
+HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Devices","Dummy Printer On LPT1",2,"winspool,LPT1:"
 HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Network",,0x00000012
-HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\PrinterPorts",,0x00000012
+HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\PrinterPorts","Dummy Printer On LPT1",2,"winspool,LPT1:,15,45"
 HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Program Manager",,0x00000012
 
 HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows","DebugOptions",2,"2048"
-HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows","device",2,""
+HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows","Device",2,"Dummy Printer On LPT1,winspool,LPT1:"
 HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows","Documents",2,""
 HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows","DosPrint",2,"no"
 HKCU,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows","load",2,""
index f9c4f92..71d18ba 100644 (file)
@@ -26,6 +26,6 @@ add_library(winspool SHARED
 set_target_properties(winspool PROPERTIES SUFFIX ".drv")
 set_module_type(winspool win32dll UNICODE)
 target_link_libraries(winspool wine ${PSEH_LIB})
-add_importlibs(winspool gdi32 rpcrt4 msvcrt kernel32 ntdll)
+add_importlibs(winspool advapi32 gdi32 rpcrt4 msvcrt kernel32 ntdll)
 add_pch(winspool precomp.h SOURCE)
 add_cd_file(TARGET winspool DESTINATION reactos/system32 FOR all)
index a34a43f..53b113f 100644 (file)
@@ -7,6 +7,13 @@
 
 #include "precomp.h"
 
+// Local Constants
+
+/** And the award for the most confusingly named setting goes to "Device", for storing the default printer of the current user.
+    Ok, I admit that this has historical reasons. It's still not straightforward in any way though! */
+static const WCHAR wszWindowsKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
+static const WCHAR wszDeviceValue[] = L"Device";
+
 static void
 _MarshallUpPrinterInfo(PBYTE* ppPrinterInfo, DWORD Level)
 {
@@ -415,13 +422,131 @@ Cleanup:
 BOOL WINAPI
 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
 {
-    return FALSE;
+    DWORD dwErrorCode;
+    PWSTR pwszBuffer = NULL;
+
+    // Sanity check.
+    if (!pcchBuffer)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    // Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
+    if (pszBuffer && *pcchBuffer)
+    {
+        pwszBuffer = HeapAlloc(hProcessHeap, 0, *pcchBuffer * sizeof(WCHAR));
+        if (!pwszBuffer)
+        {
+            dwErrorCode = GetLastError();
+            ERR("HeapAlloc failed with error %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+    }
+
+    if (!GetDefaultPrinterW(pwszBuffer, pcchBuffer))
+    {
+        dwErrorCode = GetLastError();
+        goto Cleanup;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (pwszBuffer)
+        HeapFree(hProcessHeap, 0, pwszBuffer);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
 {
-    return FALSE;
+    DWORD cbNeeded;
+    DWORD cchInputBuffer;
+    DWORD dwErrorCode;
+    HKEY hWindowsKey = NULL;
+    PWSTR pwszDevice = NULL;
+    PWSTR pwszComma;
+
+    // Sanity check.
+    if (!pcchBuffer)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    cchInputBuffer = *pcchBuffer;
+
+    // Open the registry key where the default printer for the current user is stored.
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_READ, &hWindowsKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Determine the size of the required buffer.
+    dwErrorCode = (DWORD)RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, NULL, &cbNeeded);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Allocate it.
+    pwszDevice = HeapAlloc(hProcessHeap, 0, cbNeeded);
+    if (!pwszDevice)
+    {
+        dwErrorCode = GetLastError();
+        ERR("HeapAlloc failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Now get the actual value.
+    dwErrorCode = RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, (PBYTE)pwszDevice, &cbNeeded);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // We get a string "<Printer Name>,winspool,<Port>:".
+    // Extract the printer name from it.
+    pwszComma = wcschr(pwszDevice, L',');
+    if (!pwszComma)
+    {
+        ERR("Found no or invalid default printer: %S!\n", pwszDevice);
+        dwErrorCode = ERROR_INVALID_NAME;
+        goto Cleanup;
+    }
+
+    // Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
+    *pcchBuffer = pwszComma - pwszDevice + 1;
+
+    // Check if the supplied buffer is large enough.
+    if (cchInputBuffer < *pcchBuffer)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // Copy the default printer.
+    *pwszComma = 0;
+    CopyMemory(pszBuffer, pwszDevice, *pcchBuffer * sizeof(WCHAR));
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (hWindowsKey)
+        RegCloseKey(hWindowsKey);
+
+    if (pwszDevice)
+        HeapFree(hProcessHeap, 0, pwszDevice);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
@@ -632,6 +757,147 @@ ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
     return FALSE;
 }
 
+BOOL WINAPI
+SetDefaultPrinterA(LPCSTR pszPrinter)
+{
+    BOOL bReturnValue = FALSE;
+    DWORD cch;
+    PWSTR pwszPrinter = NULL;
+
+    if (pszPrinter)
+    {
+        // Convert pszPrinter to a Unicode string pwszPrinter
+        cch = strlen(pszPrinter);
+
+        pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!pwszPrinter)
+        {
+            ERR("HeapAlloc failed for pwszPrinter with last error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, pwszPrinter, cch + 1);
+    }
+
+    bReturnValue = SetDefaultPrinterW(pwszPrinter);
+
+Cleanup:
+    if (pwszPrinter)
+        HeapFree(hProcessHeap, 0, pwszPrinter);
+
+    return bReturnValue;
+}
+
+BOOL WINAPI
+SetDefaultPrinterW(LPCWSTR pszPrinter)
+{
+    const WCHAR wszDevicesKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
+
+    DWORD cbDeviceValueData;
+    DWORD cbPrinterValueData = 0;
+    DWORD cchPrinter;
+    DWORD dwErrorCode;
+    HKEY hDevicesKey = NULL;
+    HKEY hWindowsKey = NULL;
+    PWSTR pwszDeviceValueData = NULL;
+    WCHAR wszPrinter[MAX_PRINTER_NAME + 1];
+
+    // Open the Devices registry key.
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszDevicesKey, 0, KEY_READ, &hDevicesKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Did the caller give us a printer to set as default?
+    if (pszPrinter && *pszPrinter)
+    {
+        // Check if the given printer exists and query the value data size.
+        dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, NULL, &cbPrinterValueData);
+        if (dwErrorCode == ERROR_FILE_NOT_FOUND)
+        {
+            // The caller gave us an invalid printer name, return with ERROR_FILE_NOT_FOUND.
+            goto Cleanup;
+        }
+        else if (dwErrorCode != ERROR_SUCCESS)
+        {
+            ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+
+        cchPrinter = wcslen(pszPrinter);
+    }
+    else
+    {
+        // If there is already a default printer, we're done!
+        cchPrinter = _countof(wszPrinter);
+        if (GetDefaultPrinterW(wszPrinter, &cchPrinter))
+        {
+            dwErrorCode = ERROR_SUCCESS;
+            goto Cleanup;
+        }
+
+        // Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
+        cchPrinter = _countof(wszPrinter);
+        dwErrorCode = (DWORD)RegEnumValueW(hDevicesKey, 0, wszPrinter, &cchPrinter, NULL, NULL, NULL, &cbPrinterValueData);
+        if (dwErrorCode != ERROR_MORE_DATA)
+            goto Cleanup;
+
+        pszPrinter = wszPrinter;
+    }
+
+    // We now need to query the value data, which has the format "winspool,<Port>:"
+    // and make "<Printer Name>,winspool,<Port>:" out of it.
+    // Allocate a buffer large enough for the final data.
+    cbDeviceValueData = (cchPrinter + 1) * sizeof(WCHAR) + cbPrinterValueData;
+    pwszDeviceValueData = HeapAlloc(hProcessHeap, 0, cbDeviceValueData);
+    if (!pwszDeviceValueData)
+    {
+        dwErrorCode = GetLastError();
+        ERR("HeapAlloc failed with error %lu\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Copy the Printer Name and a comma into it.
+    CopyMemory(pwszDeviceValueData, pszPrinter, cchPrinter * sizeof(WCHAR));
+    pwszDeviceValueData[cchPrinter] = L',';
+
+    // Append the value data, which has the format "winspool,<Port>:"
+    dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, (PBYTE)&pwszDeviceValueData[cchPrinter + 1], &cbPrinterValueData);
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    // Open the Windows registry key.
+    dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_SET_VALUE, &hWindowsKey);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Store our new default printer.
+    dwErrorCode = (DWORD)RegSetValueExW(hWindowsKey, wszDeviceValue, 0, REG_SZ, (PBYTE)pwszDeviceValueData, cbDeviceValueData);
+    if (dwErrorCode != ERROR_SUCCESS)
+    {
+        ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+Cleanup:
+    if (hDevicesKey)
+        RegCloseKey(hDevicesKey);
+
+    if (hWindowsKey)
+        RegCloseKey(hWindowsKey);
+
+    if (pwszDeviceValueData)
+        HeapFree(hProcessHeap, 0, pwszDeviceValueData);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
 BOOL WINAPI
 SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
 {
index 9dcd522..21140cd 100644 (file)
 199 stub EnumPrinterDriversA
 200 stdcall EnumPrinterDriversW(wstr wstr long ptr long ptr ptr)
 201 stdcall GetDefaultPrinterA(ptr ptr)
-202 stub SetDefaultPrinterA
+202 stdcall SetDefaultPrinterA(str)
 203 stdcall GetDefaultPrinterW(ptr ptr)
-204 stub SetDefaultPrinterW
+204 stdcall SetDefaultPrinterW(wstr)
 205 stub -noname SplReadPrinter
 206 stub -noname AddPerMachineConnectionA
 207 stub -noname AddPerMachineConnectionW
index d877baa..86875cc 100644 (file)
@@ -1,13 +1,16 @@
 /*
  * PROJECT:     ReactOS Printing Include files
  * LICENSE:     GNU LGPLv2 or any later version as published by the Free Software Foundation
- * PURPOSE:     Undocumented APIs of the Spooler Router "spoolss.dll"
+ * PURPOSE:     Undocumented APIs of the Spooler Router "spoolss.dll" and internally shared interfaces
  * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #ifndef _REACTOS_SPOOLSS_H
 #define _REACTOS_SPOOLSS_H
 
+// Constants
+#define MAX_PRINTER_NAME        220
+
 typedef struct _MARSHALL_DOWN_INFO
 {
     DWORD dwOffset;             /** Byte offset of this element within the structure or MAXDWORD to indicate the end of the array */
index b57fd65..8419d5a 100644 (file)
@@ -36,7 +36,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(localspl);
 #define IS_VALID_PRIORITY(P)    (P >= MIN_PRIORITY && P <= MAX_PRIORITY)
 
 // Constants
-#define MAX_PRINTER_NAME        220
 #define SHD_WIN2003_SIGNATURE   0x4968
 
 // Function pointers
index 435c612..4bbcf6e 100644 (file)
@@ -75,6 +75,12 @@ static DWORD dwPrinterInfo5Offsets[] = {
     MAXDWORD
 };
 
+/** These values serve no purpose anymore, but are still used in PRINTER_INFO_5 and
+    HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts */
+static const DWORD dwDeviceNotSelectedTimeout = 15000;
+static const DWORD dwTransmissionRetryTimeout = 45000;
+
+
 /**
  * @name _PrinterListCompareRoutine
  *
@@ -765,8 +771,8 @@ _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo,
 
     // Set the general fields.
     (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
-    (*ppPrinterInfo)->DeviceNotSelectedTimeout = 0;
-    (*ppPrinterInfo)->TransmissionRetryTimeout = 0;
+    (*ppPrinterInfo)->DeviceNotSelectedTimeout = dwDeviceNotSelectedTimeout;
+    (*ppPrinterInfo)->TransmissionRetryTimeout = dwTransmissionRetryTimeout;
 
     // Set the pPrinterName field.
     pwszStrings[0] = DllAllocSplMem(cbPrinterName);
index 9c0c77c..7f39341 100644 (file)
@@ -3,6 +3,7 @@ list(APPEND SOURCE
     ClosePrinter.c
     EnumPrinters.c
     EnumPrintProcessorDatatypes.c
+    GetDefaultPrinter.c
     GetPrintProcessorDirectory.c
     IsValidDevmode.c
     OpenPrinter.c
diff --git a/rostests/apitests/winspool/GetDefaultPrinter.c b/rostests/apitests/winspool/GetDefaultPrinter.c
new file mode 100644 (file)
index 0000000..1559913
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * PROJECT:     ReactOS Print Spooler DLL API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for GetDefaultPrinterA/GetDefaultPrinterW/SetDefaultPrinterA/SetDefaultPrinterW
+ * 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>
+
+START_TEST(GetDefaultPrinter)
+{
+    DWORD cchDefaultPrinter;
+    PWSTR pwszDefaultPrinter;
+
+    // Don't supply any parameters, this has to fail with ERROR_INVALID_PARAMETER.
+    SetLastError(0xDEADBEEF);
+    ok(!GetDefaultPrinterW(NULL, NULL), "GetDefaultPrinterW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetDefaultPrinterW returns error %lu!\n", GetLastError());
+
+    // Determine the size of the required buffer. This has to bail out with ERROR_INSUFFICIENT_BUFFER.
+    cchDefaultPrinter = 0;
+    SetLastError(0xDEADBEEF);
+    ok(!GetDefaultPrinterW(NULL, &cchDefaultPrinter), "GetDefaultPrinterW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetDefaultPrinterW returns error %lu!\n", GetLastError());
+
+    // Try with a buffer large enough.
+    pwszDefaultPrinter = HeapAlloc(GetProcessHeap(), 0, cchDefaultPrinter * sizeof(WCHAR));
+    SetLastError(0xDEADBEEF);
+    ok(GetDefaultPrinterW(pwszDefaultPrinter, &cchDefaultPrinter), "GetDefaultPrinterW returns FALSE!\n");
+    ok(GetLastError() == ERROR_SUCCESS, "GetDefaultPrinterW returns error %lu!\n", GetLastError());
+
+    // SetDefaultPrinterW with NULL needs to succeed and leave the default printer unchanged.
+    SetLastError(0xDEADBEEF);
+    ok(SetDefaultPrinterW(NULL), "SetDefaultPrinterW returns FALSE!\n");
+    ok(GetLastError() == ERROR_SUCCESS, "SetDefaultPrinterW returns error %lu!\n", GetLastError());
+
+    // SetDefaultPrinterW with the previous default printer also needs to succeed.
+    SetLastError(0xDEADBEEF);
+    ok(SetDefaultPrinterW(pwszDefaultPrinter), "SetDefaultPrinterW returns FALSE!\n");
+    ok(GetLastError() == ERROR_SUCCESS, "SetDefaultPrinterW returns error %lu!\n", GetLastError());
+
+    HeapFree(GetProcessHeap(), 0, pwszDefaultPrinter);
+}
index 3cc4174..74a70fb 100644 (file)
@@ -13,6 +13,7 @@
 extern void func_ClosePrinter(void);
 extern void func_EnumPrinters(void);
 extern void func_EnumPrintProcessorDatatypes(void);
+extern void func_GetDefaultPrinter(void);
 extern void func_GetPrintProcessorDirectoryA(void);
 extern void func_GetPrintProcessorDirectoryW(void);
 extern void func_IsValidDevmodeA(void);
@@ -25,6 +26,7 @@ const struct test winetest_testlist[] =
     { "ClosePrinter", func_ClosePrinter },
     { "EnumPrinters", func_EnumPrinters },
     { "EnumPrintProcessorDatatypes", func_EnumPrintProcessorDatatypes },
+    { "GetDefaultPrinter", func_GetDefaultPrinter },
     { "GetPrintProcessorDirectoryA", func_GetPrintProcessorDirectoryA },
     { "GetPrintProcessorDirectoryW", func_GetPrintProcessorDirectoryW },
     { "IsValidDevmodeA", func_IsValidDevmodeA },