Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1643 lines
39 KiB

  1. /*++
  2. Copyright (c) 1993 - 1995 Microsoft Corporation
  3. Abstract:
  4. This module provides the exported API WaitForPrinterChange,
  5. and the support functions internal to the local spooler.
  6. Author:
  7. Andrew Bell (AndrewBe) March 1993
  8. Revision History:
  9. --*/
  10. #include<precomp.h>
  11. typedef struct _NOTIFY_FIELD_TABLE {
  12. WORD Field;
  13. WORD Table;
  14. WORD Offset;
  15. } NOTIFY_FIELD_TYPE, *PNOTIFY_FIELD_TYPE;
  16. //
  17. // Translation table from PRINTER_NOTIFY_FIELD_* to bit vector
  18. //
  19. NOTIFY_FIELD_TYPE NotifyFieldTypePrinter[] = {
  20. #define DEFINE(field, x, y, table, offset) \
  21. { PRINTER_NOTIFY_FIELD_##field, table, OFFSETOF(INIPRINTER, offset) },
  22. #include <ntfyprn.h>
  23. #undef DEFINE
  24. { 0, 0, 0 }
  25. };
  26. NOTIFY_FIELD_TYPE NotifyFieldTypeJob[] = {
  27. #define DEFINE(field, x, y, table, offset) \
  28. { JOB_NOTIFY_FIELD_##field, table, OFFSETOF(INIJOB, offset) },
  29. #include <ntfyjob.h>
  30. #undef DEFINE
  31. { 0, 0, 0 }
  32. };
  33. typedef struct _NOTIFY_RAW_DATA {
  34. PVOID pvData;
  35. DWORD dwId;
  36. } NOTIFY_RAW_DATA, *PNOTIFY_RAW_DATA;
  37. //
  38. // Currently we assume that the number of PRINTER_NOTIFY_FIELD_* elements
  39. // will fit in one DWORD vector (32 bits). If this is ever false,
  40. // we need to re-write this code.
  41. //
  42. PNOTIFY_FIELD_TYPE apNotifyFieldTypes[NOTIFY_TYPE_MAX] = {
  43. NotifyFieldTypePrinter,
  44. NotifyFieldTypeJob
  45. };
  46. DWORD adwNotifyFieldOffsets[NOTIFY_TYPE_MAX] = {
  47. I_PRINTER_END,
  48. I_JOB_END
  49. };
  50. #define NOTIFY_FIELD_TOTAL (I_PRINTER_END + I_JOB_END)
  51. //
  52. // Common NotifyVectors used in the system.
  53. // NV*
  54. //
  55. NOTIFYVECTOR NVPrinterStatus = {
  56. BIT(I_PRINTER_STATUS), // | BIT(I_PRINTER_STATUS_STRING),
  57. BIT_NONE
  58. };
  59. NOTIFYVECTOR NVPrinterSD = {
  60. BIT(I_PRINTER_SECURITY_DESCRIPTOR),
  61. BIT_NONE
  62. };
  63. NOTIFYVECTOR NVJobStatus = {
  64. BIT_NONE,
  65. BIT(I_JOB_STATUS)
  66. };
  67. NOTIFYVECTOR NVJobStatusAndString = {
  68. BIT_NONE,
  69. BIT(I_JOB_STATUS) | BIT(I_JOB_STATUS_STRING)
  70. };
  71. NOTIFYVECTOR NVJobStatusString = {
  72. BIT_NONE,
  73. BIT(I_JOB_STATUS_STRING)
  74. };
  75. NOTIFYVECTOR NVPurge = {
  76. BIT(I_PRINTER_STATUS),
  77. BIT_NONE,
  78. };
  79. NOTIFYVECTOR NVDeletedJob = {
  80. BIT(I_PRINTER_CJOBS),
  81. BIT(I_JOB_STATUS)
  82. };
  83. NOTIFYVECTOR NVAddJob = {
  84. BIT(I_PRINTER_CJOBS),
  85. BIT_ALL
  86. };
  87. NOTIFYVECTOR NVPrinterAll = {
  88. BIT_ALL,
  89. BIT_NONE
  90. };
  91. NOTIFYVECTOR NVSpoolJob = {
  92. BIT_NONE,
  93. BIT(I_JOB_TOTAL_BYTES) | BIT(I_JOB_TOTAL_PAGES)
  94. };
  95. NOTIFYVECTOR NVWriteJob = {
  96. BIT_NONE,
  97. BIT(I_JOB_BYTES_PRINTED) | BIT(I_JOB_PAGES_PRINTED)
  98. };
  99. NOTIFYVECTOR NVJobPrinted = {
  100. BIT_NONE,
  101. BIT(I_JOB_BYTES_PRINTED) | BIT(I_JOB_PAGES_PRINTED) | BIT(I_JOB_STATUS)
  102. };
  103. //
  104. // Forward prototypes.
  105. //
  106. ESTATUS
  107. ValidateStartNotify(
  108. PSPOOL pSpool,
  109. DWORD fdwFilterFlags,
  110. DWORD fdwOptions,
  111. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  112. PINIPRINTER* ppIniPrinter);
  113. BOOL
  114. SetSpoolChange(
  115. PSPOOL pSpool,
  116. PNOTIFY_RAW_DATA pNotifyRawData,
  117. PDWORD pdwNotifyVectors,
  118. DWORD Flags);
  119. BOOL
  120. SetupNotifyOptions(
  121. PSPOOL pSpool,
  122. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions);
  123. VOID
  124. NotifyInfoTypes(
  125. PSPOOL pSpool,
  126. PNOTIFY_RAW_DATA pNotifyRawData,
  127. PDWORD pdwNotifyVectors,
  128. DWORD ChangeFlags);
  129. BOOL
  130. RefreshBuildInfoData(
  131. PSPOOL pSpool,
  132. PPRINTER_NOTIFY_INFO pInfo,
  133. UINT cInfo,
  134. WORD Type,
  135. PNOTIFY_RAW_DATA pNotifyRawData);
  136. DWORD
  137. LocalWaitForPrinterChange(
  138. HANDLE hPrinter,
  139. DWORD fdwFilterFlags)
  140. /*++
  141. Routine Description:
  142. This API may be called by an application if it wants to know
  143. when the status of a printer or print server changes.
  144. Valid events to wait for are defined by the PRINTER_CHANGE_* manifests.
  145. Arguments:
  146. hPrinter - A printer handle returned by OpenPrinter.
  147. This may correspond to either a printer or a server.
  148. fdwFilterFlags - One or more PRINTER_CHANGE_* values combined.
  149. The function will return if any of these changes occurs.
  150. Return Value:
  151. Non-zero: A mask containing the change which occurred.
  152. Zero: Either an error occurred or the handle (hPrinter) was closed
  153. by another thread. In the latter case GetLastError returns
  154. ERROR_INVALID_HANDLE.
  155. When a call is made to WaitForPrinterChange, we create an event in the
  156. SPOOL structure pointed to by the handle, to enable signaling between
  157. the thread causing the printer change and the thread waiting for it.
  158. When a change occurs, e.g. StartDocPrinter, the function SetPrinterChange
  159. is called, which traverses the linked list of handles pointed to by
  160. the PRINTERINI structure associated with that printer, and also any
  161. open handles on the server, then signals any events which it finds
  162. which has reuested to be informed if this change takes place.
  163. If there is no thread currently waiting, the change flag is maintained,
  164. so that later calls to WaitForPrinterChange can return immediately.
  165. This ensures that changes which occur between calls will not be lost.
  166. --*/
  167. {
  168. PSPOOL pSpool = (PSPOOL)hPrinter;
  169. PINIPRINTER pIniPrinter = NULL; /* Remains NULL for server */
  170. DWORD rc = 0;
  171. DWORD ChangeFlags = 0;
  172. HANDLE ChangeEvent = 0;
  173. DWORD TimeoutFlags = 0;
  174. #if DBG
  175. static DWORD Count = 0;
  176. #endif
  177. DBGMSG(DBG_NOTIFY,
  178. ("WaitForPrinterChange( %08x, %08x )\n", hPrinter, fdwFilterFlags));
  179. EnterSplSem();
  180. switch (ValidateStartNotify(pSpool,
  181. fdwFilterFlags,
  182. 0,
  183. NULL,
  184. &pIniPrinter)) {
  185. case STATUS_PORT:
  186. DBGMSG(DBG_NOTIFY, ("Port with no monitor: Calling WaitForPrinterChange\n"));
  187. LeaveSplSem();
  188. return WaitForPrinterChange(pSpool->hPort, fdwFilterFlags);
  189. case STATUS_FAIL:
  190. LeaveSplSem();
  191. return 0;
  192. case STATUS_VALID:
  193. break;
  194. }
  195. DBGMSG(DBG_NOTIFY, ("WaitForPrinterChange %08x on %ws:\n%d caller%s waiting\n",
  196. fdwFilterFlags,
  197. pIniPrinter ? pIniPrinter->pName : pSpool->pIniSpooler->pMachineName,
  198. Count, Count == 1 ? "" : "s"));
  199. //
  200. // There may already have been a change since we last called:
  201. //
  202. if ((pSpool->ChangeFlags == PRINTER_CHANGE_CLOSE_PRINTER) ||
  203. (pSpool->ChangeFlags & fdwFilterFlags)) {
  204. if (pSpool->ChangeFlags == PRINTER_CHANGE_CLOSE_PRINTER)
  205. ChangeFlags = 0;
  206. else
  207. ChangeFlags = pSpool->ChangeFlags;
  208. DBGMSG(DBG_NOTIFY, ("No need to wait: Printer change %08x detected on %ws:\n%d remaining caller%s\n",
  209. (ChangeFlags & fdwFilterFlags),
  210. pIniPrinter ? pIniPrinter->pName : pSpool->pIniSpooler->pMachineName,
  211. Count, Count == 1 ? "" : "s"));
  212. pSpool->ChangeFlags = 0;
  213. LeaveSplSem();
  214. return (ChangeFlags & fdwFilterFlags);
  215. }
  216. ChangeEvent = CreateEvent(NULL,
  217. EVENT_RESET_AUTOMATIC,
  218. EVENT_INITIAL_STATE_NOT_SIGNALED,
  219. NULL);
  220. if ( !ChangeEvent ) {
  221. DBGMSG( DBG_WARNING, ("CreateEvent( ChangeEvent ) failed: Error %d\n", GetLastError()));
  222. LeaveSplSem();
  223. return 0;
  224. }
  225. DBGMSG(DBG_NOTIFY, ("ChangeEvent == %x\n", ChangeEvent));
  226. //
  227. // SetSpoolChange checks that pSpool->ChangeEvent is non-null
  228. // to decide whether to call SetEvent().
  229. //
  230. pSpool->WaitFlags = fdwFilterFlags;
  231. pSpool->ChangeEvent = ChangeEvent;
  232. pSpool->pChangeFlags = &ChangeFlags;
  233. pSpool->Status |= SPOOL_STATUS_NOTIFY;
  234. LeaveSplSem();
  235. DBGMSG( DBG_NOTIFY,
  236. ( "WaitForPrinterChange: Calling WaitForSingleObject( %x )\n",
  237. pSpool->ChangeEvent ));
  238. rc = WaitForSingleObject(pSpool->ChangeEvent,
  239. PRINTER_CHANGE_TIMEOUT_VALUE);
  240. DBGMSG( DBG_NOTIFY,
  241. ( "WaitForPrinterChange: WaitForSingleObject( %x ) returned\n",
  242. pSpool->ChangeEvent ));
  243. EnterSplSem();
  244. pSpool->Status &= ~SPOOL_STATUS_NOTIFY;
  245. pSpool->ChangeEvent = NULL;
  246. pSpool->pChangeFlags = NULL;
  247. if (rc == WAIT_TIMEOUT) {
  248. DBGMSG(DBG_INFO, ("WaitForPrinterChange on %ws timed out after %d minutes\n",
  249. pIniPrinter ? pIniPrinter->pName : pSpool->pIniSpooler->pMachineName,
  250. (PRINTER_CHANGE_TIMEOUT_VALUE / 60000)));
  251. ChangeFlags |= fdwFilterFlags;
  252. TimeoutFlags = PRINTER_CHANGE_TIMEOUT;
  253. }
  254. if (ChangeFlags == PRINTER_CHANGE_CLOSE_PRINTER) {
  255. ChangeFlags = 0;
  256. SetLastError(ERROR_INVALID_HANDLE);
  257. }
  258. DBGMSG(DBG_NOTIFY, ("Printer change %08x detected on %ws:\n%d remaining caller%s\n",
  259. ((ChangeFlags & fdwFilterFlags) | TimeoutFlags),
  260. pIniPrinter ? pIniPrinter->pName : pSpool->pIniSpooler->pMachineName,
  261. Count, Count == 1 ? "" : "s"));
  262. if (ChangeEvent && !CloseHandle(ChangeEvent)) {
  263. DBGMSG(DBG_WARNING, ("CloseHandle( %x ) failed: Error %d\n",
  264. ChangeEvent, GetLastError()));
  265. }
  266. //
  267. // If the pSpool is pending deletion, we must free it here.
  268. //
  269. if (pSpool->eStatus & STATUS_PENDING_DELETION) {
  270. FreeSplMem(pSpool);
  271. }
  272. LeaveSplSem();
  273. return ((ChangeFlags & fdwFilterFlags) | TimeoutFlags);
  274. }
  275. BOOL
  276. SetSpoolClosingChange(
  277. PSPOOL pSpool)
  278. /*++
  279. Routine Description:
  280. A print handle is closing; trigger a notification.
  281. Arguments:
  282. Return Value:
  283. --*/
  284. {
  285. return SetSpoolChange(pSpool,
  286. NULL,
  287. NULL,
  288. PRINTER_CHANGE_CLOSE_PRINTER);
  289. }
  290. BOOL
  291. SetSpoolChange(
  292. PSPOOL pSpool,
  293. PNOTIFY_RAW_DATA pNotifyRawData,
  294. PDWORD pdwNotifyVectors,
  295. DWORD Flags)
  296. /*++
  297. Routine Description:
  298. Sets the event for notification or calls ReplyPrinterChangeNotification.
  299. This is called by SetPrinterChange for every open handle on a printer
  300. and the local server.
  301. It should also be called when an individual handle is closed.
  302. Assumes we're INSIDE the spooler critical section
  303. Arguments:
  304. pSpool -- Specifies handle that changed.
  305. pIniJob -- Used if there is a watch on job information.
  306. pdwNotifyVectors -- Specifies what things have changed.
  307. Flags -- WaitForPrinterChange flags.
  308. Return Value:
  309. --*/
  310. {
  311. DWORD ChangeFlags;
  312. SplInSem();
  313. if( Flags == PRINTER_CHANGE_CLOSE_PRINTER ) {
  314. ChangeFlags = PRINTER_CHANGE_CLOSE_PRINTER;
  315. } else {
  316. ChangeFlags = ( pSpool->ChangeFlags | Flags ) & pSpool->WaitFlags;
  317. }
  318. //
  319. // If we have STATUS_VALID set
  320. // then we are using the new FFPCN code.
  321. //
  322. if ( pSpool->eStatus & STATUS_VALID ) {
  323. NotifyInfoTypes(pSpool,
  324. pNotifyRawData,
  325. pdwNotifyVectors,
  326. ChangeFlags);
  327. }
  328. if ( ChangeFlags ) {
  329. pSpool->ChangeFlags = 0;
  330. if ( pSpool->pChangeFlags ) {
  331. *pSpool->pChangeFlags = ChangeFlags;
  332. DBGMSG( DBG_NOTIFY, ( "SetSpoolChange: Calling SetEvent( %x )\n", pSpool->ChangeEvent ));
  333. SetEvent(pSpool->ChangeEvent);
  334. DBGMSG( DBG_NOTIFY, ( "SetSpoolChange: SetEvent( %x ) returned\n", pSpool->ChangeEvent ));
  335. pSpool->pChangeFlags = NULL;
  336. }
  337. }
  338. return TRUE;
  339. }
  340. BOOL
  341. SetPrinterChange(
  342. PINIPRINTER pIniPrinter,
  343. PINIJOB pIniJob,
  344. PDWORD pdwNotifyVectors,
  345. DWORD Flags,
  346. PINISPOOLER pIniSpooler)
  347. /*++
  348. Routine Description:
  349. Calls SetSpoolChange for every open handle for the server
  350. and printer, if specified.
  351. Arguments:
  352. pIniPrinter - NULL, or a valid pointer to the INIPRINTER for the printer
  353. on which the change occurred.
  354. Flags - PRINTER_CHANGE_* constant indicating what happened.
  355. Note: we pass a pointer to pPrinterNotifyInfo to SetSpoolChange.
  356. If one call needs it, it will check this parm, then create it if
  357. necessary. This way it is retrieved only once.
  358. Return Value:
  359. --*/
  360. {
  361. NOTIFY_RAW_DATA aNotifyRawData[NOTIFY_TYPE_MAX];
  362. PSPOOL pSpool;
  363. PINIPRINTER mypIniPrinter;
  364. SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
  365. SplInSem();
  366. if ( pIniSpooler->SpoolerFlags & SPL_PRINTER_CHANGES ) {
  367. aNotifyRawData[0].pvData = pIniPrinter;
  368. aNotifyRawData[0].dwId = pIniPrinter ? pIniPrinter->dwUniqueSessionID : 0;
  369. aNotifyRawData[1].pvData = pIniJob;
  370. aNotifyRawData[1].dwId = pIniJob ? pIniJob->JobId : 0;
  371. if ( pIniPrinter ) {
  372. SPLASSERT( ( pIniPrinter->signature == IP_SIGNATURE ) &&
  373. ( pIniPrinter->pIniSpooler == pIniSpooler ));
  374. DBGMSG(DBG_NOTIFY, ("SetPrinterChange %ws; Flags: %08x\n",
  375. pIniPrinter->pName, Flags));
  376. for (pSpool = pIniPrinter->pSpool; pSpool; pSpool = pSpool->pNext) {
  377. SetSpoolChange( pSpool,
  378. aNotifyRawData,
  379. pdwNotifyVectors,
  380. Flags );
  381. }
  382. } else {
  383. // WorkStation Caching requires a time stamp change
  384. // any time cached data changes
  385. if ( Flags & ( PRINTER_CHANGE_FORM | PRINTER_CHANGE_ADD_PRINTER_DRIVER ) ) {
  386. for ( mypIniPrinter = pIniSpooler->pIniPrinter;
  387. mypIniPrinter != NULL ;
  388. mypIniPrinter = mypIniPrinter->pNext ) {
  389. UpdatePrinterIni ( mypIniPrinter, CHANGEID_ONLY );
  390. }
  391. }
  392. }
  393. if ( pSpool = pIniSpooler->pSpool ) {
  394. DBGMSG( DBG_NOTIFY, ("SetPrinterChange %ws; Flags: %08x\n",
  395. pIniSpooler->pMachineName, Flags));
  396. for ( ; pSpool; pSpool = pSpool->pNext) {
  397. SetSpoolChange( pSpool,
  398. aNotifyRawData,
  399. pdwNotifyVectors,
  400. Flags );
  401. }
  402. }
  403. }
  404. return TRUE;
  405. }
  406. BOOL
  407. LocalFindFirstPrinterChangeNotification(
  408. HANDLE hPrinter,
  409. DWORD fdwFilterFlags,
  410. DWORD fdwOptions,
  411. HANDLE hNotify,
  412. PDWORD pfdwStatus,
  413. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  414. PVOID pvReserved1)
  415. {
  416. PINIPRINTER pIniPrinter = NULL;
  417. PSPOOL pSpool = (PSPOOL)hPrinter;
  418. EnterSplSem();
  419. switch (ValidateStartNotify(pSpool,
  420. fdwFilterFlags,
  421. fdwOptions,
  422. pPrinterNotifyOptions,
  423. &pIniPrinter)) {
  424. case STATUS_PORT:
  425. DBGMSG(DBG_NOTIFY, ("LFFPCN: Port nomon 0x%x\n", pSpool));
  426. pSpool->eStatus |= STATUS_PORT;
  427. LeaveSplSem();
  428. *pfdwStatus = 0;
  429. return ProvidorFindFirstPrinterChangeNotification(pSpool->hPort,
  430. fdwFilterFlags,
  431. fdwOptions,
  432. hNotify,
  433. pPrinterNotifyOptions,
  434. pvReserved1);
  435. case STATUS_FAIL:
  436. DBGMSG(DBG_WARNING, ("ValidateStartNotify failed!\n"));
  437. LeaveSplSem();
  438. return FALSE;
  439. case STATUS_VALID:
  440. break;
  441. }
  442. pSpool->eStatus = STATUS_NULL;
  443. if (pPrinterNotifyOptions) {
  444. if (!SetupNotifyOptions(pSpool, pPrinterNotifyOptions)) {
  445. DBGMSG(DBG_WARNING, ("SetupNotifyOptions failed!\n"));
  446. LeaveSplSem();
  447. return FALSE;
  448. }
  449. }
  450. //
  451. // Setup notification
  452. //
  453. DBGMSG(DBG_NOTIFY, ("LFFPCN: Port has monitor: Setup 0x%x\n", pSpool));
  454. pSpool->WaitFlags = fdwFilterFlags;
  455. pSpool->hNotify = hNotify;
  456. pSpool->eStatus |= STATUS_VALID;
  457. pSpool->Status |= SPOOL_STATUS_NOTIFY;
  458. LeaveSplSem();
  459. *pfdwStatus = PRINTER_NOTIFY_STATUS_ENDPOINT;
  460. return TRUE;
  461. }
  462. BOOL
  463. LocalFindClosePrinterChangeNotification(
  464. HANDLE hPrinter)
  465. {
  466. PSPOOL pSpool = (PSPOOL)hPrinter;
  467. BOOL bReturn = FALSE;
  468. if (ValidateSpoolHandle(pSpool, 0)) {
  469. EnterSplSem();
  470. //
  471. // If it's the port case (false connect) we pass the close
  472. // request to the right providor.
  473. // Otherwise, close ourselves.
  474. //
  475. if (pSpool->eStatus & STATUS_PORT) {
  476. DBGMSG(DBG_TRACE, ("LFCPCN: Port nomon 0x%x\n", pSpool));
  477. LeaveSplSem();
  478. bReturn = ProvidorFindClosePrinterChangeNotification(pSpool->hPort);
  479. } else {
  480. if (pSpool->eStatus & STATUS_VALID) {
  481. DBGMSG(DBG_TRACE, ("LFCPCN: Close notify 0x%x\n", pSpool));
  482. pSpool->WaitFlags = 0;
  483. pSpool->eStatus = STATUS_NULL;
  484. pSpool->Status &= ~SPOOL_STATUS_NOTIFY;
  485. bReturn = TRUE;
  486. } else {
  487. DBGMSG(DBG_WARNING, ("LFCPCN: Invalid handle 0x%x\n", pSpool));
  488. SetLastError(ERROR_INVALID_PARAMETER);
  489. }
  490. LeaveSplSem();
  491. }
  492. }
  493. return bReturn;
  494. }
  495. ESTATUS
  496. ValidateStartNotify(
  497. PSPOOL pSpool,
  498. DWORD fdwFilterFlags,
  499. DWORD fdwOptions,
  500. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  501. PINIPRINTER* ppIniPrinter)
  502. /*++
  503. Routine Description:
  504. Validates the pSpool and Flags for notifications.
  505. Arguments:
  506. pSpool - pSpool to validate
  507. fdwFilterFlags - Flags to validate
  508. fdwOptions - Options to validate
  509. pPrinterNotifyOptions
  510. ppIniPrinter - returned pIniPrinter; valid only STATUS_VALID
  511. Return Value:
  512. EWAITSTATUS
  513. --*/
  514. {
  515. PINIPORT pIniPort;
  516. if (ValidateSpoolHandle(pSpool, 0)) {
  517. if ( pSpool->TypeofHandle & PRINTER_HANDLE_PRINTER ) {
  518. *ppIniPrinter = pSpool->pIniPrinter;
  519. } else if (pSpool->TypeofHandle & PRINTER_HANDLE_SERVER) {
  520. *ppIniPrinter = NULL;
  521. } else if ((pSpool->TypeofHandle & PRINTER_HANDLE_PORT) &&
  522. (pIniPort = pSpool->pIniPort) &&
  523. (pIniPort->signature == IPO_SIGNATURE) &&
  524. !(pSpool->pIniPort->Status & PP_MONITOR)) {
  525. if (pSpool->hPort == INVALID_PORT_HANDLE) {
  526. DBGMSG(DBG_WARNING, ("WaitForPrinterChange called for invalid port handle. Setting last error to %d\n",
  527. pSpool->OpenPortError));
  528. SetLastError(pSpool->OpenPortError);
  529. return STATUS_FAIL;
  530. }
  531. return STATUS_PORT;
  532. } else {
  533. DBGMSG(DBG_WARNING, ("The handle is invalid\n"));
  534. SetLastError(ERROR_INVALID_HANDLE);
  535. return STATUS_FAIL;
  536. }
  537. } else {
  538. *ppIniPrinter = NULL;
  539. }
  540. //
  541. // Allow only one wait on each handle.
  542. //
  543. if( pSpool->Status & SPOOL_STATUS_NOTIFY ) {
  544. DBGMSG(DBG_WARNING, ("There is already a thread waiting on this handle\n"));
  545. SetLastError(ERROR_ALREADY_WAITING);
  546. return STATUS_FAIL;
  547. }
  548. if (!(fdwFilterFlags & PRINTER_CHANGE_VALID) && !pPrinterNotifyOptions) {
  549. DBGMSG(DBG_WARNING, ("The wait flags specified are invalid\n"));
  550. SetLastError(ERROR_INVALID_PARAMETER);
  551. return STATUS_FAIL;
  552. }
  553. return STATUS_VALID;
  554. }
  555. //-------------------------------------------------------------------
  556. VOID
  557. GetInfoData(
  558. PSPOOL pSpool,
  559. PNOTIFY_RAW_DATA pNotifyRawData,
  560. PNOTIFY_FIELD_TYPE pNotifyFieldType,
  561. PPRINTER_NOTIFY_INFO_DATA pData,
  562. PBYTE* ppBuffer)
  563. /*++
  564. Routine Description:
  565. Based on the type and field, find and add the information.
  566. Arguments:
  567. Return Value:
  568. --*/
  569. {
  570. static LPWSTR szNULL = L"";
  571. DWORD cbData = 0;
  572. DWORD cbNeeded = 0;
  573. union {
  574. DWORD dwData;
  575. PDWORD pdwData;
  576. PWSTR pszData;
  577. PVOID pvData;
  578. PINIJOB pIniJob;
  579. PINIPORT pIniPort;
  580. PDEVMODE pDevMode;
  581. PSECURITY_DESCRIPTOR pSecurityDescriptor;
  582. PINIPRINTER pIniPrinter;
  583. PWSTR* ppszData;
  584. PINIPORT* ppIniPort;
  585. PINIPRINTER* ppIniPrinter;
  586. PINIDRIVER* ppIniDriver;
  587. PINIPRINTPROC* ppIniPrintProc;
  588. LPDEVMODE* ppDevMode;
  589. PSECURITY_DESCRIPTOR* ppSecurityDescriptor;
  590. } Var;
  591. Var.pvData = (PBYTE)pNotifyRawData->pvData + pNotifyFieldType->Offset;
  592. *ppBuffer = NULL;
  593. //
  594. // Determine space needed, and convert Data from an offset into the
  595. // actual data.
  596. //
  597. switch (pNotifyFieldType->Table) {
  598. case TABLE_JOB_POSITION:
  599. FindJob(Var.pIniJob->pIniPrinter,
  600. Var.pIniJob->JobId,
  601. &Var.dwData);
  602. goto DoDWord;
  603. case TABLE_JOB_STATUS:
  604. Var.dwData = MapJobStatus(MAP_READABLE, *Var.pdwData);
  605. goto DoDWord;
  606. case TABLE_DWORD:
  607. Var.dwData = *Var.pdwData;
  608. goto DoDWord;
  609. case TABLE_DEVMODE:
  610. Var.pDevMode = *Var.ppDevMode;
  611. if (Var.pDevMode) {
  612. cbData = Var.pDevMode->dmSize + Var.pDevMode->dmDriverExtra;
  613. } else {
  614. cbData = 0;
  615. }
  616. break;
  617. case TABLE_SECURITYDESCRIPTOR:
  618. Var.pSecurityDescriptor = *Var.ppSecurityDescriptor;
  619. cbData = GetSecurityDescriptorLength(Var.pSecurityDescriptor);
  620. break;
  621. case TABLE_STRING:
  622. Var.pszData = *Var.ppszData;
  623. goto DoString;
  624. case TABLE_TIME:
  625. //
  626. // Var already points to the SystemTime.
  627. //
  628. cbData = sizeof(SYSTEMTIME);
  629. break;
  630. case TABLE_PRINTPROC:
  631. Var.pszData = (*Var.ppIniPrintProc)->pName;
  632. goto DoString;
  633. case TABLE_JOB_PRINTERNAME:
  634. Var.pszData = (*Var.ppIniPrinter)->pName;
  635. goto DoString;
  636. case TABLE_JOB_PORT:
  637. Var.pIniPort = *Var.ppIniPort;
  638. //
  639. // Only if the job has been scheduled will pIniJob->pIniPort be
  640. // valid. If it is NULL, then just call DoString which will
  641. // return a NULL string.
  642. //
  643. if (Var.pIniPort) {
  644. Var.pszData = Var.pIniPort->pName;
  645. }
  646. goto DoString;
  647. case TABLE_DRIVER:
  648. Var.pszData = (*Var.ppIniDriver)->pName;
  649. goto DoString;
  650. case TABLE_PRINTER_SERVERNAME:
  651. Var.pszData = pSpool->pFullMachineName;
  652. goto DoString;
  653. case TABLE_PRINTER_STATUS:
  654. Var.dwData = MapPrinterStatus(MAP_READABLE, Var.pIniPrinter->Status) |
  655. Var.pIniPrinter->PortStatus;
  656. goto DoDWord;
  657. case TABLE_PRINTER_PORT:
  658. // Get required printer port size
  659. cbNeeded = 0;
  660. GetPrinterPorts(Var.pIniPrinter, 0, &cbNeeded);
  661. *ppBuffer = AllocSplMem(cbNeeded);
  662. if (*ppBuffer)
  663. GetPrinterPorts(Var.pIniPrinter, (LPWSTR) *ppBuffer, &cbNeeded);
  664. Var.pszData = (LPWSTR) *ppBuffer;
  665. goto DoString;
  666. case TABLE_NULLSTRING:
  667. Var.pszData = NULL;
  668. goto DoString;
  669. case TABLE_ZERO:
  670. Var.dwData = 0;
  671. goto DoDWord;
  672. default:
  673. SPLASSERT(FALSE);
  674. break;
  675. }
  676. pData->NotifyData.Data.pBuf = Var.pvData;
  677. pData->NotifyData.Data.cbBuf = cbData;
  678. return;
  679. DoDWord:
  680. pData->NotifyData.adwData[0] = Var.dwData;
  681. pData->NotifyData.adwData[1] = 0;
  682. return;
  683. DoString:
  684. if (Var.pszData) {
  685. //
  686. // Calculate string length.
  687. //
  688. pData->NotifyData.Data.cbBuf = (wcslen(Var.pszData)+1) *
  689. sizeof(Var.pszData[0]);
  690. pData->NotifyData.Data.pBuf = Var.pszData;
  691. } else {
  692. //
  693. // Use NULL string.
  694. //
  695. pData->NotifyData.Data.cbBuf = sizeof(Var.pszData[0]);
  696. pData->NotifyData.Data.pBuf = szNULL;
  697. }
  698. return;
  699. }
  700. //-------------------------------------------------------------------
  701. VOID
  702. NotifyInfoTypes(
  703. PSPOOL pSpool,
  704. PNOTIFY_RAW_DATA pNotifyRawData,
  705. PDWORD pdwNotifyVectors,
  706. DWORD ChangeFlags)
  707. /*++
  708. Routine Description:
  709. Sends notification info (possibly with PRINTER_NOTIFY_INFO) to
  710. the router.
  711. Arguments:
  712. pSpool -- Handle the notification is occurring on.
  713. pNotifyRawData -- Array of size NOTIFY_TYPE_MAX that has the
  714. offset structure can be used against + id.
  715. pdwNotifyVectors -- Identifies what's changing (# elements
  716. is also NOTIFY_TYPE_MAX).
  717. NULL if no changes needed.
  718. ChangeFlags -- Old style change flags.
  719. Return Value:
  720. --*/
  721. {
  722. PNOTIFY_FIELD_TYPE pNotifyFieldType;
  723. PRINTER_NOTIFY_INFO_DATA Data;
  724. PBYTE pBuffer;
  725. BOOL bReturn;
  726. DWORD i,j;
  727. DWORD dwMask;
  728. //
  729. // If we are not valid, OR
  730. // we have no notify vectors, OR
  731. // we have no RAW data OR
  732. // our vectors don't match what change
  733. // then
  734. // If no ChangeFlags return
  735. // DoReply and avoid any Partials.
  736. //
  737. if (!(pSpool->eStatus & STATUS_INFO) ||
  738. !pdwNotifyVectors ||
  739. !pNotifyRawData ||
  740. (!(pdwNotifyVectors[0] & pSpool->adwNotifyVectors[0] ||
  741. pdwNotifyVectors[1] & pSpool->adwNotifyVectors[1]))) {
  742. if (!ChangeFlags)
  743. return;
  744. goto DoReply;
  745. }
  746. //
  747. // HACK: Special case NVPurge so that it causes a discard.
  748. // (We don't want to send all those notifications.)
  749. //
  750. if (pdwNotifyVectors == NVPurge) {
  751. PartialReplyPrinterChangeNotification(pSpool->hNotify, NULL);
  752. goto DoReply;
  753. }
  754. for (i=0; i< NOTIFY_TYPE_MAX; i++, pdwNotifyVectors++) {
  755. dwMask = 0x1;
  756. SPLASSERT(adwNotifyFieldOffsets[i] < sizeof(DWORD)*8);
  757. for (j=0; j< adwNotifyFieldOffsets[i]; j++, dwMask <<= 1) {
  758. //
  759. // If we have a change we are interested in,
  760. // PartialReply.
  761. //
  762. if (dwMask & *pdwNotifyVectors & pSpool->adwNotifyVectors[i]) {
  763. pNotifyFieldType = &apNotifyFieldTypes[i][j];
  764. GetInfoData(pSpool,
  765. &pNotifyRawData[i],
  766. pNotifyFieldType,
  767. &Data,
  768. &pBuffer);
  769. Data.Type = (WORD)i;
  770. Data.Field = pNotifyFieldType->Field;
  771. Data.Reserved = 0;
  772. Data.Id = pNotifyRawData[i].dwId;
  773. //
  774. // If the partial reply failed, then we will be refreshing
  775. // soon, so exit now.
  776. //
  777. bReturn = PartialReplyPrinterChangeNotification(
  778. pSpool->hNotify,
  779. &Data);
  780. if (pBuffer) {
  781. FreeSplMem(pBuffer);
  782. }
  783. if (!bReturn) {
  784. DBGMSG(DBG_TRACE, ("PartialReplyPCN %x failed: %d!\n",
  785. pSpool->hNotify,
  786. GetLastError()));
  787. goto DoReply;
  788. }
  789. }
  790. }
  791. }
  792. DoReply:
  793. //
  794. // A full reply is needed to kick off the notification.
  795. //
  796. ReplyPrinterChangeNotification(pSpool->hNotify,
  797. ChangeFlags,
  798. NULL,
  799. NULL);
  800. }
  801. BOOL
  802. RefreshBuildInfoData(
  803. PSPOOL pSpool,
  804. PPRINTER_NOTIFY_INFO pInfo,
  805. UINT cInfo,
  806. WORD Type,
  807. PNOTIFY_RAW_DATA pNotifyRawData)
  808. /*++
  809. Routine Description:
  810. Sends notification info (possibly with PRINTER_NOTIFY_INFO) to
  811. the router.
  812. Arguments:
  813. pSpool -- Handle the notification is occurring on.
  814. pInfo -- Array of structure to receive new info.
  815. cInfo -- Number of structures in array pInfo.
  816. Type -- Indicates type of notification: job or printer.
  817. pNotifyRawData -- Array of size NOTIFY_TYPE_MAX that has the
  818. offset structure can be used against + id.
  819. Return Value:
  820. --*/
  821. {
  822. PRINTER_NOTIFY_INFO_DATA Data;
  823. DWORD cbData;
  824. PNOTIFY_FIELD_TYPE pNotifyFieldType;
  825. PBYTE pBuffer;
  826. BOOL bReturn;
  827. DWORD j;
  828. DWORD dwMask;
  829. dwMask = 0x1;
  830. SPLASSERT(adwNotifyFieldOffsets[Type] < sizeof(DWORD)*8);
  831. for (j=0; j< adwNotifyFieldOffsets[Type]; j++, dwMask <<= 1) {
  832. //
  833. // If we have a change we are interested in,
  834. // add it.
  835. //
  836. if (dwMask & pSpool->adwNotifyVectors[Type]) {
  837. //
  838. // Check if we have enough space.
  839. //
  840. if (pInfo->Count >= cInfo) {
  841. SPLASSERT(pInfo->Count < cInfo);
  842. return FALSE;
  843. }
  844. pNotifyFieldType = &apNotifyFieldTypes[Type][j];
  845. GetInfoData(pSpool,
  846. pNotifyRawData,
  847. pNotifyFieldType,
  848. &Data,
  849. &pBuffer);
  850. Data.Type = Type;
  851. Data.Field = pNotifyFieldType->Field;
  852. Data.Reserved = 0;
  853. Data.Id = pNotifyRawData->dwId;
  854. bReturn = AppendPrinterNotifyInfoData(pInfo, &Data, 0);
  855. if (pBuffer)
  856. FreeSplMem(pBuffer);
  857. if (!bReturn) {
  858. DBGMSG(DBG_WARNING, ("AppendPrinterNotifyInfoData failed: %d!\n",
  859. GetLastError()));
  860. return FALSE;
  861. }
  862. }
  863. }
  864. return TRUE;
  865. }
  866. //-------------------------------------------------------------------
  867. BOOL
  868. SetupNotifyVector(
  869. PDWORD pdwNotifyVectors,
  870. PPRINTER_NOTIFY_OPTIONS_TYPE pType)
  871. /*++
  872. Routine Description:
  873. Setup the notification vector based on pPrinterNotifyType.
  874. We assume that the size of pPrinterNotifyType has been validated
  875. (so that it fits within the containing structure). We only
  876. need to verify that the Count falls within its stated Size.
  877. Arguments:
  878. pdwNotifyVectors - Structure to fill in.
  879. pType - Source information.
  880. Return Value:
  881. TRUE = success,
  882. FALSE = failure.
  883. --*/
  884. {
  885. PNOTIFY_FIELD_TYPE pNotifyFieldType;
  886. PWORD pFields;
  887. DWORD i, j;
  888. DWORD Count;
  889. BOOL bReturn = FALSE;
  890. __try {
  891. if( pType ){
  892. Count = pType->Count;
  893. pFields = pType->pFields;
  894. if (pType->Type >= NOTIFY_TYPE_MAX) {
  895. DBGMSG(DBG_WARN, ("SetupNotifyVector: type %d field %d not found!\n",
  896. pType->Type, *pFields));
  897. } else {
  898. for (i=0; i < Count; i++, pFields++) {
  899. if (*pFields >= adwNotifyFieldOffsets[pType->Type]) {
  900. DBGMSG(DBG_WARN, ("SetupNotifyVector: type %d field %d not found!\n",
  901. pType->Type, *pFields));
  902. break;
  903. }
  904. SPLASSERT(apNotifyFieldTypes[pType->Type][*pFields].Table != TABLE_SPECIAL);
  905. SPLASSERT(apNotifyFieldTypes[pType->Type][*pFields].Field == *pFields);
  906. SPLASSERT(*pFields < 32);
  907. //
  908. // Found index j, set this bit in our array.
  909. //
  910. pdwNotifyVectors[pType->Type] |= (1 << *pFields);
  911. }
  912. if( i == Count ){
  913. bReturn = TRUE;
  914. }
  915. }
  916. }
  917. } __except( EXCEPTION_EXECUTE_HANDLER ){
  918. }
  919. return bReturn;
  920. }
  921. BOOL
  922. SetupNotifyOptions(
  923. PSPOOL pSpool,
  924. PPRINTER_NOTIFY_OPTIONS pOptions)
  925. /*++
  926. Routine Description:
  927. Initializes pSpool->adwNotifyVectors.
  928. Arguments:
  929. pSpool - Spool handle to setup the notification against.
  930. pOptions - Options that specify the notification.
  931. Return Value:
  932. TRUE - Success, FALSE - FAILURE
  933. LastError set.
  934. --*/
  935. {
  936. DWORD i;
  937. BOOL bAccessGranted = TRUE;
  938. SplInSem();
  939. ZeroMemory(pSpool->adwNotifyVectors,
  940. sizeof(pSpool->adwNotifyVectors));
  941. //
  942. // Traverse Options structure.
  943. //
  944. for (i = 0; i < pOptions->Count; i++) {
  945. if (!SetupNotifyVector(pSpool->adwNotifyVectors,
  946. &pOptions->pTypes[i])){
  947. SetLastError( ERROR_INVALID_PARAMETER );
  948. return FALSE;
  949. }
  950. }
  951. //
  952. // Now check if we have sufficient privilege to setup the notification.
  953. //
  954. //
  955. // Check if we are looking for the security descriptor on
  956. // a printer. If so, we need READ_CONTROL or ACCESS_SYSTEM_SECURITY
  957. // enabled.
  958. //
  959. if( pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE] &
  960. BIT(I_PRINTER_SECURITY_DESCRIPTOR )){
  961. if( !AreAnyAccessesGranted( pSpool->GrantedAccess,
  962. READ_CONTROL | ACCESS_SYSTEM_SECURITY )){
  963. bAccessGranted = FALSE;
  964. }
  965. }
  966. if( pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE] &
  967. ~BIT(I_PRINTER_SECURITY_DESCRIPTOR )){
  968. if( pSpool->TypeofHandle & PRINTER_HANDLE_SERVER ){
  969. //
  970. // There does not appear to be a check for EnumPrinters.
  971. //
  972. // This seems odd since you there is a security check on a
  973. // GetPrinter call, but there is none on EnumPrinters (aside
  974. // from enumerating share printers only for remote non-admins).
  975. //
  976. } else {
  977. //
  978. // This matches the check in SplGetPrinter: we need to
  979. // have PRINTER_ACCESS_USE to read the non-security information.
  980. //
  981. if( !AccessGranted( SPOOLER_OBJECT_PRINTER,
  982. PRINTER_ACCESS_USE,
  983. pSpool )){
  984. bAccessGranted = FALSE;
  985. }
  986. }
  987. }
  988. if( !bAccessGranted ){
  989. SetLastError( ERROR_ACCESS_DENIED );
  990. return FALSE;
  991. }
  992. pSpool->eStatus |= STATUS_INFO;
  993. return TRUE;
  994. }
  995. UINT
  996. PopCount(
  997. DWORD dwValue)
  998. {
  999. UINT i;
  1000. UINT cPopCount = 0;
  1001. for(i=0; i< sizeof(dwValue)*8; i++) {
  1002. if (dwValue & (1<<i))
  1003. cPopCount++;
  1004. }
  1005. return cPopCount;
  1006. }
  1007. BOOL
  1008. LocalRefreshPrinterChangeNotification(
  1009. HANDLE hPrinter,
  1010. DWORD dwColor,
  1011. PPRINTER_NOTIFY_OPTIONS pOptions,
  1012. PPRINTER_NOTIFY_INFO* ppInfo)
  1013. /*++
  1014. Routine Description:
  1015. Refreshes data in the case of overflows.
  1016. Arguments:
  1017. Return Value:
  1018. --*/
  1019. {
  1020. PINIJOB pIniJob;
  1021. PINIPRINTER pIniPrinter;
  1022. DWORD cPrinters;
  1023. PSPOOL pSpool = (PSPOOL)hPrinter;
  1024. PDWORD pdwNotifyVectors = pSpool->adwNotifyVectors;
  1025. UINT cInfo = 0;
  1026. PPRINTER_NOTIFY_INFO pInfo = NULL;
  1027. NOTIFY_RAW_DATA NotifyRawData;
  1028. EnterSplSem();
  1029. if (!ValidateSpoolHandle(pSpool, 0 ) ||
  1030. !(pSpool->eStatus & STATUS_INFO)) {
  1031. SetLastError( ERROR_INVALID_HANDLE );
  1032. goto Fail;
  1033. }
  1034. //
  1035. // New bits added, can't compare directly against PRINTER_HANDLE_SERVER.
  1036. //
  1037. if( pSpool->TypeofHandle & PRINTER_HANDLE_SERVER ){
  1038. //
  1039. // If the call is a remote one, and the user is not an admin, then
  1040. // we don't want to show unshared printers.
  1041. //
  1042. BOOL bHideUnshared = (pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL) &&
  1043. !(pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_ADMIN);
  1044. for (cPrinters = 0, pIniPrinter = pSpool->pIniSpooler->pIniPrinter;
  1045. pIniPrinter;
  1046. pIniPrinter=pIniPrinter->pNext ) {
  1047. if ((!(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED) &&
  1048. bHideUnshared)
  1049. #ifdef _HYDRA_
  1050. || !ShowThisPrinter(pIniPrinter)
  1051. #endif
  1052. ) {
  1053. continue;
  1054. }
  1055. cPrinters++;
  1056. }
  1057. cInfo += PopCount(pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE]) *
  1058. cPrinters;
  1059. //
  1060. // Traverse all printers and create info.
  1061. //
  1062. pInfo = RouterAllocPrinterNotifyInfo(cInfo);
  1063. if (!pInfo)
  1064. goto Fail;
  1065. if (pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE]) {
  1066. for (pIniPrinter = pSpool->pIniSpooler->pIniPrinter;
  1067. pIniPrinter;
  1068. pIniPrinter=pIniPrinter->pNext) {
  1069. //
  1070. // Do not send notification for non-shared printers for remote
  1071. // users who are not admins
  1072. //
  1073. if ((!(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED) &&
  1074. bHideUnshared )
  1075. #ifdef _HYDRA_
  1076. || !ShowThisPrinter(pIniPrinter)
  1077. #endif
  1078. ) {
  1079. continue;
  1080. }
  1081. NotifyRawData.pvData = pIniPrinter;
  1082. NotifyRawData.dwId = pIniPrinter->dwUniqueSessionID;
  1083. if (!RefreshBuildInfoData(pSpool,
  1084. pInfo,
  1085. cInfo,
  1086. PRINTER_NOTIFY_TYPE,
  1087. &NotifyRawData)) {
  1088. goto Fail;
  1089. }
  1090. }
  1091. }
  1092. } else {
  1093. //
  1094. // Calculate size of buffer needed.
  1095. //
  1096. if (pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE]) {
  1097. //
  1098. // Setup printer info.
  1099. //
  1100. cInfo += PopCount(pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE]);
  1101. }
  1102. if (pSpool->adwNotifyVectors[JOB_NOTIFY_TYPE]) {
  1103. cInfo += PopCount(pSpool->adwNotifyVectors[JOB_NOTIFY_TYPE]) *
  1104. pSpool->pIniPrinter->cJobs;
  1105. }
  1106. //
  1107. // Traverse all jobs and create info.
  1108. //
  1109. pInfo = RouterAllocPrinterNotifyInfo(cInfo);
  1110. if (!pInfo)
  1111. goto Fail;
  1112. if (pSpool->adwNotifyVectors[PRINTER_NOTIFY_TYPE]) {
  1113. NotifyRawData.pvData = pSpool->pIniPrinter;
  1114. NotifyRawData.dwId = pSpool->pIniPrinter->dwUniqueSessionID;
  1115. if (!RefreshBuildInfoData(pSpool,
  1116. pInfo,
  1117. cInfo,
  1118. PRINTER_NOTIFY_TYPE,
  1119. &NotifyRawData)) {
  1120. goto Fail;
  1121. }
  1122. }
  1123. if (pSpool->adwNotifyVectors[JOB_NOTIFY_TYPE]) {
  1124. for (pIniJob = pSpool->pIniPrinter->pIniFirstJob;
  1125. pIniJob;
  1126. pIniJob = pIniJob->pIniNextJob) {
  1127. //
  1128. // Hide Chained Jobs
  1129. //
  1130. if (!(pIniJob->Status & JOB_HIDDEN )) {
  1131. NotifyRawData.pvData = pIniJob;
  1132. NotifyRawData.dwId = pIniJob->JobId;
  1133. if (!RefreshBuildInfoData(pSpool,
  1134. pInfo,
  1135. cInfo,
  1136. JOB_NOTIFY_TYPE,
  1137. &NotifyRawData)) {
  1138. goto Fail;
  1139. }
  1140. }
  1141. }
  1142. }
  1143. }
  1144. SPLASSERT(cInfo >= pInfo->Count);
  1145. LeaveSplSem();
  1146. *ppInfo = pInfo;
  1147. return TRUE;
  1148. Fail:
  1149. SPLASSERT(!pInfo || cInfo >= pInfo->Count);
  1150. LeaveSplSem();
  1151. *ppInfo = NULL;
  1152. if (pInfo) {
  1153. RouterFreePrinterNotifyInfo(pInfo);
  1154. }
  1155. return FALSE;
  1156. }