1859 lines
38 KiB
C++
1859 lines
38 KiB
C++
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1994 - 1998
|
|
All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
printer.cxx
|
|
|
|
Abstract:
|
|
|
|
Handles object updates and notifications from the printing system.
|
|
|
|
Author:
|
|
|
|
Albert Ting (AlbertT) 7-Nov-1994
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#pragma hdrstop
|
|
|
|
#include "notify.hxx"
|
|
#include "data.hxx"
|
|
#include "printer.hxx"
|
|
|
|
#if DBG
|
|
//#define DBG_PRNINFO DBG_INFO
|
|
#define DBG_PRNINFO DBG_NONE
|
|
#endif
|
|
|
|
/********************************************************************
|
|
|
|
TPrinter functions.
|
|
|
|
********************************************************************/
|
|
|
|
|
|
STATUS
|
|
TPrinter::
|
|
sOpenPrinter(
|
|
IN LPCTSTR pszPrinter,
|
|
IN OUT PDWORD pdwAccess,
|
|
OUT PHANDLE phPrinter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens printer for specified access.
|
|
|
|
Arguments:
|
|
|
|
pszPrinter - Name of printer to open. szNULL or NULL implies local server.
|
|
|
|
pdwAccess - On entry, holds desired access (pointer to 0 indicates
|
|
maximal access). On successful exit, holds access granted.
|
|
If the call fails, this value is undefined.
|
|
|
|
phPrinter - Returns the open printer handle. On failure, this value
|
|
is set to NULL.
|
|
|
|
Return Value:
|
|
|
|
STATUS - win32 error code or ERROR_SUCCESS if successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
STATUS Status = ERROR_SUCCESS;
|
|
|
|
TStatusB bOpen( DBG_WARN,
|
|
ERROR_ACCESS_DENIED,
|
|
RPC_S_SERVER_UNAVAILABLE,
|
|
ERROR_INVALID_PRINTER_NAME );
|
|
bOpen DBGNOCHK = FALSE;
|
|
|
|
static const DWORD adwAccessPrinter[] = {
|
|
PRINTER_ALL_ACCESS,
|
|
PRINTER_READ,
|
|
READ_CONTROL,
|
|
0,
|
|
};
|
|
|
|
static const DWORD adwAccessServer[] = {
|
|
SERVER_ALL_ACCESS,
|
|
SERVER_READ,
|
|
0,
|
|
};
|
|
|
|
PRINTER_DEFAULTS Defaults;
|
|
Defaults.pDatatype = NULL;
|
|
Defaults.pDevMode = NULL;
|
|
|
|
if( pszPrinter && !pszPrinter[0] ){
|
|
|
|
//
|
|
// szNull indicates local server also; change it to
|
|
// NULL since OpenPrinter only likes NULL.
|
|
//
|
|
pszPrinter = NULL;
|
|
}
|
|
|
|
//
|
|
// Now determine whether we are opening a server or a printer.
|
|
// This is very messy. Look for NULL or two beginning
|
|
// backslashes and none thereafter to indicate a server.
|
|
//
|
|
PDWORD pdwAccessTypes;
|
|
|
|
if( !pszPrinter ||
|
|
( pszPrinter[0] == TEXT( '\\' ) &&
|
|
pszPrinter[1] == TEXT( '\\' ) &&
|
|
!_tcschr( &pszPrinter[2], TEXT( '\\' )))){
|
|
|
|
pdwAccessTypes = (PDWORD)adwAccessServer;
|
|
} else {
|
|
pdwAccessTypes = (PDWORD)adwAccessPrinter;
|
|
}
|
|
|
|
if( *pdwAccess ){
|
|
|
|
Defaults.DesiredAccess = *pdwAccess;
|
|
|
|
bOpen DBGCHK = OpenPrinter( (LPTSTR)pszPrinter,
|
|
phPrinter,
|
|
&Defaults );
|
|
|
|
if( !bOpen ){
|
|
Status = GetLastError();
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// If no access is specified, then attempt to retrieve the
|
|
// maximal access.
|
|
//
|
|
UINT i;
|
|
|
|
for( i = 0; !bOpen && pdwAccessTypes[i]; ++i ){
|
|
|
|
Defaults.DesiredAccess = pdwAccessTypes[i];
|
|
|
|
bOpen DBGCHK = OpenPrinter( (LPTSTR)pszPrinter,
|
|
phPrinter,
|
|
&Defaults );
|
|
|
|
if( bOpen ){
|
|
|
|
//
|
|
// Return the access requested by the successful OpenPrinter.
|
|
// On failure, this value is 0 (*pdwAccess undefined).
|
|
//
|
|
*pdwAccess = pdwAccessTypes[i];
|
|
break;
|
|
}
|
|
|
|
Status = GetLastError();
|
|
|
|
if( ERROR_ACCESS_DENIED != Status )
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !bOpen ){
|
|
SPLASSERT( Status );
|
|
*phPrinter = NULL;
|
|
return Status;
|
|
}
|
|
|
|
SPLASSERT( *phPrinter );
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL
|
|
TPrinter::
|
|
bSyncRefresh(
|
|
VOID
|
|
)
|
|
{
|
|
STATEVAR StateVar = TPrinter::kExecRefreshAll;
|
|
BOOL bTriedOpen = FALSE;
|
|
|
|
|
|
while( StateVar ){
|
|
|
|
DBGMSG( DBG_TRACE, ( "Printer.bSyncRefresh now >> %x %x\n", this, StateVar ));
|
|
|
|
if( StateVar & ( kExecDelay | kExecError )){
|
|
|
|
if( bTriedOpen ){
|
|
|
|
svClose( kExecClose );
|
|
return FALSE;
|
|
}
|
|
StateVar &= ~( kExecDelay | kExecError );
|
|
}
|
|
|
|
if( StateVar & kExecReopen ){
|
|
|
|
StateVar = svReopen( StateVar );
|
|
|
|
//
|
|
// Only try reopening the printer once.
|
|
//
|
|
bTriedOpen = TRUE;
|
|
|
|
} else if( StateVar & kExecRequestExit ){
|
|
|
|
StateVar = svRequestExit( StateVar );
|
|
|
|
} else if( StateVar & kExecNotifyStart ){
|
|
|
|
StateVar = svNotifyStart( StateVar );
|
|
|
|
} else if( StateVar & kExecRefreshAll ){
|
|
|
|
StateVar = svRefresh( StateVar );
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "Printer.bSyncRefresh: Unknown command %x %x\n",
|
|
this, StateVar ));
|
|
return FALSE;
|
|
}
|
|
|
|
DBGMSG( DBG_EXEC, ( "Printer.bSyncRefresh: %x return state %x\n", this, StateVar ));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Creation and deletion for clients
|
|
|
|
********************************************************************/
|
|
|
|
TPrinter*
|
|
TPrinter::
|
|
pNew(
|
|
IN MPrinterClient* pPrinterClient,
|
|
IN LPCTSTR pszPrinter,
|
|
IN DWORD dwAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create the printer object. Clients should call this routine
|
|
instead of the ctr for printers.
|
|
|
|
Called from UI thread only.
|
|
|
|
Arguments:
|
|
|
|
pPrinterClient - UI Client that stores info, generally the queue.
|
|
|
|
pszPrinter - Printer to be opened.
|
|
|
|
dwAccess - Access level requested.
|
|
|
|
Return Value:
|
|
|
|
TPrinter*, NULL = failure (call GetLastError()).
|
|
|
|
--*/
|
|
|
|
{
|
|
SPLASSERT( pszPrinter );
|
|
|
|
TPrinter* pPrinter = new TPrinter( pPrinterClient,
|
|
pszPrinter,
|
|
dwAccess );
|
|
|
|
if( !VALID_PTR( pPrinter )){
|
|
|
|
delete pPrinter;
|
|
return NULL;
|
|
}
|
|
|
|
DBGMSG( DBG_PRNINFO,
|
|
( "Printer.pNew: returned %x for "TSTR" %d\n",
|
|
pPrinter, DBGSTR( pszPrinter ), dwAccess ));
|
|
|
|
return pPrinter;
|
|
}
|
|
|
|
VOID
|
|
TPrinter::
|
|
vDelete(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Mark the printer object for deletion.
|
|
|
|
Called from UI thread only.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
When the printer object is marked for deletion, the object
|
|
will delete itself when there are no more commands left
|
|
to be processed.
|
|
|
|
Called from UI thread only.
|
|
|
|
--*/
|
|
|
|
{
|
|
{
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
//
|
|
// Disassociate the printer from the list view queue. Must
|
|
// be called inside critical section, since worker threads might
|
|
// be trying to change state.
|
|
//
|
|
PrinterGuard._pPrinterClient = NULL;
|
|
}
|
|
|
|
//
|
|
// Mark ourselves for deletion by adding the REQUESTEXIT job. This
|
|
// will allow the currently queued jobs to execute.
|
|
//
|
|
// Also note that bJobAdd is guarenteed to return TRUE for this
|
|
// EXIT request. vExecExitComplete may be called now, or when
|
|
// the current job has completed.
|
|
//
|
|
_pPrintLib->bJobAdd( this, kExecRequestExit );
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Internal private functions for creation and destruction. Outside
|
|
clients should use pNew and vDelete.
|
|
|
|
********************************************************************/
|
|
|
|
TPrinter::
|
|
TPrinter(
|
|
IN MPrinterClient* pPrinterClient,
|
|
IN LPCTSTR pszPrinter,
|
|
IN DWORD dwAccess
|
|
) : _pData( NULL ),
|
|
_dwAccess( 0 ),
|
|
_eJobStatusStringType( kMultipleJobStatusString )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create the printer object.
|
|
|
|
Called from UI thread only.
|
|
|
|
Arguments:
|
|
|
|
pPrinterClient - MPrinterClient that wants the data.
|
|
|
|
pszPrinter - Name of printer to open.
|
|
|
|
dwAccess - Access required (0 == highest access level).
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(pPrinterClient);
|
|
UNREFERENCED_PARAMETER( dwAccess );
|
|
|
|
TStatusB bStatus;
|
|
bStatus DBGCHK = pPrinterClient->bGetPrintLib(_pPrintLib);
|
|
|
|
if( bStatus ){
|
|
|
|
//
|
|
// Initialize ExecGuard.
|
|
//
|
|
ExecGuard._hPrinter = NULL;
|
|
|
|
//
|
|
// Initialize PrinterGuard.
|
|
//
|
|
PrinterGuard._hEventCommand = NULL;
|
|
PrinterGuard._pPrinterClient = pPrinterClient;
|
|
|
|
if( pszPrinter ){
|
|
PrinterGuard._strPrinter.bUpdate( pszPrinter );
|
|
}
|
|
}
|
|
|
|
//
|
|
// _strPrinter is our bValid check.
|
|
//
|
|
}
|
|
|
|
TPrinter::
|
|
~TPrinter(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// There shouldn't be any pending jobs in the command linked
|
|
// list.
|
|
//
|
|
SPLASSERT( PrinterGuard.Selection_bEmpty( ));
|
|
SPLASSERT( !ExecGuard._hPrinter );
|
|
SPLASSERT( !PrinterGuard._hEventCommand );
|
|
|
|
//
|
|
// The pData may not be valid. For example, if we close a printer
|
|
// window right before the svNotifyStart, this pointer may be NULL.
|
|
//
|
|
if( _pData ){
|
|
_pData->vDelete();
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
TPrinter::
|
|
vExecExitComplete(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The exit request: cleanup ourselves, then delete everything.
|
|
We may not be in the UI thread, but UIGuard is safe since we
|
|
have already deleted the UI portion of this object.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Command handling
|
|
|
|
Add to the Command linked list any pending requests.
|
|
|
|
********************************************************************/
|
|
|
|
VOID
|
|
TPrinter::
|
|
vCommandQueue(
|
|
IN TSelection* pSelection ADOPT
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queue a command for execution.
|
|
|
|
Arguments:
|
|
|
|
CommandType - Type of command, either PRINTER or JOB
|
|
|
|
dwCommand - CommandType specific job DWORD
|
|
|
|
Id - CommandType specific ID
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
SINGLETHREAD(UIThread);
|
|
SPLASSERT( pSelection );
|
|
|
|
{
|
|
//
|
|
// Append to the list of work items.
|
|
//
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
PrinterGuard.Selection_vAppend( pSelection );
|
|
}
|
|
|
|
if( _pPrintLib->bJobAdd( this, kExecCommand )){
|
|
|
|
//
|
|
// Command successfully queued, request a wakeup.
|
|
//
|
|
vCommandRequested();
|
|
|
|
//
|
|
// pSelection successfully adopted by PrinterGuard.Selection
|
|
// linked list. NULL it here so we don't free it.
|
|
//
|
|
pSelection = NULL;
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "Printer.vQueueCommand: Exec->bJobAdd failed %d\n",
|
|
GetLastError( )));
|
|
|
|
vErrorStatusChanged( 1 );
|
|
|
|
//
|
|
// Delink the item if it is linked.
|
|
//
|
|
{
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
if( pSelection->Selection_bLinked( )){
|
|
pSelection->Selection_vDelinkSelf();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Delete pSelection if not adopted by PrinterGuard.Selection.
|
|
//
|
|
delete pSelection;
|
|
}
|
|
|
|
|
|
VOID
|
|
TPrinter::
|
|
vCommandRequested(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A command was requested, so trigger PrinterGuard._hEventCommand.
|
|
If the worker thread is polling, it will be asleep now. By triggering
|
|
this event, we can force an immediate retry.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
if( PrinterGuard._hEventCommand ){
|
|
SetEvent( PrinterGuard._hEventCommand );
|
|
}
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Virtual functions for TExecWork.
|
|
|
|
Executed from worker threads; must be multithread safe.
|
|
|
|
********************************************************************/
|
|
|
|
VOID
|
|
TPrinter::
|
|
vExecFailedAddJob(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Occurs when we can't add another job to TExec.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// !! LATER !!
|
|
//
|
|
// Tune this error. 1 is not a good error...
|
|
//
|
|
vErrorStatusChanged( 1 );
|
|
}
|
|
|
|
STATEVAR
|
|
TPrinter::
|
|
svExecute(
|
|
IN STATEVAR StateVar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Very simple state machine. We examine the bits of the DWORD and
|
|
execute the appropriate action. The more important bits are
|
|
placed first so that they have priority.
|
|
|
|
Not called from UI thread.
|
|
|
|
Arguments:
|
|
|
|
StateVar - Current state of the Printer.
|
|
|
|
Return Value:
|
|
|
|
Ending state of the printer.
|
|
|
|
--*/
|
|
|
|
{
|
|
SINGLETHREADNOT(UIThread);
|
|
|
|
DBGMSG( DBG_EXEC, ( "Printer.svExecute: %x Sequence begin.\n", this ));
|
|
|
|
if( !StateVar ){
|
|
return 0;
|
|
}
|
|
|
|
BOOL bTriedOpen = FALSE;
|
|
|
|
while( StateVar ){
|
|
|
|
DBGMSG( DBG_EXEC, ( "Printer.svExecute now >> %x %x\n", this, StateVar ));
|
|
|
|
if( StateVar & kExecExit ){
|
|
|
|
//
|
|
// Quit case, return kExecExit to allow vExecExitComplete() to
|
|
// run, which cleans up everything.
|
|
//
|
|
return kExecExit;
|
|
|
|
} else if( StateVar & kExecError ){
|
|
|
|
svClose( kExecClose );
|
|
|
|
//
|
|
// Don't do anymore work until the user hits refresh.
|
|
//
|
|
return 0;
|
|
|
|
#ifdef SLEEP_ON_MINIMIZE
|
|
} else if( StateVar & kExecAwake ){
|
|
|
|
StateVar = svAwake( StateVar );
|
|
|
|
} else if( StateVar & kExecSleep && !( StateVar & kExecCommand )){
|
|
|
|
//
|
|
// Only go to sleep if we have no commands pending.
|
|
//
|
|
StateVar = svSleep( StateVar );
|
|
#endif
|
|
} else if( StateVar & kExecDelay ){
|
|
|
|
if( bTriedOpen ){
|
|
StateVar = svDelay( StateVar );
|
|
} else {
|
|
StateVar &= ~kExecDelay;
|
|
}
|
|
|
|
} else if( StateVar & kExecReopen ){
|
|
|
|
StateVar = svReopen( StateVar );
|
|
|
|
//
|
|
// Only try reopening the printer once.
|
|
//
|
|
bTriedOpen = TRUE;
|
|
|
|
} else if( StateVar & kExecCommand ){
|
|
|
|
StateVar = svCommand( StateVar );
|
|
|
|
} else if( StateVar & kExecRequestExit ){
|
|
|
|
StateVar = svRequestExit( StateVar );
|
|
|
|
} else if( StateVar & kExecNotifyStart ){
|
|
|
|
StateVar = svNotifyStart( StateVar );
|
|
|
|
} else if( StateVar & kExecRefreshAll ){
|
|
|
|
StateVar = svRefresh( StateVar );
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "Printer.svExecute: Unknown command %x %x\n",
|
|
this, StateVar ));
|
|
}
|
|
|
|
DBGMSG( DBG_EXEC, ( "Printer.svExecute %x return state %x\n", this, StateVar ));
|
|
|
|
//
|
|
// Get any pending work items so that we can process them
|
|
// now. This is necessary because we may have multiple jobs
|
|
// that must execute before we close ourselves.
|
|
//
|
|
// Plus, if kExecExit was set while we were busy, then we
|
|
// want to pick it up so we quit soon.
|
|
//
|
|
StateVar |= _pPrintLib->svClearPendingWork( this );
|
|
|
|
DBGMSG( DBG_EXEC, ( "Printer.svExecute %x updated %x\n", this, StateVar ));
|
|
}
|
|
|
|
//
|
|
// Clear the status bar panes.
|
|
//
|
|
vConnectStatusChanged( kConnectStatusNull );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
STATEVAR
|
|
TPrinter::
|
|
svReopen(
|
|
IN STATEVAR StateVar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open or reopen the printer.
|
|
|
|
Not called from the UI thread, since this may take a while.
|
|
|
|
Note: _strPrinter does _not_ need to be guarded by PrinterGuard
|
|
in this case since only ExecGuard threads write to it. Since
|
|
we are in ExecGuard now, there is no need to grab PrinterGuard.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
SINGLETHREADNOT(UIThread);
|
|
|
|
//
|
|
// Close if necessary.
|
|
//
|
|
svClose( kExecClose );
|
|
|
|
//
|
|
// Update status.
|
|
//
|
|
vConnectStatusChanged( kConnectStatusOpen );
|
|
|
|
TCHAR szPrinter[kPrinterBufMax];
|
|
LPTSTR pszPrinter = pszPrinterName( szPrinter );
|
|
|
|
STATUS Status = TPrinter::sOpenPrinter( pszPrinter,
|
|
&_dwAccess,
|
|
&ExecGuard._hPrinter );
|
|
if( Status ){
|
|
|
|
//
|
|
// Ensure hPrinter is NULL.
|
|
//
|
|
SPLASSERT( !ExecGuard._hPrinter );
|
|
|
|
DBGMSG( DBG_WARN, ( "Printer.sOpen: failed to open %ws: %d\n",
|
|
DBGSTR( (LPCTSTR)PrinterGuard._strPrinter ),
|
|
Status ));
|
|
|
|
//
|
|
// If the error is invalid printer name, immediately punt
|
|
// and don't retry unless the user requests a refresh.
|
|
//
|
|
// Do the same for access denied. We'll get here only if
|
|
// the spooler hasn't cached the printer. If it has, then
|
|
// this will succeed (since it's async), and the FFPCN will
|
|
// fail.
|
|
//
|
|
CONNECT_STATUS ConnectStatus;
|
|
|
|
switch( Status ){
|
|
case ERROR_INVALID_PRINTER_NAME:
|
|
|
|
ConnectStatus = kConnectStatusInvalidPrinterName;
|
|
break;
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
|
|
ConnectStatus = kConnectStatusAccessDenied;
|
|
break;
|
|
|
|
default:
|
|
|
|
ConnectStatus = kConnectStatusOpenError;
|
|
break;
|
|
}
|
|
|
|
vConnectStatusChanged( ConnectStatus );
|
|
return kExecError;
|
|
|
|
//
|
|
// !! POLICY !!
|
|
//
|
|
// Should we sleep, then retry, or just punt?
|
|
// If we want to sleep then retry, we should return the
|
|
// following state value.
|
|
//
|
|
// return StateVar | kExecDelay;
|
|
//
|
|
}
|
|
|
|
//
|
|
// Read the SingleJobStatusString printer value. This
|
|
// fix is for FAX support. The fax software does not
|
|
// want multiple status strings tacked together with
|
|
// '-' when a job error occurrs.
|
|
//
|
|
|
|
DWORD dwStatus;
|
|
DWORD dwType = REG_DWORD;
|
|
DWORD dwValue = 0;
|
|
DWORD cbNeeded = 0;
|
|
|
|
dwStatus = GetPrinterData( ExecGuard._hPrinter,
|
|
(LPTSTR)gszUISingleJobStatus,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
sizeof( DWORD ),
|
|
&cbNeeded );
|
|
|
|
//
|
|
// Assume multiple job status string, This is the default.
|
|
//
|
|
_eJobStatusStringType = kMultipleJobStatusString;
|
|
|
|
//
|
|
// If the printer data was fetched and the value read is one
|
|
// of the known values then set the new status string type.
|
|
//
|
|
if( dwStatus == ERROR_SUCCESS &&
|
|
cbNeeded == sizeof( DWORD ) &&
|
|
( dwValue == kMultipleJobStatusString ||
|
|
dwValue == kSingleJobStatusString ) )
|
|
{
|
|
_eJobStatusStringType = (EJobStatusString)dwValue;
|
|
}
|
|
|
|
//
|
|
// Success, start the notification process.
|
|
//
|
|
return (StateVar | kExecNotifyStart) & ~(kExecReopen | kExecDelay | kExecError);
|
|
}
|
|
|
|
STATEVAR
|
|
TPrinter::
|
|
svDelay(
|
|
IN STATEVAR StateVar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
An error occurred. Put a simple message in the status bar and sleep
|
|
for a while. We create a trigger event in case we want to abort
|
|
the sleep and retry immediately.
|
|
|
|
Not called from UI thread.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
StateVar - kExecDelay will be removed.
|
|
|
|
--*/
|
|
|
|
{
|
|
SINGLETHREADNOT(UIThread);
|
|
|
|
//
|
|
// Create a handle event so that new commands will cause
|
|
// us to immediately try and reopen.
|
|
//
|
|
HANDLE hEvent = CreateEvent( NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if( hEvent ){
|
|
|
|
{
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
PrinterGuard._hEventCommand = hEvent;
|
|
}
|
|
|
|
//
|
|
// Update status.
|
|
//
|
|
vConnectStatusChanged( kConnectStatusPoll );
|
|
|
|
WaitForSingleObject( hEvent, kSleepRetry );
|
|
|
|
{
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
PrinterGuard._hEventCommand = NULL;
|
|
}
|
|
CloseHandle( hEvent );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Failed to create event, just sleep for a bit.
|
|
//
|
|
Sleep( kSleepRetry );
|
|
}
|
|
|
|
//
|
|
// !! LATER !!
|
|
//
|
|
// Use TSleepN to avoid using a thread while we are
|
|
// sleeping.
|
|
//
|
|
return StateVar & ~kExecDelay;
|
|
}
|
|
|
|
STATEVAR
|
|
TPrinter::
|
|
svClose(
|
|
IN STATEVAR StateVar
|
|
)
|
|
{
|
|
SINGLETHREADNOT(UIThread);
|
|
|
|
svNotifyEnd( kExecNotifyEnd );
|
|
|
|
if( ExecGuard._hPrinter && !ClosePrinter( ExecGuard._hPrinter )) {
|
|
|
|
STATUS Status = GetLastError();
|
|
|
|
DBGMSG( DBG_WARN, ( "Printer.sClose: failed to close %ws: %d\n",
|
|
DBGSTR( (LPCTSTR)PrinterGuard._strPrinter ),
|
|
Status ));
|
|
}
|
|
|
|
//
|
|
// Reset the notifications count.
|
|
//
|
|
if( _pData ){
|
|
|
|
INFO Info;
|
|
Info.dwData = 0;
|
|
vContainerChanged( kContainerClearItems, Info );
|
|
|
|
_pData->vDelete();
|
|
_pData = NULL;
|
|
}
|
|
|
|
ExecGuard._hPrinter = NULL;
|
|
return StateVar & ( kExecExit | kExecCommand );
|
|
}
|
|
|
|
|
|
STATEVAR
|
|
TPrinter::
|
|
svCommand(
|
|
IN STATEVAR StateVar
|
|
)
|
|
{
|
|
SINGLETHREADNOT(UIThread);
|
|
|
|
BOOL bSuccess = TRUE;
|
|
BOOL bReopen = FALSE;
|
|
|
|
//
|
|
// Update status.
|
|
//
|
|
vConnectStatusChanged( kConnectStatusCommand );
|
|
|
|
while( bSuccess ){
|
|
|
|
TSelection* pSelection;
|
|
|
|
//
|
|
// Get a single request.
|
|
//
|
|
{
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
pSelection = PrinterGuard.Selection_pHead();
|
|
|
|
if( pSelection ){
|
|
pSelection->Selection_vDelinkSelf();
|
|
}
|
|
}
|
|
|
|
if( !pSelection ){
|
|
|
|
//
|
|
// Done with work.
|
|
//
|
|
break;
|
|
}
|
|
|
|
switch( pSelection->_CommandType ){
|
|
case TSelection::kCommandTypeJob:
|
|
{
|
|
//
|
|
// Need to do multiple SetJobs.
|
|
//
|
|
COUNT i;
|
|
|
|
for( i=0; i < pSelection->_cSelected; ++i ){
|
|
|
|
bSuccess = SetJob( ExecGuard._hPrinter,
|
|
pSelection->_pid[i],
|
|
0,
|
|
NULL,
|
|
pSelection->_dwCommandAction );
|
|
if( !bSuccess ){
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if a refresh is pending and execute it.
|
|
//
|
|
StateVar |= _pPrintLib->svClearPendingWork( this );
|
|
|
|
if( StateVar & kExecRefresh ){
|
|
|
|
//
|
|
// We need to check explicity for refreshes, since
|
|
// a ton of changes may come in at once, and
|
|
// we should keep the UI somewhat current.
|
|
//
|
|
StateVar = svRefresh( StateVar );
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
case TSelection::kCommandTypePrinter:
|
|
|
|
bSuccess = SetPrinter( ExecGuard._hPrinter,
|
|
0,
|
|
NULL,
|
|
pSelection->_dwCommandAction );
|
|
|
|
break;
|
|
|
|
case TSelection::kCommandTypePrinterAttributes:
|
|
{
|
|
//
|
|
// First read the current printer info5 state. Then update
|
|
// this structure with the new attribute the set the new
|
|
// attribute state. Printer info 5 is a little more efficent
|
|
// than using a printer info2.
|
|
//
|
|
PPRINTER_INFO_5 pInfo5 = NULL;
|
|
DWORD cbInfo5 = 0;
|
|
|
|
bSuccess = VDataRefresh::bGetPrinter( ExecGuard._hPrinter, 5, (PVOID*)&pInfo5, &cbInfo5 );
|
|
|
|
if( bSuccess )
|
|
{
|
|
pInfo5->Attributes = pSelection->_dwCommandAction;
|
|
|
|
bSuccess = SetPrinter( ExecGuard._hPrinter, 5, (PBYTE)pInfo5, 0 );
|
|
|
|
FreeMem( pInfo5 );
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "Printer.svCommand: unknown command %x %d %d\n",
|
|
pSelection, pSelection->_CommandType,
|
|
pSelection->_dwCommandAction ));
|
|
break;
|
|
}
|
|
|
|
if( !bSuccess ){
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "Printer.svCommand: Type %d Command %d to hPrinter %x failed %d\n",
|
|
pSelection->_CommandType,
|
|
pSelection->_dwCommandAction,
|
|
ExecGuard._hPrinter,
|
|
GetLastError( )));
|
|
}
|
|
|
|
//
|
|
// Free the pSelection.
|
|
//
|
|
delete pSelection;
|
|
}
|
|
|
|
if( bSuccess ){
|
|
|
|
//
|
|
// Successfully executed commands, clear error string
|
|
// in status bar.
|
|
//
|
|
vErrorStatusChanged( ERROR_SUCCESS );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Currently we punt on the old command. Should we requeue it?
|
|
//
|
|
|
|
//
|
|
// An error occurred; bomb out of all.
|
|
//
|
|
STATUS dwError = GetLastError();
|
|
SPLASSERT( dwError );
|
|
|
|
vErrorStatusChanged( dwError );
|
|
|
|
//
|
|
// If we encountered an invalid handle, we should reopen
|
|
// the printer.
|
|
//
|
|
if( dwError == ERROR_INVALID_HANDLE ){
|
|
StateVar |= kExecReopen;
|
|
}
|
|
|
|
//
|
|
// !! POLICY !!
|
|
//
|
|
// We don't re-execute jobs--delete any pending jobs.
|
|
//
|
|
{
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
TSelection* pSelection;
|
|
TIter Iter;
|
|
|
|
for( PrinterGuard.Selection_vIterInit( Iter ), Iter.vNext();
|
|
Iter.bValid(); ){
|
|
|
|
pSelection = PrinterGuard.Selection_pConvert( Iter );
|
|
Iter.vNext();
|
|
|
|
pSelection->Selection_vDelinkSelf();
|
|
delete pSelection;
|
|
}
|
|
}
|
|
}
|
|
|
|
return StateVar & ~kExecCommand;
|
|
}
|
|
|
|
STATEVAR
|
|
TPrinter::
|
|
svRequestExit(
|
|
IN STATEVAR StateVar
|
|
)
|
|
{
|
|
//
|
|
// Close up everything.
|
|
//
|
|
svClose( StateVar | kExecClose );
|
|
|
|
//
|
|
// kExecExit forces a cleanup.
|
|
//
|
|
return kExecExit;
|
|
}
|
|
|
|
STATEVAR
|
|
TPrinter::
|
|
svRefresh(
|
|
IN STATEVAR StateVar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Refresh the printer queue.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
SINGLETHREADNOT(UIThread);
|
|
|
|
//
|
|
// If the printer has not been initialized, reopen it now.
|
|
//
|
|
if( !ExecGuard._hPrinter || !_pData ){
|
|
return kExecReopen;
|
|
}
|
|
|
|
//
|
|
// Get the printer name
|
|
//
|
|
TCHAR szPrinter[kPrinterBufMax];
|
|
LPTSTR pszPrinter = pszPrinterName( szPrinter );
|
|
|
|
//
|
|
// Update state.
|
|
//
|
|
vConnectStatusChanged( kConnectStatusRefresh );
|
|
|
|
//
|
|
// _pData->svRefresh is responsible for calling pPrinter's
|
|
// vItemChanged or pPrinter->vAllItemsChanged.
|
|
//
|
|
StateVar = _pData->svRefresh( StateVar );
|
|
|
|
return StateVar;
|
|
}
|
|
|
|
|
|
|
|
STATEVAR
|
|
TPrinter::
|
|
svNotifyStart(
|
|
IN STATEVAR StateVar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Start the notification process.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
SINGLETHREADNOT(UIThread);
|
|
|
|
DBGMSG( DBG_NOTIFY,
|
|
( "Printer.svNotifyStart: %x %ws\n",
|
|
this, DBGSTR( (LPCTSTR)PrinterGuard._strPrinter )));
|
|
|
|
vConnectStatusChanged( kConnectStatusInitialize );
|
|
|
|
//
|
|
// If we have an extablished _pData (e.g., non-NULL), then just
|
|
// pass along the request.
|
|
//
|
|
// If we don't have one yet, we need to create one.
|
|
//
|
|
StateVar = _pData ?
|
|
_pData->svNotifyStart( StateVar ) :
|
|
VData::svNew( this, StateVar, _pData );
|
|
|
|
if( StateVar & kExecDelay ){
|
|
|
|
//
|
|
// Error occurred--if it's Access Denied, then fail.
|
|
// This happens when the spooler caches the printer:
|
|
// OpenPrinter succeeds even though the user has no access.
|
|
// The first "real" action fails.
|
|
//
|
|
if( GetLastError() == ERROR_ACCESS_DENIED ){
|
|
vConnectStatusChanged( kConnectStatusAccessDenied );
|
|
return kExecError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we succeeded, the kExecRegister will be set, so we can
|
|
// register it then return.
|
|
//
|
|
if( StateVar & kExecRegister ){
|
|
|
|
SPLASSERT( _pData );
|
|
|
|
TStatus Status;
|
|
Status DBGCHK = _pPrintLib->pNotify()->sRegister( _pData );
|
|
|
|
if( Status != ERROR_SUCCESS ){
|
|
|
|
//
|
|
// Failed to register; delay then reopen printer. We could
|
|
// just try and re-register later, but this should be a very
|
|
// rare event, so do the least amount of work.
|
|
//
|
|
DBGMSG( DBG_WARN,
|
|
( "Printer.svNotifyStart: sRegister %x failed %d\n",
|
|
this, Status ));
|
|
|
|
StateVar |= kExecDelay | kExecReopen;
|
|
}
|
|
|
|
//
|
|
// No longer need to register. In the future, if we need to
|
|
// register, we will set the bit on again.
|
|
//
|
|
StateVar &= ~kExecRegister;
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_TRACE,
|
|
( "Printer.svNotifyStart: %x pData->svNotifyStart failed %d %d\n",
|
|
this, GetLastError(), StateVar ));
|
|
}
|
|
|
|
return StateVar;
|
|
}
|
|
|
|
|
|
STATEVAR
|
|
TPrinter::
|
|
svNotifyEnd(
|
|
IN STATEVAR StateVar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shut down the notification process.
|
|
|
|
Note: _strPrinter does _not_ need to be guarded by PrinterGuard
|
|
in this case since only ExecGuard threads write to it. Since
|
|
we are in ExecGuard now, there is no need to grab PrinterGuard.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
SINGLETHREADNOT(UIThread);
|
|
DBGMSG( DBG_NOTIFY,
|
|
( "Printer.svNotifyEnd: %x %ws\n",
|
|
this, DBGSTR( (LPCTSTR)PrinterGuard._strPrinter )));
|
|
|
|
if( _pData ){
|
|
return _pData->svNotifyEnd( StateVar );
|
|
}
|
|
|
|
return StateVar & ~kExecNotifyEnd;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Status updates
|
|
|
|
********************************************************************/
|
|
|
|
VOID
|
|
TPrinter::
|
|
vErrorStatusChanged(
|
|
IN DWORD dwStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Error state of _user_ command failed. (If the user executes a
|
|
command then leaves, they want to know the result of the command.
|
|
If we change this when we execute one of our own commands, the
|
|
previous result is lost.)
|
|
|
|
Arguments:
|
|
|
|
dwStatus - GetLastError() code.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
INFO Info;
|
|
Info.dwData = dwStatus;
|
|
|
|
vContainerChanged( kContainerErrorStatus, Info );
|
|
}
|
|
|
|
|
|
VOID
|
|
TPrinter::
|
|
vConnectStatusChanged(
|
|
IN CONNECT_STATUS ConnectStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The connection status of the printer changed (opening, intializing,
|
|
refreshing, etc.).
|
|
|
|
Arguments:
|
|
|
|
dwStatus - New IDS_* of connection status.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
INFO Info;
|
|
Info.dwData = ConnectStatus;
|
|
|
|
vContainerChanged( kContainerConnectStatus, Info );
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
|
|
TPrinterClientRef
|
|
|
|
********************************************************************/
|
|
|
|
TPrinter::
|
|
TPrinterClientRef::
|
|
TPrinterClientRef(
|
|
const TPrinter* pPrinter
|
|
)
|
|
{
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
_pPrinterClient = pPrinter->PrinterGuard._pPrinterClient;
|
|
|
|
if( !_pPrinterClient ){
|
|
return;
|
|
}
|
|
|
|
_pPrinterClient->vIncRef();
|
|
}
|
|
|
|
|
|
TPrinter::
|
|
TPrinterClientRef::
|
|
~TPrinterClientRef(
|
|
VOID
|
|
)
|
|
{
|
|
if( _pPrinterClient ){
|
|
_pPrinterClient->cDecRef();
|
|
}
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
MDataClient definitions.
|
|
|
|
********************************************************************/
|
|
|
|
VOID
|
|
TPrinter::
|
|
vContainerChanged(
|
|
CONTAINER_CHANGE ContainerChange,
|
|
INFO Info
|
|
)
|
|
{
|
|
TPrinterClientRef PrinterClientRef( this );
|
|
|
|
if( !PrinterClientRef.bValid( )){
|
|
return;
|
|
}
|
|
|
|
switch( ContainerChange ){
|
|
case kContainerServerName:
|
|
case kContainerName:
|
|
|
|
{
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
TStatusB bStatus;
|
|
|
|
bStatus DBGCHK = ( ContainerChange == kContainerServerName ) ?
|
|
PrinterGuard._strServer.bUpdate( Info.pszData ) :
|
|
PrinterGuard._strPrinter.bUpdate( Info.pszData );
|
|
|
|
if( !bStatus ){
|
|
|
|
//
|
|
// Failed, execute a refesh with delay.
|
|
//
|
|
ContainerChange = kContainerStateVar;
|
|
Info.dwData = kExecDelay | kExecRefreshAll;
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
PrinterClientRef.ptr()->vContainerChanged( kContainerName,
|
|
kInfoNull );
|
|
break;
|
|
|
|
default:
|
|
|
|
Fail:
|
|
|
|
PrinterClientRef.ptr()->vContainerChanged( ContainerChange, Info );
|
|
break;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
TPrinter::
|
|
vItemChanged(
|
|
ITEM_CHANGE ItemChange,
|
|
HITEM hItem,
|
|
INFO Info,
|
|
INFO InfoNew
|
|
)
|
|
{
|
|
if( PrinterGuard._pPrinterClient ){
|
|
PrinterGuard._pPrinterClient->vItemChanged( ItemChange,
|
|
hItem,
|
|
Info,
|
|
InfoNew );
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
TPrinter::
|
|
vSaveSelections(
|
|
VOID
|
|
)
|
|
{
|
|
if( PrinterGuard._pPrinterClient ){
|
|
PrinterGuard._pPrinterClient->vSaveSelections();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
TPrinter::
|
|
vRestoreSelections(
|
|
VOID
|
|
)
|
|
{
|
|
if( PrinterGuard._pPrinterClient ){
|
|
PrinterGuard._pPrinterClient->vRestoreSelections();
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
TPrinter::
|
|
bGetPrintLib(
|
|
TRefLock<TPrintLib> &refLock
|
|
) const
|
|
{
|
|
ASSERT(pPrintLib().pGet());
|
|
if (pPrintLib().pGet())
|
|
{
|
|
refLock.vAcquire(pPrintLib().pGet());
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VDataNotify*
|
|
TPrinter::
|
|
pNewNotify(
|
|
MDataClient* pDataClient
|
|
) const
|
|
{
|
|
TPrinterClientRef PrinterClientRef( this );
|
|
|
|
if( PrinterClientRef.bValid( )){
|
|
return PrinterClientRef.ptr()->pNewNotify( pDataClient );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
VDataRefresh*
|
|
TPrinter::
|
|
pNewRefresh(
|
|
MDataClient* pDataClient
|
|
) const
|
|
{
|
|
TPrinterClientRef PrinterClientRef( this );
|
|
|
|
if( PrinterClientRef.bValid( )){
|
|
return PrinterClientRef.ptr()->pNewRefresh( pDataClient );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
LPTSTR
|
|
TPrinter::
|
|
pszPrinterName(
|
|
OUT LPTSTR pszPrinterBuffer CHANGE
|
|
) const
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves the fully qualified name of the printer (\\server\printer or
|
|
just printer for local printers).
|
|
|
|
Arguments:
|
|
|
|
pszPrinterBuffer - Uninitialized buffer that receives the printer name.
|
|
Must be at least kPrinterBufMax in length.
|
|
|
|
Return Value:
|
|
|
|
Pointer to name of printer (generally pointer to pszPrinterBuffer).
|
|
|
|
--*/
|
|
|
|
{
|
|
pszPrinterBuffer[0] = 0;
|
|
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
//
|
|
// If we have a server name in TPrinter, prepend it to the
|
|
// printer name if it's different. We could just always prepend
|
|
// it, since localspl.dll correctly grabs it, but then the
|
|
// title bar displays the fully qualified name.
|
|
//
|
|
if( ((LPCTSTR)PrinterGuard._strServer)[0] &&
|
|
_pPrintLib->strComputerName( ) != PrinterGuard.strServer( )){
|
|
|
|
lstrcpy( pszPrinterBuffer, PrinterGuard._strServer );
|
|
|
|
//
|
|
// We assume that the machine - printer separator
|
|
// is always a backslash.
|
|
//
|
|
lstrcat( pszPrinterBuffer, TEXT( "\\" ));
|
|
}
|
|
|
|
lstrcat( pszPrinterBuffer, PrinterGuard._strPrinter );
|
|
return pszPrinterBuffer;
|
|
}
|
|
|
|
LPTSTR
|
|
TPrinter::
|
|
pszServerName(
|
|
OUT LPTSTR pszServerBuffer CHANGE
|
|
) const
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves the fully qualified name of the server. May be NULL
|
|
for local servers.
|
|
|
|
Arguments:
|
|
|
|
pszServerBuffer - Uninitialized buffer that receives the server name.
|
|
Must be at least kServerMax in length.
|
|
|
|
Return Value:
|
|
|
|
Pointer to name of printer (generally pointer to pszServerBuffer).
|
|
|
|
--*/
|
|
|
|
{
|
|
LPTSTR pszServer = NULL;
|
|
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
//
|
|
// If we have a server name that is different from gpPrintLib,
|
|
// then return it. Otherwise return NULL which indicates the
|
|
// local server.
|
|
//
|
|
if( ((LPCTSTR)PrinterGuard._strServer)[0] &&
|
|
_pPrintLib->strComputerName( ) != PrinterGuard.strServer( )){
|
|
|
|
lstrcpy( pszServerBuffer, PrinterGuard._strServer );
|
|
|
|
pszServer = pszServerBuffer;
|
|
}
|
|
|
|
return pszServer;
|
|
}
|
|
|
|
HANDLE
|
|
TPrinter::
|
|
hPrinter(
|
|
VOID
|
|
) const
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the handle to the common printer handle. Note that
|
|
the callee must not use any RPC calls that do not return in a
|
|
timely fashion (like WPC or FFPCN that uses WPC).
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
SINGLETHREADNOT(UIThread);
|
|
return ExecGuard._hPrinter;
|
|
}
|
|
|
|
HANDLE
|
|
TPrinter::
|
|
hPrinterNew(
|
|
VOID
|
|
) const
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a new printer handle. hPrinter() returns a common one,
|
|
this returns one that is new and orphaned.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
hPrinter - Must be ClosePrinter'd by callee().
|
|
NULL - failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
TStatusB bStatus;
|
|
LPTSTR pszPrinter;
|
|
TCHAR szPrinter[kPrinterBufMax];
|
|
HANDLE hPrinter;
|
|
|
|
pszPrinter = pszPrinterName( szPrinter );
|
|
|
|
if( pszPrinter && !pszPrinter[0] ){
|
|
//
|
|
// szNull indicates local server also; change it to
|
|
// NULL since OpenPrinter only likes NULL.
|
|
//
|
|
pszPrinter = NULL;
|
|
}
|
|
|
|
bStatus DBGCHK = OpenPrinter( pszPrinter,
|
|
&hPrinter,
|
|
NULL );
|
|
|
|
if( !bStatus ){
|
|
return NULL;
|
|
}
|
|
|
|
return hPrinter;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
|
|
Default MPrinterClient definitions.
|
|
|
|
********************************************************************/
|
|
|
|
COUNT
|
|
MPrinterClient::
|
|
cSelected(
|
|
VOID
|
|
) const
|
|
{
|
|
DBGMSG( DBG_WARN, ( "PrinterClient.cSelected: unimplemented\n" ));
|
|
return kInvalidCountValue;
|
|
}
|
|
|
|
HANDLE
|
|
MPrinterClient::
|
|
GetFirstSelItem(
|
|
VOID
|
|
) const
|
|
{
|
|
DBGMSG( DBG_WARN, ( "PrinterClient.GetFirstSelItem: unimplemented\n" ));
|
|
return NULL;
|
|
}
|
|
|
|
HANDLE
|
|
MPrinterClient::
|
|
GetNextSelItem(
|
|
HANDLE hItem
|
|
) const
|
|
{
|
|
UNREFERENCED_PARAMETER( hItem );
|
|
|
|
DBGMSG( DBG_WARN, ( "PrinterClient.GetNextSelItem: unimplemented\n" ));
|
|
return NULL;
|
|
}
|
|
|
|
IDENT
|
|
MPrinterClient::
|
|
GetId(
|
|
HANDLE hItem
|
|
) const
|
|
{
|
|
UNREFERENCED_PARAMETER( hItem );
|
|
|
|
DBGMSG( DBG_WARN, ( "PrinterClient.GetId: unimplemented\n" ));
|
|
return kInvalidIdentValue;
|
|
}
|
|
|
|
VOID
|
|
MPrinterClient::
|
|
vSaveSelections(
|
|
VOID
|
|
)
|
|
{
|
|
}
|
|
|
|
|
|
VOID
|
|
MPrinterClient::
|
|
vRestoreSelections(
|
|
VOID
|
|
)
|
|
{
|
|
}
|
|
|