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.

1706 lines
39 KiB

  1. /*++
  2. Copyright (c) 1990-1994 Microsoft Corporation
  3. All rights reserved
  4. Module Name:
  5. Change.c
  6. Abstract:
  7. Handles implementation for WaitForPrinterChange and related apis.
  8. FindFirstPrinterChangeNotification
  9. RouterFindNextPrinterChangeNotification
  10. FindClosePrinterChangeNotification
  11. Used by providors:
  12. ReplyPrinterChangeNotification [Function Call]
  13. CallRouterFindFirstPrinterChangeNotification [Function Call]
  14. Author:
  15. Albert Ting (AlbertT) 18-Jan-94
  16. Environment:
  17. User Mode -Win32
  18. Revision History:
  19. --*/
  20. #include "precomp.h"
  21. #pragma hdrstop
  22. #include <ntfytab.h>
  23. #define PRINTER_NOTIFY_DEFAULT_POLLTIME 10000
  24. CRITICAL_SECTION RouterNotifySection;
  25. PCHANGEINFO pChangeInfoHead;
  26. HANDLE hEventPoll;
  27. BOOL
  28. SetupReplyNotification(
  29. PCHANGE pChange,
  30. DWORD fdwOptions,
  31. DWORD fdwStatus,
  32. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  33. PPRINTER_NOTIFY_INIT pPrinterNotifyInit);
  34. BOOL
  35. FindFirstPrinterChangeNotificationWorker(
  36. HANDLE hPrinter,
  37. DWORD fdwFilterFlags,
  38. DWORD fdwOptions,
  39. DWORD dwPID,
  40. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  41. PHANDLE phEvent,
  42. BOOL bSpooler);
  43. DWORD
  44. FindClosePrinterChangeNotificationWorker(
  45. HANDLE hPrinter);
  46. BOOL
  47. SetupChange(
  48. PPRINTHANDLE pPrintHandle,
  49. DWORD dwPrinterRemote,
  50. LPWSTR pszLocalMachine,
  51. PDWORD pfdwStatus,
  52. DWORD fdwOptions,
  53. DWORD fdwFilterFlags,
  54. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  55. PPRINTER_NOTIFY_INIT* pPrinterNotifyInit);
  56. VOID
  57. FailChange(
  58. PPRINTHANDLE pPrintHandle);
  59. BOOL
  60. RouterRefreshPrinterChangeNotification(
  61. HANDLE hPrinter,
  62. DWORD dwColor,
  63. PVOID pPrinterNotifyRefresh,
  64. PPRINTER_NOTIFY_INFO* ppInfo);
  65. BOOL
  66. WPCInit()
  67. {
  68. //
  69. // Create non-signaled, autoreset event.
  70. //
  71. hEventPoll = CreateEvent(NULL, FALSE, FALSE, NULL);
  72. if (!hEventPoll)
  73. return FALSE;
  74. __try {
  75. InitializeCriticalSection(&RouterNotifySection);
  76. } __except(EXCEPTION_EXECUTE_HANDLER) {
  77. SetLastError(GetExceptionCode());
  78. return FALSE;
  79. }
  80. return TRUE;
  81. }
  82. VOID
  83. WPCDestroy()
  84. {
  85. #if 0
  86. DeleteCriticalSection(&RouterNotifySection);
  87. CloseHandle(hEventPoll);
  88. #endif
  89. }
  90. BOOL
  91. RouterFindFirstPrinterChangeNotification(
  92. HANDLE hPrinter,
  93. DWORD fdwFilterFlags,
  94. DWORD fdwOptions,
  95. DWORD dwPID,
  96. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  97. PHANDLE phEvent)
  98. {
  99. return FindFirstPrinterChangeNotificationWorker(
  100. hPrinter,
  101. fdwFilterFlags,
  102. fdwOptions,
  103. dwPID,
  104. pPrinterNotifyOptions,
  105. phEvent,
  106. FALSE);
  107. }
  108. BOOL
  109. FindFirstPrinterChangeNotificationWorker(
  110. HANDLE hPrinter,
  111. DWORD fdwFilterFlags,
  112. DWORD fdwOptions,
  113. DWORD dwPID,
  114. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  115. PHANDLE phEvent,
  116. BOOL bSpooler)
  117. /*++
  118. Routine Description:
  119. Sets up notification (coming from client side, winspool.drv).
  120. Create an event and duplicate it into the clients address
  121. space so we can communicate with it.
  122. Arguments:
  123. hPrinter - Printer to watch
  124. fdwFilterFlags - Type of notification to set up (filter)
  125. fdwOptions - user specified option (GROUPING, etc.)
  126. dwPID - PID of the client process (needed to dup handle)
  127. phEvent - hEvent to pass back to the client.
  128. bSpooler - Indicates if called from spooler. If TRUE, then we don't
  129. need to duplicate the event.
  130. Return Value:
  131. --*/
  132. {
  133. LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
  134. HANDLE hProcess;
  135. DWORD fdwStatus = 0;
  136. PCHANGE pChange = NULL;
  137. PPRINTER_NOTIFY_INIT pPrinterNotifyInit = NULL;
  138. BOOL bReturn;
  139. if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
  140. SetLastError(ERROR_INVALID_HANDLE);
  141. return FALSE;
  142. }
  143. EnterRouterSem();
  144. //
  145. // Clear this out
  146. //
  147. *phEvent = NULL;
  148. // Give a unique DWORD session ID for pPrintHandle
  149. while (pPrintHandle->dwUniqueSessionID == 0 ||
  150. pPrintHandle->dwUniqueSessionID == 0xffffffff) {
  151. pPrintHandle->dwUniqueSessionID = dwRouterUniqueSessionID++;
  152. }
  153. if (!SetupChange(pPrintHandle,
  154. pPrintHandle->dwUniqueSessionID,
  155. szMachineName,
  156. &fdwStatus,
  157. fdwOptions,
  158. fdwFilterFlags,
  159. pPrinterNotifyOptions,
  160. &pPrinterNotifyInit)) {
  161. LeaveRouterSem();
  162. return FALSE;
  163. }
  164. //
  165. // !! LATER !!
  166. //
  167. // When Delegation is supported:
  168. //
  169. // Create the event with security access based on the impersonation
  170. // token so that we can filter out bogus notifications from
  171. // random people. (Save the token in the pSpool in localspl, then
  172. // impersonate before RPCing back here. Then we can check if
  173. // we have access to the event.)
  174. //
  175. //
  176. // Create the event here that we trigger on notifications.
  177. // We will duplicate this event into the target client process.
  178. //
  179. pPrintHandle->pChange->hEvent = CreateEvent(
  180. NULL,
  181. TRUE,
  182. FALSE,
  183. NULL);
  184. if (!pPrintHandle->pChange->hEvent) {
  185. goto Fail;
  186. }
  187. if (bSpooler) {
  188. //
  189. // Client is in the spooler process.
  190. //
  191. *phEvent = pPrintHandle->pChange->hEvent;
  192. } else {
  193. //
  194. // Client is local.
  195. //
  196. //
  197. // Success, create pair
  198. //
  199. hProcess = OpenProcess(PROCESS_DUP_HANDLE,
  200. FALSE,
  201. dwPID);
  202. if (!hProcess) {
  203. goto Fail;
  204. }
  205. bReturn = DuplicateHandle(GetCurrentProcess(),
  206. pPrintHandle->pChange->hEvent,
  207. hProcess,
  208. phEvent,
  209. EVENT_ALL_ACCESS,
  210. TRUE,
  211. 0);
  212. CloseHandle(hProcess);
  213. if (!bReturn) {
  214. goto Fail;
  215. }
  216. }
  217. bReturn = SetupReplyNotification(pPrintHandle->pChange,
  218. fdwStatus,
  219. fdwOptions,
  220. pPrinterNotifyOptions,
  221. pPrinterNotifyInit);
  222. if (bReturn) {
  223. pPrintHandle->pChange->eStatus &= ~STATUS_CHANGE_FORMING;
  224. pPrintHandle->pChange->eStatus |=
  225. STATUS_CHANGE_VALID|STATUS_CHANGE_CLIENT;
  226. } else {
  227. Fail:
  228. if (pPrintHandle->pChange->hEvent)
  229. CloseHandle(pPrintHandle->pChange->hEvent);
  230. //
  231. // Local case must close handle twice since we may have
  232. // duplicated the event.
  233. //
  234. if (!bSpooler && *phEvent)
  235. CloseHandle(*phEvent);
  236. FailChange(pPrintHandle);
  237. bReturn = FALSE;
  238. }
  239. LeaveRouterSem();
  240. return bReturn;
  241. }
  242. BOOL
  243. RemoteFindFirstPrinterChangeNotification(
  244. HANDLE hPrinter,
  245. DWORD fdwFilterFlags,
  246. DWORD fdwOptions,
  247. LPWSTR pszLocalMachine,
  248. DWORD dwPrinterRemote,
  249. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions)
  250. /*++
  251. Routine Description:
  252. Handles FFPCN coming from other machines. Providers can use
  253. the ProvidorRemoteFFPCN call to initiate the RPC which this function
  254. handles. This code ships the call to the print provider. Note
  255. that we don't create any events here since the client is on
  256. a remote machine.
  257. Arguments:
  258. hPrinter - printer to watch
  259. fdwFilterFlags - type of notification to watch
  260. fdwOptions -- options on watch
  261. pszLocalMachine - name of local machine that requested the watch
  262. dwPrinterRemote - remote Printer handle
  263. Return Value:
  264. --*/
  265. {
  266. LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
  267. BOOL bReturn;
  268. DWORD fdwStatus = 0;
  269. PCHANGE pChange = NULL;
  270. PPRINTER_NOTIFY_INIT pPrinterNotifyInit = NULL;
  271. LPWSTR pszLocalMachineCopy;
  272. pszLocalMachineCopy = AllocSplStr(pszLocalMachine);
  273. if (!pszLocalMachineCopy)
  274. return FALSE;
  275. EnterRouterSem();
  276. if (!SetupChange(pPrintHandle,
  277. dwPrinterRemote,
  278. pszLocalMachineCopy,
  279. &fdwStatus,
  280. fdwOptions,
  281. fdwFilterFlags,
  282. pPrinterNotifyOptions,
  283. &pPrinterNotifyInit)) {
  284. LeaveRouterSem();
  285. return FALSE;
  286. }
  287. bReturn = SetupReplyNotification(pPrintHandle->pChange,
  288. fdwStatus,
  289. fdwOptions,
  290. pPrinterNotifyOptions,
  291. pPrinterNotifyInit);
  292. if (bReturn) {
  293. pPrintHandle->pChange->eStatus = STATUS_CHANGE_VALID;
  294. } else {
  295. FailChange(pPrintHandle);
  296. }
  297. LeaveRouterSem();
  298. return bReturn;
  299. }
  300. BOOL
  301. RouterFindNextPrinterChangeNotification(
  302. HANDLE hPrinter,
  303. DWORD fdwFlags,
  304. LPDWORD pfdwChangeFlags,
  305. PPRINTER_NOTIFY_OPTIONS pOptions,
  306. PPRINTER_NOTIFY_INFO* ppInfo)
  307. /*++
  308. Routine Description:
  309. Return information about notification that just occurred and
  310. reset to look for more notifications.
  311. Arguments:
  312. hPrinter - printer to reset event handle
  313. fdwFlags - flags (PRINTER_NOTIFY_NEXT_INFO)
  314. pfdwChange - return result of changes
  315. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions
  316. pReplyContainer - Reply info to pass out.
  317. Return Value:
  318. BOOL
  319. ** NOTE **
  320. Always assume client process is on the same machine. The client
  321. machine router always handles this call.
  322. --*/
  323. {
  324. LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
  325. PCHANGE pChange = pPrintHandle->pChange;
  326. BOOL bReturn = FALSE;
  327. if (ppInfo) {
  328. *ppInfo = NULL;
  329. }
  330. //
  331. // Currently only REFRESH option is defined.
  332. //
  333. if( pOptions && ( pOptions->Flags & ~PRINTER_NOTIFY_OPTIONS_REFRESH )){
  334. SetLastError( ERROR_INVALID_PARAMETER );
  335. return FALSE;
  336. }
  337. EnterRouterSem();
  338. if (pPrintHandle->signature != PRINTHANDLE_SIGNATURE ||
  339. !pChange ||
  340. !(pChange->eStatus & (STATUS_CHANGE_VALID|STATUS_CHANGE_CLIENT))) {
  341. SetLastError(ERROR_INVALID_HANDLE);
  342. goto Done;
  343. }
  344. //
  345. // For now, we collapse all notifications into 1.
  346. //
  347. pChange->dwCount = 0;
  348. //
  349. // Tell the user what changes occurred,
  350. // then clear it out.
  351. //
  352. *pfdwChangeFlags = pChange->fdwChangeFlags;
  353. pChange->fdwChangeFlags = 0;
  354. ResetEvent(pChange->hEvent);
  355. if (pOptions && pOptions->Flags & PRINTER_NOTIFY_OPTIONS_REFRESH) {
  356. //
  357. // Increment color.
  358. //
  359. pPrintHandle->pChange->dwColor++;
  360. LeaveRouterSem();
  361. bReturn = RouterRefreshPrinterChangeNotification(
  362. hPrinter,
  363. pPrintHandle->pChange->dwColor,
  364. pOptions,
  365. ppInfo);
  366. return bReturn;
  367. }
  368. //
  369. // If they want data && (we have data || if we have flags that we wish
  370. // to send to the user), copy the data.
  371. //
  372. if( ppInfo &&
  373. (fdwFlags & PRINTER_NOTIFY_NEXT_INFO) &&
  374. NotifyNeeded( pChange )){
  375. *ppInfo = pChange->ChangeInfo.pPrinterNotifyInfo;
  376. pChange->ChangeInfo.pPrinterNotifyInfo = NULL;
  377. //
  378. // If we need to notify because of a DISCARD, but we don't
  379. // have a pPrinterNotifyInfo, then allocate one and return it.
  380. //
  381. if( !*ppInfo ){
  382. DBGMSG( DBG_TRACE,
  383. ( "RFNPCN: Discard with no pPrinterNotifyInfo: pChange %x\n",
  384. pChange ));
  385. //
  386. // We need to return some information back to the client, so
  387. // allocate a block that has zero items. The header will be
  388. // marked as DISCARDED, which tells the user to refresh.
  389. //
  390. *ppInfo = RouterAllocPrinterNotifyInfo( 0 );
  391. if( !*ppInfo ){
  392. //
  393. // Failed to alloc memory; fail the call.
  394. //
  395. bReturn = FALSE;
  396. goto Done;
  397. }
  398. //
  399. // Mark the newly allocated information as DISCARDED.
  400. //
  401. (*ppInfo)->Flags = PRINTER_NOTIFY_INFO_DISCARDED;
  402. }
  403. }
  404. bReturn = TRUE;
  405. Done:
  406. LeaveRouterSem();
  407. return bReturn;
  408. }
  409. BOOL
  410. FindClosePrinterChangeNotification(
  411. HANDLE hPrinter)
  412. {
  413. DWORD dwError;
  414. LPPRINTHANDLE pPrintHandle = (LPPRINTHANDLE)hPrinter;
  415. EnterRouterSem();
  416. if (pPrintHandle->signature != PRINTHANDLE_SIGNATURE ||
  417. !pPrintHandle->pChange ||
  418. !(pPrintHandle->pChange->eStatus & STATUS_CHANGE_VALID)) {
  419. DBGMSG(DBG_WARN, ("FCPCNW: Invalid handle 0x%x\n", pPrintHandle));
  420. dwError = ERROR_INVALID_HANDLE;
  421. } else {
  422. dwError = FindClosePrinterChangeNotificationWorker(hPrinter);
  423. }
  424. LeaveRouterSem();
  425. if (dwError) {
  426. SetLastError(dwError);
  427. return FALSE;
  428. }
  429. return TRUE;
  430. }
  431. DWORD
  432. FindClosePrinterChangeNotificationWorker(
  433. HANDLE hPrinter)
  434. /*++
  435. Routine Description:
  436. Close a notification.
  437. Arguments:
  438. hPrinter -- printer that we want to close
  439. Return Value:
  440. ERROR code
  441. --*/
  442. {
  443. LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
  444. BOOL bLocal = FALSE;
  445. HANDLE hNotifyRemote = NULL;
  446. DWORD dwError = ERROR_SUCCESS;
  447. DBGMSG(DBG_NOTIFY, ("FCPCN: Closing 0x%x ->pChange 0x%x\n",
  448. pPrintHandle, pPrintHandle->pChange));
  449. RouterInSem();
  450. //
  451. // If the notification exists, shut it down (this is the
  452. // local case). If we are called remotely, we don't need to
  453. // do this, since hEvent wasn't created.
  454. //
  455. if (pPrintHandle->pChange->eStatus & STATUS_CHANGE_CLIENT) {
  456. CloseHandle(pPrintHandle->pChange->hEvent);
  457. bLocal = TRUE;
  458. }
  459. //
  460. // Remember what the hNotifyRemote is, in case we want to delete it.
  461. //
  462. hNotifyRemote = pPrintHandle->pChange->hNotifyRemote;
  463. //
  464. // Failure to free implies we're using it now. In this case,
  465. // don't try and free the hNotifyRemote.
  466. //
  467. if (!FreeChange(pPrintHandle->pChange)) {
  468. hNotifyRemote = NULL;
  469. }
  470. //
  471. // If local, don't allow new reply-s to be set up.
  472. //
  473. if (bLocal) {
  474. RemoveReplyClient(pPrintHandle,
  475. REPLY_TYPE_NOTIFICATION);
  476. }
  477. //
  478. // We must zero this out to prevent other threads from
  479. // attempting to close this context handle (client side)
  480. // at the same time we are closing it.
  481. //
  482. pPrintHandle->pChange = NULL;
  483. if (!bLocal) {
  484. //
  485. // Remote case, shut down the notification handle if
  486. // there is one here. (If there is a double hop, only
  487. // the second hop will have a notification reply. Currently
  488. // only 1 hop is support during registration, however.)
  489. //
  490. LeaveRouterSem();
  491. CloseReplyRemote(hNotifyRemote);
  492. EnterRouterSem();
  493. }
  494. LeaveRouterSem();
  495. RouterOutSem();
  496. if (!(*pPrintHandle->pProvidor->PrintProvidor.
  497. fpFindClosePrinterChangeNotification) (pPrintHandle->hPrinter)) {
  498. dwError = GetLastError();
  499. }
  500. EnterRouterSem();
  501. return dwError;
  502. }
  503. BOOL
  504. SetupReplyNotification(
  505. PCHANGE pChange,
  506. DWORD fdwStatus,
  507. DWORD fdwOptions,
  508. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  509. PPRINTER_NOTIFY_INIT pPrinterNotifyInit)
  510. {
  511. DWORD dwReturn = ERROR_SUCCESS;
  512. RouterInSem();
  513. if (!pChange) {
  514. dwReturn = ERROR_INVALID_PARAMETER;
  515. goto Fail;
  516. }
  517. SPLASSERT(pChange->eStatus & STATUS_CHANGE_FORMING);
  518. if (fdwStatus & PRINTER_NOTIFY_STATUS_ENDPOINT) {
  519. //
  520. // For remote notification, we must setup a reply.
  521. //
  522. if (_wcsicmp(pChange->pszLocalMachine, szMachineName)) {
  523. LeaveRouterSem();
  524. dwReturn = OpenReplyRemote(pChange->pszLocalMachine,
  525. &pChange->hNotifyRemote,
  526. pChange->dwPrinterRemote,
  527. REPLY_TYPE_NOTIFICATION,
  528. 0,
  529. NULL);
  530. EnterRouterSem();
  531. if (dwReturn)
  532. goto Fail;
  533. }
  534. //
  535. // The user can specify different status flags for the
  536. // notfication. Process them here.
  537. //
  538. if (fdwStatus & PRINTER_NOTIFY_STATUS_POLL) {
  539. //
  540. // If there wasn't an error, then our reply notification
  541. // handle is valid, and we should do the polling.
  542. //
  543. pChange->ChangeInfo.dwPollTime =
  544. (pPrinterNotifyInit &&
  545. pPrinterNotifyInit->PollTime) ?
  546. pPrinterNotifyInit->PollTime :
  547. PRINTER_NOTIFY_DEFAULT_POLLTIME;
  548. pChange->ChangeInfo.dwPollTimeLeft = pChange->ChangeInfo.dwPollTime;
  549. //
  550. // Don't cause a poll the first time it's added.
  551. //
  552. pChange->ChangeInfo.bResetPollTime = TRUE;
  553. LinkAdd(&pChange->ChangeInfo.Link, (PLINK*)&pChangeInfoHead);
  554. SetEvent(hEventPoll);
  555. }
  556. pChange->ChangeInfo.fdwStatus = fdwStatus;
  557. } else {
  558. pChange->dwPrinterRemote = 0;
  559. }
  560. if (pPrinterNotifyOptions) {
  561. pChange->eStatus |= STATUS_CHANGE_INFO;
  562. }
  563. Fail:
  564. if (dwReturn) {
  565. SetLastError(dwReturn);
  566. return FALSE;
  567. }
  568. return TRUE;
  569. }
  570. //
  571. // The Reply* functions handle calls from the server back to the client,
  572. // indicating that something changed.
  573. //
  574. BOOL
  575. ReplyPrinterChangeNotificationWorker(
  576. HANDLE hPrinter,
  577. DWORD dwColor,
  578. DWORD fdwChangeFlags,
  579. PDWORD pdwResult,
  580. PPRINTER_NOTIFY_INFO pPrinterNotifyInfo
  581. )
  582. /*++
  583. Routine Description:
  584. Notifies the client that something happened. If local, simply
  585. set the event. If remote, call ThreadNotify which spawns a thread
  586. to RPC to the client router. (This call from the server -> client
  587. requires that the spooler pipe use the NULL session, since we
  588. are in local system context and RPC calls (with no impersonation)
  589. use the NULL session.)
  590. Arguments:
  591. hPrinter -- printer that changed
  592. dwColor -- color time stamp of data
  593. fdwChangeFlags -- flags that changed
  594. pdwResult -- result flags (OPTIONAL)
  595. pPrinterNotifyInfo -- Notify info data. Note that if this is NULL,
  596. we don't set the DISCARDED flag. This is different
  597. than PartialRPN,
  598. Return Value:
  599. BOOL TRUE = success
  600. FALSE = fail
  601. --*/
  602. {
  603. LPPRINTHANDLE pPrintHandle = (LPPRINTHANDLE)hPrinter;
  604. PCHANGE pChange;
  605. BOOL bReturn = FALSE;
  606. EnterRouterSem();
  607. if (!pPrintHandle ||
  608. pPrintHandle->signature != PRINTHANDLE_SIGNATURE ||
  609. !pPrintHandle->pChange ||
  610. !(pPrintHandle->pChange->eStatus & STATUS_CHANGE_VALID)) {
  611. SetLastError(ERROR_INVALID_HANDLE);
  612. goto Done;
  613. }
  614. pChange = pPrintHandle->pChange;
  615. if (pdwResult) {
  616. *pdwResult = 0;
  617. }
  618. if (pChange->eStatus & STATUS_CHANGE_DISCARDED) {
  619. DBGMSG(DBG_WARNING,
  620. ("RPCNW: Discarded x%x, eStatus = 0x%x, pInfo = 0x%x\n",
  621. pChange,
  622. pChange->eStatus,
  623. pChange->ChangeInfo.pPrinterNotifyInfo));
  624. if (pdwResult && pPrinterNotifyInfo) {
  625. *pdwResult |= (PRINTER_NOTIFY_INFO_DISCARDED |
  626. PRINTER_NOTIFY_INFO_DISCARDNOTED);
  627. }
  628. //
  629. // If the discard has already been noted, then we don't need
  630. // to notify the client. If it hasn't been noted, we need to
  631. // trigger a notification, since the the client needs to refresh.
  632. //
  633. if (pChange->eStatus & STATUS_CHANGE_DISCARDNOTED) {
  634. bReturn = TRUE;
  635. goto Done;
  636. }
  637. }
  638. if (pChange->eStatus & STATUS_CHANGE_INFO && pPrinterNotifyInfo) {
  639. *pdwResult |= AppendPrinterNotifyInfo(pPrintHandle,
  640. dwColor,
  641. pPrinterNotifyInfo);
  642. //
  643. // AppendPrinterNotifyInfo will set the color mismatch
  644. // bit if the notification info is stale (doesn't match color).
  645. //
  646. if (*pdwResult & PRINTER_NOTIFY_INFO_COLORMISMATCH) {
  647. DBGMSG(DBG_WARNING, ("RPCN: Color mismatch; discarding\n"));
  648. bReturn = TRUE;
  649. goto Done;
  650. }
  651. }
  652. //
  653. // Store up the changes for querying later.
  654. //
  655. pChange->fdwChangeFlags |= fdwChangeFlags;
  656. //
  657. // Ensure that there is a valid notification waiting.
  658. // This is an optimization that avoids the case where the print
  659. // providor calls PartialRPCN several times, then is about to
  660. // call ReplyPCN. Before it does this, we process the
  661. // the notification (either the client picks it up, or it rpcs
  662. // out to a remote router). Suddenly we have no notification
  663. // data, so return instead of sending nothing.
  664. //
  665. if (!NotifyNeeded(pChange)) {
  666. bReturn = TRUE;
  667. goto Done;
  668. }
  669. //
  670. // Notify Here
  671. //
  672. // If this is the local machine, then just set the event and update.
  673. //
  674. if (pChange->eStatus & STATUS_CHANGE_CLIENT) {
  675. if (!pChange->hEvent ||
  676. pChange->hEvent == INVALID_HANDLE_VALUE) {
  677. DBGMSG(DBG_WARNING, ("ReplyNotify invalid event\n"));
  678. SetLastError(ERROR_INVALID_HANDLE);
  679. goto Done;
  680. }
  681. if (!SetEvent(pChange->hEvent)) {
  682. //
  683. // SetEvent failed!
  684. //
  685. DBGMSG(DBG_ERROR, ("ReplyNotify SetEvent Failed (ignore it!): Error %d.\n", GetLastError()));
  686. goto Done;
  687. }
  688. //
  689. // Keep count of notifications so that we return the correct
  690. // number of FNPCNs.
  691. //
  692. pChange->dwCount++;
  693. DBGMSG(DBG_NOTIFY, (">>>> Local trigger 0x%x\n", fdwChangeFlags));
  694. bReturn = TRUE;
  695. } else {
  696. //
  697. // Remote case.
  698. //
  699. // Note: pPrintHandle is invalid, since hNotify is valid only in the
  700. // client router address space.
  701. //
  702. DBGMSG(DBG_NOTIFY, ("*** Trigger remote event *** 0x%x\n",
  703. pPrintHandle));
  704. bReturn = ThreadNotify(pPrintHandle);
  705. }
  706. Done:
  707. LeaveRouterSem();
  708. return bReturn;
  709. }
  710. BOOL
  711. FreeChange(
  712. PCHANGE pChange)
  713. /*++
  714. Routine Description:
  715. Frees the change structure.
  716. Arguments:
  717. Return Value:
  718. TRUE = Deleted
  719. FALSE = deferred.
  720. NOTE: Assumes in Critical Section
  721. --*/
  722. {
  723. RouterInSem();
  724. //
  725. // Remove ourselves from the list if the providor wanted us
  726. // to send out polling notifications.
  727. //
  728. if (pChange->ChangeInfo.fdwStatus & PRINTER_NOTIFY_STATUS_POLL)
  729. LinkDelete(&pChange->ChangeInfo.Link, (PLINK*)&pChangeInfoHead);
  730. //
  731. // pPrintHandle should never refer to the pChange again. This
  732. // ensures that the FreePrinterHandle only frees the pChange once.
  733. //
  734. if (pChange->ChangeInfo.pPrintHandle) {
  735. pChange->ChangeInfo.pPrintHandle->pChange = NULL;
  736. pChange->ChangeInfo.pPrintHandle = NULL;
  737. }
  738. if (pChange->cRef || pChange->eStatus & STATUS_CHANGE_FORMING) {
  739. pChange->eStatus |= STATUS_CHANGE_CLOSING;
  740. DBGMSG(DBG_NOTIFY, ("FreeChange: 0x%x in use: cRef = %d\n",
  741. pChange,
  742. pChange->cRef));
  743. return FALSE;
  744. }
  745. //
  746. // If the pszLocalMachine is ourselves, then don't free it,
  747. // since there's a single instance locally.
  748. //
  749. if (pChange->pszLocalMachine != szMachineName && pChange->pszLocalMachine)
  750. FreeSplStr(pChange->pszLocalMachine);
  751. if (pChange->ChangeInfo.pPrinterNotifyInfo) {
  752. RouterFreePrinterNotifyInfo(pChange->ChangeInfo.pPrinterNotifyInfo);
  753. }
  754. DBGMSG(DBG_NOTIFY, ("FreeChange: 0x%x ->pPrintHandle 0x%x\n",
  755. pChange, pChange->ChangeInfo.pPrintHandle));
  756. FreeSplMem(pChange);
  757. return TRUE;
  758. }
  759. VOID
  760. FreePrinterHandle(
  761. PPRINTHANDLE pPrintHandle
  762. )
  763. {
  764. if( !pPrintHandle ){
  765. return;
  766. }
  767. EnterRouterSem();
  768. //
  769. // If on wait list, force wait list to free it.
  770. //
  771. if (pPrintHandle->pChange) {
  772. FreeChange(pPrintHandle->pChange);
  773. }
  774. //
  775. // Inform all notifys on this printer handle that they are gone.
  776. //
  777. FreePrinterHandleNotifys(pPrintHandle);
  778. LeaveRouterSem();
  779. DBGMSG(DBG_NOTIFY, ("FreePrinterHandle: 0x%x, pChange = 0x%x, pNotify = 0x%x\n",
  780. pPrintHandle, pPrintHandle->pNotify,
  781. pPrintHandle->pChange));
  782. // Log warning to detect handle free
  783. DBGMSG(DBG_TRACE, ("FreePrinterHandle: 0x%x\n", pPrintHandle));
  784. // Close tempfile handle, if any
  785. if (pPrintHandle->hFileSpooler != INVALID_HANDLE_VALUE) {
  786. CloseHandle(pPrintHandle->hFileSpooler);
  787. }
  788. if (pPrintHandle->szTempSpoolFile) {
  789. if (!DeleteFile(pPrintHandle->szTempSpoolFile)) {
  790. MoveFileEx(pPrintHandle->szTempSpoolFile, NULL,
  791. MOVEFILE_DELAY_UNTIL_REBOOT);
  792. }
  793. FreeSplMem(pPrintHandle->szTempSpoolFile);
  794. pPrintHandle->szTempSpoolFile = NULL;
  795. }
  796. FreeSplStr( pPrintHandle->pszPrinter );
  797. FreeSplMem( pPrintHandle );
  798. }
  799. VOID
  800. HandlePollNotifications(
  801. VOID)
  802. /*++
  803. Routine Description:
  804. This handles the pulsing of notifications for any providor that wants
  805. to do polling. It never returns.
  806. Arguments:
  807. VOID
  808. Return Value:
  809. VOID (also never returns)
  810. NOTE: This thread should never exit, since hpmon uses this thread
  811. for initialization. If this thread exists, certain services
  812. this thread initializes quit.
  813. --*/
  814. {
  815. HANDLE hWaitObjects[1];
  816. PCHANGEINFO pChangeInfo;
  817. DWORD dwSleepTime = INFINITE, dwTimeElapsed, dwPreSleepTicks,
  818. dwEvent;
  819. MSG msg;
  820. hWaitObjects[0] = hEventPoll;
  821. while (TRUE) {
  822. dwPreSleepTicks = GetTickCount();
  823. dwEvent = MsgWaitForMultipleObjects(1,
  824. hWaitObjects,
  825. FALSE,
  826. dwSleepTime,
  827. QS_ALLEVENTS | QS_SENDMESSAGE);
  828. if ( dwEvent == WAIT_TIMEOUT ) {
  829. dwTimeElapsed = dwSleepTime;
  830. } else if ( dwEvent == WAIT_OBJECT_0 + 0 ) {
  831. dwTimeElapsed = GetTickCount() - dwPreSleepTicks;
  832. } else {
  833. while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
  834. TranslateMessage(&msg);
  835. DispatchMessage(&msg);
  836. }
  837. continue;
  838. }
  839. EnterRouterSem();
  840. //
  841. // Initialize sleep time to INFINITY.
  842. //
  843. dwSleepTime = INFINITE;
  844. for (pChangeInfo = pChangeInfoHead;
  845. pChangeInfo;
  846. pChangeInfo = (PCHANGEINFO)pChangeInfo->Link.pNext) {
  847. //
  848. // If first time or a notification came in,
  849. // we just want to reset the time.
  850. //
  851. if (pChangeInfo->bResetPollTime) {
  852. pChangeInfo->dwPollTimeLeft = pChangeInfo->dwPollTime;
  853. pChangeInfo->bResetPollTime = FALSE;
  854. } else if (pChangeInfo->dwPollTimeLeft <= dwTimeElapsed) {
  855. //
  856. // Cause a notification.
  857. //
  858. ReplyPrinterChangeNotificationWorker(
  859. pChangeInfo->pPrintHandle,
  860. 0,
  861. pChangeInfo->fdwFilterFlags,
  862. NULL,
  863. NULL);
  864. pChangeInfo->dwPollTimeLeft = pChangeInfo->dwPollTime;
  865. } else {
  866. //
  867. // They've slept dwTimeElapsed, so take that off of
  868. // their dwPollTimeLeft.
  869. //
  870. pChangeInfo->dwPollTimeLeft -= dwTimeElapsed;
  871. }
  872. //
  873. // Now compute what is the least amout of time we wish
  874. // to sleep before the next guy should be woken up.
  875. //
  876. if (dwSleepTime > pChangeInfo->dwPollTimeLeft)
  877. dwSleepTime = pChangeInfo->dwPollTimeLeft;
  878. }
  879. LeaveRouterSem();
  880. }
  881. }
  882. BOOL
  883. SetupChange(
  884. PPRINTHANDLE pPrintHandle,
  885. DWORD dwPrinterRemote,
  886. LPWSTR pszLocalMachine,
  887. PDWORD pfdwStatus,
  888. DWORD fdwOptions,
  889. DWORD fdwFilterFlags,
  890. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  891. PPRINTER_NOTIFY_INIT* ppPrinterNotifyInit)
  892. /*++
  893. Routine Description:
  894. Sets up the pChange structure. Validates the handle,
  895. then attempts to alloc it.
  896. Arguments:
  897. Return Value:
  898. --*/
  899. {
  900. PCHANGE pChange;
  901. BOOL bReturn;
  902. RouterInSem();
  903. if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
  904. SetLastError(ERROR_INVALID_HANDLE);
  905. return FALSE;
  906. }
  907. if (pPrintHandle->pChange) {
  908. DBGMSG(DBG_ERROR, ("FFPCN: Already watching printer handle.\n"));
  909. //
  910. // Error: already watched
  911. //
  912. SetLastError(ERROR_ALREADY_WAITING);
  913. return FALSE;
  914. }
  915. pChange = AllocSplMem(sizeof(CHANGE));
  916. DBGMSG(DBG_NOTIFY, ("FFPCN pChange allocated 0x%x\n", pChange));
  917. if (!pChange) {
  918. //
  919. // Failed to allocate memory, quit.
  920. //
  921. return FALSE;
  922. }
  923. pPrintHandle->pChange = pChange;
  924. pChange->signature = CHANGEHANDLE_SIGNATURE;
  925. pChange->eStatus = STATUS_CHANGE_FORMING;
  926. pChange->ChangeInfo.pPrintHandle = pPrintHandle;
  927. pChange->ChangeInfo.fdwOptions = fdwOptions;
  928. //
  929. // Don't watch for Failed Connections.
  930. //
  931. pChange->ChangeInfo.fdwFilterFlags =
  932. fdwFilterFlags & ~PRINTER_CHANGE_FAILED_CONNECTION_PRINTER;
  933. pChange->dwPrinterRemote = dwPrinterRemote;
  934. pChange->pszLocalMachine = pszLocalMachine;
  935. //
  936. // As soon as we leave the critical section, pPrintHandle
  937. // may vanish! If this is the case, out pChange->eStatus STATUS_CHANGE_CLOSING
  938. // bit will be set.
  939. //
  940. LeaveRouterSem();
  941. //
  942. // Once we leave the critical section, we are may try and
  943. // alter the notification. To guard against this, we always
  944. // check eValid.
  945. //
  946. bReturn = (*pPrintHandle->pProvidor->PrintProvidor.
  947. fpFindFirstPrinterChangeNotification) (pPrintHandle->hPrinter,
  948. fdwFilterFlags,
  949. fdwOptions,
  950. (HANDLE)pPrintHandle,
  951. pfdwStatus,
  952. pPrinterNotifyOptions,
  953. ppPrinterNotifyInit);
  954. EnterRouterSem();
  955. //
  956. // On fail exit.
  957. //
  958. if (!bReturn) {
  959. pPrintHandle->pChange = NULL;
  960. if (pChange) {
  961. //
  962. // We no longer need to be saved, so change
  963. // eStatus to be 0.
  964. //
  965. pChange->eStatus = 0;
  966. DBGMSG(DBG_NOTIFY, ("FFPCN: Error %d, pChange deleting 0x%x %d\n",
  967. GetLastError(),
  968. pChange));
  969. FreeChange(pChange);
  970. }
  971. return FALSE;
  972. }
  973. return TRUE;
  974. }
  975. VOID
  976. FailChange(
  977. PPRINTHANDLE pPrintHandle)
  978. {
  979. LeaveRouterSem();
  980. //
  981. // Shut it down since we failed.
  982. //
  983. (*pPrintHandle->pProvidor->PrintProvidor.
  984. fpFindClosePrinterChangeNotification) (pPrintHandle->hPrinter);
  985. EnterRouterSem();
  986. //
  987. // We no longer need to be saved, so change
  988. // eStatus to be 0.
  989. //
  990. pPrintHandle->pChange->eStatus = 0;
  991. DBGMSG(DBG_NOTIFY, ("FFPCN: Error %d, pChange deleting 0x%x %d\n",
  992. GetLastError(),
  993. pPrintHandle->pChange));
  994. FreeChange(pPrintHandle->pChange);
  995. }
  996. /*------------------------------------------------------------------------
  997. Entry points for providors.
  998. ------------------------------------------------------------------------*/
  999. BOOL
  1000. ProvidorFindFirstPrinterChangeNotification(
  1001. HANDLE hPrinter,
  1002. DWORD fdwFilterFlags,
  1003. DWORD fdwOptions,
  1004. HANDLE hChange,
  1005. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  1006. PPRINTER_NOTIFY_INIT* ppPrinterNotifyInit)
  1007. /*++
  1008. Routine Description:
  1009. Handles any FFPCN that originates from a providor.
  1010. Localspl does this when it wants to put a notification on a port.
  1011. Arguments:
  1012. Return Value:
  1013. --*/
  1014. {
  1015. LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
  1016. BOOL bReturnValue;
  1017. DWORD fdwStatus = 0;
  1018. bReturnValue = (*pPrintHandle->pProvidor->PrintProvidor.
  1019. fpFindFirstPrinterChangeNotification) (pPrintHandle->hPrinter,
  1020. fdwFilterFlags,
  1021. fdwOptions,
  1022. hChange,
  1023. &fdwStatus,
  1024. pPrinterNotifyOptions,
  1025. ppPrinterNotifyInit);
  1026. if (bReturnValue) {
  1027. //
  1028. // !! LATER !! Check return value of SetupReply...
  1029. //
  1030. EnterRouterSem();
  1031. SetupReplyNotification(((PPRINTHANDLE)hChange)->pChange,
  1032. fdwStatus,
  1033. fdwOptions,
  1034. pPrinterNotifyOptions,
  1035. ppPrinterNotifyInit ?
  1036. *ppPrinterNotifyInit :
  1037. NULL);
  1038. LeaveRouterSem();
  1039. }
  1040. return bReturnValue;
  1041. }
  1042. DWORD
  1043. CallRouterFindFirstPrinterChangeNotification(
  1044. HANDLE hPrinterRPC,
  1045. DWORD fdwFilterFlags,
  1046. DWORD fdwOptions,
  1047. HANDLE hPrinterLocal,
  1048. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions)
  1049. /*++
  1050. Routine Description:
  1051. This is called by providers if they want to pass a notification
  1052. along to another machine. This notification must originate from
  1053. this machine but needs to be passed to another spooler.
  1054. Arguments:
  1055. hPrinter - context handle to use for communication
  1056. fdwFilterFlags - watch items
  1057. fdwOptions - watch options
  1058. hPrinterLocal - pPrinter structure valid in this address space,
  1059. and also is the sink for the notifications.
  1060. Return Value:
  1061. Error code
  1062. --*/
  1063. {
  1064. DWORD ReturnValue = 0;
  1065. LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinterLocal;
  1066. EnterRouterSem();
  1067. BeginReplyClient(pPrintHandle,
  1068. REPLY_TYPE_NOTIFICATION);
  1069. LeaveRouterSem();
  1070. RpcTryExcept {
  1071. ReturnValue = RpcRemoteFindFirstPrinterChangeNotificationEx(
  1072. hPrinterRPC,
  1073. fdwFilterFlags,
  1074. fdwOptions,
  1075. pPrintHandle->pChange->pszLocalMachine,
  1076. pPrintHandle->dwUniqueSessionID,
  1077. (PRPC_V2_NOTIFY_OPTIONS)pPrinterNotifyOptions);
  1078. } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
  1079. ReturnValue = RpcExceptionCode();
  1080. } RpcEndExcept
  1081. //
  1082. // Talking to downlevel (Daytona) box.
  1083. //
  1084. if (ReturnValue == RPC_S_PROCNUM_OUT_OF_RANGE) {
  1085. if (!pPrinterNotifyOptions) {
  1086. RpcTryExcept {
  1087. ReturnValue = RpcRemoteFindFirstPrinterChangeNotification(
  1088. hPrinterRPC,
  1089. fdwFilterFlags,
  1090. fdwOptions,
  1091. pPrintHandle->pChange->pszLocalMachine,
  1092. pPrintHandle->dwUniqueSessionID,
  1093. 0,
  1094. NULL);
  1095. } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
  1096. ReturnValue = RpcExceptionCode();
  1097. } RpcEndExcept
  1098. }
  1099. }
  1100. EnterRouterSem();
  1101. EndReplyClient(pPrintHandle,
  1102. REPLY_TYPE_NOTIFICATION);
  1103. LeaveRouterSem();
  1104. return ReturnValue;
  1105. }
  1106. BOOL
  1107. ProvidorFindClosePrinterChangeNotification(
  1108. HANDLE hPrinter)
  1109. /*++
  1110. Routine Description:
  1111. Handles any FCPCN that originates from a providor.
  1112. Localspl does this when it wants to put a notification on a port.
  1113. Arguments:
  1114. Return Value:
  1115. NOTE: Assumes a client notification was setup already.
  1116. --*/
  1117. {
  1118. LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
  1119. return (*pPrintHandle->pProvidor->PrintProvidor.
  1120. fpFindClosePrinterChangeNotification) (pPrintHandle->hPrinter);
  1121. }
  1122. /*------------------------------------------------------------------------
  1123. Entry points for spooler components.
  1124. ------------------------------------------------------------------------*/
  1125. BOOL
  1126. SpoolerFindFirstPrinterChangeNotification(
  1127. HANDLE hPrinter,
  1128. DWORD fdwFilterFlags,
  1129. DWORD fdwOptions,
  1130. PHANDLE phEvent,
  1131. PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
  1132. PVOID pvReserved)
  1133. {
  1134. return FindFirstPrinterChangeNotificationWorker(
  1135. hPrinter,
  1136. fdwFilterFlags,
  1137. fdwOptions,
  1138. 0,
  1139. pPrinterNotifyOptions,
  1140. phEvent,
  1141. TRUE);
  1142. }
  1143. BOOL
  1144. SpoolerFindNextPrinterChangeNotification(
  1145. HANDLE hPrinter,
  1146. LPDWORD pfdwChange,
  1147. LPVOID pPrinterNotifyOptions,
  1148. LPVOID *ppPrinterNotifyInfo)
  1149. /*++
  1150. Routine Description:
  1151. Jump routine for FindNext for internal spooler components to use.
  1152. Arguments:
  1153. Return Value:
  1154. --*/
  1155. {
  1156. BOOL bReturn;
  1157. DWORD fdwFlags = 0;
  1158. if (ppPrinterNotifyInfo) {
  1159. fdwFlags = PRINTER_NOTIFY_NEXT_INFO;
  1160. }
  1161. bReturn = RouterFindNextPrinterChangeNotification(
  1162. hPrinter,
  1163. fdwFlags,
  1164. pfdwChange,
  1165. pPrinterNotifyOptions,
  1166. (PPRINTER_NOTIFY_INFO*)ppPrinterNotifyInfo);
  1167. return bReturn;
  1168. }
  1169. VOID
  1170. SpoolerFreePrinterNotifyInfo(
  1171. PPRINTER_NOTIFY_INFO pInfo)
  1172. {
  1173. RouterFreePrinterNotifyInfo(pInfo);
  1174. }
  1175. BOOL
  1176. SpoolerFindClosePrinterChangeNotification(
  1177. HANDLE hPrinter)
  1178. /*++
  1179. Routine Description:
  1180. Jump routine for FindClose for internal spooler components to use.
  1181. Arguments:
  1182. Return Value:
  1183. --*/
  1184. {
  1185. return FindClosePrinterChangeNotification(hPrinter);
  1186. }
  1187. #if DBG
  1188. VOID
  1189. EnterRouterSem()
  1190. {
  1191. EnterCriticalSection(&RouterNotifySection);
  1192. }
  1193. VOID
  1194. LeaveRouterSem(
  1195. VOID)
  1196. {
  1197. if ((ULONG_PTR)RouterNotifySection.OwningThread != GetCurrentThreadId()) {
  1198. DBGMSG(DBG_ERROR, ("Not in router semaphore\n"));
  1199. }
  1200. LeaveCriticalSection(&RouterNotifySection);
  1201. }
  1202. VOID
  1203. RouterInSem(
  1204. VOID
  1205. )
  1206. {
  1207. if ((ULONG_PTR)RouterNotifySection.OwningThread != GetCurrentThreadId()) {
  1208. DBGMSG(DBG_ERROR, ("Not in spooler semaphore\n"));
  1209. }
  1210. }
  1211. VOID
  1212. RouterOutSem(
  1213. VOID
  1214. )
  1215. {
  1216. if ((ULONG_PTR)RouterNotifySection.OwningThread == GetCurrentThreadId()) {
  1217. DBGMSG(DBG_ERROR, ("Inside spooler semaphore !!\n"));
  1218. SPLASSERT((ULONG_PTR)RouterNotifySection.OwningThread != GetCurrentThreadId());
  1219. }
  1220. }
  1221. #endif /* DBG */