912 lines
19 KiB
C
912 lines
19 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1997 Microsoft Corporation
|
||
|
All rights reserved
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
handle.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Contains all functions related to the maintanence of print handles.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
User Mode -Win32
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "client.h"
|
||
|
|
||
|
DWORD
|
||
|
OpenPrinterRPC(
|
||
|
PSPOOL pSpool
|
||
|
);
|
||
|
|
||
|
BOOL
|
||
|
ClosePrinterRPC(
|
||
|
IN PSPOOL pSpool,
|
||
|
IN BOOL bRevalidate
|
||
|
);
|
||
|
|
||
|
BOOL
|
||
|
ClosePrinterContextHandle(
|
||
|
HANDLE hPrinter
|
||
|
);
|
||
|
|
||
|
BOOL
|
||
|
ClosePrinterWorker(
|
||
|
PSPOOL pSpool
|
||
|
);
|
||
|
|
||
|
DWORD gcClientHandle = 0;
|
||
|
|
||
|
#ifdef DBG_TRACE_HANDLE
|
||
|
PSPOOL gpFirstSpool = NULL;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
EProtectResult
|
||
|
eProtectHandle(
|
||
|
IN HANDLE hPrinter,
|
||
|
IN BOOL bClose
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Protect a print handle so that it will not be deleted while it is
|
||
|
being used. If this is called by the Close routine, then this call
|
||
|
returns whether the Close should continue or be aborted.
|
||
|
|
||
|
Note: This only provides close protection--it does not guard against
|
||
|
simultaneous access by non-close operations.
|
||
|
|
||
|
There must always be a matching vUnprotect call when the callee is
|
||
|
done with the handle.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hPrinter - pSpool to protect.
|
||
|
|
||
|
bClose - If TRUE, indicates that the callee wants to close the handle.
|
||
|
(Generally called by ClosePrinter only.) The return value will
|
||
|
indicate whether the calleeis allowed to close the printer.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
kProtectHandleSuccess - Call succeeded; printer handle can be used normally.
|
||
|
|
||
|
kProtectHandleInvalid - Handle is invalid; call failed.
|
||
|
|
||
|
kProtectHandlePendingDeletion - This only occurs when bClose is TRUE. The
|
||
|
Operation on handle is in process, and the close will happen when the
|
||
|
other thread has completed.
|
||
|
|
||
|
LastError only set if handle kProtectHandleInvalid is returned.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
EProtectResult eResult = kProtectHandleInvalid;
|
||
|
PSPOOL pSpool = (PSPOOL)hPrinter;
|
||
|
|
||
|
vEnterSem();
|
||
|
|
||
|
try {
|
||
|
if( pSpool &&
|
||
|
(pSpool->signature == SP_SIGNATURE ) &&
|
||
|
!( pSpool->Status & ( SPOOL_STATUS_CLOSE |
|
||
|
SPOOL_STATUS_PENDING_DELETION ))){
|
||
|
|
||
|
//
|
||
|
// Valid handle.
|
||
|
//
|
||
|
eResult = kProtectHandleSuccess;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
DBGMSG( DBG_WARN,
|
||
|
( "Bad hPrinter %x %x\n",
|
||
|
pSpool,
|
||
|
pSpool ? pSpool->signature : 0 ));
|
||
|
}
|
||
|
|
||
|
} except( EXCEPTION_EXECUTE_HANDLER ){
|
||
|
|
||
|
DBGMSG( DBG_WARN, ( "Unmapped pSpool %x\n", pSpool ));
|
||
|
}
|
||
|
|
||
|
if( eResult == kProtectHandleSuccess ){
|
||
|
|
||
|
//
|
||
|
// If bClose, then see if an operation is currently executing.
|
||
|
//
|
||
|
if( bClose ){
|
||
|
|
||
|
if(( pSpool->Status & SPOOL_STATUS_PENDING_DELETION ) ||
|
||
|
pSpool->cActive ){
|
||
|
|
||
|
//
|
||
|
// Mark pSpool to close itself once the operation has
|
||
|
// completed in the other thread.
|
||
|
//
|
||
|
pSpool->Status |= SPOOL_STATUS_PENDING_DELETION;
|
||
|
eResult = kProtectHandlePendingDeletion;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// No call is active, so mark ourselves as closing so
|
||
|
// that no other call will succeed using this handle.
|
||
|
//
|
||
|
pSpool->Status |= SPOOL_STATUS_CLOSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Not a valid handle.
|
||
|
//
|
||
|
SetLastError( ERROR_INVALID_HANDLE );
|
||
|
}
|
||
|
|
||
|
if( eResult == kProtectHandleSuccess ){
|
||
|
|
||
|
//
|
||
|
// Returning success, we are now active.
|
||
|
//
|
||
|
++pSpool->cActive;
|
||
|
}
|
||
|
|
||
|
vLeaveSem();
|
||
|
|
||
|
return eResult;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
vUnprotectHandle(
|
||
|
IN HANDLE hPrinter
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Unprotect a print handle. This must be called once for each
|
||
|
successful bProtectHandle.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hPrinter - Handle to unprotect.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PSPOOL pSpool = (PSPOOL)hPrinter;
|
||
|
BOOL bCallClosePrinter = FALSE;
|
||
|
|
||
|
vEnterSem();
|
||
|
|
||
|
//
|
||
|
// No longer active. However, it it's closing, leave it marked
|
||
|
// as closing since we don't want anyone else to use it.
|
||
|
//
|
||
|
--pSpool->cActive;
|
||
|
|
||
|
if( pSpool->Status & SPOOL_STATUS_PENDING_DELETION &&
|
||
|
!pSpool->cActive ){
|
||
|
|
||
|
//
|
||
|
// Someone called Close while we were active. Since we are now
|
||
|
// going to close it, don't let anyone else initiate a close by
|
||
|
// marking SPOOL_STATUS_CLOSE.
|
||
|
//
|
||
|
pSpool->Status |= SPOOL_STATUS_CLOSE;
|
||
|
pSpool->Status &= ~SPOOL_STATUS_PENDING_DELETION;
|
||
|
bCallClosePrinter = TRUE;
|
||
|
}
|
||
|
|
||
|
vLeaveSem();
|
||
|
|
||
|
if( bCallClosePrinter ){
|
||
|
ClosePrinterWorker( pSpool );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/********************************************************************
|
||
|
|
||
|
OpenPrinter worker functions.
|
||
|
|
||
|
********************************************************************/
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
OpenPrinterW(
|
||
|
LPWSTR pPrinterName,
|
||
|
LPHANDLE phPrinter,
|
||
|
LPPRINTER_DEFAULTS pDefault
|
||
|
)
|
||
|
{
|
||
|
HANDLE hPrinter;
|
||
|
PSPOOL pSpool = NULL;
|
||
|
DWORD dwError;
|
||
|
|
||
|
//
|
||
|
// Pre-initialize the out parameter, so that *phPrinter is NULL
|
||
|
// on failure. This fixes Borland Paradox 7.
|
||
|
//
|
||
|
try {
|
||
|
*phPrinter = NULL;
|
||
|
} except( EXCEPTION_EXECUTE_HANDLER ){
|
||
|
SetLastError(TranslateExceptionCode(GetExceptionCode()));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pSpool = AllocSpool();
|
||
|
|
||
|
if( !pSpool ){
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Copy DevMode, defaults. The printer name doesn't change.
|
||
|
//
|
||
|
if( !UpdatePrinterDefaults( pSpool, pPrinterName, pDefault )){
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the access, since this is not set by UpdatePrinterDefaults.
|
||
|
//
|
||
|
if( pDefault ){
|
||
|
pSpool->Default.DesiredAccess = pDefault->DesiredAccess;
|
||
|
}
|
||
|
|
||
|
dwError = OpenPrinterRPC( pSpool );
|
||
|
|
||
|
if( dwError != ERROR_SUCCESS ){
|
||
|
SetLastError( dwError );
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We finally have a good pSpool. Only now update the output
|
||
|
// handle. Since it was NULL initialized, this guarantees that
|
||
|
// OpenPrinter returns *phPrinter NULL when it fails.
|
||
|
//
|
||
|
*phPrinter = pSpool;
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
Fail:
|
||
|
|
||
|
FreeSpool( pSpool );
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
OpenPrinterRPC(
|
||
|
PSPOOL pSpool
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Open the printer handle using information in the pSpool object.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pSpool - Printer handle to open. Internal state of pSpool updated.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
ERROR_SUCCES - Succeed.
|
||
|
Status code - Failed.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
DEVMODE_CONTAINER DevModeContainer;
|
||
|
HANDLE hPrinter = NULL;
|
||
|
DWORD dwReturn;
|
||
|
DWORD dwSize;
|
||
|
SPLCLIENT_CONTAINER SplClientContainer;
|
||
|
|
||
|
DevModeContainer.cbBuf = 0;
|
||
|
DevModeContainer.pDevMode = NULL;
|
||
|
|
||
|
SplClientContainer.Level = 2;
|
||
|
SplClientContainer.ClientInfo.pClientInfo2 = NULL;
|
||
|
|
||
|
RpcTryExcept {
|
||
|
|
||
|
//
|
||
|
// Construct the DevMode container.
|
||
|
//
|
||
|
if( bValidDevModeW( pSpool->Default.pDevMode )){
|
||
|
|
||
|
dwSize = pSpool->Default.pDevMode->dmSize +
|
||
|
pSpool->Default.pDevMode->dmDriverExtra;
|
||
|
|
||
|
DevModeContainer.cbBuf = pSpool->Default.pDevMode->dmSize +
|
||
|
pSpool->Default.pDevMode->dmDriverExtra;
|
||
|
DevModeContainer.pDevMode = (LPBYTE)pSpool->Default.pDevMode;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the call is made from within the spooler, we also retrieve the
|
||
|
// server side hPrinter. This will help avoid unnecessary RPC. We cant,
|
||
|
// however, avoid RPC in this case since the spooler may need a client side
|
||
|
// handle to pass to other functions or the driver.
|
||
|
//
|
||
|
|
||
|
if (bLoadedBySpooler) {
|
||
|
|
||
|
if (SplClientContainer.ClientInfo.pClientInfo2 =
|
||
|
(LPSPLCLIENT_INFO_2) AllocSplMem(sizeof(SPLCLIENT_INFO_2))) {
|
||
|
|
||
|
SplClientContainer.ClientInfo.pClientInfo2->hSplPrinter = 0;
|
||
|
|
||
|
dwReturn = RpcSplOpenPrinter( (LPTSTR)pSpool->pszPrinter,
|
||
|
&hPrinter,
|
||
|
pSpool->Default.pDatatype,
|
||
|
&DevModeContainer,
|
||
|
pSpool->Default.DesiredAccess,
|
||
|
&SplClientContainer );
|
||
|
} else {
|
||
|
|
||
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
|
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
dwReturn = RpcOpenPrinter( (LPTSTR)pSpool->pszPrinter,
|
||
|
&hPrinter,
|
||
|
pSpool->Default.pDatatype,
|
||
|
&DevModeContainer,
|
||
|
pSpool->Default.DesiredAccess );
|
||
|
}
|
||
|
|
||
|
|
||
|
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
|
||
|
|
||
|
dwReturn = TranslateExceptionCode( RpcExceptionCode() );
|
||
|
|
||
|
} RpcEndExcept
|
||
|
|
||
|
|
||
|
if( dwReturn == ERROR_SUCCESS ){
|
||
|
|
||
|
vEnterSem();
|
||
|
|
||
|
//
|
||
|
// hPrinter gets adopted by pSpool->hPrinter.
|
||
|
//
|
||
|
pSpool->hPrinter = hPrinter;
|
||
|
|
||
|
if (bLoadedBySpooler) {
|
||
|
pSpool->hSplPrinter = (HANDLE) SplClientContainer.ClientInfo.pClientInfo2->hSplPrinter;
|
||
|
} else {
|
||
|
pSpool->hSplPrinter = NULL;
|
||
|
}
|
||
|
|
||
|
vLeaveSem();
|
||
|
}
|
||
|
|
||
|
if (SplClientContainer.ClientInfo.pClientInfo2) {
|
||
|
FreeSplMem(SplClientContainer.ClientInfo.pClientInfo2);
|
||
|
}
|
||
|
|
||
|
return dwReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
/********************************************************************
|
||
|
|
||
|
ClosePrinter worker functions.
|
||
|
|
||
|
********************************************************************/
|
||
|
|
||
|
BOOL
|
||
|
ClosePrinter(
|
||
|
HANDLE hPrinter
|
||
|
)
|
||
|
{
|
||
|
PSPOOL pSpool = (PSPOOL)hPrinter;
|
||
|
|
||
|
switch( eProtectHandle( hPrinter, TRUE )){
|
||
|
case kProtectHandleInvalid:
|
||
|
return FALSE;
|
||
|
case kProtectHandlePendingDeletion:
|
||
|
return TRUE;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Note, there isn't a corresponding vUnprotectHandle, but that's ok
|
||
|
// since we're deleting the handle.
|
||
|
//
|
||
|
|
||
|
return ClosePrinterWorker( pSpool );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// A simpler way to have a central function for closing spool file handles so we
|
||
|
// don't have to reproduce code constantly.
|
||
|
//
|
||
|
VOID
|
||
|
CloseSpoolFileHandles(
|
||
|
PSPOOL pSpool
|
||
|
)
|
||
|
{
|
||
|
if ( pSpool->hSpoolFile != INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
CloseHandle( pSpool->hSpoolFile );
|
||
|
pSpool->hSpoolFile = INVALID_HANDLE_VALUE;
|
||
|
}
|
||
|
|
||
|
if (pSpool->hFile != INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
CloseHandle(pSpool->hFile);
|
||
|
pSpool->hFile = INVALID_HANDLE_VALUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
ClosePrinterWorker(
|
||
|
PSPOOL pSpool
|
||
|
)
|
||
|
{
|
||
|
BOOL bReturnValue;
|
||
|
FlushBuffer(pSpool, NULL);
|
||
|
|
||
|
if (pSpool->Status & SPOOL_STATUS_ADDJOB)
|
||
|
ScheduleJobWorker( pSpool, pSpool->JobId );
|
||
|
|
||
|
vEnterSem();
|
||
|
|
||
|
if( pSpool->pNotify ){
|
||
|
|
||
|
//
|
||
|
// There is a notification; disassociate it from
|
||
|
// pSpool, since we are about to free it.
|
||
|
//
|
||
|
pSpool->pNotify->pSpool = NULL;
|
||
|
}
|
||
|
|
||
|
vLeaveSem();
|
||
|
|
||
|
//
|
||
|
// Close any open file handles, we do this before the RPC closeprinter
|
||
|
// to allow the closeprinter on the other side a chance to delete the spool
|
||
|
// files if they still exist.
|
||
|
//
|
||
|
CloseSpoolFileHandles( pSpool );
|
||
|
|
||
|
bReturnValue = ClosePrinterRPC( pSpool, FALSE );
|
||
|
FreeSpool( pSpool );
|
||
|
|
||
|
return bReturnValue;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
ClosePrinterRPC(
|
||
|
IN PSPOOL pSpool,
|
||
|
IN BOOL bRevalidate
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Close down all RPC/network handles related to the pSpool object.
|
||
|
Must be called outside the critical section. This function also
|
||
|
is called handle revalidation in which case we don't want to
|
||
|
close the event handle on the client side.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pSpool - Spooler handle to shut down.
|
||
|
bRevalidate - If TRUE, this is being called as a result of a handle
|
||
|
revalidation.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE - Success
|
||
|
FALSE - Failed, LastError set.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
BOOL bRetval = FALSE;
|
||
|
HANDLE hPrinterRPC = NULL;
|
||
|
|
||
|
vEnterSem();
|
||
|
|
||
|
hPrinterRPC = pSpool->hPrinter;
|
||
|
|
||
|
if ( hPrinterRPC )
|
||
|
{
|
||
|
pSpool->hPrinter = NULL;
|
||
|
|
||
|
FindClosePrinterChangeNotificationWorker( pSpool->pNotify,
|
||
|
hPrinterRPC,
|
||
|
bRevalidate );
|
||
|
|
||
|
vLeaveSem();
|
||
|
|
||
|
bRetval = ClosePrinterContextHandle( hPrinterRPC );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vLeaveSem();
|
||
|
|
||
|
SetLastError( ERROR_INVALID_HANDLE );
|
||
|
}
|
||
|
|
||
|
return bRetval;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
ClosePrinterContextHandle(
|
||
|
HANDLE hPrinterRPC
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Close a printer context handle.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hPrinterRPC - RPC context handle to close.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE - Success
|
||
|
FALSE - Failure; LastError set
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
BOOL bReturnValue;
|
||
|
DWORD Status;
|
||
|
|
||
|
if( !hPrinterRPC ){
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
RpcTryExcept {
|
||
|
|
||
|
if( Status = RpcClosePrinter( &hPrinterRPC )) {
|
||
|
|
||
|
SetLastError( Status );
|
||
|
|
||
|
bReturnValue = FALSE;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
bReturnValue = TRUE;
|
||
|
}
|
||
|
|
||
|
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
|
||
|
|
||
|
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
|
||
|
bReturnValue = FALSE;
|
||
|
|
||
|
} RpcEndExcept
|
||
|
|
||
|
//
|
||
|
// If we failed for some reason, then RpcClosePrinter did not
|
||
|
// zero out the context handle. Destroy it here.
|
||
|
//
|
||
|
if( hPrinterRPC ){
|
||
|
RpcSmDestroyClientContext( &hPrinterRPC );
|
||
|
}
|
||
|
|
||
|
return bReturnValue;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/********************************************************************
|
||
|
|
||
|
Constructor and destructor of pSpool.
|
||
|
|
||
|
********************************************************************/
|
||
|
|
||
|
PSPOOL
|
||
|
AllocSpool(
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Allocate a spool handle. Client should set pSpool->hPrinter
|
||
|
when it is acquired.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
pSpool - allocated handle.
|
||
|
NULL - failed.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PSPOOL pSpool = AllocSplMem(sizeof(SPOOL));
|
||
|
|
||
|
if( pSpool ){
|
||
|
|
||
|
InterlockedIncrement( &gcClientHandle );
|
||
|
|
||
|
pSpool->signature = SP_SIGNATURE;
|
||
|
pSpool->hFile = INVALID_HANDLE_VALUE;
|
||
|
pSpool->hSpoolFile = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
#ifdef DBG_TRACE_HANDLE
|
||
|
{
|
||
|
ULONG Hash;
|
||
|
|
||
|
//
|
||
|
// Add to linked list.
|
||
|
//
|
||
|
vEnterSem();
|
||
|
pSpool->pNext = gpFirstSpool;
|
||
|
gpFirstSpool = pSpool;
|
||
|
vLeaveSem();
|
||
|
|
||
|
#if i386
|
||
|
//
|
||
|
// Capture backtrace.
|
||
|
//
|
||
|
RtlCaptureStackBackTrace( 1,
|
||
|
COUNTOF( pSpool->apvBackTrace ),
|
||
|
pSpool->apvBackTrace,
|
||
|
&Hash );
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
return pSpool;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
FreeSpool(
|
||
|
PSPOOL pSpool
|
||
|
)
|
||
|
{
|
||
|
if( !pSpool ){
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
InterlockedDecrement( &gcClientHandle );
|
||
|
|
||
|
if (pSpool->pBuffer != NULL ) {
|
||
|
if (!VirtualFree(pSpool->pBuffer, 0, MEM_RELEASE)) {
|
||
|
DBGMSG(DBG_WARNING, ("ClosePrinter VirtualFree Failed %x\n",
|
||
|
GetLastError()));
|
||
|
}
|
||
|
DBGMSG(DBG_TRACE, ("Closeprinter cWritePrinters %d cFlushBuffers %d\n",
|
||
|
pSpool->cWritePrinters, pSpool->cFlushBuffers));
|
||
|
}
|
||
|
|
||
|
FreeSplStr( pSpool->pszPrinter );
|
||
|
FreeSplMem( pSpool->Default.pDevMode );
|
||
|
FreeSplMem( pSpool->Default.pDatatype );
|
||
|
FreeSplMem( pSpool->pDoceventFilter);
|
||
|
|
||
|
CloseSpoolFileHandles( pSpool );
|
||
|
|
||
|
#ifdef DBG_TRACE_HANDLE
|
||
|
{
|
||
|
//
|
||
|
// Free from linked list.
|
||
|
//
|
||
|
PSPOOL *ppSpool;
|
||
|
|
||
|
vEnterSem();
|
||
|
|
||
|
for( ppSpool = &gpFirstSpool; *ppSpool; ppSpool = &(*ppSpool)->pNext ){
|
||
|
|
||
|
if( *ppSpool == pSpool ){
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( *ppSpool ){
|
||
|
*ppSpool = pSpool->pNext;
|
||
|
} else {
|
||
|
DBGMSG( DBG_WARN,
|
||
|
( "pSpool %x not found on linked list\n", pSpool ));
|
||
|
}
|
||
|
vLeaveSem();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
FreeSplMem( pSpool );
|
||
|
}
|
||
|
|
||
|
|
||
|
/********************************************************************
|
||
|
|
||
|
Utility functions.
|
||
|
|
||
|
********************************************************************/
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
RevalidateHandle(
|
||
|
PSPOOL pSpool
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Revalidates a pSpool with a new RPC handle. This allows the spooler
|
||
|
to be restarted yet allow the handle to remain valid.
|
||
|
|
||
|
This should only be called when a call fails with ERROR_INVALID_HANDLE.
|
||
|
We can only save simple state information (pDefaults) from OpenPrinter
|
||
|
and ResetPrinter. If a user spooling and the context handle is lost,
|
||
|
there is no hope of recovering the spool file state, since the
|
||
|
spooler probably died before it could flush its buffers.
|
||
|
|
||
|
We should not encounter any infinite loops when the server goes down,
|
||
|
since the initial call will timeout with an RPC rather than invalid
|
||
|
handle code.
|
||
|
|
||
|
Note: If the printer is renamed, the context handle remains valid,
|
||
|
but revalidation will fail, since we store the old printer name.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pSpool - Printer handle to revalidate.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE - Success
|
||
|
FALSE - Failed.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
DWORD dwError;
|
||
|
HANDLE hPrinter;
|
||
|
|
||
|
//
|
||
|
// Close the existing handle. We can't shouldn't just destroy the client
|
||
|
// context since an api may return ERROR_INVALID_HANDLE even though
|
||
|
// RPC context handle is fine (a handle downstream went bad).
|
||
|
//
|
||
|
ClosePrinterRPC( pSpool, TRUE );
|
||
|
|
||
|
//
|
||
|
// Reopen the printer handle with current defaults.
|
||
|
//
|
||
|
dwError = OpenPrinterRPC( pSpool );
|
||
|
|
||
|
if( dwError ){
|
||
|
SetLastError( dwError );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
UpdatePrinterDefaults(
|
||
|
IN OUT PSPOOL pSpool,
|
||
|
IN LPCTSTR pszPrinter, OPTIONAL
|
||
|
IN PPRINTER_DEFAULTS pDefault OPTIONAL
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Update the pSpool to the new defaults in pDefault, EXCEPT for
|
||
|
pDefault->DesiredAccess.
|
||
|
|
||
|
Since this attempts to read and update pSpool, we enter the
|
||
|
critical section and revalidate the pSpool.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pSpool - Spooler handle to update.
|
||
|
|
||
|
pszPrinter - New printer name.
|
||
|
|
||
|
pDefault - New defaults.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE - Success
|
||
|
FALSE - Failure.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
BOOL bReturnValue = FALSE;
|
||
|
|
||
|
vEnterSem();
|
||
|
|
||
|
if( !UpdateString( pszPrinter, &pSpool->pszPrinter )){
|
||
|
goto DoneExitSem;
|
||
|
}
|
||
|
|
||
|
if( pDefault ){
|
||
|
|
||
|
//
|
||
|
// Update the Datatype.
|
||
|
//
|
||
|
if( !UpdateString( pDefault->pDatatype, &pSpool->Default.pDatatype )){
|
||
|
goto DoneExitSem;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the DevMode.
|
||
|
//
|
||
|
if( bValidDevModeW( pDefault->pDevMode )){
|
||
|
|
||
|
DWORD dwSize;
|
||
|
PDEVMODE pDevModeNew;
|
||
|
|
||
|
dwSize = pDefault->pDevMode->dmSize +
|
||
|
pDefault->pDevMode->dmDriverExtra;
|
||
|
|
||
|
pDevModeNew = AllocSplMem( dwSize );
|
||
|
|
||
|
if( !pDevModeNew ){
|
||
|
goto DoneExitSem;
|
||
|
}
|
||
|
|
||
|
CopyMemory( pDevModeNew, pDefault->pDevMode, dwSize );
|
||
|
|
||
|
FreeSplMem( pSpool->Default.pDevMode );
|
||
|
pSpool->Default.pDevMode = pDevModeNew;
|
||
|
}
|
||
|
}
|
||
|
bReturnValue = TRUE;
|
||
|
|
||
|
DoneExitSem:
|
||
|
|
||
|
vLeaveSem();
|
||
|
return bReturnValue;
|
||
|
}
|
||
|
|
||
|
|
||
|
|