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.

1864 lines
40 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1994 - 1998
  3. All rights reserved.
  4. Module Name:
  5. printer.cxx
  6. Abstract:
  7. Handles object updates and notifications from the printing system.
  8. Author:
  9. Albert Ting (AlbertT) 7-Nov-1994
  10. Revision History:
  11. --*/
  12. #include "precomp.hxx"
  13. #pragma hdrstop
  14. #include "notify.hxx"
  15. #include "data.hxx"
  16. #include "printer.hxx"
  17. #if DBG
  18. //#define DBG_PRNINFO DBG_INFO
  19. #define DBG_PRNINFO DBG_NONE
  20. #endif
  21. /********************************************************************
  22. TPrinter functions.
  23. ********************************************************************/
  24. STATUS
  25. TPrinter::
  26. sOpenPrinter(
  27. IN LPCTSTR pszPrinter,
  28. IN OUT PDWORD pdwAccess,
  29. OUT PHANDLE phPrinter
  30. )
  31. /*++
  32. Routine Description:
  33. Opens printer for specified access.
  34. Arguments:
  35. pszPrinter - Name of printer to open. szNULL or NULL implies local server.
  36. pdwAccess - On entry, holds desired access (pointer to 0 indicates
  37. maximal access). On successful exit, holds access granted.
  38. If the call fails, this value is undefined.
  39. phPrinter - Returns the open printer handle. On failure, this value
  40. is set to NULL.
  41. Return Value:
  42. STATUS - win32 error code or ERROR_SUCCESS if successful.
  43. --*/
  44. {
  45. STATUS Status = ERROR_SUCCESS;
  46. TStatusB bOpen( DBG_WARN,
  47. ERROR_ACCESS_DENIED,
  48. RPC_S_SERVER_UNAVAILABLE,
  49. ERROR_INVALID_PRINTER_NAME );
  50. bOpen DBGNOCHK = FALSE;
  51. static const DWORD adwAccessPrinter[] = {
  52. PRINTER_ALL_ACCESS,
  53. PRINTER_READ,
  54. READ_CONTROL,
  55. 0,
  56. };
  57. static const DWORD adwAccessServer[] = {
  58. SERVER_ALL_ACCESS,
  59. SERVER_READ,
  60. 0,
  61. };
  62. PRINTER_DEFAULTS Defaults;
  63. Defaults.pDatatype = NULL;
  64. Defaults.pDevMode = NULL;
  65. if( pszPrinter && !pszPrinter[0] ){
  66. //
  67. // szNull indicates local server also; change it to
  68. // NULL since OpenPrinter only likes NULL.
  69. //
  70. pszPrinter = NULL;
  71. }
  72. //
  73. // Now determine whether we are opening a server or a printer.
  74. // This is very messy. Look for NULL or two beginning
  75. // backslashes and none thereafter to indicate a server.
  76. //
  77. PDWORD pdwAccessTypes;
  78. if( !pszPrinter ||
  79. ( pszPrinter[0] == TEXT( '\\' ) &&
  80. pszPrinter[1] == TEXT( '\\' ) &&
  81. !_tcschr( &pszPrinter[2], TEXT( '\\' )))){
  82. pdwAccessTypes = (PDWORD)adwAccessServer;
  83. } else {
  84. pdwAccessTypes = (PDWORD)adwAccessPrinter;
  85. }
  86. if( *pdwAccess ){
  87. Defaults.DesiredAccess = *pdwAccess;
  88. bOpen DBGCHK = OpenPrinter( (LPTSTR)pszPrinter,
  89. phPrinter,
  90. &Defaults );
  91. if( !bOpen ){
  92. Status = GetLastError();
  93. }
  94. } else {
  95. //
  96. // If no access is specified, then attempt to retrieve the
  97. // maximal access.
  98. //
  99. UINT i;
  100. for( i = 0; !bOpen && pdwAccessTypes[i]; ++i ){
  101. Defaults.DesiredAccess = pdwAccessTypes[i];
  102. bOpen DBGCHK = OpenPrinter( (LPTSTR)pszPrinter,
  103. phPrinter,
  104. &Defaults );
  105. if( bOpen ){
  106. //
  107. // Return the access requested by the successful OpenPrinter.
  108. // On failure, this value is 0 (*pdwAccess undefined).
  109. //
  110. *pdwAccess = pdwAccessTypes[i];
  111. break;
  112. }
  113. Status = GetLastError();
  114. if( ERROR_ACCESS_DENIED != Status )
  115. break;
  116. }
  117. }
  118. if( !bOpen ){
  119. SPLASSERT( Status );
  120. *phPrinter = NULL;
  121. return Status;
  122. }
  123. SPLASSERT( *phPrinter );
  124. return ERROR_SUCCESS;
  125. }
  126. BOOL
  127. TPrinter::
  128. bSyncRefresh(
  129. VOID
  130. )
  131. {
  132. STATEVAR StateVar = TPrinter::kExecRefreshAll;
  133. BOOL bTriedOpen = FALSE;
  134. while( StateVar ){
  135. DBGMSG( DBG_TRACE, ( "Printer.bSyncRefresh now >> %x %x\n", this, StateVar ));
  136. if( StateVar & ( kExecDelay | kExecError )){
  137. if( bTriedOpen ){
  138. svClose( kExecClose );
  139. return FALSE;
  140. }
  141. StateVar &= ~( kExecDelay | kExecError );
  142. }
  143. if( StateVar & kExecReopen ){
  144. StateVar = svReopen( StateVar );
  145. //
  146. // Only try reopening the printer once.
  147. //
  148. bTriedOpen = TRUE;
  149. } else if( StateVar & kExecRequestExit ){
  150. StateVar = svRequestExit( StateVar );
  151. } else if( StateVar & kExecNotifyStart ){
  152. StateVar = svNotifyStart( StateVar );
  153. } else if( StateVar & kExecRefreshAll ){
  154. StateVar = svRefresh( StateVar );
  155. } else {
  156. DBGMSG( DBG_ERROR,
  157. ( "Printer.bSyncRefresh: Unknown command %x %x\n",
  158. this, StateVar ));
  159. return FALSE;
  160. }
  161. DBGMSG( DBG_EXEC, ( "Printer.bSyncRefresh: %x return state %x\n", this, StateVar ));
  162. }
  163. return TRUE;
  164. }
  165. /********************************************************************
  166. Creation and deletion for clients
  167. ********************************************************************/
  168. TPrinter*
  169. TPrinter::
  170. pNew(
  171. IN MPrinterClient* pPrinterClient,
  172. IN LPCTSTR pszPrinter,
  173. IN DWORD dwAccess
  174. )
  175. /*++
  176. Routine Description:
  177. Create the printer object. Clients should call this routine
  178. instead of the ctr for printers.
  179. Called from UI thread only.
  180. Arguments:
  181. pPrinterClient - UI Client that stores info, generally the queue.
  182. pszPrinter - Printer to be opened.
  183. dwAccess - Access level requested.
  184. Return Value:
  185. TPrinter*, NULL = failure (call GetLastError()).
  186. --*/
  187. {
  188. SPLASSERT( pszPrinter );
  189. TPrinter* pPrinter = new TPrinter( pPrinterClient,
  190. pszPrinter,
  191. dwAccess );
  192. if( !VALID_PTR( pPrinter )){
  193. delete pPrinter;
  194. return NULL;
  195. }
  196. DBGMSG( DBG_PRNINFO,
  197. ( "Printer.pNew: returned %x for "TSTR" %d\n",
  198. pPrinter, DBGSTR( pszPrinter ), dwAccess ));
  199. return pPrinter;
  200. }
  201. VOID
  202. TPrinter::
  203. vDelete(
  204. VOID
  205. )
  206. /*++
  207. Routine Description:
  208. Mark the printer object for deletion.
  209. Called from UI thread only.
  210. Arguments:
  211. Return Value:
  212. Notes:
  213. When the printer object is marked for deletion, the object
  214. will delete itself when there are no more commands left
  215. to be processed.
  216. Called from UI thread only.
  217. --*/
  218. {
  219. {
  220. CCSLock::Locker CSL( *gpCritSec );
  221. //
  222. // Disassociate the printer from the list view queue. Must
  223. // be called inside critical section, since worker threads might
  224. // be trying to change state.
  225. //
  226. PrinterGuard._pPrinterClient = NULL;
  227. }
  228. //
  229. // Mark ourselves for deletion by adding the REQUESTEXIT job. This
  230. // will allow the currently queued jobs to execute.
  231. //
  232. // Also note that bJobAdd is guarenteed to return TRUE for this
  233. // EXIT request. vExecExitComplete may be called now, or when
  234. // the current job has completed.
  235. //
  236. _pPrintLib->bJobAdd( this, kExecRequestExit );
  237. }
  238. /********************************************************************
  239. Internal private functions for creation and destruction. Outside
  240. clients should use pNew and vDelete.
  241. ********************************************************************/
  242. TPrinter::
  243. TPrinter(
  244. IN MPrinterClient* pPrinterClient,
  245. IN LPCTSTR pszPrinter,
  246. IN DWORD dwAccess
  247. ) : _pData( NULL ),
  248. _dwAccess( 0 ),
  249. _eJobStatusStringType( kMultipleJobStatusString )
  250. /*++
  251. Routine Description:
  252. Create the printer object.
  253. Called from UI thread only.
  254. Arguments:
  255. pPrinterClient - MPrinterClient that wants the data.
  256. pszPrinter - Name of printer to open.
  257. dwAccess - Access required (0 == highest access level).
  258. Return Value:
  259. --*/
  260. {
  261. ASSERT(pPrinterClient);
  262. UNREFERENCED_PARAMETER( dwAccess );
  263. TStatusB bStatus;
  264. bStatus DBGCHK = pPrinterClient->bGetPrintLib(_pPrintLib);
  265. if( bStatus ){
  266. //
  267. // Initialize ExecGuard.
  268. //
  269. ExecGuard._hPrinter = NULL;
  270. //
  271. // Initialize PrinterGuard.
  272. //
  273. PrinterGuard._hEventCommand = NULL;
  274. PrinterGuard._pPrinterClient = pPrinterClient;
  275. if( pszPrinter ){
  276. PrinterGuard._strPrinter.bUpdate( pszPrinter );
  277. }
  278. }
  279. //
  280. // _strPrinter is our bValid check.
  281. //
  282. }
  283. TPrinter::
  284. ~TPrinter(
  285. VOID
  286. )
  287. {
  288. //
  289. // There shouldn't be any pending jobs in the command linked
  290. // list.
  291. //
  292. SPLASSERT( PrinterGuard.Selection_bEmpty( ));
  293. SPLASSERT( !ExecGuard._hPrinter );
  294. SPLASSERT( !PrinterGuard._hEventCommand );
  295. //
  296. // The pData may not be valid. For example, if we close a printer
  297. // window right before the svNotifyStart, this pointer may be NULL.
  298. //
  299. if( _pData ){
  300. _pData->vDelete();
  301. }
  302. }
  303. VOID
  304. TPrinter::
  305. vExecExitComplete(
  306. VOID
  307. )
  308. /*++
  309. Routine Description:
  310. The exit request: cleanup ourselves, then delete everything.
  311. We may not be in the UI thread, but UIGuard is safe since we
  312. have already deleted the UI portion of this object.
  313. Arguments:
  314. Return Value:
  315. --*/
  316. {
  317. delete this;
  318. }
  319. /********************************************************************
  320. Command handling
  321. Add to the Command linked list any pending requests.
  322. ********************************************************************/
  323. VOID
  324. TPrinter::
  325. vCommandQueue(
  326. IN TSelection* pSelection ADOPT
  327. )
  328. /*++
  329. Routine Description:
  330. Queue a command for execution.
  331. Arguments:
  332. CommandType - Type of command, either PRINTER or JOB
  333. dwCommand - CommandType specific job DWORD
  334. Id - CommandType specific ID
  335. Return Value:
  336. --*/
  337. {
  338. SINGLETHREAD(UIThread);
  339. SPLASSERT( pSelection );
  340. {
  341. //
  342. // Append to the list of work items.
  343. //
  344. CCSLock::Locker CSL( *gpCritSec );
  345. PrinterGuard.Selection_vAppend( pSelection );
  346. }
  347. if( _pPrintLib->bJobAdd( this, kExecCommand )){
  348. //
  349. // Command successfully queued, request a wakeup.
  350. //
  351. vCommandRequested();
  352. //
  353. // pSelection successfully adopted by PrinterGuard.Selection
  354. // linked list. NULL it here so we don't free it.
  355. //
  356. pSelection = NULL;
  357. } else {
  358. DBGMSG( DBG_WARN,
  359. ( "Printer.vQueueCommand: Exec->bJobAdd failed %d\n",
  360. GetLastError( )));
  361. vErrorStatusChanged( 1 );
  362. //
  363. // Delink the item if it is linked.
  364. //
  365. {
  366. CCSLock::Locker CSL( *gpCritSec );
  367. if( pSelection->Selection_bLinked( )){
  368. pSelection->Selection_vDelinkSelf();
  369. }
  370. }
  371. }
  372. //
  373. // Delete pSelection if not adopted by PrinterGuard.Selection.
  374. //
  375. delete pSelection;
  376. }
  377. VOID
  378. TPrinter::
  379. vCommandRequested(
  380. VOID
  381. )
  382. /*++
  383. Routine Description:
  384. A command was requested, so trigger PrinterGuard._hEventCommand.
  385. If the worker thread is polling, it will be asleep now. By triggering
  386. this event, we can force an immediate retry.
  387. Arguments:
  388. Return Value:
  389. --*/
  390. {
  391. CCSLock::Locker CSL( *gpCritSec );
  392. if( PrinterGuard._hEventCommand ){
  393. SetEvent( PrinterGuard._hEventCommand );
  394. }
  395. }
  396. /********************************************************************
  397. Virtual functions for TExecWork.
  398. Executed from worker threads; must be multithread safe.
  399. ********************************************************************/
  400. VOID
  401. TPrinter::
  402. vExecFailedAddJob(
  403. VOID
  404. )
  405. /*++
  406. Routine Description:
  407. Occurs when we can't add another job to TExec.
  408. Arguments:
  409. Return Value:
  410. --*/
  411. {
  412. //
  413. // !! LATER !!
  414. //
  415. // Tune this error. 1 is not a good error...
  416. //
  417. vErrorStatusChanged( 1 );
  418. }
  419. STATEVAR
  420. TPrinter::
  421. svExecute(
  422. IN STATEVAR StateVar
  423. )
  424. /*++
  425. Routine Description:
  426. Very simple state machine. We examine the bits of the DWORD and
  427. execute the appropriate action. The more important bits are
  428. placed first so that they have priority.
  429. Not called from UI thread.
  430. Arguments:
  431. StateVar - Current state of the Printer.
  432. Return Value:
  433. Ending state of the printer.
  434. --*/
  435. {
  436. SINGLETHREADNOT(UIThread);
  437. DBGMSG( DBG_EXEC, ( "Printer.svExecute: %x Sequence begin.\n", this ));
  438. if( !StateVar ){
  439. return 0;
  440. }
  441. BOOL bTriedOpen = FALSE;
  442. while( StateVar ){
  443. DBGMSG( DBG_EXEC, ( "Printer.svExecute now >> %x %x\n", this, StateVar ));
  444. if( StateVar & kExecExit ){
  445. //
  446. // Quit case, return kExecExit to allow vExecExitComplete() to
  447. // run, which cleans up everything.
  448. //
  449. return kExecExit;
  450. } else if( StateVar & kExecError ){
  451. svClose( kExecClose );
  452. //
  453. // Don't do anymore work until the user hits refresh.
  454. //
  455. return 0;
  456. #ifdef SLEEP_ON_MINIMIZE
  457. } else if( StateVar & kExecAwake ){
  458. StateVar = svAwake( StateVar );
  459. } else if( StateVar & kExecSleep && !( StateVar & kExecCommand )){
  460. //
  461. // Only go to sleep if we have no commands pending.
  462. //
  463. StateVar = svSleep( StateVar );
  464. #endif
  465. } else if( StateVar & kExecDelay ){
  466. if( bTriedOpen ){
  467. StateVar = svDelay( StateVar );
  468. } else {
  469. StateVar &= ~kExecDelay;
  470. }
  471. } else if( StateVar & kExecReopen ){
  472. StateVar = svReopen( StateVar );
  473. //
  474. // Only try reopening the printer once.
  475. //
  476. bTriedOpen = TRUE;
  477. } else if( StateVar & kExecCommand ){
  478. StateVar = svCommand( StateVar );
  479. } else if( StateVar & kExecRequestExit ){
  480. StateVar = svRequestExit( StateVar );
  481. } else if( StateVar & kExecNotifyStart ){
  482. StateVar = svNotifyStart( StateVar );
  483. } else if( StateVar & kExecRefreshAll ){
  484. StateVar = svRefresh( StateVar );
  485. } else {
  486. DBGMSG( DBG_ERROR,
  487. ( "Printer.svExecute: Unknown command %x %x\n",
  488. this, StateVar ));
  489. }
  490. DBGMSG( DBG_EXEC, ( "Printer.svExecute %x return state %x\n", this, StateVar ));
  491. //
  492. // Get any pending work items so that we can process them
  493. // now. This is necessary because we may have multiple jobs
  494. // that must execute before we close ourselves.
  495. //
  496. // Plus, if kExecExit was set while we were busy, then we
  497. // want to pick it up so we quit soon.
  498. //
  499. StateVar |= _pPrintLib->svClearPendingWork( this );
  500. DBGMSG( DBG_EXEC, ( "Printer.svExecute %x updated %x\n", this, StateVar ));
  501. }
  502. //
  503. // Clear the status bar panes.
  504. //
  505. vConnectStatusChanged( kConnectStatusNull );
  506. return 0;
  507. }
  508. STATEVAR
  509. TPrinter::
  510. svReopen(
  511. IN STATEVAR StateVar
  512. )
  513. /*++
  514. Routine Description:
  515. Open or reopen the printer.
  516. Not called from the UI thread, since this may take a while.
  517. Note: _strPrinter does _not_ need to be guarded by PrinterGuard
  518. in this case since only ExecGuard threads write to it. Since
  519. we are in ExecGuard now, there is no need to grab PrinterGuard.
  520. Arguments:
  521. Return Value:
  522. --*/
  523. {
  524. SINGLETHREADNOT(UIThread);
  525. //
  526. // Close if necessary.
  527. //
  528. svClose( kExecClose );
  529. //
  530. // Update status.
  531. //
  532. vConnectStatusChanged( kConnectStatusOpen );
  533. TCHAR szPrinter[kPrinterBufMax];
  534. LPTSTR pszPrinter = pszPrinterName( ARRAYSIZE(szPrinter), szPrinter );
  535. STATUS Status = TPrinter::sOpenPrinter( pszPrinter,
  536. &_dwAccess,
  537. &ExecGuard._hPrinter );
  538. if( Status ){
  539. //
  540. // Ensure hPrinter is NULL.
  541. //
  542. SPLASSERT( !ExecGuard._hPrinter );
  543. DBGMSG( DBG_WARN, ( "Printer.sOpen: failed to open %ws: %d\n",
  544. DBGSTR( (LPCTSTR)PrinterGuard._strPrinter ),
  545. Status ));
  546. //
  547. // If the error is invalid printer name, immediately punt
  548. // and don't retry unless the user requests a refresh.
  549. //
  550. // Do the same for access denied. We'll get here only if
  551. // the spooler hasn't cached the printer. If it has, then
  552. // this will succeed (since it's async), and the FFPCN will
  553. // fail.
  554. //
  555. CONNECT_STATUS ConnectStatus;
  556. switch( Status ){
  557. case ERROR_INVALID_PRINTER_NAME:
  558. ConnectStatus = kConnectStatusInvalidPrinterName;
  559. break;
  560. case ERROR_ACCESS_DENIED:
  561. ConnectStatus = kConnectStatusAccessDenied;
  562. break;
  563. default:
  564. ConnectStatus = kConnectStatusOpenError;
  565. break;
  566. }
  567. vConnectStatusChanged( ConnectStatus );
  568. return kExecError;
  569. //
  570. // !! POLICY !!
  571. //
  572. // Should we sleep, then retry, or just punt?
  573. // If we want to sleep then retry, we should return the
  574. // following state value.
  575. //
  576. // return StateVar | kExecDelay;
  577. //
  578. }
  579. //
  580. // Read the SingleJobStatusString printer value. This
  581. // fix is for FAX support. The fax software does not
  582. // want multiple status strings tacked together with
  583. // '-' when a job error occurrs.
  584. //
  585. DWORD dwStatus;
  586. DWORD dwType = REG_DWORD;
  587. DWORD dwValue = 0;
  588. DWORD cbNeeded = 0;
  589. dwStatus = GetPrinterData( ExecGuard._hPrinter,
  590. (LPTSTR)gszUISingleJobStatus,
  591. &dwType,
  592. (LPBYTE)&dwValue,
  593. sizeof( DWORD ),
  594. &cbNeeded );
  595. //
  596. // Assume multiple job status string, This is the default.
  597. //
  598. _eJobStatusStringType = kMultipleJobStatusString;
  599. //
  600. // If the printer data was fetched and the value read is one
  601. // of the known values then set the new status string type.
  602. //
  603. if( dwStatus == ERROR_SUCCESS &&
  604. cbNeeded == sizeof( DWORD ) &&
  605. ( dwValue == kMultipleJobStatusString ||
  606. dwValue == kSingleJobStatusString ) )
  607. {
  608. _eJobStatusStringType = (EJobStatusString)dwValue;
  609. }
  610. //
  611. // Success, start the notification process.
  612. //
  613. return (StateVar | kExecNotifyStart) & ~(kExecReopen | kExecDelay | kExecError);
  614. }
  615. STATEVAR
  616. TPrinter::
  617. svDelay(
  618. IN STATEVAR StateVar
  619. )
  620. /*++
  621. Routine Description:
  622. An error occurred. Put a simple message in the status bar and sleep
  623. for a while. We create a trigger event in case we want to abort
  624. the sleep and retry immediately.
  625. Not called from UI thread.
  626. Arguments:
  627. Return Value:
  628. StateVar - kExecDelay will be removed.
  629. --*/
  630. {
  631. SINGLETHREADNOT(UIThread);
  632. //
  633. // Create a handle event so that new commands will cause
  634. // us to immediately try and reopen.
  635. //
  636. HANDLE hEvent = CreateEvent( NULL,
  637. FALSE,
  638. FALSE,
  639. NULL );
  640. if( hEvent ){
  641. {
  642. CCSLock::Locker CSL( *gpCritSec );
  643. PrinterGuard._hEventCommand = hEvent;
  644. }
  645. //
  646. // Update status.
  647. //
  648. vConnectStatusChanged( kConnectStatusPoll );
  649. WaitForSingleObject( hEvent, kSleepRetry );
  650. {
  651. CCSLock::Locker CSL( *gpCritSec );
  652. PrinterGuard._hEventCommand = NULL;
  653. }
  654. CloseHandle( hEvent );
  655. } else {
  656. //
  657. // Failed to create event, just sleep for a bit.
  658. //
  659. Sleep( kSleepRetry );
  660. }
  661. //
  662. // !! LATER !!
  663. //
  664. // Use TSleepN to avoid using a thread while we are
  665. // sleeping.
  666. //
  667. return StateVar & ~kExecDelay;
  668. }
  669. STATEVAR
  670. TPrinter::
  671. svClose(
  672. IN STATEVAR StateVar
  673. )
  674. {
  675. SINGLETHREADNOT(UIThread);
  676. svNotifyEnd( kExecNotifyEnd );
  677. if( ExecGuard._hPrinter && !ClosePrinter( ExecGuard._hPrinter )) {
  678. STATUS Status = GetLastError();
  679. DBGMSG( DBG_WARN, ( "Printer.sClose: failed to close %ws: %d\n",
  680. DBGSTR( (LPCTSTR)PrinterGuard._strPrinter ),
  681. Status ));
  682. }
  683. //
  684. // Reset the notifications count.
  685. //
  686. if( _pData ){
  687. INFO Info;
  688. Info.dwData = 0;
  689. vContainerChanged( kContainerClearItems, Info );
  690. _pData->vDelete();
  691. _pData = NULL;
  692. }
  693. ExecGuard._hPrinter = NULL;
  694. return StateVar & ( kExecExit | kExecCommand );
  695. }
  696. STATEVAR
  697. TPrinter::
  698. svCommand(
  699. IN STATEVAR StateVar
  700. )
  701. {
  702. SINGLETHREADNOT(UIThread);
  703. BOOL bSuccess = TRUE;
  704. BOOL bReopen = FALSE;
  705. //
  706. // Update status.
  707. //
  708. vConnectStatusChanged( kConnectStatusCommand );
  709. while( bSuccess ){
  710. TSelection* pSelection;
  711. //
  712. // Get a single request.
  713. //
  714. {
  715. CCSLock::Locker CSL( *gpCritSec );
  716. pSelection = PrinterGuard.Selection_pHead();
  717. if( pSelection ){
  718. pSelection->Selection_vDelinkSelf();
  719. }
  720. }
  721. if( !pSelection ){
  722. //
  723. // Done with work.
  724. //
  725. break;
  726. }
  727. switch( pSelection->_CommandType ){
  728. case TSelection::kCommandTypeJob:
  729. {
  730. //
  731. // Need to do multiple SetJobs.
  732. //
  733. COUNT i;
  734. for( i=0; i < pSelection->_cSelected; ++i ){
  735. bSuccess = SetJob( ExecGuard._hPrinter,
  736. pSelection->_pid[i],
  737. 0,
  738. NULL,
  739. pSelection->_dwCommandAction );
  740. if( !bSuccess ){
  741. break;
  742. }
  743. //
  744. // Check if a refresh is pending and execute it.
  745. //
  746. StateVar |= _pPrintLib->svClearPendingWork( this );
  747. if( StateVar & kExecRefresh ){
  748. //
  749. // We need to check explicity for refreshes, since
  750. // a ton of changes may come in at once, and
  751. // we should keep the UI somewhat current.
  752. //
  753. StateVar = svRefresh( StateVar );
  754. }
  755. }
  756. break;
  757. }
  758. case TSelection::kCommandTypePrinter:
  759. bSuccess = SetPrinter( ExecGuard._hPrinter,
  760. 0,
  761. NULL,
  762. pSelection->_dwCommandAction );
  763. break;
  764. case TSelection::kCommandTypePrinterAttributes:
  765. {
  766. //
  767. // First read the current printer info5 state. Then update
  768. // this structure with the new attribute the set the new
  769. // attribute state. Printer info 5 is a little more efficent
  770. // than using a printer info2.
  771. //
  772. PPRINTER_INFO_5 pInfo5 = NULL;
  773. DWORD cbInfo5 = 0;
  774. bSuccess = VDataRefresh::bGetPrinter( ExecGuard._hPrinter, 5, (PVOID*)&pInfo5, &cbInfo5 );
  775. if( bSuccess )
  776. {
  777. pInfo5->Attributes = pSelection->_dwCommandAction;
  778. bSuccess = SetPrinter( ExecGuard._hPrinter, 5, (PBYTE)pInfo5, 0 );
  779. FreeMem( pInfo5 );
  780. }
  781. }
  782. break;
  783. default:
  784. DBGMSG( DBG_WARN,
  785. ( "Printer.svCommand: unknown command %x %d %d\n",
  786. pSelection, pSelection->_CommandType,
  787. pSelection->_dwCommandAction ));
  788. break;
  789. }
  790. if( !bSuccess ){
  791. DBGMSG( DBG_WARN,
  792. ( "Printer.svCommand: Type %d Command %d to hPrinter %x failed %d\n",
  793. pSelection->_CommandType,
  794. pSelection->_dwCommandAction,
  795. ExecGuard._hPrinter,
  796. GetLastError( )));
  797. }
  798. //
  799. // Free the pSelection.
  800. //
  801. delete pSelection;
  802. }
  803. if( bSuccess ){
  804. //
  805. // Successfully executed commands, clear error string
  806. // in status bar.
  807. //
  808. vErrorStatusChanged( ERROR_SUCCESS );
  809. } else {
  810. //
  811. // Currently we punt on the old command. Should we requeue it?
  812. //
  813. //
  814. // An error occurred; bomb out of all.
  815. //
  816. STATUS dwError = GetLastError();
  817. SPLASSERT( dwError );
  818. vErrorStatusChanged( dwError );
  819. //
  820. // If we encountered an invalid handle, we should reopen
  821. // the printer.
  822. //
  823. if( dwError == ERROR_INVALID_HANDLE ){
  824. StateVar |= kExecReopen;
  825. }
  826. //
  827. // !! POLICY !!
  828. //
  829. // We don't re-execute jobs--delete any pending jobs.
  830. //
  831. {
  832. CCSLock::Locker CSL( *gpCritSec );
  833. TSelection* pSelection;
  834. TIter Iter;
  835. for( PrinterGuard.Selection_vIterInit( Iter ), Iter.vNext();
  836. Iter.bValid(); ){
  837. pSelection = PrinterGuard.Selection_pConvert( Iter );
  838. Iter.vNext();
  839. pSelection->Selection_vDelinkSelf();
  840. delete pSelection;
  841. }
  842. }
  843. }
  844. return StateVar & ~kExecCommand;
  845. }
  846. STATEVAR
  847. TPrinter::
  848. svRequestExit(
  849. IN STATEVAR StateVar
  850. )
  851. {
  852. //
  853. // Close up everything.
  854. //
  855. svClose( StateVar | kExecClose );
  856. //
  857. // kExecExit forces a cleanup.
  858. //
  859. return kExecExit;
  860. }
  861. STATEVAR
  862. TPrinter::
  863. svRefresh(
  864. IN STATEVAR StateVar
  865. )
  866. /*++
  867. Routine Description:
  868. Refresh the printer queue.
  869. Arguments:
  870. Return Value:
  871. --*/
  872. {
  873. SINGLETHREADNOT(UIThread);
  874. //
  875. // If the printer has not been initialized, reopen it now.
  876. //
  877. if( !ExecGuard._hPrinter || !_pData ){
  878. return kExecReopen;
  879. }
  880. //
  881. // Get the printer name
  882. //
  883. TCHAR szPrinter[kPrinterBufMax];
  884. LPTSTR pszPrinter = pszPrinterName( ARRAYSIZE(szPrinter), szPrinter );
  885. //
  886. // Update state.
  887. //
  888. vConnectStatusChanged( kConnectStatusRefresh );
  889. //
  890. // _pData->svRefresh is responsible for calling pPrinter's
  891. // vItemChanged or pPrinter->vAllItemsChanged.
  892. //
  893. StateVar = _pData->svRefresh( StateVar );
  894. return StateVar;
  895. }
  896. STATEVAR
  897. TPrinter::
  898. svNotifyStart(
  899. IN STATEVAR StateVar
  900. )
  901. /*++
  902. Routine Description:
  903. Start the notification process.
  904. Arguments:
  905. Return Value:
  906. --*/
  907. {
  908. SINGLETHREADNOT(UIThread);
  909. DBGMSG( DBG_NOTIFY,
  910. ( "Printer.svNotifyStart: %x %ws\n",
  911. this, DBGSTR( (LPCTSTR)PrinterGuard._strPrinter )));
  912. vConnectStatusChanged( kConnectStatusInitialize );
  913. //
  914. // If we have an extablished _pData (e.g., non-NULL), then just
  915. // pass along the request.
  916. //
  917. // If we don't have one yet, we need to create one.
  918. //
  919. StateVar = _pData ?
  920. _pData->svNotifyStart( StateVar ) :
  921. VData::svNew( this, StateVar, _pData );
  922. if( StateVar & kExecDelay ){
  923. //
  924. // Error occurred--if it's Access Denied, then fail.
  925. // This happens when the spooler caches the printer:
  926. // OpenPrinter succeeds even though the user has no access.
  927. // The first "real" action fails.
  928. //
  929. if( GetLastError() == ERROR_ACCESS_DENIED ){
  930. vConnectStatusChanged( kConnectStatusAccessDenied );
  931. return kExecError;
  932. }
  933. }
  934. //
  935. // If we succeeded, the kExecRegister will be set, so we can
  936. // register it then return.
  937. //
  938. if( StateVar & kExecRegister ){
  939. SPLASSERT( _pData );
  940. TStatus Status;
  941. Status DBGCHK = _pPrintLib->pNotify()->sRegister( _pData );
  942. if( Status != ERROR_SUCCESS ){
  943. //
  944. // Failed to register; delay then reopen printer. We could
  945. // just try and re-register later, but this should be a very
  946. // rare event, so do the least amount of work.
  947. //
  948. DBGMSG( DBG_WARN,
  949. ( "Printer.svNotifyStart: sRegister %x failed %d\n",
  950. this, Status ));
  951. StateVar |= kExecDelay | kExecReopen;
  952. }
  953. //
  954. // No longer need to register. In the future, if we need to
  955. // register, we will set the bit on again.
  956. //
  957. StateVar &= ~kExecRegister;
  958. } else {
  959. DBGMSG( DBG_TRACE,
  960. ( "Printer.svNotifyStart: %x pData->svNotifyStart failed %d %d\n",
  961. this, GetLastError(), StateVar ));
  962. }
  963. return StateVar;
  964. }
  965. STATEVAR
  966. TPrinter::
  967. svNotifyEnd(
  968. IN STATEVAR StateVar
  969. )
  970. /*++
  971. Routine Description:
  972. Shut down the notification process.
  973. Note: _strPrinter does _not_ need to be guarded by PrinterGuard
  974. in this case since only ExecGuard threads write to it. Since
  975. we are in ExecGuard now, there is no need to grab PrinterGuard.
  976. Arguments:
  977. Return Value:
  978. --*/
  979. {
  980. SINGLETHREADNOT(UIThread);
  981. DBGMSG( DBG_NOTIFY,
  982. ( "Printer.svNotifyEnd: %x %ws\n",
  983. this, DBGSTR( (LPCTSTR)PrinterGuard._strPrinter )));
  984. if( _pData ){
  985. return _pData->svNotifyEnd( StateVar );
  986. }
  987. return StateVar & ~kExecNotifyEnd;
  988. }
  989. /********************************************************************
  990. Status updates
  991. ********************************************************************/
  992. VOID
  993. TPrinter::
  994. vErrorStatusChanged(
  995. IN DWORD dwStatus
  996. )
  997. /*++
  998. Routine Description:
  999. Error state of _user_ command failed. (If the user executes a
  1000. command then leaves, they want to know the result of the command.
  1001. If we change this when we execute one of our own commands, the
  1002. previous result is lost.)
  1003. Arguments:
  1004. dwStatus - GetLastError() code.
  1005. Return Value:
  1006. --*/
  1007. {
  1008. INFO Info;
  1009. Info.dwData = dwStatus;
  1010. vContainerChanged( kContainerErrorStatus, Info );
  1011. }
  1012. VOID
  1013. TPrinter::
  1014. vConnectStatusChanged(
  1015. IN CONNECT_STATUS ConnectStatus
  1016. )
  1017. /*++
  1018. Routine Description:
  1019. The connection status of the printer changed (opening, intializing,
  1020. refreshing, etc.).
  1021. Arguments:
  1022. dwStatus - New IDS_* of connection status.
  1023. Return Value:
  1024. --*/
  1025. {
  1026. INFO Info;
  1027. Info.dwData = ConnectStatus;
  1028. vContainerChanged( kContainerConnectStatus, Info );
  1029. }
  1030. /********************************************************************
  1031. TPrinterClientRef
  1032. ********************************************************************/
  1033. TPrinter::
  1034. TPrinterClientRef::
  1035. TPrinterClientRef(
  1036. const TPrinter* pPrinter
  1037. )
  1038. {
  1039. CCSLock::Locker CSL( *gpCritSec );
  1040. _pPrinterClient = pPrinter->PrinterGuard._pPrinterClient;
  1041. if( !_pPrinterClient ){
  1042. return;
  1043. }
  1044. _pPrinterClient->vIncRef();
  1045. }
  1046. TPrinter::
  1047. TPrinterClientRef::
  1048. ~TPrinterClientRef(
  1049. VOID
  1050. )
  1051. {
  1052. if( _pPrinterClient ){
  1053. _pPrinterClient->cDecRef();
  1054. }
  1055. }
  1056. /********************************************************************
  1057. MDataClient definitions.
  1058. ********************************************************************/
  1059. VOID
  1060. TPrinter::
  1061. vContainerChanged(
  1062. CONTAINER_CHANGE ContainerChange,
  1063. INFO Info
  1064. )
  1065. {
  1066. TPrinterClientRef PrinterClientRef( this );
  1067. if( !PrinterClientRef.bValid( )){
  1068. return;
  1069. }
  1070. switch( ContainerChange ){
  1071. case kContainerServerName:
  1072. case kContainerName:
  1073. {
  1074. CCSLock::Locker CSL( *gpCritSec );
  1075. TStatusB bStatus;
  1076. bStatus DBGCHK = ( ContainerChange == kContainerServerName ) ?
  1077. PrinterGuard._strServer.bUpdate( Info.pszData ) :
  1078. PrinterGuard._strPrinter.bUpdate( Info.pszData );
  1079. if( !bStatus ){
  1080. //
  1081. // Failed, execute a refesh with delay.
  1082. //
  1083. ContainerChange = kContainerStateVar;
  1084. Info.dwData = kExecDelay | kExecRefreshAll;
  1085. goto Fail;
  1086. }
  1087. }
  1088. PrinterClientRef.ptr()->vContainerChanged( kContainerName,
  1089. kInfoNull );
  1090. break;
  1091. default:
  1092. Fail:
  1093. PrinterClientRef.ptr()->vContainerChanged( ContainerChange, Info );
  1094. break;
  1095. }
  1096. }
  1097. VOID
  1098. TPrinter::
  1099. vItemChanged(
  1100. ITEM_CHANGE ItemChange,
  1101. HITEM hItem,
  1102. INFO Info,
  1103. INFO InfoNew
  1104. )
  1105. {
  1106. if( PrinterGuard._pPrinterClient ){
  1107. PrinterGuard._pPrinterClient->vItemChanged( ItemChange,
  1108. hItem,
  1109. Info,
  1110. InfoNew );
  1111. }
  1112. }
  1113. VOID
  1114. TPrinter::
  1115. vSaveSelections(
  1116. VOID
  1117. )
  1118. {
  1119. if( PrinterGuard._pPrinterClient ){
  1120. PrinterGuard._pPrinterClient->vSaveSelections();
  1121. }
  1122. }
  1123. VOID
  1124. TPrinter::
  1125. vRestoreSelections(
  1126. VOID
  1127. )
  1128. {
  1129. if( PrinterGuard._pPrinterClient ){
  1130. PrinterGuard._pPrinterClient->vRestoreSelections();
  1131. }
  1132. }
  1133. BOOL
  1134. TPrinter::
  1135. bGetPrintLib(
  1136. TRefLock<TPrintLib> &refLock
  1137. ) const
  1138. {
  1139. ASSERT(pPrintLib().pGet());
  1140. if (pPrintLib().pGet())
  1141. {
  1142. refLock.vAcquire(pPrintLib().pGet());
  1143. return TRUE;
  1144. }
  1145. return FALSE;
  1146. }
  1147. VDataNotify*
  1148. TPrinter::
  1149. pNewNotify(
  1150. MDataClient* pDataClient
  1151. ) const
  1152. {
  1153. TPrinterClientRef PrinterClientRef( this );
  1154. if( PrinterClientRef.bValid( )){
  1155. return PrinterClientRef.ptr()->pNewNotify( pDataClient );
  1156. }
  1157. return NULL;
  1158. }
  1159. VDataRefresh*
  1160. TPrinter::
  1161. pNewRefresh(
  1162. MDataClient* pDataClient
  1163. ) const
  1164. {
  1165. TPrinterClientRef PrinterClientRef( this );
  1166. if( PrinterClientRef.bValid( )){
  1167. return PrinterClientRef.ptr()->pNewRefresh( pDataClient );
  1168. }
  1169. return NULL;
  1170. }
  1171. LPTSTR
  1172. TPrinter::
  1173. pszPrinterName(
  1174. IN size_t cchBuffer,
  1175. OUT LPTSTR pszPrinterBuffer CHANGE
  1176. ) const
  1177. /*++
  1178. Routine Description:
  1179. Retrieves the fully qualified name of the printer (\\server\printer or
  1180. just printer for local printers).
  1181. Arguments:
  1182. cchBuffer - the size of the buffer in characters.
  1183. pszPrinterBuffer - Uninitialized buffer that receives the printer name.
  1184. Must be at least kPrinterBufMax in length.
  1185. Return Value:
  1186. Pointer to name of printer (generally pointer to pszPrinterBuffer).
  1187. --*/
  1188. {
  1189. pszPrinterBuffer[0] = 0;
  1190. CCSLock::Locker CSL( *gpCritSec );
  1191. //
  1192. // If we have a server name in TPrinter, prepend it to the
  1193. // printer name if it's different. We could just always prepend
  1194. // it, since localspl.dll correctly grabs it, but then the
  1195. // title bar displays the fully qualified name.
  1196. //
  1197. if( ((LPCTSTR)PrinterGuard._strServer)[0] &&
  1198. _pPrintLib->strComputerName( ) != PrinterGuard.strServer( )){
  1199. StringCchCopy( pszPrinterBuffer, cchBuffer, PrinterGuard._strServer );
  1200. //
  1201. // We assume that the machine - printer separator
  1202. // is always a backslash.
  1203. //
  1204. StringCchCat( pszPrinterBuffer, cchBuffer, TEXT( "\\" ) );
  1205. }
  1206. StringCchCat( pszPrinterBuffer, cchBuffer, PrinterGuard._strPrinter );
  1207. return pszPrinterBuffer;
  1208. }
  1209. LPTSTR
  1210. TPrinter::
  1211. pszServerName(
  1212. IN size_t cchBuffer,
  1213. OUT LPTSTR pszServerBuffer CHANGE
  1214. ) const
  1215. /*++
  1216. Routine Description:
  1217. Retrieves the fully qualified name of the server. May be NULL
  1218. for local servers.
  1219. Arguments:
  1220. cchBuffer - the size of the buffer in characters.
  1221. pszServerBuffer - Uninitialized buffer that receives the server name.
  1222. Must be at least kServerMax in length.
  1223. Return Value:
  1224. Pointer to name of printer (generally pointer to pszServerBuffer).
  1225. --*/
  1226. {
  1227. LPTSTR pszServer = NULL;
  1228. CCSLock::Locker CSL( *gpCritSec );
  1229. //
  1230. // If we have a server name that is different from gpPrintLib,
  1231. // then return it. Otherwise return NULL which indicates the
  1232. // local server.
  1233. //
  1234. if( ((LPCTSTR)PrinterGuard._strServer)[0] &&
  1235. _pPrintLib->strComputerName( ) != PrinterGuard.strServer( )){
  1236. StringCchCopy( pszServerBuffer, cchBuffer, PrinterGuard._strServer );
  1237. pszServer = pszServerBuffer;
  1238. }
  1239. return pszServer;
  1240. }
  1241. HANDLE
  1242. TPrinter::
  1243. hPrinter(
  1244. VOID
  1245. ) const
  1246. /*++
  1247. Routine Description:
  1248. Return the handle to the common printer handle. Note that
  1249. the callee must not use any RPC calls that do not return in a
  1250. timely fashion (like WPC or FFPCN that uses WPC).
  1251. Arguments:
  1252. Return Value:
  1253. --*/
  1254. {
  1255. SINGLETHREADNOT(UIThread);
  1256. return ExecGuard._hPrinter;
  1257. }
  1258. HANDLE
  1259. TPrinter::
  1260. hPrinterNew(
  1261. VOID
  1262. ) const
  1263. /*++
  1264. Routine Description:
  1265. Returns a new printer handle. hPrinter() returns a common one,
  1266. this returns one that is new and orphaned.
  1267. Arguments:
  1268. Return Value:
  1269. hPrinter - Must be ClosePrinter'd by callee().
  1270. NULL - failure.
  1271. --*/
  1272. {
  1273. TStatusB bStatus;
  1274. LPTSTR pszPrinter;
  1275. TCHAR szPrinter[kPrinterBufMax];
  1276. HANDLE hPrinter;
  1277. pszPrinter = pszPrinterName( ARRAYSIZE(szPrinter), szPrinter );
  1278. if( pszPrinter && !pszPrinter[0] ){
  1279. //
  1280. // szNull indicates local server also; change it to
  1281. // NULL since OpenPrinter only likes NULL.
  1282. //
  1283. pszPrinter = NULL;
  1284. }
  1285. bStatus DBGCHK = OpenPrinter( pszPrinter,
  1286. &hPrinter,
  1287. NULL );
  1288. if( !bStatus ){
  1289. return NULL;
  1290. }
  1291. return hPrinter;
  1292. }
  1293. /********************************************************************
  1294. Default MPrinterClient definitions.
  1295. ********************************************************************/
  1296. COUNT
  1297. MPrinterClient::
  1298. cSelected(
  1299. VOID
  1300. ) const
  1301. {
  1302. DBGMSG( DBG_WARN, ( "PrinterClient.cSelected: unimplemented\n" ));
  1303. return kInvalidCountValue;
  1304. }
  1305. HANDLE
  1306. MPrinterClient::
  1307. GetFirstSelItem(
  1308. VOID
  1309. ) const
  1310. {
  1311. DBGMSG( DBG_WARN, ( "PrinterClient.GetFirstSelItem: unimplemented\n" ));
  1312. return NULL;
  1313. }
  1314. HANDLE
  1315. MPrinterClient::
  1316. GetNextSelItem(
  1317. HANDLE hItem
  1318. ) const
  1319. {
  1320. UNREFERENCED_PARAMETER( hItem );
  1321. DBGMSG( DBG_WARN, ( "PrinterClient.GetNextSelItem: unimplemented\n" ));
  1322. return NULL;
  1323. }
  1324. IDENT
  1325. MPrinterClient::
  1326. GetId(
  1327. HANDLE hItem
  1328. ) const
  1329. {
  1330. UNREFERENCED_PARAMETER( hItem );
  1331. DBGMSG( DBG_WARN, ( "PrinterClient.GetId: unimplemented\n" ));
  1332. return kInvalidIdentValue;
  1333. }
  1334. VOID
  1335. MPrinterClient::
  1336. vSaveSelections(
  1337. VOID
  1338. )
  1339. {
  1340. }
  1341. VOID
  1342. MPrinterClient::
  1343. vRestoreSelections(
  1344. VOID
  1345. )
  1346. {
  1347. }