Source code of Windows XP (NT5)
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.

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