[LOCALSPL]

- Refactor LocalEnumPrinters to make it ready for supporting additional levels.
- Correctly handle all passed flags for Level 1 queries to the Local Print Provider.
- Introduce strsafe functions to LocalEnumPrinters in a way that actually makes the code smaller. To be done in other parts too.
- Use PackStrings in LocalEnumPrinters to simplify the code.
- Return the correct 3 strings in the Description field of Level 1 queries. That also introduces the "Location" field.
- Remove debug spam in _OpenEnvironment.

[SPOOLSV]
- Make use of the newly implemented AlignRpcPtr/UndoAlignRpcPtr. Fixes a test.

[WINSPOOL]
- Dismiss invalid levels already in EnumPrintersW and zero the input buffer here (but not in localspl). Verified by a test.

EnumPrintersW for Level 1 should be fully supported now.

svn path=/trunk/; revision=74324
This commit is contained in:
Colin Finck 2017-04-16 14:12:01 +00:00
parent c860e2b80d
commit cb609f0b17
12 changed files with 307 additions and 155 deletions

View file

@ -1277,6 +1277,7 @@ HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","De
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,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"

View 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 Printers and printing
* COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
* COPYRIGHT: Copyright 2015-2017 Colin Finck <colin@reactos.org>
*/
#include "precomp.h"
@ -68,27 +68,27 @@ EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cb
BOOL bReturnValue;
DWORD cbCallBuffer;
DWORD cbNeeded;
DWORD dwErrorCode = 0xFFFFFFFF;
DWORD dwReturned;
PBYTE pCallBuffer;
PSPOOLSS_PRINT_PROVIDER pPrintProvider;
PLIST_ENTRY pEntry;
// Sanity checks.
if ((cbBuf && !pPrinterEnum) || !pcbNeeded || !pcReturned)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
// Begin counting.
*pcbNeeded = 0;
*pcReturned = 0;
if (cbBuf && !pPrinterEnum)
{
dwErrorCode = ERROR_INVALID_USER_BUFFER;
goto Cleanup;
}
// At the beginning, we have the full buffer available.
cbCallBuffer = cbBuf;
pCallBuffer = pPrinterEnum;
// Loop through all Print Provider.
// Loop through all Print Providers.
for (pEntry = PrintProviderList.Flink; pEntry != &PrintProviderList; pEntry = pEntry->Flink)
{
pPrintProvider = CONTAINING_RECORD(pEntry, SPOOLSS_PRINT_PROVIDER, Entry);
@ -109,9 +109,15 @@ EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cb
// Advance the buffer if the caller provided it.
if (pCallBuffer)
pCallBuffer += cbNeeded;
// dwErrorCode shall not be overwritten if a previous EnumPrinters call already succeeded.
if (dwErrorCode != ERROR_SUCCESS)
dwErrorCode = GetLastError();
}
return bReturnValue;
Cleanup:
SetLastError(dwErrorCode);
return (dwErrorCode == ERROR_SUCCESS);
}
BOOL WINAPI
@ -148,6 +154,7 @@ BOOL WINAPI
OpenPrinterW(PWSTR pPrinterName, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault)
{
BOOL bReturnValue;
DWORD dwErrorCode = ERROR_INVALID_PRINTER_NAME;
HANDLE hPrinter;
PLIST_ENTRY pEntry;
PSPOOLSS_PRINTER_HANDLE pHandle;
@ -156,8 +163,8 @@ OpenPrinterW(PWSTR pPrinterName, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault)
// Sanity checks.
if (!pPrinterName || !phPrinter)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
dwErrorCode = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
// Loop through all Print Providers to find one able to open this Printer.
@ -173,27 +180,33 @@ OpenPrinterW(PWSTR pPrinterName, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault)
pHandle = DllAllocSplMem(sizeof(SPOOLSS_PRINTER_HANDLE));
if (!pHandle)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
return FALSE;
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
pHandle->pPrintProvider = pPrintProvider;
pHandle->hPrinter = hPrinter;
*phPrinter = (HANDLE)pHandle;
SetLastError(ERROR_SUCCESS);
return TRUE;
dwErrorCode = ERROR_SUCCESS;
goto Cleanup;
}
else if (bReturnValue == ROUTER_STOP_ROUTING)
{
ERR("A Print Provider returned ROUTER_STOP_ROUTING for Printer \"%S\"!\n", pPrinterName);
return FALSE;
dwErrorCode = GetLastError();
goto Cleanup;
}
}
// We found no Print Provider able to open this Printer.
return FALSE;
// 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);
}
BOOL WINAPI

View 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: Precompiled Header for all source files
* COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
* COPYRIGHT: Copyright 2015-2017 Colin Finck <colin@reactos.org>
*/
#ifndef _PRECOMP_H
@ -18,14 +18,12 @@
#include <winsplp.h>
#include <winspool_s.h>
#include <spoolss.h>
#include <wine/debug.h>
WINE_DEFAULT_DEBUG_CHANNEL(spoolsv);
// rpcserver.c
DWORD WINAPI LrpcThreadProc(LPVOID lpParameter);
// Undocumented spoolss
BOOL WINAPI InitializeRouter(HANDLE SpoolerStatusHandle);
DWORD WINAPI SpoolerInit();
#endif

View 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 Printers and printing
* COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
* COPYRIGHT: Copyright 2015-2017 Colin Finck <colin@reactos.org>
*/
#include "precomp.h"
@ -137,8 +137,7 @@ DWORD
_RpcEnumPrinters(DWORD Flags, WINSPOOL_HANDLE Name, DWORD Level, BYTE* pPrinterEnum, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned)
{
DWORD dwErrorCode;
DWORD i;
PBYTE p = pPrinterEnum;
PBYTE pPrinterEnumAligned;
dwErrorCode = RpcImpersonateClient(NULL);
if (dwErrorCode != ERROR_SUCCESS)
@ -147,11 +146,15 @@ _RpcEnumPrinters(DWORD Flags, WINSPOOL_HANDLE Name, DWORD Level, BYTE* pPrinterE
return dwErrorCode;
}
EnumPrintersW(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
pPrinterEnumAligned = AlignRpcPtr(pPrinterEnum, &cbBuf);
EnumPrintersW(Flags, Name, Level, pPrinterEnumAligned, cbBuf, pcbNeeded, pcReturned);
dwErrorCode = GetLastError();
if (dwErrorCode == ERROR_SUCCESS)
{
DWORD i;
PBYTE p = pPrinterEnumAligned;
// Replace absolute pointer addresses in the output by relative offsets.
for (i = 0; i < *pcReturned; i++)
{
@ -165,6 +168,8 @@ _RpcEnumPrinters(DWORD Flags, WINSPOOL_HANDLE Name, DWORD Level, BYTE* pPrinterE
}
RpcRevertToSelf();
UndoAlignRpcPtr(pPrinterEnum, pPrinterEnumAligned, cbBuf, pcbNeeded);
return dwErrorCode;
}

View 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 Printers and printing
* COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
* COPYRIGHT: Copyright 2015-2017 Colin Finck <colin@reactos.org>
*/
#include "precomp.h"
@ -309,6 +309,16 @@ EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cb
DWORD i;
PBYTE p = pPrinterEnum;
// Dismiss invalid levels already at this point.
if (Level == 3 || Level > 5)
{
dwErrorCode = ERROR_INVALID_LEVEL;
goto Cleanup;
}
if (cbBuf && pPrinterEnum)
ZeroMemory(pPrinterEnum, cbBuf);
// Do the RPC call
RpcTryExcept
{
@ -335,6 +345,7 @@ EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cb
}
}
Cleanup:
SetLastError(dwErrorCode);
return (dwErrorCode == ERROR_SUCCESS);
}

View file

@ -24,11 +24,13 @@ PWSTR WINAPI AllocSplStr(PCWSTR pwszInput);
PVOID WINAPI DllAllocSplMem(DWORD dwBytes);
BOOL WINAPI DllFreeSplMem(PVOID pMem);
BOOL WINAPI DllFreeSplStr(PWSTR pwszString);
BOOL WINAPI InitializeRouter(HANDLE SpoolerStatusHandle);
BOOL WINAPI MarshallDownStructure(PVOID pStructure, PMARSHALL_DOWN_INFO pParameters, DWORD cbStructureSize, BOOL bSomeBoolean);
PBYTE WINAPI PackStrings(PCWSTR* pSource, PBYTE pDest, const DWORD* DestOffsets, PBYTE pEnd);
PVOID WINAPI ReallocSplMem(PVOID pOldMem, DWORD cbOld, DWORD cbNew);
BOOL WINAPI ReallocSplStr(PWSTR* ppwszString, PCWSTR pwszInput);
BOOL WINAPI SplInitializeWinSpoolDrv(PVOID* pTable);
DWORD WINAPI SpoolerInit();
PDWORD WINAPI UndoAlignRpcPtr(PVOID pDestinationBuffer, PVOID pSourceBuffer, DWORD cbBuffer, PDWORD pcbNeeded);
#endif

View 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: Main functions
* COPYRIGHT: Copyright 2015-2016 Colin Finck <colin@reactos.org>
* COPYRIGHT: Copyright 2015-2017 Colin Finck <colin@reactos.org>
*/
#include "precomp.h"
@ -18,8 +18,8 @@ const WCHAR wszDefaultDocumentName[] = L"Local Downlevel Document";
const WCHAR* wszPrintProviderInfo[3] = {
L"Windows NT Local Print Providor", // Name
L"Windows NT Local Printers", // Description
L"Locally connected Printers" // Comment
L"Locally connected Printers", // Comment
L"Windows NT Local Printers" // Description
};
// Local Constants

View file

@ -15,6 +15,7 @@
#include <lmcons.h>
#include <rpc.h>
#include <strsafe.h>
#include <windef.h>
#include <winbase.h>
#include <wingdi.h>
@ -114,6 +115,7 @@ struct _LOCAL_PRINTER
DWORD dwAttributes;
DWORD dwStatus;
PWSTR pwszLocation;
PWSTR pwszPrinterDriver;
PWSTR pwszDescription;
PWSTR pwszDefaultDatatype;

View file

@ -2,7 +2,7 @@
* PROJECT: ReactOS Local Spooler
* LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation
* PURPOSE: Functions related to Printers and printing
* COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
* COPYRIGHT: Copyright 2015-2017 Colin Finck <colin@reactos.org>
*/
#include "precomp.h"
@ -10,6 +10,13 @@
// Global Variables
SKIPLIST PrinterList;
// Local Constants
static DWORD dwPrinterInfo1Offsets[] = {
FIELD_OFFSET(PRINTER_INFO_1W, pName),
FIELD_OFFSET(PRINTER_INFO_1W, pComment),
FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
MAXDWORD
};
/**
* @name _PrinterListCompareRoutine
@ -170,6 +177,11 @@ InitializePrinterList()
pPrinter->pPort = pPort;
InitializePrinterJobList(pPrinter);
// Get the location.
pPrinter->pwszLocation = AllocAndRegQueryWSZ(hSubKey, L"Location");
if (!pPrinter->pwszLocation)
continue;
// Get the printer driver.
pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
if (!pPrinter->pwszPrinterDriver)
@ -284,9 +296,93 @@ Cleanup:
return (dwErrorCode == ERROR_SUCCESS);
}
/**
* @name _IsLocalComputerName
*
* Checks if the given Computer Name matches the local Computer Name.
*
* @param Name
* Computer Name prepended with two backslashes to check.
*
* @param pwszComputerName
* Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
* Will contain a string "\\COMPUTERNAME\" on success that can be prepended in EnumPrinters.
*
* @param pcchComputerName
* On success, this pointer receives the length in characters of pwszComputerName.
*
* @return
* ERROR_SUCCESS on success or an error code on failure.
*/
static DWORD
_IsLocalComputerName(PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName)
{
DWORD dwErrorCode;
DWORD
_LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
// Prepend slashes to the computer name.
pwszComputerName[0] = L'\\';
pwszComputerName[1] = L'\\';
// Get the local computer name for comparison.
*pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName))
{
dwErrorCode = GetLastError();
ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
goto Cleanup;
}
// Add the leading slashes to the total length.
*pcchComputerName += 2;
// Now compare this with the local computer name and reject it with ERROR_INVALID_NAME if it doesn't match.
if (wcsicmp(&Name[2], &pwszComputerName[2]) != 0)
{
dwErrorCode = ERROR_INVALID_NAME;
goto Cleanup;
}
// Add a trailing backslash to pwszComputerName, which will later be prepended in front of the printer names.
pwszComputerName[(*pcchComputerName)++] = L'\\';
pwszComputerName[*pcchComputerName] = 0;
dwErrorCode = ERROR_SUCCESS;
Cleanup:
return dwErrorCode;
}
static DWORD
_DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
{
int i;
// Count the needed bytes for Print Provider information.
*pcbNeeded = sizeof(PRINTER_INFO_1W);
for (i = 0; i < 3; i++)
*pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
// Check if the supplied buffer is large enough.
if (cbBuf < *pcbNeeded)
return ERROR_INSUFFICIENT_BUFFER;
// Copy over the Print Provider information.
((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
PackStrings(wszPrintProviderInfo, pPrinterEnum, dwPrinterInfo1Offsets, &pPrinterEnum[*pcbNeeded]);
*pcReturned = 1;
return ERROR_SUCCESS;
}
static DWORD
_LocalEnumPrintersLevel0(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
{
return ERROR_INVALID_LEVEL;
}
static DWORD
_LocalEnumPrintersLevel1(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
{
const WCHAR wszComma[] = L",";
@ -297,57 +393,28 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
DWORD dwErrorCode;
DWORD i;
PBYTE pPrinterInfo;
PBYTE pPrinterString;
PBYTE pPrinterStrings;
PSKIPLIST_NODE pNode;
PLOCAL_PRINTER pPrinter;
PRINTER_INFO_1W PrinterInfo1;
WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1];
DWORD dwOffsets[] = {
FIELD_OFFSET(PRINTER_INFO_1W, pName),
FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
FIELD_OFFSET(PRINTER_INFO_1W, pComment),
MAXDWORD
};
PWSTR p;
PWSTR pwszStrings[3];
WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
if (Flags & PRINTER_ENUM_NAME)
{
if (Name)
{
// The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
// Only process what's directed at us and dismiss every other request with ERROR_INVALID_NAME.
// Only process what's directed at us.
if (Name[0] == L'\\' && Name[1] == L'\\')
{
// Prepend slashes to the computer name.
wszComputerName[0] = L'\\';
wszComputerName[1] = L'\\';
// Get the local computer name for comparison.
cchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
if (!GetComputerNameW(&wszComputerName[2], &cchComputerName))
{
dwErrorCode = GetLastError();
ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
dwErrorCode = _IsLocalComputerName(Name, wszComputerName, &cchComputerName);
if (dwErrorCode != ERROR_SUCCESS)
goto Cleanup;
}
// Add the leading slashes to the total length.
cchComputerName += 2;
// Now compare this with the local computer name and reject if it doesn't match.
if (wcsicmp(&Name[2], &wszComputerName[2]) != 0)
{
dwErrorCode = ERROR_INVALID_NAME;
goto Cleanup;
}
// Add a trailing backslash to wszComputerName, which will later be prepended in front of the printer names.
wszComputerName[cchComputerName++] = L'\\';
wszComputerName[cchComputerName] = 0;
}
else if (wcsicmp(Name, wszPrintProviderInfo[0]) != 0)
{
// The user supplied a name that cannot be processed by the local print provider.
// The user supplied a name that cannot be processed by the Local Print Provider.
dwErrorCode = ERROR_INVALID_NAME;
goto Cleanup;
}
@ -356,23 +423,7 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
{
// The caller wants information about this Print Provider.
// spoolss packs this into an array of information about all Print Providers.
*pcbNeeded = sizeof(PRINTER_INFO_1W);
for (i = 0; i < 3; i++)
*pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
// Check if the supplied buffer is large enough.
if (cbBuf < *pcbNeeded)
{
dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
// Copy over the print processor information.
((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
PackStrings(wszPrintProviderInfo, pPrinterEnum, dwOffsets, &pPrinterEnum[*pcbNeeded]);
*pcReturned = 1;
dwErrorCode = ERROR_SUCCESS;
dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
goto Cleanup;
}
}
@ -384,14 +435,17 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
{
pPrinter = (PLOCAL_PRINTER)pNode->Element;
// This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
// But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
// On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
cbDescription = cchComputerName * sizeof(WCHAR) + cbName + cbComment + sizeof(WCHAR);
// TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
if (Flags & PRINTER_ENUM_SHARED)
continue;
*pcbNeeded += sizeof(PRINTER_INFO_1W) + cchComputerName * sizeof(WCHAR) + cbName + cbComment + cbDescription;
// Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
// On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
*pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
i++;
}
@ -402,49 +456,53 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
goto Cleanup;
}
// Put the strings right after the last PRINTER_INFO_1W structure.
// Due to all the required string processing, we can't just use PackStrings here :(
// Initialize the variables for filling the output buffer using PackStrings.
pPrinterInfo = pPrinterEnum;
pPrinterString = pPrinterEnum + i * sizeof(PRINTER_INFO_1W);
pPrinterStrings = &pPrinterEnum[*pcbNeeded];
// Copy over the printer information.
// Copy over the Printer information.
for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
{
pPrinter = (PLOCAL_PRINTER)pNode->Element;
// FIXME: As for now, the Flags member returns no information.
PrinterInfo1.Flags = 0;
// TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
if (Flags & PRINTER_ENUM_SHARED)
continue;
// Copy the printer name.
PrinterInfo1.pName = (PWSTR)pPrinterString;
CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
pPrinterString += cchComputerName * sizeof(WCHAR);
cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName);
pPrinterString += cbName;
// Indicate that this is a Printer.
((PPRINTER_INFO_1W)pPrinterInfo)->Flags = PRINTER_ENUM_ICON8;
// Copy the printer comment (equals the "Description" registry value).
PrinterInfo1.pComment = (PWSTR)pPrinterString;
// Calculate the string lengths.
cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment);
pPrinterString += cbComment;
cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
// Copy the Printer Name.
pwszStrings[0] = DllAllocSplMem(cbName);
p = pwszStrings[0];
StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
// Copy the Printer comment (equals the "Description" registry value).
pwszStrings[1] = pPrinter->pwszDescription;
// Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
pwszStrings[2] = DllAllocSplMem(cbDescription);
p = pwszStrings[2];
StringCbCopyExW(p, cbDescription, wszComputerName, &p, &cbDescription, 0);
StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterName, &p, &cbDescription, 0);
StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterDriver, &p, &cbDescription, 0);
StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
StringCbCopyExW(p, cbDescription, pPrinter->pwszLocation, &p, &cbDescription, 0);
// Copy the description, which for PRINTER_INFO_1W has the form "Name,Comment,"
PrinterInfo1.pDescription = (PWSTR)pPrinterString;
CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
pPrinterString += cchComputerName * sizeof(WCHAR);
CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName - sizeof(WCHAR));
pPrinterString += cbName - sizeof(WCHAR);
CopyMemory(pPrinterString, wszComma, sizeof(WCHAR));
pPrinterString += sizeof(WCHAR);
CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment - sizeof(WCHAR));
pPrinterString += cbComment - sizeof(WCHAR);
CopyMemory(pPrinterString, wszComma, sizeof(wszComma));
pPrinterString += sizeof(wszComma);
// Finally copy the structure and advance to the next one in the output buffer.
CopyMemory(pPrinterInfo, &PrinterInfo1, sizeof(PRINTER_INFO_1W));
pPrinterStrings = PackStrings(pwszStrings, pPrinterInfo, dwPrinterInfo1Offsets, pPrinterStrings);
pPrinterInfo += sizeof(PRINTER_INFO_1W);
// Free the memory for temporary strings.
DllFreeSplMem(pwszStrings[0]);
DllFreeSplMem(pwszStrings[2]);
}
*pcReturned = i;
@ -454,6 +512,24 @@ Cleanup:
return dwErrorCode;
}
static DWORD
_LocalEnumPrintersLevel2(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
{
return ERROR_INVALID_LEVEL;
}
static DWORD
_LocalEnumPrintersLevel4(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
{
return ERROR_INVALID_LEVEL;
}
static DWORD
_LocalEnumPrintersLevel5(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
{
return ERROR_INVALID_LEVEL;
}
BOOL WINAPI
LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
{
@ -465,24 +541,47 @@ LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DW
*pcbNeeded = 0;
*pcReturned = 0;
// Think positive :)
// Treat it as success if the caller queried no information and we don't need to return any.
dwErrorCode = ERROR_SUCCESS;
if (Flags & PRINTER_ENUM_LOCAL)
if (Flags & PRINTER_ENUM_CONNECTIONS || Flags & PRINTER_ENUM_REMOTE || Flags & PRINTER_ENUM_NETWORK)
{
// The function behaves quite differently for each level.
if (Level == 1)
{
dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
}
else
{
// TODO: Handle other levels.
// The caller supplied an invalid level.
dwErrorCode = ERROR_INVALID_LEVEL;
goto Cleanup;
}
// If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME.
// This is the internal way for a Print Provider to signal that it doesn't handle this request.
dwErrorCode = ERROR_INVALID_NAME;
goto Cleanup;
}
if (!(Flags & PRINTER_ENUM_LOCAL || Flags & PRINTER_ENUM_NAME))
{
// The Local Print Provider is the right destination for the request, but without any of these flags,
// there is no information that can be returned.
// So just signal a successful request.
dwErrorCode = ERROR_SUCCESS;
goto Cleanup;
}
if (Level == 0)
{
dwErrorCode = _LocalEnumPrintersLevel0(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
}
else if (Level == 1)
{
dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
}
else if (Level == 2)
{
dwErrorCode = _LocalEnumPrintersLevel2(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
}
else if (Level == 4)
{
dwErrorCode = _LocalEnumPrintersLevel4(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
}
else if (Level == 5)
{
dwErrorCode = _LocalEnumPrintersLevel5(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
}
else
{
// The caller supplied an invalid level.
dwErrorCode = ERROR_INVALID_LEVEL;
}
Cleanup:

View file

@ -160,10 +160,7 @@ InitializePrintProcessorList()
// Open the environment registry key.
dwErrorCode = _OpenEnvironment(wszCurrentEnvironment, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode);
goto Cleanup;
}
// Open the "Print Processors" subkey.
dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);
@ -494,10 +491,7 @@ LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE
// We use the registry and not the PrintProcessorList here, because the caller may request information about a different environment.
dwErrorCode = _OpenEnvironment(pEnvironment, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode);
goto Cleanup;
}
// Open the "Print Processors" subkey.
dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);

View file

@ -2,7 +2,7 @@
* PROJECT: ReactOS Local Spooler API Tests Injected DLL
* LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
* PURPOSE: Tests for fpEnumPrinters
* COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
* COPYRIGHT: Copyright 2015-2017 Colin Finck <colin@reactos.org>
*/
#include <apitest.h>
@ -22,6 +22,8 @@ extern BOOL GetLocalsplFuncs(LPPRINTPROVIDOR pp);
START_TEST(fpEnumPrinters)
{
BYTE TempBuffer[50];
BYTE ZeroBuffer[50];
DWORD cbNeeded;
DWORD cbTemp;
DWORD dwReturned;
@ -33,18 +35,30 @@ START_TEST(fpEnumPrinters)
if (!GetLocalsplFuncs(&pp))
return;
// Verify that fpEnumPrinters returns success and zeros cbNeeded and dwReturned (but not TempBuffer!) if no flag has been specified.
memset(TempBuffer, 0xDE, sizeof(TempBuffer));
memset(ZeroBuffer, 0, sizeof(ZeroBuffer));
cbNeeded = 0xDEADBEEF;
dwReturned = 0xDEADBEEF;
SetLastError(0xDEADBEEF);
ok(pp.fpEnumPrinters(0, NULL, 1, TempBuffer, sizeof(TempBuffer), &cbNeeded, &dwReturned), "fpEnumPrinters returns FALSE\n");
ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu!\n", GetLastError());
ok(memcmp(TempBuffer, ZeroBuffer, sizeof(TempBuffer)) != 0, "TempBuffer has been zeroed!\n");
ok(cbNeeded == 0, "cbNeeded is %lu!\n", cbNeeded);
ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
// Verify that localspl only returns information about a single print provider (namely itself).
cbNeeded = 0xDEADBEEF;
dwReturned = 0xDEADBEEF;
SetLastError(0xDEADBEEF);
ok(!pp.fpEnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbNeeded, &dwReturned), "fpEnumPrinters returns TRUE\n");
ok(!pp.fpEnumPrinters(PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbNeeded, &dwReturned), "fpEnumPrinters returns TRUE\n");
ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "fpEnumPrinters returns error %lu!\n", GetLastError());
ok(cbNeeded > 0, "cbNeeded is 0!\n");
ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
SetLastError(0xDEADBEEF);
pPrinterInfo1 = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
ok(pp.fpEnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, (PBYTE)pPrinterInfo1, cbNeeded, &cbNeeded, &dwReturned), "fpEnumPrinters returns FALSE\n");
ok(pp.fpEnumPrinters(PRINTER_ENUM_NAME, NULL, 1, (PBYTE)pPrinterInfo1, cbNeeded, &cbNeeded, &dwReturned), "fpEnumPrinters returns FALSE\n");
ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu!\n", GetLastError());
ok(cbNeeded > 0, "cbNeeded is 0!\n");
ok(dwReturned == 1, "dwReturned is %lu!\n", dwReturned);

View file

@ -2,7 +2,7 @@
* PROJECT: ReactOS Print Spooler DLL API Tests
* LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
* PURPOSE: Tests for EnumPrintersA/EnumPrintersW
* COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
* COPYRIGHT: Copyright 2015-2017 Colin Finck <colin@reactos.org>
*/
#include <apitest.h>
@ -15,6 +15,8 @@
START_TEST(EnumPrinters)
{
BYTE TempBuffer[50];
BYTE ZeroBuffer[50] = { 0 };
DWORD cbNeeded;
DWORD cbTemp;
DWORD dwReturned;
@ -22,6 +24,17 @@ START_TEST(EnumPrinters)
DWORD i;
DWORD dwValidLevels[] = { 0, 1, 2, 4, 5 };
// Verify that EnumPrintersW returns success and zeroes all input variables even though no flag has been specified.
memset(TempBuffer, 0xDE, sizeof(TempBuffer));
cbNeeded = 0xDEADBEEF;
dwReturned = 0xDEADBEEF;
SetLastError(0xDEADBEEF);
ok(EnumPrintersW(0, NULL, 1, TempBuffer, sizeof(TempBuffer), &cbNeeded, &dwReturned), "EnumPrintersW returns FALSE\n");
ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu!\n", GetLastError());
ok(memcmp(TempBuffer, ZeroBuffer, sizeof(TempBuffer)) == 0, "TempBuffer has not been zeroed!\n");
ok(cbNeeded == 0, "cbNeeded is %lu!\n", cbNeeded);
ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
// Level 5 is the highest supported under Windows Server 2003. Higher levels need to fail and leave the variables untouched!
cbNeeded = 0xDEADBEEF;
dwReturned = 0xDEADBEEF;
@ -69,7 +82,7 @@ START_TEST(EnumPrinters)
ok(cbNeeded > 0, "cbNeeded is 0 for Level %lu!\n", dwValidLevels[i]);
ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]);
// Same error has to occur with a size to small.
// Same error has to occur with no buffer, but a size < 4 (AlignRpcPtr comes into play here).
SetLastError(0xDEADBEEF);
ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, dwValidLevels[i], NULL, 1, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", dwValidLevels[i]);
ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]);