windows-nt/Source/XPSP1/NT/printscan/print/spooler/spoolss/dll/printer.c

3293 lines
88 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1990-1994 Microsoft Corporation
All rights reserved
Module Name:
printer.c
Abstract:
Author:
Environment:
User Mode -Win32
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include "local.h"
#include "clusrout.h"
#include <offsets.h>
WCHAR szNoCache[] = L",NoCache";
WCHAR szProvidorValue[] = L"Provider";
WCHAR szRegistryConnections[] = L"Printers\\Connections";
WCHAR szServerValue[] = L"Server";
WCHAR szWin32spl[] = L"win32spl.dll";
//
// Router Cache Table
//
DWORD RouterCacheSize;
PROUTERCACHE RouterCacheTable;
CRITICAL_SECTION RouterCriticalSection;
//
// Forward prototypes
//
BOOL
EnumerateConnectedPrinters(
LPBYTE pPrinter,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned,
HKEY hKeyUser);
PPRINTER_INFO_2
pGetPrinterInfo2(
HANDLE hPrinter
);
BOOL
SavePrinterConnectionInRegistry(
PPRINTER_INFO_2 pPrinterInfo2,
LPPROVIDOR pProvidor
);
BOOL
RemovePrinterConnectionInRegistry(
LPWSTR pName);
DWORD
FindClosePrinterChangeNotificationWorker(
HANDLE hPrinter);
VOID
RundownPrinterNotify(
HANDLE hNotify);
BOOL
EnumPrintersW(
DWORD Flags,
LPWSTR Name,
DWORD Level,
LPBYTE pPrinterEnum,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned)
{
DWORD cReturned, cbStruct, cbNeeded;
DWORD TotalcbNeeded = 0;
DWORD cTotalReturned = 0;
DWORD Error = ERROR_SUCCESS;
PROVIDOR *pProvidor;
DWORD BufferSize=cbBuf;
HKEY hKeyUser;
BOOL bPartialSuccess = FALSE;
if (pPrinterEnum==NULL && cbBuf!=0)
{
SetLastError(ERROR_INVALID_USER_BUFFER);
return FALSE;
}
WaitForSpoolerInitialization();
switch (Level)
{
case STRESSINFOLEVEL:
cbStruct = sizeof(PRINTER_INFO_STRESS);
break;
case 1:
cbStruct = sizeof(PRINTER_INFO_1);
break;
case 2:
cbStruct = sizeof(PRINTER_INFO_2);
break;
case 4:
cbStruct = sizeof(PRINTER_INFO_4);
break;
case 5:
cbStruct = sizeof(PRINTER_INFO_5);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
if (Level==4 && (Flags & PRINTER_ENUM_CONNECTIONS))
{
//
// The router will handle info level_4 for connected printers.
//
Flags &= ~PRINTER_ENUM_CONNECTIONS;
if (hKeyUser = GetClientUserHandle(KEY_READ))
{
if (!EnumerateConnectedPrinters(pPrinterEnum,
BufferSize,
&TotalcbNeeded,
&cTotalReturned,
hKeyUser))
{
Error = GetLastError();
}
else
{
bPartialSuccess = TRUE;
}
RegCloseKey(hKeyUser);
}
else
{
Error = GetLastError();
}
pPrinterEnum += cTotalReturned * cbStruct;
if (TotalcbNeeded <= BufferSize)
BufferSize -= TotalcbNeeded;
else
BufferSize = 0;
}
for (pProvidor = pLocalProvidor; pProvidor; )
{
cReturned = 0;
cbNeeded = 0;
if (!(*pProvidor->PrintProvidor.fpEnumPrinters) (Flags, Name, Level,
pPrinterEnum,
BufferSize,
&cbNeeded,
&cReturned))
{
Error = GetLastError();
if (Error==ERROR_INSUFFICIENT_BUFFER)
{
TotalcbNeeded += cbNeeded;
BufferSize = 0;
}
}
else
{
bPartialSuccess = TRUE;
TotalcbNeeded += cbNeeded;
cTotalReturned += cReturned;
pPrinterEnum += cReturned * cbStruct;
BufferSize -= cbNeeded;
}
if ((Flags & PRINTER_ENUM_NAME) && Name && (Error!=ERROR_INVALID_NAME))
pProvidor = NULL;
else
pProvidor = pProvidor->pNext;
}
*pcbNeeded = TotalcbNeeded;
*pcReturned = cTotalReturned;
//
// Allow partial returns
//
if (bPartialSuccess)
Error = ERROR_SUCCESS;
if (TotalcbNeeded > cbBuf)
Error = ERROR_INSUFFICIENT_BUFFER;
SetLastError(Error);
return Error==ERROR_SUCCESS;
}
BOOL
EnumerateConnectedPrinters(
LPBYTE pPrinter,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned,
HKEY hClientKey
)
/*++
Routine Description:
Handles info level four enumeration.
Arguments:
Return Value:
--*/
{
HKEY hKey1=NULL;
HKEY hKeyPrinter;
DWORD cPrinters, cchData;
WCHAR PrinterName[MAX_UNC_PRINTER_NAME];
WCHAR ServerName[MAX_UNC_PRINTER_NAME];
DWORD cReturned, cbRequired, cbNeeded, cTotalReturned;
DWORD Error=0;
PWCHAR p;
LPBYTE pEnd;
DWORD cbSize;
BOOL bInsufficientBuffer = FALSE;
if((Error = RegOpenKeyEx(hClientKey, szRegistryConnections, 0,
KEY_READ, &hKey1))!=ERROR_SUCCESS)
{
SetLastError(Error);
return(FALSE);
}
cPrinters=0;
cchData = COUNTOF(PrinterName);
cTotalReturned = 0;
cReturned = cbNeeded = 0;
cbRequired = 0;
pEnd = pPrinter + cbBuf;
while (RegEnumKeyEx(hKey1, cPrinters, PrinterName, &cchData,
NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
//
// Fetch server name. Open the key and read it
// from the "Server" field.
//
Error = RegOpenKeyEx(hKey1,
PrinterName,
0,
KEY_READ,
&hKeyPrinter);
if( Error == ERROR_SUCCESS ){
cbSize = sizeof(ServerName);
Error = RegQueryValueEx(hKeyPrinter,
szServerValue,
NULL,
NULL,
(LPBYTE)ServerName,
&cbSize);
RegCloseKey(hKeyPrinter);
}
if( Error == ERROR_SUCCESS ){
//
// Force NULL termination of ServerName.
//
ServerName[COUNTOF(ServerName)-1] = 0;
} else {
//
// On error condition, try and extract the server name
// based on the printer name. Pretty ugly...
//
wcscpy(ServerName, PrinterName);
p = wcschr(ServerName+2, ',');
if (p)
*p = 0;
}
FormatRegistryKeyForPrinter(PrinterName, PrinterName);
if (MyUNCName(PrinterName)) // don't enumerate local printers!
{
cPrinters++;
cchData = COUNTOF(PrinterName);
continue;
}
//
// At this stage we don't care about opening the printers
// We just want to enumerate the names; in effect we're
// just reading HKEY_CURRENT_USER and returning the
// contents; we will copy the name of the printer and we will
// set its attributes to NETWORK and !LOCAL
//
cbRequired = sizeof(PRINTER_INFO_4) +
wcslen(PrinterName)*sizeof(WCHAR) + sizeof(WCHAR) +
wcslen(ServerName)*sizeof(WCHAR) + sizeof(WCHAR);
if (cbBuf >= cbRequired) {
//
// copy it in
//
DBGMSG(DBG_TRACE,
("cbBuf %d cbRequired %d PrinterName %ws\n", cbBuf, cbRequired, PrinterName));
pEnd = CopyPrinterNameToPrinterInfo4(ServerName,
PrinterName,
pPrinter,
pEnd);
//
// Fill in any in structure contents
//
pPrinter += sizeof(PRINTER_INFO_4);
//
// Increment the count of structures copied
//
cTotalReturned++;
//
// Reduce the size of the buffer by amount required
//
cbBuf -= cbRequired;
//
// Keep track of the total ammount required.
//
} else {
cbBuf = 0;
bInsufficientBuffer = TRUE;
}
cbNeeded += cbRequired;
cPrinters++;
cchData = COUNTOF(PrinterName);
}
RegCloseKey(hKey1);
*pcbNeeded = cbNeeded;
*pcReturned = cTotalReturned;
if (bInsufficientBuffer) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
return TRUE;
}
LPBYTE
CopyPrinterNameToPrinterInfo4(
LPWSTR pServerName,
LPWSTR pPrinterName,
LPBYTE pPrinter,
LPBYTE pEnd)
{
LPWSTR SourceStrings[sizeof(PRINTER_INFO_4)/sizeof(LPWSTR)];
LPWSTR *pSourceStrings=SourceStrings;
LPPRINTER_INFO_4 pPrinterInfo=(LPPRINTER_INFO_4)pPrinter;
DWORD *pOffsets;
pOffsets = PrinterInfo4Strings;
*pSourceStrings++=pPrinterName;
*pSourceStrings++=pServerName;
pEnd = PackStrings(SourceStrings,
(LPBYTE) pPrinterInfo,
pOffsets,
pEnd);
pPrinterInfo->Attributes = PRINTER_ATTRIBUTE_NETWORK;
return pEnd;
}
LPPROVIDOR
FindProvidorFromConnection(
LPWSTR pszPrinter
)
/*++
Routine Description:
Looks in the current user's Printer\Connections to see if a printer
is there, and returns which provider that owns it.
Note: this will always fail if the pszPrinter is a share name.
Arguments:
pszPrinter - Printer to search.
Return Value:
pProvidor - Provider that own's it.
NULL - none found.
--*/
{
PWCHAR pszKey = NULL;
DWORD cchSize = MAX_UNC_PRINTER_NAME + COUNTOF( szRegistryConnections );
WCHAR szProvidor[MAX_PATH];
DWORD cbProvidor;
LPWSTR pszKeyPrinter;
LONG Status;
LPPROVIDOR pProvidor = NULL;
HKEY hKeyClient = NULL;
HKEY hKeyPrinter = NULL;
SPLASSERT(pszPrinter);
if ( pszPrinter && wcslen(pszPrinter) + 1 < MAX_UNC_PRINTER_NAME ) {
if(pszKey = AllocSplMem(cchSize * sizeof(WCHAR))) {
//
// Prepare to read in
// HKEY_CURRENT_USER:\Printer\Connections\,,server,printer
//
wcscpy( pszKey, szRegistryConnections );
//
// Find the end of this key so we can append the registry-formatted
// printer name to it.
//
pszKeyPrinter = &pszKey[ COUNTOF( szRegistryConnections ) - 1 ];
*pszKeyPrinter++ = L'\\';
FormatPrinterForRegistryKey( pszPrinter, pszKeyPrinter );
if( hKeyClient = GetClientUserHandle(KEY_READ)){
Status = RegOpenKeyEx( hKeyClient,
pszKey,
0,
KEY_READ,
&hKeyPrinter );
if( Status == ERROR_SUCCESS ){
cbProvidor = sizeof( szProvidor );
Status = RegQueryValueEx( hKeyPrinter,
szProvidorValue,
NULL,
NULL,
(LPBYTE)szProvidor,
&cbProvidor );
if( Status == ERROR_SUCCESS ){
//
// Scan through all providers, trying to match dll string.
//
for( pProvidor = pLocalProvidor; pProvidor; pProvidor = pProvidor->pNext ){
if( !_wcsicmp( pProvidor->lpName, szProvidor )){
break;
}
}
}
RegCloseKey( hKeyPrinter );
}
RegCloseKey( hKeyClient );
}
FreeSplMem(pszKey);
}
}
return pProvidor;
}
VOID
UpdateSignificantError(
DWORD dwNewError,
PDWORD pdwOldError
)
/*++
Routine Description:
Determines whether the new error code is more "important"
than the previous one in cases where we continue routing.
Arguments:
dwNewError - New error code that occurred.
pdwOldError - Pointer to previous significant error.
This is updated if a significant error occurs
Return Value:
--*/
{
//
// Error code must be non-zero or else it will look
// like success.
//
SPLASSERT(dwNewError);
//
// If we have no significant error yet and we have one now,
// keep it.
//
if (*pdwOldError == ERROR_INVALID_NAME &&
dwNewError &&
dwNewError != WN_BAD_NETNAME &&
dwNewError != ERROR_BAD_NETPATH &&
dwNewError != ERROR_NOT_SUPPORTED &&
dwNewError != ERROR_REM_NOT_LIST &&
dwNewError != ERROR_INVALID_LEVEL &&
dwNewError != ERROR_INVALID_PARAMETER &&
dwNewError != ERROR_INVALID_NAME &&
dwNewError != WN_BAD_LOCALNAME) {
*pdwOldError = dwNewError;
}
return;
}
BOOL
OpenPrinterPortW(
LPWSTR pPrinterName,
HANDLE *pHandle,
LPPRINTER_DEFAULTS pDefault
)
/*++
Routine Description:
This routine is exactly the same as OpenPrinterW,
except that it doesn't call the local provider.
This is so that the local provider can open a network printer
with the same name as the local printer without getting
into a loop.
Arguments:
Return Value:
--*/
{
//
// We will set bLocalPrintProvidor = FALSE here
//
return(RouterOpenPrinterW(pPrinterName,
pHandle,
pDefault,
NULL,
0,
FALSE));
}
BOOL
OpenPrinterW(
LPWSTR pPrinterName,
HANDLE *pHandle,
LPPRINTER_DEFAULTS pDefault
)
{
//
// We will set bLocalPrintProvidor = TRUE here
//
return(RouterOpenPrinterW(pPrinterName,
pHandle,
pDefault,
NULL,
0,
TRUE));
}
BOOL
OpenPrinterExW(
LPWSTR pPrinterName,
HANDLE *pHandle,
LPPRINTER_DEFAULTS pDefault,
PSPLCLIENT_CONTAINER pSplClientContainer
)
{
BOOL bReturn = FALSE;
DWORD dwLevel = 0;
if (pSplClientContainer) {
dwLevel = pSplClientContainer->Level;
}
//
// We will set bLocalPrintProvidor = TRUE here
//
switch (dwLevel) {
case 1:
bReturn = RouterOpenPrinterW(pPrinterName,
pHandle,
pDefault,
(LPBYTE) (pSplClientContainer->ClientInfo.pClientInfo1),
1,
TRUE);
break;
case 2:
bReturn = RouterOpenPrinterW(pPrinterName,
pHandle,
pDefault,
NULL,
0,
TRUE);
if (pSplClientContainer) {
if (bReturn) {
pSplClientContainer->ClientInfo.pClientInfo2->hSplPrinter = (ULONG_PTR) *pHandle;
} else {
pSplClientContainer->ClientInfo.pClientInfo2->hSplPrinter = 0;
}
}
break;
default:
break;
}
return bReturn;
}
DWORD
TryOpenPrinterAndCache(
LPPROVIDOR pProvidor,
LPWSTR pszPrinterName,
PHANDLE phPrinter,
LPPRINTER_DEFAULTS pDefault,
PDWORD pdwFirstSignificantError,
LPBYTE pSplClientInfo,
DWORD dwLevel
)
/*++
Routine Description:
Attempt to open the printer using the providor. If there is
an error, update the dwFirstSignificantError variable. If the
providor "knows" the printer (either a success, or ROUTER_STOP_ROUTING),
then update the cache.
Arguments:
pProvidor - Providor to try
pszPrinterName - Name of printer that will be sent to the providor
phPrinter - Receives printer handle on ROUTER_SUCCESS
pDefault - Defaults used to open printer
pdwFirstSignificantError - Pointer to DWORD to get updated error.
This gets updated on ROUTER_STOP_ROUTING or ROUTER_UNKNOWN.
Return Value:
ROUTER_* status code:
ROUTER_SUCCESS, phPrinter holds return handle, name cached
ROUTER_UNKNOWN, printer not recognized, error updated
ROUTER_STOP_ROUTING, printer recognized, but failure, error updated
--*/
{
DWORD OpenError;
OpenError = (*pProvidor->PrintProvidor.fpOpenPrinterEx)
(pszPrinterName,
phPrinter,
pDefault,
pSplClientInfo,
dwLevel);
if (( OpenError == ROUTER_UNKNOWN && GetLastError() == ERROR_NOT_SUPPORTED ) ||
OpenError == ERROR_NOT_SUPPORTED )
OpenError = (*pProvidor->PrintProvidor.fpOpenPrinter)
(pszPrinterName,
phPrinter,
pDefault);
if( OpenError == ROUTER_SUCCESS ||
OpenError == ROUTER_STOP_ROUTING ){
//
// Now add this entry into the cache. We never cache
// the local providor.
//
EnterRouterSem();
if (!FindEntryinRouterCache(pszPrinterName)) {
AddEntrytoRouterCache(pszPrinterName, pProvidor);
}
LeaveRouterSem();
}
if( OpenError != ROUTER_SUCCESS ){
UpdateSignificantError(GetLastError(), pdwFirstSignificantError);
}
return OpenError;
}
BOOL
RouterOpenPrinterW(
LPWSTR pszPrinterNameIn,
HANDLE *pHandle,
LPPRINTER_DEFAULTS pDefault,
LPBYTE pSplClientInfo,
DWORD dwLevel,
BOOL bLocalProvidor
)
/*++
Routine Description:
Routes the OpenPrinter{Port} call. This checks the local providor
first (if bLocalProvidor TRUE), the the cache, and finally all the
non-local providors.
To open a printer, the following steps are taken:
1. Check localspl
This must be done to ensure that masq printers are handled
correctly (see comment below in code).
2. Check cache
This will speed up most of the connections, since OpenPrinters
tend to be clumped together.
3. Check registry under connections
If this is a connected printer, first try the providor
that granted the connection.
4. Check provider order
This is the last resort, since it is the slowest.
Arguments:
pPrinterName - Name of printer to open
pHandle - Handle to receive open printer. If the open was not
successful, this value may be modified!
pDefault - Default attributes of the open.
pSplClientInfo - Pointer ClientInfox structure
dwLevel - Level of the ClientInfo structure
bLocalProvidor TRUE = OpenPrinterW called, check localspl first.
FALSE = OpenPrinterPortW called, don't check localspl.
Return Value:
TRUE = success
FALSE = fail, GetLastError indicates error (must be non-zero!)
--*/
{
BOOL bReturn = TRUE;
DWORD dwFirstSignificantError = ERROR_INVALID_NAME;
LPPROVIDOR pProvidor;
LPPROVIDOR pProvidorAlreadyTried = NULL;
PPRINTHANDLE pPrintHandle;
HANDLE hPrinter;
DWORD OpenError;
BOOL bRemoveFromCache = FALSE;
PRINTER_DEFAULTS Default;
PDEVMODE pDevModeFree = NULL;
PWSTR pszPrinterName = pszPrinterNameIn;
PWSTR pszNoCache;
//
// Max name we allow for printers is MAX_UNC_PRINTER_NAME.
// Providers can use suffixes only for OpenPrinter (not for Add/Set)
//
if ( pszPrinterName &&
wcslen(pszPrinterName) + 1 > MAX_UNC_PRINTER_NAME + PRINTER_NAME_SUFFIX_MAX ) {
SetLastError(ERROR_INVALID_PRINTER_NAME);
return FALSE;
}
WaitForSpoolerInitialization();
// There may be a ",NoCache" appended to the printer name.
// We only want to send this NoCache name to win32spl, so make
// a regular name here.
if (pszPrinterName) {
pszNoCache = wcsstr(pszPrinterNameIn, szNoCache);
if (pszNoCache) {
pszPrinterName = AllocSplStr(pszPrinterNameIn);
if (!pszPrinterName) {
DBGMSG(DBG_WARNING, ("RouterOpenPrinter - Failed to alloc pszPrinterName.\n"));
return FALSE;
}
pszPrinterName[pszNoCache - pszPrinterNameIn] = L'\0';
}
}
pPrintHandle = AllocSplMem(sizeof(PRINTHANDLE));
if (!pPrintHandle) {
DBGMSG(DBG_WARNING, ("RouterOpenPrinter - Failed to alloc print handle.\n"));
if (pszPrinterName != pszPrinterNameIn) {
FreeSplStr(pszPrinterName);
pszPrinterName = pszPrinterNameIn;
}
return FALSE;
}
if( pszPrinterName ){
pPrintHandle->pszPrinter = AllocSplStr( pszPrinterName );
if (!pPrintHandle->pszPrinter) {
DBGMSG(DBG_WARNING, ("RouterOpenPrinter - Failed to alloc print name.\n"));
if (pszPrinterName != pszPrinterNameIn) {
FreeSplStr(pszPrinterName);
pszPrinterName = pszPrinterNameIn;
}
FreePrinterHandle( pPrintHandle );
return FALSE;
}
}
//
// Retrieve the per-user DevMode. This must be done at the router
// instead of the provider, since the per-user DevMode is only available
// on the client. It also must be here instead of client side, since
// spooler components will make this call also.
//
if( !pDefault || !pDefault->pDevMode ){
//
// No default specified--get the per-user one.
//
if( bGetDevModePerUser( NULL, pszPrinterName, &pDevModeFree ) &&
pDevModeFree ){
if( pDefault ){
Default.pDatatype = pDefault->pDatatype;
Default.DesiredAccess = pDefault->DesiredAccess;
} else {
Default.pDatatype = NULL;
Default.DesiredAccess = 0;
}
Default.pDevMode = pDevModeFree;
//
// Now switch to use the temp structure.
//
pDefault = &Default;
}
}
//
// We must check the local print providor first in
// the masquerading case.
//
// For example, when a Netware printer is opened:
//
// 1. First OpenPrinter to the Netware printer will succeed
// if it has been cached.
//
// 2. We create a local printer masquerading as a network printer.
//
// 3. Second OpenPrinter must open local masquerading printer.
// If we hit the cache, we will go to the Netware providor,
// and we will never use the masquerading printer.
//
// For this reason, we will not cache local printers in the
// RouterCache. The RouterCache will only containing Network
// Print Providers, i.e., Win32spl NwProvAu and other such providers.
//
// Also, we must always check the local printprovidor since
// DeletePrinter will be called on a false connect and
// we need to delete the local network printer rather
// than the remote printer. When we get rid of the false
// connect case, we go directly to the cache.
//
if (bLocalProvidor) {
pProvidor = pLocalProvidor;
OpenError = (*pProvidor->PrintProvidor.fpOpenPrinterEx)
(pszPrinterName, &hPrinter, pDefault,
pSplClientInfo, dwLevel);
if (OpenError == ROUTER_SUCCESS) {
goto Success;
}
UpdateSignificantError(GetLastError(), &dwFirstSignificantError);
if (OpenError == ROUTER_STOP_ROUTING) {
goto StopRouting;
}
}
//
// Now check the cache.
//
EnterRouterSem();
pProvidor = FindEntryinRouterCache(pszPrinterName);
LeaveRouterSem();
if (pProvidor) {
OpenError = (*pProvidor->PrintProvidor.fpOpenPrinterEx)
(pszPrinterName,
&hPrinter,
pDefault,
pSplClientInfo,
dwLevel);
if (( OpenError == ROUTER_UNKNOWN && GetLastError() == ERROR_NOT_SUPPORTED ) ||
OpenError == ERROR_NOT_SUPPORTED ){
OpenError = (*pProvidor->PrintProvidor.fpOpenPrinter)
(pszPrinterName,
&hPrinter,
pDefault);
}
if (OpenError == ROUTER_SUCCESS) {
goto Success;
}
UpdateSignificantError(GetLastError(), &dwFirstSignificantError);
if (OpenError == ROUTER_STOP_ROUTING) {
goto StopRouting;
}
//
// Wasn't claimed by above providor, so remove from cache.
// If a providor returns ROUTER_STOP_ROUTING, then it states
// that it is the sole owner of the printer name (i.e.,
// it has been recognized but can't be opened, and can't
// be accessed by other providors). Therefore we keep
// it in the cache.
//
bRemoveFromCache = TRUE;
//
// Don't try this providor again below.
//
pProvidorAlreadyTried = pProvidor;
}
//
// Not in the cache. Check if it is in the registry under
// connections.
//
pProvidor = FindProvidorFromConnection( pszPrinterName );
//
// If we want to remove it from the cache, do so here. Note
// we only remove it if we failed above, AND the connection wasn't
// originally established using the provider.
//
// If the connection fails, but that provider "owns" the printer
// connection, leave it in the cache since we won't try other providers.
//
if( bRemoveFromCache && pProvidor != pProvidorAlreadyTried ){
EnterRouterSem();
DeleteEntryfromRouterCache(pszPrinterName);
LeaveRouterSem();
}
if( pProvidor ){
//
// If we already tried this providor, don't try it again.
//
if( pProvidor != pProvidorAlreadyTried ){
OpenError = TryOpenPrinterAndCache( pProvidor,
pszPrinterName,
&hPrinter,
pDefault,
&dwFirstSignificantError,
pSplClientInfo,
dwLevel);
if( OpenError == ROUTER_SUCCESS ){
goto Success;
}
}
//
// We stop routing at this point! If a user wants to go with
// another providor, they need to remove the connection then
// re-establish it.
//
goto StopRouting;
}
//
// Check all non-localspl providors.
//
for (pProvidor = pLocalProvidor->pNext;
pProvidor;
pProvidor = pProvidor->pNext) {
if( pProvidor == pProvidorAlreadyTried ){
//
// We already tried this providor, and it failed.
//
continue;
}
// Use ",NoCache" only if Provider is win32spl
OpenError = TryOpenPrinterAndCache( pProvidor,
_wcsicmp(pProvidor->lpName, szWin32spl) ?
pszPrinterName : pszPrinterNameIn,
&hPrinter,
pDefault,
&dwFirstSignificantError,
pSplClientInfo,
dwLevel);
switch( OpenError ) {
case ROUTER_SUCCESS:
goto Success;
case ROUTER_STOP_ROUTING:
goto StopRouting;
}
}
StopRouting:
//
// Did not find a providor, return the error.
//
FreePrinterHandle( pPrintHandle );
//
// Set using first significant error. If there was no signifcant
// error, we use ERROR_INVALID_PRINTER_NAME.
//
SPLASSERT(dwFirstSignificantError);
if (dwFirstSignificantError == ERROR_INVALID_NAME)
dwFirstSignificantError = ERROR_INVALID_PRINTER_NAME;
SetLastError(dwFirstSignificantError);
bReturn = FALSE;
Success:
if( bReturn ){
pPrintHandle->signature = PRINTHANDLE_SIGNATURE;
pPrintHandle->pProvidor = pProvidor;
pPrintHandle->hPrinter = hPrinter;
pPrintHandle->hFileSpooler = INVALID_HANDLE_VALUE;
pPrintHandle->szTempSpoolFile = NULL;
pPrintHandle->dwUniqueSessionID = 0;
*pHandle = (HANDLE)pPrintHandle;
}
FreeSplMem( pDevModeFree );
if (pszPrinterName != pszPrinterNameIn)
FreeSplStr(pszPrinterName);
return bReturn;
}
BOOL
ResetPrinterW(
HANDLE hPrinter,
LPPRINTER_DEFAULTS pDefault)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (pDefault) {
if (pDefault->pDatatype == (LPWSTR)-1 ||
pDefault->pDevMode == (LPDEVMODE)-1) {
if (!wcscmp(pPrintHandle->pProvidor->lpName, szLocalSplDll)) {
return (*pPrintHandle->pProvidor->PrintProvidor.fpResetPrinter)
(pPrintHandle->hPrinter,
pDefault);
} else {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
} else {
return (*pPrintHandle->pProvidor->PrintProvidor.fpResetPrinter)
(pPrintHandle->hPrinter,
pDefault);
}
} else {
return (*pPrintHandle->pProvidor->PrintProvidor.fpResetPrinter)
(pPrintHandle->hPrinter,
pDefault);
}
}
HANDLE
AddPrinterExW(
LPWSTR pName,
DWORD Level,
LPBYTE pPrinter,
LPBYTE pClientInfo,
DWORD dwLevel
)
{
LPPROVIDOR pProvidor;
DWORD dwFirstSignificantError = ERROR_INVALID_NAME;
HANDLE hPrinter;
PPRINTHANDLE pPrintHandle;
LPWSTR pPrinterName = NULL;
LPWSTR pszServer = NULL;
WaitForSpoolerInitialization();
if ( pPrinter ) {
switch ( Level ) {
case 1:
pPrinterName = ((PPRINTER_INFO_1)pPrinter)->pName;
break;
case 2:
pPrinterName = ((PPRINTER_INFO_2)pPrinter)->pPrinterName;
pszServer = ((PPRINTER_INFO_2)pPrinter)->pServerName;
break;
default:
break;
}
//
// Name length (plus null terminator) and server
// name (plus backslash) length check.
//
if (( pPrinterName && wcslen(pPrinterName) + 1 > MAX_PRINTER_NAME ) ||
( pszServer && wcslen(pszServer) > (MAX_UNC_PRINTER_NAME - MAX_PRINTER_NAME - 1))) {
SetLastError(ERROR_INVALID_PRINTER_NAME);
return FALSE;
}
}
pPrintHandle = AllocSplMem(sizeof(PRINTHANDLE));
if (!pPrintHandle) {
DBGMSG( DBG_WARNING, ("Failed to alloc print handle."));
goto Fail;
}
if( pPrinterName ){
WCHAR szFullPrinterName[MAX_UNC_PRINTER_NAME];
szFullPrinterName[0] = 0;
if( pszServer ){
wcscpy( szFullPrinterName, pszServer );
wcscat( szFullPrinterName, L"\\" );
}
wcscat( szFullPrinterName, pPrinterName );
pPrintHandle->pszPrinter = AllocSplStr( szFullPrinterName );
if( !pPrintHandle->pszPrinter ){
goto Fail;
}
}
pProvidor = pLocalProvidor;
while (pProvidor) {
hPrinter = (HANDLE)(*pProvidor->PrintProvidor.fpAddPrinterEx)
(pName,
Level,
pPrinter,
pClientInfo,
dwLevel);
if ( !hPrinter && GetLastError() == ERROR_NOT_SUPPORTED ) {
hPrinter = (HANDLE)(*pProvidor->PrintProvidor.fpAddPrinter)
(pName,
Level,
pPrinter);
}
if ( hPrinter ) {
//
// CLS
//
// !! HACK !!
//
// Make (HANDLE)-1 ROUTER_STOP_ROUTING.
//
if( hPrinter == (HANDLE)-1 ){
UpdateSignificantError(GetLastError(), &dwFirstSignificantError);
break;
}
pPrintHandle->signature = PRINTHANDLE_SIGNATURE;
pPrintHandle->pProvidor = pProvidor;
pPrintHandle->hPrinter = hPrinter;
pPrintHandle->hFileSpooler = INVALID_HANDLE_VALUE;
pPrintHandle->szTempSpoolFile = NULL;
pPrintHandle->dwUniqueSessionID = 0;
return (HANDLE)pPrintHandle;
}
UpdateSignificantError(GetLastError(), &dwFirstSignificantError);
pProvidor = pProvidor->pNext;
}
UpdateSignificantError(ERROR_INVALID_PRINTER_NAME, &dwFirstSignificantError);
SetLastError(dwFirstSignificantError);
Fail:
if( pPrintHandle ){
FreeSplStr( pPrintHandle->pszPrinter );
FreeSplMem(pPrintHandle);
}
return FALSE;
}
HANDLE
AddPrinterW(
LPWSTR pName,
DWORD Level,
LPBYTE pPrinter
)
{
return AddPrinterExW(pName, Level, pPrinter, NULL, 0);
}
BOOL
DeletePrinter(
HANDLE hPrinter
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpDeletePrinter)(pPrintHandle->hPrinter);
}
BOOL
AddPrinterConnectionW(
LPWSTR pName
)
{
DWORD dwLastError;
HANDLE hPrinter;
HKEY hClientKey = NULL;
BOOL rc = FALSE;
LPPRINTER_INFO_2 pPrinterInfo2;
LPPRINTHANDLE pPrintHandle;
WaitForSpoolerInitialization();
//
// If the printer connection being made is \\server\sharename,
// this may be different from the \\server\printername.
// Make sure we have the real name, so that we can be consistent
// in the registry.
//
if (!OpenPrinter(pName,
&hPrinter,
NULL)) {
return FALSE;
}
pPrinterInfo2 = pGetPrinterInfo2( hPrinter );
pPrintHandle = (LPPRINTHANDLE)hPrinter;
if( pPrinterInfo2 ){
if ((*pPrintHandle->pProvidor->PrintProvidor.
fpAddPrinterConnection)(pPrinterInfo2->pPrinterName)) {
if( SavePrinterConnectionInRegistry(
pPrinterInfo2,
pPrintHandle->pProvidor )){
rc = TRUE;
} else {
dwLastError = GetLastError();
(*pPrintHandle->pProvidor->PrintProvidor.
fpDeletePrinterConnection)(pPrinterInfo2->pPrinterName);
SetLastError(dwLastError);
}
}
FreeSplMem(pPrinterInfo2);
}
dwLastError = GetLastError();
ClosePrinter(hPrinter);
SetLastError(dwLastError);
return rc;
}
BOOL
DeletePrinterConnectionW(
LPWSTR pName
)
{
BOOL bRet = FALSE;
BOOL bDone = FALSE;
HANDLE hPrinter;
//
// If pName is empty string, all providers will fail with ERROR_INVALID_NAME
// and we will delete the registry key. For empty string, it will
// delete all subkeys under Printers\\Connections. Fix it by checking
// pName against empty string.
//
if (pName && *pName)
{
WaitForSpoolerInitialization();
//
// Adding the code required to succeed DeletePrinterConnection
// with a Share name
//
if(OpenPrinter(pName,&hPrinter,NULL))
{
DWORD PrntrInfoSize=0,PrntrInfoSizeReq=0;
PPRINTER_INFO_2 pPrinterInfo2 = NULL;
if(!GetPrinter(hPrinter,
2,
(LPBYTE)pPrinterInfo2,
PrntrInfoSize,
&PrntrInfoSizeReq) &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(pPrinterInfo2 = (PPRINTER_INFO_2)AllocSplMem((PrntrInfoSize = PrntrInfoSizeReq))) &&
GetPrinter(hPrinter,
2,
(LPBYTE)pPrinterInfo2,
PrntrInfoSize,
&PrntrInfoSizeReq))
{
PPRINTHANDLE pPrintHandle;
pPrintHandle = (PPRINTHANDLE)hPrinter;
if((bRet = (*pPrintHandle->
pProvidor->
PrintProvidor.
fpDeletePrinterConnection)(pPrinterInfo2->pPrinterName)))
{
bRet = RemovePrinterConnectionInRegistry(pPrinterInfo2->pPrinterName);
bDone = TRUE;
}
}
if(hPrinter)
ClosePrinter(hPrinter);
if(pPrinterInfo2)
FreeSplMem(pPrinterInfo2);
}
else
{
LPPROVIDOR pProvidor;
pProvidor = pLocalProvidor;
if (pName && (wcslen(pName) < MAX_PRINTER_NAME))
{
for(pProvidor=pLocalProvidor;
pProvidor && (GetLastError()!=ERROR_INVALID_NAME) &&!bDone;
pProvidor = pProvidor->pNext)
{
if(bRet = (*pProvidor->PrintProvidor.fpDeletePrinterConnection)(pName))
{
bRet = RemovePrinterConnectionInRegistry(pName);
bDone = TRUE;
}
}
}
else
{
SetLastError(ERROR_INVALID_PRINTER_NAME);
bDone = TRUE;
}
}
//
// If all providors failed with ERROR_INVALID_NAME then try to delete
// from registry
//
if(!bDone && (GetLastError()==ERROR_INVALID_NAME))
{
if(!(bRet = RemovePrinterConnectionInRegistry(pName)))
{
SetLastError(ERROR_INVALID_PRINTER_NAME);
}
}
}
else
{
SetLastError(ERROR_INVALID_PRINTER_NAME);
}
return bRet;
}
BOOL
SetPrinterW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pPrinter,
DWORD Command
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
LPWSTR pPrinterName = NULL;
PDEVMODE pDevModeRestore = NULL;
BOOL bReturn;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if ( pPrinter ) {
switch (Level) {
case 2:
pPrinterName = ((PPRINTER_INFO_2)pPrinter)->pPrinterName;
break;
case 4:
pPrinterName = ((PPRINTER_INFO_4)pPrinter)->pPrinterName;
break;
case 5:
pPrinterName = ((PPRINTER_INFO_5)pPrinter)->pPrinterName;
break;
}
if ( pPrinterName &&
wcslen(pPrinterName) + 1 > MAX_PRINTER_NAME ) {
SetLastError(ERROR_INVALID_PRINTER_NAME);
return FALSE;
}
}
switch( Level ){
case 8:
{
//
// Setting the global DevMode.
//
PPRINTER_INFO_8 pPrinterInfo8 = (PPRINTER_INFO_8)pPrinter;
PPRINTER_INFO_2 pPrinterInfo2;
DWORD rc = FALSE;;
if( Command != 0 ){
SetLastError( ERROR_INVALID_PRINTER_COMMAND );
return FALSE;
}
//
// Call GetPrinter then SetPrinter.
//
pPrinterInfo2 = pGetPrinterInfo2( hPrinter );
if( pPrinterInfo2 ){
//
// Set the DevMode, and also clear the security descriptor
// so that the set will succeed.
//
pPrinterInfo2->pDevMode = pPrinterInfo8->pDevMode;
pPrinterInfo2->pSecurityDescriptor = NULL;
rc = (*pPrintHandle->pProvidor->PrintProvidor.fpSetPrinter) (
pPrintHandle->hPrinter,
2,
(PBYTE)pPrinterInfo2,
Command );
FreeSplMem( pPrinterInfo2 );
}
return rc;
}
case 9:
{
PPRINTER_INFO_9 pPrinterInfo9 = (PPRINTER_INFO_9)pPrinter;
//
// Setting the per-user DevMode.
//
if( !pPrinter ){
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if( Command != 0 ){
SetLastError( ERROR_INVALID_PRINTER_COMMAND );
return FALSE;
}
if( !IsLocalCall( )){
SetLastError( ERROR_NOT_SUPPORTED );
return FALSE;
}
return bSetDevModePerUser( NULL,
pPrintHandle->pszPrinter,
pPrinterInfo9->pDevMode );
}
case 2:
{
PPRINTER_INFO_2 pPrinterInfo2 = (PPRINTER_INFO_2)pPrinter;
if( IsLocalCall( )){
if( pPrinterInfo2 && pPrinterInfo2->pDevMode ){
bSetDevModePerUser( NULL,
pPrintHandle->pszPrinter,
pPrinterInfo2->pDevMode );
//
// Don't set the global DevMode.
//
pDevModeRestore = pPrinterInfo2->pDevMode;
pPrinterInfo2->pDevMode = NULL;
}
}
}
default:
break;
}
bReturn = (*pPrintHandle->pProvidor->PrintProvidor.fpSetPrinter)
(pPrintHandle->hPrinter, Level, pPrinter, Command);
if( pDevModeRestore ){
((PPRINTER_INFO_2)pPrinter)->pDevMode = pDevModeRestore;
}
return bReturn;
}
BOOL
GetPrinterW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pPrinter,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
PDEVMODE pDevModeSrc = NULL;
PDEVMODE pDevModeDest = NULL;
PDEVMODE pDevModeFree = NULL;
BOOL bCallServer = TRUE;
BOOL bReturnValue = FALSE;
PPRINTER_INFO_2 pPrinterInfo2 = NULL;
DWORD cbDevModeSrc;
DWORD Error;
DWORD cbExtraSpace2 = 0;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if ((pPrinter == NULL) && (cbBuf != 0)) {
SetLastError(ERROR_INVALID_USER_BUFFER);
return FALSE;
}
switch( Level ){
case 2:
if( pPrintHandle->pszPrinter && IsLocalCall( )){
bGetDevModePerUser( NULL,
pPrintHandle->pszPrinter,
&pDevModeFree );
if( pDevModeFree ){
pDevModeSrc = pDevModeFree;
cbDevModeSrc = pDevModeSrc->dmSize +
pDevModeSrc->dmDriverExtra;
cbExtraSpace2 = DWORD_ALIGN_UP( cbDevModeSrc );
cbBuf = DWORD_ALIGN_DOWN( cbBuf );
}
}
break;
case 8:
{
PPRINTER_INFO_8 pPrinterInfo8 = (PPRINTER_INFO_8)pPrinter;
//
// Handle info level 8 calls for global DevModes.
//
if( !pPrintHandle->pszPrinter ){
SetLastError( ERROR_FILE_NOT_FOUND );
return FALSE;
}
bCallServer = FALSE;
*pcbNeeded = sizeof( PRINTER_INFO_8 );
//
// Call GetPrinter to get the real DevMode.
//
pPrinterInfo2 = pGetPrinterInfo2( hPrinter );
if( pPrinterInfo2 ){
//
// Pickup the DevMode from pPrinterInfo2;
// destination is after the pDevModeStructure.
// Don't need to free pDevModeSrc since it will be
// freed when pPrinterInfo2 is released.
//
pDevModeSrc = pPrinterInfo2->pDevMode;
if( pDevModeSrc ){
cbDevModeSrc = pDevModeSrc->dmSize +
pDevModeSrc->dmDriverExtra;
*pcbNeeded += cbDevModeSrc;
}
if( cbBuf < *pcbNeeded ){
//
// Not enough space. SetLastError and fall through
// to the end.
//
SetLastError( ERROR_INSUFFICIENT_BUFFER );
} else {
bReturnValue = TRUE;
if( pDevModeSrc ){
//
// Update the pointer and indicate via pDevModeDest
// that we need to copy the DevMode in.
//
pDevModeDest = (PDEVMODE)&pPrinterInfo8[1];
pPrinterInfo8->pDevMode = pDevModeDest;
} else {
//
// No DevMode, return pointer to NULL.
//
pPrinterInfo8->pDevMode = NULL;
}
}
}
break;
}
case 9:
{
//
// Per-user DevMode. Use the client side one.
//
PPRINTER_INFO_9 pPrinterInfo9 = (PPRINTER_INFO_9)pPrinter;
if( !pPrintHandle->pszPrinter ){
SetLastError( ERROR_INVALID_HANDLE );
return FALSE;
}
if( !IsLocalCall( )){
SetLastError( ERROR_NOT_SUPPORTED );
return FALSE;
}
bCallServer = FALSE;
*pcbNeeded = sizeof( PRINTER_INFO_9 );
if( bGetDevModePerUserEvenForShares( NULL,
pPrintHandle->pszPrinter,
&pDevModeFree )){
pDevModeSrc = pDevModeFree;
if( pDevModeSrc ){
cbDevModeSrc = pDevModeSrc->dmSize +
pDevModeSrc->dmDriverExtra;
*pcbNeeded += cbDevModeSrc;
}
}
if( cbBuf < *pcbNeeded ){
//
// Not enough space. We'll fall through below
// and fail.
//
SetLastError( ERROR_INSUFFICIENT_BUFFER );
} else {
bReturnValue = TRUE;
if( pDevModeSrc ){
pDevModeDest = (PDEVMODE)&pPrinterInfo9[1];
pPrinterInfo9->pDevMode = pDevModeDest;
} else {
//
// No per-user DevMode. Return SUCCESS, but indicate
// no DevMode available.
//
pPrinterInfo9->pDevMode = NULL;
}
}
break;
}
default:
break;
}
if( bCallServer ){
DWORD cbAvailable;
//
// Allocate extra space at the end for the per-user DevMode,
// in case there isn't a global devmode in the printer info 2
// structure.
//
cbAvailable = ( cbBuf >= cbExtraSpace2 ) ?
cbBuf - cbExtraSpace2 :
0;
bReturnValue = (*pPrintHandle->pProvidor->PrintProvidor.fpGetPrinter)
(pPrintHandle->hPrinter, Level, pPrinter,
cbAvailable, pcbNeeded);
*pcbNeeded += cbExtraSpace2;
}
Error = GetLastError();
if( Level == 9 && pDevModeSrc == NULL){
PPRINTER_INFO_2 pInfo2 = (PPRINTER_INFO_2)pPrinter;
if( pInfo2 && pInfo2->pDevMode ){
pDevModeSrc = pInfo2->pDevMode;
}
}
//
// Special case INFO level 2, since we want to get the provider's
// information, but then we also want the per-user DevMode.
//
// Only do this for local calls.
//
if( cbExtraSpace2 ){
PPRINTER_INFO_2 pInfo2 = (PPRINTER_INFO_2)pPrinter;
//
// If we succeeded and we have a buffer, then we need to check if we
// need to put the per-user DevMode at the end of the buffer.
//
if( pInfo2 && bReturnValue ){
//
// If we have no DevMode, or it's compatible, then we want to
// use the per-user DevMode.
//
if( !pInfo2->pDevMode ||
bCompatibleDevMode( pPrintHandle,
pInfo2->pDevMode,
pDevModeSrc )){
pDevModeDest = (PDEVMODE)(pPrinter + cbBuf - cbExtraSpace2 );
pInfo2->pDevMode = pDevModeDest;
} else {
//
// !! POLICY !!
//
// Not compatible with per-user DevMode. Delete the
// per-user one.
//
bSetDevModePerUser( NULL, pPrintHandle->pszPrinter, NULL );
}
}
}
//
// Check if we need to copy over a DevMode.
//
if( pDevModeDest ){
//
// Update the DevMode.
//
CopyMemory( (PVOID)pDevModeDest,
(PVOID)pDevModeSrc,
cbDevModeSrc );
bReturnValue = TRUE;
}
FreeSplMem( pDevModeFree );
FreeSplMem( pPrinterInfo2 );
SetLastError( Error );
return bReturnValue;
}
DWORD
GetPrinterDataW(
HANDLE hPrinter,
LPWSTR pValueName,
LPDWORD pType,
LPBYTE pData,
DWORD nSize,
LPDWORD pcbNeeded
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpGetPrinterData)(pPrintHandle->hPrinter,
pValueName,
pType,
pData,
nSize,
pcbNeeded);
}
DWORD
GetPrinterDataExW(
HANDLE hPrinter,
LPCWSTR pKeyName,
LPCWSTR pValueName,
LPDWORD pType,
LPBYTE pData,
DWORD nSize,
LPDWORD pcbNeeded
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpGetPrinterDataEx)(pPrintHandle->hPrinter,
pKeyName,
pValueName,
pType,
pData,
nSize,
pcbNeeded);
}
DWORD
EnumPrinterDataW(
HANDLE hPrinter,
DWORD dwIndex, // index of value to query
LPWSTR pValueName, // address of buffer for value string
DWORD cbValueName, // size of buffer for value string
LPDWORD pcbValueName, // address for size of value buffer
LPDWORD pType, // address of buffer for type code
LPBYTE pData, // address of buffer for value data
DWORD cbData, // size of buffer for value data
LPDWORD pcbData // address for size of data buffer
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpEnumPrinterData)(pPrintHandle->hPrinter,
dwIndex,
pValueName,
cbValueName,
pcbValueName,
pType,
pData,
cbData,
pcbData);
}
DWORD
EnumPrinterDataExW(
HANDLE hPrinter,
LPCWSTR pKeyName, // address of key name
LPBYTE pEnumValues,
DWORD cbEnumValues,
LPDWORD pcbEnumValues,
LPDWORD pnEnumValues
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpEnumPrinterDataEx)(pPrintHandle->hPrinter,
pKeyName,
pEnumValues,
cbEnumValues,
pcbEnumValues,
pnEnumValues);
}
DWORD
EnumPrinterKeyW(
HANDLE hPrinter,
LPCWSTR pKeyName, // address of key name
LPWSTR pSubkey, // address of buffer for value string
DWORD cbSubkey, // size of buffer for value string
LPDWORD pcbSubkey // address for size of value buffer
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpEnumPrinterKey)(pPrintHandle->hPrinter,
pKeyName,
pSubkey,
cbSubkey,
pcbSubkey);
}
DWORD
DeletePrinterDataW(
HANDLE hPrinter,
LPWSTR pValueName
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpDeletePrinterData)(pPrintHandle->hPrinter,
pValueName);
}
DWORD
DeletePrinterDataExW(
HANDLE hPrinter,
LPCWSTR pKeyName,
LPCWSTR pValueName
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpDeletePrinterDataEx)(pPrintHandle->hPrinter,
pKeyName,
pValueName);
}
DWORD
DeletePrinterKeyW(
HANDLE hPrinter,
LPCWSTR pKeyName
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpDeletePrinterKey)(pPrintHandle->hPrinter,
pKeyName);
}
DWORD
SetPrinterDataW(
HANDLE hPrinter,
LPWSTR pValueName,
DWORD Type,
LPBYTE pData,
DWORD cbData
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpSetPrinterData)(pPrintHandle->hPrinter,
pValueName,
Type,
pData,
cbData);
}
DWORD
SetPrinterDataExW(
HANDLE hPrinter,
LPCWSTR pKeyName,
LPCWSTR pValueName,
DWORD Type,
LPBYTE pData,
DWORD cbData
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpSetPrinterDataEx)(pPrintHandle->hPrinter,
pKeyName,
pValueName,
Type,
pData,
cbData);
}
DWORD
WaitForPrinterChange(
HANDLE hPrinter,
DWORD Flags
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpWaitForPrinterChange)
(pPrintHandle->hPrinter, Flags);
}
BOOL
ClosePrinter(
HANDLE hPrinter
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
EnterRouterSem();
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
LeaveRouterSem();
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
//
// Close any notifications on this handle.
//
// The local case cleans up the event, while the remote
// case potentially cleans up the Reply Notification context
// handle.
//
// We must close this first, since the Providor->ClosePrinter
// call removes data structures that FindClose... relies on.
//
// Client side should be shutdown by winspool.drv.
//
if (pPrintHandle->pChange &&
(pPrintHandle->pChange->eStatus & STATUS_CHANGE_VALID)) {
FindClosePrinterChangeNotificationWorker(hPrinter);
}
LeaveRouterSem();
if ((*pPrintHandle->pProvidor->PrintProvidor.fpClosePrinter) (pPrintHandle->hPrinter)) {
//
// We can't just free it, since there may be a reply waiting
// on it.
//
FreePrinterHandle(pPrintHandle);
return TRUE;
} else
return FALSE;
}
/* FormatPrinterForRegistryKey
*
* Returns a pointer to a copy of the source string with backslashes removed.
* This is to store the printer name as the key name in the registry,
* which interprets backslashes as branches in the registry structure.
* Convert them to commas, since we don't allow printer names with commas,
* so there shouldn't be any clashes.
* If there are no backslashes, the string is unchanged.
*/
LPWSTR
FormatPrinterForRegistryKey(
LPCWSTR pSource, /* The string from which backslashes are to be removed. */
LPWSTR pScratch /* Scratch buffer for the function to write in; */
) /* must be at least as long as pSource. */
{
LPWSTR p;
if (pScratch != pSource) {
//
// Copy the string into the scratch buffer:
//
wcscpy(pScratch, pSource);
}
//
// Check each character, and, if it's a backslash,
// convert it to a comma:
//
for (p = pScratch; *p; ++p) {
if (*p == L'\\')
*p = L',';
}
return pScratch;
}
/* FormatRegistryKeyForPrinter
*
* Returns a pointer to a copy of the source string with backslashes added.
* This must be the opposite of FormatPrinterForRegistryKey, so the mapping
* _must_ be 1-1.
*
* If there are no commas, the string is unchanged.
*/
LPWSTR
FormatRegistryKeyForPrinter(
LPWSTR pSource, /* The string from which backslashes are to be added. */
LPWSTR pScratch /* Scratch buffer for the function to write in; */
) /* must be at least as long as pSource. */
{
/* Copy the string into the scratch buffer:
*/
wcscpy(pScratch, pSource);
/* Check each character, and, if it's a backslash,
* convert it to a comma:
*/
for (pSource = pScratch; *pSource; pSource++) {
if (*pSource == L',')
*pSource = L'\\';
}
return pScratch;
}
/* SavePrinterConnectionInRegistry
*
* Saves data in the registry for a printer connection.
* Creates a key under the current impersonation client's key
* in the registry under \Printers\Connections.
* The printer name is stripped of backslashes, since the registry
* API does not permit the creation of keys with backslashes.
* They are replaced by commas, which are invalid characters
* in printer names, so we should never get one passed in.
*
*
* *** WARNING ***
*
* IF YOU MAKE CHANGES TO THE LOCATION IN THE REGISTRY
* WHERE PRINTER CONNECTIONS ARE STORED, YOU MUST MAKE
* CORRESPONDING CHANGES IN USER\USERINIT\USERINIT.C.
*
*/
BOOL
SavePrinterConnectionInRegistry(
PPRINTER_INFO_2 pPrinterInfo2,
LPPROVIDOR pProvidor
)
{
HKEY hClientKey = NULL;
HKEY hConnectionsKey;
HKEY hPrinterKey;
DWORD Status;
BOOL rc = FALSE;
LPCTSTR pszProvidor = pProvidor->lpName;
PWSTR pKeyName = NULL;
DWORD cchSize = MAX_PATH;
DWORD dwError;
//
// CLS
//
// If the provider is localspl, change it to win32spl.dll.
// This is required for clustering since localspl handles printer
// connections, but they should be "owned" by win32spl.dll. When
// Someone opens a printer that they are connected to, we will
// always hit localspl.dll first before we look at this entry.
//
// When the cluster is remote, then they need to go through win32spl.dll.
//
if( pProvidor == pLocalProvidor ){
pszProvidor = szWin32spl;
}
hClientKey = GetClientUserHandle(KEY_READ);
if (hClientKey) {
if (wcslen(pPrinterInfo2->pPrinterName) < cchSize &&
(pKeyName = AllocSplMem(cchSize * sizeof(WCHAR)))) {
Status = RegCreateKeyEx(hClientKey, szRegistryConnections,
REG_OPTION_RESERVED, NULL, REG_OPTION_NON_VOLATILE,
KEY_WRITE, NULL, &hConnectionsKey, NULL);
if (Status == NO_ERROR) {
/* Make a key name without backslashes, so that the
* registry doesn't interpret them as branches in the registry tree:
*/
FormatPrinterForRegistryKey(pPrinterInfo2->pPrinterName,
pKeyName);
Status = RegCreateKeyEx(hConnectionsKey, pKeyName, REG_OPTION_RESERVED,
NULL, 0, KEY_WRITE, NULL, &hPrinterKey, NULL);
if (Status == NO_ERROR) {
RegSetValueEx(hPrinterKey,
szServerValue,
0,
REG_SZ,
(LPBYTE)pPrinterInfo2->pServerName,
(lstrlen(pPrinterInfo2->pServerName)+1) *
sizeof(pPrinterInfo2->pServerName[0]));
Status = RegSetValueEx(hPrinterKey,
szProvidorValue,
0,
REG_SZ,
(LPBYTE)pszProvidor,
(lstrlen(pszProvidor)+1) *
sizeof(pszProvidor[0]));
if (Status == ERROR_SUCCESS) {
dwError = UpdatePrinterRegUser(hClientKey,
NULL,
pPrinterInfo2->pPrinterName,
NULL,
UPDATE_REG_CHANGE);
if (dwError == ERROR_SUCCESS) {
BroadcastMessage(BROADCAST_TYPE_MESSAGE,
WM_WININICHANGE,
0,
(LPARAM)szDevices);
rc = TRUE;
} else {
DBGMSG(DBG_TRACE, ("UpdatePrinterRegUser failed: Error %d\n",
dwError));
}
} else {
DBGMSG(DBG_WARNING, ("RegSetValueEx(%ws) failed: Error %d\n",
pszProvidor, Status));
rc = FALSE;
}
RegCloseKey(hPrinterKey);
} else {
DBGMSG(DBG_WARNING, ("RegCreateKeyEx(%ws) failed: Error %d\n",
pKeyName, Status ));
rc = FALSE;
}
RegCloseKey(hConnectionsKey);
} else {
DBGMSG(DBG_WARNING, ("RegCreateKeyEx(%ws) failed: Error %d\n",
szRegistryConnections, Status ));
rc = FALSE;
}
if (!rc) {
DBGMSG(DBG_WARNING, ("Error updating registry: %d\n",
GetLastError())); /* This may not be the error */
/* that caused the failure. */
if (pKeyName)
RegDeleteKey(hClientKey, pKeyName);
}
FreeSplMem(pKeyName);
}
RegCloseKey(hClientKey);
}
return rc;
}
BOOL
RemovePrinterConnectionInRegistry(
LPWSTR pName)
{
HKEY hClientKey;
HKEY hPrinterConnectionsKey;
DWORD Status = NO_ERROR;
DWORD i = 0;
PWSTR pKeyName = NULL;
DWORD cchSize = MAX_PATH;
BOOL Found = FALSE;
BOOL bRet = FALSE;
if (pName &&
wcslen(pName) < cchSize) {
if (pKeyName = AllocSplMem(cchSize * sizeof(WCHAR))) {
hClientKey = GetClientUserHandle(KEY_READ);
if (hClientKey) {
Status = RegOpenKeyEx(hClientKey, szRegistryConnections,
REG_OPTION_RESERVED,
KEY_READ | KEY_WRITE, &hPrinterConnectionsKey);
if (Status == NO_ERROR) {
FormatPrinterForRegistryKey(pName, pKeyName);
bRet = DeleteSubKeyTree(hPrinterConnectionsKey, pKeyName);
RegCloseKey(hPrinterConnectionsKey);
}
if ( bRet ) {
UpdatePrinterRegUser(hClientKey,
NULL,
pName,
NULL,
UPDATE_REG_DELETE);
}
RegCloseKey(hClientKey);
if ( bRet ) {
BroadcastMessage(BROADCAST_TYPE_MESSAGE,
WM_WININICHANGE,
0,
(LPARAM)szDevices);
}
}
FreeSplMem(pKeyName);
}
}
return bRet;
}
VOID
PrinterHandleRundown(
HANDLE hPrinter)
{
LPPRINTHANDLE pPrintHandle;
if (hPrinter) {
pPrintHandle = (LPPRINTHANDLE)hPrinter;
switch (pPrintHandle->signature) {
case PRINTHANDLE_SIGNATURE:
// Log warning to detect handle free
DBGMSG(DBG_WARNING, ("PrinterHandleRundown: 0x%x 0x%x", pPrintHandle, pPrintHandle->hPrinter));
DBGMSG(DBG_TRACE, ("Rundown PrintHandle 0x%x\n", hPrinter));
ClosePrinter(hPrinter);
break;
case NOTIFYHANDLE_SIGNATURE:
DBGMSG(DBG_TRACE, ("Rundown NotifyHandle 0x%x\n", hPrinter));
RundownPrinterNotify(hPrinter);
break;
case CLUSTERHANDLE_SIGNATURE:
DBGMSG(DBG_TRACE, ("Rundown ClusterHandle 0x%x\n", hPrinter ));
ClusterSplClose(hPrinter);
break;
default:
//
// Unknown type.
//
DBGMSG( DBG_ERROR, ("Rundown: Unknown type 0x%x\n", hPrinter ) );
break;
}
}
return;
}
BOOL
ValidatePrinterName(
LPWSTR pPrinterName)
/*++
Function Description: Validates the fully qualified printer name. Performs the following checks
1) Length < MAX_UNC_PRINTER_NAME
2) No invalid chars in the names \,!
3) No empty names after removing trailing blanks
Parameters: pPrinterName - printer name
Return Values: TRUE if valid name; FALSE otherwise
--*/
{
DWORD dwLength;
LPWSTR pWack, pTemp, pLastSpace;
WCHAR szServer[MAX_UNC_PRINTER_NAME], szPrinter[MAX_UNC_PRINTER_NAME];
// Make length checks
dwLength = wcslen( pPrinterName );
if ( dwLength < 5 || dwLength > MAX_UNC_PRINTER_NAME )
{
return FALSE;
}
// Search for invalid characters , and !
if ( wcschr( pPrinterName, L',' ) ||
wcschr( pPrinterName, L'!' ) )
{
return FALSE;
}
// Search for exactly 3 wacks
if ( (pPrinterName[0] != L'\\') ||
(pPrinterName[1] != L'\\') ||
!(pWack = wcschr( pPrinterName+2, L'\\' )) ||
wcschr( pWack+1, L'\\' ) )
{
return FALSE;
}
// Check for empty server or printer names
wcsncpy( szServer, pPrinterName+2, (size_t) (pWack - (pPrinterName+2)) );
szServer[pWack - (pPrinterName+2)] = L'\0';
wcsncpy( szPrinter, pWack+1, dwLength - (size_t) ((pWack+1 - pPrinterName)) );
szPrinter[dwLength - (pWack+1 - pPrinterName)] = L'\0';
// Remove trailing spaces in the server name
for (pLastSpace = NULL, pTemp = szServer; pTemp && *pTemp; ++pTemp)
{
if (*pTemp == L' ') {
if (!pLastSpace) {
pLastSpace = pTemp;
}
} else {
pLastSpace = NULL;
}
}
if (pLastSpace) {
*pLastSpace = L'\0';
}
// Remove trailing spaces in the printer name
for (pLastSpace = NULL, pTemp = szPrinter; pTemp && *pTemp; ++pTemp)
{
if (*pTemp == L' ') {
if (!pLastSpace) {
pLastSpace = pTemp;
}
} else {
pLastSpace = NULL;
}
}
if (pLastSpace) {
*pLastSpace = L'\0';
}
if (!*szServer || !*szPrinter) {
return FALSE;
}
return TRUE;
}
BOOL
RouterAddPerMachineConnection(
LPCWSTR pPrinterNameP,
LPCWSTR pPrintServerP,
LPCWSTR pProviderP)
/*++
Function Description: RouterAddPerMachineConnection adds a subkey to HKEY_LOCAL_MACHINE\
SYSTEM\CurrentControlSet\Control\Print\Connections with the PrinterName.
The PrintServer name and the name of the dll used as a provider for
this connection are stored as values in the key.
Parameters:
pPrinterNameP - pointer to the fully qualified printer name. (\\printserver\name)
pPrintServerP - pointer to the print server name.
pProviderP - pointer to the provider name. Currently only LanMan Print Services
is supported. This corresponds to win32spl.dll. NULL or szNULL value
defaults to this provider. Currently there is no check to enforce that
only LanMan Print Services is passed.
Return Value: TRUE for success
FALSE otherwise.
--*/
{
BOOL bReturn = TRUE;
DWORD dwLocalConnection = 1, dwLastError, dwType, cbBuf;
HKEY hMcConnectionKey = NULL, hPrinterKey = NULL;
HKEY hProviderKey = NULL, hAllProviderKey = NULL;
HANDLE hImpersonationToken = NULL;
LPWSTR pPrintServer=NULL, pProvider=NULL, pPrinterName=NULL, pEnd;
WCHAR szConnData[MAX_PATH];
WCHAR szRegistryConnections[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Connections";
WCHAR szRegistryProviders[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Providers";
EnterRouterSem();
// Getting the name of the library for the provider.
if (!pProviderP || !*pProviderP) {
pProvider = AllocSplStr(L"win32spl.dll");
} else {
cbBuf = sizeof(szConnData);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegistryProviders, 0,
KEY_READ, &hAllProviderKey) ||
RegOpenKeyEx(hAllProviderKey, pProviderP, 0, KEY_READ,
&hProviderKey) ||
RegQueryValueEx(hProviderKey, L"Name", 0, &dwType,
(LPBYTE)szConnData,&cbBuf)) {
SetLastError(ERROR_INVALID_PARAMETER);
bReturn = FALSE;
goto CleanUp;
} else {
pProvider = AllocSplStr(szConnData);
}
}
pPrintServer = AllocSplStr(pPrintServerP);
pPrinterName = AllocSplStr(pPrinterNameP);
if (!pProvider || !pPrintServer || !pPrinterName) {
bReturn = FALSE;
goto CleanUp;
}
// Check for a fully qualified printer name without commas
if (!ValidatePrinterName(pPrinterName)) {
SetLastError(ERROR_INVALID_PRINTER_NAME);
bReturn = FALSE;
goto CleanUp;
}
// Replacing the \'s from the Printer name with ,'s
FormatPrinterForRegistryKey(pPrinterName, pPrinterName);
hImpersonationToken = RevertToPrinterSelf();
// Creating the subkey for the holding all printer connections.
if ((dwLastError = RegCreateKeyEx(HKEY_LOCAL_MACHINE, szRegistryConnections, 0,
NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
NULL, &hMcConnectionKey, NULL)) ||
(dwLastError = RegCreateKeyEx(hMcConnectionKey, pPrinterName, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&hPrinterKey, NULL))) {
SetLastError(dwLastError);
bReturn = FALSE;
goto CleanUp;
}
// Setting the connection data.
if ((dwLastError = RegSetValueEx(hPrinterKey, L"Server", 0, REG_SZ, (LPBYTE) pPrintServer,
(wcslen(pPrintServer)+1)*sizeof(pPrintServer[0]))) ||
(dwLastError = RegSetValueEx(hPrinterKey, L"Provider", 0, REG_SZ, (LPBYTE) pProvider,
(wcslen(pProvider)+1)*sizeof(pProvider[0]))) ||
(dwLastError = RegSetValueEx(hPrinterKey, L"LocalConnection", 0, REG_DWORD,
(LPBYTE) &dwLocalConnection, sizeof(dwLocalConnection)))) {
SetLastError(dwLastError);
bReturn = FALSE;
}
CleanUp:
if (pPrintServer) {
FreeSplStr(pPrintServer);
}
if (pProvider) {
FreeSplStr(pProvider);
}
if (hAllProviderKey) {
RegCloseKey(hAllProviderKey);
}
if (hProviderKey) {
RegCloseKey(hProviderKey);
}
if (hPrinterKey) {
RegCloseKey(hPrinterKey);
}
if (!bReturn) {
if (hMcConnectionKey) RegDeleteKey(hMcConnectionKey,pPrinterName);
}
if (pPrinterName) {
FreeSplStr(pPrinterName);
}
if (hMcConnectionKey) {
RegCloseKey(hMcConnectionKey);
}
if (hImpersonationToken) {
ImpersonatePrinterClient(hImpersonationToken);
}
LeaveRouterSem();
return bReturn;
}
BOOL
AddPerMachineConnectionW(
LPCWSTR pServer,
LPCWSTR pPrinterName,
LPCWSTR pPrintServer,
LPCWSTR pProvider
)
{
LPPROVIDOR pProvidor;
WaitForSpoolerInitialization();
pProvidor = pLocalProvidor;
if ((*pProvidor->PrintProvidor.fpAddPerMachineConnection)
(pServer, pPrinterName, pPrintServer, pProvider)) {
return RouterAddPerMachineConnection(pPrinterName,pPrintServer,pProvider);
} else if (GetLastError() != ERROR_INVALID_NAME) {
return FALSE;
}
pProvidor = pProvidor->pNext;
while (pProvidor) {
if ((*pProvidor->PrintProvidor.fpAddPerMachineConnection)
(pServer, pPrinterName, pPrintServer, pProvider)) {
return TRUE;
}
if (GetLastError() != ERROR_INVALID_NAME) {
return FALSE;
}
pProvidor = pProvidor->pNext;
}
return FALSE;
}
BOOL
RouterDeletePerMachineConnection(
LPCWSTR pPrinterNameP
)
/*++
Function Description: This function deletes the registry entry in HKEY_LOCAL_MACHINE\
SYSTEM\CurrentControlSet\Control\Print\Connections corresponding to
pPrinterNameP. All users will lose the connection when they logon.
Parameters: pPrinterNameP - pointer to the fully qualified name of the printer.
Return Values: TRUE for Success
FALSE otherwise.
--*/
{
BOOL bReturn = TRUE, bEnteredRouterSem = FALSE;
HANDLE hImpersonationToken = NULL;
HKEY hMcConnectionKey = NULL;
LPWSTR pPrinterName = NULL;
DWORD dwLastError;
WCHAR szRegistryConnections[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Connections";
if (!(pPrinterName = AllocSplStr(pPrinterNameP))) {
bReturn = FALSE;
goto CleanUp;
}
// Convert \'s to ,'s in the printer name.
FormatPrinterForRegistryKey(pPrinterName, pPrinterName);
EnterRouterSem();
bEnteredRouterSem = TRUE;
hImpersonationToken = RevertToPrinterSelf();
if (dwLastError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegistryConnections, 0,
KEY_ALL_ACCESS, &hMcConnectionKey)) {
SetLastError(dwLastError);
bReturn = FALSE;
goto CleanUp;
}
if (dwLastError = RegDeleteKey(hMcConnectionKey, pPrinterName)) {
SetLastError(dwLastError);
bReturn = FALSE;
}
CleanUp:
if (hMcConnectionKey) {
RegCloseKey(hMcConnectionKey);
}
if (pPrinterName) {
FreeSplStr(pPrinterName);
}
if (hImpersonationToken) {
ImpersonatePrinterClient(hImpersonationToken);
}
if (bEnteredRouterSem) {
LeaveRouterSem();
}
return bReturn;
}
BOOL
DeletePerMachineConnectionW(
LPCWSTR pServer,
LPCWSTR pPrinterName
)
{
LPPROVIDOR pProvidor;
WaitForSpoolerInitialization();
pProvidor = pLocalProvidor;
if ((*pProvidor->PrintProvidor.fpDeletePerMachineConnection)
(pServer, pPrinterName)) {
return RouterDeletePerMachineConnection(pPrinterName);
} else if (GetLastError() != ERROR_INVALID_NAME) {
return FALSE;
}
pProvidor = pProvidor->pNext;
while (pProvidor) {
if ((*pProvidor->PrintProvidor.fpDeletePerMachineConnection)
(pServer, pPrinterName)) {
return TRUE;
}
if (GetLastError() != ERROR_INVALID_NAME) {
return FALSE;
}
pProvidor = pProvidor->pNext;
}
return FALSE;
}
BOOL
RouterEnumPerMachineConnections(
LPCWSTR pServer,
LPBYTE pPrinterEnum,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
/*++
Function Description: This function copies the PRINTER_INFO_4 structs for all the per
machine connections into the buffer (pPrinterEnum).
Parameters: pServer - pointer to the server name (NULL for local)
pPrinterEnum - pointer to the buffer
cbBuf - size of the buffer in bytes
pcbNeeded - pointer to a variable which contains the number of bytes written
into the buffer/ number of bytes required (if the given buffer
is insufficient)
pcReturned - pointer to the variable which contains the number of PRINTER_INFO_4
structs returned in the buffer.
Return Values: TRUE for success
FALSE otherwise.
--*/
{
DWORD dwRegIndex, dwType, cbdata, dwNameSize, dwLastError;
BOOL bReturn = TRUE, bEnteredRouterSem = FALSE;
HANDLE hImpersonationToken = NULL;
HKEY hMcConnectionKey = NULL, hPrinterKey = NULL;
LPBYTE pStart = NULL, pEnd = NULL;
WCHAR szMachineConnections[]=L"SYSTEM\\CurrentControlSet\\Control\\Print\\Connections";
WCHAR szPrinterName[MAX_UNC_PRINTER_NAME],szConnData[MAX_UNC_PRINTER_NAME];
// Check for local machine
if (pServer && *pServer) {
if (!MyUNCName((LPWSTR)pServer)) {
SetLastError(ERROR_INVALID_NAME);
bReturn = FALSE;
goto CleanUp;
}
}
EnterRouterSem();
bEnteredRouterSem = TRUE;
hImpersonationToken = RevertToPrinterSelf();
*pcbNeeded = *pcReturned = 0;
// Open the key containing all per-machine connections.
if (dwLastError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szMachineConnections, 0,
KEY_READ , &hMcConnectionKey)) {
bReturn = (dwLastError == ERROR_FILE_NOT_FOUND) ? TRUE
: FALSE;
if (!bReturn) {
SetLastError(dwLastError);
}
goto CleanUp;
}
// pStart and pEnd point to the start and end of the buffer respt.
pStart = pPrinterEnum;
pEnd = pPrinterEnum + cbBuf;
for (dwRegIndex = 0;
dwNameSize = COUNTOF(szPrinterName),
((dwLastError = RegEnumKeyEx(hMcConnectionKey, dwRegIndex, szPrinterName,
&dwNameSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS);
++dwRegIndex) {
// Enumerate each of the connections and copy data into the buffer
cbdata = sizeof(szConnData);
if ((dwLastError = RegOpenKeyEx(hMcConnectionKey, szPrinterName, 0,
KEY_READ, &hPrinterKey)) ||
(dwLastError = RegQueryValueEx(hPrinterKey, L"Server", NULL, &dwType,
(LPBYTE)szConnData, &cbdata))) {
SetLastError(dwLastError);
bReturn = FALSE;
goto CleanUp;
}
RegCloseKey(hPrinterKey);
hPrinterKey=NULL;
// Update the size of the required buffer
*pcbNeeded = *pcbNeeded + sizeof(PRINTER_INFO_4) + sizeof(DWORD) +
(wcslen(szConnData) + 1)*sizeof(szConnData[0]) +
(wcslen(szPrinterName) + 1)*sizeof(szPrinterName[0]);
// Copy data into the buffer if there is space.
if (*pcbNeeded <= cbBuf) {
pEnd = CopyPrinterNameToPrinterInfo4(szConnData,szPrinterName,pStart,pEnd);
FormatRegistryKeyForPrinter(((PPRINTER_INFO_4) pStart)->pPrinterName,
((PPRINTER_INFO_4) pStart)->pPrinterName);
pStart += sizeof(PRINTER_INFO_4);
(*pcReturned)++;
}
}
if (dwLastError != ERROR_NO_MORE_ITEMS) {
SetLastError(dwLastError);
bReturn = FALSE;
goto CleanUp;
}
if (cbBuf < *pcbNeeded) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
bReturn = FALSE;
}
CleanUp:
if (hMcConnectionKey) {
RegCloseKey(hMcConnectionKey);
}
if (hPrinterKey) {
RegCloseKey(hPrinterKey);
}
if (hImpersonationToken) {
ImpersonatePrinterClient(hImpersonationToken);
}
if (bEnteredRouterSem) {
LeaveRouterSem();
}
if (!bReturn) {
*pcReturned = 0;
}
return bReturn;
}
BOOL
EnumPerMachineConnectionsW(
LPCWSTR pServer,
LPBYTE pPrinterEnum,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
LPPROVIDOR pProvidor;
if ((pPrinterEnum == NULL) && (cbBuf != 0)) {
SetLastError(ERROR_INVALID_USER_BUFFER);
return FALSE;
}
WaitForSpoolerInitialization();
if (RouterEnumPerMachineConnections(pServer, pPrinterEnum, cbBuf,
pcbNeeded, pcReturned)) {
return TRUE;
} else if (GetLastError() != ERROR_INVALID_NAME) {
return FALSE;
}
pProvidor = pLocalProvidor;
while (pProvidor) {
if ((*pProvidor->PrintProvidor.fpEnumPerMachineConnections)
(pServer, pPrinterEnum, cbBuf, pcbNeeded, pcReturned)) {
return TRUE;
}
if (GetLastError() != ERROR_INVALID_NAME) {
return FALSE;
}
pProvidor = pProvidor->pNext;
}
return FALSE;
}
PPRINTER_INFO_2
pGetPrinterInfo2(
HANDLE hPrinter
)
/*++
Routine Description:
Retrieve a printer info 2 structure from an hPrinter. Data must
be FreeSplMem'd by caller.
Arguments:
hPrinter - Printer to query.
Return Value:
PRINTER_INFO_2 - On success, a valid structure.
NULL - On failure.
--*/
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
DWORD cbPrinter = 0x1000;
DWORD cbNeeded;
PPRINTER_INFO_2 pPrinterInfo2;
BOOL bSuccess = FALSE;
if( pPrinterInfo2 = AllocSplMem( cbPrinter )){
bSuccess = (*pPrintHandle->pProvidor->PrintProvidor.fpGetPrinter)
( pPrintHandle->hPrinter,
2,
(PBYTE)pPrinterInfo2,
cbPrinter,
&cbNeeded );
if( !bSuccess ){
if( GetLastError() == ERROR_INSUFFICIENT_BUFFER ){
FreeSplMem( pPrinterInfo2 );
if (pPrinterInfo2 = (PPRINTER_INFO_2)AllocSplMem( cbNeeded )){
cbPrinter = cbNeeded;
bSuccess = (*pPrintHandle->pProvidor->PrintProvidor.fpGetPrinter)
( pPrintHandle->hPrinter,
2,
(PBYTE)pPrinterInfo2,
cbPrinter,
&cbNeeded );
}
}
}
}
if( !bSuccess ){
FreeSplMem( pPrinterInfo2 );
return NULL;
}
return pPrinterInfo2;
}
VOID
SplDriverUnloadComplete(
LPWSTR pDriverFile
)
/*++
Function Description: Notify the print provider that the driver is being unloaded
so that it may continue with any pending driver upgrades.
Parameters: pDriverFile -- name of the library that has been unloaded
Return Values: NONE
--*/
{
LPPROVIDOR pProvidor;
for (pProvidor = pLocalProvidor; pProvidor; pProvidor = pProvidor->pNext) {
if ((*pProvidor->PrintProvidor.fpDriverUnloadComplete)(pDriverFile)) {
break;
}
}
return;
}