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

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