Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1928 lines
49 KiB

/*++
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 <offsets.h>
WCHAR szProvidorValue[] = L"Provider";
WCHAR szRegistryConnections[] = L"Printers\\Connections";
WCHAR szServerValue[] = L"Server";
//
// Router Cache Table
//
DWORD RouterCacheSize;
PROUTERCACHE RouterCacheTable;
CRITICAL_SECTION RouterCriticalSection;
//
// Forward prototypes
//
BOOL
EnumerateConnectedPrinters(
LPBYTE pPrinter,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned,
HKEY hKeyUser);
LPWSTR
FormatPrinterForRegistryKey(
LPWSTR 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
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. */
BOOL
SavePrinterConnectionInRegistry(
PPRINTER_INFO_2 pPrinterInfo2,
LPPROVIDOR pProvidor
);
BOOL
RemovePrinterConnectionInRegistry(
LPWSTR pName);
LPBYTE
CopyPrinterNameToPrinterInfo4(
LPWSTR pServerName,
LPWSTR pPrinterName,
LPBYTE pPrinter,
LPBYTE pEnd);
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;
}
if (!Initialized) {
WaitForSingleObject(hEventInit, INFINITE);
}
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;
}
CloseHandle(hKeyUser);
} else {
Error = GetLastError();
}
pPrinterEnum += cTotalReturned * cbStruct;
if (TotalcbNeeded <= BufferSize)
BufferSize -= TotalcbNeeded;
else
BufferSize = 0;
}
pProvidor = pLocalProvidor;
while (pProvidor) {
cReturned = 0;
cbNeeded = 0;
if (!(*pProvidor->PrintProvidor.fpEnumPrinters) (Flags, Name, Level,
pPrinterEnum,
BufferSize,
&cbNeeded,
&cReturned)) {
Error = GetLastError();
} else {
bPartialSuccess = TRUE;
}
cTotalReturned += cReturned;
pPrinterEnum += cReturned * cbStruct;
if (cbNeeded <= BufferSize)
BufferSize -= cbNeeded;
else
BufferSize = 0;
TotalcbNeeded += 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;
if (Error) {
SetLastError(Error);
return FALSE;
} else
return TRUE;
}
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, cbData;
WCHAR PrinterName[MAX_PATH];
WCHAR ServerName[MAX_PATH];
DWORD cReturned, cbRequired, cbNeeded, cTotalReturned;
DWORD Error=0;
PWCHAR p;
LPBYTE pEnd;
DWORD cbSize;
BOOL bInsufficientBuffer = FALSE;
RegOpenKeyEx(hClientKey, szRegistryConnections, 0,
KEY_READ, &hKey1);
cPrinters=0;
cbData = sizeof(PrinterName);
cTotalReturned = 0;
cReturned = cbNeeded = 0;
cbRequired = 0;
pEnd = pPrinter + cbBuf;
while (RegEnumKeyEx(hKey1, cPrinters, PrinterName, &cbData,
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) {
cbSize = sizeof(ServerName);
Error = RegQueryValueEx(hKeyPrinter,
szServerValue,
NULL,
NULL,
(LPBYTE)ServerName,
&cbSize);
RegCloseKey(hKeyPrinter);
}
//
// On error condition, try and extract the server name
// based on the printer name. Pretty ugly...
//
if (Error) {
wcscpy(ServerName, PrinterName);
p = wcschr(ServerName+2, ',');
if (p)
*p = 0;
}
FormatRegistryKeyForPrinter(PrinterName, PrinterName);
//
// 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 the sucker 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++;
cbData = sizeof(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.
--*/
{
WCHAR szKey[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 )
return NULL;
//
// Prepare to read in
// HKEY_CURRENT_USER:\Printer\Connections\,,server,printer
//
wcscpy( szKey, szRegistryConnections );
//
// Find the end of this key so we can append the registry-formatted
// printer name to it.
//
pszKeyPrinter = &szKey[ COUNTOF( szRegistryConnections ) - 1 ];
*pszKeyPrinter++ = L'\\';
FormatPrinterForRegistryKey( pszPrinter, pszKeyPrinter );
hKeyClient = GetClientUserHandle( KEY_READ );
if( !hKeyClient ){
goto Fail;
}
Status = RegOpenKeyEx( hKeyClient,
szKey,
0,
KEY_READ,
&hKeyPrinter );
if( Status != ERROR_SUCCESS ){
goto Fail;
}
cbProvidor = sizeof( szProvidor );
Status = RegQueryValueEx( hKeyPrinter,
szProvidorValue,
NULL,
NULL,
(LPBYTE)szProvidor,
&cbProvidor );
if( Status != ERROR_SUCCESS ){
goto Fail;
}
//
// Scan through all providers, trying to match dll string.
//
for( pProvidor = pLocalProvidor; pProvidor; pProvidor = pProvidor->pNext ){
if( !_wcsicmp( pProvidor->lpName, szProvidor )){
break;
}
}
Fail:
if( hKeyClient ){
RegCloseKey( hKeyClient );
}
if( hKeyPrinter ){
RegCloseKey( hKeyPrinter );
}
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,
LPBYTE pSplClientInfo,
DWORD dwLevel
)
{
//
// We will set bLocalPrintProvidor = TRUE here
//
return(RouterOpenPrinterW(pPrinterName,
pHandle,
pDefault,
pSplClientInfo,
dwLevel,
TRUE));
}
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 == 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 pPrinterName,
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!)
--*/
{
DWORD dwFirstSignificantError = ERROR_INVALID_NAME;
LPPROVIDOR pProvidor;
LPPROVIDOR pProvidorAlreadyTried = NULL;
PPRINTHANDLE pPrintHandle;
HANDLE hPrinter;
DWORD OpenError;
BOOL bRemoveFromCache = FALSE;
//
// Max name we allow for printers is MAX_UNC_PRINTER_NAME.
// Providers can use suffixes only for OpenPrinter (not for Add/Set)
//
if ( pPrinterName &&
wcslen(pPrinterName) + 1 > MAX_UNC_PRINTER_NAME + PRINTER_NAME_SUFFIX_MAX ) {
SetLastError(ERROR_INVALID_PRINTER_NAME);
return FALSE;
}
if (!Initialized) {
WaitForSingleObject(hEventInit, INFINITE);
}
pPrintHandle = AllocSplMem(sizeof(PRINTHANDLE));
if (!pPrintHandle) {
DBGMSG(DBG_WARNING, ("RouterOpenPrinter - Failed to alloc print handle.\n"));
return FALSE;
}
//
// 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)
(pPrinterName, &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(pPrinterName);
LeaveRouterSem();
if (pProvidor) {
OpenError = (*pProvidor->PrintProvidor.fpOpenPrinterEx)
(pPrinterName,
&hPrinter,
pDefault,
pSplClientInfo,
dwLevel);
if ( OpenError == ERROR_NOT_SUPPORTED )
OpenError = (*pProvidor->PrintProvidor.fpOpenPrinter)
(pPrinterName,
&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( pPrinterName );
//
// 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(pPrinterName);
LeaveRouterSem();
}
if( pProvidor ){
//
// If we already tried this providor, don't try it again.
//
if( pProvidor != pProvidorAlreadyTried ){
OpenError = TryOpenPrinterAndCache( pProvidor,
pPrinterName,
&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;
}
OpenError = TryOpenPrinterAndCache( pProvidor,
pPrinterName,
&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.
//
FreeSplMem(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);
return FALSE;
Success:
pPrintHandle->signature = PRINTHANDLE_SIGNATURE;
pPrintHandle->pProvidor = pProvidor;
pPrintHandle->hPrinter = hPrinter;
*pHandle = (HANDLE)pPrintHandle;
return TRUE;
}
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;
if (!Initialized) {
WaitForSingleObject(hEventInit, INFINITE);
}
if ( pPrinter ) {
switch ( Level ) {
case 1:
pPrinterName = ((PPRINTER_INFO_1)pPrinter)->pName;
break;
case 2:
pPrinterName = ((PPRINTER_INFO_2)pPrinter)->pPrinterName;
break;
default:
pPrinterName = NULL;
}
if ( pPrinterName && wcslen(pPrinterName) + 1 > MAX_PRINTER_NAME ) {
SetLastError(ERROR_INVALID_PRINTER_NAME);
return FALSE;
}
}
pPrintHandle = AllocSplMem(sizeof(PRINTHANDLE));
if (!pPrintHandle) {
DBGMSG( DBG_WARNING, ("Failed tp alloc print handle."));
return FALSE;
}
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 ) {
pPrintHandle->signature = PRINTHANDLE_SIGNATURE;
pPrintHandle->pProvidor = pProvidor;
pPrintHandle->hPrinter = hPrinter;
return (HANDLE)pPrintHandle;
} else {
UpdateSignificantError(GetLastError(), &dwFirstSignificantError);
}
pProvidor = pProvidor->pNext;
}
FreeSplMem(pPrintHandle);
UpdateSignificantError(ERROR_INVALID_PRINTER_NAME, &dwFirstSignificantError);
SetLastError(dwFirstSignificantError);
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;
DWORD cbPrinter;
DWORD cbNeeded;
LPPRINTHANDLE pPrintHandle;
if (!Initialized) {
WaitForSingleObject(hEventInit, INFINITE);
}
//
// 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;
}
cbPrinter = 256;
if (pPrinterInfo2 = AllocSplMem(cbPrinter)) {
if (!(rc = GetPrinter(hPrinter, 2, (LPBYTE)pPrinterInfo2,
cbPrinter, &cbNeeded))) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if (pPrinterInfo2 = ReallocSplMem(pPrinterInfo2, 0, cbNeeded)) {
cbPrinter = cbNeeded;
rc = GetPrinter(hPrinter, 2, (LPBYTE)pPrinterInfo2,
cbPrinter, &cbNeeded);
}
}
}
}
pPrintHandle = (LPPRINTHANDLE)hPrinter;
if (rc) {
if ((*pPrintHandle->pProvidor->PrintProvidor.
fpAddPrinterConnection)(pPrinterInfo2->pPrinterName)) {
if (!SavePrinterConnectionInRegistry(
pPrinterInfo2,
pPrintHandle->pProvidor)) {
dwLastError = GetLastError();
(*pPrintHandle->pProvidor->PrintProvidor.
fpDeletePrinterConnection)(pPrinterInfo2->pPrinterName);
SetLastError(dwLastError);
rc = FALSE;
}
} else {
rc = FALSE;
}
}
if (pPrinterInfo2)
FreeSplMem(pPrinterInfo2);
ClosePrinter(hPrinter);
return rc;
}
BOOL
DeletePrinterConnectionW(
LPWSTR pName
)
{
LPPROVIDOR pProvidor;
LPPRINTER_INFO_2 pPrinterInfo2 = NULL;
DWORD cbPrinter, cbNeeded;
LPPRINTHANDLE pPrintHandle;
HANDLE hPrinter;
BOOL bRet = FALSE;
if (!Initialized) {
WaitForSingleObject(hEventInit, INFINITE);
}
//
// If OpenPrinter worked then we will try DeletePrinterConnection with
// the printer name after calling GetPrinter (user might have passed
// the sharename).
//
// else we will just go thru each print provider and try calling
// DeletePrinterConnection using the name user passed in
//
if ( OpenPrinter(pName, &hPrinter, NULL)) {
cbPrinter = 256;
pPrinterInfo2 = AllocSplMem(cbPrinter);
if ( !pPrinterInfo2 )
goto Cleanup;
if ( !GetPrinter(hPrinter,
2,
(LPBYTE)pPrinterInfo2,
cbPrinter,
&cbNeeded) ) {
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
goto Cleanup;
pPrinterInfo2 = (LPPRINTER_INFO_2) ReallocSplMem((LPBYTE)pPrinterInfo2,
0,
cbNeeded);
if ( !pPrinterInfo2 )
goto Cleanup;
cbPrinter = cbNeeded;
if ( !GetPrinter(hPrinter,
2,
(LPBYTE)pPrinterInfo2,
cbPrinter,
&cbNeeded) )
goto Cleanup;
}
pPrintHandle = (LPPRINTHANDLE)hPrinter;
bRet = (*pPrintHandle->pProvidor->PrintProvidor.fpDeletePrinterConnection)(pPrinterInfo2->pPrinterName);
if ( bRet)
RemovePrinterConnectionInRegistry(pPrinterInfo2->pPrinterName);
} else {
hPrinter = NULL;
pProvidor = pLocalProvidor;
while (pProvidor) {
bRet = (*pProvidor->PrintProvidor.fpDeletePrinterConnection)(pName);
if ( bRet ) {
RemovePrinterConnectionInRegistry(pName);
goto Cleanup;
}
if ( GetLastError() != ERROR_INVALID_NAME )
goto Cleanup;
pProvidor = pProvidor->pNext;
}
//
// If all providors failed with ERROR_INVALID_NAME then try to delete
// from registry
//
bRet = RemovePrinterConnectionInRegistry(pName);
if ( !bRet )
SetLastError(ERROR_INVALID_PRINTER_NAME);
}
Cleanup:
if ( hPrinter )
ClosePrinter(hPrinter);
if ( pPrinterInfo2 )
FreeSplMem(pPrinterInfo2);
return bRet;
}
BOOL
SetPrinterW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pPrinter,
DWORD Command
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
LPWSTR pPrinterName = NULL;
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;
}
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpSetPrinter)
(pPrintHandle->hPrinter, Level, pPrinter, Command);
}
BOOL
GetPrinterW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pPrinter,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if ((pPrinter == NULL) && (cbBuf != 0)) {
SetLastError(ERROR_INVALID_USER_BUFFER);
return FALSE;
}
return (*pPrintHandle->pProvidor->PrintProvidor.fpGetPrinter)
(pPrintHandle->hPrinter, Level, pPrinter,
cbBuf, pcbNeeded);
}
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
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
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
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
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(
LPWSTR 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. */
{
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 (pSource = pScratch; *pSource; pSource++) {
if (*pSource == L'\\')
*pSource = 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;
LPWSTR pKeyName = NULL;
DWORD Status;
BOOL rc = FALSE;
WCHAR szPrinterReg[MAX_PATH];
DWORD dwError;
hClientKey = GetClientUserHandle(KEY_READ);
if (hClientKey) {
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:
*/
pKeyName = FormatPrinterForRegistryKey(pPrinterInfo2->pPrinterName,
szPrinterReg);
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)pProvidor->lpName,
(lstrlen(pProvidor->lpName)+1) *
sizeof(pProvidor->lpName[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",
pProvidor->lpName, 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);
}
RegCloseKey(hClientKey);
}
return rc;
}
BOOL
RemovePrinterConnectionInRegistry(
LPWSTR pName)
{
HKEY hClientKey;
HKEY hPrinterConnectionsKey;
DWORD Status = NO_ERROR;
DWORD i = 0;
WCHAR szBuffer[MAX_PATH];
BOOL Found = FALSE;
LPWSTR pKeyName;
BOOL bRet = FALSE;
hClientKey = GetClientUserHandle(KEY_READ);
if (hClientKey) {
Status = RegOpenKeyEx(hClientKey, szRegistryConnections,
REG_OPTION_RESERVED,
KEY_READ | KEY_WRITE, &hPrinterConnectionsKey);
if (Status == NO_ERROR) {
pKeyName = FormatPrinterForRegistryKey(pName, szBuffer);
bRet = DeleteSubKeyTree(hPrinterConnectionsKey, pKeyName);
RegCloseKey(hPrinterConnectionsKey);
}
if ( bRet ) {
UpdatePrinterRegUser(hClientKey,
NULL,
pName,
NULL,
UPDATE_REG_DELETE);
}
if (hClientKey) {
RegCloseKey(hClientKey);
}
if ( bRet ) {
BroadcastMessage(BROADCAST_TYPE_MESSAGE,
WM_WININICHANGE,
0,
(LPARAM)szDevices);
}
}
return bRet;
}
VOID
PrinterHandleRundown(
HANDLE hPrinter)
{
LPPRINTHANDLE pPrintHandle;
if (hPrinter) {
pPrintHandle = (LPPRINTHANDLE)hPrinter;
switch (pPrintHandle->signature) {
case PRINTHANDLE_SIGNATURE:
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;
default:
//
// Unknown type.
//
DBGMSG( DBG_ERROR, ("Rundown: Unknown type 0x%x\n", hPrinter ) );
break;
}
}
return;
}