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

1292 lines
34 KiB
C

/*++
Copyright (c) 1990 - 1996 Microsoft Corporation
Module Name:
port.c
Abstract:
This module contains functions to control port threads
PrintDocumentThruPrintProcessor
CreatePortThread
DestroyPortThread
PortThread
Author:
Dave Snipp (DaveSn) 15-Mar-1991
Revision History:
KrishnaG 3-Feb-1991 - moved all monitor based functions to monitor.c
Matthew Felton (mattfe) Feb 1994 Added OpenMonitorPort CloseMonitorPort
--*/
#include <precomp.h>
WCHAR *szFilePort = L"FILE:";
VOID
PrintDocumentThruPrintProcessor(
PINIPORT pIniPort,
PPRINTPROCESSOROPENDATA pOpenData
);
// ShutdownPorts
//
// Called when the DLL_PROCESS_DETATCH is called
// Close all portthreads
// Close all monitorports
VOID
ShutdownPorts(
PINISPOOLER pIniSpooler
)
{
PINIPORT pIniPort;
if ( pIniSpooler == NULL ) {
return;
}
EnterSplSem();
SplInSem();
pIniPort = pIniSpooler->pIniPort;
while(pIniPort) {
DestroyPortThread(pIniPort, TRUE);
//
// Don't close monitor port since DLL_ATTACH may have been called
//
// CloseMonitorPort(pIniPort);
RemoveDeviceName(pIniPort);
pIniPort = pIniPort->pNext;
}
LeaveSplSem();
return;
}
BOOL
OpenMonitorPort(
PINIPORT pIniPort,
PINIMONITOR pIniLangMonitor,
LPWSTR pszPrinterName,
BOOL bWaitForEvent
)
{
BOOL bRet = TRUE;
LPMONITOR pMonitor = NULL;
SplInSem();
SPLASSERT (pIniPort != NULL || pIniPort->signature == IPO_SIGNATURE);
//
// If going to file or no monitor associated do not have to open
//
if ( (pIniPort->Status & PP_FILE) || !(pIniPort->Status & PP_MONITOR) ) {
return TRUE;
}
//
// If a LM is passed and it does not have an OpenPortEx can't use it
//
if ( pIniLangMonitor && !pIniLangMonitor->fn.pfnOpenPortEx )
pIniLangMonitor = NULL;
//
// The port is already open by the correct monitor?
//
if ( pIniLangMonitor == pIniPort->pIniLangMonitor && pIniPort->hPort )
return TRUE;
INCPORTREF(pIniPort);
LeaveSplSem();
SplOutSem();
if ( bWaitForEvent &&
WAIT_OBJECT_0 != WaitForSingleObject(pIniPort->hWaitToOpenOrClose,
INFINITE) ) {
DBGMSG(DBG_ERROR,
("OpenMonitorPort: WaitForSingleObject failed with error %d\n",
GetLastError()));
EnterSplSem();
DECPORTREF(pIniPort);
return FALSE;
}
EnterSplSem();
if ( pIniPort->hPort ) {
//
// If the port is already open by the correct monitor return it
//
if ( pIniLangMonitor == pIniPort->pIniLangMonitor )
goto Cleanup;
if ( !CloseMonitorPort(pIniPort, FALSE) ) {
DBGMSG(DBG_WARNING,
("CloseMonitorPort failed for %ws -- LastError%d\n",
pIniPort->pName, GetLastError()));
bRet = FALSE;
goto Cleanup;
}
}
SPLASSERT(!pIniPort->hPort);
LeaveSplSem();
SplOutSem();
DBGMSG(DBG_TRACE,
("OpenPort port %ws (IniPort : %x)\n", pIniPort->pName, pIniPort));
if ( pIniLangMonitor ) {
SPLASSERT(pIniPort->pIniMonitor);
//
// Make sure the MONITOR struct is same size as pNewIniMonitor
// if not it could access beyond the struct size
//
if ( pIniPort->pIniMonitor->dwMonitorSize
>= pIniLangMonitor->dwMonitorSize ) {
pMonitor = &pIniPort->pIniMonitor->fn;
} else {
pMonitor = (LPMONITOR) AllocSplMem(pIniLangMonitor->dwMonitorSize);
if ( !pMonitor )
goto Cleanup;
CopyMemory((LPBYTE)pMonitor,
(LPBYTE)&pIniPort->pIniMonitor->fn,
sizeof(pIniPort->pIniMonitor->dwMonitorSize));
}
bRet = (*pIniLangMonitor->fn.pfnOpenPortEx)(pIniPort->pName,
pszPrinterName,
&pIniPort->hPort,
pMonitor);
} else {
bRet = (*pIniPort->pIniMonitor->fn.pfnOpenPort)(pIniPort->pName,
&pIniPort->hPort);
}
EnterSplSem();
if ( bRet && pIniPort->hPort ) {
if ( pIniLangMonitor )
pIniPort->pIniLangMonitor = pIniLangMonitor;
DBGMSG(DBG_TRACE, ("OpenPort success for %ws\n", pIniPort->pName));
} else {
if ( bRet || pIniPort->hPort )
DBGMSG(DBG_WARNING,
("OpenPort: unexpected return %d with hPort %x\n",
bRet, pIniPort->hPort));
bRet = FALSE;
pIniPort->hPort = NULL;
DBGMSG(DBG_WARNING, ("OpenPort failed %ws error %d\n",
pIniPort->pName, GetLastError()));
}
Cleanup:
SplInSem();
if ( bWaitForEvent)
SetEvent(pIniPort->hWaitToOpenOrClose);
if ( pMonitor && pMonitor != &pIniPort->pIniMonitor->fn )
FreeSplMem(pMonitor);
if ( !bRet )
DECPORTREF(pIniPort);
return bRet;
}
BOOL
CloseMonitorPort(
PINIPORT pIniPort,
BOOL bWaitForEvent
)
{
BOOL bRet = TRUE;
PINIMONITOR pIniMonitor;
SPLASSERT ( pIniPort != NULL ||
pIniPort->signature == IPO_SIGNATURE );
LeaveSplSem();
SplOutSem();
if ( bWaitForEvent &&
WAIT_OBJECT_0 != WaitForSingleObject(pIniPort->hWaitToOpenOrClose,
INFINITE) ) {
DBGMSG(DBG_ERROR,
("CloseMonitorPort: WaitForSingleObject failed with error %d\n", GetLastError()));
EnterSplSem();
return FALSE;
}
EnterSplSem();
//
// If going to file hPort should be NULL
//
SPLASSERT(!(pIniPort->Status & PP_FILE) || !pIniPort->hPort);
if ( !pIniPort->hPort ) {
goto Cleanup;
}
if ( pIniPort->pIniLangMonitor )
pIniMonitor = pIniPort->pIniLangMonitor;
else
pIniMonitor = pIniPort->pIniMonitor;
//
// Only Close the Port Once
//
SPLASSERT ( pIniMonitor && pIniPort->cRef >= 1 );
LeaveSplSem();
SplOutSem();
DBGMSG(DBG_TRACE, ("Close Port %ws -- %d\n", pIniPort->pName, pIniPort->cRef));
bRet = (*pIniMonitor->fn.pfnClosePort)( pIniPort->hPort );
EnterSplSem();
if ( bRet ) {
pIniPort->hPort = NULL;
DECPORTREF(pIniPort);
if ( pIniMonitor == pIniPort->pIniLangMonitor )
pIniPort->pIniLangMonitor = NULL;
} else {
//
// When net stop spooler is done the monitor could have been
// called to shutdown (hpmon does it)
//
DBGMSG(DBG_WARNING,
("ClosePort failed for %ws -- LastError%d\n", pIniPort->pName, GetLastError()));
}
Cleanup:
SplInSem();
if ( bWaitForEvent )
SetEvent(pIniPort->hWaitToOpenOrClose);
return bRet;
}
BOOL
CreatePortThread(
PINIPORT pIniPort
)
{
DWORD ThreadId;
BOOL bReturnValue = FALSE;
SplInSem();
SPLASSERT (( pIniPort != NULL) &&
( pIniPort->signature == IPO_SIGNATURE));
// Don't bother creating a thread for ports that don't have a monitor:
if (!(pIniPort->Status & PP_MONITOR))
return TRUE;
if ( pIniPort->Status & PP_THREADRUNNING)
return TRUE;
try {
pIniPort->Semaphore = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( pIniPort->Semaphore == NULL )
leave;
pIniPort->Ready = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( pIniPort->Ready == NULL ) {
leave;
}
pIniPort->Status |= PP_RUNTHREAD;
pIniPort->hPortThread = CreateThread(NULL, 16*1024,
(LPTHREAD_START_ROUTINE)PortThread,
pIniPort,
0, &ThreadId);
if( pIniPort->hPortThread == NULL ) {
pIniPort->Status &= ~PP_RUNTHREAD;
leave;
}
if ( !SetThreadPriority(pIniPort->hPortThread, dwPortThreadPriority) ) {
DBGMSG(DBG_WARNING, ("CreatePortThread - Setting thread priority failed %d\n", GetLastError()));
}
LeaveSplSem();
// Make CreatePortThread Synchronous
WaitForSingleObject( pIniPort->Ready, INFINITE );
EnterSplSem();
SplInSem();
pIniPort->Status |= PP_THREADRUNNING;
bReturnValue = TRUE;
} finally {
if ( !bReturnValue ) {
if ( pIniPort->Semaphore != NULL ) {
CloseHandle( pIniPort->Semaphore );
pIniPort->Semaphore = NULL;
}
if ( pIniPort->Ready != NULL ) {
CloseHandle( pIniPort->Ready );
pIniPort->Ready = NULL;
}
}
}
return bReturnValue;
}
BOOL
DestroyPortThread(
PINIPORT pIniPort,
BOOL bShutdown
)
{
SplInSem();
// PortThread checks for PP_RUNTHREAD
// and exits if it is not set.
pIniPort->Status &= ~PP_RUNTHREAD;
if (!SetEvent(pIniPort->Semaphore)) {
return FALSE;
}
if( pIniPort->hPortThread != NULL) {
LeaveSplSem();
if ( WaitForSingleObject( pIniPort->hPortThread, INFINITE) == WAIT_FAILED ) {
EnterSplSem();
return FALSE;
}
EnterSplSem();
}
if (pIniPort->hPortThread != NULL) {
CloseHandle(pIniPort->hPortThread);
pIniPort->hPortThread = NULL;
}
//
// The port may have been changed while the printer was printing.
// Thus when the port thread finally goes away now is the time to
// close the monitor. However we can't call the monitor during shutdown
// since DLL_DETACH may already have been issued to the monitor dll
//
if ( !pIniPort->cPrinters && !bShutdown)
CloseMonitorPort(pIniPort, TRUE);
return TRUE;
}
VOID
RemoveIniPortFromIniJob(
PINIJOB pIniJob,
PINIPORT pIniPort
)
{
NOTIFYVECTOR NotifyVector;
SplInSem();
SPLASSERT(pIniJob &&
pIniJob->signature == IJ_SIGNATURE &&
pIniJob->pIniPort);
SPLASSERT( pIniJob->pIniPort == pIniPort );
pIniPort->cJobs--;
pIniJob->pIniPort = NULL;
SPLASSERT( pIniJob->Status & JOB_DESPOOLING );
// Chained Jobs
// For a Chained Master Job do not remove JOB_DESPOOLING
// since we don't want the scheduler to reschedule this
// to another port
if ( pIniPort->pIniJob != pIniJob ) {
// Normal Path
// When NOT a chained job.
pIniJob->Status &= ~JOB_DESPOOLING;
COPYNV(NotifyVector, NVJobStatus);
NotifyVector[JOB_NOTIFY_TYPE] |= BIT(I_JOB_PORT_NAME);
SetPrinterChange( pIniJob->pIniPrinter,
pIniJob,
NotifyVector,
PRINTER_CHANGE_SET_JOB,
pIniJob->pIniPrinter->pIniSpooler);
}
// RestartJob() doesn't remove JOB_PRINTED or JOB_BLOCKED_DEVQ
// or JOB_DESPOOLING if the despooling bit is on
// this is to avoid problems where we have completed "Printing"
// the job via a print processor and now the port thread is logging
// the job printed and sending an alert message.
if ( pIniJob->Status & JOB_RESTART )
pIniJob->Status &= ~( JOB_PRINTED | JOB_BLOCKED_DEVQ);
}
DWORD
PortThread(
PINIPORT pIniPort
)
{
DWORD rc;
PRINTPROCESSOROPENDATA OpenData;
PINIJOB pIniJob;
DWORD NextJobId = 0;
DWORD Position;
DWORD dwDevQueryPrint = 0;
DWORD dwJobDirect = 0;
DWORD dwDevQueryPrintStatus = 0;
WCHAR ErrorString[MAX_PATH];
EnterSplSem();
SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
if ( pIniPort->Status & PP_MONITOR ) {
if ( pIniPort->Status & PP_FILE ) {
rc = ( *pIniPort->pIniMonitor->fn.pfnOpenPort)(L"FILE:", &pIniPort->hPort );
DBGMSG(DBG_TRACE, (" After opening the file pseudo monitor port %d\n", rc));
} else {
// LPRMON returns NULL ( fails and expect us to open it again
// inside PrintingDirectlyToPort, so for now remove this assert
// since OpenMonitorPort was added to PrintingDirectlyToPort
// SPLASSERT( pIniPort->hPort != NULL );
}
// BUGBUG - No check is made of rc after the port is opened, it might have failed.
// mattfe Jan31 94
// Also there is no reason to open a monitor if you are going to print to a
// file, it should be removed.
}
while (TRUE) {
SplInSem();
SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
DBGMSG(DBG_TRACE, ("Re-entering the Port Loop -- will blow away any Current Job\n"));
pIniPort->Status |= PP_WAITING;
SetEvent( pIniPort->Ready );
CHECK_SCHEDULER();
DBGMSG( DBG_PORT, ("Port %ws: WaitForSingleObject( %x )\n",
pIniPort->pName, pIniPort->Semaphore ) );
LeaveSplSem();
SplOutSem();
//
// Any modification to the pIniPort structure by other threads
// can be done only at this point.
//
rc = WaitForSingleObject( pIniPort->Semaphore, INFINITE );
EnterSplSem();
SplInSem();
SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
DBGMSG( DBG_PORT, ("Port %ws: WaitForSingleObject( %x ) returned\n",
pIniPort->pName, pIniPort->Semaphore));
if ( !( pIniPort->Status & PP_RUNTHREAD ) ) {
DBGMSG(DBG_TRACE, ("Thread for Port %ws Closing Down\n", pIniPort->pName));
pIniPort->Status &= ~(PP_THREADRUNNING | PP_WAITING);
CloseHandle( pIniPort->Semaphore );
pIniPort->Semaphore = NULL;
CloseHandle( pIniPort->Ready );
pIniPort->Ready = NULL;
if ( pIniPort->Status & PP_FILE ) {
rc = (*pIniPort->pIniMonitor->fn.pfnClosePort)(pIniPort->hPort);
pIniPort->hPort = NULL;
DBGMSG(DBG_TRACE, (" After closing the file pseudo monitor port\n %d\n"));
}
LeaveSplSem();
SplOutSem();
ExitThread (FALSE);
}
ResetEvent( pIniPort->Ready );
//
// Bad assumption -- that at this point we definitely have a Job
//
if ( ( pIniJob = pIniPort->pIniJob ) &&
pIniPort->pIniJob->pIniPrintProc ) {
SPLASSERT( pIniJob->signature == IJ_SIGNATURE );
SPLASSERT( pIniJob->Status & JOB_DESPOOLING );
DBGMSG(DBG_PORT, ("Port %ws: received job\n", pIniPort->pName));
SetCurrentSid(pIniJob->hToken);
SPLASSERT(pIniJob->cRef != 0);
DBGMSG(DBG_PORT, ("PortThread(1):cRef = %d\n", pIniJob->cRef));
// BUGBUG These allocations could fail.
OpenData.pDevMode = AllocDevMode(pIniJob->pDevMode);
OpenData.pDatatype = AllocSplStr(pIniJob->pDatatype);
OpenData.pParameters = AllocSplStr(pIniJob->pParameters);
OpenData.JobId = pIniJob->JobId;
OpenData.pDocumentName = AllocSplStr(pIniJob->pDocument);
OpenData.pOutputFile = AllocSplStr(pIniJob->pOutputFile);
OpenData.pPrinterName = AllocSplStr(pIniJob->pIniPrinter->pName);
dwDevQueryPrint = pIniJob->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_ENABLE_DEVQ;
if ((pIniJob->Status & JOB_DIRECT) ||
((pIniJob->Status & JOB_TYPE_ADDJOB) &&
ValidRawDatatype(pIniJob->pDatatype))) {
dwJobDirect = 1;
}
// If we are restarting to print a document
// clear its counters and remove the restart flag
if ( pIniJob->Status & JOB_RESTART ) {
pIniJob->Status &= ~JOB_RESTART;
pIniJob->cbPrinted = 0;
pIniJob->cPagesPrinted = 0;
}
LeaveSplSem();
SplOutSem();
pIniJob->dwReboots++;
WriteShadowJob(pIniJob);
if ( ( dwDevQueryPrintStatus = CallDevQueryPrint( OpenData.pPrinterName,
OpenData.pDevMode,
ErrorString, MAX_PATH,
dwDevQueryPrint, dwJobDirect ) ) ) {
PrintDocumentThruPrintProcessor( pIniPort, &OpenData );
}
SplOutSem();
EnterSplSem();
SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
SPLASSERT( pIniPort->pIniJob != NULL );
SPLASSERT( pIniJob == pIniPort->pIniJob);
SPLASSERT( pIniJob->signature == IJ_SIGNATURE );
//
// Chained Jobs
// If we have a chain of jobs, we now need to find the next job in the chain
// and make sure its printed to the same port.
//
if (!( pIniJob->Status & ( JOB_PENDING_DELETION | JOB_RESTART )) &&
( pIniJob->pCurrentIniJob != NULL ) &&
( pIniJob->pCurrentIniJob->NextJobId != 0 )) {
// Follow the Chained Job to the Next Job
// Look at scheduler to see where it picks up this job and assigns it back
// to this port thread.
pIniJob->pCurrentIniJob = FindJob( pIniJob->pIniPrinter, pIniJob->pCurrentIniJob->NextJobId, &Position );
if ( pIniJob->pCurrentIniJob == NULL ) {
pIniPort->pIniJob = NULL;
DBGMSG( DBG_WARNING, ("PortThread didn't find NextJob\n"));
} else {
SPLASSERT( pIniJob->pCurrentIniJob->signature == IJ_SIGNATURE );
DBGMSG( DBG_WARNING, ("PortThread completed JobId %d, NextJobId %d\n", pIniJob->JobId,
pIniJob->pCurrentIniJob->JobId ));
}
} else {
//
// Nothing More in Chain
//
pIniJob->pCurrentIniJob = NULL;
pIniPort->pIniJob = NULL;
}
DBGMSG(DBG_PORT, ("PortThread job has now printed - status:0x%0x\n", pIniJob->Status));
FreeDevMode(OpenData.pDevMode);
FreeSplStr(OpenData.pDatatype);
FreeSplStr(OpenData.pParameters);
FreeSplStr(OpenData.pDocumentName);
FreeSplStr(OpenData.pOutputFile);
FreeSplStr(OpenData.pPrinterName);
// SPLASSERT( pIniJob->Time != 0 );
pIniJob->Time = GetTickCount() - pIniJob->Time;
if (!dwDevQueryPrintStatus) {
DBGMSG(DBG_PORT, ("PortThread Job has not printed because of DevQueryPrint failed\n"));
pIniJob->Status |= JOB_BLOCKED_DEVQ;
SPLASSERT( !(pIniJob->Status & JOB_PRINTED));
pIniJob->Time = 0;
FreeSplStr( pIniJob->pStatus );
pIniJob->pStatus = AllocSplStr(ErrorString);
SetPrinterChange(pIniJob->pIniPrinter,
pIniJob,
NVJobStatusAndString,
PRINTER_CHANGE_SET_JOB,
pIniJob->pIniPrinter->pIniSpooler );
} else if ( !( pIniJob->Status & JOB_TIMEOUT ) ) {
//
// Only Log the event and send a popup if the last in the chain
//
if ( !(pIniJob->Status & JOB_RESTART) &&
pIniJob->pCurrentIniJob == NULL ) {
pIniJob->Status |= JOB_PRINTED;
//
// Moved this code from DeleteJob to fix the Shadow Jobs
// access violating. Bump up the reference count so that
// the job cannot be deleted from under you.
//
INCJOBREF(pIniJob);
LeaveSplSem();
// For Remote NT Jobs cPagesPrinted and cTotalPagesPrinted
// are NOT updated since we are getting RAW data. So we
// use the cPages field instead.
if (pIniJob->cPagesPrinted == 0) {
pIniJob->cPagesPrinted = pIniJob->cPages;
pIniJob->pIniPrinter->cTotalPagesPrinted += pIniJob->cPages;
}
LogJobPrinted(pIniJob);
SendJobAlert(pIniJob);
EnterSplSem();
DECJOBREF(pIniJob);
}
}
SplInSem();
DBGMSG(DBG_PORT, ("PortThread(2):cRef = %d\n", pIniJob->cRef));
// Hi End Print Shops like to keep around jobs after they have
// completed. They do this so they can print a proof it and then
// print it again for the final run. Spooling the job again may take
// several hours which they want to avoid.
// Even if KEEPPRINTEDJOBS is set they can still manually delete
// the job via printman.
if (( pIniJob->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS ) ||
( pIniJob->Status & JOB_TIMEOUT ) ) {
pIniJob->Status &= ~JOB_PENDING_DELETION;
pIniJob->cbPrinted = 0;
pIniJob->cPagesPrinted = 0;
LeaveSplSem();
SplOutSem();
--pIniJob->dwReboots;
WriteShadowJob( pIniJob );
SplOutSem();
EnterSplSem();
SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
SPLASSERT( pIniJob->signature == IJ_SIGNATURE );
}
SplInSem();
SPLASSERT( pIniJob->cRef != 0 );
DECJOBREF(pIniJob);
RemoveIniPortFromIniJob(pIniJob, pIniPort);
DeleteJobCheck(pIniJob);
} else {
//
// !! VERIFY !!
//
SPLASSERT(pIniJob != NULL);
if (pIniJob != NULL) {
DBGMSG(DBG_PORT, ("Port %ws: deleting job\n", pIniPort->pName));
// SPLASSERT( pIniJob->Time != 0 );
pIniJob->Time = GetTickCount() - pIniJob->Time;
pIniJob->Status |= JOB_PRINTED;
CloseHandle( pIniJob->hWriteFile );
pIniJob->hWriteFile = INVALID_HANDLE_VALUE;
DBGMSG(DBG_PORT, ("Port %ws - calling DeleteJob because PrintProcessor wasn't available\n"));
RemoveIniPortFromIniJob(pIniJob, pIniPort);
DeleteJob(pIniJob,BROADCAST);
}
}
SetCurrentSid(NULL);
DBGMSG(DBG_PORT,("Returning back to pickup a new job or to delete the PortThread\n"));
}
return 0;
}
VOID
PrintDocumentThruPrintProcessor(
PINIPORT pIniPort,
PPRINTPROCESSOROPENDATA pOpenData
)
/*++
Routine Description:
Print the document associated with pIniPort on the print
processor.
Status of pIniPort->Status = PP_RUNTHREAD
PP_THREADRUNNING
PP_MONITOR
~PP_WAITING
NOTE: If PrintProc->Open is called and succeeds, PrintProc->Close
must be called to cleanup.
Arguments:
Return Value:
--*/
{
PINIJOB pIniJob = pIniPort->pIniJob;
WCHAR szSpecialPortorPrinterName[MAX_UNC_PRINTER_NAME + PRINTER_NAME_SUFFIX_MAX];
BOOL bJobError = FALSE;
NOTIFYVECTOR NotifyVector;
DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor pIniPort %x pOpenData %x\n", pIniPort, pOpenData));
COPYNV(NotifyVector, NVJobStatus);
//
// Now create the port name, so that we can do the
// secret open printer. the printer name will be
// "FILE:, Port" and this will open a PRINTER_HANDLE_PORT
// If we fail, then if the app thread may be waiting for
// the pIniJob->StartDocComplete to be set, which would
// ordinarily be done in the StartDocPrinter of the port.
// We will do this little courtesy,
//
wsprintf(szSpecialPortorPrinterName, L"%ws, Port", pIniPort->pName);
DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor Attempting PrintProcessor Open on %ws\n", szSpecialPortorPrinterName ));
if (!(pIniPort->hProc = (HANDLE)(*pIniJob->pIniPrintProc->Open)
(szSpecialPortorPrinterName, pOpenData))) {
DBGMSG( DBG_WARNING, ("PrintDocumentThruPrintProcessor Failed Open error %d\n", GetLastError() ));
EnterSplSem();
//
// App might be waiting for the StartDoc to Complete
//
if ( pIniJob->StartDocComplete ) {
SetEvent(pIniJob->StartDocComplete);
}
bJobError = TRUE;
LeaveSplSem();
goto Complete;
}
EnterSplSem();
pIniJob->Status |= JOB_PRINTING;
pIniJob->Time = GetTickCount();
NotifyVector[JOB_NOTIFY_TYPE] |= BIT(I_JOB_PORT_NAME);
SetPrinterChange(pIniJob->pIniPrinter,
pIniJob,
NotifyVector,
PRINTER_CHANGE_SET_JOB,
pIniJob->pIniPrinter->pIniSpooler);
LeaveSplSem();
//
// Create Special Name "PrinterName, Job xxx"
//
wsprintf(szSpecialPortorPrinterName,
L"%ws, Job %d",
pIniJob->pIniPrinter->pName,
pIniJob->JobId);
DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor calling Print hProc %x file %ws\n",
pIniPort->hProc, szSpecialPortorPrinterName ));
if (!(*pIniJob->pIniPrintProc->Print)(pIniPort->hProc, szSpecialPortorPrinterName)) {
DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor Print hProc %x Error %d\n",
pIniPort->hProc, GetLastError() ));
EnterSplSem();
if ( pIniJob->StartDocComplete ) {
SetEvent( pIniJob->StartDocComplete );
}
bJobError = TRUE;
LeaveSplSem();
} else {
DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor Print hProc %x %ws Success\n",
pIniPort->hProc, szSpecialPortorPrinterName ));
}
//
// Now close the print processor.
//
EnterSplSem();
SPLASSERT( pIniPort->hProc != NULL );
DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor calling Close hProc %x\n", pIniPort->hProc ));
pIniJob->Status &= ~JOB_PRINTING;
LeaveSplSem();
if (!(pIniJob->pIniPrintProc->InCriticalSection & PRINTPROC_CLOSE)) {
EnterCriticalSection(&pIniJob->pIniPrintProc->CriticalSection);
pIniJob->pIniPrintProc->InCriticalSection |= PRINTPROC_CLOSE;
if ( !(*pIniJob->pIniPrintProc->Close)( pIniPort->hProc ) ) {
DBGMSG( DBG_WARNING, ("PrintDocumentThruPrintProcessor failed Close hProc %x Error %d\n",
pIniPort->hProc, GetLastError() ));
}
pIniPort->hProc = NULL;
pIniJob->pIniPrintProc->InCriticalSection &= ~PRINTPROC_CLOSE;
LeaveCriticalSection(&pIniJob->pIniPrintProc->CriticalSection);
}
Complete:
EnterSplSem();
//
// If the job had an error, mark it pending deletion. The port monitor
// may not do this if EndDocPort was never called.
//
if ( !(pIniJob->Status & JOB_RESTART) && bJobError ){
pIniJob->Status |= JOB_PENDING_DELETION;
}
LeaveSplSem();
}
VOID
UpdatePortStatusForAllPrinters(
PINIPORT pIniPort
)
/*++
Routine Description:
This routine is called when an IniPorts status changed so that we go
through each printer connected to the port and update their port status
Arguments:
pIniPort - Port whose status chanegd
Return Value:
Nothing
--*/
{
PINIPRINTER pIniPrinter;
PINIPORT pIniPrinterPort;
DWORD dwIndex1, dwIndex2, dwPortStatus, dwSeverity;
for ( dwIndex1 = 0 ; dwIndex1 < pIniPort->cPrinters ; ++dwIndex1 ) {
pIniPrinter = pIniPort->ppIniPrinter[dwIndex1];
dwSeverity = 0;
dwPortStatus = 0;
//
// Pick the most severe status associated with all ports
//
for ( dwIndex2 = 0 ; dwIndex2 < pIniPrinter->cPorts ; ++dwIndex2 ) {
pIniPrinterPort = pIniPrinter->ppIniPorts[dwIndex2];
if ( pIniPrinterPort->Status & PP_ERROR ) {
dwSeverity = PP_ERROR;
dwPortStatus = PortToPrinterStatus(pIniPrinterPort->PrinterStatus);
break; // no need to go thru rest of the ports for this printer
} else if ( pIniPrinterPort->Status & PP_WARNING ) {
if ( dwSeverity != PP_WARNING ) {
dwSeverity = PP_WARNING;
dwPortStatus = PortToPrinterStatus(pIniPrinterPort->PrinterStatus);
}
} else if ( pIniPrinterPort->Status & PP_INFORMATIONAL ) {
if ( dwSeverity == 0 ) {
dwSeverity = PP_INFORMATIONAL;
dwPortStatus = PortToPrinterStatus(pIniPrinterPort->PrinterStatus);
}
}
}
if ( pIniPrinter->PortStatus != dwPortStatus ) {
pIniPrinter->PortStatus = dwPortStatus;
SetPrinterChange(pIniPrinter,
NULL,
NVPrinterStatus,
PRINTER_CHANGE_SET_PRINTER,
pIniPrinter->pIniSpooler);
}
}
}
//
// Table is by port status values in winspool.h
//
DWORD PortToPrinterStatusMappings[] = {
0,
PRINTER_STATUS_OFFLINE,
PRINTER_STATUS_PAPER_JAM,
PRINTER_STATUS_PAPER_OUT,
PRINTER_STATUS_OUTPUT_BIN_FULL,
PRINTER_STATUS_PAPER_PROBLEM,
PRINTER_STATUS_NO_TONER,
PRINTER_STATUS_DOOR_OPEN,
PRINTER_STATUS_USER_INTERVENTION,
PRINTER_STATUS_OUT_OF_MEMORY,
PRINTER_STATUS_TONER_LOW,
PRINTER_STATUS_WARMING_UP,
PRINTER_STATUS_POWER_SAVE,
};
BOOL
LocalSetPort(
LPWSTR pszName,
LPWSTR pszPortName,
DWORD dwLevel,
LPBYTE pPortInfo
)
{
PINISPOOLER pIniSpooler = pLocalIniSpooler;
PINIPORT pIniPort;
PPORT_INFO_3 pPortInfo3 = (PPORT_INFO_3) pPortInfo;
DWORD dwLastError = ERROR_SUCCESS;
DWORD dwNewStatus, dwOldStatus;
BOOL bJobStatusChanged = FALSE;
if ( !MyName(pszName, pIniSpooler) ) {
return FALSE;
}
if ( !ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
SERVER_ACCESS_ENUMERATE,
NULL, pIniSpooler )) {
return FALSE;
}
EnterSplSem();
pIniPort = FindPort(pszPortName);
if ( !pIniPort ) {
dwLastError = ERROR_UNKNOWN_PORT;
goto Cleanup;
}
if ( dwLevel != 3 ) {
dwLastError = ERROR_INVALID_LEVEL;
goto Cleanup;
}
if ( !pPortInfo ) {
dwLastError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
switch (pPortInfo3->dwSeverity) {
case 0:
if ( pPortInfo3->dwStatus || pPortInfo3->pszStatus ) {
dwLastError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
dwNewStatus = 0;
break;
case PORT_STATUS_TYPE_ERROR:
dwNewStatus = PP_ERROR;
break;
case PORT_STATUS_TYPE_WARNING:
dwNewStatus = PP_WARNING;
break;
case PORT_STATUS_TYPE_INFO:
dwNewStatus = PP_INFORMATIONAL;
break;
default:
dwLastError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
dwOldStatus = pIniPort->Status;
//
// Clear old status
//
pIniPort->PrinterStatus = 0;
pIniPort->Status &= ~(PP_ERROR | PP_WARNING | PP_INFORMATIONAL);
if ( pIniPort->pszStatus ) {
//
// If the job currently has the same status as port free it
//
if ( pIniPort->pIniJob &&
pIniPort->pIniJob->pStatus &&
!wcscmp(pIniPort->pIniJob->pStatus, pIniPort->pszStatus) ) {
FreeSplStr(pIniPort->pIniJob->pStatus);
pIniPort->pIniJob->pStatus = NULL;
bJobStatusChanged = TRUE;
}
FreeSplStr(pIniPort->pszStatus);
pIniPort->pszStatus = NULL;
}
//
// If string field is used for status use it, else look at dwStatus
//
if ( pPortInfo3->pszStatus && *pPortInfo3->pszStatus ) {
pIniPort->pszStatus = AllocSplStr(pPortInfo3->pszStatus);
if ( !pIniPort->pszStatus ) {
dwLastError = GetLastError();
goto Cleanup;
}
if ( pIniPort->pIniJob && !pIniPort->pIniJob->pStatus ) {
pIniPort->pIniJob->pStatus = AllocSplStr(pIniPort->pszStatus);
bJobStatusChanged = TRUE;
}
} else {
//
// If we add new entries to winspool.h they should be added here too
//
if ( pPortInfo3->dwStatus >=
sizeof(PortToPrinterStatusMappings)/sizeof(PortToPrinterStatusMappings[0]) ) {
dwLastError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
pIniPort->PrinterStatus = pPortInfo3->dwStatus;
}
if( bJobStatusChanged ){
SetPrinterChange( pIniPort->pIniJob->pIniPrinter,
pIniPort->pIniJob,
NVJobStatusString,
PRINTER_CHANGE_SET_JOB,
pIniPort->pIniJob->pIniPrinter->pIniSpooler );
}
pIniPort->Status |= dwNewStatus;
UpdatePortStatusForAllPrinters(pIniPort);
if ( (dwOldStatus & PP_ERROR) &&
!(dwNewStatus & PP_ERROR) ) {
CHECK_SCHEDULER();
}
Cleanup:
LeaveSplSem();
SplOutSem();
if ( dwLastError != ERROR_SUCCESS ) {
SetLastError(dwLastError);
return FALSE;
} else {
return TRUE;
}
}