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.

4153 lines
121 KiB

  1. /*++
  2. Copyright (c) 1990 - 1996 Microsoft Corporation
  3. Module Name:
  4. spooler.c
  5. Abstract:
  6. This module provides all the public exported APIs relating to spooling
  7. and printing for the Local Print Providor. They include
  8. LocalStartDocPrinter
  9. LocalWritePrinter
  10. LocalReadPrinter
  11. LocalEndDocPrinter
  12. LocalAbortPrinter
  13. Author:
  14. Dave Snipp (DaveSn) 15-Mar-1991
  15. Revision History:
  16. --*/
  17. #include <precomp.h>
  18. #pragma hdrstop
  19. #include "jobid.h"
  20. #include "filepool.hxx"
  21. //extern HANDLE hFilePool;
  22. BOOL
  23. SpoolThisJob(
  24. PSPOOL pSpool,
  25. DWORD Level,
  26. LPBYTE pDocInfo
  27. );
  28. BOOL
  29. PrintingDirectlyToPort(
  30. PSPOOL pSpool,
  31. DWORD Level,
  32. LPBYTE pDocInfo,
  33. LPDWORD pJobId
  34. );
  35. BOOL
  36. PrintingDirect(
  37. PSPOOL pSpool,
  38. DWORD Level,
  39. LPBYTE pDocInfo
  40. );
  41. DWORD
  42. ReadFromPrinter(
  43. PSPOOL pSpool,
  44. LPBYTE pBuf,
  45. DWORD cbBuf
  46. );
  47. BOOL
  48. InternalReadPrinter(
  49. HANDLE hPrinter,
  50. LPVOID pBuf,
  51. DWORD cbBuf,
  52. LPBYTE *pMapBuffer,
  53. LPDWORD pNoBytesRead,
  54. BOOL bReadMappedView
  55. );
  56. BOOL SetMappingPointer(
  57. PSPOOL pSpool,
  58. LPBYTE *pMappedBuffer,
  59. DWORD cbReadSize
  60. );
  61. DWORD
  62. WriteToPrinter(
  63. PSPOOL pSpool,
  64. LPBYTE pByte,
  65. DWORD cbBuf
  66. );
  67. BOOL
  68. IsGoingToFile(
  69. LPWSTR pOutputFile,
  70. PINISPOOLER pIniSpooler
  71. );
  72. VOID
  73. MyPostThreadMessage(
  74. IN HANDLE hThread,
  75. IN DWORD idThread,
  76. IN UINT Msg,
  77. IN WPARAM wParam,
  78. IN LPARAM lParam)
  79. {
  80. SplOutSem();
  81. //
  82. // PostThreadMessage will fail under the following cases:
  83. // a. Too early -- MessageBox has not been created yet
  84. // b. Too late -- User has cancelled the dialog
  85. //
  86. // In case a. if we wait for few seconds and retry Post will succeed
  87. // In case b. WaitForSingleObject on thread handle will return WAIT_OBJECT_O
  88. //
  89. while ( !PostThreadMessage(idThread, Msg, wParam, lParam) ) {
  90. DBGMSG(DBG_WARNING, ("PostThreadMessage FAILED %d\n", GetLastError()));
  91. //
  92. // As far as thread is alive after 1 second retry the post
  93. //
  94. if ( WaitForSingleObject(hThread, 1000) != WAIT_TIMEOUT )
  95. break;
  96. }
  97. }
  98. VOID
  99. SeekPrinterSetEvent(
  100. PINIJOB pIniJob,
  101. HANDLE hFile,
  102. BOOL bEndDoc
  103. )
  104. {
  105. DWORD dwFileSizeHigh,dwFileSizeLow;
  106. if (!hFile) {
  107. hFile = pIniJob->hWriteFile;
  108. }
  109. if (pIniJob->bWaitForSeek && pIniJob->WaitForSeek != NULL ){
  110. if (!bEndDoc) {
  111. // Compare the sizes.
  112. if (pIniJob->Status & JOB_TYPE_OPTIMIZE) {
  113. dwFileSizeHigh = 0;
  114. dwFileSizeLow = pIniJob->dwValidSize;
  115. } else {
  116. dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
  117. if ((dwFileSizeLow == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  118. return;
  119. }
  120. }
  121. if ((pIniJob->liFileSeekPosn.u.HighPart > (LONG)dwFileSizeHigh) ||
  122. ((pIniJob->liFileSeekPosn.u.HighPart == (LONG)dwFileSizeHigh) &&
  123. (pIniJob->liFileSeekPosn.u.LowPart > dwFileSizeLow))) {
  124. return;
  125. }
  126. }
  127. SetEvent(pIniJob->WaitForSeek);
  128. }
  129. return;
  130. }
  131. DWORD
  132. LocalStartDocPrinter(
  133. HANDLE hPrinter,
  134. DWORD Level,
  135. LPBYTE pDocInfo
  136. )
  137. {
  138. PINIPRINTER pIniPrinter;
  139. PINIPORT pIniPort;
  140. PSPOOL pSpool=(PSPOOL)hPrinter;
  141. DWORD LastError=0, JobId=0;
  142. PDOC_INFO_1 pDocInfo1 = (PDOC_INFO_1)pDocInfo;
  143. BOOL bPrintingDirect;
  144. SPLASSERT(Level == 1);
  145. if (ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER ) &&
  146. !(pSpool->Status & SPOOL_STATUS_STARTDOC) &&
  147. !(pSpool->Status & SPOOL_STATUS_ADDJOB)) {
  148. if ((pSpool->TypeofHandle & PRINTER_HANDLE_PORT) &&
  149. (pIniPort = pSpool->pIniPort) &&
  150. (pIniPort->signature == IPO_SIGNATURE)) {
  151. if (!(PrintingDirectlyToPort(pSpool, Level, pDocInfo, &JobId))) {
  152. return FALSE;
  153. }
  154. } else if ((pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER) &&
  155. (pIniPrinter = pSpool->pIniPrinter)) {
  156. bPrintingDirect = FALSE;
  157. if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DIRECT) {
  158. bPrintingDirect = TRUE;
  159. } else {
  160. EnterSplSem();
  161. bPrintingDirect = IsGoingToFile(pDocInfo1->pOutputFile,
  162. pSpool->pIniSpooler);
  163. LeaveSplSem();
  164. }
  165. if (bPrintingDirect) {
  166. if (!PrintingDirect(pSpool, Level, pDocInfo))
  167. return FALSE;
  168. } else {
  169. if (!SpoolThisJob(pSpool, Level, pDocInfo))
  170. return FALSE;
  171. }
  172. } else
  173. LastError = ERROR_INVALID_PARAMETER;
  174. if (!LastError) {
  175. pSpool->Status |= SPOOL_STATUS_STARTDOC;
  176. pSpool->Status &= ~SPOOL_STATUS_CANCELLED;
  177. }
  178. } else
  179. LastError = ERROR_INVALID_HANDLE;
  180. if (LastError) {
  181. DBGMSG(DBG_WARNING, ("StartDoc FAILED %d\n", LastError));
  182. SetLastError(LastError);
  183. return FALSE;
  184. }
  185. if (JobId)
  186. return JobId;
  187. else
  188. return pSpool->pIniJob->JobId;
  189. }
  190. BOOL
  191. LocalStartPagePrinter(
  192. HANDLE hPrinter
  193. )
  194. /*++
  195. Bug-Bug: StartPagePrinter and EndPagePrinter calls should be
  196. supported only for SPOOL_STATUS_STARTDOC handles only. However
  197. because of our fixes for the engine, we cannot fail StartPagePrinter
  198. and EndPagePrinter for SPOOL_STATUS_ADDJOB as well.
  199. --*/
  200. {
  201. PSPOOL pSpool = (PSPOOL)hPrinter;
  202. HANDLE hFile = INVALID_HANDLE_VALUE;
  203. DWORD dwFileSize;
  204. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
  205. return(FALSE);
  206. }
  207. if (pSpool->Status & SPOOL_STATUS_CANCELLED) {
  208. SetLastError(ERROR_PRINT_CANCELLED);
  209. return FALSE;
  210. }
  211. if (pSpool->pIniJob != NULL) {
  212. if ( (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) &&
  213. ((pSpool->pIniJob->Status & JOB_PRINTING) ||
  214. (pSpool->pIniJob->Status & JOB_DESPOOLING))) {
  215. //
  216. // Account for Pages Printed in LocalEndPagePrinter
  217. //
  218. } else {
  219. // We Are Spooling
  220. UpdateJobAttributes(pSpool->pIniJob);
  221. pSpool->pIniJob->cLogicalPages++;
  222. if (pSpool->pIniJob->cLogicalPages >=
  223. pSpool->pIniJob->dwJobNumberOfPagesPerSide)
  224. {
  225. pSpool->pIniJob->cLogicalPages = 0;
  226. pSpool->pIniJob->cPages++;
  227. }
  228. if ( pSpool->pIniJob->Status & JOB_TYPE_ADDJOB ) {
  229. // If the Job is being written on the client side
  230. // the size is not getting updated so do it now on
  231. // the start page
  232. if ( pSpool->hReadFile != INVALID_HANDLE_VALUE ) {
  233. hFile = pSpool->hReadFile;
  234. } else {
  235. hFile = pSpool->pIniJob->hWriteFile;
  236. }
  237. if ( hFile != INVALID_HANDLE_VALUE ) {
  238. dwFileSize = GetFileSize( hFile, 0 );
  239. if ( pSpool->pIniJob->Size < dwFileSize ) {
  240. DBGMSG( DBG_TRACE, ("StartPagePrinter adjusting size old %d new %d\n",
  241. pSpool->pIniJob->Size, dwFileSize));
  242. pSpool->pIniJob->dwValidSize = pSpool->pIniJob->Size;
  243. pSpool->pIniJob->Size = dwFileSize;
  244. // Support for despooling whilst spooling
  245. // for Down Level jobs
  246. if (pSpool->pIniJob->WaitForWrite != NULL)
  247. SetEvent( pSpool->pIniJob->WaitForWrite );
  248. }
  249. }
  250. }
  251. }
  252. } else {
  253. DBGMSG(DBG_TRACE, ("StartPagePrinter issued with no Job\n"));
  254. }
  255. return TRUE;
  256. }
  257. PINIPORT
  258. FindFilePort(
  259. LPWSTR pFileName,
  260. PINISPOOLER pIniSpooler)
  261. {
  262. PINIPORT pIniPort;
  263. SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
  264. pIniPort = pIniSpooler->pIniPort;
  265. while (pIniPort) {
  266. if (!wcscmp(pIniPort->pName, pFileName)
  267. && (pIniPort->Status & PP_FILE)){
  268. return (pIniPort);
  269. }
  270. pIniPort = pIniPort->pNext;
  271. }
  272. return NULL;
  273. }
  274. PINIMONITOR
  275. FindFilePortMonitor(
  276. PINISPOOLER pIniSpooler
  277. )
  278. {
  279. PINIPORT pIniPort;
  280. SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
  281. pIniPort = pIniSpooler->pIniPort;
  282. while (pIniPort) {
  283. if (!wcscmp(pIniPort->pName, L"FILE:")) {
  284. return pIniPort->pIniMonitor;
  285. }
  286. pIniPort = pIniPort->pNext;
  287. }
  288. return NULL;
  289. }
  290. BOOL
  291. AddIniPrinterToIniPort(
  292. PINIPORT pIniPort,
  293. PINIPRINTER pIniPrinter
  294. )
  295. {
  296. DWORD i;
  297. PINIPRINTER *ppIniPrinter;
  298. //
  299. // If Printer already attatched to Port
  300. //
  301. for (i = 0; i < pIniPort->cPrinters; i++) {
  302. if (pIniPort->ppIniPrinter[i] == pIniPrinter) {
  303. return TRUE;
  304. }
  305. }
  306. ppIniPrinter = RESIZEPORTPRINTERS(pIniPort, 1);
  307. if ( ppIniPrinter != NULL ) {
  308. pIniPort->ppIniPrinter = ppIniPrinter;
  309. if ( !pIniPort->cPrinters )
  310. CreateRedirectionThread(pIniPort);
  311. pIniPort->ppIniPrinter[pIniPort->cPrinters++] = pIniPrinter;
  312. DBGMSG( DBG_TRACE, ("AddIniPrinterToIniPort pIniPrinter %x %ws pIniPort %x %ws\n",
  313. pIniPrinter, pIniPrinter->pName,
  314. pIniPort, pIniPort->pName ));
  315. return TRUE;
  316. } else {
  317. DBGMSG( DBG_WARNING, ("AddIniPrintertoIniPort failed pIniPort %x pIniPrinter %x error %d\n",
  318. pIniPort, pIniPrinter, GetLastError() ));
  319. return FALSE;
  320. }
  321. }
  322. BOOL
  323. AddIniPortToIniPrinter(
  324. IN PINIPRINTER pIniPrinter,
  325. IN PINIPORT pIniPort
  326. )
  327. /*++
  328. Routine Description:
  329. Adds a IniPort structure to a IniPrinter.
  330. A link between a printer and a port must be bi-directional.
  331. It is mandatory to call AddIniPrinterToIniPort in pair with this function or to handle the bi-dir link.
  332. Arguments:
  333. pIniPrinter - printer that will gonna use the port
  334. pIniPort - port to be assigned to printer
  335. Return Value:
  336. BOOL - TRUE if the port succesfully assigned to printer or if the printer is already assigned to port
  337. --*/
  338. {
  339. DWORD i;
  340. PINIPORT *ppIniPorts;
  341. //
  342. // Search if Port is already attached to Printer and return TRUE if it does
  343. //
  344. for (i = 0; i < pIniPrinter->cPorts; i++) {
  345. if (pIniPrinter->ppIniPorts[i] == pIniPort) {
  346. return TRUE;
  347. }
  348. }
  349. ppIniPorts = RESIZEPRINTERPORTS(pIniPrinter, 1);
  350. if ( ppIniPorts != NULL ) {
  351. pIniPrinter->ppIniPorts = ppIniPorts;
  352. pIniPrinter->ppIniPorts[pIniPrinter->cPorts++] = pIniPort;
  353. DBGMSG( DBG_TRACE, ("AddIniPortToIniPrinter pIniPort %x %ws pIniPrinter %x %ws\n",
  354. pIniPort, pIniPort->pName,
  355. pIniPrinter, pIniPrinter->pName ));
  356. return TRUE;
  357. } else {
  358. DBGMSG( DBG_WARNING, ("AddIniPrintertoIniPort failed pIniPort %x pIniPrinter %x error %d\n",
  359. pIniPort, pIniPrinter, GetLastError() ));
  360. return FALSE;
  361. }
  362. }
  363. VOID
  364. AddJobEntry(
  365. PINIPRINTER pIniPrinter,
  366. PINIJOB pIniJob
  367. )
  368. {
  369. DWORD Position;
  370. SplInSem();
  371. // DO NOT Add the Same Job more than once
  372. SPLASSERT(pIniJob != FindJob(pIniPrinter, pIniJob->JobId, &Position));
  373. pIniJob->pIniPrevJob = pIniPrinter->pIniLastJob;
  374. if (pIniJob->pIniPrevJob)
  375. pIniJob->pIniPrevJob->pIniNextJob = pIniJob;
  376. pIniPrinter->pIniLastJob = pIniJob;
  377. if (!pIniPrinter->pIniFirstJob)
  378. pIniPrinter->pIniFirstJob=pIniJob;
  379. }
  380. BOOL
  381. CheckDataTypes(
  382. PINIPRINTPROC pIniPrintProc,
  383. LPWSTR pDatatype
  384. )
  385. {
  386. PDATATYPES_INFO_1 pDatatypeInfo;
  387. DWORD i;
  388. pDatatypeInfo = (PDATATYPES_INFO_1)pIniPrintProc->pDatatypes;
  389. for (i=0; i<pIniPrintProc->cDatatypes; i++)
  390. if (!lstrcmpi(pDatatypeInfo[i].pName, pDatatype))
  391. return TRUE;
  392. return FALSE;
  393. }
  394. PINIPRINTPROC
  395. FindDatatype(
  396. PINIPRINTPROC pDefaultPrintProc,
  397. LPWSTR pDatatype
  398. )
  399. {
  400. PINIPRINTPROC pIniPrintProc;
  401. if ( pDatatype == NULL ) {
  402. return NULL;
  403. }
  404. //
  405. // !! HACK !!
  406. //
  407. // Our method of exposing NT EMF 1.00x is broken. EMF jobs are created
  408. // by GDI using NT EMF 1.003 on NT4 and 1.008 on Win2000. Therefore,
  409. // a print processor written for NT4 will not work on Win2000 because
  410. // they didn't know about the new datatype. Usually the print processor
  411. // doesn't parse the EMF. If they do, they are really broken.
  412. //
  413. // This hack is to call the IHV print processor with 1.008 EMF.
  414. //
  415. if (pDefaultPrintProc)
  416. {
  417. //
  418. // If the datatype is supported by the print processor OR
  419. // the datatype is NT EMF 1.008 (Win2000) and the print processor
  420. // supports NT EMF 1.003, then return this print processor.
  421. //
  422. if (CheckDataTypes(pDefaultPrintProc, pDatatype) ||
  423. (!_wcsicmp(pDatatype, gszNT5EMF) &&
  424. CheckDataTypes(pDefaultPrintProc, gszNT4EMF))) {
  425. return pDefaultPrintProc;
  426. }
  427. }
  428. pIniPrintProc = pThisEnvironment->pIniPrintProc;
  429. while ( pIniPrintProc ) {
  430. if ( CheckDataTypes( pIniPrintProc, pDatatype )) {
  431. return pIniPrintProc;
  432. }
  433. pIniPrintProc = pIniPrintProc->pNext;
  434. }
  435. DBGMSG( DBG_WARNING, ( "FindDatatype: Could not find Datatype\n") );
  436. return FALSE;
  437. }
  438. BOOL
  439. IsGoingToFile(
  440. LPWSTR pOutputFile,
  441. PINISPOOLER pIniSpooler)
  442. {
  443. PINIPORT pIniPort;
  444. LPWSTR pszShare;
  445. SplInSem();
  446. SPLASSERT(pIniSpooler->signature == ISP_SIGNATURE);
  447. // Validate the contents of the pIniJob->pOutputFile
  448. // if it is a valid file, then return true
  449. // if it is a port name or any other kind of name then ignore
  450. if (pOutputFile && *pOutputFile) {
  451. //
  452. // we have a non-null pOutputFile
  453. // match this with all available ports
  454. //
  455. pIniPort = pIniSpooler->pIniPort;
  456. while ( pIniPort ) {
  457. SPLASSERT( pIniPort->signature == IPO_SIGNATURE );
  458. if (!_wcsicmp( pIniPort->pName, pOutputFile )) {
  459. //
  460. // We have matched the pOutputFile field with a
  461. // valid port and the port is not a file port
  462. //
  463. if (pIniPort->Status & PP_FILE) {
  464. pIniPort = pIniPort->pNext;
  465. continue;
  466. }
  467. return FALSE;
  468. }
  469. pIniPort = pIniPort->pNext;
  470. }
  471. //
  472. // We have no port that matches exactly
  473. // so let's assume its a file.
  474. //
  475. // ugly hack -- check for Net: as the name
  476. //
  477. // This would normally match files like "NewFile" or "Nextbox,"
  478. // but since we always fully qualify filenames, we don't encounter
  479. // any problems.
  480. //
  481. if (!_wcsnicmp(pOutputFile, L"Ne", 2)) {
  482. return FALSE;
  483. }
  484. //
  485. // We have the problem LAN man ports coming as UNC path and being
  486. // treated as files. This is a HACK for that
  487. //
  488. if ( pOutputFile &&
  489. pOutputFile[0] == L'\\' &&
  490. pOutputFile[1] == L'\\' &&
  491. (pszShare = wcschr(pOutputFile+2, L'\\')) ) {
  492. pszShare++;
  493. if ( FindPrinter(pszShare, pIniSpooler) ||
  494. FindPrinterShare(pszShare, pIniSpooler) )
  495. return FALSE;
  496. }
  497. return TRUE;
  498. }
  499. return FALSE;
  500. }
  501. BOOL
  502. SpoolThisJob(
  503. PSPOOL pSpool,
  504. DWORD Level,
  505. LPBYTE pDocInfo
  506. )
  507. {
  508. WCHAR szFileName[MAX_PATH];
  509. PDOC_INFO_1 pDocInfo1=(PDOC_INFO_1)pDocInfo;
  510. HANDLE hImpersonationToken;
  511. DWORD dwId = 0;
  512. HANDLE hWriteFile = INVALID_HANDLE_VALUE;
  513. LPWSTR pszDatatype = NULL;
  514. HANDLE pSplFilePoolItem = NULL;
  515. HRESULT RetVal = S_OK;
  516. LPWSTR pszName = NULL;
  517. DBGMSG(DBG_TRACE, ("Spooling document %ws\n",
  518. pDocInfo1->pDocName ? pDocInfo1->pDocName : L""));
  519. if( pDocInfo1 && pDocInfo1->pDatatype ){
  520. pszDatatype = pDocInfo1->pDatatype;
  521. //
  522. // !! HACK !!
  523. //
  524. // We will do not support sending NT4 EMF to NT5 servers (NT EMF 1.003).
  525. // However, the HP LJ 1100 monolith installation program requires
  526. // that this datatype is available. So we added this back to winprint,
  527. // but we don't want people to use it. Therefore we will reject
  528. // the datatype here. Big hack.
  529. //
  530. if( !FindDatatype( NULL, pszDatatype ) ||
  531. !_wcsicmp(pszDatatype, gszNT4EMF)){
  532. DBGMSG(DBG_WARNING, ("Datatype %ws is invalid\n", pDocInfo1->pDatatype));
  533. SetLastError(ERROR_INVALID_DATATYPE);
  534. return FALSE;
  535. }
  536. }
  537. EnterSplSem();
  538. //
  539. // Check if we need to disallow EMF printing.
  540. //
  541. if( pSpool->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_RAW_ONLY ){
  542. if( !pszDatatype ){
  543. pszDatatype = pSpool->pDatatype ?
  544. pSpool->pDatatype :
  545. pSpool->pIniPrinter->pDatatype;
  546. }
  547. if( !ValidRawDatatype( pszDatatype )){
  548. LeaveSplSem();
  549. DBGMSG(DBG_WARNING, ("Datatype %ws is not RAW to a RAW printer\n", pDocInfo1->pDatatype));
  550. SetLastError(ERROR_INVALID_DATATYPE);
  551. return FALSE;
  552. }
  553. }
  554. dwId = GetNextId( pSpool->pIniPrinter->pIniSpooler->hJobIdMap );
  555. //
  556. // If we are using keep printed jobs, or an independent spool directory
  557. // exists for this printer, then we don't want to use the file pool.
  558. //
  559. if ( pSpool->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS ||
  560. pSpool->pIniPrinter->pSpoolDir)
  561. {
  562. GetFullNameFromId(pSpool->pIniPrinter, dwId, TRUE,
  563. szFileName, FALSE);
  564. }
  565. LeaveSplSem();
  566. SplOutSem();
  567. //
  568. // WMI Trace Event.
  569. //
  570. LogWmiTraceEvent(dwId, EVENT_TRACE_TYPE_SPL_SPOOLJOB, NULL);
  571. if (!(hImpersonationToken = RevertToPrinterSelf())) {
  572. DBGMSG(DBG_WARNING, ("SpoolThisJob RevertToPrinterSelf failed: %d\n", GetLastError()));
  573. SplOutSem();
  574. return FALSE;
  575. }
  576. //
  577. // If keep printed jobs is enabled for this printer, or if the printer has
  578. // a spool directory, then we don't use the file pool.
  579. //
  580. if (pSpool->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS ||
  581. pSpool->pIniPrinter->pSpoolDir)
  582. {
  583. hWriteFile = CreateFile(szFileName,
  584. GENERIC_WRITE | GENERIC_READ,
  585. FILE_SHARE_READ | FILE_SHARE_WRITE,
  586. NULL,
  587. CREATE_ALWAYS,
  588. FILE_ATTRIBUTE_NORMAL |
  589. FILE_FLAG_SEQUENTIAL_SCAN,
  590. NULL);
  591. }
  592. else
  593. {
  594. //
  595. // We're not keeping Printed Jobs, use the Pool.
  596. //
  597. //
  598. // This sets up the Spool and Shadow Files at the same time.
  599. //
  600. RetVal = GetFileItemHandle(pSpool->pIniPrinter->pIniSpooler->hFilePool, &pSplFilePoolItem, NULL);
  601. if (SUCCEEDED(RetVal))
  602. {
  603. RetVal = GetWriterFromHandle(pSplFilePoolItem, &hWriteFile, TRUE);
  604. if (FAILED(RetVal))
  605. {
  606. hWriteFile = INVALID_HANDLE_VALUE;
  607. }
  608. RetVal = GetNameFromHandle(pSplFilePoolItem, &pszName, TRUE);
  609. if (SUCCEEDED(RetVal))
  610. {
  611. wcscpy(szFileName, pszName);
  612. }
  613. else
  614. {
  615. wcscpy(szFileName, L"");
  616. }
  617. }
  618. else
  619. {
  620. hWriteFile = INVALID_HANDLE_VALUE;
  621. }
  622. }
  623. if (!ImpersonatePrinterClient(hImpersonationToken)) {
  624. DBGMSG(DBG_WARNING, ("SpoolThisJob ImpersonatePrinterClient failed: %d\n", GetLastError()));
  625. SplOutSem();
  626. return FALSE;
  627. }
  628. if ( hWriteFile == INVALID_HANDLE_VALUE ) {
  629. DBGMSG(DBG_WARNING, ("SpoolThisJob CreateFile( %ws ) GENERIC_WRITE failed: Error %d\n",
  630. szFileName, GetLastError()));
  631. SplOutSem();
  632. return FALSE;
  633. } else {
  634. DBGMSG(DBG_TRACE, ("SpoolThisJob CreateFile( %ws) GENERIC_WRITE Success:hWriteFile %x\n",szFileName, hWriteFile));
  635. }
  636. EnterSplSem();
  637. if( !(pSpool->pIniJob = CreateJobEntry(pSpool,
  638. Level,
  639. pDocInfo,
  640. dwId,
  641. !IsLocalCall(),
  642. 0,
  643. NULL)))
  644. {
  645. LeaveSplSem();
  646. if ( pSplFilePoolItem )
  647. {
  648. FinishedWriting(pSplFilePoolItem, TRUE);
  649. ReleasePoolHandle(&pSplFilePoolItem);
  650. }
  651. else
  652. {
  653. CloseHandle( hWriteFile );
  654. DeleteFile( szFileName );
  655. }
  656. SplOutSem();
  657. return FALSE;
  658. }
  659. if ( pSplFilePoolItem )
  660. {
  661. pSpool->pIniJob->hFileItem = pSplFilePoolItem;
  662. if ( pszName )
  663. {
  664. pSpool->pIniJob->pszSplFileName = pszName;
  665. }
  666. }
  667. SPLASSERT(!IsGoingToFile(pSpool->pIniJob->pOutputFile,
  668. pSpool->pIniSpooler));
  669. pSpool->pIniJob->Status |= JOB_SPOOLING;
  670. // Gather Stress Information for Max Number of concurrent spooling jobs
  671. pSpool->pIniPrinter->cSpooling++;
  672. if (pSpool->pIniPrinter->cSpooling > pSpool->pIniPrinter->cMaxSpooling)
  673. pSpool->pIniPrinter->cMaxSpooling = pSpool->pIniPrinter->cSpooling;
  674. pSpool->pIniJob->hWriteFile = hWriteFile;
  675. //
  676. // !! NOTE !!
  677. //
  678. // Removed WriteShadowJob call here.
  679. //
  680. // We shouldn't need it because if the job is spooling and we
  681. // restart the spooler, we won't accept the shadow file because it's
  682. // not yet completely spooled. Once it has spooled, the EndDocPrinter
  683. // will call WriteShadowJob, so we should be fine.
  684. //
  685. AddJobEntry(pSpool->pIniPrinter, pSpool->pIniJob);
  686. // This bit can be set in the print to file case. Clear it for
  687. // a following spool job. Bit should really be in the job.
  688. pSpool->TypeofHandle &= ~PRINTER_HANDLE_DIRECT;
  689. SetPrinterChange(pSpool->pIniPrinter,
  690. pSpool->pIniJob,
  691. NVAddJob,
  692. PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER,
  693. pSpool->pIniSpooler);
  694. //
  695. // RapidPrint might start despooling right away
  696. //
  697. CHECK_SCHEDULER();
  698. LeaveSplSem();
  699. SplOutSem();
  700. return TRUE;
  701. }
  702. BOOL
  703. PrintingDirect(
  704. PSPOOL pSpool,
  705. DWORD Level,
  706. LPBYTE pDocInfo
  707. )
  708. {
  709. PDOC_INFO_1 pDocInfo1=(PDOC_INFO_1)pDocInfo;
  710. PINIPORT pIniPort = NULL;
  711. BOOL bGoingToFile = FALSE;
  712. DWORD dwId = 0; // WMI Var
  713. DBGMSG(DBG_TRACE, ("Printing document %ws direct\n",
  714. pDocInfo1->pDocName ? pDocInfo1->pDocName : L"(Null)"));
  715. if (pDocInfo1 &&
  716. pDocInfo1->pDatatype &&
  717. !ValidRawDatatype(pDocInfo1->pDatatype)) {
  718. DBGMSG(DBG_WARNING, ("Datatype is not RAW\n"));
  719. SetLastError(ERROR_INVALID_DATATYPE);
  720. return FALSE;
  721. }
  722. EnterSplSem();
  723. if (pDocInfo1 && pDocInfo1->pOutputFile
  724. && IsGoingToFile(pDocInfo1->pOutputFile, pSpool->pIniSpooler)) {
  725. bGoingToFile = TRUE;
  726. }
  727. if (bGoingToFile) {
  728. //
  729. // If we already have a thread/process printing to this filename
  730. // fail. Do not allow multiple processes/threads to write to the
  731. // same output file.
  732. //
  733. if (FindFilePort(pDocInfo1->pOutputFile, pSpool->pIniSpooler)) {
  734. LeaveSplSem();
  735. SetLastError(ERROR_SHARING_VIOLATION);
  736. return(FALSE);
  737. }
  738. }
  739. //
  740. // WMI Trace Events
  741. //
  742. dwId = GetNextId( pSpool->pIniPrinter->pIniSpooler->hJobIdMap );
  743. LeaveSplSem();
  744. LogWmiTraceEvent(dwId, EVENT_TRACE_TYPE_SPL_SPOOLJOB, NULL);
  745. EnterSplSem();
  746. pSpool->pIniJob = CreateJobEntry(
  747. pSpool,
  748. Level,
  749. pDocInfo,
  750. dwId,
  751. !IsLocalCall(),
  752. JOB_DIRECT,
  753. NULL);
  754. if (!pSpool->pIniJob) {
  755. LeaveSplSem();
  756. return FALSE;
  757. }
  758. pSpool->pIniJob->StartDocComplete = CreateEvent( NULL,
  759. EVENT_RESET_AUTOMATIC,
  760. EVENT_INITIAL_STATE_NOT_SIGNALED,
  761. NULL );
  762. pSpool->pIniJob->WaitForWrite = CreateEvent( NULL,
  763. EVENT_RESET_AUTOMATIC,
  764. EVENT_INITIAL_STATE_NOT_SIGNALED,
  765. NULL );
  766. pSpool->pIniJob->WaitForRead = CreateEvent( NULL,
  767. EVENT_RESET_AUTOMATIC,
  768. EVENT_INITIAL_STATE_NOT_SIGNALED,
  769. NULL );
  770. AddJobEntry(pSpool->pIniPrinter, pSpool->pIniJob);
  771. pSpool->TypeofHandle |= PRINTER_HANDLE_DIRECT;
  772. if (bGoingToFile) {
  773. PINIMONITOR pIniMonitor;
  774. pSpool->pIniJob->Status |= JOB_PRINT_TO_FILE;
  775. pIniMonitor = FindFilePortMonitor( pSpool->pIniSpooler );
  776. pIniPort = CreatePortEntry( pSpool->pIniJob->pOutputFile,
  777. pIniMonitor, pSpool->pIniSpooler);
  778. if (!pIniPort) {
  779. DECJOBREF(pSpool->pIniJob);
  780. DeleteJob(pSpool->pIniJob, NO_BROADCAST);
  781. LeaveSplSem();
  782. return FALSE;
  783. }
  784. pIniPort->Status |= PP_FILE;
  785. AddIniPrinterToIniPort(pIniPort, pSpool->pIniPrinter);
  786. }
  787. CHECK_SCHEDULER();
  788. if (pSpool->pIniJob->pIniPort) {
  789. SplInSem();
  790. pSpool->pIniJob->Status |= JOB_PRINTING;
  791. }
  792. SetPrinterChange(pSpool->pIniPrinter,
  793. pSpool->pIniJob,
  794. NVAddJob,
  795. PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER,
  796. pSpool->pIniSpooler);
  797. LeaveSplSem();
  798. SplOutSem();
  799. // Wait until the port thread calls StartDocPrinter through
  800. // the print processor:
  801. DBGMSG(DBG_PORT, ("PrintingDirect: Calling WaitForSingleObject( %x )\n",
  802. pSpool->pIniJob->StartDocComplete));
  803. WaitForSingleObject( pSpool->pIniJob->StartDocComplete, INFINITE );
  804. EnterSplSem();
  805. // Close the event and set its value to NULL.
  806. // If anything goes wrong, or if the job gets cancelled,
  807. // the port thread will check this event, and if it's non-NULL,
  808. // it will set it to allow this thread to wake up.
  809. DBGMSG(DBG_PORT, ("PrintingDirect: Calling CloseHandle( %x )\n",
  810. pSpool->pIniJob->StartDocComplete));
  811. CloseHandle(pSpool->pIniJob->StartDocComplete);
  812. pSpool->pIniJob->StartDocComplete = NULL;
  813. /* If an error occurred, set the error on this thread:
  814. */
  815. if (pSpool->pIniJob->StartDocError) {
  816. SetLastError(pSpool->pIniJob->StartDocError);
  817. // We have to decrement by 2 because we've just created this job
  818. // in CreateJobEntry setting it to 1 and the other thread who
  819. // actually failed the StartDoc above (PortThread) did
  820. // not know to blow away the job. He just failed the StartDocPort.
  821. // No, we don't have to decrement by 2 because the PortThread
  822. // decrement does go through, am restoring to decrement by 1
  823. SPLASSERT(pSpool->pIniJob->cRef != 0);
  824. DECJOBREF(pSpool->pIniJob);
  825. DeleteJobCheck(pSpool->pIniJob);
  826. DBGMSG(DBG_TRACE, ("PrintingDirect:cRef %d\n", pSpool->pIniJob->cRef));
  827. LeaveSplSem();
  828. return FALSE;
  829. }
  830. LeaveSplSem();
  831. return TRUE;
  832. }
  833. VOID
  834. ClearJobError(
  835. PINIJOB pIniJob
  836. )
  837. /*++
  838. Routine Description:
  839. Clears the error status bits of a job.
  840. This routine should be called when port monitor successfully
  841. sends bytes to the printer.
  842. Arguments:
  843. pIniJob - Job in error state that should be cleared.
  844. Return Value:
  845. --*/
  846. {
  847. SplInSem();
  848. pIniJob->Status &= ~(JOB_PAPEROUT | JOB_OFFLINE | JOB_ERROR);
  849. SetPrinterChange( pIniJob->pIniPrinter,
  850. pIniJob,
  851. NVJobStatus,
  852. PRINTER_CHANGE_SET_JOB,
  853. pIniJob->pIniPrinter->pIniSpooler );
  854. }
  855. BOOL
  856. LocalCloseSpoolFileHandle(
  857. HANDLE hPrinter)
  858. /*++
  859. Function Description: Sets the end of file pointer for the spool file. In memory mapped writes
  860. the spool size grows in 64K chunks and it needs to be truncated after the
  861. writes are completed.
  862. Parameters: hPrinter -- printer handle
  863. Return Values: TRUE if successful;
  864. FALSE otherwise
  865. --*/
  866. {
  867. BOOL bReturn = TRUE;
  868. DWORD LastError = ERROR_SUCCESS;
  869. PSPOOL pSpool = (PSPOOL) hPrinter;
  870. EnterSplSem();
  871. // Check handle validity
  872. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER)) {
  873. LastError = ERROR_INVALID_HANDLE;
  874. } else if (!(pSpool->Status & SPOOL_STATUS_STARTDOC) ||
  875. (pSpool->Status & SPOOL_STATUS_ADDJOB)) {
  876. LastError = ERROR_SPL_NO_STARTDOC;
  877. } else if (!pSpool->pIniJob ||
  878. (pSpool->pIniJob->hWriteFile == INVALID_HANDLE_VALUE) ||
  879. (pSpool->TypeofHandle & (PRINTER_HANDLE_PORT |
  880. PRINTER_HANDLE_DIRECT)) ||
  881. !(pSpool->pIniJob->Status & JOB_TYPE_OPTIMIZE)) {
  882. LastError = ERROR_INVALID_HANDLE;
  883. } else if ((pSpool->Status & SPOOL_STATUS_CANCELLED) &&
  884. (pSpool->pIniJob->Status & (JOB_PENDING_DELETION))) {
  885. LastError = ERROR_PRINT_CANCELLED;
  886. }
  887. if (LastError) {
  888. SetLastError(LastError);
  889. bReturn = FALSE;
  890. goto CleanUp;
  891. }
  892. if (!(pSpool->pIniJob->Status & JOB_DESPOOLING)) {
  893. // Move the file pointer to the number of bytes committed and set the end of
  894. // file.
  895. if (SetFilePointer(pSpool->pIniJob->hWriteFile, pSpool->pIniJob->dwValidSize,
  896. NULL, FILE_BEGIN) != 0xffffffff) {
  897. SetEndOfFile(pSpool->pIniJob->hWriteFile);
  898. }
  899. }
  900. CleanUp:
  901. LeaveSplSem();
  902. return bReturn;
  903. }
  904. BOOL
  905. LocalCommitSpoolData(
  906. HANDLE hPrinter,
  907. DWORD cbCommit)
  908. /*++
  909. Function Description: This function updates the Valid data size in the spool file.
  910. The application writes directly into the spool file and commits the
  911. data written using CommitSpoolData.
  912. Parameters: hPrinter -- printer handle
  913. cbCommit -- number of bytes to be committed
  914. Return Values: TRUE if successful;
  915. FALSE otherwise
  916. --*/
  917. {
  918. BOOL bReturn = TRUE;
  919. DWORD LastError = ERROR_SUCCESS, dwPosition;
  920. PSPOOL pSpool = (PSPOOL) hPrinter;
  921. PINIJOB pIniJob = NULL, pChainedJob;
  922. if (!cbCommit) {
  923. return bReturn;
  924. }
  925. EnterSplSem();
  926. // Check handle validity
  927. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER)) {
  928. LastError = ERROR_INVALID_HANDLE;
  929. } else if (!(pSpool->Status & SPOOL_STATUS_STARTDOC) ||
  930. (pSpool->Status & SPOOL_STATUS_ADDJOB)) {
  931. LastError = ERROR_SPL_NO_STARTDOC;
  932. } else if (!pSpool->pIniJob ||
  933. (pSpool->pIniJob->hWriteFile == INVALID_HANDLE_VALUE) ||
  934. (pSpool->TypeofHandle & (PRINTER_HANDLE_PORT|
  935. PRINTER_HANDLE_DIRECT)) ||
  936. !(pSpool->pIniJob->Status & JOB_TYPE_OPTIMIZE)) {
  937. LastError = ERROR_INVALID_HANDLE;
  938. } else if ((pSpool->Status & SPOOL_STATUS_CANCELLED) ||
  939. (pSpool->pIniJob->Status & (JOB_PENDING_DELETION))) {
  940. LastError = ERROR_PRINT_CANCELLED;
  941. }
  942. if (LastError) {
  943. SetLastError(LastError);
  944. bReturn = FALSE;
  945. goto CleanUp;
  946. }
  947. pIniJob = pSpool->pIniJob;
  948. pIniJob->dwValidSize += cbCommit;
  949. pIniJob->Size += cbCommit;
  950. SetFilePointer(pIniJob->hWriteFile, cbCommit, NULL, FILE_CURRENT);
  951. // Chained job size include all the jobs in the chain
  952. // But since the next jobs size field will have the size
  953. // of all subsequent jobs we do not need to walk thru the
  954. // whole chain
  955. if (pIniJob->NextJobId) {
  956. if (pChainedJob = FindJob(pSpool->pIniPrinter,
  957. pIniJob->NextJobId,
  958. &dwPosition)) {
  959. pIniJob->Size += pChainedJob->Size;
  960. } else {
  961. SPLASSERT(pChainedJob != NULL);
  962. }
  963. }
  964. // SetEvent on WaitForSeek if sufficient number bytes have been written out.
  965. SeekPrinterSetEvent(pSpool->pIniJob, NULL, FALSE);
  966. // For Printing whilst Despooling, make sure we have enough bytes before
  967. // scheduling this job
  968. if (((pIniJob->dwValidSize - cbCommit) < dwFastPrintSlowDownThreshold) &&
  969. (pIniJob->dwValidSize >= dwFastPrintSlowDownThreshold) &&
  970. (pIniJob->WaitForWrite == NULL)) {
  971. CHECK_SCHEDULER();
  972. }
  973. // Support for despooling whilst spooling
  974. if ( pIniJob->WaitForWrite != NULL )
  975. SetEvent( pIniJob->WaitForWrite );
  976. SetPrinterChange(pSpool->pIniPrinter,
  977. pIniJob,
  978. NVSpoolJob,
  979. PRINTER_CHANGE_WRITE_JOB,
  980. pSpool->pIniSpooler);
  981. // If there was no error, and the job was marked in an error
  982. // state, clear it.
  983. if (pIniJob->Status & (JOB_PAPEROUT | JOB_OFFLINE | JOB_ERROR)) {
  984. ClearJobError(pIniJob);
  985. }
  986. CleanUp:
  987. LeaveSplSem();
  988. return bReturn;
  989. }
  990. BOOL
  991. LocalGetSpoolFileHandle(
  992. HANDLE hPrinter,
  993. LPWSTR *pSpoolDir,
  994. LPHANDLE phFile,
  995. HANDLE hSpoolerProcess,
  996. HANDLE hAppProcess)
  997. /*++
  998. Function Description: This function duplicates the spoolfile handle for local jobs into the
  999. applications process space. For remote jobs it returns the spool directory.
  1000. The router will create a temp file and return its handle to the
  1001. application.
  1002. Parameters: hPrinter -- printer handle
  1003. pSpoolDir -- pointer to recieve the spool directory
  1004. phFile -- pointer to get the duplicate handle
  1005. hSpoolerProcess -- spooler process handle
  1006. hAppProcess -- application process handle
  1007. Return Values: TRUE if the LOCAL job and handle can be duplicated
  1008. OR REMOTE job and spool directory is available
  1009. FALSE otherwise
  1010. --*/
  1011. {
  1012. BOOL bReturn = TRUE, bDuplicate;
  1013. PSPOOL pSpool;
  1014. DWORD LastError = 0;
  1015. PMAPPED_JOB pMappedJob = NULL, pTempMappedJob;
  1016. LPWSTR pszSpoolFile = NULL;
  1017. if (pSpoolDir) {
  1018. *pSpoolDir = NULL;
  1019. }
  1020. if (phFile) {
  1021. *phFile = INVALID_HANDLE_VALUE;
  1022. }
  1023. if ((hPrinter && !phFile) || (!hPrinter && !pSpoolDir)) {
  1024. SetLastError(ERROR_INVALID_PARAMETER);
  1025. return FALSE;
  1026. }
  1027. EnterSplSem();
  1028. // For a local hPrinter return the SpoolFile handle
  1029. if (hPrinter) {
  1030. pSpool = (PSPOOL) hPrinter;
  1031. // Check handle validity
  1032. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER)) {
  1033. LastError = ERROR_INVALID_HANDLE;
  1034. } else if (!(pSpool->Status & SPOOL_STATUS_STARTDOC) ||
  1035. (pSpool->Status & SPOOL_STATUS_ADDJOB)) {
  1036. LastError = ERROR_SPL_NO_STARTDOC;
  1037. } else if (!pSpool->pIniJob ||
  1038. (pSpool->pIniJob->hWriteFile == INVALID_HANDLE_VALUE) ||
  1039. (pSpool->TypeofHandle & (PRINTER_HANDLE_PORT |
  1040. PRINTER_HANDLE_DIRECT))) {
  1041. LastError = ERROR_INVALID_HANDLE;
  1042. } else if ((pSpool->Status & SPOOL_STATUS_CANCELLED) &&
  1043. (pSpool->pIniJob->Status & JOB_PENDING_DELETION)) {
  1044. LastError = ERROR_PRINT_CANCELLED;
  1045. }
  1046. if (LastError) {
  1047. SetLastError(LastError);
  1048. bReturn = FALSE;
  1049. } else {
  1050. // Duplicate hWriteFile into the App process
  1051. bReturn = ((pMappedJob = AllocSplMem(sizeof( MAPPED_JOB ))) != NULL) &&
  1052. ((pszSpoolFile = AllocSplMem(MAX_PATH * sizeof( WCHAR ))) != NULL) &&
  1053. DuplicateHandle(hSpoolerProcess,
  1054. pSpool->pIniJob->hWriteFile,
  1055. hAppProcess,
  1056. phFile,
  1057. 0,
  1058. TRUE,
  1059. DUPLICATE_SAME_ACCESS);
  1060. if (bReturn) {
  1061. // Store the jobid and the spool file name in pSpool, so that in the event
  1062. // that EndDoc is not called by the application/GDI, the spooler can delete the
  1063. // spool file and free the job id from the id map on ClosePrinter.
  1064. pSpool->pIniJob->Status |= JOB_TYPE_OPTIMIZE;
  1065. if (pSpool->pIniJob->hFileItem != INVALID_HANDLE_VALUE)
  1066. {
  1067. wcscpy(pszSpoolFile, pSpool->pIniJob->pszSplFileName);
  1068. }
  1069. else
  1070. {
  1071. GetFullNameFromId(pSpool->pIniJob->pIniPrinter, pSpool->pIniJob->JobId, TRUE,
  1072. pszSpoolFile, FALSE);
  1073. }
  1074. // Avoid duplicate entries in the pSpool->pMappedJob list
  1075. bDuplicate = FALSE;
  1076. for (pTempMappedJob = pSpool->pMappedJob;
  1077. pTempMappedJob;
  1078. pTempMappedJob = pTempMappedJob->pNext) {
  1079. if (pTempMappedJob->JobId == pSpool->pIniJob->JobId) {
  1080. bDuplicate = TRUE;
  1081. break;
  1082. }
  1083. }
  1084. if (!bDuplicate) {
  1085. pMappedJob->pszSpoolFile = pszSpoolFile;
  1086. pMappedJob->JobId = pSpool->pIniJob->JobId;
  1087. pMappedJob->pNext = pSpool->pMappedJob;
  1088. pSpool->pMappedJob = pMappedJob;
  1089. } else {
  1090. FreeSplMem(pszSpoolFile);
  1091. FreeSplMem(pMappedJob);
  1092. }
  1093. }
  1094. }
  1095. } else {
  1096. // Use the default spool dir or spool\Printers
  1097. if (pLocalIniSpooler->pDefaultSpoolDir) {
  1098. *pSpoolDir = AllocSplStr(pLocalIniSpooler->pDefaultSpoolDir);
  1099. } else if (pLocalIniSpooler->pDir) {
  1100. *pSpoolDir = AllocSplMem( (wcslen(pLocalIniSpooler->pDir) +
  1101. wcslen(szPrinterDir) + 2) * sizeof(WCHAR));
  1102. if (*pSpoolDir) {
  1103. wsprintf(*pSpoolDir, L"%ws\\%ws", pLocalIniSpooler->pDir, szPrinterDir);
  1104. }
  1105. }
  1106. if (!*pSpoolDir) {
  1107. bReturn = FALSE;
  1108. }
  1109. }
  1110. LeaveSplSem();
  1111. if (!bReturn) {
  1112. if (pMappedJob) {
  1113. FreeSplMem(pMappedJob);
  1114. }
  1115. if (pszSpoolFile) {
  1116. FreeSplMem(pszSpoolFile);
  1117. }
  1118. }
  1119. return bReturn;
  1120. }
  1121. BOOL
  1122. LocalFlushPrinter(
  1123. HANDLE hPrinter,
  1124. LPVOID pBuf,
  1125. DWORD cbBuf,
  1126. LPDWORD pcWritten,
  1127. DWORD cSleep
  1128. )
  1129. /*++
  1130. Function Description: FlushPrinter is typically used by the driver to send a burst of zeros
  1131. to the printer and introduce a delay in the i/o line to the printer.
  1132. The spooler does not schedule any job for cSleep milliseconds.
  1133. The driver can call FlushPrinter several times to have a cumulative
  1134. effect. The printer could be sleeping for a long time, but that is acceptable
  1135. since the driver is authenticated to keep doing WritePrinters indefinitely.
  1136. Thus FlushPrinter does not introduce any security loophole.
  1137. Parameters: hPrinter - printer handle
  1138. pBuf - buffer to be sent to the printer
  1139. cbBuf - size of the buffer
  1140. pcWritten - pointer to return the number of bytes written
  1141. cSleep - sleep time in milliseconds.
  1142. Return Values: TRUE if successful;
  1143. FALSE otherwise
  1144. --*/
  1145. {
  1146. BOOL bReturn = FALSE;
  1147. DWORD CurrentTime;
  1148. PSPOOL pSpool = (PSPOOL)hPrinter;
  1149. PINIPORT pIniPort;
  1150. PINIMONITOR pIniMonitor;
  1151. EnterSplSem();
  1152. //
  1153. // Validate parameters
  1154. //
  1155. if (!pcWritten ||
  1156. (cbBuf && !pBuf))
  1157. {
  1158. SetLastError( ERROR_INVALID_PARAMETER );
  1159. goto CleanUp;
  1160. }
  1161. //
  1162. // FlushPrinter can be called only for port handles where a prior call to WritePrinter
  1163. // failed
  1164. //
  1165. if (!ValidateSpoolHandle( pSpool, PRINTER_HANDLE_SERVER ) ||
  1166. !(pSpool->TypeofHandle & PRINTER_HANDLE_PORT) ||
  1167. !(pSpool->Status & SPOOL_STATUS_FLUSH_PRINTER))
  1168. {
  1169. SetLastError( ERROR_INVALID_HANDLE );
  1170. goto CleanUp;
  1171. }
  1172. //
  1173. // Send the contents of the buffer to the port with a monitor. It doesn't make sense for
  1174. // file ports or masq printers.
  1175. //
  1176. pIniPort = pSpool->pIniPort;
  1177. if ( pIniPort && pIniPort->Status & PP_ERROR)
  1178. {
  1179. //
  1180. // Don't send more data to a printer who's writing to a port in error state. LocalFlushPrinter is called in order to reset the printer
  1181. // By doing this, when printer buffer gets full, the writing will hung and job cannot be restarted/deleted anymore
  1182. //
  1183. SetLastError( ERROR_PRINT_CANCELLED );
  1184. goto CleanUp;
  1185. }
  1186. if (pIniPort && (pIniPort->Status & PP_MONITOR))
  1187. {
  1188. pIniMonitor = pIniPort->pIniMonitor;
  1189. //
  1190. // Use the language monitor if one is present
  1191. //
  1192. if (pIniPort->pIniLangMonitor)
  1193. {
  1194. pIniMonitor = pIniPort->pIniLangMonitor;
  1195. }
  1196. if (pIniMonitor)
  1197. {
  1198. //
  1199. // LeaveSplSem before calling into the monitor
  1200. //
  1201. INCPORTREF( pIniPort );
  1202. pIniMonitor->cRef++;
  1203. *pcWritten = 0;
  1204. LeaveSplSem();
  1205. SplOutSem();
  1206. bReturn = (*pIniMonitor->Monitor2.pfnWritePort)( pIniPort->hPort,
  1207. pBuf,
  1208. cbBuf,
  1209. pcWritten );
  1210. EnterSplSem();
  1211. DECPORTREF( pIniPort );
  1212. --pIniMonitor->cRef;
  1213. }
  1214. }
  1215. //
  1216. // Update the IniPort to introduce cSleep ms delay before scheduling
  1217. //
  1218. if (pIniPort)
  1219. {
  1220. CurrentTime = GetTickCount();
  1221. if (pIniPort->bIdleTimeValid && (int)(pIniPort->IdleTime - CurrentTime) > 0)
  1222. {
  1223. pIniPort->IdleTime += cSleep;
  1224. }
  1225. else
  1226. {
  1227. pIniPort->IdleTime = CurrentTime + cSleep;
  1228. pIniPort->bIdleTimeValid = TRUE;
  1229. }
  1230. }
  1231. CleanUp:
  1232. LeaveSplSem();
  1233. return bReturn;
  1234. }
  1235. VOID
  1236. QuitThread(
  1237. LPHANDLE phThread,
  1238. DWORD dwThreadId,
  1239. BOOL bInsideSplSem
  1240. )
  1241. { //
  1242. // This function is called on LocalWritePrinter to destroy thread created on PromptErrorMessage
  1243. //
  1244. if( phThread && *phThread ) {
  1245. if( WAIT_TIMEOUT == WaitForSingleObject( *phThread, 0 )) {
  1246. if(bInsideSplSem){
  1247. SplInSem();
  1248. LeaveSplSem();
  1249. }
  1250. //
  1251. // See if the thread is still running or dismissed by user.
  1252. // If it is still running, wait for it to terminate before pIniJob can be freed.
  1253. //
  1254. MyPostThreadMessage( *phThread, dwThreadId, WM_QUIT, IDRETRY, 0 );
  1255. WaitForSingleObject( *phThread, INFINITE );
  1256. if(bInsideSplSem){
  1257. SplOutSem();
  1258. EnterSplSem();
  1259. }
  1260. }
  1261. CloseHandle( *phThread );
  1262. *phThread = NULL;
  1263. }
  1264. }
  1265. BOOL
  1266. LocalWritePrinter(
  1267. HANDLE hPrinter,
  1268. LPVOID pBuf,
  1269. DWORD cbBuf,
  1270. LPDWORD pcWritten
  1271. )
  1272. {
  1273. PSPOOL pSpool=(PSPOOL)hPrinter;
  1274. PINIPORT pIniPort;
  1275. DWORD cWritten, cTotal;
  1276. DWORD rc;
  1277. LPBYTE pByte=pBuf;
  1278. DWORD LastError=0;
  1279. PINIJOB pIniJob, pChainedJob;
  1280. PINIMONITOR pIniMonitor;
  1281. HANDLE hThread = NULL;
  1282. DWORD dwThreadId=0, dwPosition;
  1283. DWORD dwWaitingResult;
  1284. DWORD Size = 0;
  1285. *pcWritten = 0;
  1286. SplOutSem();
  1287. EnterSplSem();
  1288. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER ))
  1289. LastError = ERROR_INVALID_HANDLE;
  1290. else if (!(pSpool->Status & SPOOL_STATUS_STARTDOC))
  1291. LastError = ERROR_SPL_NO_STARTDOC;
  1292. else if (pSpool->Status & SPOOL_STATUS_ADDJOB)
  1293. LastError = ERROR_SPL_NO_STARTDOC;
  1294. else if (pSpool->pIniJob &&
  1295. !(pSpool->TypeofHandle & (PRINTER_HANDLE_PORT |
  1296. PRINTER_HANDLE_DIRECT)) &&
  1297. ((pSpool->pIniJob->hWriteFile == INVALID_HANDLE_VALUE) ||
  1298. (pSpool->pIniJob->Status & JOB_TYPE_OPTIMIZE))) {
  1299. LastError = ERROR_INVALID_HANDLE;
  1300. DBGMSG( DBG_TRACE, ("LocalWritePrinter: hWriteFile == INVALID_HANDLE_VALUE hPrinter %x\n",hPrinter ));
  1301. }
  1302. else if (pSpool->Status & SPOOL_STATUS_CANCELLED)
  1303. LastError = ERROR_PRINT_CANCELLED;
  1304. else if (pSpool->pIniJob && (pSpool->pIniJob->Status & JOB_PENDING_DELETION) )
  1305. LastError = ERROR_PRINT_CANCELLED;
  1306. pIniPort = pSpool->pIniPort;
  1307. if (LastError) {
  1308. DBGMSG(DBG_TRACE, ("WritePrinter LastError: %x hPrinter %x\n", LastError, hPrinter));
  1309. //
  1310. // Mark port handles to allow FlushPrinter to be called when WritePrinter fails.
  1311. //
  1312. if (LastError == ERROR_PRINT_CANCELLED &&
  1313. pSpool->TypeofHandle & PRINTER_HANDLE_PORT)
  1314. {
  1315. pSpool->Status |= SPOOL_STATUS_FLUSH_PRINTER;
  1316. }
  1317. LeaveSplSem();
  1318. SplOutSem();
  1319. SetLastError(LastError);
  1320. return FALSE;
  1321. }
  1322. LeaveSplSem();
  1323. SplOutSem();
  1324. //
  1325. // WMI Trace Events
  1326. //
  1327. // The port thread is already being tracked.
  1328. if (!(pSpool->TypeofHandle & PRINTER_HANDLE_PORT))
  1329. {
  1330. if( pSpool->pIniJob )
  1331. {
  1332. LogWmiTraceEvent(pSpool->pIniJob->JobId,
  1333. EVENT_TRACE_TYPE_SPL_TRACKTHREAD,
  1334. NULL);
  1335. }
  1336. else if ( pSpool->pIniPort && pSpool->pIniPort->pIniJob )
  1337. {
  1338. LogWmiTraceEvent(pSpool->pIniPort->pIniJob->JobId,
  1339. EVENT_TRACE_TYPE_SPL_TRACKTHREAD, NULL);
  1340. }
  1341. }
  1342. cWritten = cTotal = 0;
  1343. while (cbBuf) {
  1344. SplOutSem();
  1345. if ( pSpool->TypeofHandle & PRINTER_HANDLE_PORT ) {
  1346. //
  1347. // For a print pool, check if the port is in error state and if the event that syncronizes
  1348. // restarting is not null.
  1349. // A more natural testing if this synchronisation must be done is by testing against
  1350. // dwRestartJobOnPoolEnabled but this is bogus when dwRestartJobOnPoolEnabled is TRUE
  1351. // and SNMP is disabled ( LocalSetPort is not called and the event is not created )
  1352. //
  1353. EnterSplSem();
  1354. if ( (pSpool->pIniPrinter->cPorts > 1) &&
  1355. (pSpool->pIniPort->Status & PP_ERROR) &&
  1356. (pIniPort->hErrorEvent != NULL) ) {
  1357. //
  1358. // This event will be set on LocalSetPort when port gets into a non eror state
  1359. // or when timeout and job is restarted (on another port).
  1360. // Printing is cancelled if the event is not set in DelayErrorTime.
  1361. //
  1362. LeaveSplSem();
  1363. SplOutSem();
  1364. dwWaitingResult = WaitForSingleObject( pIniPort->hErrorEvent, pSpool->pIniSpooler->dwRestartJobOnPoolTimeout * 1000 );
  1365. EnterSplSem();
  1366. if( pSpool->pIniJob ){
  1367. pIniJob = pSpool->pIniJob;
  1368. } else if( pSpool->pIniPort && pSpool->pIniPort->pIniJob ){
  1369. pIniJob = pSpool->pIniPort->pIniJob;
  1370. } else {
  1371. pIniJob = NULL;
  1372. }
  1373. //
  1374. // Check if the job was be deleted or restarted
  1375. //
  1376. if( pIniJob && pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART)){
  1377. // We had started a message box. See if the thread is still running or dismissed by user.
  1378. // If it is still running, wait for it to terminate before pIniJob can be freed.
  1379. // We need to leave the semaphore, since the UI thread we
  1380. // are waiting on could need to acquire it.
  1381. QuitThread( &hThread, dwThreadId, TRUE );
  1382. SetLastError( ERROR_PRINT_CANCELLED );
  1383. rc = FALSE;
  1384. goto Fail;
  1385. }
  1386. //
  1387. // If the error problem wasn't solved , set the job on error and continue
  1388. //
  1389. if( dwWaitingResult == WAIT_TIMEOUT ){
  1390. pIniJob->Status |= JOB_ERROR;
  1391. SetPrinterChange( pIniJob->pIniPrinter,
  1392. pIniJob,
  1393. NVJobStatus,
  1394. PRINTER_CHANGE_SET_JOB,
  1395. pIniJob->pIniPrinter->pIniSpooler );
  1396. }
  1397. }
  1398. LeaveSplSem();
  1399. SplOutSem();
  1400. if ( pSpool->pIniPort->Status & PP_MONITOR ) {
  1401. if ( pSpool->pIniPort->pIniLangMonitor ) {
  1402. pIniMonitor = pSpool->pIniPort->pIniLangMonitor;
  1403. } else {
  1404. pIniMonitor = pSpool->pIniPort->pIniMonitor;
  1405. }
  1406. SplOutSem();
  1407. cWritten = 0;
  1408. rc = (*pIniMonitor->Monitor2.pfnWritePort)(
  1409. pSpool->pIniPort->hPort,
  1410. pByte,
  1411. cbBuf,
  1412. &cWritten );
  1413. //
  1414. // Only update if cWritten != 0. If it is zero
  1415. // (for instance, when hpmon is stuck at Status
  1416. // not available), then we go into a tight loop
  1417. // sending out notifications.
  1418. //
  1419. if (cWritten) {
  1420. //
  1421. // For stress Test information gather the total
  1422. // number of types written.
  1423. //
  1424. EnterSplSem();
  1425. pSpool->pIniPrinter->cTotalBytes.QuadPart =
  1426. pSpool->pIniPrinter->cTotalBytes.QuadPart +
  1427. cWritten;
  1428. LeaveSplSem();
  1429. SplOutSem();
  1430. } else {
  1431. if (rc && dwWritePrinterSleepTime) {
  1432. //
  1433. // Sleep to avoid consuming too much CPU.
  1434. // Hpmon has this problem where they return
  1435. // success, but don't write any bytes.
  1436. //
  1437. // Be very careful: this may get called several
  1438. // times by a monitor that writes a lot of zero
  1439. // bytes (perhaps at the beginning of jobs).
  1440. //
  1441. Sleep(dwWritePrinterSleepTime);
  1442. }
  1443. }
  1444. }
  1445. else {
  1446. DBGMSG(DBG_TRACE, ("LocalWritePrinter: Port has no monitor\n"));
  1447. if (pSpool->Status & SPOOL_STATUS_PRINT_FILE) {
  1448. DBGMSG(DBG_TRACE, ("LocalWritePrinter: Port has no monitor - writing to file\n"));
  1449. rc = WriteFile(pSpool->hFile, pByte, cbBuf, &cWritten, NULL);
  1450. } else {
  1451. DBGMSG(DBG_TRACE, ("LocalWritePrinter: Port has no monitor - calling into router\n"));
  1452. rc = WritePrinter(pSpool->hPort, pByte, cbBuf, &cWritten);
  1453. }
  1454. }
  1455. } else if ( pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT ) {
  1456. cWritten = WriteToPrinter(pSpool, pByte, cbBuf);
  1457. if (cWritten) {
  1458. pSpool->pIniJob->dwValidSize = pSpool->pIniJob->Size;
  1459. pSpool->pIniJob->Size+=cWritten;
  1460. EnterSplSem();
  1461. SetPrinterChange(pSpool->pIniPrinter,
  1462. pSpool->pIniJob,
  1463. NVSpoolJob,
  1464. PRINTER_CHANGE_WRITE_JOB,
  1465. pSpool->pIniSpooler);
  1466. LeaveSplSem();
  1467. }
  1468. SplOutSem();
  1469. rc = (BOOL)cWritten;
  1470. } else {
  1471. SplOutSem();
  1472. rc = WriteFile(pSpool->pIniJob->hWriteFile, pByte, cbBuf, &cWritten, NULL);
  1473. if (cWritten) {
  1474. Size = GetFileSize( pSpool->pIniJob->hWriteFile,NULL);
  1475. EnterSplSem();
  1476. pSpool->pIniJob->Size = Size;
  1477. //
  1478. // Chained job size include all the jobs in the chain
  1479. // But since the next jobs size field will have the size
  1480. // of all subsequent jobs we do not need to walk thru the
  1481. // whole chain
  1482. //
  1483. if ( pSpool->pIniJob->NextJobId ) {
  1484. if ( pChainedJob = FindJob(pSpool->pIniPrinter,
  1485. pSpool->pIniJob->NextJobId,
  1486. &dwPosition) )
  1487. pSpool->pIniJob->Size += pChainedJob->Size;
  1488. else
  1489. SPLASSERT(pChainedJob != NULL);
  1490. }
  1491. pSpool->pIniJob->dwValidSize = pSpool->pIniJob->Size;
  1492. // SetEvent on WaitForSeek if sufficient number bytes have been written out.
  1493. LeaveSplSem();
  1494. SeekPrinterSetEvent(pSpool->pIniJob, NULL, FALSE);
  1495. EnterSplSem();
  1496. //
  1497. // For Printing whilst Despooling, make sure we have enough bytes before
  1498. // scheduling this job
  1499. //
  1500. if (( (pSpool->pIniJob->Size - cWritten) < dwFastPrintSlowDownThreshold ) &&
  1501. ( pSpool->pIniJob->Size >= dwFastPrintSlowDownThreshold ) &&
  1502. ( pSpool->pIniJob->WaitForWrite == NULL )) {
  1503. CHECK_SCHEDULER();
  1504. }
  1505. // Support for despooling whilst spooling
  1506. if ( pSpool->pIniJob->WaitForWrite != NULL )
  1507. SetEvent( pSpool->pIniJob->WaitForWrite );
  1508. SetPrinterChange(pSpool->pIniPrinter,
  1509. pSpool->pIniJob,
  1510. NVSpoolJob,
  1511. PRINTER_CHANGE_WRITE_JOB,
  1512. pSpool->pIniSpooler);
  1513. LeaveSplSem();
  1514. SplOutSem();
  1515. }
  1516. }
  1517. SplOutSem();
  1518. (*pcWritten)+=cWritten;
  1519. cbBuf-=cWritten;
  1520. pByte+=cWritten;
  1521. EnterSplSem();
  1522. if( pSpool->pIniJob ){
  1523. pIniJob = pSpool->pIniJob;
  1524. } else if( pSpool->pIniPort && pSpool->pIniPort->pIniJob ){
  1525. pIniJob = pSpool->pIniPort->pIniJob;
  1526. } else {
  1527. pIniJob = NULL;
  1528. }
  1529. if( pIniJob ){
  1530. if( pIniJob && pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART) ){
  1531. // We had started a message box. See if the thread is still running or dismissed by user.
  1532. // If it is still running, wait for it to terminate before pIniJob can be freed.
  1533. // We need to leave the semaphore, since the UI thread we
  1534. // are waiting on could need to acquire it.
  1535. QuitThread( &hThread, dwThreadId, TRUE );
  1536. SetLastError( ERROR_PRINT_CANCELLED );
  1537. rc = FALSE;
  1538. goto Fail;
  1539. }
  1540. //
  1541. // If there was no error, and the job was marked in an error
  1542. // state, clear it.
  1543. //
  1544. if( rc &&
  1545. ( pIniJob->Status & (JOB_PAPEROUT | JOB_OFFLINE | JOB_ERROR ))){
  1546. ClearJobError( pIniJob );
  1547. }
  1548. }
  1549. LeaveSplSem();
  1550. //
  1551. // If we failed and we have more bytes to write, then put
  1552. // up the warning. Some monitors may return FALSE, but actually
  1553. // write data to the port. Therefore we need to check both rc
  1554. // and also cbBuf.
  1555. //
  1556. if (!rc && cbBuf)
  1557. {
  1558. //
  1559. // Warning: We are sending in a stack variable. We need to be sure
  1560. // the error UI thread is cleaned up before LocalWritePrinter()
  1561. // returns!
  1562. //
  1563. if( PromptWriteError( pSpool, &hThread, &dwThreadId ) == IDCANCEL )
  1564. {
  1565. //
  1566. // In this case I know thread will die by itself
  1567. //
  1568. CloseHandle(hThread);
  1569. hThread = NULL;
  1570. EnterSplSem();
  1571. goto Fail;
  1572. }
  1573. }
  1574. else
  1575. {
  1576. // We have started a message box and now the automatically
  1577. // retry has succeeded, we need to kill the message box
  1578. // and continue to print.
  1579. QuitThread( &hThread, dwThreadId, FALSE );
  1580. }
  1581. }
  1582. rc = TRUE;
  1583. EnterSplSem();
  1584. Fail:
  1585. SplInSem();
  1586. //
  1587. // Mark port handles to allow FlushPrinter to be called when WritePrinter fails.
  1588. //
  1589. if (!rc && (pSpool->TypeofHandle & PRINTER_HANDLE_PORT))
  1590. {
  1591. pSpool->Status |= SPOOL_STATUS_FLUSH_PRINTER;
  1592. }
  1593. LeaveSplSem();
  1594. DBGMSG(DBG_TRACE, ("WritePrinter Written %d : %d\n", *pcWritten, rc));
  1595. SplOutSem();
  1596. SPLASSERT( hThread == NULL );
  1597. return rc;
  1598. }
  1599. BOOL
  1600. WaitForSeekPrinter(
  1601. PSPOOL pSpool,
  1602. HANDLE hFile,
  1603. LARGE_INTEGER liSeekFilePosition,
  1604. DWORD dwMoveMethod
  1605. )
  1606. /*++
  1607. Function Description: WaitForSeekPrinter waits till there is enough data written to the
  1608. spool file before the file pointer can be moved.
  1609. Parameters: pSpool - pointer to the SPOOL struct.
  1610. hFile - handle to the file whose pointer is to be set.
  1611. liDistanceToMove - Offset to move the file pointer.
  1612. dwMoveMethod - position to take offset. FILE_BEGIN | FILE_CURRENT | FILE_END
  1613. Return Values: TRUE for success
  1614. FALSE otherwise
  1615. --*/
  1616. {
  1617. BOOL bWaitForWrite = FALSE, bReturn = FALSE;
  1618. DWORD dwFileSizeHigh, dwFileSizeLow, dwWaitResult;
  1619. LARGE_INTEGER liCurrentFilePosition;
  1620. // For Print while spooling wait till sufficient number of bytes have been written.
  1621. if ( pSpool->pIniJob->Status & JOB_SPOOLING ) {
  1622. if ( dwMoveMethod == FILE_END ) {
  1623. pSpool->pIniJob->bWaitForEnd = TRUE;
  1624. bWaitForWrite = TRUE;
  1625. } else {
  1626. // Save the current file position.
  1627. liCurrentFilePosition.QuadPart = 0;
  1628. liCurrentFilePosition.u.LowPart = SetFilePointer( hFile,
  1629. liCurrentFilePosition.u.LowPart,
  1630. &liCurrentFilePosition.u.HighPart,
  1631. FILE_CURRENT );
  1632. if ((liCurrentFilePosition.u.LowPart == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  1633. goto CleanUp;
  1634. }
  1635. // Get the current size of the file
  1636. if (pSpool->pIniJob->Status & JOB_TYPE_OPTIMIZE) {
  1637. dwFileSizeLow = pSpool->pIniJob->dwValidSize;
  1638. dwFileSizeHigh = 0;
  1639. } else {
  1640. dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
  1641. if ((dwFileSizeLow == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  1642. goto CleanUp;
  1643. }
  1644. }
  1645. // Set the new file pointer.
  1646. liSeekFilePosition.u.LowPart = SetFilePointer( hFile,
  1647. liSeekFilePosition.u.LowPart,
  1648. &liSeekFilePosition.u.HighPart,
  1649. dwMoveMethod );
  1650. if ((liSeekFilePosition.u.LowPart == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  1651. goto CleanUp;
  1652. }
  1653. // Reset the file pointer using the saved current file position.
  1654. liCurrentFilePosition.u.LowPart = SetFilePointer( hFile,
  1655. liCurrentFilePosition.u.LowPart,
  1656. &liCurrentFilePosition.u.HighPart,
  1657. FILE_BEGIN );
  1658. if ((liCurrentFilePosition.u.LowPart == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  1659. goto CleanUp;
  1660. }
  1661. // Check new position of the file pointer with the current file size.
  1662. if ((liSeekFilePosition.u.HighPart > (LONG)dwFileSizeHigh) ||
  1663. ( (liSeekFilePosition.u.HighPart == (LONG)dwFileSizeHigh) &&
  1664. (liSeekFilePosition.u.LowPart > dwFileSizeLow))) {
  1665. // Set the fields in INIJOB.
  1666. pSpool->pIniJob->liFileSeekPosn.QuadPart = liSeekFilePosition.QuadPart;
  1667. bWaitForWrite = TRUE;
  1668. }
  1669. }
  1670. if (bWaitForWrite) {
  1671. // Create and wait on an event. Exit the Spooler semaphore.
  1672. if (pSpool->pIniJob->WaitForSeek == NULL) {
  1673. pSpool->pIniJob->WaitForSeek = CreateEvent( NULL,
  1674. EVENT_RESET_AUTOMATIC,
  1675. EVENT_INITIAL_STATE_NOT_SIGNALED,
  1676. NULL );
  1677. }
  1678. if (!pSpool->pIniJob->WaitForSeek ||
  1679. !ResetEvent(pSpool->pIniJob->WaitForSeek)) {
  1680. pSpool->pIniJob->WaitForSeek = NULL;
  1681. goto CleanUp;
  1682. }
  1683. pSpool->pIniJob->bWaitForSeek = TRUE;
  1684. // Increment ref counts before leaving the semaphore
  1685. pSpool->cRef++;
  1686. INCJOBREF(pSpool->pIniJob);
  1687. LeaveSplSem();
  1688. dwWaitResult = WaitForSingleObject(pSpool->pIniJob->WaitForSeek,
  1689. INFINITE);
  1690. EnterSplSem();
  1691. pSpool->cRef--;
  1692. DECJOBREF(pSpool->pIniJob);
  1693. // If wait failed or the handles are invalid fail the call
  1694. if ((dwWaitResult == WAIT_FAILED) ||
  1695. (dwWaitResult == WAIT_TIMEOUT) ||
  1696. (pSpool->Status & SPOOL_STATUS_CANCELLED) ||
  1697. (pSpool->pIniJob->Status & (JOB_TIMEOUT | JOB_PENDING_DELETION |
  1698. JOB_ABANDON | JOB_RESTART))) {
  1699. goto CleanUp;
  1700. }
  1701. }
  1702. }
  1703. // Set the return value.
  1704. bReturn = TRUE;
  1705. CleanUp:
  1706. pSpool->pIniJob->bWaitForSeek = FALSE;
  1707. return bReturn;
  1708. }
  1709. BOOL
  1710. LocalSeekPrinter(
  1711. HANDLE hPrinter,
  1712. LARGE_INTEGER liDistanceToMove,
  1713. PLARGE_INTEGER pliNewPointer,
  1714. DWORD dwMoveMethod,
  1715. BOOL bWritePrinter
  1716. )
  1717. /*++
  1718. Routine Description: LocalSeekPrinter moves the file pointer in the spool file to the position
  1719. indicated by liDistanceToMove. This call is synchronous and it waits if
  1720. the job is being spooled and the required number of bytes have not been
  1721. written as yet.
  1722. Arguments: hPrinter - handle to the Printer.
  1723. liDistanceToMove - offset to move the file pointer.
  1724. pliNewPointer - pointer to a LARGE_INTEGER which will contain the new position
  1725. of the file pointer.
  1726. dwMoveMethod - position to take offset. FILE_BEGIN | FILE_CURRENT | FILE_END
  1727. Return Value: TRUE if the file pointer can be moved to the required location
  1728. FALSE otherwise.
  1729. --*/
  1730. {
  1731. PSPOOL pSpool = (PSPOOL)hPrinter;
  1732. HANDLE hFile;
  1733. BOOL bReturn = FALSE;
  1734. DWORD dwFileSizeHigh, dwFileSizeLow;
  1735. SplOutSem();
  1736. EnterSplSem();
  1737. // Check for handle validity
  1738. if( !ValidateSpoolHandle( pSpool, PRINTER_HANDLE_SERVER )){
  1739. DBGMSG( DBG_WARNING, ("LocalSeekPrinter ERROR_INVALID_HANDLE\n"));
  1740. goto CleanUp;
  1741. }
  1742. if( pSpool->Status & SPOOL_STATUS_CANCELLED ){
  1743. DBGMSG( DBG_WARNING, ("LocalSeekPrinter ERROR_PRINT_CANCELLED\n"));
  1744. SetLastError( ERROR_PRINT_CANCELLED );
  1745. goto CleanUp;
  1746. }
  1747. if( !( pSpool->TypeofHandle & PRINTER_HANDLE_JOB ) ||
  1748. ( pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT )){
  1749. DBGMSG( DBG_WARNING, ("LocalSeekPrinter: not a job handle, or direct\n" ));
  1750. SetLastError( ERROR_NOT_SUPPORTED );
  1751. goto CleanUp;
  1752. }
  1753. // Avoid waiting for jobs that can't print.
  1754. if( !pSpool->pIniJob ||
  1755. pSpool->pIniJob->Status & (JOB_TIMEOUT | JOB_ABANDON |
  1756. JOB_PENDING_DELETION | JOB_RESTART) ) {
  1757. DBGMSG( DBG_WARNING, ("LocalSeekPrinter ERROR_PRINT_CANCELLED\n"));
  1758. SetLastError( ERROR_PRINT_CANCELLED );
  1759. goto CleanUp;
  1760. }
  1761. // Seek fails while writing to the spool file.
  1762. if( bWritePrinter ) {
  1763. goto CleanUp;
  1764. } else {
  1765. hFile = pSpool->hReadFile;
  1766. }
  1767. // Wait for data to be written, if necessary.
  1768. if (!WaitForSeekPrinter( pSpool,
  1769. hFile,
  1770. liDistanceToMove,
  1771. dwMoveMethod )) {
  1772. goto CleanUp;
  1773. }
  1774. // Set the file pointer.
  1775. pliNewPointer->u.LowPart = SetFilePointer( hFile,
  1776. liDistanceToMove.u.LowPart,
  1777. &liDistanceToMove.u.HighPart,
  1778. dwMoveMethod );
  1779. if( pliNewPointer->u.LowPart == 0xffffffff && GetLastError() != NO_ERROR ){
  1780. goto CleanUp;
  1781. }
  1782. pliNewPointer->u.HighPart = liDistanceToMove.u.HighPart;
  1783. // Fail the call if the pointer is moved beyond the end of file.
  1784. // Get the current size of the file
  1785. if (pSpool->pIniJob->Status & JOB_TYPE_OPTIMIZE) {
  1786. dwFileSizeLow = pSpool->pIniJob->dwValidSize;
  1787. dwFileSizeHigh = 0;
  1788. } else {
  1789. dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
  1790. if ((dwFileSizeLow == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  1791. goto CleanUp;
  1792. }
  1793. }
  1794. // Check new position of the file pointer with the current file size.
  1795. if ((pliNewPointer->u.HighPart > (LONG)dwFileSizeHigh) ||
  1796. ( (pliNewPointer->u.HighPart == (LONG)dwFileSizeHigh) &&
  1797. (pliNewPointer->u.LowPart > dwFileSizeLow))) {
  1798. SetLastError(ERROR_NO_MORE_ITEMS);
  1799. goto CleanUp;
  1800. }
  1801. bReturn = TRUE;
  1802. CleanUp:
  1803. LeaveSplSem();
  1804. return bReturn;
  1805. }
  1806. BOOL
  1807. LocalEndPagePrinter(
  1808. HANDLE hPrinter
  1809. )
  1810. {
  1811. PSPOOL pSpool = (PSPOOL)hPrinter;
  1812. HANDLE hFile = INVALID_HANDLE_VALUE;
  1813. DWORD dwFileSize;
  1814. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
  1815. return(FALSE);
  1816. }
  1817. if (pSpool->Status & SPOOL_STATUS_CANCELLED) {
  1818. SetLastError(ERROR_PRINT_CANCELLED);
  1819. return FALSE;
  1820. }
  1821. if (pSpool->pIniJob != NULL) {
  1822. if ( (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) &&
  1823. ((pSpool->pIniJob->Status & JOB_PRINTING) ||
  1824. (pSpool->pIniJob->Status & JOB_DESPOOLING))) {
  1825. // Despooling ( RapidPrint )
  1826. UpdateJobAttributes(pSpool->pIniJob);
  1827. pSpool->pIniJob->cLogicalPagesPrinted++;
  1828. if (pSpool->pIniJob->cLogicalPagesPrinted >=
  1829. pSpool->pIniJob->dwDrvNumberOfPagesPerSide)
  1830. {
  1831. pSpool->pIniJob->cLogicalPagesPrinted = 0;
  1832. pSpool->pIniJob->cPagesPrinted++;
  1833. pSpool->pIniPrinter->cTotalPagesPrinted++;
  1834. }
  1835. } else {
  1836. //
  1837. // Spooling
  1838. //
  1839. if ( pSpool->pIniJob->Status & JOB_TYPE_ADDJOB ) {
  1840. // If the Job is being written on the client side
  1841. // the size is not getting updated so do it now on
  1842. // the start page
  1843. if ( pSpool->hReadFile != INVALID_HANDLE_VALUE ) {
  1844. hFile = pSpool->hReadFile;
  1845. } else {
  1846. hFile = pSpool->pIniJob->hWriteFile;
  1847. }
  1848. if ( hFile != INVALID_HANDLE_VALUE ) {
  1849. dwFileSize = GetFileSize( hFile, 0 );
  1850. if ( pSpool->pIniJob->Size < dwFileSize ) {
  1851. DBGMSG( DBG_TRACE, ("EndPagePrinter adjusting size old %d new %d\n",
  1852. pSpool->pIniJob->Size, dwFileSize));
  1853. pSpool->pIniJob->dwValidSize = pSpool->pIniJob->Size;
  1854. pSpool->pIniJob->Size = dwFileSize;
  1855. // Support for despooling whilst spooling
  1856. // for Down Level jobs
  1857. if (pSpool->pIniJob->WaitForWrite != NULL)
  1858. SetEvent( pSpool->pIniJob->WaitForWrite );
  1859. }
  1860. }
  1861. CHECK_SCHEDULER();
  1862. }
  1863. }
  1864. } else {
  1865. DBGMSG(DBG_TRACE, ("LocalEndPagePrinter issued with no Job\n"));
  1866. }
  1867. return TRUE;
  1868. }
  1869. BOOL
  1870. LocalAbortPrinter(
  1871. HANDLE hPrinter
  1872. )
  1873. {
  1874. PSPOOL pSpool=(PSPOOL)hPrinter;
  1875. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
  1876. DBGMSG( DBG_WARNING, ("ERROR in AbortPrinter: %x\n", ERROR_INVALID_HANDLE));
  1877. return FALSE;
  1878. }
  1879. if (!(pSpool->Status & SPOOL_STATUS_STARTDOC)) {
  1880. SetLastError(ERROR_SPL_NO_STARTDOC);
  1881. return(FALSE);
  1882. }
  1883. if (pSpool->pIniPort && !(pSpool->pIniPort->Status & PP_MONITOR)) {
  1884. if (pSpool->Status & SPOOL_STATUS_PRINT_FILE) {
  1885. if (!CloseHandle(pSpool->hFile)) {
  1886. return(FALSE);
  1887. }
  1888. pSpool->Status &= ~SPOOL_STATUS_PRINT_FILE;
  1889. pSpool->Status |= SPOOL_STATUS_CANCELLED;
  1890. return(TRUE);
  1891. } else {
  1892. return AbortPrinter(pSpool->hPort);
  1893. }
  1894. }
  1895. pSpool->Status |= SPOOL_STATUS_CANCELLED;
  1896. if (pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER) {
  1897. EnterSplSem();
  1898. if (pSpool->pIniJob) {
  1899. //
  1900. // Reset JOB_RESTART flag, otherwise the job doesn't get deleted.
  1901. // If AbortPrinter is called while the job is restarting, DeleteJob ignores the
  1902. // job if JOB_RESTART is set and the Scheduler also ignores the job since it is
  1903. // marked as JOB_PENDING_DELETION. The job would stay in Deleting-Restarting forever.
  1904. //
  1905. pSpool->pIniJob->Status |= JOB_PENDING_DELETION;
  1906. pSpool->pIniJob->Status &= ~JOB_RESTART;
  1907. //
  1908. // Release any thread waiting on LocalSetPort
  1909. //
  1910. SetPortErrorEvent(pSpool->pIniJob->pIniPort);
  1911. }
  1912. LeaveSplSem();
  1913. }
  1914. //
  1915. // KrishnaG - fixes bug 2646, we need to clean up AbortPrinter
  1916. // rewrite so that it doesn't fail on cases which EndDocPrinter should fail
  1917. // get rid of comment when done
  1918. //
  1919. LocalEndDocPrinter(hPrinter);
  1920. return TRUE;
  1921. }
  1922. BOOL
  1923. SplReadPrinter(
  1924. HANDLE hPrinter,
  1925. LPBYTE *pBuf,
  1926. DWORD cbBuf
  1927. )
  1928. /*++
  1929. Function Description: SplReadPrinter is an internal ReadPrinter which uses a memory mapped
  1930. spool file and returns the pointer to the required data in *pBuf.
  1931. Parameters: hPrinter -- printer handle
  1932. *pBuf -- pointer to the buffer (mapped view)
  1933. cbBuf -- number of bytes to be read
  1934. Return Value: TRUE if successful;
  1935. FALSE otherwise
  1936. --*/
  1937. {
  1938. DWORD NoBytesRead;
  1939. BOOL bReturn;
  1940. // Not used currently
  1941. bReturn = InternalReadPrinter(hPrinter, NULL, cbBuf, pBuf, &NoBytesRead, TRUE);
  1942. if (!bReturn && (GetLastError() == ERROR_SUCCESS)) {
  1943. // Memory mapped ReadPrinter may fail without setting the last error
  1944. SetLastError(ERROR_NOT_SUPPORTED);
  1945. }
  1946. return bReturn;
  1947. }
  1948. BOOL
  1949. LocalReadPrinter(
  1950. HANDLE hPrinter,
  1951. LPVOID pBuf,
  1952. DWORD cbBuf,
  1953. LPDWORD pNoBytesRead
  1954. )
  1955. /*++
  1956. Routine Description: LocalReadPrinter reads the required number of bytes(or available) into the
  1957. specified buffer.
  1958. Arguments: hPrinter -- printer handle
  1959. pBuf -- pointer to the buffer to store data
  1960. cbBuf -- number of bytes to be read
  1961. pNoBytesRead -- pointer to variable to return number of bytes read
  1962. Return Value: TRUE if successful;
  1963. FALSE otherwise
  1964. --*/
  1965. {
  1966. return InternalReadPrinter(hPrinter, pBuf, cbBuf, NULL, pNoBytesRead, FALSE);
  1967. }
  1968. BOOL
  1969. InternalReadPrinter(
  1970. HANDLE hPrinter,
  1971. LPVOID pBuf,
  1972. DWORD cbBuf,
  1973. LPBYTE *pMapBuffer,
  1974. LPDWORD pNoBytesRead,
  1975. BOOL bReadMappedView
  1976. )
  1977. /*++
  1978. Routine Description: InternalReadPrinter reads the required number of bytes(or available) into the
  1979. specified buffer or returns a pointer to the mapped file view.
  1980. Arguments: hPrinter -- printer handle
  1981. pBuf -- pointer to the buffer to store data
  1982. cbBuf -- number of bytes to be read
  1983. *pMapBuffer -- pointer to the mapped file view
  1984. pNoBytesRead -- pointer to variable to return number of bytes read
  1985. bReadMappedView -- flag for using mapped spool file
  1986. Return Value: TRUE if successful;
  1987. FALSE otherwise
  1988. --*/
  1989. {
  1990. PSPOOL pSpool=(PSPOOL)hPrinter;
  1991. DWORD Error=0, rc;
  1992. HANDLE hWait;
  1993. DWORD dwFileSize = 0, dwCurrentPosition, dwOldValidSize;
  1994. DWORD ThisPortSecsToWait;
  1995. DWORD cbReadSize = cbBuf;
  1996. DWORD SizeInFile = 0;
  1997. DWORD BytesAllowedToRead = 0;
  1998. NOTIFYVECTOR NotifyVector;
  1999. PINIMONITOR pIniMonitor;
  2000. SplOutSem();
  2001. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
  2002. DBGMSG( DBG_WARNING, ("LocalReadPrinter ERROR_INVALID_HANDLE\n"));
  2003. return FALSE;
  2004. }
  2005. if (pSpool->Status & SPOOL_STATUS_CANCELLED) {
  2006. DBGMSG( DBG_WARNING, ("LocalReadPrinter ERROR_PRINT_CANCELLED\n"));
  2007. SetLastError(ERROR_PRINT_CANCELLED);
  2008. return FALSE;
  2009. }
  2010. if ( pNoBytesRead != NULL ) {
  2011. *pNoBytesRead = 0;
  2012. }
  2013. if (bReadMappedView) {
  2014. // Supported only for JOB handles that aren't DIRECT
  2015. if ( !(pSpool->TypeofHandle & PRINTER_HANDLE_JOB) ||
  2016. pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT ) {
  2017. SetLastError(ERROR_NOT_SUPPORTED);
  2018. return FALSE;
  2019. }
  2020. }
  2021. if (pSpool->TypeofHandle & PRINTER_HANDLE_JOB) {
  2022. if (pSpool->pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART)) {
  2023. DBGMSG( DBG_WARNING, ("LocalReadPrinter Error IniJob->Status %x\n",pSpool->pIniJob->Status));
  2024. SetLastError(ERROR_PRINT_CANCELLED);
  2025. return FALSE;
  2026. }
  2027. if (pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT) {
  2028. *pNoBytesRead = ReadFromPrinter(pSpool, pBuf, cbReadSize);
  2029. SplOutSem();
  2030. return TRUE;
  2031. }
  2032. //
  2033. // Check that the user has read access.
  2034. //
  2035. if( !AccessGranted( SPOOLER_OBJECT_DOCUMENT,
  2036. JOB_READ,
  2037. pSpool )){
  2038. SetLastError( ERROR_ACCESS_DENIED );
  2039. return FALSE;
  2040. }
  2041. SplOutSem();
  2042. EnterSplSem();
  2043. //
  2044. // RapidPrint
  2045. //
  2046. // NOTE this while loop is ONLY in operation if during RapidPrint
  2047. // ie when we are Printing the same job we are Spooling
  2048. //
  2049. while (( pSpool->pIniJob->WaitForWrite != NULL ) &&
  2050. ( pSpool->pIniJob->Status & JOB_SPOOLING )) {
  2051. // Get the current file position.
  2052. dwCurrentPosition = SetFilePointer( pSpool->hReadFile,
  2053. 0,
  2054. NULL,
  2055. FILE_CURRENT );
  2056. if (dwCurrentPosition < pSpool->pIniJob->dwValidSize) {
  2057. // Wait is not required
  2058. break;
  2059. }
  2060. SplInSem();
  2061. //
  2062. // We cannot rely on pIniJob->Size to be accurate since for
  2063. // downlevel jobs or jobs that to AddJob they are writing
  2064. // to a file without calling WritePrinter.
  2065. // So we call the file system to get an accurate file size
  2066. //
  2067. dwFileSize = GetFileSize( pSpool->hReadFile, 0 );
  2068. if ( pSpool->pIniJob->Size != dwFileSize ) {
  2069. DBGMSG( DBG_TRACE, ("LocalReadPrinter adjusting size old %d new %d\n",
  2070. pSpool->pIniJob->Size, dwFileSize));
  2071. dwOldValidSize = pSpool->pIniJob->dwValidSize;
  2072. //
  2073. // Fix for print while spooling. If it was AddJobed, then
  2074. // the valid size is going to be the previous size, since
  2075. // we know the old data will be flushed by the time the new
  2076. // one is extended.
  2077. //
  2078. if( pSpool->pIniJob->Status & JOB_TYPE_ADDJOB ){
  2079. //
  2080. // The previous size becomes the next valid size.
  2081. //
  2082. pSpool->pIniJob->dwValidSize = pSpool->pIniJob->Size;
  2083. } else if (!(pSpool->pIniJob->Status & JOB_TYPE_OPTIMIZE)) {
  2084. //
  2085. // The valid size is not necessary for non-AddJob
  2086. // jobs, since the write has been committed.
  2087. //
  2088. pSpool->pIniJob->dwValidSize = dwFileSize;
  2089. }
  2090. pSpool->pIniJob->Size = dwFileSize;
  2091. if (dwOldValidSize != pSpool->pIniJob->dwValidSize) {
  2092. SetPrinterChange(pSpool->pIniPrinter,
  2093. pSpool->pIniJob,
  2094. NVSpoolJob,
  2095. PRINTER_CHANGE_WRITE_JOB,
  2096. pSpool->pIniSpooler);
  2097. }
  2098. continue;
  2099. }
  2100. if (pSpool->pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART | JOB_ABANDON )) {
  2101. SetLastError(ERROR_PRINT_CANCELLED);
  2102. LeaveSplSem();
  2103. SplOutSem();
  2104. DBGMSG( DBG_WARNING, ("LocalReadPrinter Error 2 IniJob->Status %x\n",pSpool->pIniJob->Status));
  2105. return FALSE;
  2106. }
  2107. //
  2108. // Wait until something is written to the file
  2109. //
  2110. hWait = pSpool->pIniJob->WaitForWrite;
  2111. ResetEvent( hWait );
  2112. DBGMSG( DBG_TRACE, ("LocalReadPrinter Waiting for Data %d milliseconds\n",dwFastPrintWaitTimeout));
  2113. LeaveSplSem();
  2114. SplOutSem();
  2115. rc = WaitForSingleObjectEx( hWait, dwFastPrintWaitTimeout, FALSE );
  2116. SplOutSem();
  2117. EnterSplSem();
  2118. DBGMSG( DBG_TRACE, ("LocalReadPrinter Returned from Waiting %x\n", rc));
  2119. SPLASSERT ( pSpool->pIniJob != NULL );
  2120. //
  2121. // If we did NOT timeout then we may have some Data to read
  2122. //
  2123. if ( rc != WAIT_TIMEOUT )
  2124. continue;
  2125. //
  2126. // If there are any other jobs that could be printed on
  2127. // this port give up waiting.
  2128. //
  2129. pSpool->pIniJob->Status |= JOB_TIMEOUT;
  2130. // Set the event for SeekPrinter to fail rendering threads
  2131. SeekPrinterSetEvent(pSpool->pIniJob, NULL, TRUE);
  2132. if ( NULL == AssignFreeJobToFreePort(pSpool->pIniJob->pIniPort, &ThisPortSecsToWait) )
  2133. continue;
  2134. //
  2135. // There is another Job waiting
  2136. // Freeze this job, the user can Restart it later
  2137. //
  2138. pSpool->pIniJob->Status |= JOB_ABANDON;
  2139. CloseHandle( pSpool->pIniJob->WaitForWrite );
  2140. pSpool->pIniJob->WaitForWrite = NULL;
  2141. // Assign it our Error String
  2142. ReallocSplStr(&pSpool->pIniJob->pStatus, szFastPrintTimeout);
  2143. SetPrinterChange(pSpool->pIniJob->pIniPrinter,
  2144. pSpool->pIniJob,
  2145. NVJobStatusAndString,
  2146. PRINTER_CHANGE_SET_JOB,
  2147. pSpool->pIniJob->pIniPrinter->pIniSpooler );
  2148. DBGMSG( DBG_WARNING,
  2149. ("LocalReadPrinter Timeout on pIniJob %x %ws %ws\n",
  2150. pSpool->pIniJob,
  2151. pSpool->pIniJob->pUser,
  2152. pSpool->pIniJob->pDocument));
  2153. LogJobInfo( pSpool->pIniSpooler,
  2154. MSG_DOCUMENT_TIMEOUT,
  2155. pSpool->pIniJob->JobId,
  2156. pSpool->pIniJob->pDocument,
  2157. pSpool->pIniJob->pUser,
  2158. pSpool->pIniJob->pIniPrinter->pName,
  2159. dwFastPrintWaitTimeout );
  2160. SetLastError(ERROR_SEM_TIMEOUT);
  2161. LeaveSplSem();
  2162. SplOutSem();
  2163. return FALSE;
  2164. } // END WHILE
  2165. pSpool->pIniJob->Status &= ~( JOB_TIMEOUT | JOB_ABANDON );
  2166. //
  2167. // RapidPrint
  2168. //
  2169. // Some printers (like HP 4si with PSCRIPT) timeout if they
  2170. // don't get data, so if we fall below a threshold of data
  2171. // in the spoolfile then throttle back the Reads to 1 Byte
  2172. // per second until we have more data to ship to the printer
  2173. //
  2174. if (( pSpool->pIniJob->WaitForWrite != NULL ) &&
  2175. ( pSpool->pIniJob->Status & JOB_SPOOLING )) {
  2176. // Get the current file position.
  2177. dwCurrentPosition = SetFilePointer( pSpool->hReadFile,
  2178. 0,
  2179. NULL,
  2180. FILE_CURRENT );
  2181. SizeInFile = pSpool->pIniJob->Size - dwCurrentPosition;
  2182. if ( dwFastPrintSlowDownThreshold >= SizeInFile ) {
  2183. cbReadSize = 1;
  2184. hWait = pSpool->pIniJob->WaitForWrite;
  2185. ResetEvent( hWait );
  2186. DBGMSG( DBG_TRACE, ("LocalReadPrinter Throttling IOs waiting %d milliseconds SizeInFile %d\n",
  2187. dwFastPrintThrottleTimeout,SizeInFile));
  2188. LeaveSplSem();
  2189. SplOutSem();
  2190. rc = WaitForSingleObjectEx( hWait, dwFastPrintThrottleTimeout, FALSE );
  2191. SplOutSem();
  2192. EnterSplSem();
  2193. DBGMSG( DBG_TRACE, ("LocalReadPrinter Returned from Waiting %x\n", rc));
  2194. SPLASSERT ( pSpool->pIniJob != NULL );
  2195. } else {
  2196. BytesAllowedToRead = SizeInFile - dwFastPrintSlowDownThreshold;
  2197. if ( cbReadSize > BytesAllowedToRead ) {
  2198. cbReadSize = BytesAllowedToRead;
  2199. }
  2200. }
  2201. }
  2202. //
  2203. // A client calls AddJob to get the spool filename and
  2204. // ScheduleJob when the file is completed. According to the
  2205. // api spec, the spooler should not look at the job until
  2206. // ScheduleJob has been called.
  2207. //
  2208. // However, our print while spooling implementation tries
  2209. // to read the job before ScheduleJob is called. We do this
  2210. // by checking if the size of the file has changed.
  2211. //
  2212. // This causes a problem: the server service extends the
  2213. // file then writes to it. The spooler's size detection
  2214. // thread sees this extension and reads the file before
  2215. // the data is written, which puts garbage (zeros) into the
  2216. // data stream.
  2217. //
  2218. // The server always extends, writes, extends, writes, etc.
  2219. // The spooler can exploit the fact that they are in order
  2220. // and read a write only when the file is extended again,
  2221. // or the file is complete.
  2222. //
  2223. // Note that the API is still broken, but this fixes it
  2224. // for the server service (a client could extend, extend,
  2225. // write, write, which breaks this fix).
  2226. //
  2227. if( pSpool->pIniJob->Status & JOB_SPOOLING ){
  2228. // Get the current file position.
  2229. dwCurrentPosition = SetFilePointer( pSpool->hReadFile,
  2230. 0,
  2231. NULL,
  2232. FILE_CURRENT );
  2233. SPLASSERT( dwCurrentPosition <= pSpool->pIniJob->dwValidSize );
  2234. //
  2235. // Even though the file system will satisfy a large request, limit
  2236. // it to the extent of the previous (not current) write.
  2237. //
  2238. BytesAllowedToRead = pSpool->pIniJob->dwValidSize - dwCurrentPosition;
  2239. if( cbReadSize > BytesAllowedToRead ){
  2240. cbReadSize = BytesAllowedToRead;
  2241. }
  2242. }
  2243. LeaveSplSem();
  2244. SplOutSem();
  2245. if (bReadMappedView) {
  2246. // Mapping partial views serves no purpose, since it can't be used incrementally.
  2247. if (cbBuf != cbReadSize) {
  2248. rc = FALSE;
  2249. } else {
  2250. rc = SetMappingPointer(pSpool, pMapBuffer, cbReadSize);
  2251. }
  2252. if (rc) {
  2253. *pNoBytesRead = cbReadSize;
  2254. }
  2255. } else {
  2256. rc = ReadFile( pSpool->hReadFile, pBuf, cbReadSize, pNoBytesRead, NULL);
  2257. }
  2258. if (!bReadMappedView) {
  2259. DBGMSG( DBG_TRACE,
  2260. ("LocalReadPrinter rc %x hReadFile %x pBuf %x cbReadSize %d *pNoBytesRead %d\n",
  2261. rc, pSpool->hReadFile, pBuf, cbReadSize, *pNoBytesRead));
  2262. }
  2263. // Provide Feedback so user can see printing progress
  2264. // on despooling, the size is update here and not in write
  2265. // printer because the journal data is larger than raw
  2266. if ( ( pSpool->pIniJob->Status & JOB_PRINTING ) &&
  2267. ( *pNoBytesRead != 0 )) {
  2268. SplOutSem();
  2269. EnterSplSem();
  2270. dwFileSize = GetFileSize( pSpool->hReadFile, 0 );
  2271. COPYNV(NotifyVector, NVWriteJob);
  2272. if ( pSpool->pIniJob->Size < dwFileSize ) {
  2273. DBGMSG( DBG_TRACE, ("LocalReadPrinter 2 adjusting size old %d new %d\n",
  2274. pSpool->pIniJob->Size, dwFileSize));
  2275. pSpool->pIniJob->Size = dwFileSize;
  2276. ADDNV(NotifyVector, NVSpoolJob);
  2277. }
  2278. pSpool->pIniJob->cbPrinted += *pNoBytesRead;
  2279. //
  2280. // HACK !!! Each time we read from the spool file we add the
  2281. // number of bytes read to pIniJob->cbPrinted. GDI will read twice certain
  2282. // parts of the spool file. The spooler cannot know what is read twice, so it adds
  2283. // to cbPrinted the number of bytes read at each call of this function.
  2284. // This causes cbPrinted to be larger than the actual size of the spool file.
  2285. //
  2286. // Don't let the ratio cbPrinted/cbSize get above 1
  2287. //
  2288. if (pSpool->pIniJob->cbPrinted > pSpool->pIniJob->Size)
  2289. {
  2290. pSpool->pIniJob->cbPrinted = pSpool->pIniJob->Size;
  2291. }
  2292. //
  2293. // Provide Feedback to Printman that data has been
  2294. // written. Note the size written is not used to
  2295. // update the IniJob->cbPrinted becuase there is a
  2296. // difference in size between journal data (in the
  2297. // spool file) and the size of RAW bytes written to
  2298. // the printer.
  2299. //
  2300. SetPrinterChange(pSpool->pIniPrinter,
  2301. pSpool->pIniJob,
  2302. NotifyVector,
  2303. PRINTER_CHANGE_WRITE_JOB,
  2304. pSpool->pIniSpooler);
  2305. LeaveSplSem();
  2306. SplOutSem();
  2307. }
  2308. } else if ( pSpool->TypeofHandle & PRINTER_HANDLE_PORT ) {
  2309. if (pSpool->pIniPort->Status & PP_FILE)
  2310. rc = ReadFile( pSpool->hReadFile, pBuf, cbReadSize, pNoBytesRead, NULL);
  2311. else if ( pSpool->pIniPort->Status & PP_MONITOR ) {
  2312. if ( pSpool->pIniPort->pIniLangMonitor ) {
  2313. pIniMonitor = pSpool->pIniPort->pIniLangMonitor;
  2314. } else {
  2315. pIniMonitor = pSpool->pIniPort->pIniMonitor;
  2316. }
  2317. SplOutSem();
  2318. rc = (*pIniMonitor->Monitor2.pfnReadPort)(
  2319. pSpool->pIniPort->hPort,
  2320. pBuf,
  2321. cbReadSize,
  2322. pNoBytesRead );
  2323. } else
  2324. rc = ReadPrinter(pSpool->hPort, pBuf, cbReadSize, pNoBytesRead);
  2325. } else {
  2326. SetLastError(ERROR_INVALID_HANDLE);
  2327. rc = FALSE;
  2328. }
  2329. SplOutSem();
  2330. DBGMSG( DBG_TRACE, ("LocalReadPrinter returns hPrinter %x pIniJob %x rc %x pNoBytesRead %d\n",hPrinter, pSpool->pIniJob, rc, *pNoBytesRead));
  2331. return rc;
  2332. }
  2333. LPBYTE SearchForExistingView(
  2334. PSPOOL pSpool,
  2335. DWORD dwRequired)
  2336. /*++
  2337. Function Description -- Searches for an existing mapped view of the spool file which
  2338. has the required number of bytes.
  2339. Parameters -- pSpool -- Pointer to a SPOOL structure
  2340. dwRequired -- Number of bytes to be mapped from the start of the page
  2341. Return Value -- pointer to the start of the mapped view;
  2342. NULL if the call fails.
  2343. --*/
  2344. {
  2345. LPBYTE pReturn = NULL;
  2346. PSPLMAPVIEW pSplMapView;
  2347. for (pSplMapView = pSpool->pSplMapView;
  2348. pSplMapView;
  2349. pSplMapView = pSplMapView->pNext) {
  2350. if (dwRequired <= pSplMapView->dwMapSize) {
  2351. pReturn = pSplMapView->pStartMapView;
  2352. break;
  2353. }
  2354. }
  2355. return pReturn;
  2356. }
  2357. LPBYTE CreateNewMapView(
  2358. PSPOOL pSpool,
  2359. DWORD dwRequired)
  2360. /*++
  2361. Function Description -- Creates a new mapping view of the required segment of the spool
  2362. file
  2363. Parameters -- pSpool -- Pointer to a SPOOL structure
  2364. dwRequired -- Number of bytes to be mapped from the start of the page
  2365. Return Value -- pointer to the start of the mapped view;
  2366. NULL if the call fails.
  2367. --*/
  2368. {
  2369. HANDLE hMapSpoolFile;
  2370. LPBYTE pStartMapView;
  2371. DWORD dwMapSize, dwFileSize;
  2372. LPBYTE pReturn = NULL;
  2373. PSPLMAPVIEW pSplMapView;
  2374. pSplMapView = (PSPLMAPVIEW) AllocSplMem(sizeof(SPLMAPVIEW));
  2375. if (!pSplMapView) {
  2376. // Allocation failed.
  2377. goto CleanUp;
  2378. }
  2379. dwFileSize = GetFileSize(pSpool->hReadFile, NULL);
  2380. pSplMapView->dwMapSize = (dwFileSize <= MAX_SPL_MAPVIEW_SIZE) ? dwFileSize
  2381. : dwRequired;
  2382. pSplMapView->hMapSpoolFile = NULL;
  2383. pSplMapView->pStartMapView = NULL;
  2384. pSplMapView->pNext = NULL;
  2385. // Create file mapping
  2386. pSplMapView->hMapSpoolFile = CreateFileMapping(pSpool->hReadFile, NULL, PAGE_READONLY,
  2387. 0, pSplMapView->dwMapSize, NULL);
  2388. if (!pSplMapView->hMapSpoolFile) {
  2389. goto CleanUp;
  2390. }
  2391. // Map a view of the file
  2392. pSplMapView->pStartMapView = (LPBYTE) MapViewOfFile(pSplMapView->hMapSpoolFile, FILE_MAP_READ,
  2393. 0, 0, pSplMapView->dwMapSize);
  2394. pReturn = pSplMapView->pStartMapView;
  2395. CleanUp:
  2396. if (!pReturn && pSplMapView) {
  2397. // Free any allocated resources
  2398. if (pSplMapView->pStartMapView) {
  2399. UnmapViewOfFile( (LPVOID) pSplMapView->pStartMapView);
  2400. }
  2401. if (pSplMapView->hMapSpoolFile) {
  2402. CloseHandle(pSplMapView->hMapSpoolFile);
  2403. }
  2404. FreeSplMem(pSplMapView);
  2405. }
  2406. if (pReturn) {
  2407. pSplMapView->pNext = pSpool->pSplMapView;
  2408. pSpool->pSplMapView = pSplMapView;
  2409. }
  2410. return pReturn;
  2411. }
  2412. BOOL SetMappingPointer(
  2413. PSPOOL pSpool,
  2414. LPBYTE *pMapBuffer,
  2415. DWORD cbReadSize
  2416. )
  2417. /*++
  2418. Function Description: SetMappingPointer creates a file mapping object and a mapped view (if necessary).
  2419. If the required number of bytes are present in the view, the pointer to the
  2420. data is returned in the buffer (pMappedBuffer) else the call fails.
  2421. The current offset is taken from pSpool->hReadFile and if the buffer is available,
  2422. the offset of hReadFile is changed correspondingly. This ensures that SplReadPrinter
  2423. and ReadPrinter can be used alternately.
  2424. ****Modifications required for 64 bit architecture****
  2425. Parameters: pSpool -- pointer to the SPOOL structure
  2426. *pMapBuffer -- pointer to the mapped file view
  2427. cbReadView -- number of bytes to be read
  2428. Return Values: TRUE if successful;
  2429. FALSE otherwise
  2430. --*/
  2431. {
  2432. BOOL bReturn = FALSE;
  2433. DWORD dwCurrentPosition, dwNewPosition, dwRequired;
  2434. LPBYTE pStartMapView;
  2435. dwCurrentPosition = SetFilePointer(pSpool->hReadFile, 0, NULL, FILE_CURRENT);
  2436. if (dwCurrentPosition == 0xffffffff && GetLastError() != NO_ERROR) {
  2437. goto CleanUp;
  2438. }
  2439. dwRequired = dwCurrentPosition + cbReadSize;
  2440. if (dwRequired > MAX_SPL_MAPVIEW_SIZE) {
  2441. // Map size is insufficient; fail the call
  2442. SetLastError(ERROR_NOT_SUPPORTED);
  2443. goto CleanUp;
  2444. }
  2445. pStartMapView = SearchForExistingView(pSpool, dwRequired);
  2446. if (!pStartMapView) {
  2447. pStartMapView = CreateNewMapView(pSpool, dwRequired);
  2448. }
  2449. if (!pStartMapView) {
  2450. // Required view not created
  2451. goto CleanUp;
  2452. }
  2453. // Check for DWORD alignment
  2454. if ((((ULONG_PTR) pStartMapView + dwCurrentPosition) & 3) != 0) {
  2455. // Fails unaligned reads
  2456. SetLastError(ERROR_MAPPED_ALIGNMENT);
  2457. goto CleanUp;
  2458. }
  2459. dwNewPosition = SetFilePointer(pSpool->hReadFile, cbReadSize, NULL, FILE_CURRENT);
  2460. if (dwNewPosition == 0xffffffff && GetLastError() != NO_ERROR) {
  2461. goto CleanUp;
  2462. }
  2463. if (pMapBuffer) {
  2464. *pMapBuffer = (LPBYTE) ((ULONG_PTR) pStartMapView + dwCurrentPosition);
  2465. }
  2466. bReturn = TRUE;
  2467. CleanUp:
  2468. // All the handles and associated resources will be freed along with pSpool
  2469. return bReturn;
  2470. }
  2471. BOOL
  2472. LocalEndDocPrinter(
  2473. HANDLE hPrinter
  2474. )
  2475. /*++
  2476. Routine Description:
  2477. By Default the routine is in critical section.
  2478. The reference counts for any object we are working on (pSpool and pIniJob)
  2479. are incremented, so that when we leave critical section for lengthy
  2480. operations these objects are not deleted.
  2481. Arguments:
  2482. Return Value:
  2483. --*/
  2484. {
  2485. PSPOOL pSpool=(PSPOOL)hPrinter;
  2486. BOOL bReturn = TRUE;
  2487. DWORD rc;
  2488. PINIMONITOR pIniMonitor;
  2489. DBGMSG(DBG_TRACE, ("Entering LocalEndDocPrinter with %x\n", hPrinter));
  2490. SplOutSem();
  2491. EnterSplSem();
  2492. if (pSpool &&
  2493. pSpool->pIniJob &&
  2494. !(pSpool->TypeofHandle & PRINTER_HANDLE_PORT)) {
  2495. INCJOBREF(pSpool->pIniJob);
  2496. LeaveSplSem();
  2497. LogWmiTraceEvent(pSpool->pIniJob->JobId, EVENT_TRACE_TYPE_SPL_TRACKTHREAD, NULL);
  2498. EnterSplSem();
  2499. DECJOBREF(pSpool->pIniJob);
  2500. }
  2501. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
  2502. LeaveSplSem();
  2503. return(FALSE);
  2504. }
  2505. if (!(pSpool->Status & SPOOL_STATUS_STARTDOC)) {
  2506. SetLastError(ERROR_SPL_NO_STARTDOC);
  2507. LeaveSplSem();
  2508. return(FALSE);
  2509. }
  2510. if (pSpool->Status & SPOOL_STATUS_ADDJOB) {
  2511. SetLastError(ERROR_SPL_NO_STARTDOC);
  2512. LeaveSplSem();
  2513. return(FALSE);
  2514. }
  2515. if ( pSpool->pIniJob ) {
  2516. pSpool->pIniJob->dwAlert |= JOB_ENDDOC_CALL;
  2517. }
  2518. pSpool->Status &= ~SPOOL_STATUS_STARTDOC;
  2519. //
  2520. // Case-1 Printer Handle is PRINTER_HANDLE_PORT
  2521. // Note - there are two cases to keep in mind here
  2522. // A] The first case is the despooling thread calling
  2523. // a port with a monitor - LPT1:/COM1: or any port
  2524. // created by the monitor
  2525. // B] The second case is when the application thread is
  2526. // doing an EndDocPrinter to a port which has no monitor
  2527. // This is the local printer masquerading as a remote printer
  2528. // case. Remember for this case there is no IniJob created
  2529. // on the local printer at all. We just pass the call
  2530. // straight to the remote printer
  2531. if (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) {
  2532. SPLASSERT(!(pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER));
  2533. //
  2534. // Now check if this pSpool object's port has
  2535. // a monitor
  2536. if ( pSpool->pIniPort->Status & PP_MONITOR ) { //Case A]
  2537. //
  2538. // Check if our job is really around
  2539. //
  2540. if (!pSpool->pIniJob) {
  2541. SetLastError(ERROR_CAN_NOT_COMPLETE);
  2542. bReturn = FALSE;
  2543. goto CleanUp;
  2544. }
  2545. //
  2546. // Originally we had a call to UpdateJobAttributes, but
  2547. // we do not believe it is needed since it is also called during
  2548. // LocalStartDocPrinter. Note that you can't change the DevMode
  2549. // in SetLocalJob, so once we calculate the information, we
  2550. // don't have to worry about it changing.
  2551. //
  2552. if (pSpool->pIniJob->cLogicalPagesPrinted)
  2553. {
  2554. pSpool->pIniJob->cLogicalPagesPrinted = 0;
  2555. pSpool->pIniJob->cPagesPrinted++;
  2556. pSpool->pIniPrinter->cTotalPagesPrinted++;
  2557. SetPrinterChange(pSpool->pIniPrinter,
  2558. pSpool->pIniJob,
  2559. NVWriteJob,
  2560. PRINTER_CHANGE_WRITE_JOB,
  2561. pSpool->pIniSpooler);
  2562. }
  2563. //
  2564. // We need to leave the spooler critical section
  2565. // because we're going call into the Monitor.
  2566. // so bump up ref count on pSpool and pIniJob
  2567. //
  2568. pSpool->cRef++;
  2569. INCJOBREF(pSpool->pIniJob);
  2570. if ( pSpool->pIniPort->pIniLangMonitor ) {
  2571. pIniMonitor = pSpool->pIniPort->pIniLangMonitor;
  2572. //
  2573. // If job is printing thru a language monitor we will get
  2574. // SetJob with JOB_CONTROL_LAST_PAGE_EJECTED in addition to
  2575. // JOB_CONTROL_SENT_TO_PRINTER
  2576. //
  2577. pSpool->pIniJob->dwJobControlsPending += 2;
  2578. } else {
  2579. pIniMonitor = pSpool->pIniPort->pIniMonitor;
  2580. pSpool->pIniJob->dwJobControlsPending++;
  2581. }
  2582. //
  2583. // LocalEndDocPrinter cal be called because a normal termination of
  2584. // a job or because of a delete/restart operation.
  2585. // We need to know this when the monitor sends JOB_CONTROL_LAST_PAGE_EJECTED
  2586. // to make the distinction if this is a real TEOJ or not.
  2587. // Since JOB_PENDING_DELETION and JOB_RESTART are cleared later on,
  2588. // we'll set this special flag.
  2589. // JOB_INTERRUPTED means that LocalEndDocPrinter was issued by a
  2590. // was a cancel/restart action.
  2591. //
  2592. if (pSpool->pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART)) {
  2593. pSpool->pIniJob->Status |= JOB_INTERRUPTED;
  2594. }
  2595. LeaveSplSem();
  2596. SPLASSERT(pIniMonitor);
  2597. STARTENDDOC( pSpool->pIniPort, pSpool->pIniJob, 1 );
  2598. SPLASSERT( pSpool->pIniPort->Status & PP_STARTDOC );
  2599. SplOutSem();
  2600. (*pIniMonitor->Monitor2.pfnEndDocPort)(pSpool->pIniPort->hPort);
  2601. EnterSplSem();
  2602. pSpool->pIniPort->Status &= ~PP_STARTDOC;
  2603. pSpool->cRef--;
  2604. DECJOBREF(pSpool->pIniJob);
  2605. goto CleanUp;
  2606. } else { // Case B]
  2607. //
  2608. // We leave critical section here so bump pSpool object only
  2609. // Note ----THERE IS NO INIJOB HERE AT ALL---Note
  2610. // this call is synchronous; we will call into the router
  2611. // who will then call the appropriate network print providor
  2612. // e.g win32spl.dll
  2613. //
  2614. pSpool->cRef++;
  2615. LeaveSplSem();
  2616. if (pSpool->Status & SPOOL_STATUS_PRINT_FILE) {
  2617. if (!CloseHandle(pSpool->hFile)) {
  2618. DBGMSG(DBG_TRACE, ("LocalEndDocPrinter: Printing to File, CloseHandle failed\n"));
  2619. bReturn = FALSE;
  2620. } else {
  2621. DBGMSG(DBG_TRACE, ("LocalEndDocPrinter: Printing to File, CloseHandle succeeded\n"));
  2622. pSpool->Status &= ~SPOOL_STATUS_PRINT_FILE;
  2623. bReturn = TRUE;
  2624. }
  2625. } else {
  2626. bReturn = (BOOL) EndDocPrinter(pSpool->hPort);
  2627. }
  2628. EnterSplSem();
  2629. pSpool->cRef--;
  2630. goto CleanUp;
  2631. }
  2632. }
  2633. SplInSem();
  2634. //
  2635. // Case-2 Printer Handle is Direct
  2636. //
  2637. //
  2638. // and the else clause is
  2639. //
  2640. //
  2641. // Case-3 Printer Handle is Spooled
  2642. //
  2643. if (!pSpool->pIniJob) {
  2644. SetLastError(ERROR_CAN_NOT_COMPLETE);
  2645. bReturn = FALSE;
  2646. goto CleanUp;
  2647. }
  2648. if (pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT) {
  2649. HANDLE WaitForRead = pSpool->pIniJob->WaitForRead;
  2650. PINIPORT pIniPort1 = pSpool->pIniJob->pIniPort;
  2651. if (pIniPort1) { // Port may have been deleted by another EndDocPrinter
  2652. SPLASSERT(!(pSpool->TypeofHandle & PRINTER_HANDLE_PORT));
  2653. // Printer Handle is Direct
  2654. pSpool->cRef++;
  2655. INCJOBREF(pSpool->pIniJob);
  2656. pIniPort1->cRef++;
  2657. //
  2658. // If the job was canceled by the user, there is no need to wait
  2659. // for the write and read events. In certain cases when the job is
  2660. // direct and canceled before LocalEndDocPrinter, those events
  2661. // will never be signaled again. So waiting on them will be infinite
  2662. //
  2663. if (!(pSpool->pIniJob->Status & JOB_PENDING_DELETION))
  2664. {
  2665. LeaveSplSem();
  2666. if( (WaitForRead != NULL) ){
  2667. WaitForSingleObject(WaitForRead, INFINITE);
  2668. }
  2669. pSpool->pIniJob->cbBuffer = 0;
  2670. SetEvent(pSpool->pIniJob->WaitForWrite);
  2671. WaitForSingleObject(pIniPort1->Ready, INFINITE);
  2672. SplOutSem();
  2673. EnterSplSem();
  2674. } else {
  2675. //
  2676. // If the job is canceled, there are no more Write operations coming in.
  2677. // Unlock the port thread which is waiting on this event inside ReadPrinter.
  2678. //
  2679. SetEvent(pSpool->pIniJob->WaitForWrite);
  2680. //
  2681. // Set cbBuffer on 0, since there are no more Read/Write oparatins expected.
  2682. //
  2683. pSpool->pIniJob->cbBuffer = 0;
  2684. }
  2685. pSpool->cRef--;
  2686. pIniPort1->cRef--;
  2687. DECJOBREF(pSpool->pIniJob);
  2688. if ((pIniPort1->Status & PP_DELETING) && !pIniPort1->cRef)
  2689. DeletePortEntry(pIniPort1);
  2690. }
  2691. } else {
  2692. // Printer Handle is Spooled
  2693. SPLASSERT(!(pSpool->TypeofHandle & PRINTER_HANDLE_PORT));
  2694. SPLASSERT(!(pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT));
  2695. // Update page count
  2696. LeaveSplSem();
  2697. UpdateJobAttributes(pSpool->pIniJob);
  2698. EnterSplSem();
  2699. if (pSpool->pIniJob->cLogicalPages)
  2700. {
  2701. pSpool->pIniJob->cLogicalPages = 0;
  2702. pSpool->pIniJob->cPages++;
  2703. // Notify the change in the page count
  2704. SetPrinterChange(pSpool->pIniPrinter,
  2705. pSpool->pIniJob,
  2706. NVSpoolJob,
  2707. PRINTER_CHANGE_WRITE_JOB,
  2708. pSpool->pIniSpooler);
  2709. }
  2710. #if 0
  2711. // Thought to be a performance hit
  2712. // so removed from PPC release.
  2713. // In the event of a power failure we want to make certain that all
  2714. // data for this job has been written to disk
  2715. rc = FlushFileBuffers(pSpool->pIniJob->hWriteFile);
  2716. if ( !rc ) {
  2717. DBGMSG(DBG_WARNING, ("LocalEndDocPrinter FlushFileBuffers failed hWriteFile %x Error %d\n",
  2718. pSpool->pIniJob->hWriteFile, GetLastError() ));
  2719. }
  2720. #endif
  2721. if (pSpool->pIniJob->hFileItem != INVALID_HANDLE_VALUE)
  2722. {
  2723. // If this job is a keeper or the job is greater than 200KB and not
  2724. // already printing close the write file, so that the memory from
  2725. // the file can be reclaimed by the system. Without this the
  2726. // spooler can eat up a lot of memory. File pooling doesn't
  2727. // significantly help speed up printing of the larger files anyway.
  2728. // If the printer is stopped or the job is big and not already
  2729. // despooling or printing then close files, which closes the memory
  2730. // mappings (buffered I/O).
  2731. // Not necessarily bad, this also is true if the printer is paused.
  2732. if ((PrinterStatusBad(pSpool->pIniJob->pIniPrinter->Status) ||
  2733. (pSpool->pIniJob->pIniPrinter->Attributes &
  2734. PRINTER_ATTRIBUTE_WORK_OFFLINE)) &&
  2735. !(pSpool->pIniJob->Status & JOB_PRINTING) &&
  2736. !(pSpool->pIniJob->Status & JOB_DESPOOLING))
  2737. {
  2738. LeaveSplSem();
  2739. CloseFiles(pSpool->pIniJob->hFileItem, TRUE);
  2740. EnterSplSem();
  2741. }
  2742. else if (!(pSpool->pIniJob->Status & JOB_PRINTING) &&
  2743. (pSpool->pIniJob->Size > FP_LARGE_SIZE))
  2744. {
  2745. LeaveSplSem();
  2746. CloseFiles(pSpool->pIniJob->hFileItem, TRUE);
  2747. EnterSplSem();
  2748. }
  2749. FinishedWriting(pSpool->pIniJob->hFileItem, TRUE);
  2750. pSpool->pIniJob->hWriteFile = INVALID_HANDLE_VALUE;
  2751. }
  2752. else if (!CloseHandle(pSpool->pIniJob->hWriteFile)) {
  2753. DBGMSG(DBG_WARNING, ("CloseHandle failed %d %d\n", pSpool->pIniJob->hWriteFile, GetLastError()));
  2754. } else {
  2755. DBGMSG(DBG_TRACE, ("LocalEndDocPrinter: ClosedHandle Success hWriteFile\n" ));
  2756. pSpool->pIniJob->hWriteFile = INVALID_HANDLE_VALUE;
  2757. }
  2758. // Despooling whilst spooling requires us to wake the writing
  2759. // thread if it is waiting.
  2760. if ( pSpool->pIniJob->WaitForWrite != NULL )
  2761. SetEvent(pSpool->pIniJob->WaitForWrite);
  2762. // Set the event for SeekPrinter
  2763. SeekPrinterSetEvent(pSpool->pIniJob, NULL, TRUE);
  2764. }
  2765. SPLASSERT(pSpool);
  2766. SPLASSERT(pSpool->pIniJob);
  2767. // Case 2 - (Direct) and Case 3 - (Spooled) will both execute
  2768. // this block of code because both direct and spooled handles
  2769. // are first and foremost PRINTER_HANDLE_PRINTER handles
  2770. if (pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER) {
  2771. SPLASSERT(!(pSpool->TypeofHandle & PRINTER_HANDLE_PORT));
  2772. // WARNING
  2773. // If pIniJob->Status has JOB_SPOOLING removed and we leave
  2774. // the critical section then the scheduler thread will
  2775. // Start the job printing. This could cause a problem
  2776. // in that the job could be completed and deleted
  2777. // before the shadow job is complete. This would lead
  2778. // to access violations.
  2779. SPLASSERT(pSpool);
  2780. SPLASSERT(pSpool->pIniJob);
  2781. if (pSpool->pIniJob->Status & JOB_SPOOLING) {
  2782. pSpool->pIniJob->Status &= ~JOB_SPOOLING;
  2783. pSpool->pIniJob->pIniPrinter->cSpooling--;
  2784. }
  2785. // It looks like the ref count on the job is != 0, so the job should not
  2786. // get deleted while this shadow is being written.
  2787. // Having this operation in the critsec is preventing us from getting
  2788. // good CPU utilization. Icecap is showing many (100+ in some cases)
  2789. // other spool threads waiting on this when I push the server.
  2790. WriteShadowJob(pSpool->pIniJob, TRUE);
  2791. SplInSem();
  2792. //
  2793. // This line of code is crucial; for timing reasons it
  2794. // has been moved from the Direct (Case 2) and the
  2795. // Spooled (Case 3) clauses. This decrement is for the
  2796. // initial
  2797. //
  2798. SPLASSERT(pSpool->pIniJob->cRef != 0);
  2799. DECJOBREF(pSpool->pIniJob);
  2800. if (pSpool->pIniJob->Status & JOB_PENDING_DELETION) {
  2801. DBGMSG(DBG_TRACE, ("EndDocPrinter: Deleting Pending Deletion Job\n"));
  2802. DeleteJob(pSpool->pIniJob,BROADCAST);
  2803. pSpool->pIniJob = NULL;
  2804. } else {
  2805. if ( pSpool->pIniJob->Status & JOB_TIMEOUT ) {
  2806. pSpool->pIniJob->Status &= ~( JOB_TIMEOUT | JOB_ABANDON );
  2807. FreeSplStr(pSpool->pIniJob->pStatus);
  2808. pSpool->pIniJob->pStatus = NULL;
  2809. }
  2810. DBGMSG(DBG_TRACE, ("EndDocPrinter:PRINTER:cRef = %d\n", pSpool->pIniJob->cRef));
  2811. CHECK_SCHEDULER();
  2812. }
  2813. }
  2814. if (pSpool->pIniJob) {
  2815. SetPrinterChange(pSpool->pIniPrinter,
  2816. pSpool->pIniJob,
  2817. NVJobStatus,
  2818. PRINTER_CHANGE_SET_JOB,
  2819. pSpool->pIniSpooler);
  2820. }
  2821. CleanUp:
  2822. if (pSpool->pIniJob) {
  2823. pSpool->pIniJob->dwAlert &= ~JOB_ENDDOC_CALL;
  2824. //
  2825. // WMI Trace Events
  2826. //
  2827. if (((pSpool->pIniJob->Status & JOB_PAUSED) ||
  2828. (pSpool->pIniJob->pIniPrinter->Status & PRINTER_PAUSED)) &&
  2829. (!((pSpool->pIniJob->Status & JOB_PRINTING) ||
  2830. (pSpool->pIniJob->Status & JOB_PRINTED))))
  2831. {
  2832. INCJOBREF(pSpool->pIniJob);
  2833. LeaveSplSem();
  2834. LogWmiTraceEvent(pSpool->pIniJob->JobId, EVENT_TRACE_TYPE_SPL_PAUSE, NULL);
  2835. EnterSplSem();
  2836. DECJOBREF(pSpool->pIniJob);
  2837. }
  2838. }
  2839. LeaveSplSem();
  2840. SplOutSem();
  2841. return bReturn;
  2842. }
  2843. BOOL
  2844. PrintingDirectlyToPort(
  2845. PSPOOL pSpool,
  2846. DWORD Level,
  2847. LPBYTE pDocInfo,
  2848. LPDWORD pJobId
  2849. )
  2850. {
  2851. PDOC_INFO_1 pDocInfo1=(PDOC_INFO_1)pDocInfo;
  2852. BOOL rc;
  2853. DWORD Error;
  2854. BOOL bPrinttoFile = FALSE;
  2855. BOOL bErrorOccurred = FALSE;
  2856. PINIMONITOR pIniMonitor = NULL, pIniLangMonitor = NULL;
  2857. LPWSTR pszPrinter;
  2858. HANDLE hThread = NULL;
  2859. DWORD dwThreadId = 0;
  2860. TCHAR szFullPrinter[MAX_UNC_PRINTER_NAME];
  2861. //
  2862. // Some monitors rely on having the non-qualified name, so only
  2863. // use the fully qualified name for clustered spoolers.
  2864. //
  2865. // This means that anyone writing a cluster aware monitor will
  2866. // need to handle both types of names.
  2867. //
  2868. if( pSpool->pIniPrinter->pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ){
  2869. //
  2870. // Must use fully qualified name.
  2871. //
  2872. pszPrinter = szFullPrinter;
  2873. wsprintf( szFullPrinter,
  2874. L"%ws\\%ws",
  2875. pSpool->pIniPrinter->pIniSpooler->pMachineName,
  2876. pSpool->pIniPrinter->pName );
  2877. } else {
  2878. //
  2879. // Local name is sufficient.
  2880. //
  2881. pszPrinter = pSpool->pIniPrinter->pName;
  2882. }
  2883. DBGMSG( DBG_TRACE,
  2884. ( "PrintingDirectlyToPort: Printing document %ws direct to port\n",
  2885. DBGSTR( pDocInfo1->pDocName )));
  2886. if (pDocInfo1 &&
  2887. pDocInfo1->pDatatype &&
  2888. !ValidRawDatatype(pDocInfo1->pDatatype)) {
  2889. //
  2890. // XEROX
  2891. //
  2892. // We want to skip the error if this flags is on and there
  2893. // is no monitor.
  2894. //
  2895. if (!(pSpool->pIniSpooler->SpoolerFlags & SPL_NON_RAW_TO_MASQ_PRINTERS &&
  2896. !(pSpool->pIniPort->Status & PP_MONITOR))){
  2897. DBGMSG(DBG_WARNING, ("Datatype is not RAW\n"));
  2898. SetLastError(ERROR_INVALID_DATATYPE);
  2899. rc = FALSE;
  2900. goto CleanUp;
  2901. }
  2902. }
  2903. if (pSpool->pIniPort->Status & PP_MONITOR) {
  2904. if ( !(pSpool->pIniPort->Status & PP_FILE) &&
  2905. (pSpool->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_ENABLE_BIDI) )
  2906. pIniLangMonitor = pSpool->pIniPrinter->pIniDriver->pIniLangMonitor;
  2907. //
  2908. // WARNING!!
  2909. //
  2910. // We should never leave this loop unless we check for the presence of the UI
  2911. // thread (hThread) and make sure it has been terminted.
  2912. //
  2913. //
  2914. do {
  2915. //
  2916. // This fixes Intergraph's problem -- of wanting to print
  2917. // to file but their 3.1 print-processor does not pass
  2918. // thru the file name.
  2919. //
  2920. if (pSpool->pIniJob->Status & JOB_PRINT_TO_FILE) {
  2921. if ( pDocInfo1 && !pDocInfo1->pOutputFile ) {
  2922. pDocInfo1->pOutputFile = pSpool->pIniJob->pOutputFile;
  2923. }
  2924. }
  2925. //
  2926. // Some monitors (LPRMON) may fail to initialize at startup
  2927. // because a driver they are dependent on.
  2928. //
  2929. SplOutSem();
  2930. EnterSplSem();
  2931. //
  2932. // Check if being deleted.
  2933. //
  2934. if( pSpool->pIniJob->Status & JOB_PENDING_DELETION ){
  2935. LeaveSplSem();
  2936. SetLastError(ERROR_PRINT_CANCELLED);
  2937. if( hThread ) {
  2938. //
  2939. // See if the thread is still running or
  2940. // dismissed by user.
  2941. //
  2942. if( WAIT_TIMEOUT == WaitForSingleObject( hThread, 0 )) {
  2943. MyPostThreadMessage(hThread, dwThreadId,
  2944. WM_QUIT, IDRETRY, 0);
  2945. WaitForSingleObject( hThread, INFINITE );
  2946. }
  2947. CloseHandle( hThread );
  2948. hThread = NULL;
  2949. }
  2950. rc = FALSE;
  2951. goto CleanUp;
  2952. }
  2953. rc = OpenMonitorPort(pSpool->pIniPort,
  2954. &pIniLangMonitor,
  2955. pszPrinter,
  2956. TRUE);
  2957. //
  2958. // If the port monitor would not work with a lang monitor try to
  2959. // use port monitor directly
  2960. //
  2961. if ( !rc && pIniLangMonitor ) {
  2962. PINIMONITOR pIniLangMonitor2 = NULL;
  2963. rc = OpenMonitorPort(pSpool->pIniPort,
  2964. &pIniLangMonitor2,
  2965. pszPrinter,
  2966. TRUE);
  2967. if ( rc )
  2968. pIniLangMonitor = NULL;
  2969. }
  2970. LeaveSplSem();
  2971. if ( rc ) {
  2972. if ( pIniLangMonitor )
  2973. pIniMonitor = pIniLangMonitor;
  2974. else
  2975. pIniMonitor = pSpool->pIniPort->pIniMonitor;
  2976. STARTENDDOC( pSpool->pIniPort, pSpool->pIniJob, 0 );
  2977. SplOutSem();
  2978. rc = (*pIniMonitor->Monitor2.pfnStartDocPort)(
  2979. pSpool->pIniPort->hPort,
  2980. pszPrinter,
  2981. pSpool->pIniJob->JobId,
  2982. Level, pDocInfo);
  2983. }
  2984. if ( rc ) {
  2985. pSpool->pIniPort->Status |= PP_STARTDOC;
  2986. //
  2987. // StartDoc successful.
  2988. //
  2989. if ( hThread ) {
  2990. //
  2991. // We have started a message box and now the
  2992. // automatically retry has succeeded, we need to
  2993. // kill the message box and continue to print.
  2994. //
  2995. // See if the thread is still running or dismissed
  2996. // by user.
  2997. //
  2998. if ( WAIT_TIMEOUT == WaitForSingleObject( hThread, 0 )) {
  2999. MyPostThreadMessage(hThread, dwThreadId,
  3000. WM_QUIT, IDRETRY, 0 );
  3001. WaitForSingleObject( hThread, INFINITE );
  3002. }
  3003. CloseHandle( hThread );
  3004. hThread = NULL;
  3005. }
  3006. } else {
  3007. Error = GetLastError();
  3008. //
  3009. // Check for pending deletion first, which prevents the
  3010. // dialog from coming up if the user hits Del.
  3011. //
  3012. if ( (pSpool->pIniJob->Status & (JOB_PENDING_DELETION | JOB_RESTART)) ||
  3013. (PromptWriteError( pSpool, &hThread, &dwThreadId ) == IDCANCEL)) {
  3014. if ( hThread ) {
  3015. //
  3016. // See if the thread is still running or
  3017. // dismissed by user.
  3018. //
  3019. if ( WAIT_TIMEOUT == WaitForSingleObject( hThread, 0 )) {
  3020. MyPostThreadMessage(hThread, dwThreadId,
  3021. WM_QUIT, IDRETRY, 0 );
  3022. WaitForSingleObject( hThread, INFINITE );
  3023. }
  3024. CloseHandle( hThread );
  3025. hThread = NULL;
  3026. }
  3027. pSpool->pIniJob->StartDocError = Error;
  3028. SetLastError(ERROR_PRINT_CANCELLED);
  3029. rc = FALSE;
  3030. goto CleanUp;
  3031. }
  3032. bErrorOccurred = TRUE;
  3033. }
  3034. } while (!rc);
  3035. //
  3036. // If an error occurred, we set some error bits in the job
  3037. // status field. Clear them now since the StartDoc succeeded.
  3038. //
  3039. if( bErrorOccurred ){
  3040. EnterSplSem();
  3041. ClearJobError( pSpool->pIniJob );
  3042. LeaveSplSem();
  3043. }
  3044. pSpool->Status |= SPOOL_STATUS_STARTDOC;
  3045. if ( pIniLangMonitor ) {
  3046. pSpool->pIniJob->Status |= JOB_TRUE_EOJ;
  3047. }
  3048. if ( pSpool->pIniJob->pIniPrinter->pSepFile &&
  3049. *pSpool->pIniJob->pIniPrinter->pSepFile) {
  3050. DoSeparator(pSpool);
  3051. }
  3052. // Let the application's thread return from PrintingDirect:
  3053. DBGMSG(DBG_PORT, ("PrintingDirectlyToPort: Calling SetEvent( %x )\n",
  3054. pSpool->pIniJob->StartDocComplete));
  3055. if(pSpool->pIniJob->StartDocComplete) {
  3056. if ( !SetEvent( pSpool->pIniJob->StartDocComplete ) ) {
  3057. DBGMSG( DBG_WARNING, ("SetEvent( %x ) failed: Error %d\n",
  3058. pSpool->pIniJob->StartDocComplete,
  3059. GetLastError() ));
  3060. }
  3061. }
  3062. } else {
  3063. DBGMSG(DBG_TRACE, ("Port has no monitor: Calling StartDocPrinter or maybe printing to file\n"));
  3064. EnterSplSem();
  3065. bPrinttoFile = (pDocInfo1 && IsGoingToFile(pDocInfo1->pOutputFile,
  3066. pSpool->pIniSpooler));
  3067. LeaveSplSem();
  3068. if (bPrinttoFile) {
  3069. HANDLE hFile = INVALID_HANDLE_VALUE;
  3070. DBGMSG(DBG_TRACE, ("Port has no monitor: Printing to File %ws\n", pDocInfo1->pOutputFile));
  3071. hFile = CreateFile( pDocInfo1->pOutputFile,
  3072. GENERIC_WRITE,
  3073. FILE_SHARE_READ | FILE_SHARE_WRITE,
  3074. NULL,
  3075. OPEN_ALWAYS,
  3076. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  3077. NULL );
  3078. if (hFile == INVALID_HANDLE_VALUE) {
  3079. DBGMSG(DBG_TRACE, ("Port has no monitor: File open failed\n"));
  3080. rc = FALSE;
  3081. } else {
  3082. DBGMSG(DBG_TRACE, ("Port has no monitor: File open succeeded\n"));
  3083. SetEndOfFile(hFile);
  3084. pSpool->hFile = hFile;
  3085. pSpool->Status |= SPOOL_STATUS_PRINT_FILE;
  3086. //
  3087. // Have to set JobId to some non zero value otherwise
  3088. // StartDocPrinter expects the JobId to be off the pSpool->pIniJob
  3089. // We have none so we'll access violate!!
  3090. //
  3091. *pJobId = TRUE;
  3092. rc = TRUE;
  3093. }
  3094. } else {
  3095. DBGMSG(DBG_TRACE, ("Port has no monitor: Calling StartDocPrinter\n"));
  3096. *pJobId = StartDocPrinter(pSpool->hPort, Level, pDocInfo);
  3097. rc = *pJobId != 0;
  3098. }
  3099. if (!rc) {
  3100. DBGMSG(DBG_WARNING, ("StartDocPrinter failed: Error %d\n", GetLastError()));
  3101. }
  3102. }
  3103. SPLASSERT( hThread == NULL );
  3104. CleanUp:
  3105. return rc;
  3106. }
  3107. DWORD
  3108. WriteToPrinter(
  3109. PSPOOL pSpool,
  3110. LPBYTE pByte,
  3111. DWORD cbBuf
  3112. )
  3113. {
  3114. if( pSpool->pIniJob->WaitForRead != NULL ) {
  3115. WaitForSingleObject(pSpool->pIniJob->WaitForRead, INFINITE);
  3116. cbBuf = pSpool->pIniJob->cbBuffer = min(cbBuf, pSpool->pIniJob->cbBuffer);
  3117. memcpy(pSpool->pIniJob->pBuffer, pByte, cbBuf);
  3118. } else {
  3119. pSpool->pIniJob->cbBuffer = cbBuf = 0;
  3120. }
  3121. SetEvent(pSpool->pIniJob->WaitForWrite);
  3122. return cbBuf;
  3123. }
  3124. DWORD
  3125. ReadFromPrinter(
  3126. PSPOOL pSpool,
  3127. LPBYTE pBuf,
  3128. DWORD cbBuf
  3129. )
  3130. {
  3131. pSpool->pIniJob->pBuffer = pBuf;
  3132. pSpool->pIniJob->cbBuffer = cbBuf;
  3133. SetEvent(pSpool->pIniJob->WaitForRead);
  3134. WaitForSingleObject(pSpool->pIniJob->WaitForWrite, INFINITE);
  3135. return pSpool->pIniJob->cbBuffer;
  3136. }
  3137. BOOL
  3138. ValidRawDatatype(
  3139. LPWSTR pszDatatype)
  3140. {
  3141. if (!pszDatatype || _wcsnicmp(pszDatatype, szRaw, 3))
  3142. return FALSE;
  3143. return TRUE;
  3144. }