[PRINTING]

- 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
This commit is contained in:
Colin Finck 2017-05-09 15:44:42 +00:00
parent f06734e55d
commit 5b6e082869
10 changed files with 338 additions and 12 deletions

View 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,""

View 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)

View 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)
{

View file

@ -100,9 +100,9 @@
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

View 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 */

View 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

View 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);

View file

@ -3,6 +3,7 @@ list(APPEND SOURCE
ClosePrinter.c
EnumPrinters.c
EnumPrintProcessorDatatypes.c
GetDefaultPrinter.c
GetPrintProcessorDirectory.c
IsValidDevmode.c
OpenPrinter.c

View file

@ -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);
}

View 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 },