3293 lines
88 KiB
C
3293 lines
88 KiB
C
/*++
|
|
|
|
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;
|
|
}
|
|
|