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

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