Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1623 lines
48 KiB

  1. /*++
  2. Copyright (c) 1990 - 1996 Microsoft Corporation
  3. Module Name:
  4. port.c
  5. Abstract:
  6. This module contains functions to control port threads
  7. PrintDocumentThruPrintProcessor
  8. CreatePortThread
  9. DestroyPortThread
  10. PortThread
  11. Author:
  12. Dave Snipp (DaveSn) 15-Mar-1991
  13. Revision History:
  14. KrishnaG 3-Feb-1991 - moved all monitor based functions to monitor.c
  15. Matthew Felton (mattfe) Feb 1994 Added OpenMonitorPort CloseMonitorPort
  16. --*/
  17. #include <precomp.h>
  18. #pragma hdrstop
  19. #include "clusspl.h"
  20. #include "filepool.hxx"
  21. WCHAR *szFilePort = L"FILE:";
  22. VOID
  23. PrintDocumentThruPrintProcessor(
  24. PINIPORT pIniPort,
  25. PPRINTPROCESSOROPENDATA pOpenData
  26. );
  27. // ShutdownPorts
  28. //
  29. // Called when the DLL_PROCESS_DETATCH is called
  30. // Close all portthreads
  31. // Close all monitorports
  32. VOID
  33. ShutdownPorts(
  34. PINISPOOLER pIniSpooler
  35. )
  36. {
  37. PINIPORT pIniPort;
  38. if (!pIniSpooler || (pIniSpooler == INVALID_HANDLE_VALUE))
  39. {
  40. return;
  41. }
  42. EnterSplSem();
  43. SplInSem();
  44. pIniPort = pIniSpooler->pIniPort;
  45. while(pIniPort) {
  46. DestroyPortThread(pIniPort, TRUE);
  47. //
  48. // Don't close monitor port since DLL_ATTACH may have been called
  49. //
  50. // CloseMonitorPort(pIniPort);
  51. RemoveDeviceName(pIniPort);
  52. pIniPort = pIniPort->pNext;
  53. }
  54. LeaveSplSem();
  55. return;
  56. }
  57. BOOL
  58. CreatePortThread(
  59. PINIPORT pIniPort
  60. )
  61. {
  62. DWORD ThreadId;
  63. BOOL bReturnValue = FALSE;
  64. SplInSem();
  65. SPLASSERT (( pIniPort != NULL) &&
  66. ( pIniPort->signature == IPO_SIGNATURE));
  67. // Don't bother creating a thread for ports that don't have a monitor:
  68. if (!(pIniPort->Status & PP_MONITOR))
  69. return TRUE;
  70. if ( pIniPort->Status & PP_THREADRUNNING)
  71. return TRUE;
  72. try {
  73. pIniPort->Semaphore = CreateEvent(NULL, FALSE, FALSE, NULL);
  74. if ( pIniPort->Semaphore == NULL )
  75. leave;
  76. pIniPort->Ready = CreateEvent(NULL, FALSE, FALSE, NULL);
  77. if ( pIniPort->Ready == NULL ) {
  78. leave;
  79. }
  80. pIniPort->Status |= PP_RUNTHREAD;
  81. pIniPort->hPortThread = CreateThread(NULL, INITIAL_STACK_COMMIT,
  82. (LPTHREAD_START_ROUTINE)PortThread,
  83. pIniPort,
  84. 0, &ThreadId);
  85. if( pIniPort->hPortThread == NULL ) {
  86. pIniPort->Status &= ~PP_RUNTHREAD;
  87. leave;
  88. }
  89. if ( !SetThreadPriority(pIniPort->hPortThread, dwPortThreadPriority) ) {
  90. DBGMSG(DBG_WARNING, ("CreatePortThread - Setting thread priority failed %d\n", GetLastError()));
  91. }
  92. LeaveSplSem();
  93. // Make CreatePortThread Synchronous
  94. WaitForSingleObject( pIniPort->Ready, INFINITE );
  95. EnterSplSem();
  96. SplInSem();
  97. pIniPort->Status |= PP_THREADRUNNING;
  98. bReturnValue = TRUE;
  99. } finally {
  100. if ( !bReturnValue ) {
  101. if ( pIniPort->Semaphore != NULL ) {
  102. CloseHandle( pIniPort->Semaphore );
  103. pIniPort->Semaphore = NULL;
  104. }
  105. if ( pIniPort->Ready != NULL ) {
  106. CloseHandle( pIniPort->Ready );
  107. pIniPort->Ready = NULL;
  108. SetEvent( pIniPort->hPortThreadRunning );
  109. }
  110. }
  111. }
  112. return bReturnValue;
  113. }
  114. BOOL
  115. DestroyPortThread(
  116. PINIPORT pIniPort,
  117. BOOL bShutdown
  118. )
  119. {
  120. SplInSem();
  121. // PortThread checks for PP_RUNTHREAD
  122. // and exits if it is not set.
  123. pIniPort->Status &= ~PP_RUNTHREAD;
  124. if (pIniPort->Semaphore && !SetEvent(pIniPort->Semaphore)) {
  125. return FALSE;
  126. }
  127. if( pIniPort->hPortThread != NULL) {
  128. INCPORTREF(pIniPort);
  129. LeaveSplSem();
  130. if ( WaitForSingleObject( pIniPort->hPortThread, INFINITE) == WAIT_FAILED ) {
  131. EnterSplSem();
  132. DECPORTREF(pIniPort);
  133. return FALSE;
  134. }
  135. EnterSplSem();
  136. DECPORTREF(pIniPort);
  137. }
  138. if (pIniPort->hPortThread != NULL) {
  139. CloseHandle(pIniPort->hPortThread);
  140. pIniPort->hPortThread = NULL;
  141. }
  142. //
  143. // The port may have been changed while the printer was printing.
  144. // Thus when the port thread finally goes away now is the time to
  145. // close the monitor. However we can't call the monitor during shutdown
  146. // since DLL_DETACH may already have been issued to the monitor dll
  147. //
  148. if ( !pIniPort->cPrinters && !bShutdown)
  149. CloseMonitorPort(pIniPort);
  150. return TRUE;
  151. }
  152. VOID
  153. RemoveIniPortFromIniJob(
  154. PINIJOB pIniJob,
  155. PINIPORT pIniPort
  156. )
  157. {
  158. PINISPOOLER pIniSpooler = pIniJob->pIniPrinter->pIniSpooler;
  159. NOTIFYVECTOR NotifyVector;
  160. SplInSem();
  161. //
  162. // Increment the refcount since deleting the job may delete the
  163. // pIniJob, which would delete the pIniSpooler.
  164. //
  165. INCSPOOLERREF( pIniSpooler );
  166. SPLASSERT(pIniJob &&
  167. pIniJob->signature == IJ_SIGNATURE &&
  168. pIniJob->pIniPort);
  169. SPLASSERT( pIniJob->pIniPort == pIniPort );
  170. pIniPort->cJobs--;
  171. pIniJob->pIniPort = NULL;
  172. SPLASSERT( pIniJob->Status & JOB_DESPOOLING );
  173. // Chained Jobs
  174. // For a Chained Master Job do not remove JOB_DESPOOLING
  175. // since we don't want the scheduler to reschedule this
  176. // to another port
  177. if ( pIniPort->pIniJob != pIniJob ) {
  178. // Normal Path
  179. // When NOT a chained job.
  180. InterlockedAnd((LONG*)&(pIniJob->Status), ~JOB_DESPOOLING);
  181. COPYNV(NotifyVector, NVJobStatus);
  182. NotifyVector[JOB_NOTIFY_TYPE] |= BIT(I_JOB_PORT_NAME) |
  183. BIT(I_JOB_PAGES_PRINTED) |
  184. BIT(I_JOB_BYTES_PRINTED);
  185. SetPrinterChange( pIniJob->pIniPrinter,
  186. pIniJob,
  187. NotifyVector,
  188. PRINTER_CHANGE_SET_JOB,
  189. pIniSpooler);
  190. }
  191. // RestartJob() doesn't remove JOB_PRINTED or JOB_BLOCKED_DEVQ
  192. // or JOB_DESPOOLING or JOB_COMPLETE if the despooling bit is on
  193. // this is to avoid problems where we have completed "Printing"
  194. // the job via a print processor and now the port thread is logging
  195. // the job printed and sending an alert message.
  196. if ( pIniJob->Status & JOB_RESTART )
  197. InterlockedAnd((LONG*)&(pIniJob->Status), ~( JOB_PRINTED | JOB_BLOCKED_DEVQ | JOB_COMPLETE));
  198. DeleteJobCheck(pIniJob);
  199. //
  200. // pIniJob may be gone at this point.
  201. //
  202. //
  203. // If we're at zero then set hEventNoPrintingJobs if it exists.
  204. //
  205. if( !pIniSpooler->cFullPrintingJobs &&
  206. pIniSpooler->hEventNoPrintingJobs ){
  207. SetEvent( pIniSpooler->hEventNoPrintingJobs );
  208. }
  209. //
  210. // Matches INCSPOOLERREF at beginning of this function.
  211. //
  212. DECSPOOLERREF( pIniSpooler );
  213. }
  214. DWORD
  215. PortThread(
  216. PINIPORT pIniPort
  217. )
  218. {
  219. DWORD rc;
  220. PRINTPROCESSOROPENDATA OpenData;
  221. PINIJOB pIniJob;
  222. DWORD NextJobId = 0;
  223. DWORD Position;
  224. DWORD dwDevQueryPrint = 0;
  225. DWORD dwJobDirect = 0;
  226. DWORD dwDevQueryPrintStatus = 0;
  227. WCHAR ErrorString[MAX_PATH];
  228. BOOL bRawDatatype;
  229. //
  230. // Power management. While we have port threads, we don't want the
  231. // system to go to sleep. Note that if we have a hung job, we will
  232. // not go to sleep.
  233. //
  234. SetThreadExecutionState( ES_SYSTEM_REQUIRED | ES_CONTINUOUS );
  235. EnterSplSem();
  236. INCSPOOLERREF( pIniPort->pIniSpooler );
  237. SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
  238. if ( pIniPort->Status & PP_MONITOR ) {
  239. if ( pIniPort->Status & PP_FILE )
  240. {
  241. rc = StatusFromHResult(OpenMonitorPort(pIniPort,
  242. NULL,
  243. NULL));
  244. if (rc == ERROR_SUCCESS)
  245. {
  246. ReleaseMonitorPort(pIniPort);
  247. }
  248. DBGMSG(DBG_TRACE, (" After opening the file pseudo monitor port %d\n", rc));
  249. } else {
  250. // LPRMON returns NULL ( fails and expect us to open it again
  251. // inside PrintingDirectlyToPort, so for now remove this assert
  252. // since OpenMonitorPort was added to PrintingDirectlyToPort
  253. // SPLASSERT( pIniPort->hPort != NULL );
  254. }
  255. }
  256. SetEvent( pIniPort->Ready );
  257. ResetEvent( pIniPort->hPortThreadRunning );
  258. while (TRUE) {
  259. SplInSem();
  260. SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
  261. DBGMSG(DBG_TRACE, ("Re-entering the Port Loop -- will blow away any Current Job\n"));
  262. pIniPort->Status |= PP_WAITING;
  263. //
  264. // Signal Ready event, to tell CreatePortThread that the Loop has successfully started.
  265. // hPortThreadRunning is signalled in the loop for each port it goes through. (and once
  266. // before leaving the port thread).
  267. //
  268. CHECK_SCHEDULER();
  269. DBGMSG( DBG_PORT, ("Port %ws: WaitForSingleObject( %x )\n",
  270. pIniPort->pName, pIniPort->Semaphore ) );
  271. LeaveSplSem();
  272. SplOutSem();
  273. //
  274. // Any modification to the pIniPort structure by other threads
  275. // can be done only at this point.
  276. //
  277. rc = WaitForSingleObject( pIniPort->Semaphore, INFINITE );
  278. EnterSplSem();
  279. SplInSem();
  280. SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
  281. DBGMSG( DBG_PORT, ("Port %ws: WaitForSingleObject( %x ) returned\n",
  282. pIniPort->pName, pIniPort->Semaphore));
  283. if ( !( pIniPort->Status & PP_RUNTHREAD ) ) {
  284. DBGMSG(DBG_TRACE, ("Thread for Port %ws Closing Down\n", pIniPort->pName));
  285. pIniPort->Status &= ~(PP_THREADRUNNING | PP_WAITING);
  286. CloseHandle( pIniPort->Semaphore );
  287. pIniPort->Semaphore = NULL;
  288. CloseHandle( pIniPort->Ready );
  289. pIniPort->Ready = NULL;
  290. SetEvent(pIniPort->hPortThreadRunning);
  291. if ( pIniPort->Status & PP_FILE )
  292. {
  293. rc = StatusFromHResult(CloseMonitorPort(pIniPort));
  294. DBGMSG(DBG_TRACE, (" After closing the file pseudo monitor port\n %d\n"));
  295. DBGMSG( DBG_WARN,
  296. ( "PortThread: DecSpoolerRef %x\n",
  297. pIniPort->pIniSpooler ));
  298. }
  299. DECSPOOLERREF( pIniPort->pIniSpooler );
  300. LeaveSplSem();
  301. SplOutSem();
  302. //
  303. // Power management. We are done.
  304. //
  305. SetThreadExecutionState(ES_CONTINUOUS);
  306. ExitThread (FALSE);
  307. }
  308. //
  309. // Bad assumption -- that at this point we definitely have a Job
  310. //
  311. if ( ( pIniJob = pIniPort->pIniJob ) &&
  312. pIniPort->pIniJob->pIniPrintProc ) {
  313. SPLASSERT( pIniJob->signature == IJ_SIGNATURE );
  314. SPLASSERT( pIniJob->Status & JOB_DESPOOLING );
  315. //
  316. // WMI Trace Events
  317. //
  318. INCJOBREF(pIniJob);
  319. LeaveSplSem();
  320. LogWmiTraceEvent(pIniJob->JobId, EVENT_TRACE_TYPE_SPL_PRINTJOB, NULL);
  321. EnterSplSem();
  322. DECJOBREF(pIniJob);
  323. DBGMSG(DBG_PORT, ("Port %ws: received job\n", pIniPort->pName));
  324. SPLASSERT(pIniJob->cRef != 0);
  325. DBGMSG(DBG_PORT, ("PortThread(1):cRef = %d\n", pIniJob->cRef));
  326. //
  327. // !! HACK !!
  328. //
  329. // If the datatype is 1.008 but the print proc doesn't support it,
  330. // then change it to 1.003 just for the print proc.
  331. //
  332. // This happens for the lexmark print processor. They support
  333. // NT EMF 1.003, but not 1.008. They just call GdiPlayEMF, so
  334. // they really can support 1.008 since they don't look at the
  335. // data. However, since they don't advertise this, they can't
  336. // print.
  337. //
  338. // We work around this by switching the datatype back to 1.003.
  339. //
  340. if (!_wcsicmp(pIniJob->pDatatype, gszNT5EMF) &&
  341. !CheckDataTypes(pIniJob->pIniPrintProc, gszNT5EMF))
  342. {
  343. OpenData.pDatatype = AllocSplStr(gszNT4EMF);
  344. }
  345. else
  346. {
  347. OpenData.pDatatype = AllocSplStr(pIniJob->pDatatype);
  348. }
  349. OpenData.pDevMode = AllocDevMode(pIniJob->pDevMode);
  350. OpenData.pParameters = AllocSplStr(pIniJob->pParameters);
  351. OpenData.JobId = pIniJob->JobId;
  352. OpenData.pDocumentName = AllocSplStr(pIniJob->pDocument);
  353. OpenData.pOutputFile = AllocSplStr(pIniJob->pOutputFile);
  354. //
  355. // Check if we have RAW Printing
  356. //
  357. bRawDatatype = ValidRawDatatype(pIniJob->pDatatype);
  358. OpenData.pPrinterName = pszGetPrinterName(
  359. pIniJob->pIniPrinter,
  360. pIniPort->pIniSpooler != pLocalIniSpooler,
  361. NULL );
  362. //
  363. // If all of the allocations succeeded, then call into the print
  364. // processor, otherwise, we discard the job for now. Some of this
  365. // data can be NULL (pParameters, pOutputFile).
  366. //
  367. if (OpenData.pDatatype && OpenData.pPrinterName &&
  368. (OpenData.pDevMode || !pIniJob->pDevMode) &&
  369. (OpenData.pParameters || !pIniJob->pParameters) &&
  370. (OpenData.pDocumentName || !pIniJob->pDocument) &&
  371. (OpenData.pOutputFile || !pIniJob->pOutputFile) ) {
  372. dwDevQueryPrint = pIniJob->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_ENABLE_DEVQ;
  373. if ((pIniJob->Status & JOB_DIRECT) ||
  374. ((pIniJob->Status & JOB_DOWNLEVEL) &&
  375. ValidRawDatatype(pIniJob->pDatatype))) {
  376. dwJobDirect = 1;
  377. }
  378. //
  379. // If we are restarting to print a document
  380. // clear its counters and remove the restart flag
  381. //
  382. if ( pIniJob->Status & JOB_RESTART ) {
  383. InterlockedAnd((LONG*)&(pIniJob->Status), ~(JOB_RESTART | JOB_INTERRUPTED));
  384. pIniJob->cbPrinted = 0;
  385. pIniJob->cPagesPrinted = 0;
  386. //
  387. // Only use dwReboots if not RAW.
  388. //
  389. if (!bRawDatatype)
  390. {
  391. //
  392. // Solves bug 229913;
  393. // Decrement number of reboots if job is restarted.
  394. // ReadShadowJob checks the number of reboots and delete the job if too many
  395. //
  396. if( pIniJob->dwReboots ){
  397. pIniJob->dwReboots--;
  398. }
  399. }
  400. }
  401. //
  402. // Job is being restarted, so clear all errors?
  403. //
  404. ClearJobError( pIniJob );
  405. pIniJob->dwAlert = 0;
  406. //
  407. // Only use dwReboots if not RAW.
  408. //
  409. if (!bRawDatatype)
  410. {
  411. pIniJob->dwReboots++;
  412. WriteShadowJob(pIniJob, TRUE);
  413. }
  414. LeaveSplSem();
  415. SplOutSem();
  416. if ( ( dwDevQueryPrintStatus = CallDevQueryPrint(OpenData.pPrinterName,
  417. OpenData.pDevMode,
  418. ErrorString,
  419. MAX_PATH,
  420. dwDevQueryPrint,
  421. dwJobDirect) ) ) {
  422. PrintDocumentThruPrintProcessor( pIniPort, &OpenData );
  423. }
  424. SplOutSem();
  425. EnterSplSem();
  426. }
  427. else {
  428. DiscardJobFromPortThread(pIniJob, TRUE);
  429. }
  430. //
  431. // Decrement number of EMF jobs rendering and update available memory
  432. //
  433. RemoveFromJobList(pIniJob, JOB_SCHEDULE_LIST);
  434. SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
  435. SPLASSERT( pIniPort->pIniJob != NULL );
  436. SPLASSERT( pIniJob == pIniPort->pIniJob);
  437. SPLASSERT( pIniJob->signature == IJ_SIGNATURE );
  438. //
  439. // Chained Jobs
  440. // If we have a chain of jobs, we now need to find the next job in the chain
  441. // and make sure its printed to the same port.
  442. //
  443. if (!( pIniJob->Status & ( JOB_PENDING_DELETION | JOB_RESTART )) &&
  444. ( pIniJob->pCurrentIniJob != NULL ) &&
  445. ( pIniJob->pCurrentIniJob->NextJobId != 0 )) {
  446. // Follow the Chained Job to the Next Job
  447. // Look at scheduler to see where it picks up this job and assigns it back
  448. // to this port thread.
  449. pIniJob->pCurrentIniJob = FindJob( pIniJob->pIniPrinter, pIniJob->pCurrentIniJob->NextJobId, &Position );
  450. if ( pIniJob->pCurrentIniJob == NULL ) {
  451. pIniPort->pIniJob = NULL;
  452. DBGMSG( DBG_WARNING, ("PortThread didn't find NextJob\n"));
  453. } else {
  454. SPLASSERT( pIniJob->pCurrentIniJob->signature == IJ_SIGNATURE );
  455. DBGMSG( DBG_WARNING, ("PortThread completed JobId %d, NextJobId %d\n", pIniJob->JobId,
  456. pIniJob->pCurrentIniJob->JobId ));
  457. }
  458. } else {
  459. //
  460. // Nothing More in Chain
  461. //
  462. pIniJob->pCurrentIniJob = NULL;
  463. pIniPort->pIniJob = NULL;
  464. }
  465. if( !pIniJob->pCurrentIniJob ){
  466. //
  467. // Decrement the pIniSpooler job count. We only decrement
  468. // at the end of a chain since we don't increment in the
  469. // middle of a chained job.
  470. //
  471. --pIniJob->pIniPrinter->pIniSpooler->cFullPrintingJobs;
  472. }
  473. DBGMSG(DBG_PORT, ("PortThread job has now printed - status:0x%0x\n", pIniJob->Status));
  474. FreeDevMode(OpenData.pDevMode);
  475. FreeSplStr(OpenData.pDatatype);
  476. FreeSplStr(OpenData.pParameters);
  477. FreeSplStr(OpenData.pDocumentName);
  478. FreeSplStr(OpenData.pOutputFile);
  479. FreeSplStr(OpenData.pPrinterName);
  480. pIniJob->Time = GetTickCount() - pIniJob->Time;
  481. if (!dwDevQueryPrintStatus) {
  482. DBGMSG(DBG_PORT, ("PortThread Job has not printed because of DevQueryPrint failed\n"));
  483. InterlockedOr((LONG*)&(pIniJob->Status), JOB_BLOCKED_DEVQ);
  484. SPLASSERT( !(pIniJob->Status & JOB_PRINTED));
  485. pIniJob->Time = 0;
  486. FreeSplStr( pIniJob->pStatus );
  487. pIniJob->pStatus = AllocSplStr(ErrorString);
  488. SetPrinterChange(pIniJob->pIniPrinter,
  489. pIniJob,
  490. NVJobStatusAndString,
  491. PRINTER_CHANGE_SET_JOB,
  492. pIniJob->pIniPrinter->pIniSpooler );
  493. } else if ( !( pIniJob->Status & JOB_TIMEOUT ) ) {
  494. //
  495. // Only Log the event and send a popup if the last in the chain
  496. //
  497. if ( !(pIniJob->Status & JOB_RESTART) &&
  498. pIniJob->pCurrentIniJob == NULL ) {
  499. //
  500. // A job can be in JOB_COMPLETE state when it was sent ot printer
  501. // but the last page isn't ejected yet. A job completely sent to printer
  502. // can be either in JOB_COMPLETE or JOB_PRINTED state.
  503. // Monitors that doesn't support TEOJ will set the job as JOB_PRINTED
  504. // right after the job was sent to printer and we don't want to set it on
  505. // JOB_COMPLETE.
  506. // For BIDI Monitors we'll come down here possibly before
  507. // the monitor sets the job as JOB_PRINTED. We set the job as JOB_COMPLETE
  508. // so that the scheduler will ignore it.
  509. //
  510. if (!(pIniJob->Status & (JOB_ERROR | JOB_PAPEROUT | JOB_OFFLINE)) &&
  511. !(pIniJob->Status & JOB_PRINTED))
  512. {
  513. if (pIniJob->cPages == 0 &&
  514. (pIniJob->Size == 0 || pIniJob->dwValidSize == 0) &&
  515. !(pIniJob->Status & JOB_TYPE_ADDJOB))
  516. {
  517. //
  518. // Set empty document to Printed as the monitor won't do it.
  519. // Make exception for job submitted with AddJob. The monitor
  520. // is still in charge for doing it.
  521. //
  522. InterlockedOr((LONG*)&(pIniJob->Status), JOB_PRINTED);
  523. }
  524. else
  525. {
  526. InterlockedOr((LONG*)&(pIniJob->Status), JOB_COMPLETE);
  527. }
  528. }
  529. // For Remote NT Jobs cPagesPrinted and cTotalPagesPrinted
  530. // are NOT updated since we are getting RAW data. So we
  531. // use the cPages field instead.
  532. if (pIniJob->cPagesPrinted == 0) {
  533. pIniJob->cPagesPrinted = pIniJob->cPages;
  534. pIniJob->pIniPrinter->cTotalPagesPrinted += pIniJob->cPages;
  535. }
  536. INCJOBREF(pIniJob);
  537. LeaveSplSem();
  538. LogJobPrinted(pIniJob);
  539. EnterSplSem();
  540. DECJOBREF(pIniJob);
  541. }
  542. }
  543. SplInSem();
  544. DBGMSG(DBG_PORT, ("PortThread(2):cRef = %d\n", pIniJob->cRef));
  545. // Hi End Print Shops like to keep around jobs after they have
  546. // completed. They do this so they can print a proof it and then
  547. // print it again for the final run. Spooling the job again may take
  548. // several hours which they want to avoid.
  549. // Even if KEEPPRINTEDJOBS is set they can still manually delete
  550. // the job via printman.
  551. if (( pIniJob->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS ) ||
  552. ( pIniJob->Status & JOB_TIMEOUT ) ) {
  553. //
  554. // WMI Trace Events.
  555. //
  556. // Treat a keep and restart as a delete and spool new job.
  557. WMI_SPOOL_DATA WmiData;
  558. DWORD CreateInfo;
  559. if (GetFileCreationInfo(pIniJob->hFileItem, &CreateInfo) != S_OK) {
  560. // Assume all file created.
  561. CreateInfo = FP_ALL_FILES_CREATED;
  562. }
  563. SplWmiCopyEndJobData(&WmiData, pIniJob, CreateInfo);
  564. InterlockedAnd((LONG*)&(pIniJob->Status), ~JOB_PENDING_DELETION);
  565. pIniJob->cbPrinted = 0;
  566. //
  567. // Set the job as JOB_COMPLETE if not already set as JOB_PRINTED
  568. // by Monitor.
  569. // Monitors that doesn't support TEOJ will set the job as JOB_PRINTED
  570. // right after the job was sent to printer and we don't want to set it on
  571. // JOB_COMPLETE.
  572. // For BIDI Monitors we'll come down here possibly before
  573. // the monitor sets the job as JOB_PRINTED. We set the job as JOB_COMPLETE
  574. // so that the scheduler will ignore it.
  575. //
  576. if (!(pIniJob->Status & JOB_PRINTED)) {
  577. InterlockedOr((LONG*)&(pIniJob->Status), JOB_COMPLETE);
  578. }
  579. //
  580. // Only use dwReboots if not RAW.
  581. //
  582. if (!bRawDatatype)
  583. {
  584. --pIniJob->dwReboots;
  585. }
  586. //
  587. // We need to update the shadow file regardless the job type.
  588. // There is the job status that we need to update.
  589. //
  590. INCJOBREF(pIniJob);
  591. //
  592. // WriteShadowJob leaves the CS, So make sure that the ref on the
  593. // pIniJob is kept high.
  594. //
  595. WriteShadowJob(pIniJob, TRUE);
  596. LeaveSplSem();
  597. //
  598. // WMI Trace Events.
  599. //
  600. // The job is done. If it is restarted you get a new spool job event.
  601. LogWmiTraceEvent(pIniJob->JobId, EVENT_TRACE_TYPE_SPL_DELETEJOB,
  602. &WmiData);
  603. EnterSplSem();
  604. DECJOBREF(pIniJob);
  605. SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
  606. SPLASSERT( pIniJob->signature == IJ_SIGNATURE );
  607. }
  608. SplInSem();
  609. SPLASSERT( pIniJob->cRef != 0 );
  610. DECJOBREF(pIniJob);
  611. RemoveIniPortFromIniJob(pIniJob, pIniPort);
  612. //
  613. // N.B. The pIniJob may be gone at this point.
  614. //
  615. } else {
  616. //
  617. // !! VERIFY !!
  618. //
  619. SPLASSERT(pIniJob != NULL);
  620. if (pIniJob != NULL) {
  621. DBGMSG(DBG_PORT, ("Port %ws: deleting job\n", pIniPort->pName));
  622. // SPLASSERT( pIniJob->Time != 0 );
  623. pIniJob->Time = GetTickCount() - pIniJob->Time;
  624. //pIniJob->Status |= JOB_PRINTED;
  625. if ( pIniJob->hFileItem == INVALID_HANDLE_VALUE )
  626. {
  627. CloseHandle( pIniJob->hWriteFile );
  628. }
  629. pIniJob->hWriteFile = INVALID_HANDLE_VALUE;
  630. DBGMSG(DBG_PORT, ("Port %ws - calling DeleteJob because PrintProcessor wasn't available\n"));
  631. RemoveIniPortFromIniJob(pIniJob, pIniPort);
  632. DeleteJob(pIniJob,BROADCAST);
  633. //
  634. // N.B. The pIniJob may be gone at this point.
  635. //
  636. }
  637. }
  638. //SetCurrentSid(NULL);
  639. DBGMSG(DBG_PORT,("Returning back to pickup a new job or to delete the PortThread\n"));
  640. }
  641. SetEvent( pIniPort->hPortThreadRunning );
  642. SPLASSERT( FALSE );
  643. return 0;
  644. }
  645. VOID
  646. PrintDocumentThruPrintProcessor(
  647. PINIPORT pIniPort,
  648. PPRINTPROCESSOROPENDATA pOpenData
  649. )
  650. /*++
  651. Routine Description:
  652. Print the document associated with pIniPort on the print
  653. processor.
  654. Status of pIniPort->Status = PP_RUNTHREAD
  655. PP_THREADRUNNING
  656. PP_MONITOR
  657. ~PP_WAITING
  658. NOTE: If PrintProc->Open is called and succeeds, PrintProc->Close
  659. must be called to cleanup.
  660. Arguments:
  661. Return Value:
  662. --*/
  663. {
  664. PINIJOB pIniJob = pIniPort->pIniJob;
  665. WCHAR szSpecialPortorPrinterName[MAX_UNC_PRINTER_NAME + MAX_PATH + PRINTER_NAME_SUFFIX_MAX];
  666. BOOL bJobError = FALSE;
  667. NOTIFYVECTOR NotifyVector;
  668. LPTSTR pszModify;
  669. UINT cchLen;
  670. BOOL bFailJob = FALSE;
  671. BOOL bRemoteGuest = FALSE;
  672. BOOL bSpecialCaseDriver = FALSE;
  673. DWORD Error;
  674. //
  675. // Check if printing principal is remote guest. Remote guest does not have enough
  676. // permissions to print EMF. The EMF playback code in GDI32 fails for certain EMF records.
  677. // Because of this, we create an impersonation token based on the process token.
  678. //
  679. if ((bSpecialCaseDriver = IsSpecialDriver(pIniJob->pIniDriver, pIniJob->pIniPrintProc, pIniJob->pIniPrinter->pIniSpooler)) ||
  680. (Error = PrincipalIsRemoteGuest(pIniJob->hToken, &bRemoteGuest)) == ERROR_SUCCESS)
  681. {
  682. if (bRemoteGuest || bSpecialCaseDriver)
  683. {
  684. Error = ImpersonateSelf(SecurityImpersonation) ? ERROR_SUCCESS : GetLastError();
  685. }
  686. else
  687. {
  688. Error = SetCurrentSid(pIniJob->hToken) ? ERROR_SUCCESS : GetLastError();
  689. }
  690. }
  691. if (Error != ERROR_SUCCESS)
  692. {
  693. LogPrintProcError(Error, pIniJob);
  694. bFailJob = TRUE;
  695. goto Complete;
  696. }
  697. DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor pIniPort %x pOpenData %x\n", pIniPort, pOpenData));
  698. COPYNV(NotifyVector, NVJobStatus);
  699. cchLen = lstrlen( pIniJob->pIniPrinter->pIniSpooler->pMachineName );
  700. //
  701. // Do a length check. PRINTER_NAME_SUFFIX_MAX holds the extra 4 separator
  702. // characters and the NULL terminator.
  703. // We need to be able to fit both the port name and the printer name into
  704. // the buffer, plus the suffix and internal characters.
  705. //
  706. if (lstrlen(pIniPort->pName) + cchLen + PRINTER_NAME_SUFFIX_MAX > COUNTOF(szSpecialPortorPrinterName) ||
  707. lstrlen(pIniJob->pIniPrinter->pName) + cchLen + PRINTER_NAME_SUFFIX_MAX > COUNTOF(szSpecialPortorPrinterName)) {
  708. //
  709. // We should log an event, but this is a very rare event, only
  710. // in the print api tests.
  711. //
  712. bFailJob = TRUE;
  713. goto Complete;
  714. }
  715. //
  716. // For clustered spoolers, make sure it is fully qualified.
  717. // pszModify points to the string immediately after the server
  718. // name that can be modified.
  719. //
  720. // Always modify the string at pszModify, but pass in
  721. // szSpecialPortorPrinterName.
  722. //
  723. StrNCatBuff(szSpecialPortorPrinterName, COUNTOF(szSpecialPortorPrinterName), pIniJob->pIniPrinter->pIniSpooler->pMachineName, L"\\", NULL);
  724. pszModify = &szSpecialPortorPrinterName[cchLen+1];
  725. //
  726. // \\Server\
  727. // ^---------------------- szSpecialPortorPrinterName
  728. // ^------------- pszModify
  729. //
  730. // Append the rest of the string at pszModify:
  731. //
  732. // \\Server\PortName, Port
  733. // \\Server\PrinterName, Job 33
  734. //
  735. //
  736. // Now create the port name, so that we can do the
  737. // secret open printer. the printer name will be
  738. // "FILE:, Port" and this will open a PRINTER_HANDLE_PORT
  739. // If we fail, then if the app thread may be waiting for
  740. // the pIniJob->StartDocComplete to be set, which would
  741. // ordinarily be done in the StartDocPrinter of the port.
  742. // We will do this little courtesy,
  743. //
  744. StringCchPrintf(pszModify, COUNTOF(szSpecialPortorPrinterName) - (pszModify - szSpecialPortorPrinterName), L"%ws, Port", pIniPort->pName );
  745. DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor Attempting PrintProcessor Open on %ws\n", szSpecialPortorPrinterName ));
  746. if (!(pIniPort->hProc = (HANDLE)(*pIniJob->pIniPrintProc->Open)
  747. (szSpecialPortorPrinterName, pOpenData))) {
  748. DBGMSG( DBG_WARNING, ("PrintDocumentThruPrintProcessor Failed Open error %d\n", GetLastError() ));
  749. bFailJob = TRUE;
  750. goto Complete;
  751. }
  752. //
  753. // For Jobs, turn this off even if it's not clustered.
  754. //
  755. if( !( pIniJob->pIniPrinter->pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER )){
  756. pszModify = szSpecialPortorPrinterName;
  757. //
  758. //
  759. // ^---------------------- szSpecialPortorPrinterName
  760. // ^---------------------- pszModify
  761. //
  762. // PortName, Port
  763. // PrinterName, Job 33
  764. //
  765. }
  766. EnterSplSem();
  767. InterlockedOr((LONG*)&(pIniJob->Status), JOB_PRINTING);
  768. pIniJob->Time = GetTickCount();
  769. NotifyVector[JOB_NOTIFY_TYPE] |= BIT(I_JOB_PORT_NAME);
  770. SetPrinterChange(pIniJob->pIniPrinter,
  771. pIniJob,
  772. NotifyVector,
  773. PRINTER_CHANGE_SET_JOB,
  774. pIniJob->pIniPrinter->pIniSpooler);
  775. LeaveSplSem();
  776. //
  777. // Create Special Name "PrinterName, Job xxx"
  778. //
  779. StringCchPrintf(pszModify, COUNTOF(szSpecialPortorPrinterName) - (pszModify - szSpecialPortorPrinterName), L"%ws, Job %d", pIniJob->pIniPrinter->pName, pIniJob->JobId);
  780. DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor calling Print hProc %x file %ws\n",
  781. pIniPort->hProc, szSpecialPortorPrinterName ));
  782. if (!(*pIniJob->pIniPrintProc->Print)(pIniPort->hProc, szSpecialPortorPrinterName)) {
  783. Error = GetLastError();
  784. if (ERROR_PRINT_CANCELLED != Error)
  785. {
  786. LogPrintProcError(Error, pIniJob);
  787. }
  788. DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor Print hProc %x Error %d\n", pIniPort->hProc, Error));
  789. EnterSplSem();
  790. if ( pIniJob->StartDocComplete ) {
  791. SetEvent( pIniJob->StartDocComplete );
  792. }
  793. bJobError = TRUE;
  794. LeaveSplSem();
  795. } else {
  796. DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor Print hProc %x %ws Success\n",
  797. pIniPort->hProc, szSpecialPortorPrinterName ));
  798. }
  799. //
  800. // Now close the print processor.
  801. //
  802. EnterSplSem();
  803. SPLASSERT( pIniPort->hProc != NULL );
  804. DBGMSG( DBG_TRACE, ("PrintDocumentThruPrintProcessor calling Close hProc %x\n", pIniPort->hProc ));
  805. InterlockedAnd((LONG*)&(pIniJob->Status), ~JOB_PRINTING);
  806. LeaveSplSem();
  807. //
  808. // JOB_PP_CLOSE is used to prevent the print processor from recursively
  809. // calling back into itself. This happens for some third party print processor.
  810. // Race conditions don't apply for this flag since 2 threads don't access it
  811. // simultaneously.
  812. //
  813. if (!(pIniJob->Status & JOB_PP_CLOSE))
  814. {
  815. EnterCriticalSection(&pIniJob->pIniPrintProc->CriticalSection);
  816. InterlockedOr((LONG*)&(pIniJob->Status), JOB_PP_CLOSE);
  817. if (!(*pIniJob->pIniPrintProc->Close)(pIniPort->hProc))
  818. {
  819. DBGMSG( DBG_WARNING, ("PrintDocumentThruPrintProcessor failed Close hProc %x Error %d\n",
  820. pIniPort->hProc, GetLastError() ));
  821. }
  822. pIniPort->hProc = NULL;
  823. InterlockedAnd((LONG*)&(pIniJob->Status), ~JOB_PP_CLOSE);
  824. LeaveCriticalSection(&pIniJob->pIniPrintProc->CriticalSection);
  825. //
  826. // WMI Trace Events
  827. //
  828. if (pIniJob->pDevMode)
  829. {
  830. WMI_SPOOL_DATA Data;
  831. SplWmiCopyRenderedData(&Data, pIniJob->pDevMode);
  832. LogWmiTraceEvent(pIniJob->JobId, EVENT_TRACE_TYPE_SPL_JOBRENDERED, &Data);
  833. }
  834. }
  835. Complete:
  836. if (bFailJob || bJobError)
  837. {
  838. DiscardJobFromPortThread(pIniJob, bFailJob);
  839. }
  840. //
  841. // RevertToSelf and SetCurrentSid have identical behavior. If one call ImpersonateSelf
  842. // and the SetCurrentSid instead of RevertToSelf, that's still fine.
  843. //
  844. if (bRemoteGuest || bSpecialCaseDriver)
  845. {
  846. RevertToSelf();
  847. }
  848. else
  849. {
  850. SetCurrentSid(NULL);
  851. }
  852. }
  853. VOID
  854. UpdatePortStatusForAllPrinters(
  855. PINIPORT pIniPort
  856. )
  857. /*++
  858. Routine Description:
  859. This routine is called when an IniPorts status changed so that we go
  860. through each printer connected to the port and update their port status
  861. Arguments:
  862. pIniPort - Port whose status chanegd
  863. Return Value:
  864. Nothing
  865. --*/
  866. {
  867. PINIPRINTER pIniPrinter;
  868. PINIPORT pIniPrinterPort;
  869. DWORD dwIndex1, dwIndex2, dwPortStatus, dwSeverity;
  870. for ( dwIndex1 = 0 ; dwIndex1 < pIniPort->cPrinters ; ++dwIndex1 ) {
  871. pIniPrinter = pIniPort->ppIniPrinter[dwIndex1];
  872. dwSeverity = 0;
  873. dwPortStatus = 0;
  874. //
  875. // Pick the most severe status associated with all ports
  876. //
  877. for ( dwIndex2 = 0 ; dwIndex2 < pIniPrinter->cPorts ; ++dwIndex2 ) {
  878. pIniPrinterPort = pIniPrinter->ppIniPorts[dwIndex2];
  879. if ( pIniPrinterPort->Status & PP_ERROR ) {
  880. dwSeverity = PP_ERROR;
  881. dwPortStatus = PortToPrinterStatus(pIniPrinterPort->PrinterStatus);
  882. break; // no need to go thru rest of the ports for this printer
  883. } else if ( pIniPrinterPort->Status & PP_WARNING ) {
  884. if ( dwSeverity != PP_WARNING ) {
  885. dwSeverity = PP_WARNING;
  886. dwPortStatus = PortToPrinterStatus(pIniPrinterPort->PrinterStatus);
  887. }
  888. } else if ( pIniPrinterPort->Status & PP_INFORMATIONAL ) {
  889. if ( dwSeverity == 0 ) {
  890. dwSeverity = PP_INFORMATIONAL;
  891. dwPortStatus = PortToPrinterStatus(pIniPrinterPort->PrinterStatus);
  892. }
  893. }
  894. }
  895. if ( pIniPrinter->PortStatus != dwPortStatus ) {
  896. pIniPrinter->PortStatus = dwPortStatus;
  897. SetPrinterChange(pIniPrinter,
  898. NULL,
  899. NVPrinterStatus,
  900. PRINTER_CHANGE_SET_PRINTER,
  901. pIniPrinter->pIniSpooler);
  902. }
  903. }
  904. }
  905. //
  906. // Table is by port status values in winspool.h
  907. //
  908. DWORD PortToPrinterStatusMappings[] = {
  909. 0,
  910. PRINTER_STATUS_OFFLINE,
  911. PRINTER_STATUS_PAPER_JAM,
  912. PRINTER_STATUS_PAPER_OUT,
  913. PRINTER_STATUS_OUTPUT_BIN_FULL,
  914. PRINTER_STATUS_PAPER_PROBLEM,
  915. PRINTER_STATUS_NO_TONER,
  916. PRINTER_STATUS_DOOR_OPEN,
  917. PRINTER_STATUS_USER_INTERVENTION,
  918. PRINTER_STATUS_OUT_OF_MEMORY,
  919. PRINTER_STATUS_TONER_LOW,
  920. PRINTER_STATUS_WARMING_UP,
  921. PRINTER_STATUS_POWER_SAVE,
  922. };
  923. BOOL
  924. LocalSetPort(
  925. LPWSTR pszName,
  926. LPWSTR pszPortName,
  927. DWORD dwLevel,
  928. LPBYTE pPortInfo
  929. )
  930. {
  931. PINIPORT pIniPort;
  932. PPORT_INFO_3 pPortInfo3 = (PPORT_INFO_3) pPortInfo;
  933. DWORD dwLastError = ERROR_SUCCESS;
  934. DWORD dwNewStatus, dwOldStatus;
  935. BOOL bJobStatusChanged = FALSE;
  936. WCHAR szPort[MAX_PATH + 9];
  937. LPWSTR pszComma;
  938. PINISPOOLER pIniSpooler = FindSpoolerByNameIncRef( pszName, NULL );
  939. BOOL SemEntered = FALSE;
  940. if( !pIniSpooler )
  941. {
  942. dwLastError = ERROR_INVALID_NAME;
  943. goto Cleanup;
  944. }
  945. if ( !MyName(pszName, pIniSpooler) ) {
  946. dwLastError = GetLastError();
  947. goto Cleanup;
  948. }
  949. //
  950. // The monitor needs to be able to set or clear the error for the port. The monitor
  951. // is loaded by the spooler. If the monitor doesn't link to winspool.drv, then the
  952. // call to SetPort comes directly, i.e. not via RPC. In this case we do not want
  953. // to check for admin privileges. We allow any user to set the port status.
  954. //
  955. if (!ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
  956. IsCallViaRPC() ? SERVER_ACCESS_ADMINISTER : SERVER_ACCESS_ENUMERATE,
  957. NULL,
  958. NULL,
  959. pIniSpooler )) {
  960. dwLastError = GetLastError();
  961. goto Cleanup;
  962. }
  963. if( !pszPortName ){
  964. dwLastError = ERROR_UNKNOWN_PORT ;
  965. goto Cleanup;
  966. }
  967. //
  968. // Some ports will come in as "port,1234abcd" so truncate the
  969. // suffix.
  970. //
  971. StringCchCopy(szPort, COUNTOF(szPort), pszPortName);
  972. pszComma = wcschr( szPort, TEXT( ',' ));
  973. if( pszComma ){
  974. *pszComma = 0;
  975. }
  976. SemEntered = TRUE;
  977. EnterSplSem();
  978. pIniPort = FindPort(szPort, pIniSpooler);
  979. if ( !pIniPort ) {
  980. dwLastError = ERROR_UNKNOWN_PORT;
  981. goto Cleanup;
  982. }
  983. if ( dwLevel != 3 ) {
  984. dwLastError = ERROR_INVALID_LEVEL;
  985. goto Cleanup;
  986. }
  987. if ( !pPortInfo ) {
  988. dwLastError = ERROR_INVALID_PARAMETER;
  989. goto Cleanup;
  990. }
  991. switch (pPortInfo3->dwSeverity) {
  992. case 0:
  993. if ( pPortInfo3->dwStatus || pPortInfo3->pszStatus ) {
  994. dwLastError = ERROR_INVALID_PARAMETER;
  995. goto Cleanup;
  996. }
  997. dwNewStatus = 0;
  998. break;
  999. case PORT_STATUS_TYPE_ERROR:
  1000. dwNewStatus = PP_ERROR;
  1001. break;
  1002. case PORT_STATUS_TYPE_WARNING:
  1003. dwNewStatus = PP_WARNING;
  1004. break;
  1005. case PORT_STATUS_TYPE_INFO:
  1006. dwNewStatus = PP_INFORMATIONAL;
  1007. break;
  1008. default:
  1009. dwLastError = ERROR_INVALID_PARAMETER;
  1010. goto Cleanup;
  1011. }
  1012. dwOldStatus = pIniPort->Status;
  1013. //
  1014. // Clear old status
  1015. //
  1016. pIniPort->PrinterStatus = 0;
  1017. pIniPort->Status &= ~(PP_ERROR | PP_WARNING | PP_INFORMATIONAL);
  1018. if ( pIniPort->pszStatus ) {
  1019. //
  1020. // If the job currently has the same status as port free it
  1021. //
  1022. if ( pIniPort->pIniJob &&
  1023. pIniPort->pIniJob->pStatus &&
  1024. !wcscmp(pIniPort->pIniJob->pStatus, pIniPort->pszStatus) ) {
  1025. FreeSplStr(pIniPort->pIniJob->pStatus);
  1026. pIniPort->pIniJob->pStatus = NULL;
  1027. bJobStatusChanged = TRUE;
  1028. }
  1029. FreeSplStr(pIniPort->pszStatus);
  1030. pIniPort->pszStatus = NULL;
  1031. }
  1032. //
  1033. // If string field is used for status use it, else look at dwStatus
  1034. //
  1035. if ( pPortInfo3->pszStatus && *pPortInfo3->pszStatus ) {
  1036. pIniPort->pszStatus = AllocSplStr(pPortInfo3->pszStatus);
  1037. if ( !pIniPort->pszStatus ) {
  1038. dwLastError = GetLastError();
  1039. goto Cleanup;
  1040. }
  1041. if ( pIniPort->pIniJob && !pIniPort->pIniJob->pStatus ) {
  1042. pIniPort->pIniJob->pStatus = AllocSplStr(pIniPort->pszStatus);
  1043. bJobStatusChanged = TRUE;
  1044. }
  1045. } else {
  1046. //
  1047. // If we add new entries to winspool.h they should be added here too
  1048. //
  1049. if ( pPortInfo3->dwStatus >=
  1050. sizeof(PortToPrinterStatusMappings)/sizeof(PortToPrinterStatusMappings[0]) ) {
  1051. dwLastError = ERROR_INVALID_PARAMETER;
  1052. goto Cleanup;
  1053. }
  1054. pIniPort->PrinterStatus = pPortInfo3->dwStatus;
  1055. }
  1056. if( bJobStatusChanged ){
  1057. SetPrinterChange( pIniPort->pIniJob->pIniPrinter,
  1058. pIniPort->pIniJob,
  1059. NVJobStatusString,
  1060. PRINTER_CHANGE_SET_JOB,
  1061. pIniPort->pIniJob->pIniPrinter->pIniSpooler );
  1062. }
  1063. pIniPort->Status |= dwNewStatus;
  1064. UpdatePortStatusForAllPrinters(pIniPort);
  1065. if ( (dwOldStatus & PP_ERROR) &&
  1066. !(dwNewStatus & PP_ERROR) ) {
  1067. //
  1068. // if it is a transition to an non - error state , set event to unlock LocalWritePrinter
  1069. //
  1070. pIniPort->ErrorTime = 0;
  1071. if( pIniPort->hErrorEvent != NULL ){
  1072. SetEvent(pIniPort->hErrorEvent);
  1073. }
  1074. CHECK_SCHEDULER();
  1075. }
  1076. if ( !(dwOldStatus & PP_ERROR) &&
  1077. !(dwNewStatus & PP_ERROR) ) {
  1078. //
  1079. // when non-error state persists(after two calls with an non - error state) ,
  1080. // close the hErrorEvent handle
  1081. //
  1082. if( pIniPort->hErrorEvent != NULL ){
  1083. CloseHandle(pIniPort->hErrorEvent);
  1084. pIniPort->hErrorEvent = NULL;
  1085. }
  1086. }
  1087. if ( !(dwOldStatus & PP_ERROR) &&
  1088. (dwNewStatus & PP_ERROR) &&
  1089. (pIniPort->cJobs) &&
  1090. (pIniSpooler->bRestartJobOnPoolEnabled) &&
  1091. ( pPortInfo3->dwStatus == PORT_STATUS_OFFLINE ||
  1092. pPortInfo3->dwStatus == PORT_STATUS_PAPER_JAM ||
  1093. pPortInfo3->dwStatus == PORT_STATUS_PAPER_OUT ||
  1094. pPortInfo3->dwStatus == PORT_STATUS_DOOR_OPEN ||
  1095. pPortInfo3->dwStatus == PORT_STATUS_PAPER_PROBLEM ||
  1096. pPortInfo3->dwStatus == PORT_STATUS_NO_TONER)) {
  1097. //
  1098. // If it is a transition to an error state and port has an job assigned, create event as non-signalled or Reset event it
  1099. // LocalWritePrinter will get stuck if the port is in error state and this event is reset
  1100. //
  1101. if( pIniPort->ErrorTime == 0 ){
  1102. pIniPort->ErrorTime = GetTickCount();
  1103. if( pIniPort->hErrorEvent == NULL ){
  1104. pIniPort->hErrorEvent = CreateEvent(NULL,
  1105. EVENT_RESET_MANUAL,
  1106. EVENT_INITIAL_STATE_NOT_SIGNALED,
  1107. NULL );
  1108. }else{
  1109. ResetEvent( pIniPort->hErrorEvent );
  1110. }
  1111. }
  1112. }
  1113. if ( (dwOldStatus & PP_ERROR) &&
  1114. (dwNewStatus & PP_ERROR) &&
  1115. (pIniPort->cJobs) &&
  1116. (pIniPort->hErrorEvent != NULL) ) {
  1117. //
  1118. // When error state persists , check the time since error occured.
  1119. //
  1120. if( (GetTickCount() - pIniPort->ErrorTime) > pIniSpooler->dwRestartJobOnPoolTimeout * 1000 ){
  1121. //
  1122. // If time out and printer is a pool ( more than one port assigned ),
  1123. // clear job error and restart the job.
  1124. //
  1125. if( (pIniPort->pIniJob) &&
  1126. (pIniPort->pIniJob->pIniPrinter) &&
  1127. (pIniPort->pIniJob->pIniPrinter->cPorts > 1) ){
  1128. //
  1129. // Don't restart the job if it is already deleted or restarted.
  1130. //
  1131. BOOL bWasRestarted = pIniPort->pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART);
  1132. if( !bWasRestarted ){
  1133. ClearJobError( pIniPort->pIniJob );
  1134. RestartJob( pIniPort->pIniJob );
  1135. }
  1136. SetEvent( pIniPort->hErrorEvent );
  1137. }
  1138. }
  1139. }
  1140. Cleanup:
  1141. if(pIniSpooler)
  1142. {
  1143. FindSpoolerByNameDecRef( pIniSpooler );
  1144. }
  1145. if(SemEntered)
  1146. {
  1147. LeaveSplSem();
  1148. SplOutSem();
  1149. }
  1150. if(dwLastError != ERROR_SUCCESS)
  1151. {
  1152. SetLastError(dwLastError);
  1153. return FALSE;
  1154. }
  1155. else
  1156. {
  1157. return TRUE;
  1158. }
  1159. }
  1160. VOID
  1161. SetPortErrorEvent(
  1162. PINIPORT pIniPort
  1163. )
  1164. {
  1165. SplInSem();
  1166. if(pIniPort && pIniPort->hErrorEvent) {
  1167. SetEvent(pIniPort->hErrorEvent);
  1168. }
  1169. }
  1170. /*++
  1171. Routine Name:
  1172. DiscardJob
  1173. Description:
  1174. This routine discards the given job when an irrecoverable error occurs
  1175. in the port thread.
  1176. Arguments:
  1177. pIniJob - The job we are wanting to discard.
  1178. bStartDocComplete - If TRUE, we should set the StartDocComplete event.
  1179. Returns:
  1180. Nothing.
  1181. --*/
  1182. VOID
  1183. DiscardJobFromPortThread(
  1184. IN PINIJOB pIniJob,
  1185. IN BOOL bStartDocComplete
  1186. )
  1187. {
  1188. EnterSplSem();
  1189. //
  1190. // App might be waiting for the StartDoc to Complete
  1191. //
  1192. if (pIniJob->StartDocComplete && bStartDocComplete) {
  1193. SetEvent(pIniJob->StartDocComplete);
  1194. }
  1195. //
  1196. // If the job had an error, mark it pending deletion. The port monitor
  1197. // may not do this if EndDocPort was never called.
  1198. //
  1199. if (!(pIniJob->Status & JOB_RESTART)){
  1200. InterlockedOr((LONG*)&(pIniJob->Status), JOB_PENDING_DELETION);
  1201. //
  1202. // Release any thread waiting on LocalSetPort
  1203. //
  1204. SetPortErrorEvent(pIniJob->pIniPort);
  1205. //
  1206. // Release any thread waiting on SeekPrinter
  1207. //
  1208. SeekPrinterSetEvent(pIniJob, NULL, TRUE);
  1209. }
  1210. LeaveSplSem();
  1211. }