NT4/private/windows/spooler/localspl/spooler.c
2020-09-30 17:12:29 +02:00

2347 lines
63 KiB
C

/*++
Copyright (c) 1990 - 1996 Microsoft Corporation
Module Name:
spooler.c
Abstract:
This module provides all the public exported APIs relating to spooling
and printing for the Local Print Providor. They include
LocalStartDocPrinter
LocalWritePrinter
LocalReadPrinter
LocalEndDocPrinter
LocalAbortPrinter
Author:
Dave Snipp (DaveSn) 15-Mar-1991
Revision History:
--*/
#include <precomp.h>
BOOL
SpoolThisJob(
PSPOOL pSpool,
DWORD Level,
LPBYTE pDocInfo
);
BOOL
PrintingDirectlyToPort(
PSPOOL pSpool,
DWORD Level,
LPBYTE pDocInfo,
LPDWORD pJobId
);
BOOL
PrintingDirect(
PSPOOL pSpool,
DWORD Level,
LPBYTE pDocInfo
);
DWORD
ReadFromPrinter(
PSPOOL pSpool,
LPBYTE pBuf,
DWORD cbBuf
);
DWORD
WriteToPrinter(
PSPOOL pSpool,
LPBYTE pByte,
DWORD cbBuf
);
BOOL
ReallocJobIdMap(
DWORD NewSize
);
BOOL
IsGoingToFile(
LPWSTR pOutputFile,
PINISPOOLER pIniSpooler
);
VOID
ClearJobError(
PINIJOB pIniJob
);
DWORD
LocalStartDocPrinter(
HANDLE hPrinter,
DWORD Level,
LPBYTE pDocInfo
)
{
PINIPRINTER pIniPrinter;
PINIPORT pIniPort;
PSPOOL pSpool=(PSPOOL)hPrinter;
DWORD LastError=0, JobId=0;
PDOC_INFO_1 pDocInfo1 = (PDOC_INFO_1)pDocInfo;
BOOL bPrintingDirect;
SPLASSERT(Level == 1);
if (ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER ) &&
!(pSpool->Status & SPOOL_STATUS_STARTDOC) &&
!(pSpool->Status & SPOOL_STATUS_ADDJOB)) {
if ((pSpool->TypeofHandle & PRINTER_HANDLE_PORT) &&
(pIniPort = pSpool->pIniPort) &&
(pIniPort->signature == IPO_SIGNATURE)) {
if (!(PrintingDirectlyToPort(pSpool, Level, pDocInfo, &JobId))) {
return FALSE;
}
} else if ((pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER) &&
(pIniPrinter = pSpool->pIniPrinter)) {
bPrintingDirect = FALSE;
if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DIRECT) {
bPrintingDirect = TRUE;
} else {
EnterSplSem();
bPrintingDirect = IsGoingToFile(pDocInfo1->pOutputFile,
pSpool->pIniSpooler);
LeaveSplSem();
}
if (bPrintingDirect) {
if (!PrintingDirect(pSpool, Level, pDocInfo))
return FALSE;
} else {
if (!SpoolThisJob(pSpool, Level, pDocInfo))
return FALSE;
}
} else
LastError = ERROR_INVALID_PARAMETER;
if (!LastError) {
pSpool->Status |= SPOOL_STATUS_STARTDOC;
pSpool->Status &= ~SPOOL_STATUS_CANCELLED;
}
} else
LastError = ERROR_INVALID_HANDLE;
if (LastError) {
DBGMSG(DBG_WARNING, ("StartDoc FAILED %d\n", LastError));
SetLastError(LastError);
return FALSE;
}
if (JobId)
return JobId;
else
return pSpool->pIniJob->JobId;
}
BOOL
LocalStartPagePrinter(
HANDLE hPrinter
)
/*++
Bug-Bug: StartPagePrinter and EndPagePrinter calls should be
supported only for SPOOL_STATUS_STARTDOC handles only. However
because of our fixes for the engine, we cannot fail StartPagePrinter
and EndPagePrinter for SPOOL_STATUS_ADDJOB as well.
--*/
{
PSPOOL pSpool = (PSPOOL)hPrinter;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwFileSize;
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
return(FALSE);
}
if (pSpool->Status & SPOOL_STATUS_CANCELLED) {
SetLastError(ERROR_PRINT_CANCELLED);
return FALSE;
}
if (pSpool->pIniJob != NULL) {
if ( (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) &&
((pSpool->pIniJob->Status & JOB_PRINTING) ||
(pSpool->pIniJob->Status & JOB_DESPOOLING))) {
//
// Account for Pages Printed in LocalEndPagePrinter
//
} else {
// We Are Spooling
pSpool->pIniJob->cPages++;
if ( pSpool->pIniJob->Status & JOB_TYPE_ADDJOB ) {
// If the Job is being written on the client side
// the size is not getting updated so do it now on
// the start page
if ( pSpool->hReadFile != INVALID_HANDLE_VALUE ) {
hFile = pSpool->hReadFile;
} else {
hFile = pSpool->pIniJob->hWriteFile;
}
if ( hFile != INVALID_HANDLE_VALUE ) {
dwFileSize = GetFileSize( hFile, 0 );
if ( pSpool->pIniJob->Size < dwFileSize ) {
DBGMSG( DBG_TRACE, ("StartPagePrinter adjusting size old %d new %d\n",
pSpool->pIniJob->Size, dwFileSize));
pSpool->pIniJob->Size = dwFileSize;
// Support for despooling whilst spooling
// for Down Level jobs
if (pSpool->pIniJob->WaitForWrite != INVALID_HANDLE_VALUE)
SetEvent( pSpool->pIniJob->WaitForWrite );
}
}
}
}
} else {
DBGMSG(DBG_TRACE, ("StartPagePrinter issued with no Job\n"));
}
return TRUE;
}
/* ReallocJobIdMap -- grows job id bitmap
*
* in: u - suggestion (minimum) for new max jobid
* out: ok?
* uMaxJobId - new maximum job id
*/
BOOL
ReallocJobIdMap(
DWORD NewSize)
{
if (NewSize & 7) {
NewSize&=~7;
NewSize+=8;
}
pJobIdMap=ReallocSplMem(pJobIdMap, MaxJobId/8, NewSize/8);
if ( pJobIdMap == NULL ) {
DBGMSG(DBG_ERROR, ("ReallocJobIdMap failed ReallocSplMem Newsize %d Error %d\n",
NewSize, GetLastError() ));
}
else
MaxJobId = NewSize;
return pJobIdMap != NULL;
}
DWORD
GetNextId(
VOID)
{
DWORD id;
do {
for (id = CurrentJobId + 1; id < MaxJobId; id++) {
if (! ISBITON(pJobIdMap, id) ) {
MARKUSE(pJobIdMap, id);
return CurrentJobId = id;
}
}
for (id = 1; id < CurrentJobId; id ++) {
if (! ISBITON(pJobIdMap, id) ) {
MARKUSE(pJobIdMap, id);
return CurrentJobId = id;
}
}
} while (ReallocJobIdMap(MaxJobId + 128));
return 0;
}
PINIPORT
FindFilePort(
LPWSTR pFileName,
PINISPOOLER pIniSpooler)
{
PINIPORT pIniPort;
SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
pIniPort = pIniSpooler->pIniPort;
while (pIniPort) {
if (!wcscmp(pIniPort->pName, pFileName)
&& (pIniPort->Status & PP_FILE)){
return (pIniPort);
}
pIniPort = pIniPort->pNext;
}
return NULL;
}
PINIMONITOR
FindFilePortMonitor(
PINISPOOLER pIniSpooler
)
{
PINIPORT pIniPort;
SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
pIniPort = pIniSpooler->pIniPort;
while (pIniPort) {
if (!wcscmp(pIniPort->pName, L"FILE:")) {
return pIniPort->pIniMonitor;
}
pIniPort = pIniPort->pNext;
}
return NULL;
}
BOOL
AddIniPrinterToIniPort(
PINIPORT pIniPort,
PINIPRINTER pIniPrinter
)
{
DWORD i;
PINIPRINTER *ppIniPrinter;
//
// If Printer already attatched to Port
//
for (i = 0; i < pIniPort->cPrinters; i++) {
if (pIniPort->ppIniPrinter[i] == pIniPrinter) {
return TRUE;
}
}
ppIniPrinter = RESIZEPORTPRINTERS(pIniPort, 1);
if ( ppIniPrinter != NULL ) {
pIniPort->ppIniPrinter = ppIniPrinter;
if ( !pIniPort->cPrinters )
CreateRedirectionThread(pIniPort);
pIniPort->ppIniPrinter[pIniPort->cPrinters++] = pIniPrinter;
DBGMSG( DBG_TRACE, ("AddIniPrinterToIniPort pIniPrinter %x %ws pIniPort %x %ws\n",
pIniPrinter, pIniPrinter->pName,
pIniPort, pIniPort->pName ));
return TRUE;
} else {
DBGMSG( DBG_WARNING, ("AddIniPrintertoIniPort failed pIniPort %x pIniPrinter %x error %d\n",
pIniPort, pIniPrinter, GetLastError() ));
return FALSE;
}
}
VOID
AddJobEntry(
PINIPRINTER pIniPrinter,
PINIJOB pIniJob
)
{
DWORD Position;
SplInSem();
// DO NOT Add the Same Job more than once
SPLASSERT(pIniJob != FindJob(pIniPrinter, pIniJob->JobId, &Position));
pIniJob->pIniPrevJob = pIniPrinter->pIniLastJob;
if (pIniJob->pIniPrevJob)
pIniJob->pIniPrevJob->pIniNextJob = pIniJob;
pIniPrinter->pIniLastJob = pIniJob;
if (!pIniPrinter->pIniFirstJob)
pIniPrinter->pIniFirstJob=pIniJob;
}
BOOL
CheckDataTypes(
PINIPRINTPROC pIniPrintProc,
LPWSTR pDatatype
)
{
PDATATYPES_INFO_1 pDatatypeInfo;
DWORD i;
pDatatypeInfo = (PDATATYPES_INFO_1)pIniPrintProc->pDatatypes;
for (i=0; i<pIniPrintProc->cDatatypes; i++)
if (!lstrcmpi(pDatatypeInfo[i].pName, pDatatype))
return TRUE;
return FALSE;
}
PINIPRINTPROC
FindDatatype(
PINIPRINTPROC pDefaultPrintProc,
LPWSTR pDatatype
)
{
PINIPRINTPROC pIniPrintProc;
if ( pDatatype == NULL ) {
return NULL;
}
if ( pDefaultPrintProc &&
CheckDataTypes( pDefaultPrintProc, pDatatype )) {
return pDefaultPrintProc;
}
pIniPrintProc = pThisEnvironment->pIniPrintProc;
while ( pIniPrintProc ) {
if ( CheckDataTypes( pIniPrintProc, pDatatype )) {
return pIniPrintProc;
}
pIniPrintProc = pIniPrintProc->pNext;
}
DBGMSG( DBG_WARNING, ( "FindDatatype: Could not find Datatype\n") );
return FALSE;
}
BOOL
IsGoingToFile(
LPWSTR pOutputFile,
PINISPOOLER pIniSpooler)
{
PINIPORT pIniPort;
LPWSTR pszShare;
SplInSem();
SPLASSERT(pIniSpooler->signature == ISP_SIGNATURE);
// Validate the contents of the pIniJob->pOutputFile
// if it is a valid file, then return true
// if it is a port name or any other kind of name then ignore
if (pOutputFile && *pOutputFile) {
//
// we have a non-null pOutputFile
// match this with all available ports
//
pIniPort = pIniSpooler->pIniPort;
while ( pIniPort ) {
SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
if (!_wcsicmp( pIniPort->pName, pOutputFile )) {
//
// We have matched the pOutputFile field with a
// valid port and the port is not a file port
//
if (pIniPort->Status & PP_FILE) {
pIniPort = pIniPort->pNext;
continue;
}
return FALSE;
}
pIniPort = pIniPort->pNext;
}
//
// We have no port that matches exactly
// so let's assume its a file.
//
// ugly hack -- check for Net: as the name
//
// This would normally match files like "NewFile" or "Nextbox,"
// but since we always fully qualify filenames, we don't encounter
// any problems.
//
if (!_wcsnicmp(pOutputFile, L"Ne", 2)) {
return FALSE;
}
//
// We have the problem LAN man ports coming as UNC path and being
// treated as files. This is a HACK for that
//
if ( pOutputFile &&
pOutputFile[0] == L'\\' &&
pOutputFile[1] == L'\\' &&
(pszShare = wcschr(pOutputFile+2, L'\\')) ) {
pszShare++;
if ( FindPrinter(pszShare) ||
FindPrinterShare(pszShare, pIniSpooler) )
return FALSE;
}
return TRUE;
}
return FALSE;
}
BOOL
SpoolThisJob(
PSPOOL pSpool,
DWORD Level,
LPBYTE pDocInfo
)
{
WCHAR szFileName[MAX_PATH];
PDOC_INFO_1 pDocInfo1=(PDOC_INFO_1)pDocInfo;
HANDLE hImpersonationToken;
DWORD dwId = 0;
HANDLE hWriteFile = INVALID_HANDLE_VALUE;
LPWSTR pszDatatype = NULL;
DBGMSG(DBG_TRACE, ("Spooling document %ws\n",
pDocInfo1->pDocName ? pDocInfo1->pDocName : L""));
if( pDocInfo1 && pDocInfo1->pDatatype ){
pszDatatype = pDocInfo1->pDatatype;
if( !FindDatatype( NULL, pszDatatype )){
DBGMSG(DBG_WARNING, ("Datatype %ws is invalid\n", pDocInfo1->pDatatype));
SetLastError(ERROR_INVALID_DATATYPE);
return FALSE;
}
}
EnterSplSem();
//
// Check if we need to disallow EMF printing.
//
if( pSpool->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_RAW_ONLY ){
if( !pszDatatype ){
pszDatatype = pSpool->pDatatype ?
pSpool->pDatatype :
pSpool->pIniPrinter->pDatatype;
}
if( !ValidRawDatatype( pszDatatype )){
LeaveSplSem();
DBGMSG(DBG_WARNING, ("Datatype %ws is not RAW to a RAW printer\n", pDocInfo1->pDatatype));
SetLastError(ERROR_INVALID_DATATYPE);
return FALSE;
}
}
dwId = GetNextId();
GetFullNameFromId(pSpool->pIniPrinter, dwId, TRUE,
szFileName, FALSE);
LeaveSplSem();
SplOutSem();
if (!(hImpersonationToken = RevertToPrinterSelf())) {
DBGMSG(DBG_WARNING, ("SpoolThisJob RevertToPrinterSelf failed: %d\n", GetLastError()));
SplOutSem();
return FALSE;
}
hWriteFile = CreateFile(szFileName,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (!ImpersonatePrinterClient(hImpersonationToken)) {
DBGMSG(DBG_WARNING, ("SpoolThisJob ImpersonatePrinterClient failed: %d\n", GetLastError()));
SplOutSem();
return FALSE;
}
if ( hWriteFile == INVALID_HANDLE_VALUE ) {
DBGMSG(DBG_WARNING, ("SpoolThisJob CreateFile( %ws ) GENERIC_WRITE failed: Error %d\n",
szFileName, GetLastError()));
SplOutSem();
return FALSE;
} else {
DBGMSG(DBG_TRACE, ("SpoolThisJob CreateFile( %ws) GENERIC_WRITE Success:hWriteFile %x\n",szFileName, hWriteFile));
}
EnterSplSem();
if( !(pSpool->pIniJob = CreateJobEntry(pSpool,
Level,
pDocInfo,
dwId,
!IsInteractiveUser(),
0)))
{
LeaveSplSem();
CloseHandle( hWriteFile );
DeleteFile( szFileName );
SplOutSem();
return FALSE;
}
SPLASSERT(!IsGoingToFile(pSpool->pIniJob->pOutputFile,
pSpool->pIniSpooler));
pSpool->pIniJob->Status |= JOB_SPOOLING;
// Gather Stress Information for Max Number of concurrent spooling jobs
pSpool->pIniPrinter->cSpooling++;
if (pSpool->pIniPrinter->cSpooling > pSpool->pIniPrinter->cMaxSpooling)
pSpool->pIniPrinter->cMaxSpooling = pSpool->pIniPrinter->cSpooling;
pSpool->pIniJob->hWriteFile = hWriteFile;
LeaveSplSem();
SplOutSem();
WriteShadowJob(pSpool->pIniJob);
EnterSplSem();
AddJobEntry(pSpool->pIniPrinter, pSpool->pIniJob);
SetPrinterChange(pSpool->pIniPrinter,
pSpool->pIniJob,
NVAddJob,
PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER,
pSpool->pIniSpooler);
//
// RapidPrint might start despooling right away
//
CHECK_SCHEDULER();
LeaveSplSem();
SplOutSem();
return TRUE;
}
BOOL
PrintingDirect(
PSPOOL pSpool,
DWORD Level,
LPBYTE pDocInfo
)
{
PDOC_INFO_1 pDocInfo1=(PDOC_INFO_1)pDocInfo;
PINIPORT pIniPort = NULL;
BOOL bGoingToFile = FALSE;
DBGMSG(DBG_TRACE, ("Printing document %ws direct\n",
pDocInfo1->pDocName ? pDocInfo1->pDocName : L"(Null)"));
if (pDocInfo1 &&
pDocInfo1->pDatatype &&
!ValidRawDatatype(pDocInfo1->pDatatype)) {
DBGMSG(DBG_WARNING, ("Datatype is not RAW\n"));
SetLastError(ERROR_INVALID_DATATYPE);
return FALSE;
}
EnterSplSem();
if (pDocInfo1 && pDocInfo1->pOutputFile
&& IsGoingToFile(pDocInfo1->pOutputFile, pSpool->pIniSpooler)) {
bGoingToFile = TRUE;
}
if (bGoingToFile) {
//
// If we already have a thread/process printing to this filename
// fail. Do not allow multiple processes/threads to write to the
// same output file.
//
if (FindFilePort(pDocInfo1->pOutputFile, pSpool->pIniSpooler)) {
LeaveSplSem();
SetLastError(ERROR_SHARING_VIOLATION);
return(FALSE);
}
}
pSpool->pIniJob = CreateJobEntry(pSpool,
Level,
pDocInfo,
GetNextId(),
!IsInteractiveUser(),
JOB_DIRECT);
if (!pSpool->pIniJob) {
LeaveSplSem();
return FALSE;
}
pSpool->pIniJob->StartDocComplete = CreateEvent( NULL,
EVENT_RESET_AUTOMATIC,
EVENT_INITIAL_STATE_NOT_SIGNALED,
NULL );
pSpool->pIniJob->WaitForWrite = CreateEvent( NULL,
EVENT_RESET_AUTOMATIC,
EVENT_INITIAL_STATE_NOT_SIGNALED,
NULL );
pSpool->pIniJob->WaitForRead = CreateEvent( NULL,
EVENT_RESET_AUTOMATIC,
EVENT_INITIAL_STATE_NOT_SIGNALED,
NULL );
AddJobEntry(pSpool->pIniPrinter, pSpool->pIniJob);
pSpool->TypeofHandle |= PRINTER_HANDLE_DIRECT;
if (bGoingToFile) {
PINIMONITOR pIniMonitor;
pSpool->pIniJob->Status |= JOB_PRINT_TO_FILE;
pIniMonitor = FindFilePortMonitor( pSpool->pIniSpooler );
pIniPort = CreatePortEntry( pSpool->pIniJob->pOutputFile,
pIniMonitor, pSpool->pIniSpooler);
pIniPort->Status |= PP_FILE;
AddIniPrinterToIniPort(pIniPort, pSpool->pIniPrinter);
}
CHECK_SCHEDULER();
if (pSpool->pIniJob->pIniPort) {
SplInSem();
pSpool->pIniJob->Status |= JOB_PRINTING;
}
SetPrinterChange(pSpool->pIniPrinter,
pSpool->pIniJob,
NVAddJob,
PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER,
pSpool->pIniSpooler);
LeaveSplSem();
SplOutSem();
// Wait until the port thread calls StartDocPrinter through
// the print processor:
DBGMSG(DBG_PORT, ("PrintingDirect: Calling WaitForSingleObject( %x )\n",
pSpool->pIniJob->StartDocComplete));
WaitForSingleObject( pSpool->pIniJob->StartDocComplete, INFINITE );
EnterSplSem();
// Close the event and set its value to NULL.
// If anything goes wrong, or if the job gets cancelled,
// the port thread will check this event, and if it's non-NULL,
// it will set it to allow this thread to wake up.
DBGMSG(DBG_PORT, ("PrintingDirect: Calling CloseHandle( %x )\n",
pSpool->pIniJob->StartDocComplete));
CloseHandle(pSpool->pIniJob->StartDocComplete);
pSpool->pIniJob->StartDocComplete = NULL;
/* If an error occurred, set the error on this thread:
*/
if (pSpool->pIniJob->StartDocError) {
SetLastError(pSpool->pIniJob->StartDocError);
// We have to decrement by 2 because we've just created this job
// in CreateJobEntry setting it to 1 and the other thread who
// actually failed the StartDoc above (PortThread) did
// not know to blow away the job. He just failed the StartDocPort.
// No, we don't have to decrement by 2 because the PortThread
// decrement does go through, am restoring to decrement by 1
SPLASSERT(pSpool->pIniJob->cRef != 0);
DECJOBREF(pSpool->pIniJob);
DeleteJobCheck(pSpool->pIniJob);
DBGMSG(DBG_TRACE, ("PrintingDirect:cRef %d\n", pSpool->pIniJob->cRef));
LeaveSplSem();
return FALSE;
}
LeaveSplSem();
return TRUE;
}
VOID
ClearJobError(
PINIJOB pIniJob
)
/*++
Routine Description:
Clears the error status bits of a job.
This routine should be called when port monitor successfully
sends bytes to the printer.
Arguments:
pIniJob - Job in error state that should be cleared.
Return Value:
--*/
{
SplInSem();
SPLASSERT( pIniJob->Status & (JOB_PAPEROUT | JOB_OFFLINE | JOB_ERROR ));
pIniJob->Status &= ~(JOB_PAPEROUT | JOB_OFFLINE | JOB_ERROR);
SetPrinterChange( pIniJob->pIniPrinter,
pIniJob,
NVJobStatus,
PRINTER_CHANGE_SET_JOB,
pIniJob->pIniPrinter->pIniSpooler );
}
BOOL
LocalWritePrinter(
HANDLE hPrinter,
LPVOID pBuf,
DWORD cbBuf,
LPDWORD pcWritten
)
{
PSPOOL pSpool=(PSPOOL)hPrinter;
PINIPORT pIniPort;
DWORD cWritten, cTotal;
DWORD rc;
LPBYTE pByte=pBuf;
DWORD LastError=0;
PINIJOB pIniJob;
PINIMONITOR pIniMonitor;
HANDLE hThread = NULL;
DWORD dwThreadId;
*pcWritten = 0;
SplOutSem();
EnterSplSem();
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER ))
LastError = ERROR_INVALID_HANDLE;
else if (!(pSpool->Status & SPOOL_STATUS_STARTDOC))
LastError = ERROR_SPL_NO_STARTDOC;
else if (pSpool->Status & SPOOL_STATUS_ADDJOB)
LastError = ERROR_SPL_NO_STARTDOC;
else if (pSpool->pIniJob &&
!(pSpool->TypeofHandle & (PRINTER_HANDLE_PORT|
PRINTER_HANDLE_DIRECT)) &&
(pSpool->pIniJob->hWriteFile == INVALID_HANDLE_VALUE)) {
LastError = ERROR_INVALID_HANDLE;
DBGMSG( DBG_TRACE, ("LocalWritePrinter: hWriteFile == INVALID_HANDLE_VALUE hPrinter %x\n",hPrinter ));
}
else if (pSpool->Status & SPOOL_STATUS_CANCELLED)
LastError = ERROR_PRINT_CANCELLED;
else if (pSpool->pIniJob && (pSpool->pIniJob->Status & JOB_PENDING_DELETION) )
LastError = ERROR_PRINT_CANCELLED;
pIniPort = pSpool->pIniPort;
LeaveSplSem();
SplOutSem();
if (LastError) {
DBGMSG(DBG_TRACE, ("WritePrinter LastError: %x hPrinter %x\n", LastError, hPrinter));
SetLastError(LastError);
return FALSE;
}
cWritten = cTotal = 0;
while (cbBuf) {
SplOutSem();
if ( pSpool->TypeofHandle & PRINTER_HANDLE_PORT ) {
if ( pSpool->pIniPort->Status & PP_MONITOR ) {
if ( pSpool->pIniPort->pIniLangMonitor ) {
pIniMonitor = pSpool->pIniPort->pIniLangMonitor;
} else {
pIniMonitor = pSpool->pIniPort->pIniMonitor;
}
SplOutSem();
rc = (*pIniMonitor->fn.pfnWritePort)(pSpool->pIniPort->hPort,
pByte,
cbBuf,
&cWritten);
//
// Only update if cWritten != 0. If it is zero
// (for instance, when hpmon is stuck at Status
// not available), then we go into a tight loop
// sending out notifications.
//
if (cWritten) {
//
// For stress Test information gather the total
// number of types written.
//
EnterSplSem();
pSpool->pIniPrinter->cTotalBytes.QuadPart =
pSpool->pIniPrinter->cTotalBytes.QuadPart +
cWritten;
LeaveSplSem();
SplOutSem();
} else {
if (rc && dwWritePrinterSleepTime) {
//
// Sleep to avoid consuming too much CPU.
// Hpmon has this problem where they return
// success, but don't write any bytes.
//
// Be very careful: this may get called several
// times by a monitor that writes a lot of zero
// bytes (perhaps at the beginning of jobs).
//
Sleep(dwWritePrinterSleepTime);
}
}
}
else {
DBGMSG(DBG_TRACE, ("LocalWritePrinter: Port has no monitor\n"));
if (pSpool->Status & SPOOL_STATUS_PRINT_FILE) {
DBGMSG(DBG_TRACE, ("LocalWritePrinter: Port has no monitor - writing to file\n"));
rc = WriteFile(pSpool->hFile, pByte, cbBuf, &cWritten, NULL);
} else {
DBGMSG(DBG_TRACE, ("LocalWritePrinter: Port has no monitor - calling into router\n"));
rc = WritePrinter(pSpool->hPort, pByte, cbBuf, &cWritten);
}
}
} else if ( pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT ) {
cWritten = WriteToPrinter(pSpool, pByte, cbBuf);
if (cWritten) {
pSpool->pIniJob->Size+=cWritten;
EnterSplSem();
SetPrinterChange(pSpool->pIniPrinter,
pSpool->pIniJob,
NVSpoolJob,
PRINTER_CHANGE_WRITE_JOB,
pSpool->pIniSpooler);
LeaveSplSem();
}
SplOutSem();
rc = (BOOL)cWritten;
} else {
SplOutSem();
rc = WriteFile(pSpool->pIniJob->hWriteFile, pByte, cbBuf, &cWritten, NULL);
if (cWritten) {
EnterSplSem();
pSpool->pIniJob->Size = GetFileSize( pSpool->pIniJob->hWriteFile, 0 );
//
// For Printing whilst Despooling, make sure we have enough bytes before
// scheduling this job
//
if (( (pSpool->pIniJob->Size - cWritten) < dwFastPrintSlowDownThreshold ) &&
( pSpool->pIniJob->Size >= dwFastPrintSlowDownThreshold ) &&
( pSpool->pIniJob->WaitForWrite == INVALID_HANDLE_VALUE )) {
CHECK_SCHEDULER();
}
// Support for despooling whilst spooling
if ( pSpool->pIniJob->WaitForWrite != INVALID_HANDLE_VALUE )
SetEvent( pSpool->pIniJob->WaitForWrite );
SetPrinterChange(pSpool->pIniPrinter,
pSpool->pIniJob,
NVSpoolJob,
PRINTER_CHANGE_WRITE_JOB,
pSpool->pIniSpooler);
LeaveSplSem();
SplOutSem();
}
}
SplOutSem();
(*pcWritten)+=cWritten;
cbBuf-=cWritten;
pByte+=cWritten;
EnterSplSem();
if( pSpool->pIniJob ){
pIniJob = pSpool->pIniJob;
} else if( pSpool->pIniPort && pSpool->pIniPort->pIniJob ){
pIniJob = pSpool->pIniPort->pIniJob;
} else {
pIniJob = NULL;
}
if( pIniJob ){
if( pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART )){
SetLastError(ERROR_PRINT_CANCELLED);
rc = FALSE;
if( hThread ) {
// we had started a message box. See if the thread is still running or dismissed by user.
// If it is still running, wait for it to terminate before pIniJob can be freed.
if( WAIT_TIMEOUT == WaitForSingleObject( hThread, 0 )) {
PostThreadMessage( dwThreadId, WM_QUIT, IDRETRY, 0 );
// We need to leave the semaphore, since the UI thread we
// are waiting on could need to acquire it.
LeaveSplSem();
WaitForSingleObject( hThread, INFINITE );
EnterSplSem();
}
CloseHandle( hThread );
hThread = NULL;
}
goto Fail;
}
//
// If there was no error, and the job was marked in an error
// state, clear it.
//
if( rc &&
( pIniJob->Status & (JOB_PAPEROUT | JOB_OFFLINE | JOB_ERROR ))){
ClearJobError( pIniJob );
}
}
LeaveSplSem();
if (!rc) {
// Warning: We are sending in a stack variable. We need to be sure the error UI thread is
// cleaned up before LocalWritePrinter() returns!
if( PromptWriteError( pSpool, &hThread, &dwThreadId ) == IDCANCEL ) {
EnterSplSem();
goto Fail;
}
}
else {
if( hThread ) {
// we have started a message box and now the automatically
// retry has succeeded, we need to kill the message box
// and continue to print.
// See if the thread is still running or dismissed by user.
// If it is still running, wait for it to terminate before pIniJob can be freed.
if( WAIT_TIMEOUT == WaitForSingleObject( hThread, 0 )) {
PostThreadMessage( dwThreadId, WM_QUIT, IDRETRY, 0 );
SplOutSem();
WaitForSingleObject( hThread, INFINITE );
}
CloseHandle( hThread );
hThread = NULL;
}
}
}
rc = TRUE;
EnterSplSem();
Fail:
SplInSem();
LeaveSplSem();
DBGMSG(DBG_TRACE, ("WritePrinter Written %d : %d\n", *pcWritten, rc));
SplOutSem();
SPLASSERT( hThread == NULL );
return rc;
}
BOOL
LocalEndPagePrinter(
HANDLE hPrinter
)
{
PSPOOL pSpool = (PSPOOL)hPrinter;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwFileSize;
// BUGBUG
// This routine is outside critical section but should be inside
//
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
return(FALSE);
}
if (pSpool->Status & SPOOL_STATUS_CANCELLED) {
SetLastError(ERROR_PRINT_CANCELLED);
return FALSE;
}
if (pSpool->pIniJob != NULL) {
if ( (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) &&
((pSpool->pIniJob->Status & JOB_PRINTING) ||
(pSpool->pIniJob->Status & JOB_DESPOOLING))) {
// Despooling ( RapidPrint )
pSpool->pIniJob->cPagesPrinted++;
pSpool->pIniPrinter->cTotalPagesPrinted++;
} else {
//
// Spooling
//
if ( pSpool->pIniJob->Status & JOB_TYPE_ADDJOB ) {
// If the Job is being written on the client side
// the size is not getting updated so do it now on
// the start page
if ( pSpool->hReadFile != INVALID_HANDLE_VALUE ) {
hFile = pSpool->hReadFile;
} else {
hFile = pSpool->pIniJob->hWriteFile;
}
if ( hFile != INVALID_HANDLE_VALUE ) {
dwFileSize = GetFileSize( hFile, 0 );
if ( pSpool->pIniJob->Size < dwFileSize ) {
DBGMSG( DBG_TRACE, ("EndPagePrinter adjusting size old %d new %d\n",
pSpool->pIniJob->Size, dwFileSize));
pSpool->pIniJob->Size = dwFileSize;
// Support for despooling whilst spooling
// for Down Level jobs
if (pSpool->pIniJob->WaitForWrite != INVALID_HANDLE_VALUE)
SetEvent( pSpool->pIniJob->WaitForWrite );
}
}
CHECK_SCHEDULER();
}
}
} else {
DBGMSG(DBG_TRACE, ("LocalEndPagePrinter issued with no Job\n"));
}
return TRUE;
}
BOOL
LocalAbortPrinter(
HANDLE hPrinter
)
{
PSPOOL pSpool=(PSPOOL)hPrinter;
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
DBGMSG( DBG_WARNING, ("ERROR in AbortPrinter: %x\n", ERROR_INVALID_HANDLE));
return FALSE;
}
if (!(pSpool->Status & SPOOL_STATUS_STARTDOC)) {
SetLastError(ERROR_SPL_NO_STARTDOC);
return(FALSE);
}
if (pSpool->pIniPort && !(pSpool->pIniPort->Status & PP_MONITOR)) {
if (pSpool->Status & SPOOL_STATUS_PRINT_FILE) {
if (!CloseHandle(pSpool->hFile)) {
return(FALSE);
}
pSpool->Status &= ~SPOOL_STATUS_PRINT_FILE;
pSpool->Status |= SPOOL_STATUS_CANCELLED;
return(TRUE);
} else {
return AbortPrinter(pSpool->hPort);
}
}
pSpool->Status |= SPOOL_STATUS_CANCELLED;
if (pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER)
if (pSpool->pIniJob)
pSpool->pIniJob->Status |= JOB_PENDING_DELETION;
//
// KrishnaG - fixes bug 2646, we need to clean up AbortPrinter
// rewrite so that it doesn't fail on cases which EndDocPrinter should fail
// get rid of comment when done
//
LocalEndDocPrinter(hPrinter);
return TRUE;
}
BOOL
LocalReadPrinter(
HANDLE hPrinter,
LPVOID pBuf,
DWORD cbBuf,
LPDWORD pNoBytesRead
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PSPOOL pSpool=(PSPOOL)hPrinter;
DWORD Error=0, rc;
HANDLE hWait = INVALID_HANDLE_VALUE;
DWORD dwFileSize = 0;
DWORD ThisPortSecsToWait;
DWORD cbReadSize = cbBuf;
DWORD SizeInFile = 0;
DWORD BytesAllowedToRead = 0;
NOTIFYVECTOR NotifyVector;
PINIMONITOR pIniMonitor;
SplOutSem();
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
DBGMSG( DBG_WARNING, ("LocalReadPrinter ERROR_INVALID_HANDLE\n"));
return(FALSE);
}
if (pSpool->Status & SPOOL_STATUS_CANCELLED) {
DBGMSG( DBG_WARNING, ("LocalReadPrinter ERROR_PRINT_CANCELLED\n"));
SetLastError(ERROR_PRINT_CANCELLED);
return FALSE;
}
if ( pNoBytesRead != NULL ) {
*pNoBytesRead = 0;
}
if (pSpool->TypeofHandle & PRINTER_HANDLE_JOB) {
if (pSpool->pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART)) {
DBGMSG( DBG_WARNING, ("LocalReadPrinter Error IniJob->Status %x\n",pSpool->pIniJob->Status));
SetLastError(ERROR_PRINT_CANCELLED);
return FALSE;
}
if (pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT) {
*pNoBytesRead = ReadFromPrinter(pSpool, pBuf, cbReadSize);
SplOutSem();
return TRUE;
}
SplOutSem();
EnterSplSem();
// RapidPrint
//
// NOTE this while loop is ONLY in operation if during RapidPrint
// ie when we are Printing the same job we are Spooling
//
while (( pSpool->pIniJob->WaitForWrite != INVALID_HANDLE_VALUE ) &&
( pSpool->pIniJob->Status & JOB_SPOOLING ) &&
( pSpool->pIniJob->cbPrinted == pSpool->pIniJob->Size )){
SplInSem();
//
// We cannot rely on pIniJob->Size to be accurate since for
// downlevel jobs or jobs that to AddJob they are writing
// to a file without calling WritePrinter.
// So we call the file system to get an accurate file size
//
dwFileSize = GetFileSize( pSpool->hReadFile, 0 );
if ( pSpool->pIniJob->Size != dwFileSize ) {
DBGMSG( DBG_TRACE, ("LocalReadPrinter adjusting size old %d new %d\n",
pSpool->pIniJob->Size, dwFileSize));
pSpool->pIniJob->Size = dwFileSize;
SetPrinterChange( pSpool->pIniPrinter,
pSpool->pIniJob,
NVSpoolJob,
PRINTER_CHANGE_WRITE_JOB,
pSpool->pIniSpooler );
continue;
}
if (pSpool->pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART | JOB_ERROR | JOB_ABANDON )) {
SetLastError(ERROR_PRINT_CANCELLED);
LeaveSplSem();
SplOutSem();
DBGMSG( DBG_WARNING, ("LocalReadPrinter Error 2 IniJob->Status %x\n",pSpool->pIniJob->Status));
return FALSE;
}
//
// Wait until something is written to the file
//
hWait = pSpool->pIniJob->WaitForWrite;
ResetEvent( hWait );
DBGMSG( DBG_TRACE, ("LocalReadPrinter Waiting for Data %d milliseconds\n",dwFastPrintWaitTimeout));
LeaveSplSem();
SplOutSem();
rc = WaitForSingleObjectEx( hWait, dwFastPrintWaitTimeout, FALSE );
SplOutSem();
EnterSplSem();
DBGMSG( DBG_TRACE, ("LocalReadPrinter Returned from Waiting %x\n", rc));
SPLASSERT ( pSpool->pIniJob != NULL );
//
// If we did NOT timeout then we may have some Data to read
//
if ( rc != WAIT_TIMEOUT )
continue;
//
// In the unlikely event that the file size changed event
// though we timed out do one last check
// Note the SizeThread wakes every 2.5 seconds to check the
// size so this is very unlikely
//
if ( pSpool->pIniJob->Size != GetFileSize( pSpool->hReadFile, 0 ) )
continue;
//
// If there are any other jobs that could be printed on
// this port give up waiting.
//
pSpool->pIniJob->Status |= JOB_TIMEOUT;
if ( NULL == AssignFreeJobToFreePort(pSpool->pIniJob->pIniPort, &ThisPortSecsToWait) )
continue;
//
// There is another Job waiting
// Freeze this job, the user can Restart it later
//
pSpool->pIniJob->Status |= JOB_ABANDON;
CloseHandle( pSpool->pIniJob->WaitForWrite );
pSpool->pIniJob->WaitForWrite = INVALID_HANDLE_VALUE;
// Assign it our Error String
ReallocSplStr(&pSpool->pIniJob->pStatus, szFastPrintTimeout);
SetPrinterChange(pSpool->pIniJob->pIniPrinter,
pSpool->pIniJob,
NVJobStatusAndString,
PRINTER_CHANGE_SET_JOB,
pSpool->pIniJob->pIniPrinter->pIniSpooler );
DBGMSG( DBG_WARNING,
("LocalReadPrinter Timeout on pIniJob %x %ws %ws\n",
pSpool->pIniJob,
pSpool->pIniJob->pUser,
pSpool->pIniJob->pDocument));
LogJobInfo( pSpool->pIniSpooler,
MSG_DOCUMENT_TIMEOUT,
pSpool->pIniJob->JobId,
pSpool->pIniJob->pDocument,
pSpool->pIniJob->pUser,
pSpool->pIniJob->pIniPrinter->pName,
dwFastPrintWaitTimeout );
SetLastError(ERROR_SEM_TIMEOUT);
LeaveSplSem();
SplOutSem();
return FALSE;
} // END WHILE
pSpool->pIniJob->Status &= ~( JOB_TIMEOUT | JOB_ABANDON );
// RapidPrint
//
// Some printers (like HP 4si with PSCRIPT) timeout if they
// don't get data, so if we fall below a threshold of data
// in the spoolfile then throttle back the Reads to 1 Byte
// per second until we have more data to ship to the printer
//
if (( pSpool->pIniJob->WaitForWrite != INVALID_HANDLE_VALUE ) &&
( pSpool->pIniJob->Status & JOB_SPOOLING )) {
SizeInFile = pSpool->pIniJob->Size - pSpool->pIniJob->cbPrinted;
if ( dwFastPrintSlowDownThreshold >= SizeInFile ) {
cbReadSize = 1;
hWait = pSpool->pIniJob->WaitForWrite;
ResetEvent( hWait );
DBGMSG( DBG_TRACE, ("LocalReadPrinter Throttling IOs waiting %d milliseconds SizeInFile %d\n",
dwFastPrintThrottleTimeout,SizeInFile));
LeaveSplSem();
SplOutSem();
rc = WaitForSingleObjectEx( hWait, dwFastPrintThrottleTimeout, FALSE );
SplOutSem();
EnterSplSem();
DBGMSG( DBG_TRACE, ("LocalReadPrinter Returned from Waiting %x\n", rc));
SPLASSERT ( pSpool->pIniJob != NULL );
} else {
BytesAllowedToRead = SizeInFile - dwFastPrintSlowDownThreshold;
if ( cbReadSize > BytesAllowedToRead ) {
cbReadSize = BytesAllowedToRead;
}
}
}
LeaveSplSem();
SplOutSem();
rc = ReadFile( pSpool->hReadFile, pBuf, cbReadSize, pNoBytesRead, NULL);
DBGMSG( DBG_TRACE, ("LocalReadPrinter rc %x hReadFile %x pBuf %x cbReadSize %d *pNoBytesRead %d\n",
rc, pSpool->hReadFile, pBuf, cbReadSize, *pNoBytesRead));
// Provide Feedback so user can see printing progress
// on despooling, the size is update here and not in write
// printer because the journal data is larger than raw
if ( ( pSpool->pIniJob->Status & JOB_PRINTING ) &&
( *pNoBytesRead != 0 )) {
SplOutSem();
EnterSplSem();
dwFileSize = GetFileSize( pSpool->hReadFile, 0 );
COPYNV(NotifyVector, NVWriteJob);
if ( pSpool->pIniJob->Size < dwFileSize ) {
DBGMSG( DBG_TRACE, ("LocalReadPrinter 2 adjusting size old %d new %d\n",
pSpool->pIniJob->Size, dwFileSize));
pSpool->pIniJob->Size = dwFileSize;
ADDNV(NotifyVector, NVSpoolJob);
}
pSpool->pIniJob->cbPrinted += *pNoBytesRead;
//
// Provide Feedback to Printman that data has been
// written. Note the size written is not used to
// update the IniJob->cbPrinted becuase there is a
// difference in size between journal data (in the
// spool file) and the size of RAW bytes written to
// the printer.
//
SetPrinterChange(pSpool->pIniPrinter,
pSpool->pIniJob,
NotifyVector,
PRINTER_CHANGE_WRITE_JOB,
pSpool->pIniSpooler);
LeaveSplSem();
SplOutSem();
}
} else if ( pSpool->TypeofHandle & PRINTER_HANDLE_PORT ) {
if (pSpool->pIniPort->Status & PP_FILE)
rc = ReadFile( pSpool->hReadFile, pBuf, cbReadSize, pNoBytesRead, NULL);
else if ( pSpool->pIniPort->Status & PP_MONITOR ) {
if ( pSpool->pIniPort->pIniLangMonitor ) {
pIniMonitor = pSpool->pIniPort->pIniLangMonitor;
} else {
pIniMonitor = pSpool->pIniPort->pIniMonitor;
}
SplOutSem();
rc = (*pIniMonitor->fn.pfnReadPort)(pSpool->pIniPort->hPort,
pBuf,
cbReadSize,
pNoBytesRead);
} else
rc = ReadPrinter(pSpool->hPort, pBuf, cbReadSize, pNoBytesRead);
} else {
SetLastError(ERROR_INVALID_HANDLE);
rc = FALSE;
}
SplOutSem();
DBGMSG( DBG_TRACE, ("LocalReadPrinter returns hPrinter %x pIniJob %x rc %x pNoBytesRead %d\n",hPrinter, pSpool->pIniJob, rc, *pNoBytesRead));
return rc;
}
BOOL
LocalEndDocPrinter(
HANDLE hPrinter
)
/*++
Routine Description:
By Default the routine is in critical section.
The reference counts for any object we are working on (pSpool and pIniJob)
are incremented, so that when we leave critical section for lengthy
operations these objects are not deleted.
Arguments:
Return Value:
--*/
{
PSPOOL pSpool=(PSPOOL)hPrinter;
BOOL bNotify = TRUE;
DWORD rc;
PINIMONITOR pIniMonitor;
DBGMSG(DBG_TRACE, ("Entering LocalEndDocPrinter with %x\n", hPrinter));
SplOutSem();
EnterSplSem();
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
LeaveSplSem();
return(FALSE);
}
if (!(pSpool->Status & SPOOL_STATUS_STARTDOC)) {
SetLastError(ERROR_SPL_NO_STARTDOC);
LeaveSplSem();
return(FALSE);
}
if (pSpool->Status & SPOOL_STATUS_ADDJOB) {
SetLastError(ERROR_SPL_NO_STARTDOC);
LeaveSplSem();
return(FALSE);
}
pSpool->Status &= ~SPOOL_STATUS_STARTDOC;
//
// Case-1 Printer Handle is PRINTER_HANDLE_PORT
// Note - there are two cases to keep in mind here
// A] The first case is the despooling thread calling
// a port with a monitor - LPT1:/COM1: or any port
// created by the monitor
// B] The second case is when the application thread is
// doing an EndDocPrinter to a port which has no monitor
// This is the local printer masquerading as a remote printer
// case. Remember for this case there is no IniJob created
// on the local printer at all. We just pass the call
// straight to the remote printer
if (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) { //Case A]
SPLASSERT(!(pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER));
//
// Now check if this pSpool object's port has
// a monitor
if ( pSpool->pIniPort->Status & PP_MONITOR ) {
//
// Check if our job is really around
//
if (!pSpool->pIniJob) {
LeaveSplSem();
SetLastError(ERROR_CAN_NOT_COMPLETE);
SplOutSem();
return(FALSE);
}
//
// We need to leave the spooler critical section
// because we're going call into the Monitor.
// so bump up ref count on pSpool and pIniJob
//
pSpool->cRef++;
INCJOBREF(pSpool->pIniJob);
if ( pSpool->pIniPort->pIniLangMonitor ) {
pIniMonitor = pSpool->pIniPort->pIniLangMonitor;
//
// If job is printing thru a language monitor we will get
// SetJob with JOB_CONTROL_LAST_PAGE_EJECTED in addition to
// JOB_CONTROL_SENT_TO_PRINTER
//
pSpool->pIniJob->dwJobControlsPending += 2;
} else {
pIniMonitor = pSpool->pIniPort->pIniMonitor;
pSpool->pIniJob->dwJobControlsPending++;
}
LeaveSplSem();
SPLASSERT(pIniMonitor);
SplOutSem();
(*pIniMonitor->fn.pfnEndDocPort)(pSpool->pIniPort->hPort);
EnterSplSem();
pSpool->cRef--;
DECJOBREF(pSpool->pIniJob);
LeaveSplSem();
SplOutSem();
return(TRUE);
} else { // Case B]
//
// We leave critical section here so bump pSpool object only
// Note ----THERE IS NO INIJOB HERE AT ALL---Note
// this call is synchronous; we will call into the router
// who will then call the appropriate network print providor
// e.g win32spl.dll
//
pSpool->cRef++;
LeaveSplSem();
if (pSpool->Status & SPOOL_STATUS_PRINT_FILE) {
if (!CloseHandle(pSpool->hFile)) {
DBGMSG(DBG_TRACE, ("LocalEndDocPrinter: Printing to File, CloseHandle failed\n"));
rc = FALSE;
} else {
DBGMSG(DBG_TRACE, ("LocalEndDocPrinter: Printing to File, CloseHandle succeeded\n"));
pSpool->Status &= ~SPOOL_STATUS_PRINT_FILE;
rc = TRUE;
}
} else {
rc = EndDocPrinter(pSpool->hPort);
}
EnterSplSem();
pSpool->cRef--;
SetPrinterChange(pSpool->pIniPrinter,
pSpool->pIniJob,
NVJobStatus,
PRINTER_CHANGE_SET_JOB,
pSpool->pIniSpooler);
LeaveSplSem();
SplOutSem();
return(rc);
}
}
SplInSem();
//
// Case-2 Printer Handle is Direct
//
//
// and the else clause is
//
//
// Case-3 Printer Handle is Spooled
//
if (!pSpool->pIniJob) {
LeaveSplSem();
SetLastError(ERROR_CAN_NOT_COMPLETE);
SplOutSem();
return(FALSE);
}
if (pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT) {
HANDLE WaitForRead = pSpool->pIniJob->WaitForRead;
PINIPORT pIniPort1 = pSpool->pIniJob->pIniPort;
if (pIniPort1) { // Port may have been deleted by another EndDocPrinter
SPLASSERT(!(pSpool->TypeofHandle & PRINTER_HANDLE_PORT));
// Printer Handle is Direct
pSpool->cRef++;
INCJOBREF(pSpool->pIniJob);
pIniPort1->cRef++;
LeaveSplSem();
if( WaitForRead != INVALID_HANDLE_VALUE ){
WaitForSingleObject(pSpool->pIniJob->WaitForRead, INFINITE);
}
pSpool->pIniJob->cbBuffer = 0;
SetEvent(pSpool->pIniJob->WaitForWrite);
WaitForSingleObject(pIniPort1->Ready, INFINITE);
SplOutSem();
EnterSplSem();
pSpool->cRef--;
pIniPort1->cRef--;
DECJOBREF(pSpool->pIniJob);
if ((pIniPort1->Status & PP_DELETING) && !pIniPort1->cRef)
DeletePortEntry(pIniPort1);
}
} else {
// Printer Handle is Spooled
SPLASSERT(!(pSpool->TypeofHandle & PRINTER_HANDLE_PORT));
SPLASSERT(!(pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT));
#if 0
// Thought to be a performance hit
// so removed from PPC release.
// In the event of a power failure we want to make certain that all
// data for this job has been written to disk
rc = FlushFileBuffers(pSpool->pIniJob->hWriteFile);
if ( !rc ) {
DBGMSG(DBG_WARNING, ("LocalEndDocPrinter FlushFileBuffers failed hWriteFile %x Error %d\n",
pSpool->pIniJob->hWriteFile, GetLastError() ));
}
#endif
if (!CloseHandle(pSpool->pIniJob->hWriteFile)) {
DBGMSG(DBG_WARNING, ("CloseHandle failed %d %d\n", pSpool->pIniJob->hWriteFile, GetLastError()));
} else {
DBGMSG(DBG_TRACE, ("LocalEndDocPrinter: ClosedHandle Success hWriteFile\n" ));
pSpool->pIniJob->hWriteFile = INVALID_HANDLE_VALUE;
}
// Despooling whilst spooling requires us to wake the writing
// thread if it is waiting.
if ( pSpool->pIniJob->WaitForWrite != INVALID_HANDLE_VALUE )
SetEvent(pSpool->pIniJob->WaitForWrite);
}
SPLASSERT(pSpool);
SPLASSERT(pSpool->pIniJob);
// Case 2 - (Direct) and Case 3 - (Spooled) will both execute
// this block of code because both direct and spooled handles
// are first and foremost PRINTER_HANDLE_PRINTER handles
if (pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER) {
SPLASSERT(!(pSpool->TypeofHandle & PRINTER_HANDLE_PORT));
// WARNING
// If pIniJob->Status has JOB_SPOOLING removed and we leave
// the critical section then the scheduler thread will
// Start the job printing. This could cause a problem
// in that the job could be completed and deleted
// before the shadow job is complete. This would lead
// to access violations.
SPLASSERT(pSpool);
SPLASSERT(pSpool->pIniJob);
if (pSpool->pIniJob->Status & JOB_SPOOLING) {
pSpool->pIniJob->Status &= ~JOB_SPOOLING;
pSpool->pIniJob->pIniPrinter->cSpooling--;
}
if (!(pSpool->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DIRECT)) {
//
// Quick fix for Beta 2 - this needs to be cleaned up
//
// LeaveSplSem();
WriteShadowJob(pSpool->pIniJob);
// EnterSplSem();
}
SplInSem();
//
// This line of code is crucial; for timing reasons it
// has been moved from the Direct (Case 2) and the
// Spooled (Case 3) clauses. This decrement is for the
// initial
//
SPLASSERT(pSpool->pIniJob->cRef != 0);
DECJOBREF(pSpool->pIniJob);
if (pSpool->pIniJob->Status & JOB_PENDING_DELETION) {
DBGMSG(DBG_TRACE, ("EndDocPrinter: Deleting Pending Deletion Job\n"));
DeleteJob(pSpool->pIniJob,BROADCAST);
bNotify = FALSE;
} else {
if ( pSpool->pIniJob->Status & JOB_TIMEOUT ) {
pSpool->pIniJob->Status &= ~( JOB_TIMEOUT | JOB_ABANDON );
FreeSplStr(pSpool->pIniJob->pStatus);
pSpool->pIniJob->pStatus = NULL;
}
DBGMSG(DBG_TRACE, ("EndDocPrinter:PRINTER:cRef = %d\n", pSpool->pIniJob->cRef));
CHECK_SCHEDULER();
}
}
if (bNotify && pSpool->pIniJob) {
SetPrinterChange(pSpool->pIniPrinter,
pSpool->pIniJob,
NVJobStatus,
PRINTER_CHANGE_SET_JOB,
pSpool->pIniSpooler);
}
LeaveSplSem();
SplOutSem();
return TRUE;
}
BOOL
PrintingDirectlyToPort(
PSPOOL pSpool,
DWORD Level,
LPBYTE pDocInfo,
LPDWORD pJobId
)
{
PDOC_INFO_1 pDocInfo1=(PDOC_INFO_1)pDocInfo;
BOOL rc;
DWORD Error;
BOOL bPrinttoFile = FALSE;
BOOL bErrorOccurred = FALSE;
PINIMONITOR pIniMonitor = NULL, pIniLangMonitor = NULL;
LPWSTR pPrinterName;
HANDLE hThread = NULL;
DWORD dwThreadId;
DBGMSG(DBG_TRACE, ("PrintingDirectlyToPort: Printing document %ws direct to port\n",
pDocInfo1->pDocName));
if (pDocInfo1 &&
pDocInfo1->pDatatype &&
!ValidRawDatatype(pDocInfo1->pDatatype)) {
DBGMSG(DBG_WARNING, ("Datatype is not RAW\n"));
SetLastError(ERROR_INVALID_DATATYPE);
return FALSE;
}
if (pSpool->pIniPort->Status & PP_MONITOR) {
if ( !(pSpool->pIniPort->Status & PP_FILE) &&
(pSpool->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_ENABLE_BIDI) )
pIniLangMonitor = pSpool->pIniPrinter->pIniDriver->pIniLangMonitor;
do {
//
// This fixes Intergraph's problem -- of wanting to print
// to file but their 3.1 print-processor does not pass
// thru the file name.
//
if (pSpool->pIniJob->Status & JOB_PRINT_TO_FILE) {
if ( pDocInfo1 && !pDocInfo1->pOutputFile ) {
pDocInfo1->pOutputFile = pSpool->pIniJob->pOutputFile;
}
}
// Some monitors (LPRMON) may fail to initialize at startup
// because a driver they are dependent on
SplOutSem();
EnterSplSem();
rc = OpenMonitorPort(pSpool->pIniPort,
pIniLangMonitor,
pSpool->pIniPrinter->pName,
TRUE);
//
// If the port monitor would not work with a lang monitor try to
// use port monitor directly
//
if ( !rc && pIniLangMonitor ) {
rc = OpenMonitorPort(pSpool->pIniPort,
NULL,
pSpool->pIniPrinter->pName,
TRUE);
if ( rc )
pIniLangMonitor = NULL;
}
LeaveSplSem();
if ( rc ) {
if ( pIniLangMonitor )
pIniMonitor = pIniLangMonitor;
else
pIniMonitor = pSpool->pIniPort->pIniMonitor;
SplOutSem();
rc = (*pIniMonitor->fn.pfnStartDocPort)(
pSpool->pIniPort->hPort,
pSpool->pIniPrinter->pName,
pSpool->pIniJob->JobId,
Level, pDocInfo);
}
if ( !rc ) {
Error = GetLastError();
//
// Check for pending deletion first, which prevents the
// dialog from coming up if the user hits Del.
//
if( (pSpool->pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART)) ||
(PromptWriteError( pSpool, &hThread, &dwThreadId ) == IDCANCEL)) {
if( hThread ) {
// See if the thread is still running or dismissed by user.
if( WAIT_TIMEOUT == WaitForSingleObject( hThread, 0 )) {
PostThreadMessage( dwThreadId, WM_QUIT, IDRETRY, 0 );
SplOutSem();
WaitForSingleObject( hThread, INFINITE );
}
CloseHandle( hThread );
hThread = NULL;
}
pSpool->pIniJob->StartDocError = Error;
SetLastError(ERROR_PRINT_CANCELLED);
return FALSE;
}
bErrorOccurred = TRUE;
}
else {
if( hThread ) {
// we have started a message box and now the automatically
// retry has succeeded, we need to kill the message box
// and continue to print.
// See if the thread is still running or dismissed by user.
if( WAIT_TIMEOUT == WaitForSingleObject( hThread, 0 )) {
PostThreadMessage( dwThreadId, WM_QUIT, IDRETRY, 0 );
SplOutSem();
WaitForSingleObject( hThread, INFINITE );
}
CloseHandle( hThread );
hThread = NULL;
}
}
} while (!rc);
//
// If an error occurred, we set some error bits in the job
// status field. Clear them now since the StartDoc succeeded.
//
if( bErrorOccurred ){
EnterSplSem();
ClearJobError( pSpool->pIniJob );
LeaveSplSem();
}
pSpool->Status |= SPOOL_STATUS_STARTDOC;
if ( pIniLangMonitor ) {
pSpool->pIniJob->Status |= JOB_TRUE_EOJ;
}
if ( pSpool->pIniJob->pIniPrinter->pSepFile &&
*pSpool->pIniJob->pIniPrinter->pSepFile) {
DoSeparator(pSpool);
}
// Let the application's thread return from PrintingDirect:
DBGMSG(DBG_PORT, ("PrintingDirectlyToPort: Calling SetEvent( %x )\n",
pSpool->pIniJob->StartDocComplete));
if(pSpool->pIniJob->StartDocComplete) {
if ( !SetEvent( pSpool->pIniJob->StartDocComplete ) ) {
DBGMSG( DBG_WARNING, ("SetEvent( %x ) failed: Error %d\n",
pSpool->pIniJob->StartDocComplete,
GetLastError() ));
}
}
} else {
DBGMSG(DBG_TRACE, ("Port has no monitor: Calling StartDocPrinter or maybe printing to file\n"));
EnterSplSem();
bPrinttoFile = (pDocInfo1 && IsGoingToFile(pDocInfo1->pOutputFile,
pSpool->pIniSpooler));
LeaveSplSem();
if (bPrinttoFile) {
HANDLE hFile = INVALID_HANDLE_VALUE;
DBGMSG(DBG_TRACE, ("Port has no monitor: Printing to File %ws\n", pDocInfo1->pOutputFile));
hFile = CreateFile( pDocInfo1->pOutputFile,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL );
if (hFile == INVALID_HANDLE_VALUE) {
DBGMSG(DBG_TRACE, ("Port has no monitor: File open failed\n"));
rc = FALSE;
} else {
DBGMSG(DBG_TRACE, ("Port has no monitor: File open succeeded\n"));
SetEndOfFile(hFile);
pSpool->hFile = hFile;
pSpool->Status |= SPOOL_STATUS_PRINT_FILE;
//
// Have to set JobId to some non zero value otherwise
// StartDocPrinter expects the JobId to be off the pSpool->pIniJob
// We have none so we'll access violate!!
//
*pJobId = TRUE;
rc = TRUE;
}
} else {
DBGMSG(DBG_TRACE, ("Port has no monitor: Calling StartDocPrinter\n"));
*pJobId = StartDocPrinter(pSpool->hPort, Level, pDocInfo);
rc = *pJobId != 0;
}
if (!rc) {
DBGMSG(DBG_WARNING, ("StartDocPrinter failed: Error %d\n", GetLastError()));
}
}
SPLASSERT( hThread == NULL );
return rc;
}
DWORD
WriteToPrinter(
PSPOOL pSpool,
LPBYTE pByte,
DWORD cbBuf
)
{
WaitForSingleObject(pSpool->pIniJob->WaitForRead, INFINITE);
cbBuf = pSpool->pIniJob->cbBuffer = min(cbBuf, pSpool->pIniJob->cbBuffer);
memcpy(pSpool->pIniJob->pBuffer, pByte, cbBuf);
SetEvent(pSpool->pIniJob->WaitForWrite);
return cbBuf;
}
DWORD
ReadFromPrinter(
PSPOOL pSpool,
LPBYTE pBuf,
DWORD cbBuf
)
{
pSpool->pIniJob->pBuffer = pBuf;
pSpool->pIniJob->cbBuffer = cbBuf;
SetEvent(pSpool->pIniJob->WaitForRead);
WaitForSingleObject(pSpool->pIniJob->WaitForWrite, INFINITE);
return pSpool->pIniJob->cbBuffer;
}
BOOL
ValidRawDatatype(
LPWSTR pszDatatype)
{
if (!pszDatatype || _wcsnicmp(pszDatatype, szRaw, 3))
return FALSE;
return TRUE;
}