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.

4545 lines
101 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1995 - 1999
  3. All rights reserved.
  4. Module Name:
  5. folder.cxx
  6. Abstract:
  7. Holds support for print folder notifications. The shell32.dll
  8. print folder will use the TFolder class to receive data and
  9. notifications.
  10. There are two different types of connections on NT SUR:
  11. 'True' connects and 'masq' connects:
  12. True: Normal uplevel WinNT->WinNT connection. Connection
  13. information is stored in HKEY_CURRENT_USER:\Printers\Conenctions.
  14. Win32spl handles the printer.
  15. Masq: WinNT client -> downlevel server (win9x, wfw, lm, partial
  16. print providers). The connection is really a local printer
  17. that masquarades as a network printer. When 1 person connections,
  18. everyone on that machine suddenly gets the connection. We
  19. get create/delete notifications from the server handle, but
  20. all information about the printer from the server handle is
  21. incorrect.
  22. Ideally we would just open a server handle and process all local,
  23. true, and masq printer connections the same, but the last two are
  24. not supported. Therefore we must do the following:
  25. True: watch the registry and open a separate handle to gather
  26. information and notficiations.
  27. Masq: watch the server for creates and deletes, but also
  28. open a separate handle to get information and notifications.
  29. Author:
  30. Albert Ting (AlbertT) 30-Oct-1995
  31. Revision History:
  32. --*/
  33. #include "precomp.hxx"
  34. #pragma hdrstop
  35. #include "folder.hxx"
  36. #if DBG
  37. //#define DBG_FLDRINFO DBG_INFO
  38. #define DBG_FLDRINFO DBG_NONE
  39. #endif
  40. TString *VDSConnection::gpstrConnectStatusOpen = NULL;
  41. TString *VDSConnection::gpstrConnectStatusOpenError = NULL;
  42. TString *VDSConnection::gpstrConnectStatusAccessDenied = NULL;
  43. TString *VDSConnection::gpstrConnectStatusInvalidPrinterName = NULL;
  44. TCHAR gszInternalDefaultPrinter[kPrinterBufMax];
  45. /********************************************************************
  46. Local prototypes.
  47. ********************************************************************/
  48. PBYTE
  49. pPackStrings(
  50. LPCTSTR* ppszSource,
  51. PBYTE pDest,
  52. PDWORD pdwDestOffsets,
  53. PBYTE pEnd
  54. );
  55. #if DBG_FOLDER
  56. VOID
  57. vDumpFolderPrinterData(
  58. PFOLDER_PRINTER_DATA pData
  59. );
  60. #endif
  61. VOID
  62. vGetInternalDefaultPrinter(
  63. LPTSTR pszOldDefault
  64. );
  65. VOID
  66. vUpdateInternalDefaultPrinter(
  67. VOID
  68. );
  69. /********************************************************************
  70. Public interface functions.
  71. We will try and push as much functionality into printui.dll and
  72. out of shell32.dll.
  73. ********************************************************************/
  74. HRESULT
  75. RegisterPrintNotify(
  76. IN LPCTSTR pszDataSource,
  77. IN IFolderNotify *pClientNotify,
  78. OUT LPHANDLE phFolder,
  79. OUT PBOOL pbAdministrator OPTIONAL
  80. )
  81. /*++
  82. Routine Description:
  83. Register a notification handler for a data source. This function returns
  84. a handle to a folder (in phFolder) when registers handle, which must be
  85. closed by calling UnregisterPrintNotify() function below
  86. Arguments:
  87. pszDataSource - DataSource to query, NULL = local machine.
  88. pClientNotify - Pointer to client notify interface.
  89. phFolder - Where to place the HANDLE to the folder
  90. Return Value:
  91. S_OK - Everything looks to be OK
  92. E_FAIL - In case of an error. (other error return values are also possibe
  93. for example: E_OUTOFMEMORY)
  94. --*/
  95. {
  96. //
  97. // Allow NULL pointer to be passed, so hack it here
  98. //
  99. if( !pszDataSource )
  100. {
  101. pszDataSource = gszNULL;
  102. }
  103. //
  104. // Registers the data source passed
  105. //
  106. return TFolderList::RegisterDataSource( pszDataSource, pClientNotify, phFolder, pbAdministrator );
  107. }
  108. HRESULT
  109. UnregisterPrintNotify(
  110. IN LPCTSTR pszDataSource,
  111. IN IFolderNotify *pClientNotify,
  112. OUT LPHANDLE phFolder
  113. )
  114. /*++
  115. Routine Description:
  116. Unregister a notification handler (previously registered with
  117. RegisterPrintNotify()) for a data source. This function sets the
  118. phFolder to NULL and free up the notification handler.
  119. Arguments:
  120. pszDataSource - DataSource to query, NULL = local machine.
  121. pClientNotify - Pointer to client notify interface.
  122. phFolder - Where to place the HANDLE to the folder
  123. Return Value:
  124. S_OK - Everything looks to be OK
  125. E_FAIL - In case of an error. (other error return values are also possibe
  126. for example: E_OUTOFMEMORY)
  127. --*/
  128. {
  129. //
  130. // Allow NULL pointer to be passed, so hack it here
  131. //
  132. if( !pszDataSource )
  133. {
  134. pszDataSource = gszNULL;
  135. }
  136. //
  137. // Unregisters the data source passed
  138. //
  139. return TFolderList::UnregisterDataSource( pszDataSource, pClientNotify, phFolder );
  140. }
  141. BOOL
  142. bFolderRefresh(
  143. IN HANDLE hFolder,
  144. OUT PBOOL pbAdministrator OPTIONAL
  145. )
  146. /*++
  147. Routine Description:
  148. Request that the folder refresh its data by hitting the DataSource.
  149. Arguments:
  150. hFolder - Folder to refresh.
  151. pbAdministrator - Returns whether the user has administrative access
  152. on the hFolder. If this folder is a server and connections,
  153. this applies only to the server.
  154. This should be used to determine if the user can add printers
  155. to the folder via Add Printer Wizard.
  156. Return Value:
  157. TRUE = success, FALSE = failure.
  158. --*/
  159. {
  160. TStatus bStatus;
  161. bStatus DBGNOCHK = FALSE;
  162. DWORD dwError = ERROR_SUCCESS;
  163. // validate the passed in folder handle.
  164. TFolder* pFolder = (TFolder*)hFolder;
  165. if (TFolderList::bValidFolderObject(pFolder))
  166. {
  167. // enter the notify CS
  168. CCSLock::Locker notifyLock(pFolder->pPrintLib()->pNotify()->csResumeSuspend());
  169. if (notifyLock)
  170. {
  171. // suspend the background threads callbacks before entering the folder CS
  172. if (pFolder->pPrintLib()->pNotify()->bSuspendCallbacks())
  173. {
  174. {
  175. // enter the folder CS while rebuilding the cache.
  176. CCSLock::Locker lock(pFolder->CritSec());
  177. if (lock)
  178. {
  179. //
  180. // Walk through each DataSource and tell them to refresh. Start
  181. // from the end and go to the beginning to refresh all connections
  182. // first, and hit the server last. When a "masq" printer is
  183. // discovered, it is refreshed then added to the end of the
  184. // linked list.
  185. //
  186. bStatus DBGCHK = TRUE;
  187. VDataSource* pDataSource;
  188. TIter iter;
  189. for (pFolder->DataSource_vIterInit(iter), iter.vPrev(); iter.bValid(); iter.vPrev())
  190. {
  191. pDataSource = pFolder->DataSource_pConvert(iter);
  192. bStatus DBGCHK = bStatus && pDataSource->bRefresh();
  193. }
  194. // do not continue the refresh in case of failure.
  195. if (bStatus)
  196. {
  197. // check to see if the current user is an administrator
  198. if (pbAdministrator)
  199. {
  200. pDataSource = pFolder->DataSource_pHead();
  201. *pbAdministrator = pDataSource ? pDataSource->bAdministrator() : FALSE;
  202. }
  203. // update the connections.
  204. if (pFolder->pConnectionNotify())
  205. {
  206. pFolder->vConnectionNotifyChange(FALSE);
  207. }
  208. // revalidate the masq printers.
  209. pFolder->vRevalidateMasqPrinters();
  210. }
  211. }
  212. else
  213. {
  214. // unable to enter the folder CS
  215. dwError = ERROR_OUTOFMEMORY;
  216. }
  217. }
  218. // resume callbacks after releasing the folder CS
  219. pFolder->pPrintLib()->pNotify()->vResumeCallbacks();
  220. }
  221. }
  222. else
  223. {
  224. // unable to enter the notify CS
  225. dwError = ERROR_OUTOFMEMORY;
  226. }
  227. }
  228. else
  229. {
  230. // the passed in folder handle is invalid
  231. dwError = ERROR_INVALID_PARAMETER;
  232. }
  233. // set the last error and return
  234. SetLastError(bStatus ? ERROR_SUCCESS : dwError);
  235. return bStatus;
  236. }
  237. BOOL
  238. bFolderEnumPrinters(
  239. IN HANDLE hFolder,
  240. OUT PFOLDER_PRINTER_DATA pData, CHANGE
  241. IN DWORD cbData,
  242. OUT PDWORD pcbNeeded,
  243. OUT PDWORD pcReturned
  244. )
  245. /*++
  246. Routine Description:
  247. Main query entrypoint to allow the shell to enumerate printers.
  248. This is modeled closely after the EnumPrinters call so that
  249. minimal code in the shell needs to be changed.
  250. Arguments:
  251. hFolder - Folder to query.
  252. pData - Pointer to a buffer that receives the return data. If
  253. this parameter is NULL, cbData must be 0.
  254. cbData - Indicates size of pData.
  255. pcbNeeded - Returns number of bytes needed to hold all printer
  256. information.
  257. pcReturned - Returns the number of printers stored in pData.
  258. Return Value:
  259. TRUE - success, FALSE - failure.
  260. --*/
  261. {
  262. TStatus bStatus;
  263. bStatus DBGNOCHK = FALSE;
  264. DWORD dwError = ERROR_SUCCESS;
  265. // validate the passed in folder handle.
  266. TFolder* pFolder = (TFolder*)hFolder;
  267. if (TFolderList::bValidFolderObject(pFolder))
  268. {
  269. *pcbNeeded = 0;
  270. *pcReturned = 0;
  271. // lock the folder CS while retreiving data from the cache.
  272. CCSLock::Locker lock(pFolder->CritSec());
  273. if (lock)
  274. {
  275. // first walk thorugh the DataSources see how much space we need.
  276. COUNTB cbSize = 0;
  277. VDataSource* pDataSource;
  278. TIter iter;
  279. for (pFolder->DataSource_vIterInit(iter), iter.vNext(); iter.bValid(); iter.vNext())
  280. {
  281. pDataSource = pFolder->DataSource_pConvert(iter);
  282. cbSize += pDataSource->cbAllPrinterData();
  283. }
  284. // return size to user then check if it's large enough.
  285. *pcbNeeded = cbSize;
  286. if (cbData < cbSize)
  287. {
  288. // the size of the passed in buffer is not sufficient
  289. dwError = ERROR_INSUFFICIENT_BUFFER;
  290. }
  291. else
  292. {
  293. // run though all pDataSources again and put them into the buffer.
  294. PBYTE pBegin = (PBYTE)pData;
  295. PBYTE pEnd = pBegin + cbData;
  296. COUNT cReturned = 0;
  297. for (pFolder->DataSource_vIterInit(iter), iter.vNext(); iter.bValid(); iter.vNext())
  298. {
  299. // pBegin and pEnd are automatically moved inward when the data is packed.
  300. pDataSource = pFolder->DataSource_pConvert(iter);
  301. cReturned += pDataSource->cPackAllPrinterData(pBegin, pEnd);
  302. }
  303. // update the printer's counter.
  304. if (pcReturned)
  305. {
  306. *pcReturned = cReturned;
  307. }
  308. #if DBG_FOLDER
  309. DBGMSG(DBG_FOLDER, ("bFolderEnumPrinters: Return %d Size %d\n", *pcReturned, *pcbNeeded));
  310. for (UINT i=0; i<*pcReturned; ++i)
  311. {
  312. vDumpFolderPrinterData(&pData[i]);
  313. }
  314. DBGMSG(DBG_FOLDER, ("bFolderEnumPrinters DONE\n"));
  315. #endif
  316. // indicate success here.
  317. bStatus DBGCHK = TRUE;
  318. }
  319. }
  320. else
  321. {
  322. // unable to enter the folder CS
  323. dwError = ERROR_OUTOFMEMORY;
  324. }
  325. }
  326. else
  327. {
  328. // the passed in folder handle is invalid
  329. dwError = ERROR_INVALID_PARAMETER;
  330. }
  331. // set the last error and return
  332. SetLastError(bStatus ? ERROR_SUCCESS : dwError);
  333. return bStatus;
  334. }
  335. BOOL
  336. bFolderGetPrinter(
  337. IN HANDLE hFolder,
  338. IN LPCTSTR pszPrinter,
  339. OUT PFOLDER_PRINTER_DATA pData,
  340. IN DWORD cbData,
  341. OUT PDWORD pcbNeeded
  342. )
  343. /*++
  344. Routine Description:
  345. Returns information about a specific printer in the hFolder.
  346. Modeled after GetPrinter to minimize the changes in shell.
  347. Arguments:
  348. hFolder - Folder that container the printer.
  349. pszPrinter - Printer that should be queried. The DataSource prefix
  350. for remote printers is optional.
  351. pData - Pointer to a buffer that receives the printer data.
  352. If this parameter is NULL, cbData must be 0.
  353. cbData - Size of the input buffer.
  354. pcbNeeded - Returns the size of the buffer needed to store the
  355. printer information.
  356. Return Value:
  357. TRUE = sucess, FALSE = failure.
  358. When GLE = ERROR_INSUFFICIENT_BUFFER, the client should retry with
  359. a larger buffer.
  360. --*/
  361. {
  362. TStatus bStatus;
  363. bStatus DBGNOCHK = FALSE;
  364. DWORD dwError = ERROR_SUCCESS;
  365. // validate the passed in folder handle.
  366. TFolder* pFolder = (TFolder*)hFolder;
  367. if (TFolderList::bValidFolderObject(pFolder))
  368. {
  369. // lock the folder CS while retreiving the data.
  370. CCSLock::Locker lock(pFolder->CritSec());
  371. if (lock)
  372. {
  373. // walk through all pDataSources until one has a match.
  374. VDataSource* pDataSource;
  375. TIter iter;
  376. for (pFolder->DataSource_vIterInit(iter), iter.vNext(); iter.bValid(); iter.vNext())
  377. {
  378. pDataSource = pFolder->DataSource_pConvert(iter);
  379. if (pDataSource->bGetPrinter(pszPrinter, pData, cbData, pcbNeeded))
  380. {
  381. #if DBG_FOLDER
  382. vDumpFolderPrinterData(pData);
  383. #endif
  384. dwError = ERROR_SUCCESS;
  385. bStatus DBGCHK = TRUE;
  386. break;
  387. }
  388. // on any other error other than INVALID_PRINTER_NAME we should immediately fail.
  389. dwError = GetLastError();
  390. if (dwError == ERROR_INVALID_PRINTER_NAME)
  391. {
  392. continue;
  393. }
  394. else
  395. {
  396. break;
  397. }
  398. }
  399. }
  400. else
  401. {
  402. // unable to enter the folder CS
  403. dwError = ERROR_OUTOFMEMORY;
  404. }
  405. }
  406. else
  407. {
  408. // the passed in folder handle is invalid
  409. dwError = ERROR_INVALID_PARAMETER;
  410. }
  411. // set the last error and return
  412. SetLastError(bStatus ? ERROR_SUCCESS : dwError);
  413. return bStatus;
  414. }
  415. /********************************************************************
  416. Semi-public: called from other printui functions.
  417. ********************************************************************/
  418. VOID
  419. TFolder::
  420. vCheckDeleteDefault(
  421. LPCTSTR pszDeleted
  422. )
  423. {
  424. //
  425. // HACK: WinNT Spooler doesn't support default printer.
  426. // If the default was deleted, remove it from our internal
  427. // tracking buffer.
  428. //
  429. // If we don't do this, then when we delete the default
  430. // printer, we try and UPDATEITEM a non-existant item
  431. // (trying to revert the default printer back to a normal
  432. // one). This causes an extra refresh.
  433. //
  434. {
  435. CCSLock::Locker CSL( *gpCritSec );
  436. if( !lstrcmpi( pszDeleted,
  437. gszInternalDefaultPrinter )){
  438. gszInternalDefaultPrinter[0] = 0;
  439. }
  440. }
  441. }
  442. VOID
  443. TFolder::
  444. vDefaultPrinterChanged(
  445. VOID
  446. )
  447. /*++
  448. Routine Description:
  449. The default printer has changed. Cause the entire windows
  450. to refresh. This will force the icon to be refreshed. (Local
  451. print folder only.)
  452. !! HACK !!
  453. We need to do this since the NT spooler doesn't support
  454. PRINTER_ATTRIBUTE_DEFAULT, (or per-user notifications).
  455. Arguments:
  456. Return Value:
  457. --*/
  458. {
  459. TCHAR aszDefault[2][kPrinterBufMax];
  460. //
  461. // Send two notification: one for the old default, then
  462. // one for the new one.
  463. //
  464. vGetInternalDefaultPrinter( aszDefault[0] );
  465. vUpdateInternalDefaultPrinter();
  466. vGetInternalDefaultPrinter( aszDefault[1] );
  467. if( lstrcmpi( aszDefault[0], aszDefault[1] )){
  468. COleComInitializer com; // shell ends up using COM
  469. DBGMSG( DBG_FOLDER, ( "Folder.vInternalDefaultPrinterChanged: update default printer\n" ));
  470. INT i;
  471. for( i=0; i<2; ++i ){
  472. if( aszDefault[i][0] ){
  473. SHChangeNotify( SHCNE_UPDATEITEM, SHCNF_PRINTER | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, aszDefault[i], 0 );
  474. }
  475. }
  476. }
  477. }
  478. /********************************************************************
  479. Private internal helper functions for Default Printer.
  480. ********************************************************************/
  481. VOID
  482. vGetInternalDefaultPrinter(
  483. IN OUT LPTSTR pszDefaultPrinter CHANGE
  484. )
  485. /*++
  486. Routine Description:
  487. Retrieves the default printer from our internal buffer.
  488. Note: this does not read from win.ini since we may wish to
  489. get the old default during a WININICHANGE message.
  490. Arguments:
  491. pszDefault - Receives old default printer. Must be
  492. kPrinterBufMax in size. If there is no default, then
  493. pszDefault[0] is 0.
  494. Return Value:
  495. --*/
  496. {
  497. CCSLock::Locker CSL( *gpCritSec );
  498. lstrcpy( pszDefaultPrinter, gszInternalDefaultPrinter );
  499. }
  500. VOID
  501. vUpdateInternalDefaultPrinter(
  502. VOID
  503. )
  504. /*++
  505. Routine Description:
  506. Updates our internal buffer that hold the default printer.
  507. This is read from win.ini.
  508. Arguments:
  509. Return Value:
  510. --*/
  511. {
  512. DWORD dwSize = kPrinterBufMax;
  513. TCHAR szPrinterDefault[kPrinterBufMax];
  514. TStatusB bStatus;
  515. //
  516. // Get the default printer.
  517. //
  518. bStatus DBGNOCHK = GetDefaultPrinter( szPrinterDefault, &dwSize );
  519. //
  520. // If there is not default printer then clear our internal buffer.
  521. //
  522. if( !bStatus )
  523. {
  524. szPrinterDefault[0] = 0;
  525. }
  526. //
  527. // Update the our internal default printer.
  528. //
  529. CCSLock::Locker CSL( *gpCritSec );
  530. lstrcpy( gszInternalDefaultPrinter, szPrinterDefault );
  531. return;
  532. }
  533. /********************************************************************
  534. TFolder::TNotifyHandler.
  535. ********************************************************************/
  536. TFolder::
  537. TNotifyHandler::
  538. TNotifyHandler(
  539. IFolderNotify *pClientNotify
  540. ) :
  541. _pClientNotify( pClientNotify )
  542. {
  543. if( _pClientNotify )
  544. {
  545. _bValid = TRUE;
  546. }
  547. }
  548. TFolder::
  549. TNotifyHandler::
  550. ~TNotifyHandler(
  551. VOID
  552. )
  553. {
  554. if( bValid() )
  555. {
  556. _pClientNotify = NULL;
  557. }
  558. }
  559. /********************************************************************
  560. TFolder internal interfaces.
  561. ********************************************************************/
  562. TFolder::
  563. TFolder(
  564. IN LPCTSTR pszDataSource
  565. ) : _bValid( FALSE ),
  566. _pConnectionNotify( NULL )
  567. /*++
  568. Routine Description:
  569. Construct the folder object. The base level folder watches
  570. a DataSource; if the DataSource is local, then it watches connections
  571. also.
  572. Arguments:
  573. pszDataSource - DataSource to watch, szNULL indicates local. This parameter
  574. must not be NULL.
  575. Return Value:
  576. --*/
  577. {
  578. TStatusB bSuccess;
  579. //
  580. // Keep track for the local data source
  581. //
  582. strLocalDataSource().bUpdate( pszDataSource );
  583. //
  584. // Ensure the main printlib is initialized.
  585. //
  586. bSuccess DBGCHK = TPrintLib::bGetSingleton(_pPrintLib);
  587. if( bSuccess )
  588. {
  589. //
  590. // Create the notification against the printers on the DataSource
  591. // first. Then check if it's a local DataSource. If so, create a
  592. // TConnectionNotify object, which watches for printer connections,
  593. // and adds them as appropriate.
  594. //
  595. //
  596. // Create the main DataSource notification. This watches all printers
  597. // on the DataSource. Other DataSource added from TConnectionNotify
  598. // are really printer connections (but are handled like DataSources
  599. // with one printer).
  600. //
  601. VDataSource* pDataSource = VDataSource::pNew( this,
  602. pszDataSource,
  603. VDataSource::kServer );
  604. if( !pDataSource ){
  605. return;
  606. }
  607. //
  608. // kServer always goes at the beginning of the linked list.
  609. //
  610. DataSource_vAdd( pDataSource );
  611. //
  612. // Now create the connection object if we are watching the DataSource.
  613. // _pConnectionNotify will always be non-NULL in the local case, and
  614. // NULL in remote print folders.
  615. //
  616. if( !pszDataSource[0] ){
  617. _pConnectionNotify = new TConnectionNotify( this );
  618. if( !VALID_PTR( _pConnectionNotify )){
  619. vCleanup();
  620. return;
  621. }
  622. }
  623. _bValid = TRUE;
  624. }
  625. }
  626. TFolder::
  627. ~TFolder(
  628. VOID
  629. )
  630. /*++
  631. Routine Description:
  632. Delete the folder.
  633. Arguments:
  634. Return Value:
  635. --*/
  636. {
  637. }
  638. HRESULT
  639. TFolder::
  640. pLookupNotifyHandler(
  641. IN IFolderNotify *pClientNotify,
  642. OUT TNotifyHandler **ppHandler
  643. )
  644. /*++
  645. Routine Description:
  646. Searching for a notification handler.
  647. Arguments:
  648. pClientNotify - The handler to search for
  649. Return Value:
  650. NULL - not found
  651. --*/
  652. {
  653. SPLASSERT( CritSec( ).bInside( ) );
  654. TIter iter;
  655. TFolder::TNotifyHandler *pHandler;
  656. HRESULT hr = E_INVALIDARG; // Assume pClientNotify is NULL
  657. if( pClientNotify )
  658. {
  659. hr = S_FALSE; // Assume not found
  660. for( Handlers_vIterInit( iter ), iter.vNext(); iter.bValid(); iter.vNext() )
  661. {
  662. pHandler = Handlers_pConvert( iter );
  663. if( pClientNotify == pHandler->pClientNotify( ) )
  664. {
  665. //
  666. // found!
  667. //
  668. hr = S_OK;
  669. if( ppHandler )
  670. {
  671. *ppHandler = pHandler;
  672. }
  673. break;
  674. }
  675. }
  676. }
  677. else
  678. {
  679. //
  680. // The registration will fail gracefully
  681. //
  682. DBGMSG( DBG_ERROR, ( "NULL notify handler is passed to pLookupNotifyHandler(...) function") );
  683. }
  684. return hr;
  685. }
  686. HRESULT
  687. TFolder::
  688. RegisterNotifyHandler(
  689. IN IFolderNotify *pClientNotify
  690. )
  691. /*++
  692. Routine Description:
  693. Registers a new notification handler for this
  694. folder.
  695. Arguments:
  696. pClientNotify - The new handler
  697. Return Value:
  698. S_OK - on success
  699. E_FAIL - oterwise
  700. --*/
  701. {
  702. SPLASSERT( CritSec( ).bInside( ) );
  703. HRESULT hr = pLookupNotifyHandler(pClientNotify, NULL);
  704. if( SUCCEEDED(hr) )
  705. {
  706. if( S_FALSE == hr )
  707. {
  708. //
  709. // The handler is not found - so register it!
  710. //
  711. TFolder::TNotifyHandler *pHandler = new TFolder::TNotifyHandler( pClientNotify );
  712. DBGMSG( DBG_FLDRINFO, ( "[TFolderList-DBG] TFolder CLIENT ATTACHED!!\n" ) );
  713. if(VALID_PTR(pHandler))
  714. {
  715. Handlers_vAppend( pHandler );
  716. hr = S_OK;
  717. }
  718. else
  719. {
  720. delete pHandler;
  721. hr = E_OUTOFMEMORY;
  722. }
  723. }
  724. else
  725. {
  726. //
  727. // Trying to register the handler twice
  728. //
  729. DBGMSG( DBG_WARN, ( "You are trying to register a notification handler twice\n") );
  730. hr = E_FAIL;
  731. }
  732. }
  733. return hr;
  734. }
  735. HRESULT
  736. TFolder::
  737. UnregisterNotifyHandler(
  738. IN IFolderNotify *pClientNotify
  739. )
  740. /*++
  741. Routine Description:
  742. Unregisters a notification handler for this
  743. folder.
  744. Arguments:
  745. pClientNotify - The handler, which should be
  746. unregistered
  747. Return Value:
  748. S_OK - on success
  749. E_FAIL - oterwise
  750. --*/
  751. {
  752. SPLASSERT( CritSec( ).bInside( ) );
  753. TFolder::TNotifyHandler *pHandler;
  754. HRESULT hr = pLookupNotifyHandler( pClientNotify, &pHandler );;
  755. if( SUCCEEDED(hr) )
  756. {
  757. if( S_OK == hr )
  758. {
  759. //
  760. // The handler is found! - OK.
  761. //
  762. DBGMSG( DBG_FLDRINFO, ( "[TFolderList-DBG] TFolder CLIENT DETACHED!!\n" ) );
  763. pHandler->Link_vDelinkSelf( );
  764. delete pHandler;
  765. hr = S_OK;
  766. }
  767. else
  768. {
  769. //
  770. // Trying to unregister a handler which is not regsitered
  771. //
  772. DBGMSG( DBG_WARN, ( "You are trying to unregister a handler which is not regsitered\n") );
  773. hr = E_FAIL;
  774. }
  775. }
  776. return hr;
  777. }
  778. BOOL
  779. TFolder::
  780. bNotifyAllClients(
  781. IN FOLDER_NOTIFY_TYPE NotifyType,
  782. IN LPCWSTR pszName,
  783. IN LPCWSTR pszNewName
  784. )
  785. /*++
  786. Routine Description:
  787. Notify all clients of this folder
  788. Arguments:
  789. Same as for IFolderNotify::ProcessNotify
  790. Return Value:
  791. TRUE - on success
  792. FALSE - oterwise
  793. --*/
  794. {
  795. //
  796. // Enter the folder CS as we are going to traverse the
  797. // notify handlers list (i.e. the folder data)
  798. //
  799. CCSLock::Locker CSL( CritSec( ) );
  800. TIter iter;
  801. TFolder::TNotifyHandler *pHandler;
  802. BOOL bResult = TRUE;
  803. for( Handlers_vIterInit( iter ), iter.vNext(); iter.bValid(); iter.vNext() )
  804. {
  805. pHandler = Handlers_pConvert( iter );
  806. if( !pHandler->_pClientNotify->ProcessNotify( NotifyType, pszName, pszNewName ) )
  807. {
  808. bResult = FALSE;
  809. }
  810. }
  811. return bResult;
  812. }
  813. VOID
  814. TFolder::
  815. vRefreshUI(
  816. VOID
  817. )
  818. /*++
  819. Routine Description:
  820. A lot of things have changed in the folder: refresh the UI. Ideally
  821. we would use SHCNE_UPDATEDIR, but this doesn't seem to work with
  822. non-fs folders. Do a complete refresh (note that this doesn't
  823. update icons, but that's generally ok).
  824. Arguments:
  825. Return Value:
  826. --*/
  827. {
  828. //
  829. // Something drastic about the folder has changed.
  830. // Request that the entire windows refresh.
  831. //
  832. bNotifyAllClients( kFolderUpdateAll, NULL, NULL );
  833. }
  834. BOOL
  835. TFolder::
  836. bLocal(
  837. VOID
  838. ) const
  839. /*++
  840. Routine Description:
  841. Check whether the print folder for the local machine.
  842. Arguments:
  843. Return Value:
  844. TRUE - This is a local print folder.
  845. FALSE - It's a remote print folder (or a remote invocation of the
  846. local print folder).
  847. --*/
  848. {
  849. return _pConnectionNotify ?
  850. TRUE :
  851. FALSE;
  852. }
  853. VOID
  854. TFolder::
  855. vCleanup(
  856. VOID
  857. )
  858. /*++
  859. Routine Description:
  860. Arguments:
  861. Return Value:
  862. --*/
  863. {
  864. // This call do not need to be protected with the folder
  865. // critical section, because it is called only when the
  866. // object is detached from any clients and is about to be
  867. // destroyed. Actually this is an explicit destructor
  868. // function
  869. //
  870. // Delete the connection object immediately. If the
  871. // pConnectionNotify is currently registered, this will
  872. // wait until it has been unregistered before deleting it.
  873. //
  874. delete _pConnectionNotify;
  875. {
  876. //
  877. // Walk through the DataSources and decrement the refcounts. The
  878. // DataSources will be destroyed when the refcount reaches zero.
  879. //
  880. VDataSource* pDataSource;
  881. TIter Iter;
  882. for( DataSource_vIterInit( Iter ), Iter.vNext();
  883. Iter.bValid(); ){
  884. pDataSource = DataSource_pConvert( Iter );
  885. Iter.vNext();
  886. pDataSource->DataSource_vDelinkSelf();
  887. pDataSource->vDelete();
  888. }
  889. }
  890. }
  891. /********************************************************************
  892. TFolder internals.
  893. Add and Find datasources.
  894. ********************************************************************/
  895. VOID
  896. TFolder::
  897. vAddDataSource(
  898. IN LPCTSTR pszPrinter,
  899. IN VDataSource::CONNECT_TYPE ConnectType,
  900. IN BOOL bNotify
  901. )
  902. /*++
  903. Routine Description:
  904. Add a data source based on a pszPrinter, and put it on the
  905. linked list.
  906. Arguments:
  907. pszPrinter - Name of printer to create. Client must guarantee
  908. that this printer does not already have a data source.
  909. bMasq - Indicates if a masq printer.
  910. bNotify - Indicates whether we should trigger a notification.
  911. If this is TRUE, then this is an asynchronous refresh (the
  912. registry changed). If this is FALSE, then we don't need
  913. to notify or refresh since the user is explicitly refreshing.
  914. Return Value:
  915. --*/
  916. {
  917. SPLASSERT( ConnectType == VDataSource::kTrue ||
  918. ConnectType == VDataSource::kMasq );
  919. //
  920. // Now create one and add it.
  921. //
  922. VDataSource* pDataSource = VDataSource::pNew( this,
  923. pszPrinter,
  924. ConnectType );
  925. //
  926. // If the pointer is invalid, we just punt: the
  927. // display won't be updated.
  928. //
  929. if( pDataSource ){
  930. //
  931. // Non-servers always are appended to the end of the list.
  932. //
  933. DataSource_vAppend( pDataSource );
  934. DBGMSG( DBG_FOLDER,
  935. ( "Folder.vAddDataSource: SHChangeNotify add "TSTR"\n",
  936. (LPCTSTR)pDataSource->strDataSource( )));
  937. //
  938. // Determine whether we need to send a notification.
  939. // If we just started, then we don't need one since
  940. // the user already gets the objects during the enum.
  941. //
  942. if( bNotify )
  943. {
  944. bNotifyAllClients( kFolderCreate, pDataSource->strDataSource(), NULL );
  945. }
  946. //
  947. // Kick off the notification process.
  948. //
  949. pDataSource->bRefresh();
  950. }
  951. }
  952. VDataSource*
  953. TFolder::
  954. pFindDataSource(
  955. IN LPCTSTR pszPrinter,
  956. IN VDataSource::CONNECT_TYPE ConnectType
  957. ) const
  958. /*++
  959. Routine Description:
  960. Finds a specified printer, based on a name and type.
  961. Arguments:
  962. pszPrinter - Printer to find.
  963. ConnectType - Find only this type of printer.
  964. Return Value:
  965. VDataSource* if found, NULL if not.
  966. --*/
  967. {
  968. VDataSource* pDataSource = NULL;
  969. TIter Iter;
  970. for( DataSource_vIterInit( Iter ), Iter.vNext();
  971. Iter.bValid();
  972. Iter.vNext( )){
  973. pDataSource = DataSource_pConvert( Iter );
  974. if( pDataSource->ConnectType() == ConnectType &&
  975. !lstrcmpi( pDataSource->strDataSource(), pszPrinter )){
  976. break;
  977. }
  978. }
  979. if( Iter.bValid( )){
  980. return pDataSource;
  981. }
  982. return NULL;
  983. }
  984. /********************************************************************
  985. Add, delete, and revalidate masq data sources.
  986. ********************************************************************/
  987. VOID
  988. TFolder::
  989. vAddMasqDataSource(
  990. LPCTSTR pszPrinter,
  991. BOOL bNotify
  992. )
  993. /*++
  994. Routine Description:
  995. Add a pszPrinter (that is connection based) to the pFolder.
  996. Don't add if it already exists.
  997. Arguments:
  998. pszPrinter - Name of the printer connection (\\server\share format).
  999. Return Value:
  1000. --*/
  1001. {
  1002. //
  1003. // First verify that one doesn't already exist.
  1004. //
  1005. VDataSource *pDataSource;
  1006. pDataSource = pFindDataSource( pszPrinter, VDataSource::kMasq );
  1007. if( !pDataSource ){
  1008. vAddDataSource( pszPrinter, VDataSource::kMasq, bNotify );
  1009. }
  1010. }
  1011. VOID
  1012. TFolder::
  1013. vDeleteMasqDataSource(
  1014. LPCTSTR pszPrinter
  1015. )
  1016. /*++
  1017. Routine Description:
  1018. Delete a connection based printer data source from the linked
  1019. list.
  1020. Arguments:
  1021. pszPrinter - Printer to delete (\\server\share format).
  1022. Return Value:
  1023. --*/
  1024. {
  1025. VDataSource *pDataSource;
  1026. pDataSource = pFindDataSource( pszPrinter, VDataSource::kMasq );
  1027. //
  1028. // Now delete it.
  1029. //
  1030. if( pDataSource ){
  1031. pDataSource->DataSource_vDelinkSelf();
  1032. pDataSource->vDelete();
  1033. }
  1034. }
  1035. VOID
  1036. TFolder::
  1037. vRevalidateMasqPrinters(
  1038. VOID
  1039. )
  1040. /*++
  1041. Routine Description:
  1042. During a refresh, we must verify that all the VDataSource which
  1043. represent the masq printers are in sync with the server. They
  1044. may get out of sync if one is deleted right when a refresh
  1045. occurs--we'll never get the notification to delete it.
  1046. Note: we don't have to worry about newly added masq printers,
  1047. since they will be added during the refresh.
  1048. Arguments:
  1049. Return Value:
  1050. --*/
  1051. {
  1052. CCSLock::Locker CSL( _CritSec );
  1053. TIter Iter;
  1054. DataSource_vIterInit( Iter );
  1055. Iter.vNext();
  1056. SPLASSERT( Iter.bValid( ));
  1057. //
  1058. // Server is always at the head of the list.
  1059. //
  1060. VDataSource *pdsServer = DataSource_pConvert( Iter );
  1061. SPLASSERT( pdsServer->ConnectType() == VDataSource::kServer );
  1062. Iter.vNext();
  1063. while( Iter.bValid( )){
  1064. VDataSource *pDataSource = DataSource_pConvert( Iter );
  1065. //
  1066. // Immediately move iter since we may delink pDataSource.
  1067. //
  1068. Iter.vNext();
  1069. //
  1070. // If it's not a masq case, don't bother to validate it.
  1071. //
  1072. if( pDataSource->ConnectType() != VDataSource::kMasq ){
  1073. continue;
  1074. }
  1075. //
  1076. // Now search for this printer.
  1077. //
  1078. LPCTSTR pszMasq = pDataSource->strDataSource();
  1079. if( !pdsServer->hItemFindByName( pszMasq )){
  1080. //
  1081. // Delete it.
  1082. //
  1083. DBGMSG( DBG_FOLDER,
  1084. ( "Folder.vRevalidateMasqPrinters: SHChangeNotify: Delete "TSTR"\n",
  1085. pszMasq ));
  1086. TFolder::vCheckDeleteDefault( pDataSource->strDataSource( ));
  1087. bNotifyAllClients( kFolderDelete, pDataSource->strDataSource( ), NULL );
  1088. pDataSource->DataSource_vDelinkSelf();
  1089. pDataSource->vDelete();
  1090. }
  1091. }
  1092. }
  1093. VOID
  1094. TFolder::
  1095. vConnectionNotifyChange(
  1096. BOOL bNotify
  1097. )
  1098. /*++
  1099. Routine Description:
  1100. The registry has changed. Enumerate all printer connections and
  1101. see if they match the existing printers. For printers that
  1102. don't match, delete them, and add any new ones.
  1103. Note: masq printer are not enumerated here, so we don't have
  1104. to worry about making any duplicates.
  1105. Arguments:
  1106. bNotify - Indicates whether the connection was created by
  1107. a notification (TRUE) or a refresh (FALSE).
  1108. Return Value:
  1109. --*/
  1110. {
  1111. CCSLock::Locker CSL( _CritSec );
  1112. LPPRINTER_INFO_4 pInfo4 = NULL;
  1113. DWORD cbInfo4 = 0;
  1114. DWORD cPrinters = 0;
  1115. //
  1116. // Retrieve the printer connections for this user.
  1117. //
  1118. if( !VDataRefresh::bEnumPrinters( PRINTER_ENUM_FAVORITE,
  1119. NULL,
  1120. 4,
  1121. (PVOID*)&pInfo4,
  1122. &cbInfo4,
  1123. &cPrinters )){
  1124. //
  1125. // failed! this could be either because the spooler has been stoped (or died
  1126. // accidentally) - in this case we need to delete all TRUE connects since they
  1127. // are no longer valid.
  1128. //
  1129. VDataSource* pDataSource;
  1130. TIter Iter;
  1131. for( DataSource_vIterInit( Iter ), Iter.vNext(); Iter.bValid(); ){
  1132. //
  1133. // Immediately after we convert the pointer to a pDataSource,
  1134. // increment the iter since we may delete pDataSource.
  1135. //
  1136. pDataSource = DataSource_pConvert( Iter );
  1137. Iter.vNext();
  1138. //
  1139. // if this is a true printer connection - delete it. local
  1140. // printers & masq printers will be taken care separately.
  1141. //
  1142. if( pDataSource->ConnectType() == VDataSource::kTrue ){
  1143. bNotifyAllClients( kFolderDelete, pDataSource->strDataSource( ), NULL );
  1144. pDataSource->DataSource_vDelinkSelf();
  1145. pDataSource->vDelete();
  1146. }
  1147. }
  1148. return;
  1149. }
  1150. //
  1151. // We need to add printers here that we didn't see before,
  1152. // and remove printer connections that no longer exist.
  1153. //
  1154. //
  1155. // HACK: use the Attributes field to indicate whether we've
  1156. // visited this printer before. Clear them out here.
  1157. //
  1158. UINT i;
  1159. for( i=0; i< cPrinters; ++i ){
  1160. pInfo4[i].Attributes = 0;
  1161. }
  1162. //
  1163. // O(N*N) search.
  1164. //
  1165. VDataSource* pDataSource;
  1166. TIter Iter;
  1167. for( DataSource_vIterInit( Iter ), Iter.vNext(); Iter.bValid(); ){
  1168. //
  1169. // Immediately after we convert the pointer to a pDataSource,
  1170. // increment the iter since we may delete pDataSource.
  1171. //
  1172. pDataSource = DataSource_pConvert( Iter );
  1173. Iter.vNext();
  1174. //
  1175. // Don't look for servers or masq printers, since they don't show
  1176. // up in EnumPrinters( INFO4 ) calls. They will be separately
  1177. // notified by the regular server handle.
  1178. //
  1179. if( pDataSource->ConnectType() != VDataSource::kTrue ){
  1180. continue;
  1181. }
  1182. //
  1183. // Search printers for a matching one.
  1184. //
  1185. for( i=0; i< cPrinters; ++i ){
  1186. if( !lstrcmpi( pInfo4[i].pPrinterName, pDataSource->strDataSource( ))){
  1187. pInfo4[i].Attributes = 1;
  1188. break;
  1189. }
  1190. }
  1191. if( i == cPrinters ){
  1192. //
  1193. // Printer connection no longer exists, remove and delete it.
  1194. //
  1195. DBGMSG( DBG_FOLDER,
  1196. ( "Folder.vConnectionNotifyChanged: SHChangeNotify: Delete "TSTR"\n",
  1197. (LPCTSTR)pDataSource->strDataSource( )));
  1198. TFolder::vCheckDeleteDefault( pDataSource->strDataSource( ));
  1199. bNotifyAllClients( kFolderDelete, pDataSource->strDataSource(), NULL );
  1200. pDataSource->DataSource_vDelinkSelf();
  1201. pDataSource->vDelete();
  1202. }
  1203. }
  1204. //
  1205. // Now walk through pInfo4 and check that all printers have
  1206. // been marked (Attributes set). Any that are not marked are
  1207. // new printer connections.
  1208. //
  1209. for( i=0; i< cPrinters; ++i ){
  1210. if( !pInfo4[i].Attributes ){
  1211. vAddDataSource( pInfo4[i].pPrinterName,
  1212. VDataSource::kTrue,
  1213. bNotify );
  1214. }
  1215. }
  1216. FreeMem( pInfo4 );
  1217. }
  1218. VOID
  1219. TFolder::
  1220. vRefZeroed(
  1221. VOID
  1222. )
  1223. /*++
  1224. Routine Description:
  1225. The reference count to this object has been decremented to zero,
  1226. so delete the object.
  1227. Note: only delete self if the object is valid, since during
  1228. construction, we bump up the refcount then drop it to zero when
  1229. an error occurs. This occurs in the constructor (since it's
  1230. cleaning up after itself) and we don't want to call the
  1231. destructor in there. The client is responsible for deleting
  1232. the object if it is invalid.
  1233. TFolder::ctr
  1234. (refcount is zero)
  1235. VDataSource::ctr (acquires reference to TFolder)
  1236. Failure in VDataSource::ctr
  1237. >> Discover VDataSource invalid; delete it:
  1238. VDataSource::dtr (releases reference to TFolder)
  1239. >> Here the refcount has dropped to zero, but we
  1240. >> don't want to delete it since it's not valid.
  1241. >> Don't set valid bit, since VDataSource failed.
  1242. Client deletes TFolder:
  1243. TFolder::dtr called.
  1244. Arguments:
  1245. Return Value:
  1246. --*/
  1247. {
  1248. if( bValid( ))
  1249. {
  1250. DBGMSG( DBG_FLDRINFO, ( "[TFolderList-DBG] TFolder OBJECT DESTROYED!!\n" ) );
  1251. delete this;
  1252. }
  1253. }
  1254. /********************************************************************
  1255. TConnectionNotify
  1256. Watch the registry key for changes to printer connections. When
  1257. we see a change, we call back to ConnectionNotifyClient. The
  1258. client then enumerates the printer connections and diffs for any
  1259. changes.
  1260. ********************************************************************/
  1261. TConnectionNotify::
  1262. TConnectionNotify(
  1263. TFolder* pFolder
  1264. ) : _pFolder(pFolder), _bRegistered(FALSE)
  1265. {
  1266. // create our watch event.
  1267. _shEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  1268. if (!_shEvent)
  1269. {
  1270. DBGMSG(DBG_WARN, ( "Connection.ctr: CreateEvent failed %d\n", GetLastError()));
  1271. return;
  1272. }
  1273. // Create the Printers/Connections key.
  1274. TStatus Status;
  1275. CAutoHandleHKEY shKeyPrinters;
  1276. Status DBGCHK = RegCreateKey(HKEY_CURRENT_USER, gszPrinters, &shKeyPrinters);
  1277. if (Status != ERROR_SUCCESS)
  1278. {
  1279. DBGMSG(DBG_WARN, ( "Connection.ctr: RegCreateKey failed %d\n", GetLastError()));
  1280. return;
  1281. }
  1282. Status DBGCHK = RegCreateKey(shKeyPrinters, gszConnections, &_shKeyConnections);
  1283. if (Status != ERROR_SUCCESS)
  1284. {
  1285. DBGMSG(DBG_WARN, ( "Connection.ctr: RegCreateKey failed %d\n", GetLastError()));
  1286. return;
  1287. }
  1288. if (!bSetupNotify())
  1289. {
  1290. goto Fail;
  1291. }
  1292. Status DBGCHK = _pFolder->pPrintLib()->pNotify()->sRegister(this);
  1293. if (Status != ERROR_SUCCESS)
  1294. {
  1295. goto Fail;
  1296. }
  1297. else
  1298. {
  1299. _bRegistered = TRUE;
  1300. }
  1301. // success
  1302. return;
  1303. Fail:
  1304. _shKeyConnections = NULL;
  1305. _shEvent = NULL;
  1306. }
  1307. TConnectionNotify::
  1308. ~TConnectionNotify(
  1309. VOID
  1310. )
  1311. {
  1312. // check to unregister...
  1313. if (_bRegistered)
  1314. {
  1315. _pFolder->pPrintLib()->pNotify()->sUnregister(this);
  1316. }
  1317. }
  1318. BOOL
  1319. TConnectionNotify::
  1320. bSetupNotify(
  1321. VOID
  1322. )
  1323. {
  1324. ASSERT(_shKeyConnections);
  1325. TStatus Status;
  1326. Status DBGCHK = RegNotifyChangeKeyValue(_shKeyConnections, TRUE, REG_NOTIFY_CHANGE_NAME, _shEvent, TRUE);
  1327. //
  1328. // Re-enumerate the printers.
  1329. //
  1330. return (Status == ERROR_SUCCESS);
  1331. }
  1332. /********************************************************************
  1333. Virtual definitions for MExecWork.
  1334. ********************************************************************/
  1335. HANDLE
  1336. TConnectionNotify::
  1337. hEvent(
  1338. VOID
  1339. ) const
  1340. {
  1341. ASSERT(_shEvent);
  1342. return(_shEvent);
  1343. }
  1344. VOID
  1345. TConnectionNotify::
  1346. vProcessNotifyWork(
  1347. TNotify* pNotify
  1348. )
  1349. {
  1350. UNREFERENCED_PARAMETER(pNotify);
  1351. //
  1352. // What do we do here if this fails?
  1353. //
  1354. TStatusB bStatus;
  1355. bStatus DBGCHK = bSetupNotify();
  1356. //
  1357. // Notify the client that something changed.
  1358. //
  1359. _pFolder->vConnectionNotifyChange(TRUE);
  1360. }
  1361. /********************************************************************
  1362. VDataSource internals.
  1363. We put in an ugly HACK to handle printer connections: we pretend
  1364. they are DataSources and keep a linked list of DataSources:
  1365. (NULL) local machine, with many printers.
  1366. \\server\share1 printer connection
  1367. \\server\share2 printer connection
  1368. This is needed when we show the print folder on the local machine:
  1369. we must show both printers on the local machine and printer
  1370. connections.
  1371. The distinction between the two is encapsulated in:
  1372. TDSServer - server view (many printers)
  1373. VDSConnection - single printer connection.
  1374. Another HACK: when we initially open up the print folder, we know
  1375. that the name of the printer connection (via INFO_4) so even
  1376. though we don't have info about the printer yet, we can create a
  1377. "fake" FOLDER_PRINTER_DATA structure that has the printer name, a
  1378. QUERYING token for the printer status, and attributes indicating
  1379. a printer connection.
  1380. ********************************************************************/
  1381. VDataSource*
  1382. VDataSource::
  1383. pNew(
  1384. TFolder* pFolder,
  1385. LPCTSTR pszDataSource,
  1386. CONNECT_TYPE ConnectType
  1387. )
  1388. {
  1389. VDataSource *pDataSource = NULL;
  1390. switch( ConnectType ){
  1391. case kServer:
  1392. pDataSource = new TDSServer( pFolder, pszDataSource );
  1393. break;
  1394. case kTrue:
  1395. pDataSource = new TDSCTrue( pFolder, pszDataSource );
  1396. break;
  1397. case kMasq:
  1398. pDataSource = new TDSCMasq( pFolder, pszDataSource );
  1399. break;
  1400. default:
  1401. SPLASSERT( FALSE );
  1402. break;
  1403. }
  1404. if( !VALID_PTR( pDataSource )){
  1405. delete pDataSource;
  1406. return NULL;
  1407. }
  1408. pDataSource->vIncRef();
  1409. return pDataSource;
  1410. }
  1411. VOID
  1412. VDataSource::
  1413. vDelete(
  1414. VOID
  1415. )
  1416. {
  1417. //
  1418. // Tell the printer to that the VDataSource (this) is no longer
  1419. // valid.
  1420. //
  1421. _pPrinter->vDelete();
  1422. //
  1423. // Matches pNew IncRef.
  1424. //
  1425. vDecRefDelete();
  1426. }
  1427. BOOL
  1428. VDataSource::
  1429. bSkipItem(
  1430. HANDLE hItem
  1431. ) const
  1432. /*++
  1433. Routine Description:
  1434. Determines whether a particular hItem should be enumerated back
  1435. to the VDataSource.
  1436. We want to skip items like masq printers in servers, since we
  1437. get bogus information about them. (To get real information,
  1438. we have to hit the connection directly using VDSConnection.)
  1439. Arguments:
  1440. hItem - Item to check.
  1441. Return Value:
  1442. TRUE - Skip this item from enumeration.
  1443. FALSE - Don't skip it.
  1444. --*/
  1445. {
  1446. SPLASSERT( _pFolder->CritSec().bInside( ));
  1447. LPCTSTR pszPrinter = pszGetPrinterName( hItem );
  1448. SPLASSERT( pszPrinter );
  1449. //
  1450. // If it's a server, then skip all masq cases, since
  1451. // they are handled by VDSConnection.
  1452. //
  1453. if( ConnectType() == kServer &&
  1454. TDataRPrinter::bSinglePrinter( pszPrinter )){
  1455. return TRUE;
  1456. }
  1457. //
  1458. // !! HACK !!
  1459. //
  1460. // We should fix the spooler so that it does not
  1461. // return non-shared printers remotely from EnumPrinters
  1462. // or the notification apis (unless the user is an
  1463. // admin of the server).
  1464. //
  1465. // If you're not an admin, and it's a remote server
  1466. // (strDataSource is not szNULL), then check if the
  1467. // printer is shared.
  1468. //
  1469. if( !_pFolder->bLocal( )){
  1470. if( !bAdministrator( )){
  1471. //
  1472. // If it's not shared then skip it.
  1473. //
  1474. DWORD dwAttributes = _pPrinter->pData()->GetInfo(
  1475. hItem,
  1476. TDataNPrinter::kIndexAttributes ).dwData;
  1477. if( !( dwAttributes & PRINTER_ATTRIBUTE_SHARED )){
  1478. return TRUE;
  1479. }
  1480. }
  1481. }
  1482. return FALSE;
  1483. }
  1484. COUNTB
  1485. VDataSource::
  1486. cbAllPrinterData(
  1487. VOID
  1488. ) const
  1489. /*++
  1490. Routine Description:
  1491. Determine the space we need to copy a the entire contents of
  1492. a VDataSource into PFOLDER_PRINTER_DATA structures, including
  1493. strings.
  1494. Arguments:
  1495. Return Value:
  1496. COUNTB - size in bytes (0 if no printers on machine).
  1497. --*/
  1498. {
  1499. SPLASSERT( _pFolder->CritSec().bInside( ));
  1500. //
  1501. // Walk through all printers on this DataSource and calculate
  1502. // size.
  1503. //
  1504. COUNTB cbSize = 0;
  1505. HANDLE hItem;
  1506. UINT i;
  1507. COUNT cItems = _lItems > 0 ? static_cast<COUNT>(_lItems) : 0;
  1508. for( i = cItems, hItem = NULL; i; --i ){
  1509. hItem = _pPrinter->pData()->GetNextItem( hItem );
  1510. SPLASSERT( hItem );
  1511. if( bSkipItem( hItem )){
  1512. continue;
  1513. }
  1514. cbSize += cbSinglePrinterData( hItem );
  1515. }
  1516. return cbSize;
  1517. }
  1518. COUNT
  1519. VDataSource::
  1520. cPackAllPrinterData(
  1521. IN OUT PBYTE& pBegin, CHANGE
  1522. IN OUT PBYTE& pEnd
  1523. ) const
  1524. /*++
  1525. Routine Description:
  1526. Pack all printers on this DataSource into the buffer pData. The
  1527. end of the buffer is marked by pEnd, (strings grow from the end).
  1528. Note: this assumes there is enough space to copy the data. Callee
  1529. must ensure this.
  1530. Arguments:
  1531. pBegin - Buffer to receive the data. Structures grow from the front,
  1532. strings are added to the end (at pEnd). On exit, this is moved
  1533. to the end of the structure (aligned so that the next pData in
  1534. the array can be added).
  1535. pEnd - End of the buffer. When this function returns, the end is
  1536. updated to the new 'end' of the buffer.
  1537. Return Value:
  1538. COUNT - number of structures copied.
  1539. --*/
  1540. {
  1541. SPLASSERT( _pFolder->CritSec().bInside( ));
  1542. UINT i;
  1543. HANDLE hItem = NULL;
  1544. COUNT cReturned;
  1545. COUNT cItems = _lItems > 0 ? static_cast<COUNT>(_lItems) : 0;
  1546. for( cReturned = 0, i = cItems; i; --i ){
  1547. hItem = _pPrinter->pData()->GetNextItem( hItem );
  1548. SPLASSERT( hItem );
  1549. if( bSkipItem( hItem )){
  1550. continue;
  1551. }
  1552. ++cReturned;
  1553. //
  1554. // pBegin and pEnd are updated here--they move from the
  1555. // outside in.
  1556. //
  1557. vPackSinglePrinterData( hItem,
  1558. pBegin,
  1559. pEnd );
  1560. }
  1561. SPLASSERT( pBegin <= pEnd );
  1562. return cReturned;
  1563. }
  1564. BOOL
  1565. VDataSource::
  1566. bGetPrinter(
  1567. IN LPCTSTR pszPrinter,
  1568. OUT PFOLDER_PRINTER_DATA pData,
  1569. IN DWORD cbData,
  1570. OUT PDWORD pcbNeeded
  1571. ) const
  1572. /*++
  1573. Routine Description:
  1574. Get a printer from a DataSource. Default implementation.
  1575. Arguments:
  1576. Return Value:
  1577. --*/
  1578. {
  1579. SPLASSERT( _pFolder->CritSec().bInside( ));
  1580. HANDLE hItem = hItemFindByName( pszPrinter );
  1581. if( !hItem ){
  1582. SetLastError( ERROR_INVALID_PRINTER_NAME );
  1583. return FALSE;
  1584. }
  1585. *pcbNeeded = cbSinglePrinterData( hItem );
  1586. if( *pcbNeeded > cbData ){
  1587. SetLastError( ERROR_INSUFFICIENT_BUFFER );
  1588. return FALSE;
  1589. }
  1590. PBYTE pBegin = (PBYTE)pData;
  1591. PBYTE pEnd = pBegin + cbData;
  1592. vPackSinglePrinterData( hItem, pBegin, pEnd );
  1593. SPLASSERT( (PBYTE)pData <= (PBYTE)pEnd );
  1594. return TRUE;
  1595. }
  1596. HANDLE
  1597. VDataSource::
  1598. hItemFindByName(
  1599. LPCTSTR pszPrinter
  1600. ) const
  1601. {
  1602. SPLASSERT( _pFolder->CritSec().bInside( ));
  1603. //
  1604. // Scan through all printers and look for a matching printer.
  1605. //
  1606. UINT i;
  1607. LPCTSTR pszTest;
  1608. HANDLE hItem = NULL;
  1609. COUNT cItems = _lItems > 0 ? static_cast<COUNT>(_lItems) : 0;
  1610. for( i = cItems; i; --i ){
  1611. hItem = _pPrinter->pData()->GetNextItem( hItem );
  1612. SPLASSERT( hItem );
  1613. pszTest = pszGetPrinterName( hItem );
  1614. SPLASSERT( pszTest );
  1615. if( !lstrcmpi( pszTest, pszPrinter )){
  1616. return hItem;
  1617. }
  1618. }
  1619. return NULL;
  1620. }
  1621. /********************************************************************
  1622. Internal VDataSource functions.
  1623. ********************************************************************/
  1624. VDataSource::
  1625. VDataSource(
  1626. TFolder* pFolder,
  1627. LPCTSTR pszDataSource,
  1628. CONNECT_TYPE ConnectType
  1629. ) : _pFolder( pFolder ),
  1630. _strDataSource( pszDataSource ),
  1631. _cIgnoreNotifications( 0 ),
  1632. _ConnectType( ConnectType ),
  1633. _lItems(0)
  1634. {
  1635. //
  1636. // Acquire a reference to pFolder.
  1637. //
  1638. pFolder->vIncRef();
  1639. if( !VALID_OBJ( _strDataSource )){
  1640. return;
  1641. }
  1642. _pPrinter = TPrinter::pNew( (VDataSource*)this,
  1643. pszDataSource,
  1644. 0 );
  1645. //
  1646. // _pPrinter is our valid check.
  1647. //
  1648. }
  1649. VDataSource::
  1650. ~VDataSource(
  1651. VOID
  1652. )
  1653. {
  1654. //
  1655. // Release the reference to the pFolder.
  1656. //
  1657. _pFolder->cDecRef();
  1658. }
  1659. COUNTB
  1660. VDataSource::
  1661. cbSinglePrinterData(
  1662. HANDLE hItem
  1663. ) const
  1664. /*++
  1665. Routine Description:
  1666. Determines the amount of space needed to store one printer
  1667. on a DataSource. (Includes space for struct and strings.)
  1668. Arguments:
  1669. hItem - Printer to size.
  1670. Return Value:
  1671. COUNTB - size in bytes.
  1672. --*/
  1673. {
  1674. SPLASSERT( _pFolder->CritSec().bInside( ));
  1675. //
  1676. // Add all structure elements.
  1677. //
  1678. COUNTB cbSize = sizeof( FOLDER_PRINTER_DATA );
  1679. COUNT cch;
  1680. cch = lstrlen( pszGetPrinterName( hItem ));
  1681. cch += lstrlen( pszGetCommentString( hItem ));
  1682. cch += lstrlen( pszGetLocationString( hItem ));
  1683. cch += lstrlen( pszGetModelString( hItem ));
  1684. cch += lstrlen( pszGetPortString( hItem ));
  1685. //
  1686. // Four string null terminators.
  1687. //
  1688. cch += 5;
  1689. cbSize += cch * sizeof( TCHAR );
  1690. DBGMSG( DBG_NONE, ( "DataSource.cbPrinterData size of %x is %d\n", hItem, cbSize ));
  1691. return cbSize;
  1692. }
  1693. LPCTSTR
  1694. VDataSource::
  1695. pszGetCommentString(
  1696. HANDLE hItem
  1697. ) const
  1698. /*++
  1699. Routine Description:
  1700. Default implementation for comment string; retrieves comment
  1701. from _pPrinter.
  1702. Arguments:
  1703. hItem - Find the comment about this item. If NULL, returns szNULL.
  1704. Return Value:
  1705. LPCTSTR - string if available, szNULL if no string.
  1706. The string is _not_ orphaned and should not be freed. The
  1707. lifetime is controlled by the hItem. (Lifetime of hItem is
  1708. controlled by the pFolder->_CritSec.)
  1709. --*/
  1710. {
  1711. SPLASSERT( _pFolder->CritSec().bInside( ));
  1712. LPCTSTR pszComment;
  1713. if( hItem ){
  1714. pszComment = _pPrinter->pData()->GetInfo(
  1715. hItem,
  1716. TDataNPrinter::kIndexComment ).pszData;
  1717. if( pszComment ){
  1718. return pszComment;
  1719. }
  1720. }
  1721. return gszNULL;
  1722. }
  1723. LPCTSTR
  1724. VDataSource::
  1725. pszGetLocationString(
  1726. HANDLE hItem
  1727. ) const
  1728. /*++
  1729. Routine Description:
  1730. Default implementation for location string; retrieves location
  1731. from _pPrinter.
  1732. Arguments:
  1733. hItem - Find the location about this item. If NULL, returns szNULL.
  1734. Return Value:
  1735. LPCTSTR - string if available, szNULL if no string.
  1736. The string is _not_ orphaned and should not be freed. The
  1737. lifetime is controlled by the hItem. (Lifetime of hItem is
  1738. controlled by the pFolder->_CritSec.)
  1739. --*/
  1740. {
  1741. SPLASSERT( _pFolder->CritSec().bInside( ));
  1742. LPCTSTR pszLocation = NULL;
  1743. if( hItem ){
  1744. pszLocation = _pPrinter->pData()->GetInfo(
  1745. hItem,
  1746. TDataNPrinter::kIndexLocation ).pszData;
  1747. if( pszLocation ){
  1748. return pszLocation;
  1749. }
  1750. }
  1751. return gszNULL;
  1752. }
  1753. LPCTSTR
  1754. VDataSource::
  1755. pszGetModelString(
  1756. HANDLE hItem
  1757. ) const
  1758. /*++
  1759. Routine Description:
  1760. Default implementation for model string; retrieves model
  1761. from _pPrinter.
  1762. Arguments:
  1763. hItem - Find the model about this item. If NULL, returns szNULL.
  1764. Return Value:
  1765. LPCTSTR - string if available, szNULL if no string.
  1766. The string is _not_ orphaned and should not be freed. The
  1767. lifetime is controlled by the hItem. (Lifetime of hItem is
  1768. controlled by the pFolder->_CritSec.)
  1769. --*/
  1770. {
  1771. SPLASSERT( _pFolder->CritSec().bInside( ));
  1772. LPCTSTR pszModel = NULL;
  1773. if( hItem ){
  1774. pszModel = _pPrinter->pData()->GetInfo(
  1775. hItem,
  1776. TDataNPrinter::kIndexModel ).pszData;
  1777. if( pszModel ){
  1778. return pszModel;
  1779. }
  1780. }
  1781. return gszNULL;
  1782. }
  1783. LPCTSTR
  1784. VDataSource::
  1785. pszGetPortString(
  1786. HANDLE hItem
  1787. ) const
  1788. /*++
  1789. Routine Description:
  1790. Default implementation for port string; retrieves port
  1791. from _pPrinter.
  1792. Arguments:
  1793. hItem - Find the port about this item. If NULL, returns szNULL.
  1794. Return Value:
  1795. LPCTSTR - string if available, szNULL if no string.
  1796. The string is _not_ orphaned and should not be freed. The
  1797. lifetime is controlled by the hItem. (Lifetime of hItem is
  1798. controlled by the pFolder->_CritSec.)
  1799. --*/
  1800. {
  1801. SPLASSERT( _pFolder->CritSec().bInside( ));
  1802. LPCTSTR pszPort = NULL;
  1803. if( hItem ){
  1804. pszPort = _pPrinter->pData()->GetInfo(
  1805. hItem,
  1806. TDataNPrinter::kIndexPort ).pszData;
  1807. if( pszPort ){
  1808. return pszPort;
  1809. }
  1810. }
  1811. return gszNULL;
  1812. }
  1813. VOID
  1814. VDataSource::
  1815. vPackSinglePrinterData(
  1816. IN HANDLE hItem,
  1817. IN OUT PBYTE& pBegin, CHANGE
  1818. IN OUT PBYTE& pEnd
  1819. ) const
  1820. /*++
  1821. Routine Description:
  1822. Pack the printer data into a buffer. We may want to put additional
  1823. structures in this structure, so we will build the strings at
  1824. the end of the buffer (pEnd).
  1825. Arguments:
  1826. hItem - Printer to find information.
  1827. pBegin - Buffer to place FOLDER_PRINTER_DATA.
  1828. pEnd - End of buffer.
  1829. Return Value:
  1830. New End of buffer.
  1831. --*/
  1832. {
  1833. SPLASSERT( _pFolder->CritSec().bInside( ));
  1834. PFOLDER_PRINTER_DATA pData = (PFOLDER_PRINTER_DATA)pBegin;
  1835. static DWORD gadwFolderPrinterDataOffsets[] = {
  1836. OFFSETOF( FOLDER_PRINTER_DATA, pName ),
  1837. OFFSETOF( FOLDER_PRINTER_DATA, pComment ),
  1838. OFFSETOF( FOLDER_PRINTER_DATA, pLocation ),
  1839. OFFSETOF( FOLDER_PRINTER_DATA, pDriverName ),
  1840. OFFSETOF( FOLDER_PRINTER_DATA, pPortName ),
  1841. (DWORD)-1
  1842. };
  1843. pData->cbSize = sizeof( FOLDER_PRINTER_DATA );
  1844. pData->pStatus = NULL;
  1845. LPCTSTR ppszSource[5];
  1846. ppszSource[0] = pszGetPrinterName( hItem );
  1847. ppszSource[1] = pszGetCommentString( hItem );
  1848. ppszSource[2] = pszGetLocationString( hItem );
  1849. ppszSource[3] = pszGetModelString( hItem );
  1850. ppszSource[4] = pszGetPortString( hItem );
  1851. pData->Status = _pPrinter->pData()->GetInfo(
  1852. hItem,
  1853. TDataNPrinter::kIndexStatus ).dwData;
  1854. pData->Attributes = _pPrinter->pData()->GetInfo(
  1855. hItem,
  1856. TDataNPrinter::kIndexAttributes ).dwData;
  1857. //
  1858. // Make sure the attributes have at least the right bits turned on.
  1859. //
  1860. switch( ConnectType( )){
  1861. case kMasq:
  1862. pData->Attributes |= PRINTER_ATTRIBUTE_LOCAL |
  1863. PRINTER_ATTRIBUTE_NETWORK;
  1864. break;
  1865. case kTrue:
  1866. pData->Attributes |= PRINTER_ATTRIBUTE_NETWORK;
  1867. pData->Attributes &= ~PRINTER_ATTRIBUTE_LOCAL;
  1868. break;
  1869. case kServer:
  1870. //
  1871. // The attribute bits are reported back correctly for
  1872. // local printers.
  1873. //
  1874. break;
  1875. }
  1876. pData->cJobs = _pPrinter->pData()->GetInfo(
  1877. hItem,
  1878. TDataNPrinter::kIndexCJobs ).dwData;
  1879. //
  1880. // If it's not connection based, then show the sharing icon
  1881. // over the network icon.
  1882. //
  1883. // However, if it's a connection, then show the network icon
  1884. // since nearly all connections must be shared in the first place
  1885. // (unless the client is an admin of the printers).
  1886. //
  1887. if( ConnectType() == kServer ){
  1888. if( pData->Attributes & PRINTER_ATTRIBUTE_SHARED ){
  1889. pData->Attributes &= ~PRINTER_ATTRIBUTE_NETWORK;
  1890. }
  1891. } else {
  1892. pData->Attributes |= PRINTER_ATTRIBUTE_NETWORK;
  1893. pData->Attributes &= ~PRINTER_ATTRIBUTE_SHARED;
  1894. }
  1895. pEnd = pPackStrings( ppszSource,
  1896. pBegin,
  1897. (PDWORD)gadwFolderPrinterDataOffsets,
  1898. pEnd );
  1899. pBegin += sizeof( FOLDER_PRINTER_DATA );
  1900. }
  1901. /********************************************************************
  1902. MPrinterClient virtual definitions.
  1903. ********************************************************************/
  1904. VOID
  1905. VDataSource::
  1906. vContainerChanged(
  1907. IN CONTAINER_CHANGE ContainerChange,
  1908. IN INFO Info
  1909. )
  1910. /*++
  1911. Routine Description:
  1912. The state of the container (hFolder) has changed. This is a
  1913. callback notification.
  1914. Arguments:
  1915. ContainerChange - Enumerated type indicating the type of change.
  1916. Info - Extra information about the change; exact data dependent
  1917. on the type of ContainerChange.
  1918. Return Value:
  1919. --*/
  1920. {
  1921. DBGMSG( DBG_FOLDER,
  1922. ( "DataSource.vContainerChanged: %x %x\n", ContainerChange, Info.dwData ));
  1923. //
  1924. // We must be inside the critical section since we are going to
  1925. // modify internal datastructures.
  1926. //
  1927. CCSLock::Locker CSL( _pFolder->CritSec( ) );
  1928. switch( ContainerChange ){
  1929. case kContainerClearItems:
  1930. vReloadItems();
  1931. _lItems = 0;
  1932. break;
  1933. case kContainerReloadItems:
  1934. vReloadItems();
  1935. _lItems = Info.dwData;
  1936. break;
  1937. case kContainerNewBlock:
  1938. _pPrinter->pData()->vBlockProcess();
  1939. break;
  1940. case kContainerRefreshComplete:
  1941. DBGMSG( DBG_FOLDER, ( "DataSource.vContainerChanged: refresh complete\n" ));
  1942. //
  1943. // The refresh (which may have been requested by the user)
  1944. // is now complete. Turn off the flag so that any new
  1945. // item changes will trigger notifications to the shell.
  1946. //
  1947. _cIgnoreNotifications = 0;
  1948. DBGMSG( DBG_FOLDER, ( "DataSource.vContainerChanged: Request => FALSE\n" ));
  1949. vRefreshComplete();
  1950. break;
  1951. default:
  1952. break;
  1953. }
  1954. }
  1955. BOOL
  1956. VDataSource::
  1957. bGetPrintLib(
  1958. TRefLock<TPrintLib> &refLock
  1959. ) const
  1960. {
  1961. ASSERT(_pFolder);
  1962. ASSERT(_pFolder->pPrintLib().pGet());
  1963. if (_pFolder && _pFolder->pPrintLib().pGet())
  1964. {
  1965. refLock.vAcquire(_pFolder->pPrintLib().pGet());
  1966. return TRUE;
  1967. }
  1968. return FALSE;
  1969. }
  1970. VOID
  1971. VDataSource::
  1972. vItemChanged(
  1973. IN ITEM_CHANGE ItemChange,
  1974. IN HITEM hItem,
  1975. IN INFO Info,
  1976. IN INFO InfoNew
  1977. )
  1978. /*++
  1979. Routine Description:
  1980. Callback from the notification to indicate that an item has changed.
  1981. (In this case a printer on a DataSource.)
  1982. Arguments:
  1983. ItemChange - Enumerated type indicating the type of change.
  1984. hItem - Handle to item that has changed. This may be passed to the
  1985. VData* interface to retrieve information about the item.
  1986. Info - Depends on the type of change; generally the old version
  1987. of the info.
  1988. InfoNew - Depends on the type of change; generally the new version
  1989. of the info.
  1990. Return Value:
  1991. --*/
  1992. {
  1993. //
  1994. // We should always be in the critical section here, since
  1995. // this is a callback from when we call pPrinter->pData->vBlockProcess().
  1996. //
  1997. SPLASSERT( _pFolder->CritSec().bInside( ));
  1998. FOLDER_NOTIFY_TYPE uEvent = kFolderNone;
  1999. LPCTSTR pszPrinter = NULL;
  2000. //
  2001. // Decrement the count since we received a notification. This is a
  2002. // hack to reduce the flicker: when a new object is added, you
  2003. // get notification for each column. Wait until the last one
  2004. // before repainting the entire line once.
  2005. //
  2006. if( _cIgnoreNotifications ){
  2007. --_cIgnoreNotifications;
  2008. }
  2009. switch( ItemChange ){
  2010. case kItemCreate:
  2011. //
  2012. // Send a change notification now.
  2013. //
  2014. DBGMSG( DBG_FOLDER,
  2015. ( "Folder.vItemChanged: Create %x %x %x\n",
  2016. hItem, Info.dwData, InfoNew.dwData ));
  2017. _lItems++;
  2018. pszPrinter = InfoNew.pszData;
  2019. uEvent = uItemCreate( pszPrinter, !_cIgnoreNotifications );
  2020. break;
  2021. case kItemDelete: {
  2022. //
  2023. // Send a change notification now.
  2024. //
  2025. //
  2026. // Masq hack:
  2027. //
  2028. // If a downlevel printer removed (both NETWORK and CONNECTION)
  2029. // then we need to remove the separate VDSConnection since
  2030. // the notifications isn't plugged into the server.
  2031. //
  2032. pszPrinter = pszGetPrinterName( hItem );
  2033. SPLASSERT( pszPrinter );
  2034. TFolder::vCheckDeleteDefault( pszPrinter );
  2035. if( TDataRPrinter::bSinglePrinter( pszPrinter )){
  2036. //
  2037. // Remove it.
  2038. //
  2039. _pFolder->vDeleteMasqDataSource( pszPrinter );
  2040. }
  2041. DBGMSG( DBG_FOLDER,
  2042. ( "Folder.vItemChanged: Delete %x %x %x\n",
  2043. hItem, Info.dwData, InfoNew.dwData ));
  2044. _lItems--;
  2045. //
  2046. // Reopen the printer if it was a conneciton. The printer may have
  2047. // been deleted and we must display the new new connection status.
  2048. //
  2049. if( bReopen() ){
  2050. uEvent = kFolderNone;
  2051. } else {
  2052. uEvent = kFolderDelete;
  2053. }
  2054. break;
  2055. }
  2056. case kItemName:
  2057. //
  2058. // Send a change notification now.
  2059. //
  2060. DBGMSG( DBG_FOLDER,
  2061. ( "Folder.vItemChanged: Name %x "TSTR" "TSTR"\n",
  2062. hItem, Info.pszData, InfoNew.pszData ));
  2063. //
  2064. // Reopen the printer if it was a conneciton. The printer may have
  2065. // been deleted and we must display the new new connection status.
  2066. //
  2067. if( bReopen() ){
  2068. uEvent = kFolderNone;
  2069. } else {
  2070. //
  2071. // Notify if we're not refreshing and we have a folder pidl.
  2072. //
  2073. if( !_cIgnoreNotifications )
  2074. {
  2075. DBGMSG( DBG_FOLDER,
  2076. ( "vItemChanged: SHChangeNotify: Rename "TSTR" -> "TSTR"\n",
  2077. Info.pszData, InfoNew.pszData ));
  2078. _pFolder->bNotifyAllClients( kFolderRename, Info.pszData, InfoNew.pszData );
  2079. }
  2080. }
  2081. break;
  2082. case kItemInfo:
  2083. DBGMSG( DBG_FOLDER,
  2084. ( "Folder.vItemChanged: Info %x %x %x\n",
  2085. hItem, Info.dwData, InfoNew.dwData ));
  2086. //
  2087. // Some information about the printer that affects _both_
  2088. // icon and report view has changed. Update all displays.
  2089. //
  2090. uEvent = kFolderUpdate;
  2091. break;
  2092. case kItemSecurity:
  2093. DBGMSG( DBG_FOLDER,
  2094. ( "Folder.vItemChanged: Security %x %x %x\n",
  2095. hItem, Info.dwData, InfoNew.dwData ));
  2096. //
  2097. // Some information about the printer that affects _both_
  2098. // icon and report view has changed. Update all displays.
  2099. //
  2100. uEvent = kFolderUpdateAll;
  2101. break;
  2102. case kItemAttributes:
  2103. DBGMSG( DBG_FOLDER,
  2104. ( "Folder.vItemChanged: Attribute %x %x %x\n",
  2105. hItem, Info.dwData, InfoNew.dwData ));
  2106. //
  2107. // Don't use SHCNE_UPDATEITEM. When the print folder is
  2108. // placed on the start menu, SHCNE_UPDATEITEMs cause it
  2109. // to refresh/re-enumerate everything. Since neither
  2110. // the name nor icon changed, we don't want the start menu
  2111. // to do anything.
  2112. //
  2113. // kItemAttributes indicates something about the printer
  2114. // has changed that does _not_ affect icon/list views. However,
  2115. // it does affect report (details) view.
  2116. //
  2117. uEvent = kFolderAttributes;
  2118. break;
  2119. default:
  2120. break;
  2121. }
  2122. //
  2123. // If we're not refreshing and there is an event, send a notification.
  2124. // We don't want to send a notification when we're refreshing since
  2125. // those items aren't in the client's datastore/listview. After
  2126. // the refresh, the client will refresh its display anyway.
  2127. //
  2128. if( !_cIgnoreNotifications && uEvent != kFolderNone )
  2129. {
  2130. //
  2131. // If no name set, use the one from the hItem.
  2132. //
  2133. if( !pszPrinter )
  2134. {
  2135. pszPrinter = pszGetPrinterName( hItem );
  2136. }
  2137. if( pszPrinter )
  2138. {
  2139. DBGMSG( DBG_FOLDER,
  2140. ( "vItemChanged: SHChangeNotify: Event %d, "TSTR"\n",
  2141. uEvent,
  2142. pszPrinter ));
  2143. _pFolder->bNotifyAllClients( uEvent, pszPrinter, NULL );
  2144. } else
  2145. {
  2146. DBGMSG( DBG_WARN,
  2147. ( "vItemChanged: Event %d, NULL printer event\n",
  2148. uEvent ));
  2149. }
  2150. }
  2151. }
  2152. VDataNotify*
  2153. VDataSource::
  2154. pNewNotify(
  2155. MDataClient* pDataClient
  2156. ) const
  2157. {
  2158. return new TDataNPrinter( pDataClient );
  2159. }
  2160. VDataRefresh*
  2161. VDataSource::
  2162. pNewRefresh(
  2163. MDataClient* pDataClient
  2164. ) const
  2165. {
  2166. return new TDataRPrinter( pDataClient );
  2167. }
  2168. VOID
  2169. VDataSource::
  2170. vRefZeroed(
  2171. VOID
  2172. )
  2173. {
  2174. if( bValid( )){
  2175. delete this;
  2176. }
  2177. }
  2178. /********************************************************************
  2179. TDSCTrue
  2180. ********************************************************************/
  2181. TDSCTrue::
  2182. TDSCTrue(
  2183. TFolder *pFolder,
  2184. LPCTSTR pszDataSource
  2185. ) : VDSConnection( pFolder, pszDataSource, kTrue )
  2186. {
  2187. }
  2188. TDSCTrue::
  2189. ~TDSCTrue(
  2190. VOID
  2191. )
  2192. {
  2193. }
  2194. /********************************************************************
  2195. TDSCMasq
  2196. ********************************************************************/
  2197. TDSCMasq::
  2198. TDSCMasq(
  2199. TFolder *pFolder,
  2200. LPCTSTR pszDataSource
  2201. ) : VDSConnection( pFolder, pszDataSource, kMasq )
  2202. {
  2203. }
  2204. TDSCMasq::
  2205. ~TDSCMasq(
  2206. VOID
  2207. )
  2208. {
  2209. }
  2210. /********************************************************************
  2211. General routines.
  2212. ********************************************************************/
  2213. PBYTE
  2214. pPackStrings(
  2215. IN LPCTSTR* ppszSource,
  2216. IN PBYTE pDest, CHANGE
  2217. IN PDWORD pdwDestOffsets,
  2218. IN PBYTE pEnd
  2219. )
  2220. /*++
  2221. Routine Description:
  2222. spoolss.dll's pack strings routine. Take a list of strings and
  2223. pack them at the end of the buffer.
  2224. For example, if you want to create an array of n structures that
  2225. has embedded strings, but they need to be in a contiguous block,
  2226. you need to put the structures first and the strings last.
  2227. This routine will start from the outside of the buffer, placing
  2228. the struct at the beginning, and the strings at the end:
  2229. FirstCall: >|struct1| free |struct1Strings|<
  2230. pDest->* pEnd->#
  2231. SecondCall: >|struct2| free |struct2strings|<
  2232. * #
  2233. This call assumes the buffer is large enough to hold the structure.
  2234. Arguments:
  2235. ppszSource - Array of string pointers. These strings need to be
  2236. copied into the end of pDest (pointed to be pEnd), and the
  2237. string pointer is put into pDest + pdwDestOffsets[*].
  2238. pDest - The strings pointers are placed at this address (plus
  2239. the pdwDestOffests[*]).
  2240. pdwOffsets - Indicates where the new strings should be stored
  2241. in pDest. DWORD array terminated by (DWORD)-1.
  2242. pEnd - Points to the end of the buffer pointed to by pDest.
  2243. Return Value:
  2244. New pEnd.
  2245. --*/
  2246. {
  2247. COUNTB cbStr;
  2248. pEnd = (PBYTE)WordAlignDown( pEnd );
  2249. PBYTE pEndOld = pEnd;
  2250. for( ;
  2251. *pdwDestOffsets != (DWORD)-1;
  2252. ppszSource++, pdwDestOffsets++ ){
  2253. if( *ppszSource ){
  2254. //
  2255. // String exists, copy it over.
  2256. //
  2257. cbStr = ( lstrlen(*ppszSource) + 1 ) * sizeof(TCHAR);
  2258. pEnd -= cbStr;
  2259. CopyMemory( pEnd, *ppszSource, cbStr);
  2260. *(LPTSTR *)(pDest + *pdwDestOffsets) = (LPTSTR)pEnd;
  2261. } else {
  2262. *(LPDWORD *)(pDest+ *pdwDestOffsets) = 0;
  2263. }
  2264. }
  2265. DBGMSG( 0,
  2266. ( "pPackStrings pDest %x, pEnd %x -> %x %d\n",
  2267. pDest, pEndOld, pEnd, (ULONG_PTR)(pEndOld - pEnd) ));
  2268. return pEnd;
  2269. }
  2270. /********************************************************************
  2271. TDSServer overrides.
  2272. ********************************************************************/
  2273. TDSServer::
  2274. TDSServer(
  2275. IN TFolder* pFolder,
  2276. IN LPCTSTR pszDataSource
  2277. ) : VDataSource( pFolder, pszDataSource, kServer ),
  2278. _bDiscardRefresh( FALSE )
  2279. {
  2280. }
  2281. BOOL
  2282. TDSServer::
  2283. bRefresh(
  2284. VOID
  2285. )
  2286. /*++
  2287. Routine Description:
  2288. Refresh all information about this TDSServer.
  2289. Arguments:
  2290. Return Value:
  2291. TRUE = success, FALSE = fail.
  2292. --*/
  2293. {
  2294. //
  2295. // HACK: The WinNT spooler doesn't support default printer
  2296. // notifications (or even the attribute bit). When we request
  2297. // a refresh, and we are the local print folder, look for
  2298. // store away the default printer so that if the user changes
  2299. // the default, we can send the old one an UPDATEITEM which
  2300. // will refresh it's icon.
  2301. //
  2302. if( _strDataSource.bEmpty( )){
  2303. vUpdateInternalDefaultPrinter();
  2304. }
  2305. //
  2306. // Sadly, SHCNE_UPDATEITEM of the container doesn't refresh
  2307. // the entire window. I suspect that it's doing a comparison
  2308. // of pidls to see if something anything new was added or
  2309. // deleted. This doesn't correctly handle the case where
  2310. // a printer is purged however (the pidls are identical, but
  2311. // something in details view has changed).
  2312. //
  2313. // There's some fSupportsIdentity code (DVM_SUPPORTSIDENTITY);
  2314. // this is used by the fstreex.c to see if the write timestamp
  2315. // has changed (probably to update the details view if anything
  2316. // is different), but we don't store this information in the
  2317. // printer pidl, so we can't support it.
  2318. //
  2319. // Best we can do is let the notifications through. Now
  2320. // with every refresh we get a huge number of notifications.
  2321. //
  2322. if( _bDiscardRefresh ){
  2323. _bDiscardRefresh = FALSE;
  2324. } else {
  2325. //
  2326. // The _cIgnoreNotifications is used to prevent us from sending
  2327. // notifications. Once the refresh is complete, we turn this
  2328. // flag off, then changing start calling SHChangeNotify again.
  2329. //
  2330. _cIgnoreNotifications = (COUNT)-1;
  2331. }
  2332. DBGMSG( DBG_FOLDER, ( "bFolderRefresh: %x Request => TRUE\n", this ));
  2333. return _pPrinter->bSyncRefresh();
  2334. }
  2335. BOOL
  2336. TDSServer::
  2337. bReopen(
  2338. VOID
  2339. )
  2340. /*++
  2341. Routine Description:
  2342. Reopen the data source.
  2343. Arguments:
  2344. Return Value:
  2345. TRUE = success, FALSE = fail.
  2346. --*/
  2347. {
  2348. return FALSE;
  2349. }
  2350. VOID
  2351. TDSServer::
  2352. vContainerChanged(
  2353. IN CONTAINER_CHANGE ContainerChange,
  2354. IN INFO Info
  2355. )
  2356. {
  2357. VDataSource::vContainerChanged( ContainerChange, Info );
  2358. switch( ContainerChange ){
  2359. case kContainerStateVar:
  2360. //
  2361. // Force a refresh of everything, since notification state
  2362. // was lost. This can occur if you purge the printer.
  2363. //
  2364. //
  2365. // Set bDiscardRefresh so that we know we need notifications
  2366. // when the new data comes in. This is required because even
  2367. // though we send a SHCNE_UPDATEITEM on the container (print
  2368. // folder), the shell doesn't repaint all items. It only
  2369. // repaints new/deleted items--not ones that have attribute
  2370. // changes.
  2371. //
  2372. _bDiscardRefresh = TRUE;
  2373. _pFolder->vRefreshUI();
  2374. break;
  2375. }
  2376. }
  2377. VOID
  2378. TDSServer::
  2379. vReloadItems(
  2380. VOID
  2381. )
  2382. /*++
  2383. Routine Description:
  2384. The printer is reloading all information about itself.
  2385. This should only be called from the bFolderRefresh synchronous
  2386. case (we'll never get an async refresh because we capture it
  2387. in TDSServer::vContainerChanged( kContainerStateVar, * ) and
  2388. convert it to a vRefreshUI above).
  2389. Arguments:
  2390. Return Value:
  2391. --*/
  2392. {
  2393. }
  2394. VOID
  2395. TDSServer::
  2396. vRefreshComplete(
  2397. VOID
  2398. ) const
  2399. /*++
  2400. Routine Description:
  2401. Something about the server has changed, so we need to refresh
  2402. the entire print folder (since we can have muliple printers on
  2403. one TDSServer).
  2404. Arguments:
  2405. Return Value:
  2406. --*/
  2407. {
  2408. //
  2409. // If this is a server, tell the folder to validate all it's
  2410. // masq printers. We need to do this because at the same moment
  2411. // there's a refresh, one of the masq printers could have been
  2412. // deleted. Since we just refreshed, we won't get any notifications
  2413. // about it.
  2414. //
  2415. // Alternatively, at the beginning of a refresh we could
  2416. // delete all masq printers. Then the refresh will recreate
  2417. // the currently existing printers. However, this will force
  2418. // us to close and reopen the printers--an expensive operation.
  2419. //
  2420. _pFolder->vRevalidateMasqPrinters();
  2421. }
  2422. FOLDER_NOTIFY_TYPE
  2423. TDSServer::
  2424. uItemCreate(
  2425. IN LPCTSTR pszPrinter,
  2426. IN BOOL bNotify
  2427. )
  2428. /*++
  2429. Routine Description:
  2430. If this is a server and it's the single printer
  2431. (\\server\share) name, then create a masq printer.
  2432. We do this because the spooler doesn't give us correct
  2433. information about masq printers--we need to go directly
  2434. to the source by creating a VDSConnection.
  2435. We can't rely on the attribute bits
  2436. (PRINTER_ATTRIBUTE_{LOCAL|NETWORK})
  2437. being set since win9x connections don't set both
  2438. of them.
  2439. Arguments:
  2440. Return Value:
  2441. --*/
  2442. {
  2443. if( TDataRPrinter::bSinglePrinter( pszPrinter )){
  2444. //
  2445. // Prepend the data source to talk to printer gateway if
  2446. // we are in the remote printers folder.
  2447. //
  2448. TString strPrinter( strDataSource() );
  2449. if( !strPrinter.bEmpty() ){
  2450. strPrinter.bCat( gszWack );
  2451. }
  2452. strPrinter.bCat( pszPrinter );
  2453. //
  2454. // Add the masq printer. Also send a notification
  2455. // if we're not refreshing.
  2456. //
  2457. _pFolder->vAddMasqDataSource( strPrinter,
  2458. bNotify );
  2459. //
  2460. // Don't send a notification since vAddMasqDataSource will.
  2461. //
  2462. return kFolderNone;
  2463. }
  2464. return kFolderCreate;
  2465. }
  2466. LPCTSTR
  2467. TDSServer::
  2468. pszGetPrinterName(
  2469. IN HANDLE hItem
  2470. ) const
  2471. {
  2472. LPCTSTR pszName;
  2473. pszName = _pPrinter->pData()->GetInfo(
  2474. hItem,
  2475. TDataNPrinter::kIndexPrinterName ).pszData;
  2476. if( !pszName ){
  2477. pszName = gszNULL;
  2478. }
  2479. return pszName;
  2480. }
  2481. BOOL
  2482. TDSServer::
  2483. bAdministrator(
  2484. VOID
  2485. ) const
  2486. {
  2487. return _pPrinter->dwAccess() == SERVER_ALL_ACCESS;
  2488. }
  2489. BOOL
  2490. TDSServer::
  2491. bGetPrinter(
  2492. IN LPCTSTR pszPrinter,
  2493. OUT PFOLDER_PRINTER_DATA pData,
  2494. IN DWORD cbData,
  2495. OUT PDWORD pcbNeeded
  2496. ) const
  2497. /*++
  2498. Routine Description:
  2499. Override the default implementation so that we can strip
  2500. off the server prefix if it has one.
  2501. Arguments:
  2502. Return Value:
  2503. --*/
  2504. {
  2505. UINT cchDataSource = lstrlen( _strDataSource );
  2506. //
  2507. // Check if there is a server prefix. If so, strip it off since
  2508. // our stored names don't have prefixes.
  2509. //
  2510. if( cchDataSource &&
  2511. !_tcsnicmp( pszPrinter,
  2512. _strDataSource,
  2513. cchDataSource ) &&
  2514. pszPrinter[cchDataSource] == TEXT( '\\' )){
  2515. //
  2516. // Skip the prefix: "\\DataSource\printer" -> "printer."
  2517. // Also skip the '\' separator.
  2518. //
  2519. pszPrinter += cchDataSource + 1;
  2520. }
  2521. //
  2522. // Masq HACK.
  2523. //
  2524. // If this is a server and it's a masq printer, then don't bother
  2525. // searching for it, since there's a VDataSource that has more
  2526. // up to date information. The spooler does not return accurate
  2527. // information for masq printer in a server handle, so we created
  2528. // a separate VDSConnection for the masq printer.
  2529. //
  2530. if( TDataRPrinter::bSinglePrinter( pszPrinter )){
  2531. SetLastError( ERROR_INVALID_PRINTER_NAME );
  2532. return FALSE;
  2533. }
  2534. return VDataSource::bGetPrinter( pszPrinter,
  2535. pData,
  2536. cbData,
  2537. pcbNeeded );
  2538. }
  2539. /********************************************************************
  2540. VDSConnection.
  2541. ********************************************************************/
  2542. VDSConnection::
  2543. VDSConnection(
  2544. TFolder* pFolder,
  2545. LPCTSTR pszDataSource,
  2546. CONNECT_TYPE ConnectType
  2547. ) : VDataSource( pFolder, pszDataSource, ConnectType ),
  2548. _ConnectStatus( kConnectStatusOpen )
  2549. {
  2550. SPLASSERT( ConnectType == VDataSource::kTrue ||
  2551. ConnectType == VDataSource::kMasq );
  2552. //
  2553. // These global strings should be initialized
  2554. // already.
  2555. //
  2556. SPLASSERT( gpstrConnectStatusOpen );
  2557. }
  2558. BOOL
  2559. VDSConnection::
  2560. bStaticInitShutdown(
  2561. BOOL bShutdown
  2562. )
  2563. {
  2564. if( bShutdown )
  2565. {
  2566. delete gpstrConnectStatusOpen;
  2567. delete gpstrConnectStatusOpenError;
  2568. delete gpstrConnectStatusInvalidPrinterName;
  2569. delete gpstrConnectStatusAccessDenied;
  2570. return TRUE;
  2571. }
  2572. else
  2573. {
  2574. TStatusB bStatus;
  2575. bStatus DBGNOCHK = FALSE;
  2576. gpstrConnectStatusOpen = new TString;
  2577. gpstrConnectStatusOpenError = new TString;
  2578. gpstrConnectStatusInvalidPrinterName = new TString;
  2579. gpstrConnectStatusAccessDenied = new TString;
  2580. if( gpstrConnectStatusOpen &&
  2581. gpstrConnectStatusOpenError &&
  2582. gpstrConnectStatusInvalidPrinterName &&
  2583. gpstrConnectStatusAccessDenied )
  2584. {
  2585. bStatus DBGCHK = gpstrConnectStatusOpen->bLoadString(
  2586. ghInst,
  2587. IDS_SB_OPEN );
  2588. bStatus DBGCHK = bStatus && gpstrConnectStatusOpenError->bLoadString(
  2589. ghInst,
  2590. IDS_SB_OPEN_ERROR );
  2591. bStatus DBGCHK = bStatus && gpstrConnectStatusInvalidPrinterName->bLoadString(
  2592. ghInst,
  2593. IDS_SB_INVALID_PRINTER_NAME );
  2594. bStatus DBGCHK = bStatus && gpstrConnectStatusAccessDenied->bLoadString(
  2595. ghInst,
  2596. IDS_SB_ACCESS_DENIED );
  2597. }
  2598. if( !bStatus )
  2599. {
  2600. delete gpstrConnectStatusOpen;
  2601. delete gpstrConnectStatusOpenError;
  2602. delete gpstrConnectStatusInvalidPrinterName;
  2603. delete gpstrConnectStatusAccessDenied;
  2604. gpstrConnectStatusOpen =
  2605. gpstrConnectStatusOpenError =
  2606. gpstrConnectStatusInvalidPrinterName =
  2607. gpstrConnectStatusAccessDenied = NULL;
  2608. }
  2609. return bStatus;
  2610. }
  2611. }
  2612. BOOL
  2613. VDSConnection::
  2614. bRefresh(
  2615. VOID
  2616. )
  2617. /*++
  2618. Routine Description:
  2619. Refresh all information about this TDSServer.
  2620. Arguments:
  2621. Return Value:
  2622. TRUE = success, FALSE = fail.
  2623. --*/
  2624. {
  2625. //
  2626. // Since it's a single printer, make it asynchronous. This puts
  2627. // the "querying" status on the connection and allows the shell
  2628. // to continue processing UI messages. We can do this for
  2629. // connection since we know the name of the printer.
  2630. //
  2631. BOOL bResult = FALSE;
  2632. if( _pFolder->pPrintLib()->bJobAdd( _pPrinter, TPrinter::kExecRefreshAll ))
  2633. {
  2634. //
  2635. // HACK: on a refresh, ignore all notifications until the
  2636. // last one, when the refresh is complete.
  2637. //
  2638. // Since this refresh completes asynchronously, we must
  2639. // still send a notification on the very last change.
  2640. //
  2641. _cIgnoreNotifications = TDataNPrinter::kFieldTableSize;
  2642. bResult = TRUE;
  2643. }
  2644. return bResult;
  2645. }
  2646. BOOL
  2647. VDSConnection::
  2648. bReopen(
  2649. VOID
  2650. )
  2651. /*++
  2652. Routine Description:
  2653. Reopen the data source.
  2654. Arguments:
  2655. Return Value:
  2656. TRUE = success, FALSE = fail.
  2657. --*/
  2658. {
  2659. if( _ConnectType == kTrue ){
  2660. CCSLock::Locker CSL( _pFolder->CritSec( ));
  2661. //
  2662. // Since it's a single printer, make it asynchronous. This puts
  2663. // the "querying" status on the connection and allows the shell
  2664. // to continue processing UI messages. We can do this for
  2665. // connection since we know the name of the printer.
  2666. //
  2667. if( _pFolder->pPrintLib()->bJobAdd( _pPrinter, TPrinter::kExecReopen )){
  2668. return TRUE;
  2669. }
  2670. }
  2671. return FALSE;
  2672. }
  2673. VOID
  2674. VDSConnection::
  2675. vReloadItems(
  2676. VOID
  2677. )
  2678. /*++
  2679. Routine Description:
  2680. The printer is reloading all information about itself.
  2681. Ignore notifications; we'll refresh the entire line when the refresh
  2682. has completed.
  2683. Arguments:
  2684. Return Value:
  2685. --*/
  2686. {
  2687. _cIgnoreNotifications = (COUNT)-1;
  2688. }
  2689. VOID
  2690. VDSConnection::
  2691. vRefreshComplete(
  2692. VOID
  2693. ) const
  2694. /*++
  2695. Routine Description:
  2696. The data about the connection has been refreshed. Update
  2697. the item in the window.
  2698. Arguments:
  2699. Return Value:
  2700. --*/
  2701. {
  2702. DBGMSG( DBG_FOLDER,
  2703. ( "DSConnection.vRefreshComplete: SHChangeNotify: Update "TSTR"\n",
  2704. (LPCTSTR)_strDataSource ));
  2705. _pFolder->bNotifyAllClients( kFolderAttributes, _strDataSource, NULL );
  2706. }
  2707. BOOL
  2708. VDSConnection::
  2709. bUseFakePrinterData(
  2710. VOID
  2711. ) const
  2712. {
  2713. return (_lItems <= 0) ||
  2714. _ConnectStatus == kConnectStatusInvalidPrinterName ||
  2715. _ConnectStatus == kConnectStatusAccessDenied ||
  2716. _ConnectStatus == kConnectStatusOpen ||
  2717. _ConnectStatus == kConnectStatusOpenError ||
  2718. _ConnectStatus == kConnectStatusInitialize;
  2719. }
  2720. COUNTB
  2721. VDSConnection::
  2722. cbSinglePrinterData(
  2723. HANDLE hItem
  2724. ) const
  2725. {
  2726. //
  2727. // HACK for the single printer (printer connection) case.
  2728. //
  2729. if( bUseFakePrinterData( ))
  2730. {
  2731. return sizeof( FOLDER_PRINTER_DATA ) +
  2732. ( lstrlen( _strDataSource ) + 1) * sizeof( TCHAR );
  2733. }
  2734. return VDataSource::cbSinglePrinterData( hItem );
  2735. }
  2736. VOID
  2737. VDSConnection::
  2738. vPackSinglePrinterData(
  2739. IN HANDLE hItem,
  2740. IN OUT PBYTE& pBegin, CHANGE
  2741. IN OUT PBYTE& pEnd
  2742. ) const
  2743. {
  2744. //
  2745. // HACK for the single printer (printer connection) case.
  2746. //
  2747. if( bUseFakePrinterData( ))
  2748. {
  2749. PFOLDER_PRINTER_DATA pData = (PFOLDER_PRINTER_DATA)pBegin;
  2750. //
  2751. // Put the string in the right place, and adjust pEnd so
  2752. // it points to the new place.
  2753. //
  2754. pEnd -= ( lstrlen( _strDataSource ) + 1) * sizeof( TCHAR );
  2755. pBegin += sizeof( FOLDER_PRINTER_DATA );
  2756. lstrcpy( (LPTSTR)pEnd, _strDataSource);
  2757. //
  2758. // Create a fake structure so the shell can display
  2759. // "querying."
  2760. //
  2761. pData->Status = 0;
  2762. if( ConnectType() == kMasq ){
  2763. //
  2764. // Set the attribute bit as both NETWORK and LOCAL
  2765. // so that it appears as a network icon, but when
  2766. // we delete it, we use DeletePrinter instead of
  2767. // DeletePrinterConnection.
  2768. //
  2769. pData->Attributes = PRINTER_ATTRIBUTE_NETWORK |
  2770. PRINTER_ATTRIBUTE_LOCAL;
  2771. } else {
  2772. pData->Attributes = PRINTER_ATTRIBUTE_NETWORK;
  2773. }
  2774. pData->pComment = NULL;
  2775. pData->cbSize = sizeof( FOLDER_PRINTER_DATA );
  2776. pData->pLocation = NULL;
  2777. pData->pDriverName = NULL;
  2778. pData->pPortName = NULL;
  2779. pData->pStatus = pszGetStatusString( NULL );
  2780. pData->cJobs = 0;
  2781. pData->pName = (LPCTSTR)pEnd;
  2782. SPLASSERT( pBegin <= pEnd );
  2783. }
  2784. else
  2785. {
  2786. //
  2787. // This is the normal case.
  2788. //
  2789. VDataSource::vPackSinglePrinterData( hItem, pBegin, pEnd );
  2790. }
  2791. }
  2792. COUNTB
  2793. VDSConnection::
  2794. cbAllPrinterData(
  2795. VOID
  2796. ) const
  2797. {
  2798. HANDLE hItem = NULL;
  2799. if( !bUseFakePrinterData( ))
  2800. {
  2801. hItem = _pPrinter->pData()->GetNextItem( NULL );
  2802. }
  2803. return cbSinglePrinterData( hItem );
  2804. }
  2805. COUNT
  2806. VDSConnection::
  2807. cPackAllPrinterData(
  2808. IN OUT PBYTE& pBegin, CHANGE
  2809. IN OUT PBYTE& pEnd
  2810. ) const
  2811. {
  2812. HANDLE hItem = NULL;
  2813. if( !bUseFakePrinterData( ))
  2814. {
  2815. hItem = _pPrinter->pData()->GetNextItem( NULL );
  2816. }
  2817. vPackSinglePrinterData( hItem,
  2818. pBegin,
  2819. pEnd );
  2820. return 1;
  2821. }
  2822. LPCTSTR
  2823. VDSConnection::
  2824. pszGetCommentString(
  2825. HANDLE hItem
  2826. ) const
  2827. /*++
  2828. Routine Description:
  2829. Based on the current connection status, return a string.
  2830. Arguments:
  2831. hItem - Item to get the comment about.
  2832. Return Value:
  2833. LPCTSTR - Comment string (szNULL if no string).
  2834. This string is _not_ orphaned, and should not be freed by callee.
  2835. --*/
  2836. {
  2837. SPLASSERT( _pFolder->CritSec().bInside( ));
  2838. //
  2839. // Get comment from the default implementation.
  2840. //
  2841. return VDataSource::pszGetCommentString( hItem );
  2842. }
  2843. LPCTSTR
  2844. VDSConnection::
  2845. pszGetStatusString(
  2846. HANDLE hItem
  2847. ) const
  2848. /*++
  2849. Routine Description:
  2850. Based on the current connection status, return a string.
  2851. Arguments:
  2852. hItem - Item to get the comment about.
  2853. Return Value:
  2854. LPCTSTR - Comment string (szNULL if no string).
  2855. This string is _not_ orphaned, and should not be freed by callee.
  2856. --*/
  2857. {
  2858. SPLASSERT( _pFolder->CritSec().bInside( ));
  2859. LPCTSTR pszConnect = NULL;
  2860. //
  2861. // Show an meaningful string. Note: these strings
  2862. // do not point within the pFolderPrinterData: they
  2863. // are simply pointers to "global" data.
  2864. //
  2865. switch( _ConnectStatus ){
  2866. case kConnectStatusInvalidPrinterName:
  2867. pszConnect = *gpstrConnectStatusInvalidPrinterName;
  2868. break;
  2869. case kConnectStatusAccessDenied:
  2870. pszConnect = *gpstrConnectStatusAccessDenied;
  2871. break;
  2872. case kConnectStatusOpen:
  2873. pszConnect = *gpstrConnectStatusOpen;
  2874. break;
  2875. case kConnectStatusOpenError:
  2876. pszConnect = *gpstrConnectStatusOpenError;
  2877. break;
  2878. default:
  2879. DBGMSG( DBG_FOLDER, ("Unknown connection status found %d.\n", _ConnectStatus ) );
  2880. break;
  2881. }
  2882. return pszConnect;
  2883. }
  2884. BOOL
  2885. VDSConnection::
  2886. bGetPrinter(
  2887. IN LPCTSTR pszPrinter,
  2888. OUT PFOLDER_PRINTER_DATA pData,
  2889. IN DWORD cbData,
  2890. OUT PDWORD pcbNeeded
  2891. ) const
  2892. {
  2893. if( _lItems <= 0 && !lstrcmpi( pszPrinter, _strDataSource)){
  2894. //
  2895. // Get the size of the single fake printer. Even though
  2896. // this function returns the space for all printers on the
  2897. // DataSource, since there aren't any, it will return the size
  2898. // for the fake printer.
  2899. //
  2900. *pcbNeeded = cbAllPrinterData();
  2901. if( *pcbNeeded > cbData ){
  2902. SetLastError( ERROR_INSUFFICIENT_BUFFER );
  2903. return FALSE;
  2904. }
  2905. PBYTE pBegin = (PBYTE)pData;
  2906. PBYTE pEnd = pBegin + cbData;
  2907. COUNT cFakeItems = cPackAllPrinterData( pBegin, pEnd );
  2908. //
  2909. // We are creating a "fake" printer that has a status
  2910. // of queryring. Make sure only 1 is returned.
  2911. //
  2912. SPLASSERT( cFakeItems == 1 );
  2913. SPLASSERT( (PBYTE)pData <= (PBYTE)pEnd );
  2914. return TRUE;
  2915. }
  2916. //
  2917. // There actually is printer data there; use it.
  2918. //
  2919. return VDataSource::bGetPrinter( pszPrinter,
  2920. pData,
  2921. cbData,
  2922. pcbNeeded );
  2923. }
  2924. FOLDER_NOTIFY_TYPE
  2925. VDSConnection::
  2926. uItemCreate(
  2927. LPCTSTR pszPrinter,
  2928. BOOL bNotify
  2929. )
  2930. {
  2931. return kFolderUpdate;
  2932. }
  2933. LPCTSTR
  2934. VDSConnection::
  2935. pszGetPrinterName(
  2936. HANDLE hItem
  2937. ) const
  2938. {
  2939. UNREFERENCED_PARAMETER( hItem );
  2940. return _strDataSource;
  2941. }
  2942. VOID
  2943. VDSConnection::
  2944. vContainerChanged(
  2945. IN CONTAINER_CHANGE ContainerChange,
  2946. IN INFO Info
  2947. )
  2948. /*++
  2949. Routine Description:
  2950. Something about the printer connection container has changed.
  2951. (The container only holds the 1 printer connection, either a
  2952. masq or true connect.)
  2953. Arguments:
  2954. ContainerChange - Type of change.
  2955. Info - Information about the change.
  2956. Return Value:
  2957. --*/
  2958. {
  2959. VDataSource::vContainerChanged( ContainerChange, Info );
  2960. switch( ContainerChange ){
  2961. case kContainerConnectStatus:
  2962. vUpdateConnectStatus((CONNECT_STATUS)Info.dwData);
  2963. break;
  2964. case kContainerStateVar:
  2965. //
  2966. // The printer internal state has changed--probably
  2967. // a refresh or something asynchronous failed.
  2968. // Request a state change.
  2969. //
  2970. _pFolder->pPrintLib()->bJobAdd( _pPrinter, Info.dwData );
  2971. break;
  2972. }
  2973. }
  2974. VOID
  2975. VDSConnection::
  2976. vUpdateConnectStatus(
  2977. IN CONNECT_STATUS ConnectStatusNew
  2978. )
  2979. /*++
  2980. Routine Description:
  2981. The connection status of a printer has changed. Update
  2982. the comment string if this is an "interesting" state.
  2983. Note: connection state has nothing to do with the "real" state
  2984. of the printer--it's just this particular connection.
  2985. Arguments:
  2986. ConnectStatusNew - New connection status.
  2987. Return Value:
  2988. --*/
  2989. {
  2990. //
  2991. // Change the connection status (stored in comment) only
  2992. // for those strings that are interesting.
  2993. //
  2994. switch( ConnectStatusNew ){
  2995. case kConnectStatusNull:
  2996. case kConnectStatusInvalidPrinterName:
  2997. case kConnectStatusAccessDenied:
  2998. case kConnectStatusOpen:
  2999. case kConnectStatusOpenError:
  3000. if( _ConnectStatus != ConnectStatusNew ){
  3001. TString strPrinter;
  3002. {
  3003. //
  3004. // We must be inside the critical section, since changing
  3005. // the ConnectStatus changes the connection string.
  3006. // We could be in VDataSource::bGetPrinter, which assumes
  3007. // that the data doesn't change while inside the CritSec.
  3008. //
  3009. CCSLock::Locker CSL( _pFolder->CritSec( ));
  3010. _ConnectStatus = ConnectStatusNew;
  3011. LPCTSTR pszPrinter = pszGetPrinterName( NULL );
  3012. DBGMSG( DBG_FOLDER,
  3013. ( "DSConnection.vContainerChanged: SHChangeNotify: Update "TSTR"\n",
  3014. pszPrinter ));
  3015. strPrinter.bUpdate( pszPrinter );
  3016. }
  3017. if( VALID_OBJ( strPrinter ) ){
  3018. //
  3019. // Don't use SHCNE_UPDATEITEM. When the print folder is
  3020. // placed on the start menu, SHCNE_UPDATEITEMs cause it
  3021. // to refresh/re-enumerate everything. Since neither
  3022. // the name nor icon changed, we don't want the start menu
  3023. // to do anything.
  3024. //
  3025. _pFolder->bNotifyAllClients( kFolderAttributes, strPrinter, NULL );
  3026. }
  3027. }
  3028. break;
  3029. default:
  3030. //
  3031. // Other connect status messages will come through here,
  3032. // be we don't want to update the display.
  3033. //
  3034. break;
  3035. }
  3036. }
  3037. BOOL
  3038. VDSConnection::
  3039. bAdministrator(
  3040. VOID
  3041. ) const
  3042. {
  3043. return _pPrinter->dwAccess() == PRINTER_ALL_ACCESS;
  3044. }
  3045. /********************************************************************
  3046. TFolderList class
  3047. ********************************************************************/
  3048. TFolderList *TFolderList::gpFolders = NULL;
  3049. CCSLock *TFolderList::gpFolderLock = NULL;
  3050. TFolderList::
  3051. TFolderList( )
  3052. /*++
  3053. Routine Description:
  3054. Construct the global folder list object.
  3055. Arguments:
  3056. Return Value:
  3057. --*/
  3058. {
  3059. // only one instance of this class can be created (TFolderList::gpFolders)
  3060. SPLASSERT( NULL == gpFolders );
  3061. }
  3062. TFolderList::
  3063. ~TFolderList( )
  3064. /*++
  3065. Routine Description:
  3066. Destruct the global folder list object.
  3067. Arguments:
  3068. Return Value:
  3069. --*/
  3070. {
  3071. // only one instance of this class can be created (TFolderList::gpFolders)
  3072. SPLASSERT( NULL != gpFolders );
  3073. }
  3074. HRESULT
  3075. TFolderList::
  3076. RegisterDataSource(
  3077. IN LPCTSTR pszDataSource,
  3078. IN IFolderNotify *pClientNotify,
  3079. OUT LPHANDLE phFolder,
  3080. OUT PBOOL pbAdministrator OPTIONAL
  3081. )
  3082. /*++
  3083. Routine Description:
  3084. Registers a client for notification in the corresponding
  3085. data source.
  3086. Note: pClientNotify can't be NULL
  3087. Arguments:
  3088. pszDataSource - Data source name
  3089. pClientNotify - Client feedback connection point
  3090. phFolder - Where to place a handle of the
  3091. folder object for further access
  3092. Return Value:
  3093. S_OK - Success
  3094. E_XXX - Failure (for some reason)
  3095. --*/
  3096. {
  3097. BOOL bRefresh = FALSE;
  3098. //
  3099. // Lock the global folder list
  3100. //
  3101. CCSLock::Locker CSL( *TFolderList::gpFolderLock );
  3102. HRESULT hr = E_FAIL;
  3103. TFolder *pResult = NULL;
  3104. if( !gpFolders )
  3105. {
  3106. gpFolders = new TFolderList;
  3107. //
  3108. // Check the folder list to be valid
  3109. //
  3110. if( !VALID_PTR( gpFolders ))
  3111. {
  3112. hr = E_OUTOFMEMORY;
  3113. delete gpFolders;
  3114. gpFolders = NULL;
  3115. }
  3116. }
  3117. if( gpFolders )
  3118. {
  3119. //
  3120. // Lookup for the desired data source.
  3121. // Returns S_OK if found and S_FALSE if not.
  3122. //
  3123. hr = pLookupFolder(pszDataSource, &pResult);
  3124. }
  3125. if( gpFolders && S_OK != hr )
  3126. {
  3127. //
  3128. // Create new folder object.
  3129. //
  3130. pResult = new TFolder( pszDataSource );
  3131. //
  3132. // Check if the folder object was created successfully.
  3133. //
  3134. if( VALID_PTR( pResult ))
  3135. {
  3136. //
  3137. // Remember this folder is new and should be added
  3138. // to the folders list ...
  3139. //
  3140. DBGMSG( DBG_FLDRINFO, ( "[TFolderList-DBG] TFolder OBJECT CREATED!!\n" ) );
  3141. //
  3142. // Acquire a reference to the TFolder.
  3143. //
  3144. pResult->vIncRef();
  3145. //
  3146. // This folder has just been successfully created
  3147. // add has clients hooked up, so add it to the
  3148. // folder list
  3149. //
  3150. gpFolders->Folders_vAppend( pResult );
  3151. //
  3152. // This is a new folder - request a full refresh.
  3153. //
  3154. bRefresh = TRUE;
  3155. //
  3156. // We have a valid folder. Make the piece of code below
  3157. // to work properly.
  3158. //
  3159. hr = S_OK;
  3160. }
  3161. else
  3162. {
  3163. //
  3164. // Release the folder memory and mark as invalid.
  3165. //
  3166. hr = E_OUTOFMEMORY;
  3167. delete pResult;
  3168. pResult = NULL;
  3169. }
  3170. }
  3171. if( gpFolders && S_OK == hr )
  3172. {
  3173. //
  3174. // Lock the folder CS while registering the new
  3175. // notify handler.
  3176. //
  3177. CCSLock::Locker lock( pResult->CritSec( ) );
  3178. //
  3179. // Register the new handler for this folder
  3180. //
  3181. hr = pResult->RegisterNotifyHandler( pClientNotify );
  3182. if( FAILED(hr) && pResult->Handlers_bEmpty() )
  3183. {
  3184. //
  3185. // This folder has no more clients, so
  3186. // we will destroy it outside of the
  3187. // global critical section scope
  3188. //
  3189. pResult->Link_vDelinkSelf( );
  3190. pResult->vCleanup();
  3191. pResult->vDecRefDelete();
  3192. pResult = NULL;
  3193. }
  3194. if( SUCCEEDED(hr) && phFolder )
  3195. {
  3196. *phFolder = reinterpret_cast<HANDLE>( pResult );
  3197. }
  3198. }
  3199. if( SUCCEEDED(hr) && pResult )
  3200. {
  3201. if( bRefresh )
  3202. {
  3203. //
  3204. // full refresh has been requested...
  3205. //
  3206. bFolderRefresh(reinterpret_cast<HANDLE>(pResult), pbAdministrator);
  3207. }
  3208. else
  3209. {
  3210. //
  3211. // Check to see if the current user is an administrator
  3212. //
  3213. if( pbAdministrator )
  3214. {
  3215. VDataSource *pDataSource = pResult->DataSource_pHead();
  3216. *pbAdministrator = pDataSource ? pDataSource->bAdministrator() : FALSE;
  3217. }
  3218. }
  3219. }
  3220. return hr;
  3221. }
  3222. HRESULT
  3223. TFolderList::
  3224. UnregisterDataSource(
  3225. IN LPCTSTR pszDataSource,
  3226. IN IFolderNotify *pClientNotify,
  3227. OUT LPHANDLE phFolder
  3228. )
  3229. /*++
  3230. Routine Description:
  3231. Unregisters a client for notification previousely
  3232. registered with RegisterDataSource() function
  3233. Note: pClientNotify can't be NULL
  3234. Arguments:
  3235. pszDataSource - Data source name
  3236. pClientNotify - Client feedback connection point
  3237. phFolder - Where to place a NULL handle
  3238. (to clear the handle var if provided)
  3239. Return Value:
  3240. S_OK - Success
  3241. E_XXX - Failure (for some reason)
  3242. --*/
  3243. {
  3244. TFolder *pFolder = NULL;
  3245. HRESULT hr = E_FAIL;
  3246. //
  3247. // Lock the global folder list
  3248. //
  3249. CCSLock::Locker CSL( *TFolderList::gpFolderLock );
  3250. if( gpFolders )
  3251. {
  3252. //
  3253. // Lookup for the desired data source.
  3254. // Returns S_OK if found and S_FALSE if not.
  3255. //
  3256. hr = pLookupFolder(pszDataSource, &pFolder);
  3257. if( S_OK == hr && TFolderList::bValidFolderObject(pFolder) )
  3258. {
  3259. //
  3260. // Lock the folder CS while unregistering the
  3261. // notify handler.
  3262. //
  3263. CCSLock::Locker lock( pFolder->CritSec( ) );
  3264. hr = pFolder->UnregisterNotifyHandler(pClientNotify);
  3265. //
  3266. // Everything is OK here. The client has been detached
  3267. // successfully
  3268. //
  3269. if( phFolder )
  3270. {
  3271. if( pFolder != *phFolder )
  3272. {
  3273. DBGMSG( DBG_FOLDER, ( "This must be TRUE: pFolder == *phFolder" ) );
  3274. }
  3275. *phFolder = NULL;
  3276. }
  3277. if( pFolder->Handlers_bEmpty() )
  3278. {
  3279. //
  3280. // This folder has no more clients, so
  3281. // put it in pending deletion mode, so it
  3282. // will be deleted outside the CS's.
  3283. //
  3284. pFolder->Link_vDelinkSelf( );
  3285. }
  3286. else
  3287. {
  3288. //
  3289. // Prevents folder to be deleted.
  3290. //
  3291. pFolder = NULL;
  3292. }
  3293. }
  3294. //
  3295. // Check to delete the folder list if empty
  3296. //
  3297. if( gpFolders->Folders_bEmpty() )
  3298. {
  3299. delete gpFolders;
  3300. gpFolders = NULL;
  3301. }
  3302. }
  3303. if( pFolder )
  3304. {
  3305. pFolder->vCleanup();
  3306. pFolder->vDecRefDelete();
  3307. }
  3308. return hr;
  3309. }
  3310. BOOL
  3311. TFolderList::
  3312. bValidFolderObject(
  3313. IN const TFolder *pFolder
  3314. )
  3315. /*++
  3316. Routine Description:
  3317. Verify if this folder object is valid - which means
  3318. if it is in the global folders list class
  3319. Arguments:
  3320. pFolder - The object we are trying to verify
  3321. Return Value:
  3322. TRUE - object is valid
  3323. FALSE - object has been dismised already
  3324. --*/
  3325. {
  3326. //
  3327. // Lock the global folder list
  3328. //
  3329. CCSLock::Locker fldrCSL( *TFolderList::gpFolderLock );
  3330. BOOL bResult = FALSE;
  3331. if( pFolder && gpFolders )
  3332. {
  3333. TIter iter;
  3334. TFolder *pTemp;
  3335. for( gpFolders->Folders_vIterInit( iter ), iter.vNext(); iter.bValid(); iter.vNext() )
  3336. {
  3337. pTemp = gpFolders->Folders_pConvert( iter );
  3338. if( pTemp == pFolder )
  3339. {
  3340. bResult = TRUE;
  3341. break;
  3342. }
  3343. }
  3344. }
  3345. return bResult;
  3346. }
  3347. HRESULT
  3348. TFolderList::
  3349. pLookupFolder(
  3350. IN LPCTSTR pszDataSource,
  3351. OUT TFolder **ppFolder
  3352. )
  3353. /*++
  3354. Routine Description:
  3355. Lookup for a folder in the global list for a
  3356. particular datasource.
  3357. Arguments:
  3358. pszDataSource - Datasource, which we are seeking folder for.
  3359. ppFolder - Where to return the folder object if found
  3360. Return Value:
  3361. S_OK - found
  3362. S_FALSE - not found.
  3363. E_XXXX - in case of an error
  3364. --*/
  3365. {
  3366. //
  3367. // Lock the global folder list
  3368. //
  3369. CCSLock::Locker fldrCSL( *TFolderList::gpFolderLock );
  3370. SPLASSERT( pszDataSource );
  3371. HRESULT hr = S_FALSE;
  3372. TIter iter;
  3373. TFolder *pFolder;
  3374. for( gpFolders->Folders_vIterInit( iter ), iter.vNext(); iter.bValid(); iter.vNext() )
  3375. {
  3376. pFolder = gpFolders->Folders_pConvert( iter );
  3377. if( 0 == _tcsicmp( pFolder->strLocalDataSource(), pszDataSource ) )
  3378. {
  3379. //
  3380. // found!
  3381. //
  3382. hr = S_OK;
  3383. if( ppFolder )
  3384. {
  3385. *ppFolder = pFolder;
  3386. }
  3387. break;
  3388. }
  3389. }
  3390. return hr;
  3391. }
  3392. BOOL
  3393. TFolderList::
  3394. bValid(
  3395. VOID
  3396. ) const
  3397. {
  3398. return TRUE;
  3399. }
  3400. #if DBG_FOLDER
  3401. /********************************************************************
  3402. Debug routines: dump a FOLDER_PRINTER_DATA
  3403. ********************************************************************/
  3404. VOID
  3405. vDumpFolderPrinterData(
  3406. PFOLDER_PRINTER_DATA pData
  3407. )
  3408. {
  3409. DBGMSG( DBG_FOLDER, ( "===vDumpFolderPrinterData begin.\n" ));
  3410. if( !pData ){
  3411. DBGMSG( DBG_FOLDER, ( "---NULL pData\n" ));
  3412. return;
  3413. }
  3414. DBGMSG( DBG_FOLDER, ( "--- %x\n", pData ));
  3415. DBGMSG( DBG_FOLDER, ( "--- %x name: "TSTR"\n", pData->pName, DBGSTR( pData->pName )));
  3416. DBGMSG( DBG_FOLDER, ( "--- %x comment: "TSTR"\n", pData->pComment, DBGSTR( pData->pComment )));
  3417. DBGMSG( DBG_FOLDER, ( "--- %x location: "TSTR"\n", pData->pLocation, DBGSTR( pData->pLocation )));
  3418. DBGMSG( DBG_FOLDER, ( "--- %x model: "TSTR"\n", pData->pDriverName, DBGSTR( pData->pDriverName )));
  3419. DBGMSG( DBG_FOLDER, ( "--- status : %x\n", pData->Status ));
  3420. DBGMSG( DBG_FOLDER, ( "--- attributes: %x\n", pData->Attributes ));
  3421. DBGMSG( DBG_FOLDER, ( "--- cJobs: %x\n", pData->cJobs ));
  3422. }
  3423. #endif